Source Code
Overview
ETH Balance
0 ETH
Multichain Info
N/A
Latest 25 from a total of 29 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
Amount
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Create Project | 96940634 | 362 days ago | IN | 0 ETH | 0.0000411 | ||||
| Create Project | 96940043 | 362 days ago | IN | 0 ETH | 0.0000443 | ||||
| Create Project | 96939335 | 362 days ago | IN | 0 ETH | 0.00004229 | ||||
| Create Project | 96938758 | 362 days ago | IN | 0 ETH | 0.00003903 | ||||
| Safe Transfer Fr... | 96934118 | 362 days ago | IN | 0 ETH | 0.00001224 | ||||
| Safe Transfer Fr... | 96934115 | 362 days ago | IN | 0 ETH | 0.00001224 | ||||
| Safe Transfer Fr... | 96933937 | 362 days ago | IN | 0 ETH | 0.00001223 | ||||
| Safe Transfer Fr... | 96933757 | 362 days ago | IN | 0 ETH | 0.00001223 | ||||
| Safe Transfer Fr... | 96933669 | 362 days ago | IN | 0 ETH | 0.00001226 | ||||
| Safe Transfer Fr... | 96932993 | 362 days ago | IN | 0 ETH | 0.00001178 | ||||
| Safe Transfer Fr... | 96932710 | 362 days ago | IN | 0 ETH | 0.00001178 | ||||
| Safe Transfer Fr... | 96932551 | 362 days ago | IN | 0 ETH | 0.00001178 | ||||
| Safe Transfer Fr... | 96930049 | 362 days ago | IN | 0 ETH | 0.00001383 | ||||
| Safe Transfer Fr... | 96929598 | 362 days ago | IN | 0 ETH | 0.00001201 | ||||
| Create Project | 96929104 | 362 days ago | IN | 0 ETH | 0.000042 | ||||
| Safe Transfer Fr... | 96928481 | 362 days ago | IN | 0 ETH | 0.00001386 | ||||
| Safe Transfer Fr... | 96928323 | 362 days ago | IN | 0 ETH | 0.00001012 | ||||
| Safe Transfer Fr... | 96925739 | 362 days ago | IN | 0 ETH | 0.00001474 | ||||
| Create Project | 96925218 | 362 days ago | IN | 0 ETH | 0.00004204 | ||||
| Safe Transfer Fr... | 96924948 | 362 days ago | IN | 0 ETH | 0.0000101 | ||||
| Safe Transfer Fr... | 96920497 | 362 days ago | IN | 0 ETH | 0.00001011 | ||||
| Safe Transfer Fr... | 96920416 | 362 days ago | IN | 0 ETH | 0.0000141 | ||||
| Safe Transfer Fr... | 96919903 | 362 days ago | IN | 0 ETH | 0.00001569 | ||||
| Create Project | 96918926 | 362 days ago | IN | 0 ETH | 0.00004202 | ||||
| Set Reward Contr... | 96756263 | 363 days ago | IN | 0 ETH | 0.0000047 |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
ELVX_REToken
Compiler Version
v0.8.27+commit.40a35a09
Optimization Enabled:
Yes with 100 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
/*
_ _ _ ____ _ _
| | | | _ __ | | __ _ __ ___ __ __ _ __ / ___| _ __ __ _ __ __ (_) | |_ _ _
| | | | | '_ \ | |/ / | '_ \ / _ \ \ \ /\ / / | '_ \ | | _ | '__| / _` | \ \ / / | | | __| | | | |
| |_| | | | | | | < | | | | | (_) | \ V V / | | | | | |_| | | | | (_| | \ V / | | | |_ | |_| |
\___/ |_| |_| |_|\_\ |_| |_| \___/ \_/\_/ |_| |_| \____| |_| \__,_| \_/ |_| \__| \__, |
|___/
WWW.UNKNOWNGRAVITY.COM
*/
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.27;
// ------------------------------------------
// | IMPORTS |
// ------------------------------------------
// Estandar ERC1155
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
// Extensions
import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol";
import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol";
// Security
import "@openzeppelin/contracts/access/IAccessControl.sol";
import "@openzeppelin/contracts/access/extensions/AccessControlEnumerable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
// Utils
import "@openzeppelin/contracts/utils/Strings.sol";
// Projects
import "./auxs/ELVX_Projects.sol";
// Rewards interface
import "./ELVX_REClaim.sol";
// ---------------------------------------------------
// | EleveX - REAL ESTATE TOKEN CONTRACT V.1.0 |
// ---------------------------------------------------
/**
* @title Token contract for EleveX Real Estate
* @author Unknown Gravity
* @notice Contract used to create and manage Real Estate tokens
* @dev Signer functions are used to validate the creation of projects
*/
contract ELVX_REToken is
ELVX_Projects,
ERC1155,
ERC1155Burnable,
ERC1155Supply,
AccessControlEnumerable,
ReentrancyGuard
{
using Strings for uint256;
// ------------------------------------------
// | CONSTANTS & GLOBAL VARIABLES |
// ------------------------------------------
// Contract name
string public name;
// Contract symbol
string public symbol;
// Metadata - URI CONTROL
string private SC_METADATA;
string private BASE_URI;
string private BASE_EXTENSION = "/metadata.json";
// Royalties
uint96 public ROYALTY_POINTS; // (500 = 5% de royalties | 1000 = 10% de royalties...)
uint96 private constant MAX_ROYALTY_POINTS = 500; // Maximo un 5% de royalties
bytes4 private constant _INTERFACE_ID_ERC2981 = 0x2a55205a; // Interface for ERC2981 (Royalty Standard)
address payable public ROYALTY_RECIPIENT; // Dirección que recibirá los fondos de royalties
// ------------------------------------------
// | ROLES |
// ------------------------------------------
// ADMIN ROLE BY DEFAULT
bytes32 public constant SELLER_ROLE = keccak256("SELLER_ROLE"); // SELLER_ROLE: Seller contract role
bytes32 public constant REWARD_ROLE = keccak256("REWARD_ROLE"); // REWARD_ROLE: Reward contract role
// ------------------------------------------
// | MAPPINGS |
// ------------------------------------------
// Address to projectIds
mapping(address => uint256[]) public userBalances;
// ------------------------------------------
// | EVENTS |
// ------------------------------------------
event Mint(address indexed to, uint256 indexed tokenId, uint256 amount);
event Burn(address indexed from, uint256 indexed tokenId, uint256 amount);
event SetRoyaltiesAddress(address newRoyaltiesAddress);
event SetRoyaltiesBasicPoints(uint96 newRoyaltiesBasicPoints);
// ------------------------------------------
// | MODIFIERS |
// ------------------------------------------
/**
* @dev MODIFIER - Only MINTER ROLE can call.
*/
modifier onlySeller() {
require(
hasRole(SELLER_ROLE, _msgSender()),
"Only minter can call this function"
);
_;
}
/**
* @dev MODIFIER - Only BURNER ROLE can call.
*/
modifier onlyRewards() {
require(
hasRole(REWARD_ROLE, _msgSender()),
"Only burner can call this function"
);
_;
}
modifier onlySellerOrRewards() {
require(
hasRole(SELLER_ROLE, _msgSender()) ||
hasRole(REWARD_ROLE, _msgSender()),
"Only seller or burner can call this function"
);
_;
}
modifier onlySellerOrOwner() {
require(
hasRole(SELLER_ROLE, _msgSender()) || _msgSender() == owner(),
"Only seller or owner can call this function"
);
_;
}
// ------------------------------------------
// | CONSTRUCTOR |
// ------------------------------------------
/**
* @dev Constructor
* @param _name string Contract name (compatibility).
* @param _symbol string Contract symbol (compatibility).
* @param _baseUri string Base URI for metadata.
* @param _collectionUri string Collection URI for metadata.
* @param _royaltyAddress address Royalty address for payments.
* @param _royaltyPoints uint96 Royalty points for payments.
* @param _msgGeneratorAddress address Address of the message generator contract.
* @param _oracleAddress address Address of the treasury contract.
*/
constructor(
string memory _name,
string memory _symbol,
string memory _baseUri,
string memory _collectionUri,
address payable _royaltyAddress,
uint96 _royaltyPoints,
address _msgGeneratorAddress,
address _oracleAddress
)
ERC1155("https://cdn.elevex.ai/metadata/{id}.json")
Ownable(_msgSender())
ELVX_Projects(_msgGeneratorAddress, _oracleAddress)
{
// Set contract name & symbol (compatibility)
name = _name;
symbol = _symbol;
// Metadata URI
BASE_URI = _baseUri;
// Contract URI
SC_METADATA = _collectionUri;
// Argumentos seteados
_setRoyaltyBasicPoints(_royaltyPoints);
_setRoyaltyAddress(_royaltyAddress);
}
// ------------------------------------------
// | CONTRACT CONTROL |
// ------------------------------------------
/**
* @dev Change the name of the contract.
* @param _name string New name.
*/
function setName(string memory _name) external onlyOwner {
name = _name;
}
/**
* @dev Change the symbol of the contract.
* @param _symbol string New symbol.
*/
function setSymbol(string memory _symbol) external onlyOwner {
symbol = _symbol;
}
// ------------------------------------------
// | SMART CONTRACT METADATA |
// ------------------------------------------
/**
* @dev Smart Contract metadata
* See https://docs.opensea.io/docs/contract-level-metadata
*/
function contractURI() external view returns (string memory) {
return SC_METADATA;
}
/**
* @dev Change the URI - Smart contract metadata
* See https://docs.opensea.io/docs/contract-level-metadata
*/
function setContractURI(string memory _newUri) external onlyOwner {
SC_METADATA = _newUri;
}
// ------------------------------------------
// | COLLECTION METADATA |
// ------------------------------------------
/**
* @dev Returns the URI for a given token ID.
* Throws if the token ID does not exist. May return an empty string.
* @param _tokenid uint256 ID of the token to query
* @return string URI of the token
**/
function uri(
uint256 _tokenid
) public view override returns (string memory) {
require(exists(_tokenid), "URI query for nonexistent token");
return
string.concat(BASE_URI, Strings.toString(_tokenid), BASE_EXTENSION);
}
/**
* @dev Change the URI - Collection metadata
* @param _newUri string New URI.
*/
function setBaseURI(string memory _newUri) external onlyOwner {
BASE_URI = _newUri;
}
/**
* @dev Change the extension - Collection metadata
* @param _newExtension string New extension.
*/
function setBaseExtension(string memory _newExtension) external onlyOwner {
BASE_EXTENSION = _newExtension;
}
// ------------------------------------------
// | INTERNAL FUNCTIONS |
// ------------------------------------------
/**
* @dev Adds a projectId to the user's balance if it does not already exist.
* @param _user The address of the user.
* @param _projectId The project ID to add.
*/
function _addUserProjectId(address _user, uint256 _projectId) internal {
uint256[] storage projects = userBalances[_user];
// Check if the project ID already exists to avoid duplicates
if (!ELVX_Tools._existsInArray(projects, _projectId)) {
projects.push(_projectId);
}
}
/**
* @dev Removes a projectId from the user's balance if it exists.
* @param _user The address of the user.
* @param _projectId The project ID to remove.
*/
function _removeUserProjectId(address _user, uint256 _projectId) internal {
uint256[] storage projects = userBalances[_user];
uint256 len = projects.length;
for (uint256 i = 0; i < len; i++) {
if (projects[i] == _projectId) {
projects[i] = projects[len - 1]; // Replace with the last element
projects.pop(); // Remove the last element
return;
}
}
}
/**
* @dev Checks the user balance for the specified projectId and updates the user's project list accordingly.
* If the user has a balance of the project ID, it ensures the ID is in the list; if the balance is zero, it removes it.
* @param _user The address of the user.
* @param _projectId The project ID to check and update.
*/
function _checkAndUpdateUserProjectId(
address _user,
uint256 _projectId
) internal {
if (balanceOf(_user, _projectId) > 0) {
_addUserProjectId(_user, _projectId); // Add if balance exists
} else {
_removeUserProjectId(_user, _projectId); // Remove if balance is zero
}
}
/**
* @dev Set the royalty address payment
* @param _royaltyAddress address Wallet address.
*/
function _setRoyaltyAddress(address payable _royaltyAddress) internal {
require(
_royaltyAddress != address(0),
"Exception in setRoyaltiesAddress: Address zero."
);
ROYALTY_RECIPIENT = _royaltyAddress;
}
/**
* @dev Set the royalty points payment
* @param _royaltyPoints uint256 Royalty points.
*/
function _setRoyaltyBasicPoints(uint96 _royaltyPoints) internal {
require(
_royaltyPoints <= MAX_ROYALTY_POINTS,
"Royaties error: Limit reached"
);
ROYALTY_POINTS = _royaltyPoints;
}
/**
* @dev Update project supply.
* @param projectId uint256 Project ID.
* @param amount uint256 Amount to update.
*/
function _updateProjectSupply(uint256 projectId, uint256 amount) internal {
// Check if the amount is less than the total supply
require(
getProjectMintedSupply(projectId) + amount <=
getProjectMaxSupply(projectId),
"Minting more than total supply"
);
// Update supply
projects[projectId].supply.minted += amount;
}
/**
* @dev Toggle burn for a project.
* @param projectId uint256 Project ID.
* @param isEnabled bool Enable or disable burn.
*/
function _toggleBurn(uint256 projectId, bool isEnabled) internal {
require(existsProject(projectId), "Project not exists");
require(
isProjectBurnActive(projectId) != isEnabled,
"Burn is already set"
);
// If try to disable burn, check if refund is active
// If refund is active, burn can't be disabled
if (!isEnabled)
require(
!projects[projectId].refundActive,
"Project refund is active, burn can't be disabled"
);
projects[projectId].burnActive = isEnabled;
emit ToggleBurn(projectId, isEnabled);
}
// ------------------------------------------
// | PUBLIC AND EXTERNAL FUNCTIONS |
// ------------------------------------------
/**
* @dev Get the projects of a user and the amounts of each project.
* @param _user The address of the user.
*/
function getUserProjects(
address _user
) external view returns (uint256[] memory, uint256[] memory) {
uint256[] memory projectsIds = userBalances[_user];
uint256[] memory projectAmounts = new uint256[](projectsIds.length);
for (uint256 i = 0; i < projectsIds.length; i++) {
projectAmounts[i] = balanceOf(_user, projectsIds[i]);
}
return (projectsIds, projectAmounts);
}
/**
* @dev Reduce supply of a project.
* @param projectId uint256 Project ID.
*/
function reduceSupply(uint256 projectId) external onlySellerOrOwner {
_reduceSupply(projectId);
}
// ------------------------------------------
// | PAUSE FUNCTIONS |
// ------------------------------------------
/**
* @dev Pause the contract
*/
function pause() public onlyOwner {
_pause();
}
/**
* @dev Unpause the contract
*/
function unpause() public onlyOwner {
_unpause();
}
// ------------------------------------------
// | MINTING FUNCTIONS |
// ------------------------------------------
/**
* @dev Mint tokens.
* @param to address Address to mint tokens.
* @param id uint256 Project UUID.
* @param amount uint256 Amount to mint.
*/
function mint(
address to,
uint256 id,
uint256 amount
) public onlySeller whenNotPaused nonReentrant {
// Check if project exists
require(existsProject(id), "Project not exists");
// Check if not refundable
require(!isRefundActive(id), "Project is refundable");
// Check if the amount is less than the total supply
_updateProjectSupply(id, amount);
// Mint tokens
_mint(to, id, amount, "");
// Check and update user project ID
_checkAndUpdateUserProjectId(to, id);
// Emit event
emit Mint(to, id, amount);
}
/**
* @dev Add raise to a project.
* @param projectId uint256 Project ID.
* @param amount uint256 Amount to add.
*/
function addRaiseToProject(
uint256 projectId,
uint256 amount
) external onlySeller {
_addRaiseToProject(projectId, amount);
}
/**
* @dev Set total raise to a project.
* @param projectId uint256 Project ID.
* @param amount uint256 Amount to set.
*/
function setTotalRaise(
uint256 projectId,
uint256 amount
) external onlyOwner {
_setRaiseToProject(projectId, amount);
}
/**
* @dev Set project as refundable.
* @param projectId uint256 Project ID.
*/
function enableProjectRefund(uint256 projectId) external onlySeller {
require(existsProject(projectId), "Project not exists");
require(!isRefundActive(projectId), "Refund is active");
// Enable refund
_setProjectRefundable(projectId, true);
// Enable burn
_toggleBurn(projectId, true);
}
/**
* @dev Set project as not refundable.
* @param projectId uint256 Project ID.
*/
function disableProjectRefund(uint256 projectId) external onlySeller {
require(existsProject(projectId), "Project not exists");
require(isRefundActive(projectId), "Project is already not refundable");
// Disable refund
_setProjectRefundable(projectId, false);
// Disable burn
_toggleBurn(projectId, false);
}
// ------------------------------------------
// | BURN FUNCTIONS |
// ------------------------------------------
/**
* @dev Burn tokens.
* @param from address Account to burn tokens.
* @param id uint256 Project ID.
* @param amount uint256 Amount to burn.
*/
function burn(
address from,
uint256 id,
uint256 amount
) public virtual override /* onlySellerOrRewards */ whenNotPaused {
// Check if project exists
require(existsProject(id), "Project not exists");
// Check if burning is enabled
require(isProjectBurnActive(id), "Project burn is not active");
// Burn tokens
_burn(from, id, amount);
// Check and update user project ID
_checkAndUpdateUserProjectId(from, id);
// Emit event
emit Burn(from, id, amount);
}
/**
* @dev Toggle burn for a project.
* @param projectId uint256 Project ID.
* @param isEnabled bool Enable or disable burn.
*/
function toggleBurn(
uint256 projectId,
bool isEnabled
) public onlySellerOrRewards whenNotPaused {
_toggleBurn(projectId, isEnabled);
}
// ------------------------------------------
// | OVERRIDES |
// ------------------------------------------
/**
* @dev Override _update to add pausable and supply control.
* @param from address Sender address.
* @param to address Receiver address.
* @param ids uint256[] Token IDs.
* @param values uint256[] Token amounts.
*/
function _update(
address from,
address to,
uint256[] memory ids,
uint256[] memory values
) internal virtual override(ERC1155, ERC1155Supply) whenNotPaused {
if (from != address(0) && to != address(0) && from != to) {
ELVX_REClaim rewardContract;
for (uint256 i = 0; i < ids.length; i++) {
// Check and update user project ID
_checkAndUpdateUserProjectId(from, ids[i]);
_checkAndUpdateUserProjectId(to, ids[i]);
// Check and update claimed rewards
Project memory project = projects[ids[i]];
rewardContract = ELVX_REClaim(project.addresses.rewardAddress);
rewardContract.transferClaimed(from, to, ids[i], values[i]);
}
}
super._update(from, to, ids, values);
}
// -------------------------------------------
// | ROYALTY FUNCTIONS |
// -------------------------------------------
/**
* @dev Get the royalty for a collection.
* @param _collectionId uint256 ID of the collection.
* @param _salePrice uint256 Sale price.
*/
function royaltyInfo(
uint256 _collectionId,
uint256 _salePrice
) external view returns (address receiver, uint256 royaltyAmount) {
if (exists(_collectionId))
return (ROYALTY_RECIPIENT, (_salePrice * ROYALTY_POINTS) / 10000);
return (address(0), 0);
}
/**
* @dev Set the royalty address payment
* @param rAddress address Wallet address.
*/
function setRoyaltiesAddress(address payable rAddress) external onlyOwner {
_setRoyaltyAddress(rAddress);
emit SetRoyaltiesAddress(rAddress);
}
/**
* @dev Set the royalty points payment
* @param _royaltyPoints uint256 Royalty points.
*/
function setRoyaltiesBasicPoints(uint96 _royaltyPoints) external onlyOwner {
_setRoyaltyBasicPoints(_royaltyPoints);
emit SetRoyaltiesBasicPoints(_royaltyPoints);
}
/**
* @dev Compatible with ERC2981 interface. Royalty.
* https://eips.ethereum.org/EIPS/eip-2981
*/
function supportsInterface(
bytes4 interfaceId
)
public
view
virtual
override(ERC1155, AccessControlEnumerable)
returns (bool)
{
if (interfaceId == _INTERFACE_ID_ERC2981) return true;
return super.supportsInterface(interfaceId);
}
// ---------------------------------------
// | ROLE FUNCTIONS |
// ---------------------------------------
/**
* @dev Override grantRole to check if is called by owner.
* Only owner can grant roles.
* @param role bytes32 Role.
* @param account address Account.
*/
function grantRole(
bytes32 role,
address account
) public virtual override(AccessControl, IAccessControl) onlyOwner {
super._grantRole(role, account);
}
/**
* @dev Override revokeRole to check if is called by owner.
* Only owner can revoke roles.
* @param role bytes32 Role.
* @param account address Account.
*/
function revokeRole(
bytes32 role,
address account
) public virtual override(AccessControl, IAccessControl) onlyOwner {
super._revokeRole(role, account);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// solhint-disable-next-line interface-starts-with-i
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(
uint80 _roundId
) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
mapping(bytes32 role => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
return _roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
if (!hasRole(role, account)) {
_roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/extensions/AccessControlEnumerable.sol)
pragma solidity ^0.8.20;
import {IAccessControlEnumerable} from "./IAccessControlEnumerable.sol";
import {AccessControl} from "../AccessControl.sol";
import {EnumerableSet} from "../../utils/structs/EnumerableSet.sol";
/**
* @dev Extension of {AccessControl} that allows enumerating the members of each role.
*/
abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
using EnumerableSet for EnumerableSet.AddressSet;
mapping(bytes32 role => EnumerableSet.AddressSet) private _roleMembers;
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) public view virtual returns (address) {
return _roleMembers[role].at(index);
}
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) public view virtual returns (uint256) {
return _roleMembers[role].length();
}
/**
* @dev Overload {AccessControl-_grantRole} to track enumerable memberships
*/
function _grantRole(bytes32 role, address account) internal virtual override returns (bool) {
bool granted = super._grantRole(role, account);
if (granted) {
_roleMembers[role].add(account);
}
return granted;
}
/**
* @dev Overload {AccessControl-_revokeRole} to track enumerable memberships
*/
function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) {
bool revoked = super._revokeRole(role, account);
if (revoked) {
_roleMembers[role].remove(account);
}
return revoked;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/extensions/IAccessControlEnumerable.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "../IAccessControl.sol";
/**
* @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
*/
interface IAccessControlEnumerable is IAccessControl {
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) external view returns (address);
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) external view returns (uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)
pragma solidity ^0.8.20;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev The `account` is missing a role.
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/
error AccessControlBadConfirmation();
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*/
function renounceRole(bytes32 role, address callerConfirmation) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/ERC1155.sol)
pragma solidity ^0.8.20;
import {IERC1155} from "./IERC1155.sol";
import {IERC1155Receiver} from "./IERC1155Receiver.sol";
import {IERC1155MetadataURI} from "./extensions/IERC1155MetadataURI.sol";
import {Context} from "../../utils/Context.sol";
import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol";
import {Arrays} from "../../utils/Arrays.sol";
import {IERC1155Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the basic standard multi-token.
* See https://eips.ethereum.org/EIPS/eip-1155
* Originally based on code by Enjin: https://github.com/enjin/erc-1155
*/
abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IERC1155Errors {
using Arrays for uint256[];
using Arrays for address[];
mapping(uint256 id => mapping(address account => uint256)) private _balances;
mapping(address account => mapping(address operator => bool)) private _operatorApprovals;
// Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
string private _uri;
/**
* @dev See {_setURI}.
*/
constructor(string memory uri_) {
_setURI(uri_);
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC1155).interfaceId ||
interfaceId == type(IERC1155MetadataURI).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC1155MetadataURI-uri}.
*
* This implementation returns the same URI for *all* token types. It relies
* on the token type ID substitution mechanism
* https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
*
* Clients calling this function must replace the `\{id\}` substring with the
* actual token type ID.
*/
function uri(uint256 /* id */) public view virtual returns (string memory) {
return _uri;
}
/**
* @dev See {IERC1155-balanceOf}.
*/
function balanceOf(address account, uint256 id) public view virtual returns (uint256) {
return _balances[id][account];
}
/**
* @dev See {IERC1155-balanceOfBatch}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(
address[] memory accounts,
uint256[] memory ids
) public view virtual returns (uint256[] memory) {
if (accounts.length != ids.length) {
revert ERC1155InvalidArrayLength(ids.length, accounts.length);
}
uint256[] memory batchBalances = new uint256[](accounts.length);
for (uint256 i = 0; i < accounts.length; ++i) {
batchBalances[i] = balanceOf(accounts.unsafeMemoryAccess(i), ids.unsafeMemoryAccess(i));
}
return batchBalances;
}
/**
* @dev See {IERC1155-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual {
_setApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC1155-isApprovedForAll}.
*/
function isApprovedForAll(address account, address operator) public view virtual returns (bool) {
return _operatorApprovals[account][operator];
}
/**
* @dev See {IERC1155-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes memory data) public virtual {
address sender = _msgSender();
if (from != sender && !isApprovedForAll(from, sender)) {
revert ERC1155MissingApprovalForAll(sender, from);
}
_safeTransferFrom(from, to, id, value, data);
}
/**
* @dev See {IERC1155-safeBatchTransferFrom}.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
) public virtual {
address sender = _msgSender();
if (from != sender && !isApprovedForAll(from, sender)) {
revert ERC1155MissingApprovalForAll(sender, from);
}
_safeBatchTransferFrom(from, to, ids, values, data);
}
/**
* @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`. Will mint (or burn) if `from`
* (or `to`) is the zero address.
*
* Emits a {TransferSingle} event if the arrays contain one element, and {TransferBatch} otherwise.
*
* Requirements:
*
* - If `to` refers to a smart contract, it must implement either {IERC1155Receiver-onERC1155Received}
* or {IERC1155Receiver-onERC1155BatchReceived} and return the acceptance magic value.
* - `ids` and `values` must have the same length.
*
* NOTE: The ERC-1155 acceptance check is not performed in this function. See {_updateWithAcceptanceCheck} instead.
*/
function _update(address from, address to, uint256[] memory ids, uint256[] memory values) internal virtual {
if (ids.length != values.length) {
revert ERC1155InvalidArrayLength(ids.length, values.length);
}
address operator = _msgSender();
for (uint256 i = 0; i < ids.length; ++i) {
uint256 id = ids.unsafeMemoryAccess(i);
uint256 value = values.unsafeMemoryAccess(i);
if (from != address(0)) {
uint256 fromBalance = _balances[id][from];
if (fromBalance < value) {
revert ERC1155InsufficientBalance(from, fromBalance, value, id);
}
unchecked {
// Overflow not possible: value <= fromBalance
_balances[id][from] = fromBalance - value;
}
}
if (to != address(0)) {
_balances[id][to] += value;
}
}
if (ids.length == 1) {
uint256 id = ids.unsafeMemoryAccess(0);
uint256 value = values.unsafeMemoryAccess(0);
emit TransferSingle(operator, from, to, id, value);
} else {
emit TransferBatch(operator, from, to, ids, values);
}
}
/**
* @dev Version of {_update} that performs the token acceptance check by calling
* {IERC1155Receiver-onERC1155Received} or {IERC1155Receiver-onERC1155BatchReceived} on the receiver address if it
* contains code (eg. is a smart contract at the moment of execution).
*
* IMPORTANT: Overriding this function is discouraged because it poses a reentrancy risk from the receiver. So any
* update to the contract state after this function would break the check-effect-interaction pattern. Consider
* overriding {_update} instead.
*/
function _updateWithAcceptanceCheck(
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
) internal virtual {
_update(from, to, ids, values);
if (to != address(0)) {
address operator = _msgSender();
if (ids.length == 1) {
uint256 id = ids.unsafeMemoryAccess(0);
uint256 value = values.unsafeMemoryAccess(0);
_doSafeTransferAcceptanceCheck(operator, from, to, id, value, data);
} else {
_doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, values, data);
}
}
}
/**
* @dev Transfers a `value` tokens of token type `id` from `from` to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `from` must have a balance of tokens of type `id` of at least `value` amount.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function _safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes memory data) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
_updateWithAcceptanceCheck(from, to, ids, values, data);
}
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
* - `ids` and `values` must have the same length.
*/
function _safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
_updateWithAcceptanceCheck(from, to, ids, values, data);
}
/**
* @dev Sets a new URI for all token types, by relying on the token type ID
* substitution mechanism
* https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
*
* By this mechanism, any occurrence of the `\{id\}` substring in either the
* URI or any of the values in the JSON file at said URI will be replaced by
* clients with the token type ID.
*
* For example, the `https://token-cdn-domain/\{id\}.json` URI would be
* interpreted by clients as
* `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
* for token type ID 0x4cce0.
*
* See {uri}.
*
* Because these URIs cannot be meaningfully represented by the {URI} event,
* this function emits no events.
*/
function _setURI(string memory newuri) internal virtual {
_uri = newuri;
}
/**
* @dev Creates a `value` amount of tokens of type `id`, and assigns them to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function _mint(address to, uint256 id, uint256 value, bytes memory data) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
_updateWithAcceptanceCheck(address(0), to, ids, values, data);
}
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - `ids` and `values` must have the same length.
* - `to` cannot be the zero address.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function _mintBatch(address to, uint256[] memory ids, uint256[] memory values, bytes memory data) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
_updateWithAcceptanceCheck(address(0), to, ids, values, data);
}
/**
* @dev Destroys a `value` amount of tokens of type `id` from `from`
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `from` must have at least `value` amount of tokens of type `id`.
*/
function _burn(address from, uint256 id, uint256 value) internal {
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
_updateWithAcceptanceCheck(from, address(0), ids, values, "");
}
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `from` must have at least `value` amount of tokens of type `id`.
* - `ids` and `values` must have the same length.
*/
function _burnBatch(address from, uint256[] memory ids, uint256[] memory values) internal {
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
_updateWithAcceptanceCheck(from, address(0), ids, values, "");
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the zero address.
*/
function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
if (operator == address(0)) {
revert ERC1155InvalidOperator(address(0));
}
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev Performs an acceptance check by calling {IERC1155-onERC1155Received} on the `to` address
* if it contains code at the moment of execution.
*/
function _doSafeTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256 id,
uint256 value,
bytes memory data
) private {
if (to.code.length > 0) {
try IERC1155Receiver(to).onERC1155Received(operator, from, id, value, data) returns (bytes4 response) {
if (response != IERC1155Receiver.onERC1155Received.selector) {
// Tokens rejected
revert ERC1155InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
// non-ERC1155Receiver implementer
revert ERC1155InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
/**
* @dev Performs a batch acceptance check by calling {IERC1155-onERC1155BatchReceived} on the `to` address
* if it contains code at the moment of execution.
*/
function _doSafeBatchTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
) private {
if (to.code.length > 0) {
try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, values, data) returns (
bytes4 response
) {
if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
// Tokens rejected
revert ERC1155InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
// non-ERC1155Receiver implementer
revert ERC1155InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
/**
* @dev Creates an array in memory with only one value for each of the elements provided.
*/
function _asSingletonArrays(
uint256 element1,
uint256 element2
) private pure returns (uint256[] memory array1, uint256[] memory array2) {
/// @solidity memory-safe-assembly
assembly {
// Load the free memory pointer
array1 := mload(0x40)
// Set array length to 1
mstore(array1, 1)
// Store the single element at the next word after the length (where content starts)
mstore(add(array1, 0x20), element1)
// Repeat for next array locating it right after the first array
array2 := add(array1, 0x40)
mstore(array2, 1)
mstore(add(array2, 0x20), element2)
// Update the free memory pointer by pointing after the second array
mstore(0x40, add(array2, 0x40))
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/extensions/ERC1155Burnable.sol)
pragma solidity ^0.8.20;
import {ERC1155} from "../ERC1155.sol";
/**
* @dev Extension of {ERC1155} that allows token holders to destroy both their
* own tokens and those that they have been approved to use.
*/
abstract contract ERC1155Burnable is ERC1155 {
function burn(address account, uint256 id, uint256 value) public virtual {
if (account != _msgSender() && !isApprovedForAll(account, _msgSender())) {
revert ERC1155MissingApprovalForAll(_msgSender(), account);
}
_burn(account, id, value);
}
function burnBatch(address account, uint256[] memory ids, uint256[] memory values) public virtual {
if (account != _msgSender() && !isApprovedForAll(account, _msgSender())) {
revert ERC1155MissingApprovalForAll(_msgSender(), account);
}
_burnBatch(account, ids, values);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/extensions/ERC1155Supply.sol)
pragma solidity ^0.8.20;
import {ERC1155} from "../ERC1155.sol";
/**
* @dev Extension of ERC1155 that adds tracking of total supply per id.
*
* Useful for scenarios where Fungible and Non-fungible tokens have to be
* clearly identified. Note: While a totalSupply of 1 might mean the
* corresponding is an NFT, there is no guarantees that no other token with the
* same id are not going to be minted.
*
* NOTE: This contract implies a global limit of 2**256 - 1 to the number of tokens
* that can be minted.
*
* CAUTION: This extension should not be added in an upgrade to an already deployed contract.
*/
abstract contract ERC1155Supply is ERC1155 {
mapping(uint256 id => uint256) private _totalSupply;
uint256 private _totalSupplyAll;
/**
* @dev Total value of tokens in with a given id.
*/
function totalSupply(uint256 id) public view virtual returns (uint256) {
return _totalSupply[id];
}
/**
* @dev Total value of tokens.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupplyAll;
}
/**
* @dev Indicates whether any token exist with a given id, or not.
*/
function exists(uint256 id) public view virtual returns (bool) {
return totalSupply(id) > 0;
}
/**
* @dev See {ERC1155-_update}.
*/
function _update(
address from,
address to,
uint256[] memory ids,
uint256[] memory values
) internal virtual override {
super._update(from, to, ids, values);
if (from == address(0)) {
uint256 totalMintValue = 0;
for (uint256 i = 0; i < ids.length; ++i) {
uint256 value = values[i];
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply[ids[i]] += value;
totalMintValue += value;
}
// Overflow check required: The rest of the code assumes that totalSupplyAll never overflows
_totalSupplyAll += totalMintValue;
}
if (to == address(0)) {
uint256 totalBurnValue = 0;
for (uint256 i = 0; i < ids.length; ++i) {
uint256 value = values[i];
unchecked {
// Overflow not possible: values[i] <= balanceOf(from, ids[i]) <= totalSupply(ids[i])
_totalSupply[ids[i]] -= value;
// Overflow not possible: sum_i(values[i]) <= sum_i(totalSupply(ids[i])) <= totalSupplyAll
totalBurnValue += value;
}
}
unchecked {
// Overflow not possible: totalBurnValue = sum_i(values[i]) <= sum_i(totalSupply(ids[i])) <= totalSupplyAll
_totalSupplyAll -= totalBurnValue;
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/extensions/IERC1155MetadataURI.sol)
pragma solidity ^0.8.20;
import {IERC1155} from "../IERC1155.sol";
/**
* @dev Interface of the optional ERC1155MetadataExtension interface, as defined
* in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
*/
interface IERC1155MetadataURI is IERC1155 {
/**
* @dev Returns the URI for token type `id`.
*
* If the `\{id\}` substring is present in the URI, it must be replaced by
* clients with the actual token type ID.
*/
function uri(uint256 id) external view returns (string memory);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (token/ERC1155/IERC1155.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC1155 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-1155[EIP].
*/
interface IERC1155 is IERC165 {
/**
* @dev Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`.
*/
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
/**
* @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
* transfers.
*/
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
/**
* @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
* `approved`.
*/
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
/**
* @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
*
* If an {URI} event was emitted for `id`, the standard
* https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
* returned by {IERC1155MetadataURI-uri}.
*/
event URI(string value, uint256 indexed id);
/**
* @dev Returns the value of tokens of token type `id` owned by `account`.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function balanceOf(address account, uint256 id) external view returns (uint256);
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(
address[] calldata accounts,
uint256[] calldata ids
) external view returns (uint256[] memory);
/**
* @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the caller.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address account, address operator) external view returns (bool);
/**
* @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`.
*
* WARNING: This function can potentially allow a reentrancy attack when transferring tokens
* to an untrusted contract, when invoking {onERC1155Received} on the receiver.
* Ensure to follow the checks-effects-interactions pattern and consider employing
* reentrancy guards when interacting with untrusted contracts.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
* - `from` must have a balance of tokens of type `id` of at least `value` amount.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes calldata data) external;
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
*
* WARNING: This function can potentially allow a reentrancy attack when transferring tokens
* to an untrusted contract, when invoking {onERC1155BatchReceived} on the receiver.
* Ensure to follow the checks-effects-interactions pattern and consider employing
* reentrancy guards when interacting with untrusted contracts.
*
* Emits either a {TransferSingle} or a {TransferBatch} event, depending on the length of the array arguments.
*
* Requirements:
*
* - `ids` and `values` must have the same length.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/IERC1155Receiver.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Interface that must be implemented by smart contracts in order to receive
* ERC-1155 token transfers.
*/
interface IERC1155Receiver is IERC165 {
/**
* @dev Handles the receipt of a single ERC1155 token type. This function is
* called at the end of a `safeTransferFrom` after the balance has been updated.
*
* NOTE: To accept the transfer, this must return
* `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
* (i.e. 0xf23a6e61, or its own function selector).
*
* @param operator The address which initiated the transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param id The ID of the token being transferred
* @param value The amount of tokens being transferred
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
*/
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
/**
* @dev Handles the receipt of a multiple ERC1155 token types. This function
* is called at the end of a `safeBatchTransferFrom` after the balances have
* been updated.
*
* NOTE: To accept the transfer(s), this must return
* `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
* (i.e. 0xbc197c81, or its own function selector).
*
* @param operator The address which initiated the batch transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param ids An array containing ids of each token being transferred (order and length must match values array)
* @param values An array containing amounts of each token being transferred (order and length must match ids array)
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
*/
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
* ```
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Arrays.sol)
pragma solidity ^0.8.20;
import {StorageSlot} from "./StorageSlot.sol";
import {Math} from "./math/Math.sol";
/**
* @dev Collection of functions related to array types.
*/
library Arrays {
using StorageSlot for bytes32;
/**
* @dev Searches a sorted `array` and returns the first index that contains
* a value greater or equal to `element`. If no such index exists (i.e. all
* values in the array are strictly less than `element`), the array length is
* returned. Time complexity O(log n).
*
* `array` is expected to be sorted in ascending order, and to contain no
* repeated elements.
*/
function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds towards zero (it does integer division with truncation).
if (unsafeAccess(array, mid).value > element) {
high = mid;
} else {
low = mid + 1;
}
}
// At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
if (low > 0 && unsafeAccess(array, low - 1).value == element) {
return low - 1;
} else {
return low;
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) {
bytes32 slot;
// We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
// following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
/// @solidity memory-safe-assembly
assembly {
mstore(0, arr.slot)
slot := add(keccak256(0, 0x20), pos)
}
return slot.getAddressSlot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) {
bytes32 slot;
// We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
// following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
/// @solidity memory-safe-assembly
assembly {
mstore(0, arr.slot)
slot := add(keccak256(0, 0x20), pos)
}
return slot.getBytes32Slot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) {
bytes32 slot;
// We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
// following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
/// @solidity memory-safe-assembly
assembly {
mstore(0, arr.slot)
slot := add(keccak256(0, 0x20), pos)
}
return slot.getUint256Slot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(uint256[] memory arr, uint256 pos) internal pure returns (uint256 res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(address[] memory arr, uint256 pos) internal pure returns (address res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.20;
/**
* @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.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError, bytes32) {
// 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 (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): 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.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @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) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
pragma solidity ^0.8.20;
import {Strings} from "../Strings.sol";
/**
* @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
*
* The library provides methods for generating a hash of a message that conforms to the
* https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
* specifications.
*/
library MessageHashUtils {
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing a bytes32 `messageHash` with
* `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
* keccak256, although any bytes32 value can be safely used because the final digest will
* be re-hashed.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
}
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing an arbitrary `message` with
* `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x00` (data with intended validator).
*
* The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
* `validator` address. Then hashing the result.
*
* See {ECDSA-recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
/**
* @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
*
* The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
* `\x19\x01` and hashing the result. It corresponds to the hash signed by the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
*
* See {ECDSA-recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
bool private _paused;
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}/*
_ _ _ ____ _ _
| | | | _ __ | | __ _ __ ___ __ __ _ __ / ___| _ __ __ _ __ __ (_) | |_ _ _
| | | | | '_ \ | |/ / | '_ \ / _ \ \ \ /\ / / | '_ \ | | _ | '__| / _` | \ \ / / | | | __| | | | |
| |_| | | | | | | < | | | | | (_) | \ V V / | | | | | |_| | | | | (_| | \ V / | | | |_ | |_| |
\___/ |_| |_| |_|\_\ |_| |_| \___/ \_/\_/ |_| |_| \____| |_| \__,_| \_/ |_| \__| \__, |
|___/
WWW.UNKNOWNGRAVITY.COM
*/
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.27;
// --------------------------------------------------
// | IMPORTS |
// --------------------------------------------------
import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
// Utils and tools
import "./ELVX_Tools.sol";
/**
* @title ELVX_Messages
* @dev Generates string messages and hashes for the ELVX contract.
* @notice This contract is used to generate string messages and hashes for the ELVX contract.
*/
contract ELVX_Messages {
//--------------------------------------------------
// | STRING MESSAGES VARIABLES |
//--------------------------------------------------
/**
* @dev Generates a string message for the `createProject` function.
* @param projectId ID of the project
* @param projectType Type of the project
* @param totalSupply Total supply of the project
* @param projectPrice Price of the project
* @param withdrawAddress Address to withdraw funds
* @param paymentMethods Payment methods accepted
* @param fundingThreshold Minimum amount of funds to be raised
*/
function generateCreateProjectMsg(
uint256 projectId,
uint8 projectType,
uint256 totalSupply,
uint256 projectPrice,
address withdrawAddress,
string[] memory paymentMethods,
uint8 fundingThreshold,
string memory legalContract,
uint256 nonce
) public pure returns (string memory) {
string memory createInfo = string.concat(
"CREATE | Type: ",
ELVX_Tools._uintToString(projectType),
" | Legal: ",
legalContract
);
string memory generalInfo = ELVX_Tools._concatenateProjectInfo(
projectId,
totalSupply,
projectPrice,
withdrawAddress,
paymentMethods,
fundingThreshold,
nonce
);
return string.concat(createInfo, " | ", generalInfo);
}
/**
* @dev Generates a string message for the `updateProject` function.
* @param projectId ID of the project
* @param withdrawAddress Address to withdraw funds
* @param paymentMethods Payment methods accepted
*/
function generateUpdateProjectMsg(
uint256 projectId,
address withdrawAddress,
string[] memory paymentMethods,
uint256 nonce
) public pure returns (string memory) {
return
ELVX_Tools._concatenateUpdateProjectInfo(
projectId,
withdrawAddress,
paymentMethods,
nonce
);
}
// --------------------------------------------------
// | MESSAGE HASH GENERATORS |
// --------------------------------------------------
/**
* @dev Generates a hash for a given message.
* @param _message Message to hash
*/
function _generateHash(
bytes memory _message
) internal pure returns (bytes32) {
return MessageHashUtils.toEthSignedMessageHash(_message);
}
// --------------------------------------------------
// | MESSAGE HASH GENERATORS |
// --------------------------------------------------
/**
* @dev Generates a hash for the `createProject` function.
* @param projectId ID of the project
* @param projectType Type of the project
* @param totalSupply Total supply of the project
* @param projectPrice Price of the project
* @param withdrawAddress Address to withdraw funds
* @param paymentMethods Payment methods accepted
* @param fundingThreshold Minimum amount of funds to be raised
* @return Hash of the message
*/
function generateCreateProjectMsgHash(
uint256 projectId,
uint8 projectType,
uint256 totalSupply,
uint256 projectPrice,
address withdrawAddress,
string[] memory paymentMethods,
uint8 fundingThreshold,
string memory legalContract,
uint256 nonce
) external pure returns (bytes32) {
return
_generateHash(
bytes(
generateCreateProjectMsg(
projectId,
projectType,
totalSupply,
projectPrice,
withdrawAddress,
paymentMethods,
fundingThreshold,
legalContract,
nonce
)
)
);
}
/**
* @dev Generates a hash for the `updateProject` function.
* @param projectId ID of the project
* @param withdrawAddress Address to withdraw funds
* @param paymentMethods Payment methods accepted
* @return Hash of the message
*/
function generateUpdateProjectMsgHash(
uint256 projectId,
address withdrawAddress,
string[] memory paymentMethods,
uint256 nonce
) external pure returns (bytes32) {
return
_generateHash(
bytes(
generateUpdateProjectMsg(
projectId,
withdrawAddress,
paymentMethods,
nonce
)
)
);
}
}/*
_ _ _ ____ _ _
| | | | _ __ | | __ _ __ ___ __ __ _ __ / ___| _ __ __ _ __ __ (_) | |_ _ _
| | | | | '_ \ | |/ / | '_ \ / _ \ \ \ /\ / / | '_ \ | | _ | '__| / _` | \ \ / / | | | __| | | | |
| |_| | | | | | | < | | | | | (_) | \ V V / | | | | | |_| | | | | (_| | \ V / | | | |_ | |_| |
\___/ |_| |_| |_|\_\ |_| |_| \___/ \_/\_/ |_| |_| \____| |_| \__,_| \_/ |_| \__| \__, |
|___/
WWW.UNKNOWNGRAVITY.COM
*/
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.27;
// --------------------------------------------------
// | IMPORTS |
// --------------------------------------------------
// ERC-20 Interface for token payments
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/access/IAccessControl.sol";
import "@openzeppelin/contracts/access/extensions/AccessControlEnumerable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
// Oracle
import "../ELVX_Oracles.sol";
/**
* @title ELVX_Payments
* @dev Handles token payments for the ELVX Treasury.
* @notice This contract is used to handle token payments for the ELVX contract.
*/
abstract contract ELVX_Payments is
Ownable,
AccessControlEnumerable,
ReentrancyGuard
{
ELVX_Oracles private ORACLES;
// --------------------------------------------------
// | VARIABLES |
// --------------------------------------------------
bytes32 public constant WITHDRAWER_ROLE = keccak256("WITHDRAWER_ROLE");
// Mapping of token names to token instances
mapping(string => IERC20) private tokens;
// Array of supported tokens
string[] private paymentMethods;
// ------------------------------------------------
// | EVENTS |
// ------------------------------------------------
// Event for adding a new supported token
event TokenAdded(string indexed token, address indexed tokenAddress);
// Event for removing a supported token
event TokenRemoved(string indexed token);
// Event for updating a token
event TokenUpdated(string indexed token, address indexed tokenAddress);
// Event for token withdrawal
event Withdrawal(address indexed to, string token, uint256 amount);
// Event for token payments
event Payment(
address indexed from,
address indexed to,
string token,
uint256 amount
);
// Event for attempted transfer
event AttemptedTransfer(
address indexed from,
address indexed to,
uint256 amount,
uint256 allowance
);
// ------------------------------------------
// | MODIFIERS |
// ------------------------------------------
/**
* @dev Modifier to check if the caller is the owner or a withdrawer
*/
modifier onlyOwnerOrWithdrawer() {
require(
owner() == _msgSender() || hasRole(WITHDRAWER_ROLE, _msgSender()),
"Caller is not an owner or withdrawer"
);
_;
}
// ------------------------------------------
// | CONSTRUCTOR |
// ------------------------------------------
/**
* @dev Constructor
* @param usdcAddress USDC token address
* @param usdtAddress USDT token address
* @param oracle Oracle address
*/
constructor(address usdcAddress, address usdtAddress, address oracle) {
ORACLES = ELVX_Oracles(oracle);
_addOrUpdateToken("USDC", usdcAddress);
_addOrUpdateToken("USDT", usdtAddress);
}
// ------------------------------------------
// | INTERNAL FUNCTIONS |
// ------------------------------------------
/**
* @dev Internal function to check if a token exists
* @param _token Token name
*/
function _tokenExists(string memory _token) private view returns (bool) {
return tokens[_token] != IERC20(address(0));
}
/**
* @dev Internal function to add a token to the supported tokens list (ERC20)
* @param _token Token name
* @param _tokenAddress Token address
*/
function _addOrUpdateToken(
string memory _token,
address _tokenAddress
) private {
require(_tokenAddress != address(0), "Invalid token address");
require(bytes(_token).length > 0, "Token name cannot be empty");
require(
ORACLES.isSupportedDataFeed(_token),
"Token not supported by oracle"
);
if (_tokenExists(_token)) {
emit TokenUpdated(_token, _tokenAddress);
} else {
paymentMethods.push(_token);
emit TokenAdded(_token, _tokenAddress);
}
tokens[_token] = IERC20(_tokenAddress);
}
/**
* @dev Internal function to remove a token from the supported tokens list
* @param _token Token name
*/
function _removeToken(string memory _token) private {
require(_tokenExists(_token), "Token not found");
delete tokens[_token];
bytes32 tokenHash = keccak256(bytes(_token));
uint256 length = paymentMethods.length;
for (uint256 i = 0; i < length; i++) {
if (keccak256(bytes(paymentMethods[i])) == tokenHash) {
// Verificar si hay más de un elemento antes de hacer un intercambio
if (length > 1 && i < length - 1) {
paymentMethods[i] = paymentMethods[length - 1];
}
paymentMethods.pop();
break;
}
}
emit TokenRemoved(_token);
}
/**
* @dev Internal function to get the token instance
* @param _token Token name
*/
function _getTokenInstance(
string memory _token
) private view returns (IERC20) {
require(_tokenExists(_token), "Token not found");
return tokens[_token];
}
// ------------------------------------------
/**
* @dev Internal function to handle token payments
* @param _token ERC20 token instance
* @param _from Sender address
* @param _to Recipient address
* @param _amount Amount to transfer
*/
function _transferFromToken(
IERC20 _token,
address _from,
address _to,
uint256 _amount
) private {
// Transfer the tokens
require(
_token.transferFrom(_from, _to, _amount),
"Could not transfer token. Missing approval?"
);
}
/**
* @dev Internal function to send tokens
* @param _token ERC20 token instance
* @param _to Recipient address
* @param _amount Amount to transfer
*/
function _withdrawToken(
IERC20 _token,
address _to,
uint256 _amount
) private {
require(_to != address(0), "Invalid recipient address");
require(_amount > 0, "Amount must be greater than 0");
// Check balance
require(
_token.balanceOf(address(this)) >= _amount,
"Insufficient balance"
);
// Approve the contract to spend the tokens if necessary
uint256 allowance = _token.allowance(_msgSender(), address(this));
if (allowance < _amount) {
bool response = _token.approve(address(this), _amount);
require(response, "Could not approve the contract to spend tokens");
}
// Transfer tokens
_transferFromToken(_token, address(this), _to, _amount);
}
/**
* @dev Internal function to withdraw all tokens for a specific token
* @param _to Recipient address
* @param _token Token name
*/
function _withdrawAllForThisToken(
address _to,
string memory _token
) private {
uint256 amount = getTokenBalance(_token);
if (amount > 0) {
_withdrawToken(_getTokenInstance(_token), _to, amount);
emit Withdrawal(_to, _token, amount);
}
}
/**
* @dev Internal function to send ETH
* @param _to Recipient address
* @param _amount Amount to transfer in wei
*/
function _sendEth(address _to, uint256 _amount) private nonReentrant {
// Check recipient address
require(_to != address(0), "Invalid recipient address");
// Check balance
require(address(this).balance >= _amount, "Insufficient balance");
require(_amount > 0, "Amount must be greater than 0");
// Emit event
emit Withdrawal(_to, "ETH", _amount);
// Transfer ETH
payable(_to).transfer(_amount);
}
/**
* @dev Internal function to withdraw all ETH
* @param _to Recipient address
*/
function _withdrawAllEth(address _to) private {
uint256 ethBalance = address(this).balance;
if (ethBalance > 0) _sendEth(_to, ethBalance);
}
// ------------------------------------------
// | TOKENS FUNCTIONS |
// ------------------------------------------
/**
* @dev External function to add a token to the supported tokens list
* @param _token Token name
* @param _tokenAddress Token address
*/
function addSupportedToken(
string memory _token,
address _tokenAddress
) external onlyOwner {
require(!isSupportedToken(_token), "Token already exists");
_addOrUpdateToken(_token, _tokenAddress);
}
/**
* @dev External function to update a token in the supported tokens list
* @param _token Token name
* @param _tokenAddress Token address
*/
function updateSupportedToken(
string memory _token,
address _tokenAddress
) external onlyOwner {
require(isSupportedToken(_token), "Token not found");
_addOrUpdateToken(_token, _tokenAddress);
}
/**
* @dev External function to remove a token from the supported tokens list
* @param _token Token name
*/
function removeSupportedToken(string memory _token) external onlyOwner {
_removeToken(_token);
}
/**
* @dev External function to get the supported tokens
* @return string[] Array of supported tokens
*/
function getSupportedTokens() external view returns (string[] memory) {
return paymentMethods;
}
/**
* @dev External function to check if a token is supported
* @param _token Token name
* @return bool True if the token is supported
*/
function isSupportedToken(string memory _token) public view returns (bool) {
return _tokenExists(_token);
}
/**
* @dev External function to get the balance of a supported token
* @param _token Token name
* @return uint256 Balance of the token
*/
function getTokenBalance(
string memory _token
) public view returns (uint256) {
IERC20 token = _getTokenInstance(_token);
return token.balanceOf(address(this));
}
/**
* @dev External function to get the address of a supported token
* @param _token Token name
* @return address Address of the token
*/
function getTokenAddress(
string memory _token
) public view returns (address) {
return address(_getTokenInstance(_token));
}
/**
* @dev External function to get the decimals of a supported token
* @param _token Token name
* @return uint8 Decimals of the token
*/
function getTokenDecimals(
string memory _token
) public view returns (uint8) {
return ERC20(getTokenAddress(_token)).decimals();
}
// ------------------------------------------
// | PAYMENT FUNCTIONS |
// ------------------------------------------
/**
* @dev External function to pay with a supported token
* @param _token Token name
* @param _amount Amount to pay
*/
function addToken(
address _sender,
string memory _token,
uint256 _amount
) public {
address recipient = address(this);
_transferFromToken(
_getTokenInstance(_token),
_sender,
recipient,
_amount
);
emit Payment(_sender, recipient, _token, _amount);
}
/**
* @dev External function to pay with ETH
* @param _sender Sender address
* @param _amount Amount to pay
*/
function addETH(address _sender, uint256 _amount) external payable {
require(msg.value >= _amount, "Insufficient balance");
emit Payment(_sender, address(this), "ETH", _amount);
}
// ------------------------------------------
// | WITHDRAW FUNCTIONS |
// ------------------------------------------
/**
* @dev External function to withdraw a supported token
* @param _to Recipient address
* @param _token Token name
* @param _amount Amount to withdraw
*/
function withdrawToken(
address _to,
string memory _token,
uint256 _amount
) external onlyOwnerOrWithdrawer {
require(_to != address(0), "Invalid recipient address");
require(_amount > 0, "Amount must be greater than 0");
// Check if the balance is enough
require(getTokenBalance(_token) >= _amount, "Insufficient balance");
IERC20 tokenInstance = _getTokenInstance(_token);
// Transfer the tokens
_withdrawToken(tokenInstance, _to, _amount);
emit Withdrawal(_to, _token, _amount);
}
/**
* @dev External function to withdraw ETH
* @param _to Recipient address
* @param _amount Amount to withdraw
*/
function withdrawETH(
address _to,
uint256 _amount
) external onlyOwnerOrWithdrawer {
_sendEth(_to, _amount);
}
// ------------------------------------------
// | EMERGENCY FUNCTIONS |
// ------------------------------------------
/**
* @dev External function to withdraw all supported tokens
* @param _to Recipient address
*/
function emergencyWithdraw(address _to) external onlyOwner {
require(_to != address(0), "Invalid recipient address");
for (uint256 i = 0; i < paymentMethods.length; i++) {
string memory token = paymentMethods[i];
_withdrawAllForThisToken(_to, token);
}
// Withdraw ETH
_withdrawAllEth(_to);
}
// ---------------------------------------
// | ROLE FUNCTIONS |
// ---------------------------------------
/**
* @dev Override grantRole to check if is called by owner.
* Only owner can grant roles.
* @param role bytes32 Role.
* @param account address Account.
*/
function grantRole(
bytes32 role,
address account
) public virtual override(AccessControl, IAccessControl) onlyOwner {
super._grantRole(role, account);
}
/**
* @dev Override revokeRole to check if is called by owner.
* Only owner can revoke roles.
* @param role bytes32 Role.
* @param account address Account.
*/
function revokeRole(
bytes32 role,
address account
) public virtual override(AccessControl, IAccessControl) onlyOwner {
super._revokeRole(role, account);
}
// ------------------------------------------
// | FALLBACK FUNCTION |
// ------------------------------------------
//! TODO: Consider removing fallback functions
/**
* @dev Fallback function to receive ETH
*/
receive() external payable {}
}/*
_ _ _ ____ _ _
| | | | _ __ | | __ _ __ ___ __ __ _ __ / ___| _ __ __ _ __ __ (_) | |_ _ _
| | | | | '_ \ | |/ / | '_ \ / _ \ \ \ /\ / / | '_ \ | | _ | '__| / _` | \ \ / / | | | __| | | | |
| |_| | | | | | | < | | | | | (_) | \ V V / | | | | | |_| | | | | (_| | \ V / | | | |_ | |_| |
\___/ |_| |_| |_|\_\ |_| |_| \___/ \_/\_/ |_| |_| \____| |_| \__,_| \_/ |_| \__| \__, |
|___/
WWW.UNKNOWNGRAVITY.COM
*/
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.27;
// --------------------------------------------------
// | IMPORTS |
// --------------------------------------------------
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
// Signature
import "./ELVX_Signer.sol";
// Message generator
import "./ELVX_Messages.sol";
// Treasury
import "../ELVX_Oracles.sol";
// Tools
import "./ELVX_Tools.sol";
/**
* @title ELVX_Projects
* @dev Manages the projects in the ELVX contract.
* @notice This contract is used to manage the projects in the ELVX contract.
*/
abstract contract ELVX_Projects is Ownable, Pausable, ELVX_Signer {
ELVX_Messages public MSG_GENERATOR;
ELVX_Oracles public ORACLES;
// --------------------------------------------------
// Actual reward contract address
address public REWARD_CONTRACT_ADDRESS;
// --------------------------------------------------
string private LEGAL_CONTRACT_BASE =
"https://harlequin-peculiar-wren-625.mypinata.cloud/ipfs/";
// --------------------------------------------------
// | CONSTRUCTOR |
// --------------------------------------------------
/**
* @dev Constructor
* @param _msgGenerator address Message generator address.
* @param _oracleAddress address Oracle address.
*/
constructor(address _msgGenerator, address _oracleAddress) {
MSG_GENERATOR = ELVX_Messages(_msgGenerator);
ORACLES = ELVX_Oracles(_oracleAddress);
}
// ------------------------------------------
// | ENUMS |
// ------------------------------------------
/**
* @dev Project types.
* PURCHASE_PROJECT: Pre-purchase property
* PLEDGE_PROJECT: Future project
*/
enum ProjectType {
PURCHASE_PROJECT,
PLEDGE_PROJECT
}
// ------------------------------------------
// | STRUCTS |
// ------------------------------------------
/**
* @dev Supply struct.
* maxSupply: Max supply of the project.
* minted: Minted supply (total minted tokens).
*/
struct Supply {
// Max supply
uint256 maxSupply;
// Minted supply (total minted tokens)
uint256 minted;
// Actual supply with totalSupply function
}
struct Addresses {
// Withdraw address
address withdrawAddress;
// Reward address
address rewardAddress;
// Creator
address creator;
}
/**
* @dev Project struct.
*/
struct Project {
// Project type (Purchase or Pledge)
ProjectType projectType;
// Accepted payments
string[] paymentMethods;
// Project supply
Supply supply;
// Addresses
Addresses addresses;
// Project price in USD
uint256 price;
// Burn active
bool burnActive;
// Refund active
bool refundActive;
// Funding Threshold
uint8 fundingThreshold;
// Total raised
uint256 totalRaised;
// Legal contract
string legalContract;
}
// ------------------------------------------
// | MAPPINGS |
// ------------------------------------------
mapping(uint256 => Project) public projects;
// ------------------------------------------
// | EVENTS |
// ------------------------------------------
// Create
event ProjectCreated(
uint256 indexed projectId,
uint256 totalSupply,
uint256 projectPrice,
address withdrawAddress,
address rewardContractAddress,
string[] paymentMethods,
uint8 fundingThreshold
);
// Updates
event ProjectSupplyUpdated(uint256 indexed projectId, uint256 totalSupply);
event ProjectThresholdUpdated(uint256 indexed projectId, uint8 threshold);
event ProjectPriceUpdated(uint256 indexed projectId, uint256 price);
event ProjectPaymentMethodsUpdated(
uint256 indexed projectId,
string[] paymentMethods
);
event ProjectWithdrawAddressUpdated(
uint256 indexed projectId,
address withdrawAddress
);
event ProjectRewardAddressUpdated(
uint256 indexed projectId,
address rewardAddress
);
// Raised
event ProjectTotalRaised(uint256 indexed projectId, uint256 amount);
event ProjectAddRaise(uint256 indexed projectId, uint256 amount);
// Burn and Refund
event ToggleBurn(uint256 projectId, bool isBurnEnabled);
event ToggleRefund(uint256 projectId, bool isRefundEnabled);
// Metadata
event MetadataUpdate(uint256 projectId);
// ------------------------------------------
// | MODIFIERS |
// ------------------------------------------
/**
* @dev MODIFIER - Only creator or owner can call.
* @param projectId uint256 Project ID.
*/
modifier onlyCreatorOrOwner(uint256 projectId) {
require(
getProjectCreator(projectId) == _msgSender() ||
owner() == _msgSender(),
"Not authorized"
);
_;
}
/**
* @dev MODIFIER - Only if the project exists.
* @param projectId uint256 Project ID.
*/
modifier onlyIfProjectExists(uint256 projectId) {
require(existsProject(projectId), "Project not exists");
_;
}
/**
* @dev MODIFIER - Only if the project does not exist.
* @param projectId uint256 Project ID.
*/
modifier onlyIfProjectNotExists(uint256 projectId) {
require(!existsProject(projectId), "Project already exists");
_;
}
// ------------------------------------------
// | VALIDATIONS |
// ------------------------------------------
/**
* @dev Validate payment methods.
* @param paymentMethods string[] Payment methods.
*/
function _validatePaymentMethods(
string[] memory paymentMethods
) private view {
require(paymentMethods.length > 0, "Invalid payment methods");
for (uint256 i = 0; i < paymentMethods.length; i++) {
require(
ORACLES.isSupportedDataFeed(paymentMethods[i]),
"Payment method not supported by the oracle"
);
}
}
/**
* @dev Validate project inputs - used in createProject function.
* @param projectType uint8 Project type.
* @param totalSupply uint256 Total supply of the project.
* @param projectPrice uint256 Project price in USD.
* @param withdrawAddress address Withdraw address.
* @param fundingThreshold uint8 Funding threshold.
* @param paymentMethods string[] Payment methods.
*/
function _validateProjectInputs(
ProjectType projectType,
uint256 totalSupply,
uint256 projectPrice,
address withdrawAddress,
uint8 fundingThreshold,
string[] memory paymentMethods
) private view {
require(totalSupply != 0, "Invalid supply");
require(
projectType == ProjectType.PURCHASE_PROJECT ||
projectType == ProjectType.PLEDGE_PROJECT,
"Invalid project type"
);
require(projectPrice != 0, "Invalid price");
require(withdrawAddress != address(0), "Invalid address");
require(withdrawAddress != REWARD_CONTRACT_ADDRESS, "Invalid address");
if (projectType == ProjectType.PURCHASE_PROJECT) {
require(fundingThreshold != 0, "Invalid funding threshold");
}
// Validate payment methods
_validatePaymentMethods(paymentMethods);
}
/**
* @dev Validate update project inputs - used in updateProject function.
* @param withdrawAddress address Withdraw address.
* @param paymentMethods string[] Payment methods.
*/
function _validateUpdateProjectInputs(
address withdrawAddress,
string[] memory paymentMethods
) private view {
require(withdrawAddress != address(0), "Invalid address");
require(withdrawAddress != REWARD_CONTRACT_ADDRESS, "Invalid address");
// Validate payment methods
_validatePaymentMethods(paymentMethods);
}
// ------------------------------------------
/**
* @dev Validate create project signature.
* @param projectId uint256 Project ID.
* @param projectType uint8 Project type.
* @param totalSupply uint256 Total supply of the project.
* @param projectPrice uint256 Project price in USD.
* @param withdrawAddress address Withdraw address.
* @param paymentMethods string[] Payment methods.
* @param fundingThreshold uint8 Funding threshold.
* @param nonce uint256 Nonce.
* @param _signature bytes Signature.
*/
function _validateCreateProjectSignature(
uint256 projectId,
ProjectType projectType,
uint256 totalSupply,
uint256 projectPrice,
address withdrawAddress,
string[] memory paymentMethods,
uint8 fundingThreshold,
string memory legalContract,
uint256 nonce,
bytes memory _signature
) internal returns (bool) {
// Generate the message to be signed
bytes32 message = MSG_GENERATOR.generateCreateProjectMsgHash(
projectId,
uint8(projectType),
totalSupply,
projectPrice,
withdrawAddress,
paymentMethods,
fundingThreshold,
legalContract,
nonce
);
// Validate signature
return _isSigValid(message, nonce, _signature);
}
/**
* @dev Validate update project signature.
* @param projectId uint256 Project ID.
* @param withdrawAddress address Withdraw address.
* @param paymentMethods string[] Payment methods.
* @param nonce uint256 Nonce.
* @param _signature bytes Signature.
*/
function _validateUpdateProjectSignature(
uint256 projectId,
address withdrawAddress,
string[] memory paymentMethods,
uint256 nonce,
bytes memory _signature
) internal returns (bool) {
// Generate the message to be signed
bytes32 message = MSG_GENERATOR.generateUpdateProjectMsgHash(
projectId,
withdrawAddress,
paymentMethods,
nonce
);
// Validate signature
return _isSigValid(message, nonce, _signature);
}
// ------------------------------------------
// | INTERNAL FUNCTIONS |
// ------------------------------------------
/**
* @dev Set a new message generator address.
* @param _msgGenerator address Message generator address.
*/
function _setNewMessageGenerator(address _msgGenerator) internal {
require(_msgGenerator != address(0), "Invalid address");
require(
_msgGenerator != address(MSG_GENERATOR),
"Same address as the current one"
);
MSG_GENERATOR = ELVX_Messages(_msgGenerator);
}
/**
* @dev Set a new reward contract address.
* @param _newRewardContract address Reward contract address.
*/
function _setNewRewardContractAddress(address _newRewardContract) internal {
require(_newRewardContract != address(0), "Invalid address");
require(
_newRewardContract != REWARD_CONTRACT_ADDRESS,
"Same address as the current one"
);
REWARD_CONTRACT_ADDRESS = _newRewardContract;
}
/**
* @dev Create a new project - used in createProject function.
* @param projectId uint256 Project ID.
* @param projectType uint8 Project type.
* @param totalSupply uint256 Total supply of the project.
* @param projectPrice uint256 Project price in USD.
* @param withdrawAddress address Withdraw address.
* @param paymentMethods string[] Payment methods.
* @param fundingThreshold uint8 Funding threshold.
*/
function _createProjectInternal(
uint256 projectId,
ProjectType projectType,
uint256 totalSupply,
uint256 projectPrice,
address withdrawAddress,
string[] memory paymentMethods,
uint8 fundingThreshold,
string memory legalContract
) private {
// Create supply struct
Supply memory supply = Supply({maxSupply: totalSupply, minted: 0});
// Create addresses struct
Addresses memory addresses = Addresses({
withdrawAddress: withdrawAddress,
rewardAddress: REWARD_CONTRACT_ADDRESS,
creator: _msgSender()
});
projects[projectId] = Project({
projectType: projectType,
paymentMethods: paymentMethods,
supply: supply,
addresses: addresses,
price: projectPrice,
burnActive: false,
refundActive: false,
fundingThreshold: fundingThreshold,
totalRaised: 0,
legalContract: legalContract
});
// Emit event
emit ProjectCreated(
projectId,
totalSupply,
projectPrice,
withdrawAddress,
REWARD_CONTRACT_ADDRESS,
paymentMethods,
fundingThreshold
);
}
/**
* @dev Set a project supply
* @param projectId uint256 Project ID.
* @param newSupply uint256 Supply amount.
*/
function _setProjectSupply(
uint256 projectId,
uint256 newSupply
) internal onlyIfProjectExists(projectId) whenNotPaused {
require(newSupply != 0, "Invalid supply");
require(
newSupply >= projects[projectId].supply.minted,
"Invalid supply"
);
// Update project
if (newSupply != projects[projectId].supply.maxSupply) {
projects[projectId].supply.maxSupply = newSupply;
emit ProjectSupplyUpdated(projectId, newSupply);
}
}
/**
* @dev Reduce max supply of a project to minted supply.
* @param projectId uint256 Project ID.
*/
function _reduceSupply(
uint256 projectId
) internal onlyIfProjectExists(projectId) whenNotPaused {
_setProjectSupply(projectId, getProjectMaxSupply(projectId));
}
/**
* @dev Get if a project is refundable/burnable.
* @param projectId uint256 Project ID.
*/
function _setProjectRefundable(
uint256 projectId,
bool isRefundEnabled
) internal onlyIfProjectExists(projectId) whenNotPaused {
require(
projects[projectId].refundActive != isRefundEnabled,
"Same status as the current one"
);
// Update project
projects[projectId].refundActive = isRefundEnabled;
emit ToggleRefund(projectId, isRefundEnabled);
}
/**
* @dev Add a raise to a project.
* @param projectId uint256 Project ID.
* @param amount uint256 Amount in USD to add.
*/
function _addRaiseToProject(
uint256 projectId,
uint256 amount
) internal onlyIfProjectExists(projectId) whenNotPaused {
require(amount > 0, "Invalid amount");
projects[projectId].totalRaised += amount;
emit ProjectAddRaise(projectId, amount);
}
/**
* @dev Set a raise to a project.
* @param projectId uint256 Project ID.
* @param amount uint256 Amount to set.
*/
function _setRaiseToProject(
uint256 projectId,
uint256 amount
) internal onlyIfProjectExists(projectId) whenNotPaused onlyOwner {
require(amount > 0, "Invalid amount");
projects[projectId].totalRaised = amount;
emit ProjectTotalRaised(projectId, amount);
}
/**
* @dev Set a project withdraw address.
* @param projectId uint256 Project ID.
* @param newWithdrawAddress address Withdraw address.
*/
function _setWithdrawAddress(
uint256 projectId,
address newWithdrawAddress
) internal onlyIfProjectExists(projectId) whenNotPaused {
require(newWithdrawAddress != address(0), "Invalid address");
// Update project
if (
projects[projectId].addresses.withdrawAddress != newWithdrawAddress
) {
projects[projectId].addresses.withdrawAddress = newWithdrawAddress;
emit ProjectWithdrawAddressUpdated(projectId, newWithdrawAddress);
}
}
/**
* @dev Set a project reward address.
* @param projectId uint256 Project ID.
* @param newRewardAddress address Reward address.
*/
function _setRewardAddress(
uint256 projectId,
address newRewardAddress
) internal onlyIfProjectExists(projectId) whenNotPaused {
require(newRewardAddress != address(0), "Invalid address");
// Update project
if (projects[projectId].addresses.rewardAddress != newRewardAddress) {
projects[projectId].addresses.rewardAddress = newRewardAddress;
emit ProjectRewardAddressUpdated(projectId, newRewardAddress);
}
}
/**
* @dev Set new payment methods for a project.
* @param projectId uint256 Project ID.
* @param newPaymentMethods string[] New payment methods.
*/
function _setPaymentMethods(
uint256 projectId,
string[] memory newPaymentMethods
) internal onlyIfProjectExists(projectId) whenNotPaused {
// Update project
if (
!ELVX_Tools._compareStringArray(
projects[projectId].paymentMethods,
newPaymentMethods
)
) {
projects[projectId].paymentMethods = newPaymentMethods;
emit ProjectPaymentMethodsUpdated(projectId, newPaymentMethods);
}
}
// ------------------------------------------
// | PROJECT GETTERS FUNCTIONS |
// ------------------------------------------
/**
* @dev Check if a project exists.
* @param projectId uint256 Project ID.
*/
function existsProject(uint256 projectId) public view returns (bool) {
return projects[projectId].supply.maxSupply > 0;
}
/**
* @dev Check if a project is active.
* @param projectId uint256 Project ID.
*/
function isActiveProject(uint256 projectId) public view returns (bool) {
return
projects[projectId].supply.minted <
projects[projectId].supply.maxSupply;
}
/**
* @dev Get if a token is accepted for a project.
* @param projectId uint256 Project ID.
* @param token string Token symbol.
*/
function isPaymentEnabled(
uint256 projectId,
string memory token
) public view onlyIfProjectExists(projectId) returns (bool) {
return
ELVX_Tools.checkPaymentMethod(
projects[projectId].paymentMethods,
token
);
}
/**
* @dev Get if burn is active for a project.
* @param projectId uint256 Project ID.
*/
function isProjectBurnActive(
uint256 projectId
) public view onlyIfProjectExists(projectId) returns (bool) {
return projects[projectId].burnActive;
}
/**
* @dev Get the project legal contract.
* @param projectId uint256 Project ID.
*/
function getLegalContract(
uint256 projectId
) public view onlyIfProjectExists(projectId) returns (string memory) {
return
string.concat(
LEGAL_CONTRACT_BASE,
projects[projectId].legalContract
);
}
/**
* @dev Set the legal contract base.
* @param _legalContractBase string Legal contract base.
*/
function setLegalContractBase(
string memory _legalContractBase
) external onlyOwner {
LEGAL_CONTRACT_BASE = _legalContractBase;
}
/**
* @dev Get the project type.
* @param projectId uint256 Project ID.
*/
function getProjectType(
uint256 projectId
) public view onlyIfProjectExists(projectId) returns (uint8) {
return uint8(projects[projectId].projectType);
}
/**
* @dev Get the project minted supply.
* @param projectId uint256 Project ID.
*/
function getProjectMintedSupply(
uint256 projectId
) public view onlyIfProjectExists(projectId) returns (uint256) {
return projects[projectId].supply.minted;
}
/**
* @dev Get the project max supply.
* @param projectId uint256 Project ID.
*/
function getProjectMaxSupply(
uint256 projectId
) public view onlyIfProjectExists(projectId) returns (uint256) {
return projects[projectId].supply.maxSupply;
}
/**
* @dev Get the project withdraw address.
* @param projectId uint256 Project ID.
*/
function getWithdrawAddress(
uint256 projectId
) public view onlyIfProjectExists(projectId) returns (address) {
return projects[projectId].addresses.withdrawAddress;
}
/**
* @dev Get the project price per token.
* @param projectId uint256 Project ID.
*/
function getProjectPrice(
uint256 projectId
) public view onlyIfProjectExists(projectId) returns (uint256) {
return projects[projectId].price;
}
/**
* @dev Get the project creator.
* @param projectId uint256 Project ID.
*/
function getProjectCreator(
uint256 projectId
) public view onlyIfProjectExists(projectId) returns (address) {
return projects[projectId].addresses.creator;
}
/**
* @dev Get the project funding threshold.
* @param projectId uint256 Project ID.
*/
function getProjectFundingThreshold(
uint256 projectId
) public view onlyIfProjectExists(projectId) returns (uint8) {
return projects[projectId].fundingThreshold;
}
/**
* @dev Get the project total to raise.
* @param projectId uint256 Project ID.
*/
function getProjectTotalToRaise(
uint256 projectId
) public view returns (uint256) {
return getProjectMaxSupply(projectId) * getProjectPrice(projectId);
}
/**
* @dev Get the project reward contract address.
* @param projectId uint256 Project ID.
*/
function getProjectRewardAddress(
uint256 projectId
) public view onlyIfProjectExists(projectId) returns (address) {
return projects[projectId].addresses.rewardAddress;
}
/**
* @dev Get the full project.
* @param projectId uint256 Project ID.
*/
function getFullProject(
uint256 projectId
) public view onlyIfProjectExists(projectId) returns (Project memory) {
return projects[projectId];
}
/**
* @dev Get if a project is refundable.
* @param projectId uint256 Project ID.
*/
function isRefundActive(
uint256 projectId
) public view onlyIfProjectExists(projectId) returns (bool) {
return
projects[projectId].refundActive && projects[projectId].burnActive;
}
/**
* @dev Get the project total raised.
* @param projectId uint256 Project ID.
*/
function getProjectTotalRaised(
uint256 projectId
) external view onlyIfProjectExists(projectId) returns (uint256) {
return projects[projectId].totalRaised;
}
// ------------------------------------------
// | PROJECT FUNCTIONS |
// ------------------------------------------
/**
* @dev Create a new project.
* @param projectId uint256 Project ID.
* @param projectType uint8 Project type. PURCHASE_PROJECT = 0, PLEDGE_PROJECT = 1.
* @param totalSupply uint256 Total supply of the project.
* @param projectPrice uint256 Project price in USD.
* @param withdrawAddress address Withdraw address.
* @param paymentMethods string[] Payment methods.
* @param fundingThreshold uint8 Funding threshold.
* @param nonce uint256 Nonce.
* @param _signature bytes Signature.
*/
function createProject(
uint256 projectId,
ProjectType projectType,
uint256 totalSupply,
uint256 projectPrice,
address withdrawAddress,
string[] memory paymentMethods,
uint8 fundingThreshold,
string memory legalContract,
uint256 nonce,
bytes memory _signature
) external onlyIfProjectNotExists(projectId) whenNotPaused {
// Validate inputs
_validateProjectInputs(
projectType,
totalSupply,
projectPrice,
withdrawAddress,
fundingThreshold,
paymentMethods
);
// Validate signature
require(
_validateCreateProjectSignature(
projectId,
projectType,
totalSupply,
projectPrice,
withdrawAddress,
paymentMethods,
fundingThreshold,
legalContract,
nonce,
_signature
),
"Signature validation failed"
);
// Create project
_createProjectInternal(
projectId,
projectType,
totalSupply,
projectPrice,
withdrawAddress,
paymentMethods,
fundingThreshold,
legalContract
);
}
/**
* @dev Update a project.
* @param projectId uint256 Project ID.
* @param newWithdrawAddr address Withdraw address.
* @param paymentMethods string[] Payment methods.
* @param nonce uint256 Nonce.
* @param _signature bytes Signature.
*/
function updateProject(
uint256 projectId,
address newWithdrawAddr,
string[] memory paymentMethods,
uint256 nonce,
bytes memory _signature
) external onlyCreatorOrOwner(projectId) whenNotPaused {
// Validate signature
require(
_validateUpdateProjectSignature(
projectId,
newWithdrawAddr,
paymentMethods,
nonce,
_signature
),
"Signature validation failed"
);
// Validate inputs
_validateUpdateProjectInputs(newWithdrawAddr, paymentMethods);
// Update project
_setWithdrawAddress(projectId, newWithdrawAddr);
_setPaymentMethods(projectId, paymentMethods);
}
// ------------------------------------------
// | OWNER FUNCTIONS |
// ------------------------------------------
/**
* @dev Set a new message generator address.
* @param msgGeneratorAddress address Message generator address.
*/
function setMsgGenerator(address msgGeneratorAddress) external onlyOwner {
_setNewMessageGenerator(msgGeneratorAddress);
}
/**
* @dev Set a new reward contract address.
* @param newRewardContract address Reward contract address.
*/
function setRewardContract(address newRewardContract) external onlyOwner {
_setNewRewardContractAddress(newRewardContract);
}
/**
* @dev Update a project creator.
* @param projectId uint256 Project ID.
* @param newCreator address Creator address.
*/
function setProjectCreator(
uint256 projectId,
address newCreator
) external onlyOwner onlyIfProjectExists(projectId) {
require(newCreator != address(0), "Invalid creator");
require(
newCreator != projects[projectId].addresses.creator,
"Same creator as the current one"
);
// Update project
projects[projectId].addresses.creator = newCreator;
}
/**
* @dev Update a project reward contract address.
* @param projectId uint256 Project ID.
* @param rewardContractAddress address Reward contract address.
*/
function setProjectRewardContract(
uint256 projectId,
address rewardContractAddress
) external onlyOwner onlyIfProjectExists(projectId) {
require(rewardContractAddress != address(0), "Invalid address");
// Update project
projects[projectId].addresses.rewardAddress = rewardContractAddress;
}
// ------------------------------------------
// | SIGNER FUNCTIONS |
// ------------------------------------------
/**
* @dev Update the signer address.
* @param _signAddress address Signer address.
*/
function setSigner(address _signAddress) external onlyOwner {
_setSignAddress(_signAddress);
}
}/*
_ _ _ ____ _ _
| | | | _ __ | | __ _ __ ___ __ __ _ __ / ___| _ __ __ _ __ __ (_) | |_ _ _
| | | | | '_ \ | |/ / | '_ \ / _ \ \ \ /\ / / | '_ \ | | _ | '__| / _` | \ \ / / | | | __| | | | |
| |_| | | | | | | < | | | | | (_) | \ V V / | | | | | |_| | | | | (_| | \ V / | | | |_ | |_| |
\___/ |_| |_| |_|\_\ |_| |_| \___/ \_/\_/ |_| |_| \____| |_| \__,_| \_/ |_| \__| \__, |
|___/
WWW.UNKNOWNGRAVITY.COM
*/
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.27;
// --------------------------------------------------
// | IMPORTS |
// --------------------------------------------------
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
/**
* @title ELVX_Signer
* @dev Contract to handle the signature verification.
* @notice This contract is used to verify the signature of a message.
*/
abstract contract ELVX_Signer {
address public signAddress;
mapping(uint256 => bool) public usedNonces;
// --------------------------------------------------
// | PRIVATE FUNCTIONS |
// --------------------------------------------------
/**
* @dev Internal function to verify the signature.
* @param _message Message to sign
* @param _signature Signature
* @return isValid True if the signature is valid, false otherwise
*/
function _verifySig(
bytes32 _message,
bytes memory _signature
) private view returns (bool) {
// Recover the signer address
address signer = ECDSA.recover(_message, _signature);
// Check if the signer is the sign address
return signAddress == signer;
}
// --------------------------------------------------
// | INTERNAL FUNCTIONS |
// --------------------------------------------------
/**
* @dev Internal function to set the sign address.
* @param _signAddress New sign address
*/
function _setSignAddress(address _signAddress) internal {
require(
_signAddress != address(0),
"Sign address can't be zero address"
);
signAddress = _signAddress;
}
/**
* @dev Checks if the provided signature is valid for the given message.
Increments the nonce if the signature is valid.
* @param _message Message to sign
* @param _signature Signature
* @return isValid True if the signature is valid, false otherwise
*/
function _isSigValid(
bytes32 _message,
uint256 nonce,
bytes memory _signature
) internal returns (bool) {
// Check if the nonce has been used
require(!usedNonces[nonce], "Nonce already used");
// Verify the signature
bool isValid = _verifySig(_message, _signature);
if (isValid) usedNonces[nonce] = true;
return isValid;
}
// --------------------------------------------------
// | DEBUG FUNCTIONS |
// --------------------------------------------------
/**
* @dev Internal function to toggle the nonce.
* @param nonce Nonce to toggle
*/
function _toggleNonce(uint256 nonce) internal {
usedNonces[nonce] = !usedNonces[nonce];
}
}/*
_ _ _ ____ _ _
| | | | _ __ | | __ _ __ ___ __ __ _ __ / ___| _ __ __ _ __ __ (_) | |_ _ _
| | | | | '_ \ | |/ / | '_ \ / _ \ \ \ /\ / / | '_ \ | | _ | '__| / _` | \ \ / / | | | __| | | | |
| |_| | | | | | | < | | | | | (_) | \ V V / | | | | | |_| | | | | (_| | \ V / | | | |_ | |_| |
\___/ |_| |_| |_|\_\ |_| |_| \___/ \_/\_/ |_| |_| \____| |_| \__,_| \_/ |_| \__| \__, |
|___/
WWW.UNKNOWNGRAVITY.COM
*/
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.27;
import "@openzeppelin/contracts/utils/Strings.sol";
/**
* @title ELVX_Tools
* @dev Contract to handle the tools and utilities.
* @notice This contract is used to handle the tools and utilities.
*/
library ELVX_Tools {
using Strings for uint256;
/**
* @dev Check if a value exists in an array.
* @param arr uint256[] Array to check.
* @param value uint256 Value to check.
*/
function _existsInArray(
uint256[] storage arr,
uint256 value
) internal view returns (bool) {
uint256 len = arr.length;
for (uint256 i = 0; i < len; i++) {
if (arr[i] == value) {
return true;
}
}
return false;
}
/**
* @dev Compare two string arrays.
* @param a string[] First array.
* @param b string[] Second array.
*/
function _compareStringArray(
string[] memory a,
string[] memory b
) internal pure returns (bool) {
if (a.length != b.length) return false;
for (uint i = 0; i < a.length; i++) {
if (keccak256(bytes(a[i])) != keccak256(bytes(b[i]))) {
return false;
}
}
return true;
}
/**
* @dev Check if a token is in the payment methods.
* @param paymentMethods string[] Payment methods.
* @param token string Token to check.
*/
function checkPaymentMethod(
string[] memory paymentMethods,
string memory token
) internal pure returns (bool) {
for (uint i = 0; i < paymentMethods.length; i++) {
if (
keccak256(bytes(paymentMethods[i])) == keccak256(bytes(token))
) {
return true;
}
}
return false;
}
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
* @param value Unsigned integer to convert to string
* @return str Decimal representation of the unsigned integer as a string
*/
function _uintToString(
uint256 value
) internal pure returns (string memory) {
return Strings.toString(value); // If you're using OpenZeppelin's Strings library
}
/**
* @dev Converts an `address` to its ASCII `string` hexadecimal representation.
* @param addr Address to convert to string
* @return str Hexadecimal representation of the address as a string
*/
function _addressToString(
address addr
) internal pure returns (string memory) {
return Strings.toHexString(uint256(uint160(addr)), 20); // Using OpenZeppelin's library
}
/**
* @dev Concatenates an array of strings into a single string.
* @param arr Array of strings to concatenate
* @return result Concatenated string
*/
function _concatenateArray(
string[] memory arr
) internal pure returns (string memory) {
string memory result = "";
for (uint256 i = 0; i < arr.length; i++) {
result = string(
abi.encodePacked(
result,
arr[i],
(i == arr.length - 1 ? "" : ", ")
)
);
}
return result;
}
/**
* @dev Concatenates the project information.
* @param projectId ID of the project
* @param withdrawAddress Address to withdraw funds
* @param paymentMethods Payment methods accepted
* @param nonce Nonce of the project
*/
function _concatenateUpdateProjectInfo(
uint256 projectId,
address withdrawAddress,
string[] memory paymentMethods,
uint256 nonce
) internal pure returns (string memory) {
string memory paymentMethodsStr = _concatenateArray(paymentMethods);
return
string.concat(
"Project ID: ",
_uintToString(projectId),
", Withdraw Address: ",
_addressToString(withdrawAddress),
", Payment Methods: ",
paymentMethodsStr,
", Nonce: ",
_uintToString(nonce)
);
}
/**
* @dev Concatenates the project information.
* @param projectId ID of the project
* @param totalSupply Total supply of the project
* @param projectPrice Price of the project
* @param withdrawAddress Address to withdraw funds
* @param paymentMethods Payment methods accepted
* @param fundingThreshold Minimum amount of funds to be raised
* @param nonce Nonce of the project
*/
function _concatenateProjectInfo(
uint256 projectId,
uint256 totalSupply,
uint256 projectPrice,
address withdrawAddress,
string[] memory paymentMethods,
uint8 fundingThreshold,
uint256 nonce
) internal pure returns (string memory) {
string memory paymentMethodsStr = _concatenateArray(paymentMethods);
return
string.concat(
"Project ID: ",
_uintToString(projectId),
", Total Supply: ",
_uintToString(totalSupply),
", Price: ",
_uintToString(projectPrice),
", Withdraw Address: ",
_addressToString(withdrawAddress),
", Payment Methods: ",
paymentMethodsStr,
", Threshold: ",
_uintToString(fundingThreshold),
", Nonce: ",
_uintToString(nonce)
);
}
}/*
_ _ _ ____ _ _
| | | | _ __ | | __ _ __ ___ __ __ _ __ / ___| _ __ __ _ __ __ (_) | |_ _ _
| | | | | '_ \ | |/ / | '_ \ / _ \ \ \ /\ / / | '_ \ | | _ | '__| / _` | \ \ / / | | | __| | | | |
| |_| | | | | | | < | | | | | (_) | \ V V / | | | | | |_| | | | | (_| | \ V / | | | |_ | |_| |
\___/ |_| |_| |_|\_\ |_| |_| \___/ \_/\_/ |_| |_| \____| |_| \__,_| \_/ |_| \__| \__, |
|___/
WWW.UNKNOWNGRAVITY.COM
*/
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.27;
// --------------------------------------------------
// | IMPORTS |
// --------------------------------------------------
// Oracle
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
// --------------------------------------------------
// | EleveX - REAL ESTATE ORACLE CONTRACT V.1.0 |
// --------------------------------------------------
/**
* @title ELVX_Oracles
* @dev Handles data feeds for the ELVX contract.
* @notice This contract is used to handle data feeds for the ELVX contract.
*/
contract ELVX_Oracles is Ownable {
// ------------------------------------------
// | CONSTANTS & GLOBAL VARIABLES |
// ------------------------------------------
// Data feed mapping
mapping(string => AggregatorV3Interface) private dataFeeds;
// Array of data feed names
string[] private dataFeedNames;
// ------------------------------------------
// | EVENTS |
// ------------------------------------------
event DataFeedAdded(string indexed name, address indexed dataFeedAddress);
event DataFeedRemoved(string indexed name);
event DataFeedUpdated(
string indexed name,
address indexed newDataFeedAddress
);
// ------------------------------------------
// | CONSTRUCTOR |
// ------------------------------------------
/**
* @dev Constructor
* @param usdcDataFeed USDC data feed address
* @param usdtDataFeed USDT data feed address
* @param ethDataFeed ETH data feed address
*/
constructor(
address usdcDataFeed,
address usdtDataFeed,
address ethDataFeed
) Ownable(_msgSender()) {
_addOrUpdateDataFeed("USDC", usdcDataFeed);
_addOrUpdateDataFeed("USDT", usdtDataFeed);
_addOrUpdateDataFeed("ETH", ethDataFeed);
}
// -------------------------------------------
// | INTERNAL FUNCTIONS |
// -------------------------------------------
/**
* @dev Check if a data feed exists
* @param name Data feed name
* @return bool True if data feed exists, false otherwise
*/
function _dataFeedExists(string memory name) internal view returns (bool) {
return dataFeeds[name] != AggregatorV3Interface(address(0));
}
/**
* @dev Add or update a data feed
* @param name Data feed name
* @param dataFeed Data feed address
*/
function _addOrUpdateDataFeed(
string memory name,
address dataFeed
) internal {
require(dataFeed != address(0), "Invalid data feed address");
require(bytes(name).length > 0, "Data feed name cannot be empty");
if (_dataFeedExists(name)) {
emit DataFeedUpdated(name, dataFeed);
} else {
dataFeedNames.push(name);
emit DataFeedAdded(name, dataFeed);
}
dataFeeds[name] = AggregatorV3Interface(dataFeed);
}
/**
* @dev Remove a data feed
* @param name Data feed name
*/
function _removeDataFeed(string memory name) internal {
require(_dataFeedExists(name), "Data feed does not exist");
delete dataFeeds[name];
bytes32 nameBytes = keccak256(bytes(name));
for (uint256 i = 0; i < dataFeedNames.length; i++) {
if (keccak256(bytes(dataFeedNames[i])) == nameBytes) {
dataFeedNames[i] = dataFeedNames[dataFeedNames.length - 1];
dataFeedNames.pop();
break;
}
}
emit DataFeedRemoved(name);
}
/**
* @dev Get data feed interface
* @param name Data feed name
* @return AggregatorV3Interface Data feed interface
*/
function _getDataFeed(
string memory name
) internal view returns (AggregatorV3Interface) {
require(_dataFeedExists(name), "Data feed does not exist");
return dataFeeds[name];
}
/**
* @dev Get the latest price from a data feed
* @param dataFeed Data feed interface
* @return int Latest price
*/
function _getLatestPrice(
AggregatorV3Interface dataFeed
) internal view returns (int) {
(, int price, , , ) = dataFeed.latestRoundData();
return price;
}
// ------------------------------------------
// | SETTER FUNCTIONS |
// ------------------------------------------
/**
* @dev Add a new data feed
* @param name Data feed name
* @param dataFeedAddress Data feed address
*/
function addDataFeed(
string memory name,
address dataFeedAddress
) external onlyOwner {
require(!_dataFeedExists(name), "Data feed already exists");
_addOrUpdateDataFeed(name, dataFeedAddress);
}
/**
* @dev Update an existing data feed
* @param name Data feed name
* @param newDataFeedAddress New data feed address
*/
function updateDataFeed(
string memory name,
address newDataFeedAddress
) external onlyOwner {
require(_dataFeedExists(name), "Data feed does not exist");
_addOrUpdateDataFeed(name, newDataFeedAddress);
}
/**
* @dev Remove an existing data feed
* @param name Data feed name
*/
function removeDataFeed(string memory name) external onlyOwner {
_removeDataFeed(name);
}
/**
* @dev Get all data feed names
* @return string[] Array of data feed names
*/
function getDataFeedNames() external view returns (string[] memory) {
return dataFeedNames;
}
/**
* @dev Check if a data feed is supported
* @param name Data feed name
*/
function isSupportedDataFeed(
string memory name
) public view returns (bool) {
return _dataFeedExists(name);
}
// --------------------------------------------
// | CALCULATE FUNCTIONS |
// --------------------------------------------
/**
* @dev Get the latest price of a token
* @param tokenName Token name
* @return int Latest token price
*/
function getLatestPrice(string memory tokenName) public view returns (int) {
require(_dataFeedExists(tokenName), "Data feed does not exist");
AggregatorV3Interface dataFeed = _getDataFeed(tokenName);
return _getLatestPrice(dataFeed);
}
/**
* @dev Calculate the equivalent amount of tokens for a given USD amount
* @param tokenName Token name
* @param tokenDecimals Number of token decimals
* @param usdAmount Amount in USD without decimals
* @return uint256 Equivalent token amount (with proper decimals)
*/
function calculateUsdAmount(
string memory tokenName,
uint8 tokenDecimals,
uint256 usdAmount
) public view returns (uint256) {
require(usdAmount > 0, "Invalid USD amount");
// Ensure the data feed exists for the token
require(_dataFeedExists(tokenName), "Data feed does not exist");
AggregatorV3Interface dataFeed = _getDataFeed(tokenName);
// Get the latest token price (with 8 decimals)
int tokenPrice = _getLatestPrice(dataFeed);
require(tokenPrice > 0, "Invalid token price");
// Convert price to uint256
uint256 tokenPriceUint = uint256(tokenPrice);
// Get the data feed decimals
uint256 dataFeedDecimals = dataFeed.decimals();
uint256 adjustedUsdAmount = usdAmount *
(10 ** (tokenDecimals + dataFeedDecimals));
// Calcular la cantidad de tokens: (USD ajustado * 10**tokenDecimals) / precio del token
uint256 tokenAmount = adjustedUsdAmount / tokenPriceUint;
// Adjust token amount to the correct number of decimals
return tokenAmount;
}
}/*
_ _ _ ____ _ _
| | | | _ __ | | __ _ __ ___ __ __ _ __ / ___| _ __ __ _ __ __ (_) | |_ _ _
| | | | | '_ \ | |/ / | '_ \ / _ \ \ \ /\ / / | '_ \ | | _ | '__| / _` | \ \ / / | | | __| | | | |
| |_| | | | | | | < | | | | | (_) | \ V V / | | | | | |_| | | | | (_| | \ V / | | | |_ | |_| |
\___/ |_| |_| |_|\_\ |_| |_| \___/ \_/\_/ |_| |_| \____| |_| \__,_| \_/ |_| \__| \__, |
|___/
WWW.UNKNOWNGRAVITY.COM
*/
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.27;
// --------------------------------------------------
// | IMPORTS |
// --------------------------------------------------
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/extensions/AccessControlEnumerable.sol";
//Security
import "@openzeppelin/contracts/access/IAccessControl.sol";
// Token contract
import "./ELVX_REToken.sol";
// Payments
import "./ELVX_Treasury.sol";
// -----------------------------------------------------
// | EleveX - REAL ESTATE REWARDS CONTRACT V.1.0 |
// -----------------------------------------------------
/**
* @title ELVX_REClaim
* @dev Handles the rewards claim for the ELVX contract.
* @notice This contract is used to handle the rewards claim for the ELVX contract.
*/
contract ELVX_REClaim is
Ownable,
Pausable,
ReentrancyGuard,
AccessControlEnumerable
{
// ------------------------------------------
// | CONSTANTS & GLOBAL VARIABLES |
// ------------------------------------------
// Roles
bytes32 public constant TOKEN_CONTRACT_ROLE =
keccak256("TOKEN_CONTRACT_ROLE");
// Contracts
ELVX_REToken public immutable ELVX_RETOKEN_CONTRACT;
ELVX_Treasury public immutable ELVX_TREASURY_CONTRACT;
// Mapping of available claim balances
mapping(uint256 => ProjectReward) public rewards; // projectId => ProjectReward
// Mapping of claimed amounts by users
mapping(address => mapping(uint256 => uint256)) private claimedAmounts; // user => projectId => amount
// ------------------------------------------
// | STRUCTS |
// ------------------------------------------
struct ProjectReward {
uint256 totalReward;
uint256 availableReward;
bool finalPayment;
}
// ------------------------------------------
// | MODIFIERS |
// ------------------------------------------
modifier onlyTokenContract() {
require(
hasRole(TOKEN_CONTRACT_ROLE, _msgSender()),
"Caller is not the token contract"
);
_;
}
modifier onlyCreatorOrOwner(uint256 projectId) {
require(
ELVX_RETOKEN_CONTRACT.getProjectCreator(projectId) ==
_msgSender() ||
owner() == _msgSender(),
"Caller is not the creator or owner"
);
_;
}
// ------------------------------------------
// | EVENTS |
// ------------------------------------------
/**
* @dev Claimed funds event
* @param claimer address Claimer address
* @param projectId uint256 Project ID
* @param amount uint256 Amount claimed
*/
event Claimed(
address indexed claimer,
uint256 indexed projectId,
uint256 amount
);
/**
* @dev Funds added event
* @param projectId uint256 Project ID
* @param amount uint256 Amount added
* @param finalPayment bool Final payment flag (true if it is the final payment)
*/
event FundsAdded(
uint256 indexed projectId,
uint256 amount,
bool finalPayment
);
// ------------------------------------------
// | CONSTRUCTOR |
// ------------------------------------------
/**
* @dev Constructor
* @param tokenAddress address Sales recipient
* @param treasuryAddress address Treasury address
*/
constructor(
address tokenAddress,
address payable treasuryAddress
) Ownable(_msgSender()) {
// Configure token contract
ELVX_RETOKEN_CONTRACT = ELVX_REToken(tokenAddress);
_grantRole(TOKEN_CONTRACT_ROLE, tokenAddress);
// Configure treasury contract
ELVX_TREASURY_CONTRACT = ELVX_Treasury(treasuryAddress);
}
// ------------------------------------------
// | UTILS FUNCTIONS |
// ------------------------------------------
/**
* @dev Check if a project exists
* @param projectId uint256 Project ID
* @return bool True if project exists, false otherwise
*/
function existProject(uint256 projectId) private view returns (bool) {
return ELVX_RETOKEN_CONTRACT.existsProject(projectId);
}
// ------------------------------------------
// | REWARDS FUNCTIONS |
// ------------------------------------------
/**
* @dev Add funds to a project
* @param projectId uint256 Project ID
* @param amount uint256 Amount in USDC
* @param isFinalPayment bool Is final payment
*/
function addFunds(
uint256 projectId,
uint256 amount,
bool isFinalPayment
) external onlyCreatorOrOwner(projectId) whenNotPaused nonReentrant {
require(existProject(projectId), "Project does not exist");
require(!rewards[projectId].finalPayment, "Final payment already done");
require(
ELVX_RETOKEN_CONTRACT.getProjectMintedSupply(projectId) ==
ELVX_RETOKEN_CONTRACT.getProjectMaxSupply(projectId),
"There are tokens not minted yet"
);
// Emit event
emit FundsAdded(projectId, amount, isFinalPayment);
// Add funds to the project with USDC
ELVX_TREASURY_CONTRACT.addToken(_msgSender(), "USDC", amount);
// Update the claim balance of the project
rewards[projectId].totalReward += amount;
rewards[projectId].availableReward += amount;
if (isFinalPayment) {
ELVX_RETOKEN_CONTRACT.toggleBurn(projectId, true);
rewards[projectId].finalPayment = true;
}
}
/**
* @dev Claim funds from a project
* @param projectId uint256 Project ID
*/
function claim(uint256 projectId) external nonReentrant whenNotPaused {
require(existProject(projectId), "Project does not exist");
address sender = _msgSender();
// Project balance
uint256 tokenBalance = ELVX_RETOKEN_CONTRACT.balanceOf(
sender,
projectId
);
require(tokenBalance > 0, "No tokens to claim");
// Max reward available
uint256 totalSupply = ELVX_RETOKEN_CONTRACT.getProjectMaxSupply(
projectId
);
uint256 userClaimableAmount = (rewards[projectId].totalReward *
tokenBalance) / totalSupply;
require(userClaimableAmount > 0, "No funds available to claim");
// Calculate the remaining amount to be claimed
uint256 userRemainingClaimAmount = userClaimableAmount -
claimedAmounts[sender][projectId];
require(userRemainingClaimAmount > 0, "No funds remaining to claim");
// Need burn the tokens if it is the final payment
if (rewards[projectId].finalPayment) {
ELVX_RETOKEN_CONTRACT.burn(sender, projectId, tokenBalance);
}
// Event
emit Claimed(sender, projectId, userRemainingClaimAmount);
// Update the claimed amount
claimedAmounts[sender][projectId] += userRemainingClaimAmount;
// Update the available reward
rewards[projectId].availableReward -= userRemainingClaimAmount;
// Transfer reward to the user
ELVX_TREASURY_CONTRACT.withdrawToken(
sender,
"USDC",
userRemainingClaimAmount
);
}
// ------------------------------------------
// | VIEW FUNCTIONS |
// ------------------------------------------
/**
* @dev Get all remaining amount to be claimed
* @param user address User address
*/
function getAllRemainingToBeClaimed(
address user
) external view returns (uint256) {
(uint256[] memory projectIds, ) = ELVX_RETOKEN_CONTRACT.getUserProjects(
user
);
uint256 totalToBeClaimed = 0;
for (uint256 i = 0; i < projectIds.length; i++) {
totalToBeClaimed += getRemainingToBeClaimed(projectIds[i], user);
}
return totalToBeClaimed;
}
/**
* @dev Get remaining amount to be claimed
* @param projectId uint256 Project ID
* @param user address User address
*/
function getRemainingToBeClaimed(
uint256 projectId,
address user
) public view returns (uint256) {
uint256 tokenBalance = ELVX_RETOKEN_CONTRACT.balanceOf(user, projectId);
// Max reward available
uint256 totalSupply = ELVX_RETOKEN_CONTRACT.getProjectMaxSupply(
projectId
);
uint256 userClaimableAmount = (rewards[projectId].totalReward *
tokenBalance) / totalSupply;
return userClaimableAmount - claimedAmounts[user][projectId];
}
/**
* @dev Get claimed amount of a project
* @param projectId uint256 Project ID
* @param user address User address
*/
function getClaimedAmount(
uint256 projectId,
address user
) public view returns (uint256) {
return claimedAmounts[user][projectId];
}
/**
* @dev Get unclaimed (available) rewards of a project
* @param projectId uint256 Project ID
*/
function getUnclaimedRewards(
uint256 projectId
) external view returns (uint256) {
return rewards[projectId].availableReward;
}
// ------------------------------------------
// | PAUSE FUNCTIONS |
// ------------------------------------------
/**
* @dev Pause the contract
*/
function pause() public onlyOwner {
_pause();
}
/**
* @dev Unpause the contract
*/
function unpause() public onlyOwner {
_unpause();
}
// ------------------------------------------
// | TRANSFER FUNCTIONS |
// ------------------------------------------
/**
* @dev Transfer claimed funds to another address (when the user has sold the tokens)
* @param from address Address to transfer the funds
* @param to address Address to transfer the funds
* @param projectId uint256 Project ID
* @param tokenAmount uint256 Amount of tokens to transfer
*/
function transferClaimed(
address from,
address to,
uint256 projectId,
uint256 tokenAmount
) external onlyTokenContract {
require(existProject(projectId), "Project does not exist");
uint256 claimedAmount = getClaimedAmount(projectId, from);
if (claimedAmount > 0) {
// Calcular la proporción de la recompensa que se debe transferir
uint256 totalToTransfer = (claimedAmount * tokenAmount) /
ELVX_RETOKEN_CONTRACT.balanceOf(from, projectId);
claimedAmounts[from][projectId] -= totalToTransfer;
claimedAmounts[to][projectId] += totalToTransfer;
}
}
// ---------------------------------------
// | ROLE FUNCTIONS |
// ---------------------------------------
/**
* @dev Override grantRole to check if is called by owner.
* Only owner can grant roles.
* @param role bytes32 Role.
* @param account address Account.
*/
function grantRole(
bytes32 role,
address account
) public virtual override(AccessControl, IAccessControl) onlyOwner {
super._grantRole(role, account);
}
/**
* @dev Override revokeRole to check if is called by owner.
* Only owner can revoke roles.
* @param role bytes32 Role.
* @param account address Account.
*/
function revokeRole(
bytes32 role,
address account
) public virtual override(AccessControl, IAccessControl) onlyOwner {
super._revokeRole(role, account);
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.27;
// Payments
import "./auxs/ELVX_Payments.sol";
/**
* @title ELVX_Treasury
* @dev Handles the treasury for the ELVX contract.
* @notice This contract is used to handle the treasury for the ELVX contract.
*/
contract ELVX_Treasury is Ownable, ELVX_Payments {
constructor(
address usdcAddress,
address usdtAddress,
address oracleAddress
)
Ownable(_msgSender())
ELVX_Payments(usdcAddress, usdtAddress, oracleAddress)
{}
}{
"optimizer": {
"enabled": true,
"runs": 100
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract ABI
API[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"string","name":"_baseUri","type":"string"},{"internalType":"string","name":"_collectionUri","type":"string"},{"internalType":"address payable","name":"_royaltyAddress","type":"address"},{"internalType":"uint96","name":"_royaltyPoints","type":"uint96"},{"internalType":"address","name":"_msgGeneratorAddress","type":"address"},{"internalType":"address","name":"_oracleAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ECDSAInvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ECDSAInvalidSignatureLength","type":"error"},{"inputs":[{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"ECDSAInvalidSignatureS","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC1155InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC1155InvalidApprover","type":"error"},{"inputs":[{"internalType":"uint256","name":"idsLength","type":"uint256"},{"internalType":"uint256","name":"valuesLength","type":"uint256"}],"name":"ERC1155InvalidArrayLength","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"ERC1155InvalidOperator","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC1155InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC1155InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"ERC1155MissingApprovalForAll","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"MetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ProjectAddRaise","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalSupply","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"projectPrice","type":"uint256"},{"indexed":false,"internalType":"address","name":"withdrawAddress","type":"address"},{"indexed":false,"internalType":"address","name":"rewardContractAddress","type":"address"},{"indexed":false,"internalType":"string[]","name":"paymentMethods","type":"string[]"},{"indexed":false,"internalType":"uint8","name":"fundingThreshold","type":"uint8"}],"name":"ProjectCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":false,"internalType":"string[]","name":"paymentMethods","type":"string[]"}],"name":"ProjectPaymentMethodsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"}],"name":"ProjectPriceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":false,"internalType":"address","name":"rewardAddress","type":"address"}],"name":"ProjectRewardAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalSupply","type":"uint256"}],"name":"ProjectSupplyUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"threshold","type":"uint8"}],"name":"ProjectThresholdUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ProjectTotalRaised","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":false,"internalType":"address","name":"withdrawAddress","type":"address"}],"name":"ProjectWithdrawAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newRoyaltiesAddress","type":"address"}],"name":"SetRoyaltiesAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint96","name":"newRoyaltiesBasicPoints","type":"uint96"}],"name":"SetRoyaltiesBasicPoints","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isBurnEnabled","type":"bool"}],"name":"ToggleBurn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isRefundEnabled","type":"bool"}],"name":"ToggleRefund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"TransferSingle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"value","type":"string"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"URI","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MSG_GENERATOR","outputs":[{"internalType":"contract ELVX_Messages","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ORACLES","outputs":[{"internalType":"contract ELVX_Oracles","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REWARD_CONTRACT_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REWARD_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROYALTY_POINTS","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROYALTY_RECIPIENT","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SELLER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"addRaiseToProject","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"balanceOfBatch","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"burnBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"contractURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"enum ELVX_Projects.ProjectType","name":"projectType","type":"uint8"},{"internalType":"uint256","name":"totalSupply","type":"uint256"},{"internalType":"uint256","name":"projectPrice","type":"uint256"},{"internalType":"address","name":"withdrawAddress","type":"address"},{"internalType":"string[]","name":"paymentMethods","type":"string[]"},{"internalType":"uint8","name":"fundingThreshold","type":"uint8"},{"internalType":"string","name":"legalContract","type":"string"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"createProject","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"disableProjectRefund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"enableProjectRefund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"exists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"existsProject","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"getFullProject","outputs":[{"components":[{"internalType":"enum ELVX_Projects.ProjectType","name":"projectType","type":"uint8"},{"internalType":"string[]","name":"paymentMethods","type":"string[]"},{"components":[{"internalType":"uint256","name":"maxSupply","type":"uint256"},{"internalType":"uint256","name":"minted","type":"uint256"}],"internalType":"struct ELVX_Projects.Supply","name":"supply","type":"tuple"},{"components":[{"internalType":"address","name":"withdrawAddress","type":"address"},{"internalType":"address","name":"rewardAddress","type":"address"},{"internalType":"address","name":"creator","type":"address"}],"internalType":"struct ELVX_Projects.Addresses","name":"addresses","type":"tuple"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"bool","name":"burnActive","type":"bool"},{"internalType":"bool","name":"refundActive","type":"bool"},{"internalType":"uint8","name":"fundingThreshold","type":"uint8"},{"internalType":"uint256","name":"totalRaised","type":"uint256"},{"internalType":"string","name":"legalContract","type":"string"}],"internalType":"struct ELVX_Projects.Project","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"getLegalContract","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"getProjectCreator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"getProjectFundingThreshold","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"getProjectMaxSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"getProjectMintedSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"getProjectPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"getProjectRewardAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"getProjectTotalRaised","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"getProjectTotalToRaise","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"getProjectType","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getUserProjects","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"getWithdrawAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"isActiveProject","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"string","name":"token","type":"string"}],"name":"isPaymentEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"isProjectBurnActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"isRefundActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"projects","outputs":[{"internalType":"enum ELVX_Projects.ProjectType","name":"projectType","type":"uint8"},{"components":[{"internalType":"uint256","name":"maxSupply","type":"uint256"},{"internalType":"uint256","name":"minted","type":"uint256"}],"internalType":"struct ELVX_Projects.Supply","name":"supply","type":"tuple"},{"components":[{"internalType":"address","name":"withdrawAddress","type":"address"},{"internalType":"address","name":"rewardAddress","type":"address"},{"internalType":"address","name":"creator","type":"address"}],"internalType":"struct ELVX_Projects.Addresses","name":"addresses","type":"tuple"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"bool","name":"burnActive","type":"bool"},{"internalType":"bool","name":"refundActive","type":"bool"},{"internalType":"uint8","name":"fundingThreshold","type":"uint8"},{"internalType":"uint256","name":"totalRaised","type":"uint256"},{"internalType":"string","name":"legalContract","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"reduceSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_collectionId","type":"uint256"},{"internalType":"uint256","name":"_salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"royaltyAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeBatchTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_newExtension","type":"string"}],"name":"setBaseExtension","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_newUri","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_newUri","type":"string"}],"name":"setContractURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_legalContractBase","type":"string"}],"name":"setLegalContractBase","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"msgGeneratorAddress","type":"address"}],"name":"setMsgGenerator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"}],"name":"setName","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"newCreator","type":"address"}],"name":"setProjectCreator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"rewardContractAddress","type":"address"}],"name":"setProjectRewardContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newRewardContract","type":"address"}],"name":"setRewardContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"rAddress","type":"address"}],"name":"setRoyaltiesAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint96","name":"_royaltyPoints","type":"uint96"}],"name":"setRoyaltiesBasicPoints","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_signAddress","type":"address"}],"name":"setSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_symbol","type":"string"}],"name":"setSymbol","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setTotalRaise","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"signAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"bool","name":"isEnabled","type":"bool"}],"name":"toggleBurn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"newWithdrawAddr","type":"address"},{"internalType":"string[]","name":"paymentMethods","type":"string[]"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"updateProject","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenid","type":"uint256"}],"name":"uri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"usedNonces","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"userBalances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60e060405260386080818152906165ab60a03960069061001f9082610380565b5060408051808201909152600e81526d17b6b2ba30b230ba30973539b7b760911b60208201526014906100529082610380565b5034801561005f57600080fd5b506040516165e33803806165e383398101604081905261007e91610514565b60405180606001604052806028815260200161658360289139828233806100c057604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b6100c981610165565b506000805460ff60a01b19169055600380546001600160a01b039384166001600160a01b0319918216179091556004805492909316911617905561010c816101b5565b506001600f55601061011e8982610380565b50601161012b8882610380565b5060136101388782610380565b5060126101458682610380565b5061014f836101c5565b61015884610242565b5050505050505050610619565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600a6101c18282610380565b5050565b6101f46001600160601b03821611156102205760405162461bcd60e51b815260206004820152601d60248201527f526f796174696573206572726f723a204c696d6974207265616368656400000060448201526064016100b7565b601580546001600160601b0319166001600160601b0392909216919091179055565b6001600160a01b0381166102b05760405162461bcd60e51b815260206004820152602f60248201527f457863657074696f6e20696e20736574526f79616c746965734164647265737360448201526e1d1020b2323932b9b9903d32b9379760891b60648201526084016100b7565b601580546001600160a01b039092166c01000000000000000000000000026001600160601b03909216919091179055565b634e487b7160e01b600052604160045260246000fd5b600181811c9082168061030b57607f821691505b60208210810361032b57634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111561037b57806000526020600020601f840160051c810160208510156103585750805b601f840160051c820191505b818110156103785760008155600101610364565b50505b505050565b81516001600160401b03811115610399576103996102e1565b6103ad816103a784546102f7565b84610331565b6020601f8211600181146103e157600083156103c95750848201515b600019600385901b1c1916600184901b178455610378565b600084815260208120601f198516915b8281101561041157878501518255602094850194600190920191016103f1565b508482101561042f5786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b600082601f83011261044f57600080fd5b81516001600160401b03811115610468576104686102e1565b604051601f8201601f19908116603f011681016001600160401b0381118282101715610496576104966102e1565b6040528181528382016020018510156104ae57600080fd5b60005b828110156104cd576020818601810151838301820152016104b1565b506000918101602001919091529392505050565b80516001600160a01b03811681146104f857600080fd5b919050565b80516001600160601b03811681146104f857600080fd5b600080600080600080600080610100898b03121561053157600080fd5b88516001600160401b0381111561054757600080fd5b6105538b828c0161043e565b60208b015190995090506001600160401b0381111561057157600080fd5b61057d8b828c0161043e565b60408b015190985090506001600160401b0381111561059b57600080fd5b6105a78b828c0161043e565b60608b015190975090506001600160401b038111156105c557600080fd5b6105d18b828c0161043e565b9550506105e060808a016104e1565b93506105ee60a08a016104fd565b92506105fc60c08a016104e1565b915061060a60e08a016104e1565b90509295985092959890939650565b615f5b806106286000396000f3fe608060405234801561001057600080fd5b50600436106104955760003560e01c80638456cb5911610262578063cdfa355311610151578063e4de9045116100ce578063ee15c4e411610092578063ee15c4e414610b0f578063f242432a14610b22578063f2fde38b14610b35578063f5298aca14610b48578063fa3bdc4614610b5b578063ffa15e9814610b6e57600080fd5b8063e4de904514610aa7578063e7da3ee714610aba578063e8884f2b14610acd578063e8a3d48514610af4578063e985e9c514610afc57600080fd5b8063dc95c4a711610115578063dc95c4a714610a3a578063ded238ca14610a4d578063dfed168914610a60578063e09fb94e14610a73578063e32e723c14610a8657600080fd5b8063cdfa3553146109d9578063d276cf26146109ec578063d547741f146109ff578063da3ef23f14610a12578063dbe4613d14610a2557600080fd5b8063a22cb465116101df578063bd85b039116101a3578063bd85b0391461096d578063c47f00271461098d578063c68c1091146109a0578063ca15c873146109b3578063cbf1b2c3146109c657600080fd5b8063a22cb46514610901578063a71fc22c14610914578063acdcb8a114610934578063b84c824614610947578063b996fff31461095a57600080fd5b806391d148541161022657806391d14854146108b8578063938e3d7b146108cb57806395d89b41146108de5780639cd2c92e146108e6578063a217fddf146108f957600080fd5b80638456cb59146108665780638c467cab1461086e5780638da5cb5b146108815780639010d07c146108925780639116ce7f146108a557600080fd5b80633bf7f868116103895780635a36e0da116103065780636c19e783116102ca5780636c19e783146107e75780636d261c3f146107fa578063715018a61461080d5780637aa66dd314610815578063806234441461082857806383ed62921461083b57600080fd5b80635a36e0da146107835780635c975abb14610796578063612e3c051461079e5780636717e41c146107b15780636b20c454146107d457600080fd5b80634f558e791161034d5780634f558e79146106fc57806350fe03f61461071e57806351508f0a14610738578063520f77411461074b57806355f804b31461077057600080fd5b80633bf7f868146106835780633f4ba83a14610696578063471a4ff31461069e5780634c772c06146106c95780634e1273f4146106dc57600080fd5b8063138e26e31161041757806328024964116103db57806328024964146106055780632a55205a146106185780632eb2c2d61461064a5780632f2ff15d1461065d57806336568abe1461067057600080fd5b8063138e26e3146105a1578063156e29f6146105b457806317c50a27146105c757806318160ddd146105da578063248a9ca3146105e257600080fd5b80630968144d1161045e5780630968144d1461052d5780630e89341c146105405780630f6c401a14610553578063107046bd1461056657806311fdb52e1461058e57600080fd5b8062fdd58e1461049a57806301ffc9a7146104c057806305203778146104e35780630682bdbc146104f857806306fdde0314610518575b600080fd5b6104ad6104a8366004614e42565b610b81565b6040519081526020015b60405180910390f35b6104d36104ce366004614e84565b610bab565b60405190151581526020016104b7565b6104f66104f1366004614f5d565b610bd6565b005b60015461050b906001600160a01b031681565b6040516104b79190614f91565b610520610bee565b6040516104b79190614ff5565b60055461050b906001600160a01b031681565b61052061054e366004615008565b610c7c565b6104f6610561366004615021565b610d14565b610579610574366004615008565b610e29565b6040516104b7999897969594939291906150b0565b6104f661059c36600461512e565b610f4b565b6104f66105af36600461522b565b610f9c565b6104f66105c2366004615317565b61104d565b6104ad6105d5366004615008565b611186565b600c546104ad565b6104ad6105f0366004615008565b6000908152600d602052604090206001015490565b6104d361061336600461534c565b6111a4565b61062b610626366004615392565b6112c3565b604080516001600160a01b0390931683526020830191909152016104b7565b6104f6610658366004615412565b611325565b6104f661066b366004615021565b61137d565b6104f661067e366004615021565b61138f565b6104f6610691366004615392565b6113c2565b6104f66113d4565b6104d36106ac366004615008565b600090815260076020526040902060028101546003909101541090565b61050b6106d7366004615008565b6113e6565b6106ef6106ea3660046154c9565b611432565b6040516104b791906155c4565b6104d361070a366004615008565b6000908152600b6020526040902054151590565b60155461050b90600160601b90046001600160a01b031681565b6104f66107463660046155d7565b6114fe565b61075e610759366004615008565b611512565b60405160ff90911681526020016104b7565b6104f661077e366004614f5d565b611564565b61050b610791366004615008565b611578565b6104d36115c0565b60035461050b906001600160a01b031681565b6104d36107bf366004615008565b60026020526000908152604090205460ff1681565b6104f66107e23660046155f4565b6115d0565b6104f66107f53660046155d7565b61161d565b6104d3610808366004615008565b61162e565b6104f6611691565b610520610823366004615008565b6116a3565b6104f6610836366004615008565b61170b565b60155461084e906001600160601b031681565b6040516001600160601b0390911681526020016104b7565b6104f66117a1565b6104f661087c36600461566d565b6117b1565b6000546001600160a01b031661050b565b61050b6108a0366004615392565b611867565b6104ad6108b3366004615008565b61187f565b6104d36108c6366004615021565b6118be565b6104f66108d9366004614f5d565b6118e9565b6105206118fd565b6104f66108f43660046155d7565b61190a565b6104ad600081565b6104f661090f3660046156ef565b61191b565b610927610922366004615008565b611926565b6040516104b79190615779565b61050b610942366004615008565b611bb3565b6104f6610955366004614f5d565b611bfb565b6104ad610968366004615008565b611c0f565b6104ad61097b366004615008565b6000908152600b602052604090205490565b6104f661099b366004614f5d565b611c4e565b60045461050b906001600160a01b031681565b6104ad6109c1366004615008565b611c62565b61075e6109d4366004615008565b611c79565b6104f66109e7366004615008565b611cc1565b6104f66109fa366004615840565b611d8f565b6104f6610a0d366004615021565b611e4a565b6104f6610a20366004614f5d565b611e52565b6104ad600080516020615f0683398151915281565b6104f6610a483660046155d7565b611e66565b6104f6610a5b366004615008565b611ea6565b6104f6610a6e366004615392565b611f5e565b6104d3610a81366004615008565b611f9c565b610a99610a943660046155d7565b611fde565b6040516104b7929190615865565b6104f6610ab5366004615021565b6120f0565b6104d3610ac8366004615008565b612176565b6104ad7fc002a6b5f9f56101ba6339b1b95d171fadf6e41e005b91c80d686aef7516e75981565b61052061218d565b6104d3610b0a36600461588a565b61221f565b6104ad610b1d366004615008565b61224d565b6104f6610b303660046158b8565b61228c565b6104f6610b433660046155d7565b6122dc565b6104f6610b56366004615317565b612317565b6104ad610b69366004614e42565b6123f7565b6104ad610b7c366004615008565b612428565b60008181526008602090815260408083206001600160a01b03861684529091529020545b92915050565b6000636ad56fd360e11b6001600160e01b0319831601610bcd57506001919050565b610ba582612468565b610bde61248d565b6006610bea828261598f565b5050565b60108054610bfb90615914565b80601f0160208091040260200160405190810160405280929190818152602001828054610c2790615914565b8015610c745780601f10610c4957610100808354040283529160200191610c74565b820191906000526020600020905b815481529060010190602001808311610c5757829003601f168201915b505050505081565b6000818152600b6020526040902054606090610cdf5760405162461bcd60e51b815260206004820152601f60248201527f55524920717565727920666f72206e6f6e6578697374656e7420746f6b656e0060448201526064015b60405180910390fd5b6013610cea836124ba565b6014604051602001610cfe93929190615abf565b6040516020818303038152906040529050919050565b610d1c61248d565b81610d2681612176565b610d425760405162461bcd60e51b8152600401610cd690615ae7565b6001600160a01b038216610d8a5760405162461bcd60e51b815260206004820152600f60248201526e24b73b30b634b21031b932b0ba37b960891b6044820152606401610cd6565b6000838152600760205260409020600601546001600160a01b0390811690831603610df75760405162461bcd60e51b815260206004820152601f60248201527f53616d652063726561746f72206173207468652063757272656e74206f6e65006044820152606401610cd6565b5060009182526007602052604090912060060180546001600160a01b0319166001600160a01b03909216919091179055565b60076020818152600092835260409283902080548451808601865260028301548152600383015481850152855160608101875260048401546001600160a01b03908116825260058501548116958201959095526006840154909416958401959095529281015460088201546009830154600a8401805460ff978816989794958486169561010086048116956201000090041693929091610ec890615914565b80601f0160208091040260200160405190810160405280929190818152602001828054610ef490615914565b8015610f415780601f10610f1657610100808354040283529160200191610f41565b820191906000526020600020905b815481529060010190602001808311610f2457829003601f168201915b5050505050905089565b610f5361248d565b610f5c8161254c565b6040516001600160601b03821681527f9a188ae967bf1083cd9509ad029e6235101133357eb1313c77800751dc21d0c0906020015b60405180910390a150565b89610fa681612176565b15610fec5760405162461bcd60e51b815260206004820152601660248201527550726f6a65637420616c72656164792065786973747360501b6044820152606401610cd6565b610ff46125ce565b6110028a8a8a8a898b6125f4565b6110148b8b8b8b8b8b8b8b8b8b61278d565b6110305760405162461bcd60e51b8152600401610cd690615b13565b6110408b8b8b8b8b8b8b8b61283f565b5050505050505050505050565b611065600080516020615f06833981519152336118be565b6110815760405162461bcd60e51b8152600401610cd690615b4a565b6110896125ce565b611091612a5e565b61109a82612176565b6110b65760405162461bcd60e51b8152600401610cd690615ae7565b6110bf8261162e565b156111045760405162461bcd60e51b815260206004820152601560248201527450726f6a65637420697320726566756e6461626c6560581b6044820152606401610cd6565b61110e8282612a88565b61112983838360405180602001604052806000815250612b1d565b6111338383612b66565b81836001600160a01b03167f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f8360405161116f91815260200190565b60405180910390a36111816001600f55565b505050565b600061119182612428565b61119a8361187f565b610ba59190615ba2565b6000826111b081612176565b6111cc5760405162461bcd60e51b8152600401610cd690615ae7565b6112bb60076000868152602001908152602001600020600101805480602002602001604051908101604052809291908181526020016000905b828210156112b157838290600052602060002001805461122490615914565b80601f016020809104026020016040519081016040528092919081815260200182805461125090615914565b801561129d5780601f106112725761010080835404028352916020019161129d565b820191906000526020600020905b81548152906001019060200180831161128057829003601f168201915b505050505081526020019060010190611205565b5050505084612b8c565b949350505050565b6000828152600b6020526040812054819015611317576015546001600160a01b03600160601b8204169061271090611304906001600160601b031686615ba2565b61130e9190615bb9565b9150915061131e565b5060009050805b9250929050565b336001600160a01b03861681148015906113465750611344868261221f565b155b1561136857808660405163711bec9160e11b8152600401610cd6929190615bdb565b6113758686868686612be3565b505050505050565b61138561248d565b6111818282612c4a565b6001600160a01b03811633146113b85760405163334bd91960e11b815260040160405180910390fd5b6111818282612c77565b6113ca61248d565b610bea8282612ca4565b6113dc61248d565b6113e4612d4f565b565b6000816113f281612176565b61140e5760405162461bcd60e51b8152600401610cd690615ae7565b6000838152600760205260409020600401546001600160a01b031691505b50919050565b606081518351146114635781518351604051635b05999160e01b815260048101929092526024820152604401610cd6565b600083516001600160401b0381111561147e5761147e614ea1565b6040519080825280602002602001820160405280156114a7578160200160208202803683370190505b50905060005b84518110156114f6576020808202860101516114d190602080840287010151610b81565b8282815181106114e3576114e3615bf5565b60209081029190910101526001016114ad565b509392505050565b61150661248d565b61150f81612d9e565b50565b60008161151e81612176565b61153a5760405162461bcd60e51b8152600401610cd690615ae7565b60008381526007602052604090205460ff16600181111561155d5761155d615051565b9392505050565b61156c61248d565b6013610bea828261598f565b60008161158481612176565b6115a05760405162461bcd60e51b8152600401610cd690615ae7565b50506000908152600760205260409020600601546001600160a01b031690565b600054600160a01b900460ff1690565b6001600160a01b03831633148015906115f057506115ee833361221f565b155b1561161257338360405163711bec9160e11b8152600401610cd6929190615bdb565b611181838383612e14565b61162561248d565b61150f81612e5a565b60008161163a81612176565b6116565760405162461bcd60e51b8152600401610cd690615ae7565b600083815260076020526040902060080154610100900460ff16801561155d5750505060009081526007602052604090206008015460ff1690565b61169961248d565b6113e46000612edd565b6060816116af81612176565b6116cb5760405162461bcd60e51b8152600401610cd690615ae7565b600660076000858152602001908152602001600020600a016040516020016116f4929190615c0b565b604051602081830303815290604052915050919050565b611723600080516020615f06833981519152336118be565b8061173857506000546001600160a01b031633145b6117985760405162461bcd60e51b815260206004820152602b60248201527f4f6e6c792073656c6c6572206f72206f776e65722063616e2063616c6c20746860448201526a34b990333ab731ba34b7b760a91b6064820152608401610cd6565b61150f81612f2d565b6117a961248d565b6113e4612f6d565b84336117bc82611578565b6001600160a01b031614806117db57506000546001600160a01b031633145b6118185760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b6044820152606401610cd6565b6118206125ce565b61182d8686868686612fb0565b6118495760405162461bcd60e51b8152600401610cd690615b13565b6118538585613044565b61185d86866130a1565b6113758685613175565b6000828152600e6020526040812061155d90836132ec565b60008161188b81612176565b6118a75760405162461bcd60e51b8152600401610cd690615ae7565b505060009081526007602052604090206002015490565b6000918252600d602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6118f161248d565b6012610bea828261598f565b60118054610bfb90615914565b61191261248d565b61150f816132f8565b610bea33838361336e565b61192e614cd1565b8161193881612176565b6119545760405162461bcd60e51b8152600401610cd690615ae7565b60008381526007602052604090819020815161014081019092528054829060ff16600181111561198657611986615051565b600181111561199757611997615051565b815260200160018201805480602002602001604051908101604052809291908181526020016000905b82821015611a6c5783829060005260206000200180546119df90615914565b80601f0160208091040260200160405190810160405280929190818152602001828054611a0b90615914565b8015611a585780601f10611a2d57610100808354040283529160200191611a58565b820191906000526020600020905b815481529060010190602001808311611a3b57829003601f168201915b5050505050815260200190600101906119c0565b505050908252506040805180820182526002840154815260038401546020828101919091528084019190915281516060808201845260048601546001600160a01b039081168352600587015481169383019390935260068601549092168184015291830191909152600783015490820152600882015460ff808216151560808401526101008083048216151560a0850152620100009092041660c0830152600983015460e0830152600a83018054919092019190611b2990615914565b80601f0160208091040260200160405190810160405280929190818152602001828054611b5590615914565b8015611ba25780601f10611b7757610100808354040283529160200191611ba2565b820191906000526020600020905b815481529060010190602001808311611b8557829003601f168201915b505050505081525050915050919050565b600081611bbf81612176565b611bdb5760405162461bcd60e51b8152600401610cd690615ae7565b50506000908152600760205260409020600501546001600160a01b031690565b611c0361248d565b6011610bea828261598f565b600081611c1b81612176565b611c375760405162461bcd60e51b8152600401610cd690615ae7565b505060009081526007602052604090206003015490565b611c5661248d565b6010610bea828261598f565b6000818152600e60205260408120610ba5906133fc565b600081611c8581612176565b611ca15760405162461bcd60e51b8152600401610cd690615ae7565b505060009081526007602052604090206008015462010000900460ff1690565b611cd9600080516020615f06833981519152336118be565b611cf55760405162461bcd60e51b8152600401610cd690615b4a565b611cfe81612176565b611d1a5760405162461bcd60e51b8152600401610cd690615ae7565b611d238161162e565b611d795760405162461bcd60e51b815260206004820152602160248201527f50726f6a65637420697320616c7265616479206e6f7420726566756e6461626c6044820152606560f81b6064820152608401610cd6565b611d84816000613406565b61150f816000613506565b611da7600080516020615f06833981519152336118be565b80611dd75750611dd77fc002a6b5f9f56101ba6339b1b95d171fadf6e41e005b91c80d686aef7516e759336118be565b611e385760405162461bcd60e51b815260206004820152602c60248201527f4f6e6c792073656c6c6572206f72206275726e65722063616e2063616c6c207460448201526b3434b990333ab731ba34b7b760a11b6064820152608401610cd6565b611e406125ce565b610bea8282613506565b6113b861248d565b611e5a61248d565b6014610bea828261598f565b611e6e61248d565b611e778161365d565b7f3114b783fb2f3dcd0de0d9add9db02e0ed5b722dc50df7319280c27fe038c91281604051610f919190614f91565b611ebe600080516020615f06833981519152336118be565b611eda5760405162461bcd60e51b8152600401610cd690615b4a565b611ee381612176565b611eff5760405162461bcd60e51b8152600401610cd690615ae7565b611f088161162e565b15611f485760405162461bcd60e51b815260206004820152601060248201526f526566756e642069732061637469766560801b6044820152606401610cd6565b611f53816001613406565b61150f816001613506565b611f76600080516020615f06833981519152336118be565b611f925760405162461bcd60e51b8152600401610cd690615b4a565b610bea82826136f3565b600081611fa881612176565b611fc45760405162461bcd60e51b8152600401610cd690615ae7565b505060009081526007602052604090206008015460ff1690565b6001600160a01b038116600090815260166020908152604080832080548251818502810185019093528083526060948594909392919083018282801561204357602002820191906000526020600020905b81548152602001906001019080831161202f575b50505050509050600081516001600160401b0381111561206557612065614ea1565b60405190808252806020026020018201604052801561208e578160200160208202803683370190505b50905060005b82518110156120e5576120c0868483815181106120b3576120b3615bf5565b6020026020010151610b81565b8282815181106120d2576120d2615bf5565b6020908102919091010152600101612094565b509094909350915050565b6120f861248d565b8161210281612176565b61211e5760405162461bcd60e51b8152600401610cd690615ae7565b6001600160a01b0382166121445760405162461bcd60e51b8152600401610cd690615c20565b5060009182526007602052604090912060050180546001600160a01b0319166001600160a01b03909216919091179055565b600090815260076020526040902060020154151590565b60606012805461219c90615914565b80601f01602080910402602001604051908101604052809291908181526020018280546121c890615914565b80156122155780601f106121ea57610100808354040283529160200191612215565b820191906000526020600020905b8154815290600101906020018083116121f857829003601f168201915b5050505050905090565b6001600160a01b03918216600090815260096020908152604080832093909416825291909152205460ff1690565b60008161225981612176565b6122755760405162461bcd60e51b8152600401610cd690615ae7565b505060009081526007602052604090206009015490565b336001600160a01b03861681148015906122ad57506122ab868261221f565b155b156122cf57808660405163711bec9160e11b8152600401610cd6929190615bdb565b6113758686868686613799565b6122e461248d565b6001600160a01b03811661230e576000604051631e4fbdf760e01b8152600401610cd69190614f91565b61150f81612edd565b61231f6125ce565b61232882612176565b6123445760405162461bcd60e51b8152600401610cd690615ae7565b61234d82611f9c565b6123995760405162461bcd60e51b815260206004820152601a60248201527f50726f6a656374206275726e206973206e6f74206163746976650000000000006044820152606401610cd6565b6123a4838383613813565b6123ae8383612b66565b81836001600160a01b03167f49995e5dd6158cf69ad3e9777c46755a1a826a446c6416992167462dad033b2a836040516123ea91815260200190565b60405180910390a3505050565b6016602052816000526040600020818154811061241357600080fd5b90600052602060002001600091509150505481565b60008161243481612176565b6124505760405162461bcd60e51b8152600401610cd690615ae7565b50506000908152600760208190526040909120015490565b60006001600160e01b03198216635a05180f60e01b1480610ba55750610ba58261386a565b6000546001600160a01b031633146113e4573360405163118cdaa760e01b8152600401610cd69190614f91565b606060006124c78361388f565b60010190506000816001600160401b038111156124e6576124e6614ea1565b6040519080825280601f01601f191660200182016040528015612510576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a850494508461251a57509392505050565b6101f46001600160601b03821611156125a75760405162461bcd60e51b815260206004820152601d60248201527f526f796174696573206572726f723a204c696d697420726561636865640000006044820152606401610cd6565b601580546bffffffffffffffffffffffff19166001600160601b0392909216919091179055565b6125d66115c0565b156113e45760405163d93c066560e01b815260040160405180910390fd5b846000036126145760405162461bcd60e51b8152600401610cd690615c49565b600086600181111561262857612628615051565b14806126455750600186600181111561264357612643615051565b145b6126885760405162461bcd60e51b8152602060048201526014602482015273496e76616c69642070726f6a656374207479706560601b6044820152606401610cd6565b836000036126c85760405162461bcd60e51b815260206004820152600d60248201526c496e76616c696420707269636560981b6044820152606401610cd6565b6001600160a01b0383166126ee5760405162461bcd60e51b8152600401610cd690615c20565b6005546001600160a01b039081169084160361271c5760405162461bcd60e51b8152600401610cd690615c20565b600086600181111561273057612730615051565b03612784578160ff166000036127845760405162461bcd60e51b8152602060048201526019602482015278125b9d985b1a5908199d5b991a5b99c81d1a1c995cda1bdb19603a1b6044820152606401610cd6565b61137581613967565b60035460009081906001600160a01b031663bf4cb8d98d8d60018111156127b6576127b6615051565b8d8d8d8d8d8d8d6040518a63ffffffff1660e01b81526004016127e199989796959493929190615c71565b602060405180830381865afa1580156127fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128229190615ce2565b905061282f818585613ab3565b9c9b505050505050505050505050565b604080518082018252878152600060208083019190915282516060810184526001600160a01b03888116825260055416918101919091523381840152825161014081019093529091808a600181111561289a5761289a615051565b81526020808201889052604080830186905260608301859052608083018b9052600060a0840181905260c0840181905260ff891660e085015261010084018190526101209093018790528d83526007909152902081518154829060ff19166001838181111561290b5761290b615051565b021790555060208281015180516129289260018501920190614d60565b50604082810151805160028401556020908101516003840155606084015180516004850180546001600160a01b03199081166001600160a01b039384161790915592820151600586018054851691831691909117905592015160068401805490921692169190911790556080820151600782015560a082015160088201805460c085015160e086015161ffff1990921693151561ff0019169390931761010093151584021762ff000019166201000060ff909216919091021790558201516009820155610120820151600a820190612a00908261598f565b50506005546040518c92507f4ae401670c03e01e55d6a1794356bb5f642ef93fae65259bae62f66b89baff0891612a4a918c918c918c916001600160a01b0316908c908c90615cfb565b60405180910390a250505050505050505050565b6002600f5403612a8157604051633ee5aeb560e01b815260040160405180910390fd5b6002600f55565b612a918261187f565b81612a9b84611c0f565b612aa59190615d4a565b1115612af35760405162461bcd60e51b815260206004820152601e60248201527f4d696e74696e67206d6f7265207468616e20746f74616c20737570706c7900006044820152606401610cd6565b60008281526007602052604081206003018054839290612b14908490615d4a565b90915550505050565b6001600160a01b038416612b47576000604051632bfa23e760e11b8152600401610cd69190614f91565b600080612b548585613b3c565b91509150611375600087848487613b64565b6000612b728383610b81565b1115612b8257610bea8282613bb7565b610bea8282613bf4565b6000805b8351811015612bd9578280519060200120848281518110612bb357612bb3615bf5565b60200260200101518051906020012003612bd1576001915050610ba5565b600101612b90565b5060009392505050565b6001600160a01b038416612c0d576000604051632bfa23e760e11b8152600401610cd69190614f91565b6001600160a01b038516612c36576000604051626a0d4560e21b8152600401610cd69190614f91565b612c438585858585613b64565b5050505050565b600080612c578484613cb8565b9050801561155d576000848152600e602052604090206114f69084613d4c565b600080612c848484613d61565b9050801561155d576000848152600e602052604090206114f69084613dce565b81612cae81612176565b612cca5760405162461bcd60e51b8152600401610cd690615ae7565b612cd26125ce565b612cda61248d565b60008211612cfa5760405162461bcd60e51b8152600401610cd690615d5d565b600083815260076020526040908190206009018390555183907ff39ce44c038f0d06bede207dec9961321e2b281f53902ef349174fcb1983eacc90612d429085815260200190565b60405180910390a2505050565b612d57613de3565b6000805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b604051612d949190614f91565b60405180910390a1565b6001600160a01b038116612dc45760405162461bcd60e51b8152600401610cd690615c20565b6005546001600160a01b0390811690821603612df25760405162461bcd60e51b8152600401610cd690615d85565b600580546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b038316612e3d576000604051626a0d4560e21b8152600401610cd69190614f91565b611181836000848460405180602001604052806000815250613b64565b6001600160a01b038116612ebb5760405162461bcd60e51b815260206004820152602260248201527f5369676e20616464726573732063616e2774206265207a65726f206164647265604482015261737360f01b6064820152608401610cd6565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80612f3781612176565b612f535760405162461bcd60e51b8152600401610cd690615ae7565b612f5b6125ce565b610bea82612f688461187f565b613e08565b612f756125ce565b6000805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258612d873390565b60035460405163b3e441f160e01b815260009182916001600160a01b039091169063b3e441f190612feb908a908a908a908a90600401615dbc565b602060405180830381865afa158015613008573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061302c9190615ce2565b9050613039818585613ab3565b979650505050505050565b6001600160a01b03821661306a5760405162461bcd60e51b8152600401610cd690615c20565b6005546001600160a01b03908116908316036130985760405162461bcd60e51b8152600401610cd690615c20565b610bea81613967565b816130ab81612176565b6130c75760405162461bcd60e51b8152600401610cd690615ae7565b6130cf6125ce565b6001600160a01b0382166130f55760405162461bcd60e51b8152600401610cd690615c20565b6000838152600760205260409020600401546001600160a01b03838116911614611181576000838152600760205260409081902060040180546001600160a01b0319166001600160a01b0385161790555183907f59a565e969359bbed0cf8abff93714dac9724bc7fcf5f182de4d5f93e3770a8490612d42908590614f91565b8161317f81612176565b61319b5760405162461bcd60e51b8152600401610cd690615ae7565b6131a36125ce565b61329260076000858152602001908152602001600020600101805480602002602001604051908101604052809291908181526020016000905b828210156132885783829060005260206000200180546131fb90615914565b80601f016020809104026020016040519081016040528092919081815260200182805461322790615914565b80156132745780601f1061324957610100808354040283529160200191613274565b820191906000526020600020905b81548152906001019060200180831161325757829003601f168201915b5050505050815260200190600101906131dc565b5050505083613ee7565b61118157600083815260076020908152604090912083516132bb92600190920191850190614d60565b50827f420e36f88d1abaa41f0ccfe94c9f74e77ab2cddbdeea7cf57ff9a6e6f60a99ab83604051612d429190615df7565b600061155d8383613f69565b6001600160a01b03811661331e5760405162461bcd60e51b8152600401610cd690615c20565b6003546001600160a01b039081169082160361334c5760405162461bcd60e51b8152600401610cd690615d85565b600380546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b03821661339757600060405162ced3e160e81b8152600401610cd69190614f91565b6001600160a01b03838116600081815260096020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3191016123ea565b6000610ba5825490565b8161341081612176565b61342c5760405162461bcd60e51b8152600401610cd690615ae7565b6134346125ce565b60008381526007602052604090206008015482151561010090910460ff161515036134a15760405162461bcd60e51b815260206004820152601e60248201527f53616d6520737461747573206173207468652063757272656e74206f6e6500006044820152606401610cd6565b600083815260076020908152604091829020600801805461ff001916610100861515908102919091179091558251868152918201527f9e44f4f67244847aeb2d9345bd11632c9680e9cafabb6b8ca80843bf0c148abb910160405180910390a1505050565b61350f82612176565b61352b5760405162461bcd60e51b8152600401610cd690615ae7565b80151561353783611f9c565b15150361357c5760405162461bcd60e51b8152602060048201526013602482015272109d5c9b881a5cc8185b1c9958591e481cd95d606a1b6044820152606401610cd6565b8061360157600082815260076020526040902060080154610100900460ff16156136015760405162461bcd60e51b815260206004820152603060248201527f50726f6a65637420726566756e64206973206163746976652c206275726e206360448201526f185b89dd08189948191a5cd8589b195960821b6064820152608401610cd6565b600082815260076020908152604091829020600801805460ff19168415159081179091558251858152918201527fc6e1840e043c6516f034b472cf4c33a9b38efe4e413a66f775ddc7b5dbc10c85910160405180910390a15050565b6001600160a01b0381166136cb5760405162461bcd60e51b815260206004820152602f60248201527f457863657074696f6e20696e20736574526f79616c746965734164647265737360448201526e1d1020b2323932b9b9903d32b9379760891b6064820152608401610cd6565b601580546001600160a01b03909216600160601b026001600160601b03909216919091179055565b816136fd81612176565b6137195760405162461bcd60e51b8152600401610cd690615ae7565b6137216125ce565b600082116137415760405162461bcd60e51b8152600401610cd690615d5d565b60008381526007602052604081206009018054849290613762908490615d4a565b909155505060405182815283907f0d789849b516cb66fc65abfb22583f7875df1d855b72ec028f5425aa92687f3190602001612d42565b6001600160a01b0384166137c3576000604051632bfa23e760e11b8152600401610cd69190614f91565b6001600160a01b0385166137ec576000604051626a0d4560e21b8152600401610cd69190614f91565b6000806137f98585613b3c565b9150915061380a8787848487613b64565b50505050505050565b6001600160a01b03831661383c576000604051626a0d4560e21b8152600401610cd69190614f91565b6000806138498484613b3c565b91509150612c43856000848460405180602001604052806000815250613b64565b60006001600160e01b03198216637965db0b60e01b1480610ba55750610ba582613f93565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b83106138ce5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef810000000083106138fa576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061391857662386f26fc10000830492506010015b6305f5e1008310613930576305f5e100830492506008015b612710831061394457612710830492506004015b60648310613956576064830492506002015b600a8310610ba55760010192915050565b60008151116139b25760405162461bcd60e51b8152602060048201526017602482015276496e76616c6964207061796d656e74206d6574686f647360481b6044820152606401610cd6565b60005b8151811015610bea5760045482516001600160a01b0390911690632bf3434d908490849081106139e7576139e7615bf5565b60200260200101516040518263ffffffff1660e01b8152600401613a0b9190614ff5565b602060405180830381865afa158015613a28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a4c9190615e0a565b613aab5760405162461bcd60e51b815260206004820152602a60248201527f5061796d656e74206d6574686f64206e6f7420737570706f7274656420627920604482015269746865206f7261636c6560b01b6064820152608401610cd6565b6001016139b5565b60008281526002602052604081205460ff1615613b075760405162461bcd60e51b8152602060048201526012602482015271139bdb98d948185b1c9958591e481d5cd95960721b6044820152606401610cd6565b6000613b138584613fe3565b905080156112bb576000848152600260205260409020805460ff19166001179055949350505050565b6040805160018082526020820194909452808201938452606081019290925260808201905291565b613b7085858585614009565b6001600160a01b03841615612c435782513390600103613ba95760208481015190840151613ba2838989858589614400565b5050611375565b611375818787878787614512565b6001600160a01b0382166000908152601660205260409020613bd981836145f2565b61118157805460018101825560009182526020909120015550565b6001600160a01b0382166000908152601660205260408120805490915b81811015612c435783838281548110613c2c57613c2c615bf5565b906000526020600020015403613cb05782613c48600184615e27565b81548110613c5857613c58615bf5565b9060005260206000200154838281548110613c7557613c75615bf5565b906000526020600020018190555082805480613c9357613c93615e3a565b600190038181906000526020600020016000905590555050505050565b600101613c11565b6000613cc483836118be565b613d44576000838152600d602090815260408083206001600160a01b03861684529091529020805460ff19166001179055613cfc3390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610ba5565b506000610ba5565b600061155d836001600160a01b038416614642565b6000613d6d83836118be565b15613d44576000838152600d602090815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610ba5565b600061155d836001600160a01b038416614689565b613deb6115c0565b6113e457604051638dfc202b60e01b815260040160405180910390fd5b81613e1281612176565b613e2e5760405162461bcd60e51b8152600401610cd690615ae7565b613e366125ce565b81600003613e565760405162461bcd60e51b8152600401610cd690615c49565b600083815260076020526040902060030154821015613e875760405162461bcd60e51b8152600401610cd690615c49565b600083815260076020526040902060020154821461118157600083815260076020526040908190206002018390555183907f55c11c5539296e31386179e5eb5a807c9f67f27a6cb0e47c614723a5d8e0013d90612d429085815260200190565b60008151835114613efa57506000610ba5565b60005b8351811015613f5f57828181518110613f1857613f18615bf5565b602002602001015180519060200120848281518110613f3957613f39615bf5565b60200260200101518051906020012014613f57576000915050610ba5565b600101613efd565b5060019392505050565b6000826000018281548110613f8057613f80615bf5565b9060005260206000200154905092915050565b60006001600160e01b03198216636cdb3d1360e11b1480613fc457506001600160e01b031982166303a24d0760e21b145b80610ba557506301ffc9a760e01b6001600160e01b0319831614610ba5565b600080613ff0848461477c565b6001546001600160a01b03918216911614949350505050565b6140116125ce565b6001600160a01b0384161580159061403157506001600160a01b03831615155b801561404f5750826001600160a01b0316846001600160a01b031614155b156143ee576000805b83518110156143eb576140848685838151811061407757614077615bf5565b6020026020010151612b66565b61409a8585838151811061407757614077615bf5565b6000600760008684815181106140b2576140b2615bf5565b60200260200101518152602001908152602001600020604051806101400160405290816000820160009054906101000a900460ff1660018111156140f8576140f8615051565b600181111561410957614109615051565b815260200160018201805480602002602001604051908101604052809291908181526020016000905b828210156141de57838290600052602060002001805461415190615914565b80601f016020809104026020016040519081016040528092919081815260200182805461417d90615914565b80156141ca5780601f1061419f576101008083540402835291602001916141ca565b820191906000526020600020905b8154815290600101906020018083116141ad57829003601f168201915b505050505081526020019060010190614132565b505050908252506040805180820182526002840154815260038401546020828101919091528084019190915281516060808201845260048601546001600160a01b039081168352600587015481169383019390935260068601549092168184015291830191909152600783015490820152600882015460ff808216151560808401526101008083048216151560a0850152620100009092041660c0830152600983015460e0830152600a8301805491909201919061429b90615914565b80601f01602080910402602001604051908101604052809291908181526020018280546142c790615914565b80156143145780601f106142e957610100808354040283529160200191614314565b820191906000526020600020905b8154815290600101906020018083116142f757829003601f168201915b50505050508152505090508060600151602001519250826001600160a01b031663ef704c83888888868151811061434d5761434d615bf5565b602002602001015188878151811061436757614367615bf5565b60209081029190910101516040516001600160e01b031960e087901b1681526001600160a01b03948516600482015293909216602484015260448301526064820152608401600060405180830381600087803b1580156143c657600080fd5b505af11580156143da573d6000803e3d6000fd5b505060019093019250614058915050565b50505b6143fa848484846147a6565b50505050565b6001600160a01b0384163b156113755760405163f23a6e6160e01b81526001600160a01b0385169063f23a6e61906144449089908990889088908890600401615e50565b6020604051808303816000875af192505050801561447f575060408051601f3d908101601f1916820190925261447c91810190615e8a565b60015b6144df573d8080156144ad576040519150601f19603f3d011682016040523d82523d6000602084013e6144b2565b606091505b5080516000036144d75784604051632bfa23e760e11b8152600401610cd69190614f91565b805181602001fd5b6001600160e01b0319811663f23a6e6160e01b1461380a5784604051632bfa23e760e11b8152600401610cd69190614f91565b6001600160a01b0384163b156113755760405163bc197c8160e01b81526001600160a01b0385169063bc197c81906145569089908990889088908890600401615ea7565b6020604051808303816000875af1925050508015614591575060408051601f3d908101601f1916820190925261458e91810190615e8a565b60015b6145bf573d8080156144ad576040519150601f19603f3d011682016040523d82523d6000602084013e6144b2565b6001600160e01b0319811663bc197c8160e01b1461380a5784604051632bfa23e760e11b8152600401610cd69190614f91565b8154600090815b81811015614637578385828154811061461457614614615bf5565b90600052602060002001540361462f57600192505050610ba5565b6001016145f9565b506000949350505050565b6000818152600183016020526040812054613d4457508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610ba5565b600081815260018301602052604081205480156147725760006146ad600183615e27565b85549091506000906146c190600190615e27565b90508082146147265760008660000182815481106146e1576146e1615bf5565b906000526020600020015490508087600001848154811061470457614704615bf5565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061473757614737615e3a565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610ba5565b6000915050610ba5565b60008060008061478c86866148ec565b92509250925061479c8282614939565b5090949350505050565b6147b2848484846149f2565b6001600160a01b03841661485c576000805b83518110156148425760008382815181106147e1576147e1615bf5565b6020026020010151905080600b600087858151811061480257614802615bf5565b6020026020010151815260200190815260200160002060008282546148279190615d4a565b9091555061483790508184615d4a565b9250506001016147c4565b5080600c60008282546148559190615d4a565b9091555050505b6001600160a01b0383166143fa576000805b83518110156148db57600083828151811061488b5761488b615bf5565b6020026020010151905080600b60008785815181106148ac576148ac615bf5565b60209081029190910181015182528101919091526040016000208054919091039055919091019060010161486e565b50600c805491909103905550505050565b600080600083516041036149265760208401516040850151606086015160001a61491888828585614c0c565b955095509550505050614932565b50508151600091506002905b9250925092565b600082600381111561494d5761494d615051565b03614956575050565b600182600381111561496a5761496a615051565b036149885760405163f645eedf60e01b815260040160405180910390fd5b600282600381111561499c5761499c615051565b036149bd5760405163fce698f760e01b815260048101829052602401610cd6565b60038260038111156149d1576149d1615051565b03610bea576040516335e2f38360e21b815260048101829052602401610cd6565b8051825114614a215781518151604051635b05999160e01b815260048101929092526024820152604401610cd6565b3360005b8351811015614b2d576020818102858101820151908501909101516001600160a01b03881615614adc5760008281526008602090815260408083206001600160a01b038c16845290915290205481811015614ab3576040516303dee4c560e01b81526001600160a01b038a166004820152602481018290526044810183905260648101849052608401610cd6565b60008381526008602090815260408083206001600160a01b038d16845290915290209082900390555b6001600160a01b03871615614b235760008281526008602090815260408083206001600160a01b038b16845290915281208054839290614b1d908490615d4a565b90915550505b5050600101614a25565b508251600103614bae5760208301516000906020840151909150856001600160a01b0316876001600160a01b0316846001600160a01b03167fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f628585604051614b9f929190918252602082015260400190565b60405180910390a45050612c43565b836001600160a01b0316856001600160a01b0316826001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8686604051614bfd929190615865565b60405180910390a45050505050565b600080806fa2a8918ca85bafe22016d0b997e4df60600160ff1b03841115614c3d5750600091506003905082614cc7565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015614c91573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116614cbd57506000925060019150829050614cc7565b9250600091508190505b9450945094915050565b604080516101408101909152806000815260200160608152602001614d09604051806040016040528060008152602001600081525090565b8152604080516060810182526000808252602082810182905292820152910190815260200160008152602001600015158152602001600015158152602001600060ff16815260200160008152602001606081525090565b828054828255906000526020600020908101928215614da6579160200282015b82811115614da65782518290614d96908261598f565b5091602001919060010190614d80565b50614db2929150614db6565b5090565b80821115614db2576000614dca8282614dd3565b50600101614db6565b508054614ddf90615914565b6000825580601f10614def575050565b601f01602090049060005260206000209081019061150f91905b80821115614db25760008155600101614e09565b6001600160a01b038116811461150f57600080fd5b8035614e3d81614e1d565b919050565b60008060408385031215614e5557600080fd5b8235614e6081614e1d565b946020939093013593505050565b6001600160e01b03198116811461150f57600080fd5b600060208284031215614e9657600080fd5b813561155d81614e6e565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715614edf57614edf614ea1565b604052919050565b600082601f830112614ef857600080fd5b8135602083016000806001600160401b03841115614f1857614f18614ea1565b50601f8301601f1916602001614f2d81614eb7565b915050828152858383011115614f4257600080fd5b82826020830137600092810160200192909252509392505050565b600060208284031215614f6f57600080fd5b81356001600160401b03811115614f8557600080fd5b6112bb84828501614ee7565b6001600160a01b0391909116815260200190565b60005b83811015614fc0578181015183820152602001614fa8565b50506000910152565b60008151808452614fe1816020860160208601614fa5565b601f01601f19169290920160200192915050565b60208152600061155d6020830184614fc9565b60006020828403121561501a57600080fd5b5035919050565b6000806040838503121561503457600080fd5b82359150602083013561504681614e1d565b809150509250929050565b634e487b7160e01b600052602160045260246000fd5b6002811061508557634e487b7160e01b600052602160045260246000fd5b9052565b80516001600160a01b03908116835260208083015182169084015260409182015116910152565b6150ba818b615067565b6150d1602082018a80518252602090810151910152565b6150de6060820189615089565b8660c082015285151560e082015284151561010082015260ff841661012082015282610140820152610180610160820152600061511f610180830184614fc9565b9b9a5050505050505050505050565b60006020828403121561514057600080fd5b81356001600160601b038116811461155d57600080fd5b803560028110614e3d57600080fd5b60006001600160401b0382111561517f5761517f614ea1565b5060051b60200190565b600082601f83011261519a57600080fd5b81356151ad6151a882615166565b614eb7565b8082825260208201915060208360051b8601019250858311156151cf57600080fd5b602085015b838110156152105780356001600160401b038111156151f257600080fd5b615201886020838a0101614ee7565b845250602092830192016151d4565b5095945050505050565b803560ff81168114614e3d57600080fd5b6000806000806000806000806000806101408b8d03121561524b57600080fd5b8a35995061525b60208c01615157565b985060408b0135975060608b0135965061527760808c01614e32565b955060a08b01356001600160401b0381111561529257600080fd5b61529e8d828e01615189565b9550506152ad60c08c0161521a565b935060e08b01356001600160401b038111156152c857600080fd5b6152d48d828e01614ee7565b9350506101008b013591506101208b01356001600160401b038111156152f957600080fd5b6153058d828e01614ee7565b9150509295989b9194979a5092959850565b60008060006060848603121561532c57600080fd5b833561533781614e1d565b95602085013595506040909401359392505050565b6000806040838503121561535f57600080fd5b8235915060208301356001600160401b0381111561537c57600080fd5b61538885828601614ee7565b9150509250929050565b600080604083850312156153a557600080fd5b50508035926020909101359150565b600082601f8301126153c557600080fd5b81356153d36151a882615166565b8082825260208201915060208360051b8601019250858311156153f557600080fd5b602085015b838110156152105780358352602092830192016153fa565b600080600080600060a0868803121561542a57600080fd5b853561543581614e1d565b9450602086013561544581614e1d565b935060408601356001600160401b0381111561546057600080fd5b61546c888289016153b4565b93505060608601356001600160401b0381111561548857600080fd5b615494888289016153b4565b92505060808601356001600160401b038111156154b057600080fd5b6154bc88828901614ee7565b9150509295509295909350565b600080604083850312156154dc57600080fd5b82356001600160401b038111156154f257600080fd5b8301601f8101851361550357600080fd5b80356155116151a882615166565b8082825260208201915060208360051b85010192508783111561553357600080fd5b6020840193505b8284101561555e57833561554d81614e1d565b82526020938401939091019061553a565b945050505060208301356001600160401b0381111561557c57600080fd5b615388858286016153b4565b600081518084526020840193506020830160005b828110156155ba57815186526020958601959091019060010161559c565b5093949350505050565b60208152600061155d6020830184615588565b6000602082840312156155e957600080fd5b813561155d81614e1d565b60008060006060848603121561560957600080fd5b833561561481614e1d565b925060208401356001600160401b0381111561562f57600080fd5b61563b868287016153b4565b92505060408401356001600160401b0381111561565757600080fd5b615663868287016153b4565b9150509250925092565b600080600080600060a0868803121561568557600080fd5b85359450602086013561569781614e1d565b935060408601356001600160401b038111156156b257600080fd5b6156be88828901615189565b9350506060860135915060808601356001600160401b038111156154b057600080fd5b801515811461150f57600080fd5b6000806040838503121561570257600080fd5b823561570d81614e1d565b91506020830135615046816156e1565b600082825180855260208501945060208160051b8301016020850160005b8381101561576d57601f19858403018852615757838351614fc9565b602098890198909350919091019060010161573b565b50909695505050505050565b6020815261578b602082018351615067565b600060208301516101a060408401526157a86101c084018261571d565b604085015180516060860152602081015160808601529091505060608401516157d460a0850182615089565b50608084015161010084015260a08401518015156101208501525060c08401518015156101408501525060e084015160ff811661016085015250610100840151610180840152610120840151838203601f19016101a08501526158378282614fc9565b95945050505050565b6000806040838503121561585357600080fd5b823591506020830135615046816156e1565b6040815260006158786040830185615588565b82810360208401526158378185615588565b6000806040838503121561589d57600080fd5b82356158a881614e1d565b9150602083013561504681614e1d565b600080600080600060a086880312156158d057600080fd5b85356158db81614e1d565b945060208601356158eb81614e1d565b9350604086013592506060860135915060808601356001600160401b038111156154b057600080fd5b600181811c9082168061592857607f821691505b60208210810361142c57634e487b7160e01b600052602260045260246000fd5b601f82111561118157806000526020600020601f840160051c8101602085101561596f5750805b601f840160051c820191505b81811015612c43576000815560010161597b565b81516001600160401b038111156159a8576159a8614ea1565b6159bc816159b68454615914565b84615948565b6020601f8211600181146159f057600083156159d85750848201515b600019600385901b1c1916600184901b178455612c43565b600084815260208120601f198516915b82811015615a205787850151825560209485019460019092019101615a00565b5084821015615a3e5786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b60008154615a5a81615914565b600182168015615a715760018114615a8657615ab6565b60ff1983168652811515820286019350615ab6565b84600052602060002060005b83811015615aae57815488820152600190910190602001615a92565b505081860193505b50505092915050565b6000615acb8286615a4d565b8451615adb818360208901614fa5565b61303981830186615a4d565b60208082526012908201527150726f6a656374206e6f742065786973747360701b604082015260600190565b6020808252601b908201527f5369676e61747572652076616c69646174696f6e206661696c65640000000000604082015260600190565b60208082526022908201527f4f6e6c79206d696e7465722063616e2063616c6c20746869732066756e63746960408201526137b760f11b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610ba557610ba5615b8c565b600082615bd657634e487b7160e01b600052601260045260246000fd5b500490565b6001600160a01b0392831681529116602082015260400190565b634e487b7160e01b600052603260045260246000fd5b60006112bb615c1a8386615a4d565b84615a4d565b6020808252600f908201526e496e76616c6964206164647265737360881b604082015260600190565b6020808252600e908201526d496e76616c696420737570706c7960901b604082015260600190565b89815260ff8916602082015287604082015286606082015260018060a01b038616608082015261012060a08201526000615caf61012083018761571d565b60ff861660c084015282810360e0840152615cca8186614fc9565b915050826101008301529a9950505050505050505050565b600060208284031215615cf457600080fd5b5051919050565b868152602081018690526001600160a01b0385811660408301528416606082015260c060808201819052600090615d349083018561571d565b905060ff831660a0830152979650505050505050565b80820180821115610ba557610ba5615b8c565b6020808252600e908201526d125b9d985b1a5908185b5bdd5b9d60921b604082015260600190565b6020808252601f908201527f53616d652061646472657373206173207468652063757272656e74206f6e6500604082015260600190565b8481526001600160a01b0384166020820152608060408201819052600090615de69083018561571d565b905082606083015295945050505050565b60208152600061155d602083018461571d565b600060208284031215615e1c57600080fd5b815161155d816156e1565b81810381811115610ba557610ba5615b8c565b634e487b7160e01b600052603160045260246000fd5b6001600160a01b03868116825285166020820152604081018490526060810183905260a06080820181905260009061303990830184614fc9565b600060208284031215615e9c57600080fd5b815161155d81614e6e565b6001600160a01b0386811682528516602082015260a060408201819052600090615ed390830186615588565b8281036060840152615ee58186615588565b90508281036080840152615ef98185614fc9565b9897505050505050505056fe43f25613eb2f15fb17222a5d424ca2655743e71265d98e4b93c05e5fb589ecdea264697066735822122059e50b6194d0d64b89a52558674ac691179908b12b29a6208764aed6b7c0a69064736f6c634300081b003368747470733a2f2f63646e2e656c657665782e61692f6d657461646174612f7b69647d2e6a736f6e68747470733a2f2f6861726c657175696e2d706563756c6961722d7772656e2d3632352e6d7970696e6174612e636c6f75642f697066732f00000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000004ae302bd39270eca591a664ce1adcfe7eaf5b00b000000000000000000000000000000000000000000000000000000000000006400000000000000000000000080f6130ff755803a053279fbacbdf34d0b4e213a0000000000000000000000006f71566da9dea7e8f285ba430ded3aeeca49430c000000000000000000000000000000000000000000000000000000000000000a454c565820546f6b656e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004454c565800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f68747470733a2f2f63646e2e656c657665782e61692f70726f6a656374732f00000000000000000000000000000000000000000000000000000000000000002c68747470733a2f2f63646e2e656c657665782e61692f636f6e74726163742f6d657461646174612e6a736f6e0000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106104955760003560e01c80638456cb5911610262578063cdfa355311610151578063e4de9045116100ce578063ee15c4e411610092578063ee15c4e414610b0f578063f242432a14610b22578063f2fde38b14610b35578063f5298aca14610b48578063fa3bdc4614610b5b578063ffa15e9814610b6e57600080fd5b8063e4de904514610aa7578063e7da3ee714610aba578063e8884f2b14610acd578063e8a3d48514610af4578063e985e9c514610afc57600080fd5b8063dc95c4a711610115578063dc95c4a714610a3a578063ded238ca14610a4d578063dfed168914610a60578063e09fb94e14610a73578063e32e723c14610a8657600080fd5b8063cdfa3553146109d9578063d276cf26146109ec578063d547741f146109ff578063da3ef23f14610a12578063dbe4613d14610a2557600080fd5b8063a22cb465116101df578063bd85b039116101a3578063bd85b0391461096d578063c47f00271461098d578063c68c1091146109a0578063ca15c873146109b3578063cbf1b2c3146109c657600080fd5b8063a22cb46514610901578063a71fc22c14610914578063acdcb8a114610934578063b84c824614610947578063b996fff31461095a57600080fd5b806391d148541161022657806391d14854146108b8578063938e3d7b146108cb57806395d89b41146108de5780639cd2c92e146108e6578063a217fddf146108f957600080fd5b80638456cb59146108665780638c467cab1461086e5780638da5cb5b146108815780639010d07c146108925780639116ce7f146108a557600080fd5b80633bf7f868116103895780635a36e0da116103065780636c19e783116102ca5780636c19e783146107e75780636d261c3f146107fa578063715018a61461080d5780637aa66dd314610815578063806234441461082857806383ed62921461083b57600080fd5b80635a36e0da146107835780635c975abb14610796578063612e3c051461079e5780636717e41c146107b15780636b20c454146107d457600080fd5b80634f558e791161034d5780634f558e79146106fc57806350fe03f61461071e57806351508f0a14610738578063520f77411461074b57806355f804b31461077057600080fd5b80633bf7f868146106835780633f4ba83a14610696578063471a4ff31461069e5780634c772c06146106c95780634e1273f4146106dc57600080fd5b8063138e26e31161041757806328024964116103db57806328024964146106055780632a55205a146106185780632eb2c2d61461064a5780632f2ff15d1461065d57806336568abe1461067057600080fd5b8063138e26e3146105a1578063156e29f6146105b457806317c50a27146105c757806318160ddd146105da578063248a9ca3146105e257600080fd5b80630968144d1161045e5780630968144d1461052d5780630e89341c146105405780630f6c401a14610553578063107046bd1461056657806311fdb52e1461058e57600080fd5b8062fdd58e1461049a57806301ffc9a7146104c057806305203778146104e35780630682bdbc146104f857806306fdde0314610518575b600080fd5b6104ad6104a8366004614e42565b610b81565b6040519081526020015b60405180910390f35b6104d36104ce366004614e84565b610bab565b60405190151581526020016104b7565b6104f66104f1366004614f5d565b610bd6565b005b60015461050b906001600160a01b031681565b6040516104b79190614f91565b610520610bee565b6040516104b79190614ff5565b60055461050b906001600160a01b031681565b61052061054e366004615008565b610c7c565b6104f6610561366004615021565b610d14565b610579610574366004615008565b610e29565b6040516104b7999897969594939291906150b0565b6104f661059c36600461512e565b610f4b565b6104f66105af36600461522b565b610f9c565b6104f66105c2366004615317565b61104d565b6104ad6105d5366004615008565b611186565b600c546104ad565b6104ad6105f0366004615008565b6000908152600d602052604090206001015490565b6104d361061336600461534c565b6111a4565b61062b610626366004615392565b6112c3565b604080516001600160a01b0390931683526020830191909152016104b7565b6104f6610658366004615412565b611325565b6104f661066b366004615021565b61137d565b6104f661067e366004615021565b61138f565b6104f6610691366004615392565b6113c2565b6104f66113d4565b6104d36106ac366004615008565b600090815260076020526040902060028101546003909101541090565b61050b6106d7366004615008565b6113e6565b6106ef6106ea3660046154c9565b611432565b6040516104b791906155c4565b6104d361070a366004615008565b6000908152600b6020526040902054151590565b60155461050b90600160601b90046001600160a01b031681565b6104f66107463660046155d7565b6114fe565b61075e610759366004615008565b611512565b60405160ff90911681526020016104b7565b6104f661077e366004614f5d565b611564565b61050b610791366004615008565b611578565b6104d36115c0565b60035461050b906001600160a01b031681565b6104d36107bf366004615008565b60026020526000908152604090205460ff1681565b6104f66107e23660046155f4565b6115d0565b6104f66107f53660046155d7565b61161d565b6104d3610808366004615008565b61162e565b6104f6611691565b610520610823366004615008565b6116a3565b6104f6610836366004615008565b61170b565b60155461084e906001600160601b031681565b6040516001600160601b0390911681526020016104b7565b6104f66117a1565b6104f661087c36600461566d565b6117b1565b6000546001600160a01b031661050b565b61050b6108a0366004615392565b611867565b6104ad6108b3366004615008565b61187f565b6104d36108c6366004615021565b6118be565b6104f66108d9366004614f5d565b6118e9565b6105206118fd565b6104f66108f43660046155d7565b61190a565b6104ad600081565b6104f661090f3660046156ef565b61191b565b610927610922366004615008565b611926565b6040516104b79190615779565b61050b610942366004615008565b611bb3565b6104f6610955366004614f5d565b611bfb565b6104ad610968366004615008565b611c0f565b6104ad61097b366004615008565b6000908152600b602052604090205490565b6104f661099b366004614f5d565b611c4e565b60045461050b906001600160a01b031681565b6104ad6109c1366004615008565b611c62565b61075e6109d4366004615008565b611c79565b6104f66109e7366004615008565b611cc1565b6104f66109fa366004615840565b611d8f565b6104f6610a0d366004615021565b611e4a565b6104f6610a20366004614f5d565b611e52565b6104ad600080516020615f0683398151915281565b6104f6610a483660046155d7565b611e66565b6104f6610a5b366004615008565b611ea6565b6104f6610a6e366004615392565b611f5e565b6104d3610a81366004615008565b611f9c565b610a99610a943660046155d7565b611fde565b6040516104b7929190615865565b6104f6610ab5366004615021565b6120f0565b6104d3610ac8366004615008565b612176565b6104ad7fc002a6b5f9f56101ba6339b1b95d171fadf6e41e005b91c80d686aef7516e75981565b61052061218d565b6104d3610b0a36600461588a565b61221f565b6104ad610b1d366004615008565b61224d565b6104f6610b303660046158b8565b61228c565b6104f6610b433660046155d7565b6122dc565b6104f6610b56366004615317565b612317565b6104ad610b69366004614e42565b6123f7565b6104ad610b7c366004615008565b612428565b60008181526008602090815260408083206001600160a01b03861684529091529020545b92915050565b6000636ad56fd360e11b6001600160e01b0319831601610bcd57506001919050565b610ba582612468565b610bde61248d565b6006610bea828261598f565b5050565b60108054610bfb90615914565b80601f0160208091040260200160405190810160405280929190818152602001828054610c2790615914565b8015610c745780601f10610c4957610100808354040283529160200191610c74565b820191906000526020600020905b815481529060010190602001808311610c5757829003601f168201915b505050505081565b6000818152600b6020526040902054606090610cdf5760405162461bcd60e51b815260206004820152601f60248201527f55524920717565727920666f72206e6f6e6578697374656e7420746f6b656e0060448201526064015b60405180910390fd5b6013610cea836124ba565b6014604051602001610cfe93929190615abf565b6040516020818303038152906040529050919050565b610d1c61248d565b81610d2681612176565b610d425760405162461bcd60e51b8152600401610cd690615ae7565b6001600160a01b038216610d8a5760405162461bcd60e51b815260206004820152600f60248201526e24b73b30b634b21031b932b0ba37b960891b6044820152606401610cd6565b6000838152600760205260409020600601546001600160a01b0390811690831603610df75760405162461bcd60e51b815260206004820152601f60248201527f53616d652063726561746f72206173207468652063757272656e74206f6e65006044820152606401610cd6565b5060009182526007602052604090912060060180546001600160a01b0319166001600160a01b03909216919091179055565b60076020818152600092835260409283902080548451808601865260028301548152600383015481850152855160608101875260048401546001600160a01b03908116825260058501548116958201959095526006840154909416958401959095529281015460088201546009830154600a8401805460ff978816989794958486169561010086048116956201000090041693929091610ec890615914565b80601f0160208091040260200160405190810160405280929190818152602001828054610ef490615914565b8015610f415780601f10610f1657610100808354040283529160200191610f41565b820191906000526020600020905b815481529060010190602001808311610f2457829003601f168201915b5050505050905089565b610f5361248d565b610f5c8161254c565b6040516001600160601b03821681527f9a188ae967bf1083cd9509ad029e6235101133357eb1313c77800751dc21d0c0906020015b60405180910390a150565b89610fa681612176565b15610fec5760405162461bcd60e51b815260206004820152601660248201527550726f6a65637420616c72656164792065786973747360501b6044820152606401610cd6565b610ff46125ce565b6110028a8a8a8a898b6125f4565b6110148b8b8b8b8b8b8b8b8b8b61278d565b6110305760405162461bcd60e51b8152600401610cd690615b13565b6110408b8b8b8b8b8b8b8b61283f565b5050505050505050505050565b611065600080516020615f06833981519152336118be565b6110815760405162461bcd60e51b8152600401610cd690615b4a565b6110896125ce565b611091612a5e565b61109a82612176565b6110b65760405162461bcd60e51b8152600401610cd690615ae7565b6110bf8261162e565b156111045760405162461bcd60e51b815260206004820152601560248201527450726f6a65637420697320726566756e6461626c6560581b6044820152606401610cd6565b61110e8282612a88565b61112983838360405180602001604052806000815250612b1d565b6111338383612b66565b81836001600160a01b03167f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f8360405161116f91815260200190565b60405180910390a36111816001600f55565b505050565b600061119182612428565b61119a8361187f565b610ba59190615ba2565b6000826111b081612176565b6111cc5760405162461bcd60e51b8152600401610cd690615ae7565b6112bb60076000868152602001908152602001600020600101805480602002602001604051908101604052809291908181526020016000905b828210156112b157838290600052602060002001805461122490615914565b80601f016020809104026020016040519081016040528092919081815260200182805461125090615914565b801561129d5780601f106112725761010080835404028352916020019161129d565b820191906000526020600020905b81548152906001019060200180831161128057829003601f168201915b505050505081526020019060010190611205565b5050505084612b8c565b949350505050565b6000828152600b6020526040812054819015611317576015546001600160a01b03600160601b8204169061271090611304906001600160601b031686615ba2565b61130e9190615bb9565b9150915061131e565b5060009050805b9250929050565b336001600160a01b03861681148015906113465750611344868261221f565b155b1561136857808660405163711bec9160e11b8152600401610cd6929190615bdb565b6113758686868686612be3565b505050505050565b61138561248d565b6111818282612c4a565b6001600160a01b03811633146113b85760405163334bd91960e11b815260040160405180910390fd5b6111818282612c77565b6113ca61248d565b610bea8282612ca4565b6113dc61248d565b6113e4612d4f565b565b6000816113f281612176565b61140e5760405162461bcd60e51b8152600401610cd690615ae7565b6000838152600760205260409020600401546001600160a01b031691505b50919050565b606081518351146114635781518351604051635b05999160e01b815260048101929092526024820152604401610cd6565b600083516001600160401b0381111561147e5761147e614ea1565b6040519080825280602002602001820160405280156114a7578160200160208202803683370190505b50905060005b84518110156114f6576020808202860101516114d190602080840287010151610b81565b8282815181106114e3576114e3615bf5565b60209081029190910101526001016114ad565b509392505050565b61150661248d565b61150f81612d9e565b50565b60008161151e81612176565b61153a5760405162461bcd60e51b8152600401610cd690615ae7565b60008381526007602052604090205460ff16600181111561155d5761155d615051565b9392505050565b61156c61248d565b6013610bea828261598f565b60008161158481612176565b6115a05760405162461bcd60e51b8152600401610cd690615ae7565b50506000908152600760205260409020600601546001600160a01b031690565b600054600160a01b900460ff1690565b6001600160a01b03831633148015906115f057506115ee833361221f565b155b1561161257338360405163711bec9160e11b8152600401610cd6929190615bdb565b611181838383612e14565b61162561248d565b61150f81612e5a565b60008161163a81612176565b6116565760405162461bcd60e51b8152600401610cd690615ae7565b600083815260076020526040902060080154610100900460ff16801561155d5750505060009081526007602052604090206008015460ff1690565b61169961248d565b6113e46000612edd565b6060816116af81612176565b6116cb5760405162461bcd60e51b8152600401610cd690615ae7565b600660076000858152602001908152602001600020600a016040516020016116f4929190615c0b565b604051602081830303815290604052915050919050565b611723600080516020615f06833981519152336118be565b8061173857506000546001600160a01b031633145b6117985760405162461bcd60e51b815260206004820152602b60248201527f4f6e6c792073656c6c6572206f72206f776e65722063616e2063616c6c20746860448201526a34b990333ab731ba34b7b760a91b6064820152608401610cd6565b61150f81612f2d565b6117a961248d565b6113e4612f6d565b84336117bc82611578565b6001600160a01b031614806117db57506000546001600160a01b031633145b6118185760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b6044820152606401610cd6565b6118206125ce565b61182d8686868686612fb0565b6118495760405162461bcd60e51b8152600401610cd690615b13565b6118538585613044565b61185d86866130a1565b6113758685613175565b6000828152600e6020526040812061155d90836132ec565b60008161188b81612176565b6118a75760405162461bcd60e51b8152600401610cd690615ae7565b505060009081526007602052604090206002015490565b6000918252600d602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6118f161248d565b6012610bea828261598f565b60118054610bfb90615914565b61191261248d565b61150f816132f8565b610bea33838361336e565b61192e614cd1565b8161193881612176565b6119545760405162461bcd60e51b8152600401610cd690615ae7565b60008381526007602052604090819020815161014081019092528054829060ff16600181111561198657611986615051565b600181111561199757611997615051565b815260200160018201805480602002602001604051908101604052809291908181526020016000905b82821015611a6c5783829060005260206000200180546119df90615914565b80601f0160208091040260200160405190810160405280929190818152602001828054611a0b90615914565b8015611a585780601f10611a2d57610100808354040283529160200191611a58565b820191906000526020600020905b815481529060010190602001808311611a3b57829003601f168201915b5050505050815260200190600101906119c0565b505050908252506040805180820182526002840154815260038401546020828101919091528084019190915281516060808201845260048601546001600160a01b039081168352600587015481169383019390935260068601549092168184015291830191909152600783015490820152600882015460ff808216151560808401526101008083048216151560a0850152620100009092041660c0830152600983015460e0830152600a83018054919092019190611b2990615914565b80601f0160208091040260200160405190810160405280929190818152602001828054611b5590615914565b8015611ba25780601f10611b7757610100808354040283529160200191611ba2565b820191906000526020600020905b815481529060010190602001808311611b8557829003601f168201915b505050505081525050915050919050565b600081611bbf81612176565b611bdb5760405162461bcd60e51b8152600401610cd690615ae7565b50506000908152600760205260409020600501546001600160a01b031690565b611c0361248d565b6011610bea828261598f565b600081611c1b81612176565b611c375760405162461bcd60e51b8152600401610cd690615ae7565b505060009081526007602052604090206003015490565b611c5661248d565b6010610bea828261598f565b6000818152600e60205260408120610ba5906133fc565b600081611c8581612176565b611ca15760405162461bcd60e51b8152600401610cd690615ae7565b505060009081526007602052604090206008015462010000900460ff1690565b611cd9600080516020615f06833981519152336118be565b611cf55760405162461bcd60e51b8152600401610cd690615b4a565b611cfe81612176565b611d1a5760405162461bcd60e51b8152600401610cd690615ae7565b611d238161162e565b611d795760405162461bcd60e51b815260206004820152602160248201527f50726f6a65637420697320616c7265616479206e6f7420726566756e6461626c6044820152606560f81b6064820152608401610cd6565b611d84816000613406565b61150f816000613506565b611da7600080516020615f06833981519152336118be565b80611dd75750611dd77fc002a6b5f9f56101ba6339b1b95d171fadf6e41e005b91c80d686aef7516e759336118be565b611e385760405162461bcd60e51b815260206004820152602c60248201527f4f6e6c792073656c6c6572206f72206275726e65722063616e2063616c6c207460448201526b3434b990333ab731ba34b7b760a11b6064820152608401610cd6565b611e406125ce565b610bea8282613506565b6113b861248d565b611e5a61248d565b6014610bea828261598f565b611e6e61248d565b611e778161365d565b7f3114b783fb2f3dcd0de0d9add9db02e0ed5b722dc50df7319280c27fe038c91281604051610f919190614f91565b611ebe600080516020615f06833981519152336118be565b611eda5760405162461bcd60e51b8152600401610cd690615b4a565b611ee381612176565b611eff5760405162461bcd60e51b8152600401610cd690615ae7565b611f088161162e565b15611f485760405162461bcd60e51b815260206004820152601060248201526f526566756e642069732061637469766560801b6044820152606401610cd6565b611f53816001613406565b61150f816001613506565b611f76600080516020615f06833981519152336118be565b611f925760405162461bcd60e51b8152600401610cd690615b4a565b610bea82826136f3565b600081611fa881612176565b611fc45760405162461bcd60e51b8152600401610cd690615ae7565b505060009081526007602052604090206008015460ff1690565b6001600160a01b038116600090815260166020908152604080832080548251818502810185019093528083526060948594909392919083018282801561204357602002820191906000526020600020905b81548152602001906001019080831161202f575b50505050509050600081516001600160401b0381111561206557612065614ea1565b60405190808252806020026020018201604052801561208e578160200160208202803683370190505b50905060005b82518110156120e5576120c0868483815181106120b3576120b3615bf5565b6020026020010151610b81565b8282815181106120d2576120d2615bf5565b6020908102919091010152600101612094565b509094909350915050565b6120f861248d565b8161210281612176565b61211e5760405162461bcd60e51b8152600401610cd690615ae7565b6001600160a01b0382166121445760405162461bcd60e51b8152600401610cd690615c20565b5060009182526007602052604090912060050180546001600160a01b0319166001600160a01b03909216919091179055565b600090815260076020526040902060020154151590565b60606012805461219c90615914565b80601f01602080910402602001604051908101604052809291908181526020018280546121c890615914565b80156122155780601f106121ea57610100808354040283529160200191612215565b820191906000526020600020905b8154815290600101906020018083116121f857829003601f168201915b5050505050905090565b6001600160a01b03918216600090815260096020908152604080832093909416825291909152205460ff1690565b60008161225981612176565b6122755760405162461bcd60e51b8152600401610cd690615ae7565b505060009081526007602052604090206009015490565b336001600160a01b03861681148015906122ad57506122ab868261221f565b155b156122cf57808660405163711bec9160e11b8152600401610cd6929190615bdb565b6113758686868686613799565b6122e461248d565b6001600160a01b03811661230e576000604051631e4fbdf760e01b8152600401610cd69190614f91565b61150f81612edd565b61231f6125ce565b61232882612176565b6123445760405162461bcd60e51b8152600401610cd690615ae7565b61234d82611f9c565b6123995760405162461bcd60e51b815260206004820152601a60248201527f50726f6a656374206275726e206973206e6f74206163746976650000000000006044820152606401610cd6565b6123a4838383613813565b6123ae8383612b66565b81836001600160a01b03167f49995e5dd6158cf69ad3e9777c46755a1a826a446c6416992167462dad033b2a836040516123ea91815260200190565b60405180910390a3505050565b6016602052816000526040600020818154811061241357600080fd5b90600052602060002001600091509150505481565b60008161243481612176565b6124505760405162461bcd60e51b8152600401610cd690615ae7565b50506000908152600760208190526040909120015490565b60006001600160e01b03198216635a05180f60e01b1480610ba55750610ba58261386a565b6000546001600160a01b031633146113e4573360405163118cdaa760e01b8152600401610cd69190614f91565b606060006124c78361388f565b60010190506000816001600160401b038111156124e6576124e6614ea1565b6040519080825280601f01601f191660200182016040528015612510576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a850494508461251a57509392505050565b6101f46001600160601b03821611156125a75760405162461bcd60e51b815260206004820152601d60248201527f526f796174696573206572726f723a204c696d697420726561636865640000006044820152606401610cd6565b601580546bffffffffffffffffffffffff19166001600160601b0392909216919091179055565b6125d66115c0565b156113e45760405163d93c066560e01b815260040160405180910390fd5b846000036126145760405162461bcd60e51b8152600401610cd690615c49565b600086600181111561262857612628615051565b14806126455750600186600181111561264357612643615051565b145b6126885760405162461bcd60e51b8152602060048201526014602482015273496e76616c69642070726f6a656374207479706560601b6044820152606401610cd6565b836000036126c85760405162461bcd60e51b815260206004820152600d60248201526c496e76616c696420707269636560981b6044820152606401610cd6565b6001600160a01b0383166126ee5760405162461bcd60e51b8152600401610cd690615c20565b6005546001600160a01b039081169084160361271c5760405162461bcd60e51b8152600401610cd690615c20565b600086600181111561273057612730615051565b03612784578160ff166000036127845760405162461bcd60e51b8152602060048201526019602482015278125b9d985b1a5908199d5b991a5b99c81d1a1c995cda1bdb19603a1b6044820152606401610cd6565b61137581613967565b60035460009081906001600160a01b031663bf4cb8d98d8d60018111156127b6576127b6615051565b8d8d8d8d8d8d8d6040518a63ffffffff1660e01b81526004016127e199989796959493929190615c71565b602060405180830381865afa1580156127fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128229190615ce2565b905061282f818585613ab3565b9c9b505050505050505050505050565b604080518082018252878152600060208083019190915282516060810184526001600160a01b03888116825260055416918101919091523381840152825161014081019093529091808a600181111561289a5761289a615051565b81526020808201889052604080830186905260608301859052608083018b9052600060a0840181905260c0840181905260ff891660e085015261010084018190526101209093018790528d83526007909152902081518154829060ff19166001838181111561290b5761290b615051565b021790555060208281015180516129289260018501920190614d60565b50604082810151805160028401556020908101516003840155606084015180516004850180546001600160a01b03199081166001600160a01b039384161790915592820151600586018054851691831691909117905592015160068401805490921692169190911790556080820151600782015560a082015160088201805460c085015160e086015161ffff1990921693151561ff0019169390931761010093151584021762ff000019166201000060ff909216919091021790558201516009820155610120820151600a820190612a00908261598f565b50506005546040518c92507f4ae401670c03e01e55d6a1794356bb5f642ef93fae65259bae62f66b89baff0891612a4a918c918c918c916001600160a01b0316908c908c90615cfb565b60405180910390a250505050505050505050565b6002600f5403612a8157604051633ee5aeb560e01b815260040160405180910390fd5b6002600f55565b612a918261187f565b81612a9b84611c0f565b612aa59190615d4a565b1115612af35760405162461bcd60e51b815260206004820152601e60248201527f4d696e74696e67206d6f7265207468616e20746f74616c20737570706c7900006044820152606401610cd6565b60008281526007602052604081206003018054839290612b14908490615d4a565b90915550505050565b6001600160a01b038416612b47576000604051632bfa23e760e11b8152600401610cd69190614f91565b600080612b548585613b3c565b91509150611375600087848487613b64565b6000612b728383610b81565b1115612b8257610bea8282613bb7565b610bea8282613bf4565b6000805b8351811015612bd9578280519060200120848281518110612bb357612bb3615bf5565b60200260200101518051906020012003612bd1576001915050610ba5565b600101612b90565b5060009392505050565b6001600160a01b038416612c0d576000604051632bfa23e760e11b8152600401610cd69190614f91565b6001600160a01b038516612c36576000604051626a0d4560e21b8152600401610cd69190614f91565b612c438585858585613b64565b5050505050565b600080612c578484613cb8565b9050801561155d576000848152600e602052604090206114f69084613d4c565b600080612c848484613d61565b9050801561155d576000848152600e602052604090206114f69084613dce565b81612cae81612176565b612cca5760405162461bcd60e51b8152600401610cd690615ae7565b612cd26125ce565b612cda61248d565b60008211612cfa5760405162461bcd60e51b8152600401610cd690615d5d565b600083815260076020526040908190206009018390555183907ff39ce44c038f0d06bede207dec9961321e2b281f53902ef349174fcb1983eacc90612d429085815260200190565b60405180910390a2505050565b612d57613de3565b6000805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b604051612d949190614f91565b60405180910390a1565b6001600160a01b038116612dc45760405162461bcd60e51b8152600401610cd690615c20565b6005546001600160a01b0390811690821603612df25760405162461bcd60e51b8152600401610cd690615d85565b600580546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b038316612e3d576000604051626a0d4560e21b8152600401610cd69190614f91565b611181836000848460405180602001604052806000815250613b64565b6001600160a01b038116612ebb5760405162461bcd60e51b815260206004820152602260248201527f5369676e20616464726573732063616e2774206265207a65726f206164647265604482015261737360f01b6064820152608401610cd6565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80612f3781612176565b612f535760405162461bcd60e51b8152600401610cd690615ae7565b612f5b6125ce565b610bea82612f688461187f565b613e08565b612f756125ce565b6000805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258612d873390565b60035460405163b3e441f160e01b815260009182916001600160a01b039091169063b3e441f190612feb908a908a908a908a90600401615dbc565b602060405180830381865afa158015613008573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061302c9190615ce2565b9050613039818585613ab3565b979650505050505050565b6001600160a01b03821661306a5760405162461bcd60e51b8152600401610cd690615c20565b6005546001600160a01b03908116908316036130985760405162461bcd60e51b8152600401610cd690615c20565b610bea81613967565b816130ab81612176565b6130c75760405162461bcd60e51b8152600401610cd690615ae7565b6130cf6125ce565b6001600160a01b0382166130f55760405162461bcd60e51b8152600401610cd690615c20565b6000838152600760205260409020600401546001600160a01b03838116911614611181576000838152600760205260409081902060040180546001600160a01b0319166001600160a01b0385161790555183907f59a565e969359bbed0cf8abff93714dac9724bc7fcf5f182de4d5f93e3770a8490612d42908590614f91565b8161317f81612176565b61319b5760405162461bcd60e51b8152600401610cd690615ae7565b6131a36125ce565b61329260076000858152602001908152602001600020600101805480602002602001604051908101604052809291908181526020016000905b828210156132885783829060005260206000200180546131fb90615914565b80601f016020809104026020016040519081016040528092919081815260200182805461322790615914565b80156132745780601f1061324957610100808354040283529160200191613274565b820191906000526020600020905b81548152906001019060200180831161325757829003601f168201915b5050505050815260200190600101906131dc565b5050505083613ee7565b61118157600083815260076020908152604090912083516132bb92600190920191850190614d60565b50827f420e36f88d1abaa41f0ccfe94c9f74e77ab2cddbdeea7cf57ff9a6e6f60a99ab83604051612d429190615df7565b600061155d8383613f69565b6001600160a01b03811661331e5760405162461bcd60e51b8152600401610cd690615c20565b6003546001600160a01b039081169082160361334c5760405162461bcd60e51b8152600401610cd690615d85565b600380546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b03821661339757600060405162ced3e160e81b8152600401610cd69190614f91565b6001600160a01b03838116600081815260096020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3191016123ea565b6000610ba5825490565b8161341081612176565b61342c5760405162461bcd60e51b8152600401610cd690615ae7565b6134346125ce565b60008381526007602052604090206008015482151561010090910460ff161515036134a15760405162461bcd60e51b815260206004820152601e60248201527f53616d6520737461747573206173207468652063757272656e74206f6e6500006044820152606401610cd6565b600083815260076020908152604091829020600801805461ff001916610100861515908102919091179091558251868152918201527f9e44f4f67244847aeb2d9345bd11632c9680e9cafabb6b8ca80843bf0c148abb910160405180910390a1505050565b61350f82612176565b61352b5760405162461bcd60e51b8152600401610cd690615ae7565b80151561353783611f9c565b15150361357c5760405162461bcd60e51b8152602060048201526013602482015272109d5c9b881a5cc8185b1c9958591e481cd95d606a1b6044820152606401610cd6565b8061360157600082815260076020526040902060080154610100900460ff16156136015760405162461bcd60e51b815260206004820152603060248201527f50726f6a65637420726566756e64206973206163746976652c206275726e206360448201526f185b89dd08189948191a5cd8589b195960821b6064820152608401610cd6565b600082815260076020908152604091829020600801805460ff19168415159081179091558251858152918201527fc6e1840e043c6516f034b472cf4c33a9b38efe4e413a66f775ddc7b5dbc10c85910160405180910390a15050565b6001600160a01b0381166136cb5760405162461bcd60e51b815260206004820152602f60248201527f457863657074696f6e20696e20736574526f79616c746965734164647265737360448201526e1d1020b2323932b9b9903d32b9379760891b6064820152608401610cd6565b601580546001600160a01b03909216600160601b026001600160601b03909216919091179055565b816136fd81612176565b6137195760405162461bcd60e51b8152600401610cd690615ae7565b6137216125ce565b600082116137415760405162461bcd60e51b8152600401610cd690615d5d565b60008381526007602052604081206009018054849290613762908490615d4a565b909155505060405182815283907f0d789849b516cb66fc65abfb22583f7875df1d855b72ec028f5425aa92687f3190602001612d42565b6001600160a01b0384166137c3576000604051632bfa23e760e11b8152600401610cd69190614f91565b6001600160a01b0385166137ec576000604051626a0d4560e21b8152600401610cd69190614f91565b6000806137f98585613b3c565b9150915061380a8787848487613b64565b50505050505050565b6001600160a01b03831661383c576000604051626a0d4560e21b8152600401610cd69190614f91565b6000806138498484613b3c565b91509150612c43856000848460405180602001604052806000815250613b64565b60006001600160e01b03198216637965db0b60e01b1480610ba55750610ba582613f93565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b83106138ce5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef810000000083106138fa576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061391857662386f26fc10000830492506010015b6305f5e1008310613930576305f5e100830492506008015b612710831061394457612710830492506004015b60648310613956576064830492506002015b600a8310610ba55760010192915050565b60008151116139b25760405162461bcd60e51b8152602060048201526017602482015276496e76616c6964207061796d656e74206d6574686f647360481b6044820152606401610cd6565b60005b8151811015610bea5760045482516001600160a01b0390911690632bf3434d908490849081106139e7576139e7615bf5565b60200260200101516040518263ffffffff1660e01b8152600401613a0b9190614ff5565b602060405180830381865afa158015613a28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a4c9190615e0a565b613aab5760405162461bcd60e51b815260206004820152602a60248201527f5061796d656e74206d6574686f64206e6f7420737570706f7274656420627920604482015269746865206f7261636c6560b01b6064820152608401610cd6565b6001016139b5565b60008281526002602052604081205460ff1615613b075760405162461bcd60e51b8152602060048201526012602482015271139bdb98d948185b1c9958591e481d5cd95960721b6044820152606401610cd6565b6000613b138584613fe3565b905080156112bb576000848152600260205260409020805460ff19166001179055949350505050565b6040805160018082526020820194909452808201938452606081019290925260808201905291565b613b7085858585614009565b6001600160a01b03841615612c435782513390600103613ba95760208481015190840151613ba2838989858589614400565b5050611375565b611375818787878787614512565b6001600160a01b0382166000908152601660205260409020613bd981836145f2565b61118157805460018101825560009182526020909120015550565b6001600160a01b0382166000908152601660205260408120805490915b81811015612c435783838281548110613c2c57613c2c615bf5565b906000526020600020015403613cb05782613c48600184615e27565b81548110613c5857613c58615bf5565b9060005260206000200154838281548110613c7557613c75615bf5565b906000526020600020018190555082805480613c9357613c93615e3a565b600190038181906000526020600020016000905590555050505050565b600101613c11565b6000613cc483836118be565b613d44576000838152600d602090815260408083206001600160a01b03861684529091529020805460ff19166001179055613cfc3390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610ba5565b506000610ba5565b600061155d836001600160a01b038416614642565b6000613d6d83836118be565b15613d44576000838152600d602090815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610ba5565b600061155d836001600160a01b038416614689565b613deb6115c0565b6113e457604051638dfc202b60e01b815260040160405180910390fd5b81613e1281612176565b613e2e5760405162461bcd60e51b8152600401610cd690615ae7565b613e366125ce565b81600003613e565760405162461bcd60e51b8152600401610cd690615c49565b600083815260076020526040902060030154821015613e875760405162461bcd60e51b8152600401610cd690615c49565b600083815260076020526040902060020154821461118157600083815260076020526040908190206002018390555183907f55c11c5539296e31386179e5eb5a807c9f67f27a6cb0e47c614723a5d8e0013d90612d429085815260200190565b60008151835114613efa57506000610ba5565b60005b8351811015613f5f57828181518110613f1857613f18615bf5565b602002602001015180519060200120848281518110613f3957613f39615bf5565b60200260200101518051906020012014613f57576000915050610ba5565b600101613efd565b5060019392505050565b6000826000018281548110613f8057613f80615bf5565b9060005260206000200154905092915050565b60006001600160e01b03198216636cdb3d1360e11b1480613fc457506001600160e01b031982166303a24d0760e21b145b80610ba557506301ffc9a760e01b6001600160e01b0319831614610ba5565b600080613ff0848461477c565b6001546001600160a01b03918216911614949350505050565b6140116125ce565b6001600160a01b0384161580159061403157506001600160a01b03831615155b801561404f5750826001600160a01b0316846001600160a01b031614155b156143ee576000805b83518110156143eb576140848685838151811061407757614077615bf5565b6020026020010151612b66565b61409a8585838151811061407757614077615bf5565b6000600760008684815181106140b2576140b2615bf5565b60200260200101518152602001908152602001600020604051806101400160405290816000820160009054906101000a900460ff1660018111156140f8576140f8615051565b600181111561410957614109615051565b815260200160018201805480602002602001604051908101604052809291908181526020016000905b828210156141de57838290600052602060002001805461415190615914565b80601f016020809104026020016040519081016040528092919081815260200182805461417d90615914565b80156141ca5780601f1061419f576101008083540402835291602001916141ca565b820191906000526020600020905b8154815290600101906020018083116141ad57829003601f168201915b505050505081526020019060010190614132565b505050908252506040805180820182526002840154815260038401546020828101919091528084019190915281516060808201845260048601546001600160a01b039081168352600587015481169383019390935260068601549092168184015291830191909152600783015490820152600882015460ff808216151560808401526101008083048216151560a0850152620100009092041660c0830152600983015460e0830152600a8301805491909201919061429b90615914565b80601f01602080910402602001604051908101604052809291908181526020018280546142c790615914565b80156143145780601f106142e957610100808354040283529160200191614314565b820191906000526020600020905b8154815290600101906020018083116142f757829003601f168201915b50505050508152505090508060600151602001519250826001600160a01b031663ef704c83888888868151811061434d5761434d615bf5565b602002602001015188878151811061436757614367615bf5565b60209081029190910101516040516001600160e01b031960e087901b1681526001600160a01b03948516600482015293909216602484015260448301526064820152608401600060405180830381600087803b1580156143c657600080fd5b505af11580156143da573d6000803e3d6000fd5b505060019093019250614058915050565b50505b6143fa848484846147a6565b50505050565b6001600160a01b0384163b156113755760405163f23a6e6160e01b81526001600160a01b0385169063f23a6e61906144449089908990889088908890600401615e50565b6020604051808303816000875af192505050801561447f575060408051601f3d908101601f1916820190925261447c91810190615e8a565b60015b6144df573d8080156144ad576040519150601f19603f3d011682016040523d82523d6000602084013e6144b2565b606091505b5080516000036144d75784604051632bfa23e760e11b8152600401610cd69190614f91565b805181602001fd5b6001600160e01b0319811663f23a6e6160e01b1461380a5784604051632bfa23e760e11b8152600401610cd69190614f91565b6001600160a01b0384163b156113755760405163bc197c8160e01b81526001600160a01b0385169063bc197c81906145569089908990889088908890600401615ea7565b6020604051808303816000875af1925050508015614591575060408051601f3d908101601f1916820190925261458e91810190615e8a565b60015b6145bf573d8080156144ad576040519150601f19603f3d011682016040523d82523d6000602084013e6144b2565b6001600160e01b0319811663bc197c8160e01b1461380a5784604051632bfa23e760e11b8152600401610cd69190614f91565b8154600090815b81811015614637578385828154811061461457614614615bf5565b90600052602060002001540361462f57600192505050610ba5565b6001016145f9565b506000949350505050565b6000818152600183016020526040812054613d4457508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610ba5565b600081815260018301602052604081205480156147725760006146ad600183615e27565b85549091506000906146c190600190615e27565b90508082146147265760008660000182815481106146e1576146e1615bf5565b906000526020600020015490508087600001848154811061470457614704615bf5565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061473757614737615e3a565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610ba5565b6000915050610ba5565b60008060008061478c86866148ec565b92509250925061479c8282614939565b5090949350505050565b6147b2848484846149f2565b6001600160a01b03841661485c576000805b83518110156148425760008382815181106147e1576147e1615bf5565b6020026020010151905080600b600087858151811061480257614802615bf5565b6020026020010151815260200190815260200160002060008282546148279190615d4a565b9091555061483790508184615d4a565b9250506001016147c4565b5080600c60008282546148559190615d4a565b9091555050505b6001600160a01b0383166143fa576000805b83518110156148db57600083828151811061488b5761488b615bf5565b6020026020010151905080600b60008785815181106148ac576148ac615bf5565b60209081029190910181015182528101919091526040016000208054919091039055919091019060010161486e565b50600c805491909103905550505050565b600080600083516041036149265760208401516040850151606086015160001a61491888828585614c0c565b955095509550505050614932565b50508151600091506002905b9250925092565b600082600381111561494d5761494d615051565b03614956575050565b600182600381111561496a5761496a615051565b036149885760405163f645eedf60e01b815260040160405180910390fd5b600282600381111561499c5761499c615051565b036149bd5760405163fce698f760e01b815260048101829052602401610cd6565b60038260038111156149d1576149d1615051565b03610bea576040516335e2f38360e21b815260048101829052602401610cd6565b8051825114614a215781518151604051635b05999160e01b815260048101929092526024820152604401610cd6565b3360005b8351811015614b2d576020818102858101820151908501909101516001600160a01b03881615614adc5760008281526008602090815260408083206001600160a01b038c16845290915290205481811015614ab3576040516303dee4c560e01b81526001600160a01b038a166004820152602481018290526044810183905260648101849052608401610cd6565b60008381526008602090815260408083206001600160a01b038d16845290915290209082900390555b6001600160a01b03871615614b235760008281526008602090815260408083206001600160a01b038b16845290915281208054839290614b1d908490615d4a565b90915550505b5050600101614a25565b508251600103614bae5760208301516000906020840151909150856001600160a01b0316876001600160a01b0316846001600160a01b03167fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f628585604051614b9f929190918252602082015260400190565b60405180910390a45050612c43565b836001600160a01b0316856001600160a01b0316826001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8686604051614bfd929190615865565b60405180910390a45050505050565b600080806fa2a8918ca85bafe22016d0b997e4df60600160ff1b03841115614c3d5750600091506003905082614cc7565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015614c91573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116614cbd57506000925060019150829050614cc7565b9250600091508190505b9450945094915050565b604080516101408101909152806000815260200160608152602001614d09604051806040016040528060008152602001600081525090565b8152604080516060810182526000808252602082810182905292820152910190815260200160008152602001600015158152602001600015158152602001600060ff16815260200160008152602001606081525090565b828054828255906000526020600020908101928215614da6579160200282015b82811115614da65782518290614d96908261598f565b5091602001919060010190614d80565b50614db2929150614db6565b5090565b80821115614db2576000614dca8282614dd3565b50600101614db6565b508054614ddf90615914565b6000825580601f10614def575050565b601f01602090049060005260206000209081019061150f91905b80821115614db25760008155600101614e09565b6001600160a01b038116811461150f57600080fd5b8035614e3d81614e1d565b919050565b60008060408385031215614e5557600080fd5b8235614e6081614e1d565b946020939093013593505050565b6001600160e01b03198116811461150f57600080fd5b600060208284031215614e9657600080fd5b813561155d81614e6e565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715614edf57614edf614ea1565b604052919050565b600082601f830112614ef857600080fd5b8135602083016000806001600160401b03841115614f1857614f18614ea1565b50601f8301601f1916602001614f2d81614eb7565b915050828152858383011115614f4257600080fd5b82826020830137600092810160200192909252509392505050565b600060208284031215614f6f57600080fd5b81356001600160401b03811115614f8557600080fd5b6112bb84828501614ee7565b6001600160a01b0391909116815260200190565b60005b83811015614fc0578181015183820152602001614fa8565b50506000910152565b60008151808452614fe1816020860160208601614fa5565b601f01601f19169290920160200192915050565b60208152600061155d6020830184614fc9565b60006020828403121561501a57600080fd5b5035919050565b6000806040838503121561503457600080fd5b82359150602083013561504681614e1d565b809150509250929050565b634e487b7160e01b600052602160045260246000fd5b6002811061508557634e487b7160e01b600052602160045260246000fd5b9052565b80516001600160a01b03908116835260208083015182169084015260409182015116910152565b6150ba818b615067565b6150d1602082018a80518252602090810151910152565b6150de6060820189615089565b8660c082015285151560e082015284151561010082015260ff841661012082015282610140820152610180610160820152600061511f610180830184614fc9565b9b9a5050505050505050505050565b60006020828403121561514057600080fd5b81356001600160601b038116811461155d57600080fd5b803560028110614e3d57600080fd5b60006001600160401b0382111561517f5761517f614ea1565b5060051b60200190565b600082601f83011261519a57600080fd5b81356151ad6151a882615166565b614eb7565b8082825260208201915060208360051b8601019250858311156151cf57600080fd5b602085015b838110156152105780356001600160401b038111156151f257600080fd5b615201886020838a0101614ee7565b845250602092830192016151d4565b5095945050505050565b803560ff81168114614e3d57600080fd5b6000806000806000806000806000806101408b8d03121561524b57600080fd5b8a35995061525b60208c01615157565b985060408b0135975060608b0135965061527760808c01614e32565b955060a08b01356001600160401b0381111561529257600080fd5b61529e8d828e01615189565b9550506152ad60c08c0161521a565b935060e08b01356001600160401b038111156152c857600080fd5b6152d48d828e01614ee7565b9350506101008b013591506101208b01356001600160401b038111156152f957600080fd5b6153058d828e01614ee7565b9150509295989b9194979a5092959850565b60008060006060848603121561532c57600080fd5b833561533781614e1d565b95602085013595506040909401359392505050565b6000806040838503121561535f57600080fd5b8235915060208301356001600160401b0381111561537c57600080fd5b61538885828601614ee7565b9150509250929050565b600080604083850312156153a557600080fd5b50508035926020909101359150565b600082601f8301126153c557600080fd5b81356153d36151a882615166565b8082825260208201915060208360051b8601019250858311156153f557600080fd5b602085015b838110156152105780358352602092830192016153fa565b600080600080600060a0868803121561542a57600080fd5b853561543581614e1d565b9450602086013561544581614e1d565b935060408601356001600160401b0381111561546057600080fd5b61546c888289016153b4565b93505060608601356001600160401b0381111561548857600080fd5b615494888289016153b4565b92505060808601356001600160401b038111156154b057600080fd5b6154bc88828901614ee7565b9150509295509295909350565b600080604083850312156154dc57600080fd5b82356001600160401b038111156154f257600080fd5b8301601f8101851361550357600080fd5b80356155116151a882615166565b8082825260208201915060208360051b85010192508783111561553357600080fd5b6020840193505b8284101561555e57833561554d81614e1d565b82526020938401939091019061553a565b945050505060208301356001600160401b0381111561557c57600080fd5b615388858286016153b4565b600081518084526020840193506020830160005b828110156155ba57815186526020958601959091019060010161559c565b5093949350505050565b60208152600061155d6020830184615588565b6000602082840312156155e957600080fd5b813561155d81614e1d565b60008060006060848603121561560957600080fd5b833561561481614e1d565b925060208401356001600160401b0381111561562f57600080fd5b61563b868287016153b4565b92505060408401356001600160401b0381111561565757600080fd5b615663868287016153b4565b9150509250925092565b600080600080600060a0868803121561568557600080fd5b85359450602086013561569781614e1d565b935060408601356001600160401b038111156156b257600080fd5b6156be88828901615189565b9350506060860135915060808601356001600160401b038111156154b057600080fd5b801515811461150f57600080fd5b6000806040838503121561570257600080fd5b823561570d81614e1d565b91506020830135615046816156e1565b600082825180855260208501945060208160051b8301016020850160005b8381101561576d57601f19858403018852615757838351614fc9565b602098890198909350919091019060010161573b565b50909695505050505050565b6020815261578b602082018351615067565b600060208301516101a060408401526157a86101c084018261571d565b604085015180516060860152602081015160808601529091505060608401516157d460a0850182615089565b50608084015161010084015260a08401518015156101208501525060c08401518015156101408501525060e084015160ff811661016085015250610100840151610180840152610120840151838203601f19016101a08501526158378282614fc9565b95945050505050565b6000806040838503121561585357600080fd5b823591506020830135615046816156e1565b6040815260006158786040830185615588565b82810360208401526158378185615588565b6000806040838503121561589d57600080fd5b82356158a881614e1d565b9150602083013561504681614e1d565b600080600080600060a086880312156158d057600080fd5b85356158db81614e1d565b945060208601356158eb81614e1d565b9350604086013592506060860135915060808601356001600160401b038111156154b057600080fd5b600181811c9082168061592857607f821691505b60208210810361142c57634e487b7160e01b600052602260045260246000fd5b601f82111561118157806000526020600020601f840160051c8101602085101561596f5750805b601f840160051c820191505b81811015612c43576000815560010161597b565b81516001600160401b038111156159a8576159a8614ea1565b6159bc816159b68454615914565b84615948565b6020601f8211600181146159f057600083156159d85750848201515b600019600385901b1c1916600184901b178455612c43565b600084815260208120601f198516915b82811015615a205787850151825560209485019460019092019101615a00565b5084821015615a3e5786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b60008154615a5a81615914565b600182168015615a715760018114615a8657615ab6565b60ff1983168652811515820286019350615ab6565b84600052602060002060005b83811015615aae57815488820152600190910190602001615a92565b505081860193505b50505092915050565b6000615acb8286615a4d565b8451615adb818360208901614fa5565b61303981830186615a4d565b60208082526012908201527150726f6a656374206e6f742065786973747360701b604082015260600190565b6020808252601b908201527f5369676e61747572652076616c69646174696f6e206661696c65640000000000604082015260600190565b60208082526022908201527f4f6e6c79206d696e7465722063616e2063616c6c20746869732066756e63746960408201526137b760f11b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610ba557610ba5615b8c565b600082615bd657634e487b7160e01b600052601260045260246000fd5b500490565b6001600160a01b0392831681529116602082015260400190565b634e487b7160e01b600052603260045260246000fd5b60006112bb615c1a8386615a4d565b84615a4d565b6020808252600f908201526e496e76616c6964206164647265737360881b604082015260600190565b6020808252600e908201526d496e76616c696420737570706c7960901b604082015260600190565b89815260ff8916602082015287604082015286606082015260018060a01b038616608082015261012060a08201526000615caf61012083018761571d565b60ff861660c084015282810360e0840152615cca8186614fc9565b915050826101008301529a9950505050505050505050565b600060208284031215615cf457600080fd5b5051919050565b868152602081018690526001600160a01b0385811660408301528416606082015260c060808201819052600090615d349083018561571d565b905060ff831660a0830152979650505050505050565b80820180821115610ba557610ba5615b8c565b6020808252600e908201526d125b9d985b1a5908185b5bdd5b9d60921b604082015260600190565b6020808252601f908201527f53616d652061646472657373206173207468652063757272656e74206f6e6500604082015260600190565b8481526001600160a01b0384166020820152608060408201819052600090615de69083018561571d565b905082606083015295945050505050565b60208152600061155d602083018461571d565b600060208284031215615e1c57600080fd5b815161155d816156e1565b81810381811115610ba557610ba5615b8c565b634e487b7160e01b600052603160045260246000fd5b6001600160a01b03868116825285166020820152604081018490526060810183905260a06080820181905260009061303990830184614fc9565b600060208284031215615e9c57600080fd5b815161155d81614e6e565b6001600160a01b0386811682528516602082015260a060408201819052600090615ed390830186615588565b8281036060840152615ee58186615588565b90508281036080840152615ef98185614fc9565b9897505050505050505056fe43f25613eb2f15fb17222a5d424ca2655743e71265d98e4b93c05e5fb589ecdea264697066735822122059e50b6194d0d64b89a52558674ac691179908b12b29a6208764aed6b7c0a69064736f6c634300081b0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000004ae302bd39270eca591a664ce1adcfe7eaf5b00b000000000000000000000000000000000000000000000000000000000000006400000000000000000000000080f6130ff755803a053279fbacbdf34d0b4e213a0000000000000000000000006f71566da9dea7e8f285ba430ded3aeeca49430c000000000000000000000000000000000000000000000000000000000000000a454c565820546f6b656e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004454c565800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f68747470733a2f2f63646e2e656c657665782e61692f70726f6a656374732f00000000000000000000000000000000000000000000000000000000000000002c68747470733a2f2f63646e2e656c657665782e61692f636f6e74726163742f6d657461646174612e6a736f6e0000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _name (string): ELVX Token
Arg [1] : _symbol (string): ELVX
Arg [2] : _baseUri (string): https://cdn.elevex.ai/projects/
Arg [3] : _collectionUri (string): https://cdn.elevex.ai/contract/metadata.json
Arg [4] : _royaltyAddress (address): 0x4aE302bD39270eCa591a664CE1aDCFE7eaF5b00b
Arg [5] : _royaltyPoints (uint96): 100
Arg [6] : _msgGeneratorAddress (address): 0x80F6130Ff755803a053279FBACBdF34d0b4e213a
Arg [7] : _oracleAddress (address): 0x6F71566Da9dEa7E8f285BA430Ded3aeecA49430C
-----Encoded View---------------
17 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000100
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000140
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000180
Arg [3] : 00000000000000000000000000000000000000000000000000000000000001c0
Arg [4] : 0000000000000000000000004ae302bd39270eca591a664ce1adcfe7eaf5b00b
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000064
Arg [6] : 00000000000000000000000080f6130ff755803a053279fbacbdf34d0b4e213a
Arg [7] : 0000000000000000000000006f71566da9dea7e8f285ba430ded3aeeca49430c
Arg [8] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [9] : 454c565820546f6b656e00000000000000000000000000000000000000000000
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [11] : 454c565800000000000000000000000000000000000000000000000000000000
Arg [12] : 000000000000000000000000000000000000000000000000000000000000001f
Arg [13] : 68747470733a2f2f63646e2e656c657665782e61692f70726f6a656374732f00
Arg [14] : 000000000000000000000000000000000000000000000000000000000000002c
Arg [15] : 68747470733a2f2f63646e2e656c657665782e61692f636f6e74726163742f6d
Arg [16] : 657461646174612e6a736f6e0000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.