Source Code
Latest 25 from a total of 2,079 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Log Hotdog | 39161756 | 126 days ago | IN | 0 ETH | 0.00000326 | ||||
| Log Hotdog On Be... | 38534425 | 140 days ago | IN | 0 ETH | 0.00000718 | ||||
| Log Hotdog On Be... | 38534317 | 140 days ago | IN | 0 ETH | 0.00000741 | ||||
| Log Hotdog | 38181200 | 148 days ago | IN | 0 ETH | 0.00000924 | ||||
| Log Hotdog On Be... | 37186667 | 171 days ago | IN | 0 ETH | 0.0000117 | ||||
| Log Hotdog On Be... | 37186642 | 171 days ago | IN | 0 ETH | 0.00001176 | ||||
| Log Hotdog On Be... | 37058371 | 174 days ago | IN | 0 ETH | 0.00000666 | ||||
| Log Hotdog On Be... | 35837137 | 203 days ago | IN | 0 ETH | 0.00001746 | ||||
| Log Hotdog On Be... | 35731404 | 205 days ago | IN | 0 ETH | 0.00000949 | ||||
| Log Hotdog On Be... | 35724155 | 205 days ago | IN | 0 ETH | 0.00000982 | ||||
| Log Hotdog On Be... | 35203055 | 217 days ago | IN | 0 ETH | 0.00000644 | ||||
| Log Hotdog On Be... | 35201179 | 217 days ago | IN | 0 ETH | 0.00000669 | ||||
| Log Hotdog On Be... | 34990977 | 222 days ago | IN | 0 ETH | 0.00000674 | ||||
| Log Hotdog On Be... | 34990962 | 222 days ago | IN | 0 ETH | 0.00000666 | ||||
| Log Hotdog On Be... | 34990951 | 222 days ago | IN | 0 ETH | 0.00000665 | ||||
| Log Hotdog On Be... | 34990936 | 222 days ago | IN | 0 ETH | 0.00000664 | ||||
| Log Hotdog On Be... | 34988785 | 222 days ago | IN | 0 ETH | 0.00000685 | ||||
| Log Hotdog On Be... | 34984587 | 222 days ago | IN | 0 ETH | 0.0000064 | ||||
| Log Hotdog On Be... | 34984567 | 222 days ago | IN | 0 ETH | 0.0000064 | ||||
| Log Hotdog On Be... | 34984498 | 222 days ago | IN | 0 ETH | 0.00000643 | ||||
| Log Hotdog On Be... | 34984194 | 222 days ago | IN | 0 ETH | 0.00000657 | ||||
| Log Hotdog On Be... | 34984174 | 222 days ago | IN | 0 ETH | 0.00000658 | ||||
| Log Hotdog On Be... | 34976296 | 223 days ago | IN | 0 ETH | 0.00000675 | ||||
| Log Hotdog On Be... | 34976270 | 223 days ago | IN | 0 ETH | 0.00000677 | ||||
| Log Hotdog On Be... | 34976234 | 223 days ago | IN | 0 ETH | 0.00000674 |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
LogADog
Compiler Version
v0.8.30+commit.73712a01
Optimization Enabled:
No with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "./AttestationManager.sol";
import "./CoinDeploymentManager.sol";
/**
* @title LogADog
* @dev The worlds first onchain hotdog eating competition
*/
contract LogADog is AccessControl {
// Role for operators who can act on behalf of users
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
// Platform referrer address (can be updated by owner)
address public platformReferrer;
// Attestation manager contract
AttestationManager public attestationManager;
// Coin deployment manager contract
CoinDeploymentManager public coinDeploymentManager;
struct HotdogLog {
uint256 logId;
string imageUri;
string metadataUri;
uint256 timestamp;
address eater;
address logger;
address zoraCoin; // Address of the Zora coin created for this log
}
struct Attestation {
address attestor;
bool isValid;
}
HotdogLog[] public hotdogLogs;
mapping(uint256 => Attestation[]) public attestations;
mapping(uint256 => mapping(address => bool)) public hasAttested;
event HotdogLogged(uint256 indexed logId, address indexed logger, address indexed eater, string imageUri, string metadataUri, uint256 timestamp, address zoraCoin);
event AttestationMade(uint256 indexed logId, address indexed attestor, bool isValid);
event HotdogLogRevoked(uint256 indexed logId, address indexed logger);
event AttestationRevoked(uint256 indexed logId, address indexed attestor);
event OperatorAdded(address indexed operator);
event OperatorRemoved(address indexed operator);
event DogValidityUpdated(uint256 indexed logId, address indexed eater, bool isValid, uint256 timestamp);
modifier onlyLogOwner(uint256 logId) {
require(hotdogLogs[logId].eater == msg.sender, "Caller is not the owner of this log");
_;
}
modifier operatorOnly() {
require(hasRole(OPERATOR_ROLE, msg.sender), "Caller is not an operator");
_;
}
constructor(address _platformReferrer, address _attestationManager, address _coinDeploymentManager) {
platformReferrer = _platformReferrer;
attestationManager = AttestationManager(_attestationManager);
coinDeploymentManager = CoinDeploymentManager(_coinDeploymentManager);
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(OPERATOR_ROLE, 0x360E36BEFcC2DB9C45e411E5E4840FE33a8f21B0);
}
function setPlatformReferrer(address _platformReferrer) external onlyRole(DEFAULT_ADMIN_ROLE) {
platformReferrer = _platformReferrer;
}
function setAttestationManager(address _attestationManager) external onlyRole(DEFAULT_ADMIN_ROLE) {
attestationManager = AttestationManager(_attestationManager);
}
function setCoinDeploymentManager(address _coinDeploymentManager) external onlyRole(DEFAULT_ADMIN_ROLE) {
coinDeploymentManager = CoinDeploymentManager(_coinDeploymentManager);
}
function addOperator(address operator) external onlyRole(DEFAULT_ADMIN_ROLE) {
grantRole(OPERATOR_ROLE, operator);
emit OperatorAdded(operator);
}
function removeOperator(address operator) external onlyRole(DEFAULT_ADMIN_ROLE) {
revokeRole(OPERATOR_ROLE, operator);
emit OperatorRemoved(operator);
}
/**
* @notice Logs a hotdog with an image URI, metadata URI, and specified owner.
*/
function logHotdog(string memory imageUri, string memory metadataUri, string memory coinUri, address eater, bytes calldata poolConfig) external payable returns (uint256 logId) {
require(eater == msg.sender, "Can only log hotdogs for yourself");
return _logHotdog(imageUri, metadataUri, coinUri, eater, poolConfig);
}
/**
* @notice Logs a hotdog on behalf of another user (operator only)
*/
function logHotdogOnBehalf(string memory imageUri, string memory metadataUri, string memory coinUri, address eater, bytes calldata poolConfig) external payable operatorOnly returns (uint256 logId) {
return _logHotdog(imageUri, metadataUri, coinUri, eater, poolConfig);
}
function _logHotdog(string memory imageUri, string memory metadataUri, string memory coinUri, address eater, bytes calldata poolConfig) internal returns (uint256 logId) {
logId = hotdogLogs.length;
// Deploy coin using CoinDeploymentManager
address coinAddress;
if (address(coinDeploymentManager) != address(0)) {
coinAddress = coinDeploymentManager.deployCoin{value: msg.value}(
logId,
eater,
coinUri,
poolConfig,
platformReferrer
);
}
// Log the dog
hotdogLogs.push(HotdogLog({
logId: logId,
imageUri: imageUri,
metadataUri: metadataUri,
timestamp: block.timestamp,
eater: eater,
logger: msg.sender,
zoraCoin: coinAddress
}));
// Start attestation period if attestation manager is set
if (address(attestationManager) != address(0)) {
attestationManager.startAttestationPeriod(logId);
}
emit HotdogLogged(logId, msg.sender, eater, imageUri, metadataUri, block.timestamp, coinAddress);
}
/**
* @notice Attests to the validity of a hotdog log using the new staking system
*/
function attestHotdogLog(uint256 /* logId */, bool /* isValid */, uint256 /* stakeAmount */) external pure {
revert("Please call attestToLog directly on the AttestationManager contract");
}
/**
* @notice Attests to the validity of a hotdog log on behalf of another user (operator only)
*/
function attestHotdogLogOnBehalf(uint256 logId, address attestor, bool isValid) external operatorOnly {
require(hotdogLogs[logId].eater != attestor, "Cannot attest to own log");
_attestHotdogLog(logId, attestor, isValid);
}
/**
* @notice Resolve an attestation period after it has ended
*/
function resolveAttestationPeriod(uint256 logId) external {
require(address(attestationManager) != address(0), "Attestation manager not set");
attestationManager.resolveAttestationPeriod(logId);
}
function _attestHotdogLog(uint256 logId, address attestor, bool isValid) internal {
bool attestationFound = false;
for (uint256 i = 0; i < attestations[logId].length; i++) {
if (attestations[logId][i].attestor == attestor) {
if (attestations[logId][i].isValid != isValid) {
attestations[logId][i].isValid = isValid;
emit AttestationMade(logId, attestor, isValid);
}
attestationFound = true;
break;
}
}
if (!attestationFound) {
attestations[logId].push(Attestation({attestor: attestor, isValid: isValid}));
hasAttested[logId][attestor] = true;
emit AttestationMade(logId, attestor, isValid);
}
}
/**
* @notice Revokes a hotdog log.
*/
function revokeHotdogLog(uint256 logId) external onlyLogOwner(logId) {
delete hotdogLogs[logId];
emit HotdogLogRevoked(logId, msg.sender);
}
/**
* @notice Revokes a hotdog log on behalf of another user (operator only)
*/
function revokeHotdogLogOnBehalf(uint256 logId, address owner) external operatorOnly {
require(hotdogLogs[logId].eater == owner, "Target is not the owner of this log");
delete hotdogLogs[logId];
emit HotdogLogRevoked(logId, owner);
}
/**
* @notice Revokes an attestation to a hotdog log.
*/
function revokeAttestation(uint256 logId) external {
require(hasAttested[logId][msg.sender], "Caller has not attested to this log");
_revokeAttestation(logId, msg.sender);
}
/**
* @notice Revokes an attestation on behalf of another user (operator only)
*/
function revokeAttestationOnBehalf(uint256 logId, address attestor) external operatorOnly {
require(hasAttested[logId][attestor], "Target has not attested to this log");
_revokeAttestation(logId, attestor);
}
function _revokeAttestation(uint256 logId, address attestor) internal {
uint256 index = _findAttestationIndex(logId, attestor);
_removeAttestation(logId, index);
hasAttested[logId][attestor] = false;
emit AttestationRevoked(logId, attestor);
}
/**
* @notice Gets basic info about hotdog logs (simplified version)
*/
function getHotdogLogsCount() external view returns (uint256) {
return hotdogLogs.length;
}
/**
* @notice Gets a single hotdog log by ID
*/
function getHotdogLog(uint256 logId) external view returns (HotdogLog memory log, uint256 validCount, uint256 invalidCount) {
require(logId < hotdogLogs.length, "Log does not exist");
log = hotdogLogs[logId];
(validCount, invalidCount) = _countAttestations(logId);
}
/**
* @notice Gets all hotdog logs logged by a specific user (simplified)
*/
function getUserHotdogLogCount(address user) external view returns (uint256 count) {
for (uint256 i = 0; i < hotdogLogs.length; i++) {
if (hotdogLogs[i].eater == user) {
count++;
}
}
}
/**
* @notice Gets a range of hotdog logs (basic pagination)
*/
function getHotdogLogsRange(uint256 start, uint256 limit) external view returns (HotdogLog[] memory logs) {
require(start < hotdogLogs.length, "Start index out of bounds");
uint256 end = start + limit;
if (end > hotdogLogs.length) {
end = hotdogLogs.length;
}
logs = new HotdogLog[](end - start);
for (uint256 i = start; i < end; i++) {
logs[i - start] = hotdogLogs[i];
}
}
/**
* @notice Gets paginated hotdog logs submitted between the specified start and end times along with attestation counts and user attestation status.
* @param startTime The start time for the query.
* @param endTime The end time for the query.
* @param user The address of the user to check for attestations.
* @param start The starting index for pagination.
* @param limit The maximum number of logs to return.
* @return logs An array of hotdog logs submitted between the specified times.
* @return validCounts An array of valid attestation counts corresponding to each log.
* @return invalidCounts An array of invalid attestation counts corresponding to each log.
* @return userHasAttested An array indicating whether the user has attested to each log.
* @return userAttestations An array indicating the value of the user's attestation for each log.
*/
function getHotdogLogs(uint256 startTime, uint256 endTime, address user, uint256 start, uint256 limit) external view returns (HotdogLog[] memory logs, uint256[] memory validCounts, uint256[] memory invalidCounts, bool[] memory userHasAttested, bool[] memory userAttestations) {
uint256 totalLogs = 0;
for (uint256 i = 0; i < hotdogLogs.length; i++) {
if (hotdogLogs[i].timestamp >= startTime && hotdogLogs[i].timestamp <= endTime) {
totalLogs++;
}
}
uint256 resultCount = totalLogs > start ? totalLogs - start : 0;
resultCount = resultCount > limit ? limit : resultCount;
logs = new HotdogLog[](resultCount);
validCounts = new uint256[](resultCount);
invalidCounts = new uint256[](resultCount);
userHasAttested = new bool[](resultCount);
userAttestations = new bool[](resultCount);
uint256 index = 0;
uint256 skipped = 0;
for (uint256 i = hotdogLogs.length; i > 0 && index < resultCount; i--) {
if (hotdogLogs[i - 1].timestamp >= startTime && hotdogLogs[i - 1].timestamp <= endTime) {
if (skipped >= start) {
logs[index] = hotdogLogs[i - 1];
(validCounts[index], invalidCounts[index]) = _countAttestations(i - 1);
userHasAttested[index] = hasAttested[i - 1][user];
if (userHasAttested[index]) {
userAttestations[index] = _getUserAttestation(i - 1, user);
}
index++;
} else {
skipped++;
}
}
}
}
/**
* @notice Gets the total number of pages for hotdog logs within the specified time range.
* @param startTime The start time for the query.
* @param endTime The end time for the query.
* @param pageSize The number of logs per page.
* @return totalPages The total number of pages.
*/
function getTotalPagesForLogs(uint256 startTime, uint256 endTime, uint256 pageSize) external view returns (uint256 totalPages) {
uint256 totalLogs = 0;
for (uint256 i = 0; i < hotdogLogs.length; i++) {
if (hotdogLogs[i].timestamp >= startTime && hotdogLogs[i].timestamp <= endTime) {
totalLogs++;
}
}
totalPages = (totalLogs + pageSize - 1) / pageSize; // Round up division
}
function _getUserAttestation(uint256 logId, address user) internal view returns (bool) {
for (uint256 i = 0; i < attestations[logId].length; i++) {
if (attestations[logId][i].attestor == user) {
return attestations[logId][i].isValid;
}
}
revert("User attestation not found");
}
function _findAttestationIndex(uint256 logId, address attestor) internal view returns (uint256 index) {
for (uint256 i = 0; i < attestations[logId].length; i++) {
if (attestations[logId][i].attestor == attestor) {
return i;
}
}
revert("Attestation not found");
}
function _removeAttestation(uint256 logId, uint256 index) internal {
uint256 lastIndex = attestations[logId].length - 1;
if (index != lastIndex) {
attestations[logId][index] = attestations[logId][lastIndex];
}
attestations[logId].pop();
}
function _isValidLog(uint256 logId) internal view returns (bool) {
// Check if attestation manager is set and has resolved this log
if (address(attestationManager) != address(0)) {
(,, AttestationManager.AttestationStatus status,,, bool isValid,) = attestationManager.getAttestationPeriod(logId);
if (status == AttestationManager.AttestationStatus.Resolved) {
return isValid;
}
}
// Fallback to old attestation system
(uint256 validCount, uint256 invalidCount) = _countAttestations(logId);
return validCount >= invalidCount;
}
function _countAttestations(uint256 logId) internal view returns (uint256 validCount, uint256 invalidCount) {
for (uint256 i = 0; i < attestations[logId].length; i++) {
if (attestations[logId][i].isValid) {
validCount++;
} else {
invalidCount++;
}
}
}
function getAttestationCount(uint256 logId) external view returns (uint256) {
return attestations[logId].length;
}
/**
* @notice Gets dogs logged within a time range with their attestation status
* @param startTime The start time for the query
* @param endTime The end time for the query
* @param offset The starting index for pagination
* @param limit The maximum number of logs to return
* @return logIds Array of log IDs
* @return eaters Array of eater addresses
* @return timestamps Array of log timestamps
* @return hasAttestationEnded Array indicating if attestation window has ended
* @return isValid Array indicating if log is valid (only meaningful if attestation ended)
*/
function getDogsInTimeRange(
uint256 startTime,
uint256 endTime,
uint256 offset,
uint256 limit
) external view returns (
uint256[] memory logIds,
address[] memory eaters,
uint256[] memory timestamps,
bool[] memory hasAttestationEnded,
bool[] memory isValid
) {
// Count logs in range
uint256 totalInRange = 0;
for (uint256 i = 0; i < hotdogLogs.length; i++) {
if (hotdogLogs[i].timestamp >= startTime && hotdogLogs[i].timestamp <= endTime) {
totalInRange++;
}
}
// Calculate actual return size
uint256 returnSize = totalInRange > offset ? totalInRange - offset : 0;
returnSize = returnSize > limit ? limit : returnSize;
// Initialize arrays
logIds = new uint256[](returnSize);
eaters = new address[](returnSize);
timestamps = new uint256[](returnSize);
hasAttestationEnded = new bool[](returnSize);
isValid = new bool[](returnSize);
// Populate arrays
uint256 index = 0;
uint256 skipped = 0;
for (uint256 i = 0; i < hotdogLogs.length && index < returnSize; i++) {
if (hotdogLogs[i].timestamp >= startTime && hotdogLogs[i].timestamp <= endTime) {
if (skipped >= offset) {
logIds[index] = i;
eaters[index] = hotdogLogs[i].eater;
timestamps[index] = hotdogLogs[i].timestamp;
// Check attestation status
if (address(attestationManager) != address(0)) {
(, uint256 attestationEndTime, AttestationManager.AttestationStatus status,,, bool logIsValid,) =
attestationManager.getAttestationPeriod(i);
hasAttestationEnded[index] = (block.timestamp > attestationEndTime) ||
(status == AttestationManager.AttestationStatus.Resolved);
isValid[index] = logIsValid;
} else {
// Fallback to old system
hasAttestationEnded[index] = true;
isValid[index] = _isValidLog(i);
}
index++;
} else {
skipped++;
}
}
}
}
/**
* @notice Emit validity update event for a resolved log (admin only)
* @param logId The ID of the hotdog log
*/
function emitValidityUpdate(uint256 logId) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(logId < hotdogLogs.length, "Log does not exist");
bool isValid = _isValidLog(logId);
emit DogValidityUpdated(logId, hotdogLogs[logId].eater, isValid, block.timestamp);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.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` from `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
pragma solidity ^0.8.18;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./HotdogStaking.sol";
interface ILogADog {
function hotdogLogs(uint256 logId) external view returns (
uint256 id,
string memory imageUri,
string memory metadataUri,
uint256 timestamp,
address eater,
address logger,
address zoraCoin
);
function hasRole(bytes32 role, address account) external view returns (bool);
function OPERATOR_ROLE() external view returns (bytes32);
}
/**
* @title AttestationManager
* @dev Manages attestation periods, voting, and dispute resolution for hotdog logs
*/
contract AttestationManager is AccessControl, ReentrancyGuard {
bytes32 public constant LOG_MANAGER_ROLE = keccak256("LOG_MANAGER_ROLE");
HotdogStaking public immutable stakingContract;
ILogADog public logADogContract;
// Attestation parameters
uint256 public constant ATTESTATION_WINDOW = 48 hours;
uint256 public constant MINIMUM_ATTESTATION_STAKE = 30000 * 10**18; // 30,000 HOTDOG
uint256 public constant SLASH_PERCENTAGE = 15; // 15% of staked amount
enum AttestationStatus {
Active,
Resolved,
Disputed
}
struct AttestationPeriod {
uint256 logId;
uint256 startTime;
uint256 endTime;
AttestationStatus status;
uint256 totalValidStake;
uint256 totalInvalidStake;
address[] validAttestors;
address[] invalidAttestors;
mapping(address => uint256) attestorStakes;
mapping(address => bool) hasAttested;
bool isValid; // Final result
uint256 resolvedAt; // Timestamp when period was resolved
}
mapping(uint256 => AttestationPeriod) public attestationPeriods;
mapping(address => uint256[]) public userAttestations; // Track user's active attestations
mapping(address => mapping(uint256 => bool)) public userAttestationChoices; // Track user's attestation choices (valid/invalid)
event AttestationPeriodStarted(uint256 indexed logId, uint256 startTime, uint256 endTime);
event AttestationMade(uint256 indexed logId, address indexed attestor, bool isValid, uint256 stakeAmount);
event AttestationPeriodResolved(uint256 indexed logId, bool isValid, uint256 totalValidStake, uint256 totalInvalidStake);
event RewardsDistributed(uint256 indexed logId, address[] winners, uint256[] amounts);
event TokensSlashed(uint256 indexed logId, address[] losers, uint256[] amounts);
constructor(address _stakingContract) {
stakingContract = HotdogStaking(_stakingContract);
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
/**
* @notice Set the LogADog contract address
* @param _logADogContract Address of the LogADog contract
*/
function setLogADogContract(address _logADogContract) external onlyRole(DEFAULT_ADMIN_ROLE) {
logADogContract = ILogADog(_logADogContract);
}
/**
* @notice Start an attestation period for a hotdog log
* @param logId The ID of the hotdog log
*/
function startAttestationPeriod(uint256 logId) external onlyRole(LOG_MANAGER_ROLE) {
require(attestationPeriods[logId].startTime == 0, "Attestation period already exists");
AttestationPeriod storage period = attestationPeriods[logId];
period.logId = logId;
period.startTime = block.timestamp;
period.endTime = block.timestamp + ATTESTATION_WINDOW;
period.status = AttestationStatus.Active;
emit AttestationPeriodStarted(logId, period.startTime, period.endTime);
}
/**
* @notice Attest to a hotdog log's validity
* @param logId The ID of the hotdog log
* @param isValid Whether the log is valid
* @param stakeAmount Amount of tokens to stake on this attestation
*/
function attestToLog(uint256 logId, bool isValid, uint256 stakeAmount) external nonReentrant {
AttestationPeriod storage period = attestationPeriods[logId];
require(period.startTime > 0, "Attestation period does not exist");
require(block.timestamp <= period.endTime, "Attestation period has ended");
require(period.status == AttestationStatus.Active, "Attestation period not active");
require(!period.hasAttested[msg.sender], "Already attested to this log");
require(stakeAmount >= MINIMUM_ATTESTATION_STAKE, "Stake amount too low");
// Check that user is not attesting to their own log
if (address(logADogContract) != address(0)) {
(,,,, address eater,,) = logADogContract.hotdogLogs(logId);
require(eater != msg.sender, "Cannot attest to your own log");
}
// Check if user has enough available stake
require(stakingContract.canParticipateInAttestation(msg.sender, stakeAmount), "Insufficient available stake");
// Lock tokens for this attestation
stakingContract.lockTokensForAttestation(msg.sender, stakeAmount);
// Record attestation
period.hasAttested[msg.sender] = true;
period.attestorStakes[msg.sender] = stakeAmount;
if (isValid) {
period.validAttestors.push(msg.sender);
period.totalValidStake += stakeAmount;
} else {
period.invalidAttestors.push(msg.sender);
period.totalInvalidStake += stakeAmount;
}
// Track user's attestations
userAttestations[msg.sender].push(logId);
userAttestationChoices[msg.sender][logId] = isValid;
emit AttestationMade(logId, msg.sender, isValid, stakeAmount);
}
/**
* @notice Attest to a hotdog log's validity on behalf of another user (operator only)
* @param logId The ID of the hotdog log
* @param attestor The address of the user making the attestation
* @param isValid Whether the log is valid
* @param stakeAmount Amount of tokens to stake on this attestation
*/
function attestToLogOnBehalf(uint256 logId, address attestor, bool isValid, uint256 stakeAmount) external nonReentrant {
// Check if caller has operator role in LogADog contract
require(address(logADogContract) != address(0), "LogADog contract not set");
require(logADogContract.hasRole(logADogContract.OPERATOR_ROLE(), msg.sender), "Caller is not an operator");
AttestationPeriod storage period = attestationPeriods[logId];
require(period.startTime > 0, "Attestation period does not exist");
require(block.timestamp <= period.endTime, "Attestation period has ended");
require(period.status == AttestationStatus.Active, "Attestation period not active");
require(!period.hasAttested[attestor], "User has already attested to this log");
require(stakeAmount >= MINIMUM_ATTESTATION_STAKE, "Stake amount too low");
// Check that user is not attesting to their own log
(,,,, address eater,,) = logADogContract.hotdogLogs(logId);
require(eater != attestor, "Cannot attest to your own log");
// Check if user has enough available stake
require(stakingContract.canParticipateInAttestation(attestor, stakeAmount), "Insufficient available stake");
// Lock tokens for this attestation (from the attestor's account)
stakingContract.lockTokensForAttestation(attestor, stakeAmount);
// Record attestation
period.hasAttested[attestor] = true;
period.attestorStakes[attestor] = stakeAmount;
if (isValid) {
period.validAttestors.push(attestor);
period.totalValidStake += stakeAmount;
} else {
period.invalidAttestors.push(attestor);
period.totalInvalidStake += stakeAmount;
}
// Track user's attestations
userAttestations[attestor].push(logId);
userAttestationChoices[attestor][logId] = isValid;
emit AttestationMade(logId, attestor, isValid, stakeAmount);
}
/**
* @notice Resolve an attestation period after it has ended
* @param logId The ID of the hotdog log
*/
function resolveAttestationPeriod(uint256 logId) external nonReentrant {
AttestationPeriod storage period = attestationPeriods[logId];
require(period.startTime > 0, "Attestation period does not exist");
require(block.timestamp > period.endTime, "Attestation period still active");
require(period.status == AttestationStatus.Active, "Already resolved");
// Determine the winning side
bool logIsValid = period.totalValidStake >= period.totalInvalidStake;
period.isValid = logIsValid;
period.status = AttestationStatus.Resolved;
// Calculate rewards and slashing
address[] memory winners = logIsValid ? period.validAttestors : period.invalidAttestors;
address[] memory losers = logIsValid ? period.invalidAttestors : period.validAttestors;
uint256 totalWinningStake = logIsValid ? period.totalValidStake : period.totalInvalidStake;
uint256 totalLosingStake = logIsValid ? period.totalInvalidStake : period.totalValidStake;
// Slash losers and calculate total slashed amount
uint256 totalSlashed = 0;
uint256[] memory slashedAmounts = new uint256[](losers.length);
for (uint256 i = 0; i < losers.length; i++) {
address loser = losers[i];
uint256 stakeAmount = period.attestorStakes[loser];
uint256 slashAmount = (stakeAmount * SLASH_PERCENTAGE) / 100;
stakingContract.slashTokens(loser, slashAmount);
stakingContract.unlockTokensFromAttestation(loser, stakeAmount - slashAmount);
slashedAmounts[i] = slashAmount;
totalSlashed += slashAmount;
}
// Distribute rewards to winners
uint256[] memory rewardAmounts = new uint256[](winners.length);
if (winners.length > 0 && totalSlashed > 0) {
for (uint256 i = 0; i < winners.length; i++) {
address winner = winners[i];
uint256 stakeAmount = period.attestorStakes[winner];
// Proportional reward based on stake
uint256 rewardAmount = (totalSlashed * stakeAmount) / totalWinningStake;
rewardAmounts[i] = rewardAmount;
// Unlock their original stake
stakingContract.unlockTokensFromAttestation(winner, stakeAmount);
}
// Distribute rewards
stakingContract.distributeAttestationRewards(winners, rewardAmounts);
emit RewardsDistributed(logId, winners, rewardAmounts);
} else {
// If no winners, just unlock everyone's stake
for (uint256 i = 0; i < winners.length; i++) {
address winner = winners[i];
uint256 stakeAmount = period.attestorStakes[winner];
stakingContract.unlockTokensFromAttestation(winner, stakeAmount);
}
}
if (losers.length > 0) {
emit TokensSlashed(logId, losers, slashedAmounts);
}
period.resolvedAt = block.timestamp;
emit AttestationPeriodResolved(logId, logIsValid, period.totalValidStake, period.totalInvalidStake);
}
/**
* @notice Get attestation period info
* @param logId The ID of the hotdog log
* @return startTime Start time of attestation period
* @return endTime End time of attestation period
* @return status Current status
* @return totalValidStake Total stake on valid side
* @return totalInvalidStake Total stake on invalid side
* @return isValid Final result (only valid after resolution)
* @return resolvedAt Timestamp when period was resolved (0 if not resolved)
*/
function getAttestationPeriod(uint256 logId) external view returns (
uint256 startTime,
uint256 endTime,
AttestationStatus status,
uint256 totalValidStake,
uint256 totalInvalidStake,
bool isValid,
uint256 resolvedAt
) {
AttestationPeriod storage period = attestationPeriods[logId];
return (
period.startTime,
period.endTime,
period.status,
period.totalValidStake,
period.totalInvalidStake,
period.isValid,
period.resolvedAt
);
}
/**
* @notice Check if user has attested to a log
* @param logId The ID of the hotdog log
* @param user User address
* @return Whether user has attested
*/
function hasUserAttested(uint256 logId, address user) external view returns (bool) {
return attestationPeriods[logId].hasAttested[user];
}
/**
* @notice Get user's stake in an attestation
* @param logId The ID of the hotdog log
* @param user User address
* @return Stake amount
*/
function getUserStakeInAttestation(uint256 logId, address user) external view returns (uint256) {
return attestationPeriods[logId].attestorStakes[user];
}
/**
* @notice Get all attestors for a log
* @param logId The ID of the hotdog log
* @return validAttestors Array of valid attestors
* @return invalidAttestors Array of invalid attestors
*/
function getAttestors(uint256 logId) external view returns (address[] memory validAttestors, address[] memory invalidAttestors) {
AttestationPeriod storage period = attestationPeriods[logId];
return (period.validAttestors, period.invalidAttestors);
}
/**
* @notice Get user's active attestations
* @param user User address
* @return Array of log IDs user has attested to
*/
function getUserAttestations(address user) external view returns (uint256[] memory) {
return userAttestations[user];
}
/**
* @notice Get user's attestations with their choices
* @param user User address
* @return logIds Array of log IDs user has attested to
* @return choices Array of attestation choices (true = valid, false = invalid)
*/
function getUserAttestationsWithChoices(address user) external view returns (uint256[] memory logIds, bool[] memory choices) {
uint256[] memory userLogIds = userAttestations[user];
bool[] memory userChoices = new bool[](userLogIds.length);
for (uint256 i = 0; i < userLogIds.length; i++) {
userChoices[i] = userAttestationChoices[user][userLogIds[i]];
}
return (userLogIds, userChoices);
}
/**
* @notice Get attestation counts for multiple logs
* @param logIds Array of log IDs to query
* @return validCounts Array of valid attestation counts for each log
* @return invalidCounts Array of invalid attestation counts for each log
*/
function getAttestationCounts(uint256[] calldata logIds) external view returns (uint256[] memory validCounts, uint256[] memory invalidCounts) {
validCounts = new uint256[](logIds.length);
invalidCounts = new uint256[](logIds.length);
for (uint256 i = 0; i < logIds.length; i++) {
AttestationPeriod storage period = attestationPeriods[logIds[i]];
validCounts[i] = period.validAttestors.length;
invalidCounts[i] = period.invalidAttestors.length;
}
return (validCounts, invalidCounts);
}
/**
* @notice Get attestation stakes for multiple logs
* @param logIds Array of log IDs to query
* @return validStakes Array of total valid stakes for each log
* @return invalidStakes Array of total invalid stakes for each log
*/
function getAttestationStakes(uint256[] calldata logIds) external view returns (uint256[] memory validStakes, uint256[] memory invalidStakes) {
validStakes = new uint256[](logIds.length);
invalidStakes = new uint256[](logIds.length);
for (uint256 i = 0; i < logIds.length; i++) {
AttestationPeriod storage period = attestationPeriods[logIds[i]];
validStakes[i] = period.totalValidStake;
invalidStakes[i] = period.totalInvalidStake;
}
return (validStakes, invalidStakes);
}
/**
* @notice Check if attestation period is active
* @param logId The ID of the hotdog log
* @return Whether attestation period is active
*/
function isAttestationPeriodActive(uint256 logId) external view returns (bool) {
AttestationPeriod storage period = attestationPeriods[logId];
return period.status == AttestationStatus.Active && block.timestamp <= period.endTime;
}
/**
* @notice Add log manager
* @param manager Address to grant log manager role
*/
function addLogManager(address manager) external onlyRole(DEFAULT_ADMIN_ROLE) {
_grantRole(LOG_MANAGER_ROLE, manager);
}
/**
* @notice Get batch attestation status for multiple logs
* @param logIds Array of log IDs to query
* @return statuses Array of attestation statuses
* @return isValid Array indicating if each log is valid
* @return endTimes Array of attestation end times
*/
function getBatchAttestationStatus(uint256[] calldata logIds)
external view returns (
AttestationStatus[] memory statuses,
bool[] memory isValid,
uint256[] memory endTimes
) {
statuses = new AttestationStatus[](logIds.length);
isValid = new bool[](logIds.length);
endTimes = new uint256[](logIds.length);
for (uint256 i = 0; i < logIds.length; i++) {
AttestationPeriod storage period = attestationPeriods[logIds[i]];
statuses[i] = period.status;
isValid[i] = period.isValid;
endTimes[i] = period.endTime;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
interface IZoraFactory {
function deploy(
address payoutRecipient,
address[] memory owners,
string memory uri,
string memory name,
string memory symbol,
bytes memory poolConfig,
address platformReferrer,
address postDeployHook,
bytes calldata postDeployHookData,
bytes32 coinSalt
) external payable returns (address coin, bytes memory postDeployHookDataOut);
function coinAddress(
address msgSender,
string memory name,
string memory symbol,
bytes memory poolConfig,
address platformReferrer,
bytes32 coinSalt
) external view returns (address);
}
/**
* @title CoinDeploymentManager
* @dev Manages coin deployment for hotdog logs - swappable implementation
*/
contract CoinDeploymentManager is AccessControl {
bytes32 public constant DEPLOYER_ROLE = keccak256("DEPLOYER_ROLE");
// Current coin deployment strategy
address public coinFactory;
string public coinSymbol;
event CoinDeployed(uint256 indexed logId, address indexed coinAddress, address indexed eater);
event FactoryUpdated(address indexed oldFactory, address indexed newFactory);
event SymbolUpdated(string oldSymbol, string newSymbol);
constructor(address _coinFactory, string memory _coinSymbol) {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
coinFactory = _coinFactory;
coinSymbol = _coinSymbol;
}
/**
* @notice Deploy a coin for a hotdog log
* @param logId The ID of the hotdog log
* @param eater The address of the hotdog eater
* @param metadataUri The metadata URI for the coin
* @param poolConfig The pool configuration
* @param platformReferrer The platform referrer address
* @return coinAddress The address of the deployed coin
*/
function deployCoin(
uint256 logId,
address eater,
string memory metadataUri,
bytes calldata poolConfig,
address platformReferrer
) external payable onlyRole(DEPLOYER_ROLE) returns (address coinAddress) {
require(coinFactory != address(0), "Coin factory not set");
// Create owners array
address[] memory owners = new address[](1);
owners[0] = eater;
// Generate coin name
string memory coinName = string.concat("Logged Dog #", Strings.toString(logId));
// Generate deterministic salt based on logId and eater
bytes32 coinSalt = keccak256(abi.encodePacked("LogADog", logId, eater));
// Deploy coin using latest v4 factory interface
(coinAddress,) = IZoraFactory(coinFactory).deploy{value: msg.value}(
eater, // payout recipient
owners, // owners
metadataUri, // uri
coinName, // name
coinSymbol, // symbol
poolConfig, // configuration for the pool
platformReferrer, // platform referrer
address(0), // postDeployHook (none for now)
"", // postDeployHookData (empty)
coinSalt // deterministic salt
);
emit CoinDeployed(logId, coinAddress, eater);
}
/**
* @notice Predict the address of a coin before deployment
* @param logId The ID of the hotdog log
* @param eater The address of the hotdog eater
* @param poolConfig The pool configuration
* @param platformReferrer The platform referrer address
* @return predictedAddress The predicted address of the coin
*/
function predictCoinAddress(
uint256 logId,
address eater,
bytes calldata poolConfig,
address platformReferrer
) external view returns (address predictedAddress) {
require(coinFactory != address(0), "Coin factory not set");
// Generate coin name
string memory coinName = string.concat("Logged Dog #", Strings.toString(logId));
// Generate deterministic salt based on logId and eater
bytes32 coinSalt = keccak256(abi.encodePacked("LogADog", logId, eater));
// Predict coin address
predictedAddress = IZoraFactory(coinFactory).coinAddress(
address(this), // msgSender will be this contract
coinName, // name
coinSymbol, // symbol
poolConfig, // pool configuration
platformReferrer, // platform referrer
coinSalt // deterministic salt
);
}
/**
* @notice Update the coin factory (admin only)
* @param _newFactory The new factory address
*/
function updateCoinFactory(address _newFactory) external onlyRole(DEFAULT_ADMIN_ROLE) {
address oldFactory = coinFactory;
coinFactory = _newFactory;
emit FactoryUpdated(oldFactory, _newFactory);
}
/**
* @notice Update the coin symbol (admin only)
* @param _newSymbol The new coin symbol
*/
function updateCoinSymbol(string memory _newSymbol) external onlyRole(DEFAULT_ADMIN_ROLE) {
string memory oldSymbol = coinSymbol;
coinSymbol = _newSymbol;
emit SymbolUpdated(oldSymbol, _newSymbol);
}
/**
* @notice Add a deployer role (admin only)
* @param deployer The address to grant deployer role
*/
function addDeployer(address deployer) external onlyRole(DEFAULT_ADMIN_ROLE) {
_grantRole(DEPLOYER_ROLE, deployer);
}
/**
* @notice Remove a deployer role (admin only)
* @param deployer The address to revoke deployer role
*/
function removeDeployer(address deployer) external onlyRole(DEFAULT_ADMIN_ROLE) {
_revokeRole(DEPLOYER_ROLE, deployer);
}
/**
* @notice Get current factory and symbol info
*/
function getDeploymentInfo() external view returns (address factory, string memory symbol) {
return (coinFactory, coinSymbol);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (access/IAccessControl.sol)
pragma solidity ^0.8.20;
/**
* @dev External interface of AccessControl declared to support ERC-165 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 to signal 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. This account bears the admin role (for the granted role).
* Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
*/
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.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.1.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 ERC-165 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.1.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 EIP-1153 (transient storage) is available on the chain you're deploying at,
* consider using {ReentrancyGuardTransient} instead.
*
* 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
pragma solidity ^0.8.18;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./HotdogToken.sol";
/**
* @title HotdogStaking
* @dev Staking contract for HOTDOG tokens with proportional rewards that fully deplete by September 1, 2025
*/
contract HotdogStaking is AccessControl, ReentrancyGuard {
using SafeERC20 for IERC20;
bytes32 public constant ATTESTATION_MANAGER_ROLE = keccak256("ATTESTATION_MANAGER_ROLE");
HotdogToken public immutable hotdogToken;
// Staking parameters
uint256 public constant MINIMUM_STAKE = 300000 * 10**18; // 300,000 HOTDOG minimum
uint256 public constant REWARD_END_TIME = 1756684800; // September 1, 2025 00:00:00 UTC
uint256 public constant SLASH_PERCENTAGE = 15; // 15% slashing for wrong attestations
uint256 public constant MINIMUM_STAKE_DURATION = 1 hours; // Minimum time before unstaking (flash loan protection)
uint256 private constant PRECISION = 1e18; // Precision for reward calculations
struct StakeInfo {
uint256 amount;
uint256 rewardDebt; // User's reward debt for accurate proportional calculation
uint256 pendingRewards;
bool isActive;
uint256 stakeTimestamp; // Timestamp when the stake was last increased (for flash loan protection)
}
mapping(address => StakeInfo) public stakes;
mapping(address => uint256) public lockedForAttestation; // Tokens locked for active attestations
uint256 public totalStaked;
uint256 public rewardsPool;
uint256 public lastRewardUpdate; // Global timestamp for reward calculations
uint256 public accumulatedRewardPerToken; // Global reward accumulator
event Staked(address indexed user, uint256 amount);
event Unstaked(address indexed user, uint256 amount);
event RewardsClaimed(address indexed user, uint256 amount);
event TokensSlashed(address indexed user, uint256 amount);
event TokensLocked(address indexed user, uint256 amount);
event TokensUnlocked(address indexed user, uint256 amount);
event RewardsDeposited(uint256 amount);
event GlobalRewardsUpdated(uint256 accumulatedRewardPerToken, uint256 remainingPool);
constructor(address _hotdogToken) {
hotdogToken = HotdogToken(_hotdogToken);
lastRewardUpdate = block.timestamp;
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
/**
* @notice Update global reward state and calculate emission rate
*/
function updateGlobalRewards() internal {
if (block.timestamp >= REWARD_END_TIME || totalStaked == 0 || rewardsPool == 0) {
lastRewardUpdate = block.timestamp;
return;
}
uint256 timeElapsed = block.timestamp - lastRewardUpdate;
if (timeElapsed == 0) return;
uint256 timeRemaining = REWARD_END_TIME - block.timestamp;
if (timeRemaining == 0) {
lastRewardUpdate = block.timestamp;
return;
}
// Calculate emission rate: rewards per second = rewardsPool / timeRemaining
uint256 rewardsToDistribute = (rewardsPool * timeElapsed) / timeRemaining;
// Ensure we don't distribute more than available
if (rewardsToDistribute > rewardsPool) {
rewardsToDistribute = rewardsPool;
}
// Update accumulated reward per token
accumulatedRewardPerToken += (rewardsToDistribute * PRECISION) / totalStaked;
rewardsPool -= rewardsToDistribute;
lastRewardUpdate = block.timestamp;
emit GlobalRewardsUpdated(accumulatedRewardPerToken, rewardsPool);
}
/**
* @notice Update user rewards based on their stake proportion
*/
function updateUserRewards(address user) internal {
updateGlobalRewards();
StakeInfo storage userStake = stakes[user];
if (userStake.amount > 0) {
uint256 accumulatedRewards = (userStake.amount * accumulatedRewardPerToken) / PRECISION;
uint256 pending = accumulatedRewards - userStake.rewardDebt;
userStake.pendingRewards += pending;
}
userStake.rewardDebt = (userStake.amount * accumulatedRewardPerToken) / PRECISION;
}
/**
* @notice Stake HOTDOG tokens
* @param amount Amount of tokens to stake
*/
function stake(uint256 amount) external nonReentrant {
require(amount >= MINIMUM_STAKE, "Amount below minimum stake");
require(block.timestamp < REWARD_END_TIME, "Reward period has ended");
StakeInfo storage userStake = stakes[msg.sender];
// Update rewards before changing stake
updateUserRewards(msg.sender);
IERC20(address(hotdogToken)).safeTransferFrom(msg.sender, address(this), amount);
if (userStake.isActive) {
userStake.amount += amount;
} else {
userStake.amount = amount;
userStake.isActive = true;
}
// Update stake timestamp to prevent flash loan attacks
userStake.stakeTimestamp = block.timestamp;
totalStaked += amount;
// Update reward debt after stake change
userStake.rewardDebt = (userStake.amount * accumulatedRewardPerToken) / PRECISION;
emit Staked(msg.sender, amount);
}
/**
* @notice Unstake HOTDOG tokens
* @param amount Amount of tokens to unstake
*/
function unstake(uint256 amount) external nonReentrant {
StakeInfo storage userStake = stakes[msg.sender];
require(userStake.isActive, "No active stake");
require(amount <= userStake.amount, "Insufficient staked amount");
require(amount <= getAvailableStake(msg.sender), "Tokens locked for attestation");
// Flash loan protection: enforce minimum staking duration
require(
block.timestamp >= userStake.stakeTimestamp + MINIMUM_STAKE_DURATION,
"Minimum stake duration not met"
);
// Update rewards before changing stake
updateUserRewards(msg.sender);
userStake.amount -= amount;
totalStaked -= amount;
if (userStake.amount == 0) {
userStake.isActive = false;
userStake.rewardDebt = 0;
userStake.stakeTimestamp = 0; // Reset timestamp when fully unstaked
} else {
// Update reward debt after stake change
userStake.rewardDebt = (userStake.amount * accumulatedRewardPerToken) / PRECISION;
}
IERC20(address(hotdogToken)).safeTransfer(msg.sender, amount);
emit Unstaked(msg.sender, amount);
}
/**
* @notice Claim pending rewards
*/
function claimRewards() external nonReentrant {
updateUserRewards(msg.sender);
StakeInfo storage userStake = stakes[msg.sender];
uint256 rewards = userStake.pendingRewards;
require(rewards > 0, "No rewards to claim");
userStake.pendingRewards = 0;
IERC20(address(hotdogToken)).safeTransfer(msg.sender, rewards);
emit RewardsClaimed(msg.sender, rewards);
}
/**
* @notice Lock tokens for attestation (only attestation manager)
* @param user User whose tokens to lock
* @param amount Amount to lock
*/
function lockTokensForAttestation(address user, uint256 amount) external onlyRole(ATTESTATION_MANAGER_ROLE) {
require(amount <= getAvailableStake(user), "Insufficient available stake");
lockedForAttestation[user] += amount;
emit TokensLocked(user, amount);
}
/**
* @notice Unlock tokens after attestation (only attestation manager)
* @param user User whose tokens to unlock
* @param amount Amount to unlock
*/
function unlockTokensFromAttestation(address user, uint256 amount) external onlyRole(ATTESTATION_MANAGER_ROLE) {
require(lockedForAttestation[user] >= amount, "Insufficient locked tokens");
lockedForAttestation[user] -= amount;
emit TokensUnlocked(user, amount);
}
/**
* @notice Slash tokens for wrong attestation (only attestation manager)
* @param user User whose tokens to slash
* @param amount Amount to slash
*/
function slashTokens(address user, uint256 amount) external onlyRole(ATTESTATION_MANAGER_ROLE) {
require(lockedForAttestation[user] >= amount, "Insufficient locked tokens");
StakeInfo storage userStake = stakes[user];
require(userStake.amount >= amount, "Insufficient staked tokens");
// Update rewards before slashing
updateUserRewards(user);
userStake.amount -= amount;
lockedForAttestation[user] -= amount;
totalStaked -= amount;
// Add slashed tokens to rewards pool
rewardsPool += amount;
if (userStake.amount == 0) {
userStake.isActive = false;
userStake.rewardDebt = 0;
userStake.stakeTimestamp = 0; // Reset timestamp when fully slashed
} else {
// Update reward debt after stake change
userStake.rewardDebt = (userStake.amount * accumulatedRewardPerToken) / PRECISION;
}
emit TokensSlashed(user, amount);
}
/**
* @notice Distribute rewards to winners (only attestation manager)
* @param winners Array of winner addresses
* @param amounts Array of reward amounts
*/
function distributeAttestationRewards(address[] calldata winners, uint256[] calldata amounts) external onlyRole(ATTESTATION_MANAGER_ROLE) {
require(winners.length == amounts.length, "Arrays length mismatch");
for (uint256 i = 0; i < winners.length; i++) {
if (amounts[i] > 0) {
stakes[winners[i]].pendingRewards += amounts[i];
}
}
}
/**
* @notice Deposit rewards to the pool (admin only)
* @param amount Amount of rewards to deposit
*/
function depositRewards(uint256 amount) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(block.timestamp < REWARD_END_TIME, "Reward period has ended");
// Update global rewards before adding to pool
updateGlobalRewards();
IERC20(address(hotdogToken)).safeTransferFrom(msg.sender, address(this), amount);
rewardsPool += amount;
emit RewardsDeposited(amount);
}
/**
* @notice Add attestation manager
* @param manager Address to grant attestation manager role
*/
function addAttestationManager(address manager) external onlyRole(DEFAULT_ADMIN_ROLE) {
_grantRole(ATTESTATION_MANAGER_ROLE, manager);
}
/**
* @notice Get available stake (not locked for attestation)
* @param user User address
* @return Available stake amount
*/
function getAvailableStake(address user) public view returns (uint256) {
StakeInfo memory userStake = stakes[user];
if (!userStake.isActive) return 0;
return userStake.amount - lockedForAttestation[user];
}
/**
* @notice Get pending rewards for a user
* @param user User address
* @return Pending rewards amount
*/
function getPendingRewards(address user) public view returns (uint256) {
StakeInfo memory userStake = stakes[user];
uint256 pending = userStake.pendingRewards;
if (!userStake.isActive || userStake.amount == 0 || totalStaked == 0 || rewardsPool == 0) {
return pending;
}
// Calculate new rewards since last update
uint256 timeElapsed = block.timestamp - lastRewardUpdate;
if (timeElapsed > 0 && block.timestamp < REWARD_END_TIME) {
uint256 timeRemaining = REWARD_END_TIME - block.timestamp;
if (timeRemaining > 0) {
uint256 rewardsToDistribute = (rewardsPool * timeElapsed) / timeRemaining;
if (rewardsToDistribute > rewardsPool) {
rewardsToDistribute = rewardsPool;
}
uint256 newAccumulatedRewardPerToken = accumulatedRewardPerToken +
(rewardsToDistribute * PRECISION) / totalStaked;
uint256 accumulatedRewards = (userStake.amount * newAccumulatedRewardPerToken) / PRECISION;
pending += accumulatedRewards - userStake.rewardDebt;
}
}
return pending;
}
/**
* @notice Check if user can participate in attestations
* @param user User address
* @param requiredStake Required stake amount
* @return Whether user can participate
*/
function canParticipateInAttestation(address user, uint256 requiredStake) external view returns (bool) {
return getAvailableStake(user) >= requiredStake;
}
/**
* @notice Get current emission rate (rewards per second)
* @return Current emission rate
*/
function getCurrentEmissionRate() external view returns (uint256) {
if (block.timestamp >= REWARD_END_TIME || rewardsPool == 0) {
return 0;
}
uint256 timeRemaining = REWARD_END_TIME - block.timestamp;
return rewardsPool / timeRemaining;
}
/**
* @notice Get time remaining until reward period ends
* @return Time remaining in seconds
*/
function getTimeRemaining() external view returns (uint256) {
if (block.timestamp >= REWARD_END_TIME) {
return 0;
}
return REWARD_END_TIME - block.timestamp;
}
/**
* @notice Get timestamp when user can unstake (flash loan protection)
* @param user User address
* @return Timestamp when unstaking becomes available (0 if no active stake)
*/
function getUnstakeAvailableTime(address user) external view returns (uint256) {
StakeInfo memory userStake = stakes[user];
if (!userStake.isActive) return 0;
return userStake.stakeTimestamp + MINIMUM_STAKE_DURATION;
}
/**
* @notice Check if user can unstake now
* @param user User address
* @return Whether user can unstake now
*/
function canUnstakeNow(address user) external view returns (bool) {
StakeInfo memory userStake = stakes[user];
if (!userStake.isActive) return false;
return block.timestamp >= userStake.stakeTimestamp + MINIMUM_STAKE_DURATION;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SafeCast} from "./math/SafeCast.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
using SafeCast for *;
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
uint256 private constant SPECIAL_CHARS_LOOKUP =
(1 << 0x08) | // backspace
(1 << 0x09) | // tab
(1 << 0x0a) | // newline
(1 << 0x0c) | // form feed
(1 << 0x0d) | // carriage return
(1 << 0x22) | // double quote
(1 << 0x5c); // backslash
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev The string being parsed contains characters that are not in scope of the given base.
*/
error StringsInvalidChar();
/**
* @dev The string being parsed is not a properly formatted address.
*/
error StringsInvalidAddressFormat();
/**
* @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;
assembly ("memory-safe") {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
assembly ("memory-safe") {
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 Converts an `address` with fixed length of 20 bytes to its checksummed ASCII `string` hexadecimal
* representation, according to EIP-55.
*/
function toChecksumHexString(address addr) internal pure returns (string memory) {
bytes memory buffer = bytes(toHexString(addr));
// hash the hex part of buffer (skip length + 2 bytes, length 40)
uint256 hashValue;
assembly ("memory-safe") {
hashValue := shr(96, keccak256(add(buffer, 0x22), 40))
}
for (uint256 i = 41; i > 1; --i) {
// possible values for buffer[i] are 48 (0) to 57 (9) and 97 (a) to 102 (f)
if (hashValue & 0xf > 7 && uint8(buffer[i]) > 96) {
// case shift by xoring with 0x20
buffer[i] ^= 0x20;
}
hashValue >>= 4;
}
return string(buffer);
}
/**
* @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));
}
/**
* @dev Parse a decimal string and returns the value as a `uint256`.
*
* Requirements:
* - The string must be formatted as `[0-9]*`
* - The result must fit into an `uint256` type
*/
function parseUint(string memory input) internal pure returns (uint256) {
return parseUint(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseUint-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded).
*
* Requirements:
* - The substring must be formatted as `[0-9]*`
* - The result must fit into an `uint256` type
*/
function parseUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
(bool success, uint256 value) = tryParseUint(input, begin, end);
if (!success) revert StringsInvalidChar();
return value;
}
/**
* @dev Variant of {parseUint-string} that returns false if the parsing fails because of an invalid character.
*
* NOTE: This function will revert if the result does not fit in a `uint256`.
*/
function tryParseUint(string memory input) internal pure returns (bool success, uint256 value) {
return _tryParseUintUncheckedBounds(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseUint-string-uint256-uint256} that returns false if the parsing fails because of an invalid
* character.
*
* NOTE: This function will revert if the result does not fit in a `uint256`.
*/
function tryParseUint(
string memory input,
uint256 begin,
uint256 end
) internal pure returns (bool success, uint256 value) {
if (end > bytes(input).length || begin > end) return (false, 0);
return _tryParseUintUncheckedBounds(input, begin, end);
}
/**
* @dev Implementation of {tryParseUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
* `begin <= end <= input.length`. Other inputs would result in undefined behavior.
*/
function _tryParseUintUncheckedBounds(
string memory input,
uint256 begin,
uint256 end
) private pure returns (bool success, uint256 value) {
bytes memory buffer = bytes(input);
uint256 result = 0;
for (uint256 i = begin; i < end; ++i) {
uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
if (chr > 9) return (false, 0);
result *= 10;
result += chr;
}
return (true, result);
}
/**
* @dev Parse a decimal string and returns the value as a `int256`.
*
* Requirements:
* - The string must be formatted as `[-+]?[0-9]*`
* - The result must fit in an `int256` type.
*/
function parseInt(string memory input) internal pure returns (int256) {
return parseInt(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseInt-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded).
*
* Requirements:
* - The substring must be formatted as `[-+]?[0-9]*`
* - The result must fit in an `int256` type.
*/
function parseInt(string memory input, uint256 begin, uint256 end) internal pure returns (int256) {
(bool success, int256 value) = tryParseInt(input, begin, end);
if (!success) revert StringsInvalidChar();
return value;
}
/**
* @dev Variant of {parseInt-string} that returns false if the parsing fails because of an invalid character or if
* the result does not fit in a `int256`.
*
* NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
*/
function tryParseInt(string memory input) internal pure returns (bool success, int256 value) {
return _tryParseIntUncheckedBounds(input, 0, bytes(input).length);
}
uint256 private constant ABS_MIN_INT256 = 2 ** 255;
/**
* @dev Variant of {parseInt-string-uint256-uint256} that returns false if the parsing fails because of an invalid
* character or if the result does not fit in a `int256`.
*
* NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
*/
function tryParseInt(
string memory input,
uint256 begin,
uint256 end
) internal pure returns (bool success, int256 value) {
if (end > bytes(input).length || begin > end) return (false, 0);
return _tryParseIntUncheckedBounds(input, begin, end);
}
/**
* @dev Implementation of {tryParseInt-string-uint256-uint256} that does not check bounds. Caller should make sure that
* `begin <= end <= input.length`. Other inputs would result in undefined behavior.
*/
function _tryParseIntUncheckedBounds(
string memory input,
uint256 begin,
uint256 end
) private pure returns (bool success, int256 value) {
bytes memory buffer = bytes(input);
// Check presence of a negative sign.
bytes1 sign = begin == end ? bytes1(0) : bytes1(_unsafeReadBytesOffset(buffer, begin)); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
bool positiveSign = sign == bytes1("+");
bool negativeSign = sign == bytes1("-");
uint256 offset = (positiveSign || negativeSign).toUint();
(bool absSuccess, uint256 absValue) = tryParseUint(input, begin + offset, end);
if (absSuccess && absValue < ABS_MIN_INT256) {
return (true, negativeSign ? -int256(absValue) : int256(absValue));
} else if (absSuccess && negativeSign && absValue == ABS_MIN_INT256) {
return (true, type(int256).min);
} else return (false, 0);
}
/**
* @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as a `uint256`.
*
* Requirements:
* - The string must be formatted as `(0x)?[0-9a-fA-F]*`
* - The result must fit in an `uint256` type.
*/
function parseHexUint(string memory input) internal pure returns (uint256) {
return parseHexUint(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseHexUint-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded).
*
* Requirements:
* - The substring must be formatted as `(0x)?[0-9a-fA-F]*`
* - The result must fit in an `uint256` type.
*/
function parseHexUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
(bool success, uint256 value) = tryParseHexUint(input, begin, end);
if (!success) revert StringsInvalidChar();
return value;
}
/**
* @dev Variant of {parseHexUint-string} that returns false if the parsing fails because of an invalid character.
*
* NOTE: This function will revert if the result does not fit in a `uint256`.
*/
function tryParseHexUint(string memory input) internal pure returns (bool success, uint256 value) {
return _tryParseHexUintUncheckedBounds(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseHexUint-string-uint256-uint256} that returns false if the parsing fails because of an
* invalid character.
*
* NOTE: This function will revert if the result does not fit in a `uint256`.
*/
function tryParseHexUint(
string memory input,
uint256 begin,
uint256 end
) internal pure returns (bool success, uint256 value) {
if (end > bytes(input).length || begin > end) return (false, 0);
return _tryParseHexUintUncheckedBounds(input, begin, end);
}
/**
* @dev Implementation of {tryParseHexUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
* `begin <= end <= input.length`. Other inputs would result in undefined behavior.
*/
function _tryParseHexUintUncheckedBounds(
string memory input,
uint256 begin,
uint256 end
) private pure returns (bool success, uint256 value) {
bytes memory buffer = bytes(input);
// skip 0x prefix if present
bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(buffer, begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
uint256 offset = hasPrefix.toUint() * 2;
uint256 result = 0;
for (uint256 i = begin + offset; i < end; ++i) {
uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
if (chr > 15) return (false, 0);
result *= 16;
unchecked {
// Multiplying by 16 is equivalent to a shift of 4 bits (with additional overflow check).
// This guarantees that adding a value < 16 will not cause an overflow, hence the unchecked.
result += chr;
}
}
return (true, result);
}
/**
* @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as an `address`.
*
* Requirements:
* - The string must be formatted as `(0x)?[0-9a-fA-F]{40}`
*/
function parseAddress(string memory input) internal pure returns (address) {
return parseAddress(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseAddress-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded).
*
* Requirements:
* - The substring must be formatted as `(0x)?[0-9a-fA-F]{40}`
*/
function parseAddress(string memory input, uint256 begin, uint256 end) internal pure returns (address) {
(bool success, address value) = tryParseAddress(input, begin, end);
if (!success) revert StringsInvalidAddressFormat();
return value;
}
/**
* @dev Variant of {parseAddress-string} that returns false if the parsing fails because the input is not a properly
* formatted address. See {parseAddress-string} requirements.
*/
function tryParseAddress(string memory input) internal pure returns (bool success, address value) {
return tryParseAddress(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseAddress-string-uint256-uint256} that returns false if the parsing fails because input is not a properly
* formatted address. See {parseAddress-string-uint256-uint256} requirements.
*/
function tryParseAddress(
string memory input,
uint256 begin,
uint256 end
) internal pure returns (bool success, address value) {
if (end > bytes(input).length || begin > end) return (false, address(0));
bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(bytes(input), begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
uint256 expectedLength = 40 + hasPrefix.toUint() * 2;
// check that input is the correct length
if (end - begin == expectedLength) {
// length guarantees that this does not overflow, and value is at most type(uint160).max
(bool s, uint256 v) = _tryParseHexUintUncheckedBounds(input, begin, end);
return (s, address(uint160(v)));
} else {
return (false, address(0));
}
}
function _tryParseChr(bytes1 chr) private pure returns (uint8) {
uint8 value = uint8(chr);
// Try to parse `chr`:
// - Case 1: [0-9]
// - Case 2: [a-f]
// - Case 3: [A-F]
// - otherwise not supported
unchecked {
if (value > 47 && value < 58) value -= 48;
else if (value > 96 && value < 103) value -= 87;
else if (value > 64 && value < 71) value -= 55;
else return type(uint8).max;
}
return value;
}
/**
* @dev Escape special characters in JSON strings. This can be useful to prevent JSON injection in NFT metadata.
*
* WARNING: This function should only be used in double quoted JSON strings. Single quotes are not escaped.
*
* NOTE: This function escapes all unicode characters, and not just the ones in ranges defined in section 2.5 of
* RFC-4627 (U+0000 to U+001F, U+0022 and U+005C). ECMAScript's `JSON.parse` does recover escaped unicode
* characters that are not in this range, but other tooling may provide different results.
*/
function escapeJSON(string memory input) internal pure returns (string memory) {
bytes memory buffer = bytes(input);
bytes memory output = new bytes(2 * buffer.length); // worst case scenario
uint256 outputLength = 0;
for (uint256 i; i < buffer.length; ++i) {
bytes1 char = bytes1(_unsafeReadBytesOffset(buffer, i));
if (((SPECIAL_CHARS_LOOKUP & (1 << uint8(char))) != 0)) {
output[outputLength++] = "\\";
if (char == 0x08) output[outputLength++] = "b";
else if (char == 0x09) output[outputLength++] = "t";
else if (char == 0x0a) output[outputLength++] = "n";
else if (char == 0x0c) output[outputLength++] = "f";
else if (char == 0x0d) output[outputLength++] = "r";
else if (char == 0x5c) output[outputLength++] = "\\";
else if (char == 0x22) {
// solhint-disable-next-line quotes
output[outputLength++] = '"';
}
} else {
output[outputLength++] = char;
}
}
// write the actual length and deallocate unused memory
assembly ("memory-safe") {
mstore(output, outputLength)
mstore(0x40, add(output, shl(5, shr(5, add(outputLength, 63)))))
}
return string(output);
}
/**
* @dev Reads a bytes32 from a bytes array without bounds checking.
*
* NOTE: making this function internal would mean it could be used with memory unsafe offset, and marking the
* assembly block as such would prevent some optimizations.
*/
function _unsafeReadBytesOffset(bytes memory buffer, uint256 offset) private pure returns (bytes32 value) {
// This is not memory safe in the general case, but all calls to this private function are within bounds.
assembly ("memory-safe") {
value := mload(add(buffer, add(0x20, offset)))
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* 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[ERC 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.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
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.3.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
/**
* @title HotdogToken
* @dev ERC-20 token used for staking and governance in the LogADog ecosystem
*/
contract HotdogToken is ERC20, AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
uint256 public constant INITIAL_SUPPLY = 100_000_000_000 * 10**18; // 100B tokens
uint256 public constant MAX_SUPPLY = 100_000_000_000 * 10**18; // 100B tokens max
constructor() ERC20("Hotdog Token", "HOTDOG") {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(MINTER_ROLE, msg.sender);
// Mint initial supply to deployer
_mint(msg.sender, INITIAL_SUPPLY);
}
/**
* @notice Mint new tokens (only by minters)
* @param to Address to mint tokens to
* @param amount Amount of tokens to mint
*/
function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) {
require(totalSupply() + amount <= MAX_SUPPLY, "Would exceed max supply");
_mint(to, amount);
}
/**
* @notice Add a new minter
* @param minter Address to grant minter role
*/
function addMinter(address minter) external onlyRole(DEFAULT_ADMIN_ROLE) {
_grantRole(MINTER_ROLE, minter);
}
/**
* @notice Remove a minter
* @param minter Address to revoke minter role
*/
function removeMinter(address minter) external onlyRole(DEFAULT_ADMIN_ROLE) {
_revokeRole(MINTER_ROLE, minter);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Return the 512-bit addition of two uint256.
*
* The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.
*/
function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
assembly ("memory-safe") {
low := add(a, b)
high := lt(low, a)
}
}
/**
* @dev Return the 512-bit multiplication of two uint256.
*
* The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.
*/
function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
// 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
// the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = high * 2²⁵⁶ + low.
assembly ("memory-safe") {
let mm := mulmod(a, b, not(0))
low := mul(a, b)
high := sub(sub(mm, low), lt(mm, low))
}
}
/**
* @dev Returns the addition of two unsigned integers, with a success flag (no overflow).
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
success = c >= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a - b;
success = c <= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a * b;
assembly ("memory-safe") {
// Only true when the multiplication doesn't overflow
// (c / a == b) || (a == 0)
success := or(eq(div(c, a), b), iszero(a))
}
// equivalent to: success ? c : 0
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
success = b > 0;
assembly ("memory-safe") {
// The `DIV` opcode returns zero when the denominator is 0.
result := div(a, b)
}
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
success = b > 0;
assembly ("memory-safe") {
// The `MOD` opcode returns zero when the denominator is 0.
result := mod(a, b)
}
}
}
/**
* @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.
*/
function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryAdd(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.
*/
function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {
(, uint256 result) = trySub(a, b);
return result;
}
/**
* @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.
*/
function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryMul(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
*
* IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
* However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
* one branch when needed, making this function more expensive.
*/
function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
// branchless ternary works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
return b ^ ((a ^ b) * SafeCast.toUint(condition));
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a > b, a, b);
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(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.
Panic.panic(Panic.DIVISION_BY_ZERO);
}
// The following calculation ensures accurate ceiling division without overflow.
// Since a is non-zero, (a - 1) / b will not overflow.
// The largest possible result occurs when (a - 1) / b is type(uint256).max,
// but the largest value we can obtain is type(uint256).max - 1, which happens
// when a = type(uint256).max and b = 1.
unchecked {
return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
}
}
/**
* @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
*
* 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 {
(uint256 high, uint256 low) = mul512(x, y);
// Handle non-overflow cases, 256 by 256 division.
if (high == 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 low / denominator;
}
// Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
if (denominator <= high) {
Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [high low].
uint256 remainder;
assembly ("memory-safe") {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
high := sub(high, gt(remainder, low))
low := sub(low, 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 ("memory-safe") {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [high low] by twos.
low := div(low, twos)
// Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from high into low.
low |= high * twos;
// Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
// that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv ≡ 1 mod 2⁴.
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⁸
inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
inverse *= 2 - denominator * inverse; // inverse mod 2³²
inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶
// 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²⁵⁶. Since the preconditions guarantee that the outcome is
// less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high
// is no longer required.
result = low * inverse;
return result;
}
}
/**
* @dev 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) {
return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
}
/**
* @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.
*/
function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {
unchecked {
(uint256 high, uint256 low) = mul512(x, y);
if (high >= 1 << n) {
Panic.panic(Panic.UNDER_OVERFLOW);
}
return (high << (256 - n)) | (low >> n);
}
}
/**
* @dev Calculates x * y >> n with full precision, following the selected rounding direction.
*/
function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {
return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);
}
/**
* @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
*
* If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
* If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
*
* If the input value is not inversible, 0 is returned.
*
* NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
* inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
*/
function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
unchecked {
if (n == 0) return 0;
// The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
// Used to compute integers x and y such that: ax + ny = gcd(a, n).
// When the gcd is 1, then the inverse of a modulo n exists and it's x.
// ax + ny = 1
// ax = 1 + (-y)n
// ax ≡ 1 (mod n) # x is the inverse of a modulo n
// If the remainder is 0 the gcd is n right away.
uint256 remainder = a % n;
uint256 gcd = n;
// Therefore the initial coefficients are:
// ax + ny = gcd(a, n) = n
// 0a + 1n = n
int256 x = 0;
int256 y = 1;
while (remainder != 0) {
uint256 quotient = gcd / remainder;
(gcd, remainder) = (
// The old remainder is the next gcd to try.
remainder,
// Compute the next remainder.
// Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
// where gcd is at most n (capped to type(uint256).max)
gcd - remainder * quotient
);
(x, y) = (
// Increment the coefficient of a.
y,
// Decrement the coefficient of n.
// Can overflow, but the result is casted to uint256 so that the
// next value of y is "wrapped around" to a value between 0 and n - 1.
x - y * int256(quotient)
);
}
if (gcd != 1) return 0; // No inverse exists.
return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
}
}
/**
* @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
*
* From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
* prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
* `a**(p-2)` is the modular multiplicative inverse of a in Fp.
*
* NOTE: this function does NOT check that `p` is a prime greater than `2`.
*/
function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
unchecked {
return Math.modExp(a, p - 2, p);
}
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
*
* Requirements:
* - modulus can't be zero
* - underlying staticcall to precompile must succeed
*
* IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
* sure the chain you're using it on supports the precompiled contract for modular exponentiation
* at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
* the underlying function will succeed given the lack of a revert, but the result may be incorrectly
* interpreted as 0.
*/
function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
(bool success, uint256 result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
* It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
* to operate modulo 0 or if the underlying precompile reverted.
*
* IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
* you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
* https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
* of a revert, but the result may be incorrectly interpreted as 0.
*/
function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
if (m == 0) return (false, 0);
assembly ("memory-safe") {
let ptr := mload(0x40)
// | Offset | Content | Content (Hex) |
// |-----------|------------|--------------------------------------------------------------------|
// | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x60:0x7f | value of b | 0x<.............................................................b> |
// | 0x80:0x9f | value of e | 0x<.............................................................e> |
// | 0xa0:0xbf | value of m | 0x<.............................................................m> |
mstore(ptr, 0x20)
mstore(add(ptr, 0x20), 0x20)
mstore(add(ptr, 0x40), 0x20)
mstore(add(ptr, 0x60), b)
mstore(add(ptr, 0x80), e)
mstore(add(ptr, 0xa0), m)
// Given the result < m, it's guaranteed to fit in 32 bytes,
// so we can use the memory scratch space located at offset 0.
success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
result := mload(0x00)
}
}
/**
* @dev Variant of {modExp} that supports inputs of arbitrary length.
*/
function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
(bool success, bytes memory result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Variant of {tryModExp} that supports inputs of arbitrary length.
*/
function tryModExp(
bytes memory b,
bytes memory e,
bytes memory m
) internal view returns (bool success, bytes memory result) {
if (_zeroBytes(m)) return (false, new bytes(0));
uint256 mLen = m.length;
// Encode call args in result and move the free memory pointer
result = abi.encodePacked(b.length, e.length, mLen, b, e, m);
assembly ("memory-safe") {
let dataPtr := add(result, 0x20)
// Write result on top of args to avoid allocating extra memory.
success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
// Overwrite the length.
// result.length > returndatasize() is guaranteed because returndatasize() == m.length
mstore(result, mLen)
// Set the memory pointer after the returned data.
mstore(0x40, add(dataPtr, mLen))
}
}
/**
* @dev Returns whether the provided byte array is zero.
*/
function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
for (uint256 i = 0; i < byteArray.length; ++i) {
if (byteArray[i] != 0) {
return false;
}
}
return true;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* This method is based on Newton's method for computing square roots; the algorithm is restricted to only
* using integer operations.
*/
function sqrt(uint256 a) internal pure returns (uint256) {
unchecked {
// Take care of easy edge cases when a == 0 or a == 1
if (a <= 1) {
return a;
}
// In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
// sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
// the current value as `ε_n = | x_n - sqrt(a) |`.
//
// For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
// of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
// bigger than any uint256.
//
// By noticing that
// `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
// we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
// to the msb function.
uint256 aa = a;
uint256 xn = 1;
if (aa >= (1 << 128)) {
aa >>= 128;
xn <<= 64;
}
if (aa >= (1 << 64)) {
aa >>= 64;
xn <<= 32;
}
if (aa >= (1 << 32)) {
aa >>= 32;
xn <<= 16;
}
if (aa >= (1 << 16)) {
aa >>= 16;
xn <<= 8;
}
if (aa >= (1 << 8)) {
aa >>= 8;
xn <<= 4;
}
if (aa >= (1 << 4)) {
aa >>= 4;
xn <<= 2;
}
if (aa >= (1 << 2)) {
xn <<= 1;
}
// We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
//
// We can refine our estimation by noticing that the middle of that interval minimizes the error.
// If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
// This is going to be our x_0 (and ε_0)
xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)
// From here, Newton's method give us:
// x_{n+1} = (x_n + a / x_n) / 2
//
// One should note that:
// x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
// = ((x_n² + a) / (2 * x_n))² - a
// = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
// = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
// = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
// = (x_n² - a)² / (2 * x_n)²
// = ((x_n² - a) / (2 * x_n))²
// ≥ 0
// Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
//
// This gives us the proof of quadratic convergence of the sequence:
// ε_{n+1} = | x_{n+1} - sqrt(a) |
// = | (x_n + a / x_n) / 2 - sqrt(a) |
// = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
// = | (x_n - sqrt(a))² / (2 * x_n) |
// = | ε_n² / (2 * x_n) |
// = ε_n² / | (2 * x_n) |
//
// For the first iteration, we have a special case where x_0 is known:
// ε_1 = ε_0² / | (2 * x_0) |
// ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
// ≤ 2**(2*e-4) / (3 * 2**(e-1))
// ≤ 2**(e-3) / 3
// ≤ 2**(e-3-log2(3))
// ≤ 2**(e-4.5)
//
// For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
// ε_{n+1} = ε_n² / | (2 * x_n) |
// ≤ (2**(e-k))² / (2 * 2**(e-1))
// ≤ 2**(2*e-2*k) / 2**e
// ≤ 2**(e-2*k)
xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above
xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5
xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9
xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18
xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36
xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72
// Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
// ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
// sqrt(a) or sqrt(a) + 1.
return xn - SafeCast.toUint(xn > a / xn);
}
}
/**
* @dev 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 x) internal pure returns (uint256 r) {
// If value has upper 128 bits set, log2 result is at least 128
r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
// If upper 64 bits of 128-bit half set, add 64 to result
r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
// If upper 32 bits of 64-bit half set, add 32 to result
r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
// If upper 16 bits of 32-bit half set, add 16 to result
r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
// If upper 8 bits of 16-bit half set, add 8 to result
r |= SafeCast.toUint((x >> r) > 0xff) << 3;
// If upper 4 bits of 8-bit half set, add 4 to result
r |= SafeCast.toUint((x >> r) > 0xf) << 2;
// Shifts value right by the current result and use it as an index into this lookup table:
//
// | x (4 bits) | index | table[index] = MSB position |
// |------------|---------|-----------------------------|
// | 0000 | 0 | table[0] = 0 |
// | 0001 | 1 | table[1] = 0 |
// | 0010 | 2 | table[2] = 1 |
// | 0011 | 3 | table[3] = 1 |
// | 0100 | 4 | table[4] = 2 |
// | 0101 | 5 | table[5] = 2 |
// | 0110 | 6 | table[6] = 2 |
// | 0111 | 7 | table[7] = 2 |
// | 1000 | 8 | table[8] = 3 |
// | 1001 | 9 | table[9] = 3 |
// | 1010 | 10 | table[10] = 3 |
// | 1011 | 11 | table[11] = 3 |
// | 1100 | 12 | table[12] = 3 |
// | 1101 | 13 | table[13] = 3 |
// | 1110 | 14 | table[14] = 3 |
// | 1111 | 15 | table[15] = 3 |
//
// The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.
assembly ("memory-safe") {
r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))
}
}
/**
* @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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);
}
}
/**
* @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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);
}
}
/**
* @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 x) internal pure returns (uint256 r) {
// If value has upper 128 bits set, log2 result is at least 128
r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
// If upper 64 bits of 128-bit half set, add 64 to result
r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
// If upper 32 bits of 64-bit half set, add 32 to result
r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
// If upper 16 bits of 32-bit half set, add 16 to result
r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
// Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8
return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);
}
/**
* @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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);
}
}
/**
* @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.1.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if (value > type(uint56).max) {
revert SafeCastOverflowedUintDowncast(56, value);
}
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) {
revert SafeCastOverflowedUintDowncast(48, value);
}
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toUint40(uint256 value) internal pure returns (uint40) {
if (value > type(uint40).max) {
revert SafeCastOverflowedUintDowncast(40, value);
}
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
if (value > type(uint32).max) {
revert SafeCastOverflowedUintDowncast(32, value);
}
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toUint24(uint256 value) internal pure returns (uint24) {
if (value > type(uint24).max) {
revert SafeCastOverflowedUintDowncast(24, value);
}
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
if (value > type(uint16).max) {
revert SafeCastOverflowedUintDowncast(16, value);
}
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toUint8(uint256 value) internal pure returns (uint8) {
if (value > type(uint8).max) {
revert SafeCastOverflowedUintDowncast(8, value);
}
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
if (value < 0) {
revert SafeCastOverflowedIntToUint(value);
}
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(248, value);
}
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(240, value);
}
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(232, value);
}
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(224, value);
}
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(216, value);
}
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(208, value);
}
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(200, value);
}
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(192, value);
}
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(184, value);
}
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(176, value);
}
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(168, value);
}
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(160, value);
}
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(152, value);
}
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(144, value);
}
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(136, value);
}
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(128, value);
}
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(120, value);
}
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(112, value);
}
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(104, value);
}
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(96, value);
}
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(88, value);
}
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(80, value);
}
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(72, value);
}
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(64, value);
}
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(56, value);
}
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(48, value);
}
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(40, value);
}
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(32, value);
}
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(24, value);
}
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(16, value);
}
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(8, value);
}
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
if (value > uint256(type(int256).max)) {
revert SafeCastOverflowedUintToInt(value);
}
return int256(value);
}
/**
* @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
*/
function toUint(bool b) internal pure returns (uint256 u) {
assembly ("memory-safe") {
u := iszero(iszero(b))
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
*
* IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
* However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
* one branch when needed, making this function more expensive.
*/
function ternary(bool condition, int256 a, int256 b) internal pure returns (int256) {
unchecked {
// branchless ternary works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
return b ^ ((a ^ b) * int256(SafeCast.toUint(condition)));
}
}
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return ternary(a > b, a, b);
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return ternary(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 {
// Formula from the "Bit Twiddling Hacks" by Sean Eron Anderson.
// Since `n` is a signed integer, the generated bytecode will use the SAR opcode to perform the right shift,
// taking advantage of the most significant (or "sign" bit) in two's complement representation.
// This opcode adds new most significant bits set to the value of the previous most significant bit. As a result,
// the mask will either be `bytes32(0)` (if n is positive) or `~bytes32(0)` (if n is negative).
int256 mask = n >> 255;
// A `bytes32(0)` mask leaves the input unchanged, while a `~bytes32(0)` mask complements it.
return uint256((n + mask) ^ mask);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.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 ERC-20
* applications.
*/
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}.
*
* Both 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}.
*
* Skips emitting an {Approval} event indicating an allowance update. This is not
* required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
*
* 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:
*
* ```solidity
* 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.1.0) (utils/Panic.sol)
pragma solidity ^0.8.20;
/**
* @dev Helper library for emitting standardized panic codes.
*
* ```solidity
* contract Example {
* using Panic for uint256;
*
* // Use any of the declared internal constants
* function foo() { Panic.GENERIC.panic(); }
*
* // Alternatively
* function foo() { Panic.panic(Panic.GENERIC); }
* }
* ```
*
* Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
*
* _Available since v5.1._
*/
// slither-disable-next-line unused-state
library Panic {
/// @dev generic / unspecified error
uint256 internal constant GENERIC = 0x00;
/// @dev used by the assert() builtin
uint256 internal constant ASSERT = 0x01;
/// @dev arithmetic underflow or overflow
uint256 internal constant UNDER_OVERFLOW = 0x11;
/// @dev division or modulo by zero
uint256 internal constant DIVISION_BY_ZERO = 0x12;
/// @dev enum conversion error
uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
/// @dev invalid encoding in storage
uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
/// @dev empty array pop
uint256 internal constant EMPTY_ARRAY_POP = 0x31;
/// @dev array out of bounds access
uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
/// @dev resource error (too large allocation or too large array)
uint256 internal constant RESOURCE_ERROR = 0x41;
/// @dev calling invalid internal function
uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;
/// @dev Reverts with a panic code. Recommended to use with
/// the internal constants with predefined codes.
function panic(uint256 code) internal pure {
assembly ("memory-safe") {
mstore(0x00, 0x4e487b71)
mstore(0x20, code)
revert(0x1c, 0x24)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.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 ERC-20 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.1.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC-20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 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 ERC-721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-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 ERC-1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 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);
}{
"remappings": [
"@thirdweb-dev/=node_modules/@thirdweb-dev/",
"@openzeppelin/=lib/openzeppelin-contracts/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-std/=lib/openzeppelin-contracts/lib/forge-std/src/",
"halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/"
],
"optimizer": {
"enabled": false,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": false,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_platformReferrer","type":"address"},{"internalType":"address","name":"_attestationManager","type":"address"},{"internalType":"address","name":"_coinDeploymentManager","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"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"logId","type":"uint256"},{"indexed":true,"internalType":"address","name":"attestor","type":"address"},{"indexed":false,"internalType":"bool","name":"isValid","type":"bool"}],"name":"AttestationMade","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"logId","type":"uint256"},{"indexed":true,"internalType":"address","name":"attestor","type":"address"}],"name":"AttestationRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"logId","type":"uint256"},{"indexed":true,"internalType":"address","name":"eater","type":"address"},{"indexed":false,"internalType":"bool","name":"isValid","type":"bool"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"DogValidityUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"logId","type":"uint256"},{"indexed":true,"internalType":"address","name":"logger","type":"address"}],"name":"HotdogLogRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"logId","type":"uint256"},{"indexed":true,"internalType":"address","name":"logger","type":"address"},{"indexed":true,"internalType":"address","name":"eater","type":"address"},{"indexed":false,"internalType":"string","name":"imageUri","type":"string"},{"indexed":false,"internalType":"string","name":"metadataUri","type":"string"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"},{"indexed":false,"internalType":"address","name":"zoraCoin","type":"address"}],"name":"HotdogLogged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"}],"name":"OperatorAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"}],"name":"OperatorRemoved","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"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OPERATOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"addOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"attestHotdogLog","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"logId","type":"uint256"},{"internalType":"address","name":"attestor","type":"address"},{"internalType":"bool","name":"isValid","type":"bool"}],"name":"attestHotdogLogOnBehalf","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"attestationManager","outputs":[{"internalType":"contract AttestationManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"attestations","outputs":[{"internalType":"address","name":"attestor","type":"address"},{"internalType":"bool","name":"isValid","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"coinDeploymentManager","outputs":[{"internalType":"contract CoinDeploymentManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"logId","type":"uint256"}],"name":"emitValidityUpdate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"logId","type":"uint256"}],"name":"getAttestationCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"offset","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"getDogsInTimeRange","outputs":[{"internalType":"uint256[]","name":"logIds","type":"uint256[]"},{"internalType":"address[]","name":"eaters","type":"address[]"},{"internalType":"uint256[]","name":"timestamps","type":"uint256[]"},{"internalType":"bool[]","name":"hasAttestationEnded","type":"bool[]"},{"internalType":"bool[]","name":"isValid","type":"bool[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"logId","type":"uint256"}],"name":"getHotdogLog","outputs":[{"components":[{"internalType":"uint256","name":"logId","type":"uint256"},{"internalType":"string","name":"imageUri","type":"string"},{"internalType":"string","name":"metadataUri","type":"string"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"address","name":"eater","type":"address"},{"internalType":"address","name":"logger","type":"address"},{"internalType":"address","name":"zoraCoin","type":"address"}],"internalType":"struct LogADog.HotdogLog","name":"log","type":"tuple"},{"internalType":"uint256","name":"validCount","type":"uint256"},{"internalType":"uint256","name":"invalidCount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"getHotdogLogs","outputs":[{"components":[{"internalType":"uint256","name":"logId","type":"uint256"},{"internalType":"string","name":"imageUri","type":"string"},{"internalType":"string","name":"metadataUri","type":"string"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"address","name":"eater","type":"address"},{"internalType":"address","name":"logger","type":"address"},{"internalType":"address","name":"zoraCoin","type":"address"}],"internalType":"struct LogADog.HotdogLog[]","name":"logs","type":"tuple[]"},{"internalType":"uint256[]","name":"validCounts","type":"uint256[]"},{"internalType":"uint256[]","name":"invalidCounts","type":"uint256[]"},{"internalType":"bool[]","name":"userHasAttested","type":"bool[]"},{"internalType":"bool[]","name":"userAttestations","type":"bool[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getHotdogLogsCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"getHotdogLogsRange","outputs":[{"components":[{"internalType":"uint256","name":"logId","type":"uint256"},{"internalType":"string","name":"imageUri","type":"string"},{"internalType":"string","name":"metadataUri","type":"string"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"address","name":"eater","type":"address"},{"internalType":"address","name":"logger","type":"address"},{"internalType":"address","name":"zoraCoin","type":"address"}],"internalType":"struct LogADog.HotdogLog[]","name":"logs","type":"tuple[]"}],"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":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"pageSize","type":"uint256"}],"name":"getTotalPagesForLogs","outputs":[{"internalType":"uint256","name":"totalPages","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserHotdogLogCount","outputs":[{"internalType":"uint256","name":"count","type":"uint256"}],"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":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"hasAttested","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","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":"","type":"uint256"}],"name":"hotdogLogs","outputs":[{"internalType":"uint256","name":"logId","type":"uint256"},{"internalType":"string","name":"imageUri","type":"string"},{"internalType":"string","name":"metadataUri","type":"string"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"address","name":"eater","type":"address"},{"internalType":"address","name":"logger","type":"address"},{"internalType":"address","name":"zoraCoin","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"imageUri","type":"string"},{"internalType":"string","name":"metadataUri","type":"string"},{"internalType":"string","name":"coinUri","type":"string"},{"internalType":"address","name":"eater","type":"address"},{"internalType":"bytes","name":"poolConfig","type":"bytes"}],"name":"logHotdog","outputs":[{"internalType":"uint256","name":"logId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"string","name":"imageUri","type":"string"},{"internalType":"string","name":"metadataUri","type":"string"},{"internalType":"string","name":"coinUri","type":"string"},{"internalType":"address","name":"eater","type":"address"},{"internalType":"bytes","name":"poolConfig","type":"bytes"}],"name":"logHotdogOnBehalf","outputs":[{"internalType":"uint256","name":"logId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"platformReferrer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"removeOperator","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":"uint256","name":"logId","type":"uint256"}],"name":"resolveAttestationPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"logId","type":"uint256"}],"name":"revokeAttestation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"logId","type":"uint256"},{"internalType":"address","name":"attestor","type":"address"}],"name":"revokeAttestationOnBehalf","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"logId","type":"uint256"}],"name":"revokeHotdogLog","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"logId","type":"uint256"},{"internalType":"address","name":"owner","type":"address"}],"name":"revokeHotdogLogOnBehalf","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":"address","name":"_attestationManager","type":"address"}],"name":"setAttestationManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_coinDeploymentManager","type":"address"}],"name":"setCoinDeploymentManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_platformReferrer","type":"address"}],"name":"setPlatformReferrer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]Contract Creation Code
608060405234801561000f575f5ffd5b50604051615f49380380615f498339818101604052810190610031919061030e565b8260015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508160025f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060035f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506101035f5f1b3361015160201b60201c565b506101487f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92973360e36befcc2db9c45e411e5e4840fe33a8f21b061015160201b60201c565b5050505061035e565b5f610162838361024660201b60201c565b61023c5760015f5f8581526020019081526020015f205f015f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055506101d96102a960201b60201c565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a460019050610240565b5f90505b92915050565b5f5f5f8481526020019081526020015f205f015f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff16905092915050565b5f33905090565b5f5ffd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6102dd826102b4565b9050919050565b6102ed816102d3565b81146102f7575f5ffd5b50565b5f81519050610308816102e4565b92915050565b5f5f5f60608486031215610325576103246102b0565b5b5f610332868287016102fa565b9350506020610343868287016102fa565b9250506040610354868287016102fa565b9150509250925092565b615bde8061036b5f395ff3fe608060405260043610610219575f3560e01c8063a4d413b011610122578063db86c6b4116100aa578063f5b541a61161006e578063f5b541a614610824578063f7a009461461084e578063fadb24d314610876578063fe1faae51461089e578063ff1f94b5146108da57610219565b8063db86c6b4146106fd578063dd03888e14610739578063de2c2e8b14610776578063e7de4e76146107b2578063f4f611c9146107e257610219565b8063bbc8d3f7116100f1578063bbc8d3f71461061f578063ce6fde0f14610647578063cea7de581461066f578063cf2b6278146106ab578063d547741f146106d557610219565b8063a4d413b014610567578063ac8a584a146105a5578063b1c16bf1146105cd578063b565b9ed146105f757610219565b80634b65613b116101a55780637fbb1949116101745780637fbb19491461048157806391d14854146104a95780639870d7fe146104e5578063a217fddf1461050d578063a44252ed1461053757610219565b80634b65613b146103dd5780634f902b141461040557806351845bf61461042f578063523b75df1461045957610219565b8063248a9ca3116101ec578063248a9ca3146102e95780632f2ff15d1461032557806330aa61e81461034d57806336568abe146103755780633a5c39f71461039d57610219565b806301afa8d01461021d57806301ffc9a71461024557806313f8fd4f146102815780631a32fc88146102c1575b5f5ffd5b348015610228575f5ffd5b50610243600480360381019061023e9190613f85565b610916565b005b348015610250575f5ffd5b5061026b60048036038101906102669190614005565b610a2f565b604051610278919061404a565b60405180910390f35b34801561028c575f5ffd5b506102a760048036038101906102a29190614063565b610aa8565b6040516102b895949392919061431c565b60405180910390f35b3480156102cc575f5ffd5b506102e760048036038101906102e291906143ba565b611078565b005b3480156102f4575f5ffd5b5061030f600480360381019061030a9190614418565b6110c8565b60405161031c9190614452565b60405180910390f35b348015610330575f5ffd5b5061034b6004803603810190610346919061446b565b6110e4565b005b348015610358575f5ffd5b50610373600480360381019061036e9190613f85565b611106565b005b348015610380575f5ffd5b5061039b6004803603810190610396919061446b565b6112bf565b005b3480156103a8575f5ffd5b506103c360048036038101906103be91906144a9565b61133a565b6040516103d49594939291906146eb565b60405180910390f35b3480156103e8575f5ffd5b5061040360048036038101906103fe91906143ba565b611a27565b005b348015610410575f5ffd5b50610419611a77565b60405161042691906147ba565b60405180910390f35b34801561043a575f5ffd5b50610443611a9c565b60405161045091906147e2565b60405180910390f35b348015610464575f5ffd5b5061047f600480360381019061047a91906147fb565b611ac1565b005b34801561048c575f5ffd5b506104a760048036038101906104a29190613f85565b611bd0565b005b3480156104b4575f5ffd5b506104cf60048036038101906104ca919061446b565b611c75565b6040516104dc919061404a565b60405180910390f35b3480156104f0575f5ffd5b5061050b600480360381019061050691906143ba565b611cd8565b005b348015610518575f5ffd5b50610521611d55565b60405161052e9190614452565b60405180910390f35b610551600480360381019061054c91906149c2565b611d5b565b60405161055e9190614abb565b60405180910390f35b348015610572575f5ffd5b5061058d60048036038101906105889190613f85565b611de4565b60405161059c93929190614b74565b60405180910390f35b3480156105b0575f5ffd5b506105cb60048036038101906105c691906143ba565b6120ac565b005b3480156105d8575f5ffd5b506105e1612129565b6040516105ee9190614bd0565b60405180910390f35b348015610602575f5ffd5b5061061d60048036038101906106189190614c13565b61214e565b005b34801561062a575f5ffd5b5061064560048036038101906106409190614c63565b612277565b005b348015610652575f5ffd5b5061066d600480360381019061066891906143ba565b6122b2565b005b34801561067a575f5ffd5b50610695600480360381019061069091906143ba565b612302565b6040516106a29190614abb565b60405180910390f35b3480156106b6575f5ffd5b506106bf6123ac565b6040516106cc9190614abb565b60405180910390f35b3480156106e0575f5ffd5b506106fb60048036038101906106f6919061446b565b6123b8565b005b348015610708575f5ffd5b50610723600480360381019061071e9190614cb3565b6123da565b6040516107309190614cf1565b60405180910390f35b348015610744575f5ffd5b5061075f600480360381019061075a9190614cb3565b612751565b60405161076d929190614d11565b60405180910390f35b348015610781575f5ffd5b5061079c60048036038101906107979190614d38565b6127b2565b6040516107a99190614abb565b60405180910390f35b6107cc60048036038101906107c791906149c2565b61286e565b6040516107d99190614abb565b60405180910390f35b3480156107ed575f5ffd5b5061080860048036038101906108039190613f85565b6128f2565b60405161081b9796959493929190614dd0565b60405180910390f35b34801561082f575f5ffd5b50610838612aa8565b6040516108459190614452565b60405180910390f35b348015610859575f5ffd5b50610874600480360381019061086f9190613f85565b612acc565b005b348015610881575f5ffd5b5061089c600480360381019061089791906147fb565b612bc3565b005b3480156108a9575f5ffd5b506108c460048036038101906108bf91906147fb565b612de4565b6040516108d1919061404a565b60405180910390f35b3480156108e5575f5ffd5b5061090060048036038101906108fb9190613f85565b612e0e565b60405161090d9190614abb565b60405180910390f35b5f73ffffffffffffffffffffffffffffffffffffffff1660025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16036109a5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161099c90614e95565b60405180910390fd5b60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166301afa8d0826040518263ffffffff1660e01b81526004016109ff9190614abb565b5f604051808303815f87803b158015610a16575f5ffd5b505af1158015610a28573d5f5f3e3d5ffd5b5050505050565b5f7f7965db0b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480610aa15750610aa082612e2b565b5b9050919050565b60608060608060605f5f90505f5f90505b600480549050811015610b3d578a60048281548110610adb57610ada614eb3565b5b905f5260205f2090600702016003015410158015610b1c57508960048281548110610b0957610b08614eb3565b5b905f5260205f2090600702016003015411155b15610b30578180610b2c90614f0d565b9250505b8080600101915050610ab9565b505f888211610b4c575f610b59565b8882610b589190614f54565b5b9050878111610b685780610b6a565b875b90508067ffffffffffffffff811115610b8657610b85614841565b5b604051908082528060200260200182016040528015610bb45781602001602082028036833780820191505090505b5096508067ffffffffffffffff811115610bd157610bd0614841565b5b604051908082528060200260200182016040528015610bff5781602001602082028036833780820191505090505b5095508067ffffffffffffffff811115610c1c57610c1b614841565b5b604051908082528060200260200182016040528015610c4a5781602001602082028036833780820191505090505b5094508067ffffffffffffffff811115610c6757610c66614841565b5b604051908082528060200260200182016040528015610c955781602001602082028036833780820191505090505b5093508067ffffffffffffffff811115610cb257610cb1614841565b5b604051908082528060200260200182016040528015610ce05781602001602082028036833780820191505090505b5092505f5f90505f5f90505f5f90505b60048054905081108015610d0357508383105b15611068578d60048281548110610d1d57610d1c614eb3565b5b905f5260205f2090600702016003015410158015610d5e57508c60048281548110610d4b57610d4a614eb3565b5b905f5260205f2090600702016003015411155b15611055578b821061104557808a8481518110610d7e57610d7d614eb3565b5b60200260200101818152505060048181548110610d9e57610d9d614eb3565b5b905f5260205f2090600702016004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16898481518110610de057610ddf614eb3565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505060048181548110610e2e57610e2d614eb3565b5b905f5260205f20906007020160030154888481518110610e5157610e50614eb3565b5b6020026020010181815250505f73ffffffffffffffffffffffffffffffffffffffff1660025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614610fdc575f5f5f60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632f99b231856040518263ffffffff1660e01b8152600401610f0e9190614abb565b60e060405180830381865afa158015610f29573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f4d9190614fd2565b5095505050935093505082421180610f89575060016002811115610f7457610f7361506f565b5b826002811115610f8757610f8661506f565b5b145b8a8781518110610f9c57610f9b614eb3565b5b60200260200101901515908115158152505080898781518110610fc257610fc1614eb3565b5b602002602001019015159081151581525050505050611032565b6001878481518110610ff157610ff0614eb3565b5b60200260200101901515908115158152505061100c81612e94565b86848151811061101f5761101e614eb3565b5b6020026020010190151590811515815250505b828061103d90614f0d565b935050611054565b818061105090614f0d565b9250505b5b808061106090614f0d565b915050610cf0565b5050505050945094509450945094565b5f5f1b61108481612fe3565b8160025f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b5f5f5f8381526020019081526020015f20600101549050919050565b6110ed826110c8565b6110f681612fe3565b6111008383612ff7565b50505050565b803373ffffffffffffffffffffffffffffffffffffffff166004828154811061113257611131614eb3565b5b905f5260205f2090600702016004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146111b7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ae9061510c565b60405180910390fd5b600482815481106111cb576111ca614eb3565b5b905f5260205f2090600702015f5f82015f9055600182015f6111ed9190613e6f565b600282015f6111fc9190613e6f565b600382015f9055600482015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600582015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600682015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550503373ffffffffffffffffffffffffffffffffffffffff16827fb5429b4807204ef014ef6a8143de5f24b3cd12e1c35608b6050adde1a36531a260405160405180910390a35050565b6112c76130e0565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161461132b576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61133582826130e7565b505050565b60608060608060605f5f90505f5f90505b6004805490508110156113cf578b6004828154811061136d5761136c614eb3565b5b905f5260205f20906007020160030154101580156113ae57508a6004828154811061139b5761139a614eb3565b5b905f5260205f2090600702016003015411155b156113c25781806113be90614f0d565b9250505b808060010191505061134b565b505f8882116113de575f6113eb565b88826113ea9190614f54565b5b90508781116113fa57806113fc565b875b90508067ffffffffffffffff81111561141857611417614841565b5b60405190808252806020026020018201604052801561145157816020015b61143e613eac565b8152602001906001900390816114365790505b5096508067ffffffffffffffff81111561146e5761146d614841565b5b60405190808252806020026020018201604052801561149c5781602001602082028036833780820191505090505b5095508067ffffffffffffffff8111156114b9576114b8614841565b5b6040519080825280602002602001820160405280156114e75781602001602082028036833780820191505090505b5094508067ffffffffffffffff81111561150457611503614841565b5b6040519080825280602002602001820160405280156115325781602001602082028036833780820191505090505b5093508067ffffffffffffffff81111561154f5761154e614841565b5b60405190808252806020026020018201604052801561157d5781602001602082028036833780820191505090505b5092505f5f90505f5f90505f60048054905090505b5f811180156115a057508383105b15611a15578e60046001836115b59190614f54565b815481106115c6576115c5614eb3565b5b905f5260205f209060070201600301541015801561161357508d60046001836115ef9190614f54565b81548110611600576115ff614eb3565b5b905f5260205f2090600702016003015411155b15611a02578b82106119f257600460018261162e9190614f54565b8154811061163f5761163e614eb3565b5b905f5260205f2090600702016040518060e00160405290815f820154815260200160018201805461166f90615157565b80601f016020809104026020016040519081016040528092919081815260200182805461169b90615157565b80156116e65780601f106116bd576101008083540402835291602001916116e6565b820191905f5260205f20905b8154815290600101906020018083116116c957829003601f168201915b505050505081526020016002820180546116ff90615157565b80601f016020809104026020016040519081016040528092919081815260200182805461172b90615157565b80156117765780601f1061174d57610100808354040283529160200191611776565b820191905f5260205f20905b81548152906001019060200180831161175957829003601f168201915b5050505050815260200160038201548152602001600482015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600582015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600682015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815250508a848151811061189b5761189a614eb3565b5b60200260200101819052506118bb6001826118b69190614f54565b6131d0565b8a85815181106118ce576118cd614eb3565b5b602002602001018a86815181106118e8576118e7614eb3565b5b602002602001018281525082815250505060065f6001836119099190614f54565b81526020019081526020015f205f8e73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff1687848151811061197157611970614eb3565b5b60200260200101901515908115158152505086838151811061199657611995614eb3565b5b6020026020010151156119df576119b96001826119b39190614f54565b8e613268565b8684815181106119cc576119cb614eb3565b5b6020026020010190151590811515815250505b82806119ea90614f0d565b935050611a01565b81806119fd90614f0d565b9250505b5b8080611a0d90615187565b915050611592565b50505050509550955095509550959050565b5f5f1b611a3381612fe3565b8160035f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b611aeb7f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92933611c75565b611b2a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b21906151f8565b60405180910390fd5b60065f8381526020019081526020015f205f8273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff16611bc2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611bb990615286565b60405180910390fd5b611bcc828261339b565b5050565b60065f8281526020019081526020015f205f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff16611c68576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c5f90615314565b60405180910390fd5b611c72813361339b565b50565b5f5f5f8481526020019081526020015f205f015f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff16905092915050565b5f5f1b611ce481612fe3565b611d0e7f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b929836110e4565b8173ffffffffffffffffffffffffffffffffffffffff167fac6fa858e9350a46cec16539926e0fde25b7629f84b5a72bffaae4df888ae86d60405160405180910390a25050565b5f5f1b81565b5f3373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614611dca576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611dc1906153a2565b60405180910390fd5b611dd887878787878761345e565b90509695505050505050565b611dec613eac565b5f5f6004805490508410611e35576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611e2c9061540a565b60405180910390fd5b60048481548110611e4957611e48614eb3565b5b905f5260205f2090600702016040518060e00160405290815f8201548152602001600182018054611e7990615157565b80601f0160208091040260200160405190810160405280929190818152602001828054611ea590615157565b8015611ef05780601f10611ec757610100808354040283529160200191611ef0565b820191905f5260205f20905b815481529060010190602001808311611ed357829003601f168201915b50505050508152602001600282018054611f0990615157565b80601f0160208091040260200160405190810160405280929190818152602001828054611f3590615157565b8015611f805780601f10611f5757610100808354040283529160200191611f80565b820191905f5260205f20905b815481529060010190602001808311611f6357829003601f168201915b5050505050815260200160038201548152602001600482015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600582015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600682015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681525050925061209d846131d0565b80925081935050509193909250565b5f5f1b6120b881612fe3565b6120e27f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b929836123b8565b8173ffffffffffffffffffffffffffffffffffffffff167f80c0b871b97b595b16a7741c1b06fed0c6f6f558639f18ccbce50724325dc40d60405160405180910390a25050565b60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6121787f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92933611c75565b6121b7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016121ae906151f8565b60405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff16600484815481106121e2576121e1614eb3565b5b905f5260205f2090600702016004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1603612267576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161225e90615472565b60405180910390fd5b612272838383613888565b505050565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016122a990615526565b60405180910390fd5b5f5f1b6122be81612fe3565b8160015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b5f5f5f90505b6004805490508110156123a6578273ffffffffffffffffffffffffffffffffffffffff16600482815481106123405761233f614eb3565b5b905f5260205f2090600702016004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff160361239957818061239590614f0d565b9250505b8080600101915050612308565b50919050565b5f600480549050905090565b6123c1826110c8565b6123ca81612fe3565b6123d483836130e7565b50505050565b60606004805490508310612423576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161241a9061558e565b60405180910390fd5b5f828461243091906155ac565b90506004805490508111156124485760048054905090505b83816124549190614f54565b67ffffffffffffffff81111561246d5761246c614841565b5b6040519080825280602002602001820160405280156124a657816020015b612493613eac565b81526020019060019003908161248b5790505b5091505f8490505b8181101561274957600481815481106124ca576124c9614eb3565b5b905f5260205f2090600702016040518060e00160405290815f82015481526020016001820180546124fa90615157565b80601f016020809104026020016040519081016040528092919081815260200182805461252690615157565b80156125715780601f1061254857610100808354040283529160200191612571565b820191905f5260205f20905b81548152906001019060200180831161255457829003601f168201915b5050505050815260200160028201805461258a90615157565b80601f01602080910402602001604051908101604052809291908181526020018280546125b690615157565b80156126015780601f106125d857610100808354040283529160200191612601565b820191905f5260205f20905b8154815290600101906020018083116125e457829003601f168201915b5050505050815260200160038201548152602001600482015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600582015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600682015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815250508386836127209190614f54565b8151811061273157612730614eb3565b5b602002602001018190525080806001019150506124ae565b505092915050565b6005602052815f5260405f20818154811061276a575f80fd5b905f5260205f20015f9150915050805f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690805f0160149054906101000a900460ff16905082565b5f5f5f90505f5f90505b6004805490508110156128405785600482815481106127de576127dd614eb3565b5b905f5260205f209060070201600301541015801561281f5750846004828154811061280c5761280b614eb3565b5b905f5260205f2090600702016003015411155b1561283357818061282f90614f0d565b9250505b80806001019150506127bc565b50826001848361285091906155ac565b61285a9190614f54565b612864919061560c565b9150509392505050565b5f6128997f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92933611c75565b6128d8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016128cf906151f8565b60405180910390fd5b6128e687878787878761345e565b90509695505050505050565b60048181548110612901575f80fd5b905f5260205f2090600702015f91509050805f01549080600101805461292690615157565b80601f016020809104026020016040519081016040528092919081815260200182805461295290615157565b801561299d5780601f106129745761010080835404028352916020019161299d565b820191905f5260205f20905b81548152906001019060200180831161298057829003601f168201915b5050505050908060020180546129b290615157565b80601f01602080910402602001604051908101604052809291908181526020018280546129de90615157565b8015612a295780601f10612a0057610100808354040283529160200191612a29565b820191905f5260205f20905b815481529060010190602001808311612a0c57829003601f168201915b505050505090806003015490806004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690806005015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690806006015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905087565b7f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92981565b5f5f1b612ad881612fe3565b6004805490508210612b1f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612b169061540a565b60405180910390fd5b5f612b2983612e94565b905060048381548110612b3f57612b3e614eb3565b5b905f5260205f2090600702016004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16837f2b04f5b936a447dff175af18ad4a7f2ed0867e7a83f4916937f859f360e3d1508342604051612bb692919061563c565b60405180910390a3505050565b612bed7f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92933611c75565b612c2c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612c23906151f8565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660048381548110612c5757612c56614eb3565b5b905f5260205f2090600702016004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612cdc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612cd3906156d3565b60405180910390fd5b60048281548110612cf057612cef614eb3565b5b905f5260205f2090600702015f5f82015f9055600182015f612d129190613e6f565b600282015f612d219190613e6f565b600382015f9055600482015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600582015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600682015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550508073ffffffffffffffffffffffffffffffffffffffff16827fb5429b4807204ef014ef6a8143de5f24b3cd12e1c35608b6050adde1a36531a260405160405180910390a35050565b6006602052815f5260405f20602052805f5260405f205f915091509054906101000a900460ff1681565b5f60055f8381526020019081526020015f20805490509050919050565b5f7f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b5f5f73ffffffffffffffffffffffffffffffffffffffff1660025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612fc6575f5f60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632f99b231856040518263ffffffff1660e01b8152600401612f459190614abb565b60e060405180830381865afa158015612f60573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f849190614fd2565b50955050509350505060016002811115612fa157612fa061506f565b5b826002811115612fb457612fb361506f565b5b03612fc3578092505050612fde565b50505b5f5f612fd1846131d0565b9150915080821015925050505b919050565b612ff481612fef6130e0565b613ba1565b50565b5f6130028383611c75565b6130d65760015f5f8581526020019081526020015f205f015f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055506130736130e0565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4600190506130da565b5f90505b92915050565b5f33905090565b5f6130f28383611c75565b156131c6575f5f5f8581526020019081526020015f205f015f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055506131636130e0565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16847ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a4600190506131ca565b5f90505b92915050565b5f5f5f5f90505b60055f8581526020019081526020015f20805490508110156132625760055f8581526020019081526020015f20818154811061321657613215614eb3565b5b905f5260205f20015f0160149054906101000a900460ff161561324657828061323e90614f0d565b935050613255565b818061325190614f0d565b9250505b80806001019150506131d7565b50915091565b5f5f5f90505b60055f8581526020019081526020015f2080549050811015613359578273ffffffffffffffffffffffffffffffffffffffff1660055f8681526020019081526020015f2082815481106132c4576132c3614eb3565b5b905f5260205f20015f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff160361334c5760055f8581526020019081526020015f20818154811061332c5761332b614eb3565b5b905f5260205f20015f0160149054906101000a900460ff16915050613395565b808060010191505061326e565b506040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161338c9061573b565b60405180910390fd5b92915050565b5f6133a68383613bf2565b90506133b28382613ceb565b5f60065f8581526020019081526020015f205f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff16837f6570a541a707009541b6b7f8f3c937278180f6169d5b6db44bea3d1426e6f8dd60405160405180910390a3505050565b5f60048054905090505f5f73ffffffffffffffffffffffffffffffffffffffff1660035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146135865760035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663271e75ff3484888a898960015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518863ffffffff1660e01b815260040161354296959493929190615795565b60206040518083038185885af115801561355e573d5f5f3e3d5ffd5b50505050506040513d601f19601f82011682018060405250810190613583919061580a565b90505b60046040518060e001604052808481526020018a81526020018981526020014281526020018773ffffffffffffffffffffffffffffffffffffffff1681526020013373ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff16815250908060018154018082558091505060019003905f5260205f2090600702015f909190919091505f820151815f0155602082015181600101908161364091906159cc565b50604082015181600201908161365691906159cc565b50606082015181600301556080820151816004015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060a0820151816005015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060c0820151816006015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050505f73ffffffffffffffffffffffffffffffffffffffff1660025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146138115760025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663df286f4c836040518263ffffffff1660e01b81526004016137e39190614abb565b5f604051808303815f87803b1580156137fa575f5ffd5b505af115801561380c573d5f5f3e3d5ffd5b505050505b8473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16837f0d4cd2468ea6d0f2bf4dd33a6419382b5e4dcb8d6ffcc581703619157d2b1ccc8b8b42876040516138759493929190615a9b565b60405180910390a4509695505050505050565b5f5f90505f5f90505b60055f8681526020019081526020015f2080549050811015613a1c578373ffffffffffffffffffffffffffffffffffffffff1660055f8781526020019081526020015f2082815481106138e7576138e6614eb3565b5b905f5260205f20015f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1603613a0f5782151560055f8781526020019081526020015f20828154811061395257613951614eb3565b5b905f5260205f20015f0160149054906101000a900460ff16151514613a06578260055f8781526020019081526020015f20828154811061399557613994614eb3565b5b905f5260205f20015f0160146101000a81548160ff0219169083151502179055508373ffffffffffffffffffffffffffffffffffffffff16857f5e211bd87f797087f36d0cc81a4dc3bbff6589b5674820fd67a53ab162291e74856040516139fd919061404a565b60405180910390a35b60019150613a1c565b8080600101915050613891565b5080613b9b5760055f8581526020019081526020015f2060405180604001604052808573ffffffffffffffffffffffffffffffffffffffff168152602001841515815250908060018154018082558091505060019003905f5260205f20015f909190919091505f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506020820151815f0160146101000a81548160ff0219169083151502179055505050600160065f8681526020019081526020015f205f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055508273ffffffffffffffffffffffffffffffffffffffff16847f5e211bd87f797087f36d0cc81a4dc3bbff6589b5674820fd67a53ab162291e7484604051613b92919061404a565b60405180910390a35b50505050565b613bab8282611c75565b613bee5780826040517fe2517d3f000000000000000000000000000000000000000000000000000000008152600401613be5929190615aec565b60405180910390fd5b5050565b5f5f5f90505b60055f8581526020019081526020015f2080549050811015613ca9578273ffffffffffffffffffffffffffffffffffffffff1660055f8681526020019081526020015f208281548110613c4e57613c4d614eb3565b5b905f5260205f20015f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1603613c9c5780915050613ce5565b8080600101915050613bf8565b506040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613cdc90615b5d565b60405180910390fd5b92915050565b5f600160055f8581526020019081526020015f2080549050613d0d9190614f54565b9050808214613dfe5760055f8481526020019081526020015f208181548110613d3957613d38614eb3565b5b905f5260205f200160055f8581526020019081526020015f208381548110613d6457613d63614eb3565b5b905f5260205f20015f82015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f820160149054906101000a900460ff16815f0160146101000a81548160ff0219169083151502179055509050505b60055f8481526020019081526020015f20805480613e1f57613e1e615b7b565b5b600190038181905f5260205f20015f5f82015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555f820160146101000a81549060ff021916905550509055505050565b508054613e7b90615157565b5f825580601f10613e8c5750613ea9565b601f0160209004905f5260205f2090810190613ea89190613f26565b5b50565b6040518060e001604052805f815260200160608152602001606081526020015f81526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f73ffffffffffffffffffffffffffffffffffffffff1681525090565b5b80821115613f3d575f815f905550600101613f27565b5090565b5f604051905090565b5f5ffd5b5f5ffd5b5f819050919050565b613f6481613f52565b8114613f6e575f5ffd5b50565b5f81359050613f7f81613f5b565b92915050565b5f60208284031215613f9a57613f99613f4a565b5b5f613fa784828501613f71565b91505092915050565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b613fe481613fb0565b8114613fee575f5ffd5b50565b5f81359050613fff81613fdb565b92915050565b5f6020828403121561401a57614019613f4a565b5b5f61402784828501613ff1565b91505092915050565b5f8115159050919050565b61404481614030565b82525050565b5f60208201905061405d5f83018461403b565b92915050565b5f5f5f5f6080858703121561407b5761407a613f4a565b5b5f61408887828801613f71565b945050602061409987828801613f71565b93505060406140aa87828801613f71565b92505060606140bb87828801613f71565b91505092959194509250565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b6140f981613f52565b82525050565b5f61410a83836140f0565b60208301905092915050565b5f602082019050919050565b5f61412c826140c7565b61413681856140d1565b9350614141836140e1565b805f5b8381101561417157815161415888826140ff565b975061416383614116565b925050600181019050614144565b5085935050505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6141d0826141a7565b9050919050565b6141e0816141c6565b82525050565b5f6141f183836141d7565b60208301905092915050565b5f602082019050919050565b5f6142138261417e565b61421d8185614188565b935061422883614198565b805f5b8381101561425857815161423f88826141e6565b975061424a836141fd565b92505060018101905061422b565b5085935050505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b61429781614030565b82525050565b5f6142a8838361428e565b60208301905092915050565b5f602082019050919050565b5f6142ca82614265565b6142d4818561426f565b93506142df8361427f565b805f5b8381101561430f5781516142f6888261429d565b9750614301836142b4565b9250506001810190506142e2565b5085935050505092915050565b5f60a0820190508181035f8301526143348188614122565b905081810360208301526143488187614209565b9050818103604083015261435c8186614122565b9050818103606083015261437081856142c0565b9050818103608083015261438481846142c0565b90509695505050505050565b614399816141c6565b81146143a3575f5ffd5b50565b5f813590506143b481614390565b92915050565b5f602082840312156143cf576143ce613f4a565b5b5f6143dc848285016143a6565b91505092915050565b5f819050919050565b6143f7816143e5565b8114614401575f5ffd5b50565b5f81359050614412816143ee565b92915050565b5f6020828403121561442d5761442c613f4a565b5b5f61443a84828501614404565b91505092915050565b61444c816143e5565b82525050565b5f6020820190506144655f830184614443565b92915050565b5f5f6040838503121561448157614480613f4a565b5b5f61448e85828601614404565b925050602061449f858286016143a6565b9150509250929050565b5f5f5f5f5f60a086880312156144c2576144c1613f4a565b5b5f6144cf88828901613f71565b95505060206144e088828901613f71565b94505060406144f1888289016143a6565b935050606061450288828901613f71565b925050608061451388828901613f71565b9150509295509295909350565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f61458b82614549565b6145958185614553565b93506145a5818560208601614563565b6145ae81614571565b840191505092915050565b5f60e083015f8301516145ce5f8601826140f0565b50602083015184820360208601526145e68282614581565b915050604083015184820360408601526146008282614581565b915050606083015161461560608601826140f0565b50608083015161462860808601826141d7565b5060a083015161463b60a08601826141d7565b5060c083015161464e60c08601826141d7565b508091505092915050565b5f61466483836145b9565b905092915050565b5f602082019050919050565b5f61468282614520565b61468c818561452a565b93508360208202850161469e8561453a565b805f5b858110156146d957848403895281516146ba8582614659565b94506146c58361466c565b925060208a019950506001810190506146a1565b50829750879550505050505092915050565b5f60a0820190508181035f8301526147038188614678565b905081810360208301526147178187614122565b9050818103604083015261472b8186614122565b9050818103606083015261473f81856142c0565b9050818103608083015261475381846142c0565b90509695505050505050565b5f819050919050565b5f61478261477d614778846141a7565b61475f565b6141a7565b9050919050565b5f61479382614768565b9050919050565b5f6147a482614789565b9050919050565b6147b48161479a565b82525050565b5f6020820190506147cd5f8301846147ab565b92915050565b6147dc816141c6565b82525050565b5f6020820190506147f55f8301846147d3565b92915050565b5f5f6040838503121561481157614810613f4a565b5b5f61481e85828601613f71565b925050602061482f858286016143a6565b9150509250929050565b5f5ffd5b5f5ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b61487782614571565b810181811067ffffffffffffffff8211171561489657614895614841565b5b80604052505050565b5f6148a8613f41565b90506148b4828261486e565b919050565b5f67ffffffffffffffff8211156148d3576148d2614841565b5b6148dc82614571565b9050602081019050919050565b828183375f83830152505050565b5f614909614904846148b9565b61489f565b9050828152602081018484840111156149255761492461483d565b5b6149308482856148e9565b509392505050565b5f82601f83011261494c5761494b614839565b5b813561495c8482602086016148f7565b91505092915050565b5f5ffd5b5f5ffd5b5f5f83601f84011261498257614981614839565b5b8235905067ffffffffffffffff81111561499f5761499e614965565b5b6020830191508360018202830111156149bb576149ba614969565b5b9250929050565b5f5f5f5f5f5f60a087890312156149dc576149db613f4a565b5b5f87013567ffffffffffffffff8111156149f9576149f8613f4e565b5b614a0589828a01614938565b965050602087013567ffffffffffffffff811115614a2657614a25613f4e565b5b614a3289828a01614938565b955050604087013567ffffffffffffffff811115614a5357614a52613f4e565b5b614a5f89828a01614938565b9450506060614a7089828a016143a6565b935050608087013567ffffffffffffffff811115614a9157614a90613f4e565b5b614a9d89828a0161496d565b92509250509295509295509295565b614ab581613f52565b82525050565b5f602082019050614ace5f830184614aac565b92915050565b5f60e083015f830151614ae95f8601826140f0565b5060208301518482036020860152614b018282614581565b91505060408301518482036040860152614b1b8282614581565b9150506060830151614b3060608601826140f0565b506080830151614b4360808601826141d7565b5060a0830151614b5660a08601826141d7565b5060c0830151614b6960c08601826141d7565b508091505092915050565b5f6060820190508181035f830152614b8c8186614ad4565b9050614b9b6020830185614aac565b614ba86040830184614aac565b949350505050565b5f614bba82614789565b9050919050565b614bca81614bb0565b82525050565b5f602082019050614be35f830184614bc1565b92915050565b614bf281614030565b8114614bfc575f5ffd5b50565b5f81359050614c0d81614be9565b92915050565b5f5f5f60608486031215614c2a57614c29613f4a565b5b5f614c3786828701613f71565b9350506020614c48868287016143a6565b9250506040614c5986828701614bff565b9150509250925092565b5f5f5f60608486031215614c7a57614c79613f4a565b5b5f614c8786828701613f71565b9350506020614c9886828701614bff565b9250506040614ca986828701613f71565b9150509250925092565b5f5f60408385031215614cc957614cc8613f4a565b5b5f614cd685828601613f71565b9250506020614ce785828601613f71565b9150509250929050565b5f6020820190508181035f830152614d098184614678565b905092915050565b5f604082019050614d245f8301856147d3565b614d31602083018461403b565b9392505050565b5f5f5f60608486031215614d4f57614d4e613f4a565b5b5f614d5c86828701613f71565b9350506020614d6d86828701613f71565b9250506040614d7e86828701613f71565b9150509250925092565b5f82825260208201905092915050565b5f614da282614549565b614dac8185614d88565b9350614dbc818560208601614563565b614dc581614571565b840191505092915050565b5f60e082019050614de35f83018a614aac565b8181036020830152614df58189614d98565b90508181036040830152614e098188614d98565b9050614e186060830187614aac565b614e2560808301866147d3565b614e3260a08301856147d3565b614e3f60c08301846147d3565b98975050505050505050565b7f4174746573746174696f6e206d616e61676572206e6f742073657400000000005f82015250565b5f614e7f601b83614d88565b9150614e8a82614e4b565b602082019050919050565b5f6020820190508181035f830152614eac81614e73565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f614f1782613f52565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614f4957614f48614ee0565b5b600182019050919050565b5f614f5e82613f52565b9150614f6983613f52565b9250828203905081811115614f8157614f80614ee0565b5b92915050565b5f81519050614f9581613f5b565b92915050565b60038110614fa7575f5ffd5b50565b5f81519050614fb881614f9b565b92915050565b5f81519050614fcc81614be9565b92915050565b5f5f5f5f5f5f5f60e0888a031215614fed57614fec613f4a565b5b5f614ffa8a828b01614f87565b975050602061500b8a828b01614f87565b965050604061501c8a828b01614faa565b955050606061502d8a828b01614f87565b945050608061503e8a828b01614f87565b93505060a061504f8a828b01614fbe565b92505060c06150608a828b01614f87565b91505092959891949750929550565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b7f43616c6c6572206973206e6f7420746865206f776e6572206f662074686973205f8201527f6c6f670000000000000000000000000000000000000000000000000000000000602082015250565b5f6150f6602383614d88565b91506151018261509c565b604082019050919050565b5f6020820190508181035f830152615123816150ea565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f600282049050600182168061516e57607f821691505b6020821081036151815761518061512a565b5b50919050565b5f61519182613f52565b91505f82036151a3576151a2614ee0565b5b600182039050919050565b7f43616c6c6572206973206e6f7420616e206f70657261746f72000000000000005f82015250565b5f6151e2601983614d88565b91506151ed826151ae565b602082019050919050565b5f6020820190508181035f83015261520f816151d6565b9050919050565b7f54617267657420686173206e6f7420617474657374656420746f2074686973205f8201527f6c6f670000000000000000000000000000000000000000000000000000000000602082015250565b5f615270602383614d88565b915061527b82615216565b604082019050919050565b5f6020820190508181035f83015261529d81615264565b9050919050565b7f43616c6c657220686173206e6f7420617474657374656420746f2074686973205f8201527f6c6f670000000000000000000000000000000000000000000000000000000000602082015250565b5f6152fe602383614d88565b9150615309826152a4565b604082019050919050565b5f6020820190508181035f83015261532b816152f2565b9050919050565b7f43616e206f6e6c79206c6f6720686f74646f677320666f7220796f757273656c5f8201527f6600000000000000000000000000000000000000000000000000000000000000602082015250565b5f61538c602183614d88565b915061539782615332565b604082019050919050565b5f6020820190508181035f8301526153b981615380565b9050919050565b7f4c6f6720646f6573206e6f7420657869737400000000000000000000000000005f82015250565b5f6153f4601283614d88565b91506153ff826153c0565b602082019050919050565b5f6020820190508181035f830152615421816153e8565b9050919050565b7f43616e6e6f742061747465737420746f206f776e206c6f6700000000000000005f82015250565b5f61545c601883614d88565b915061546782615428565b602082019050919050565b5f6020820190508181035f83015261548981615450565b9050919050565b7f506c656173652063616c6c20617474657374546f4c6f67206469726563746c795f8201527f206f6e20746865204174746573746174696f6e4d616e6167657220636f6e747260208201527f6163740000000000000000000000000000000000000000000000000000000000604082015250565b5f615510604383614d88565b915061551b82615490565b606082019050919050565b5f6020820190508181035f83015261553d81615504565b9050919050565b7f537461727420696e646578206f7574206f6620626f756e6473000000000000005f82015250565b5f615578601983614d88565b915061558382615544565b602082019050919050565b5f6020820190508181035f8301526155a58161556c565b9050919050565b5f6155b682613f52565b91506155c183613f52565b92508282019050808211156155d9576155d8614ee0565b5b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f61561682613f52565b915061562183613f52565b925082615631576156306155df565b5b828204905092915050565b5f60408201905061564f5f83018561403b565b61565c6020830184614aac565b9392505050565b7f546172676574206973206e6f7420746865206f776e6572206f662074686973205f8201527f6c6f670000000000000000000000000000000000000000000000000000000000602082015250565b5f6156bd602383614d88565b91506156c882615663565b604082019050919050565b5f6020820190508181035f8301526156ea816156b1565b9050919050565b7f55736572206174746573746174696f6e206e6f7420666f756e640000000000005f82015250565b5f615725601a83614d88565b9150615730826156f1565b602082019050919050565b5f6020820190508181035f83015261575281615719565b9050919050565b5f82825260208201905092915050565b5f6157748385615759565b93506157818385846148e9565b61578a83614571565b840190509392505050565b5f60a0820190506157a85f830189614aac565b6157b560208301886147d3565b81810360408301526157c78187614d98565b905081810360608301526157dc818587615769565b90506157eb60808301846147d3565b979650505050505050565b5f8151905061580481614390565b92915050565b5f6020828403121561581f5761581e613f4a565b5b5f61582c848285016157f6565b91505092915050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f600883026158917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82615856565b61589b8683615856565b95508019841693508086168417925050509392505050565b5f6158cd6158c86158c384613f52565b61475f565b613f52565b9050919050565b5f819050919050565b6158e6836158b3565b6158fa6158f2826158d4565b848454615862565b825550505050565b5f5f905090565b615911615902565b61591c8184846158dd565b505050565b5b8181101561593f576159345f82615909565b600181019050615922565b5050565b601f8211156159845761595581615835565b61595e84615847565b8101602085101561596d578190505b61598161597985615847565b830182615921565b50505b505050565b5f82821c905092915050565b5f6159a45f1984600802615989565b1980831691505092915050565b5f6159bc8383615995565b9150826002028217905092915050565b6159d582614549565b67ffffffffffffffff8111156159ee576159ed614841565b5b6159f88254615157565b615a03828285615943565b5f60209050601f831160018114615a34575f8415615a22578287015190505b615a2c85826159b1565b865550615a93565b601f198416615a4286615835565b5f5b82811015615a6957848901518255600182019150602085019450602081019050615a44565b86831015615a865784890151615a82601f891682615995565b8355505b6001600288020188555050505b505050505050565b5f6080820190508181035f830152615ab38187614d98565b90508181036020830152615ac78186614d98565b9050615ad66040830185614aac565b615ae360608301846147d3565b95945050505050565b5f604082019050615aff5f8301856147d3565b615b0c6020830184614443565b9392505050565b7f4174746573746174696f6e206e6f7420666f756e6400000000000000000000005f82015250565b5f615b47601583614d88565b9150615b5282615b13565b602082019050919050565b5f6020820190508181035f830152615b7481615b3b565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffdfea2646970667358221220cc485ad8bee3e437f7e7c4e4ebb1c8bf4e559600baabe3f4c019acd3a3530f8964736f6c634300081e00330000000000000000000000003de0ba94a1f291a7c44bb029b765adb2c487063f000000000000000000000000cbf054aa8feb4fd0484e45b766b502bc045076b8000000000000000000000000b249d4bb7400e8d6cff4e7824ee2014a89a2baaf
Deployed Bytecode
0x608060405260043610610219575f3560e01c8063a4d413b011610122578063db86c6b4116100aa578063f5b541a61161006e578063f5b541a614610824578063f7a009461461084e578063fadb24d314610876578063fe1faae51461089e578063ff1f94b5146108da57610219565b8063db86c6b4146106fd578063dd03888e14610739578063de2c2e8b14610776578063e7de4e76146107b2578063f4f611c9146107e257610219565b8063bbc8d3f7116100f1578063bbc8d3f71461061f578063ce6fde0f14610647578063cea7de581461066f578063cf2b6278146106ab578063d547741f146106d557610219565b8063a4d413b014610567578063ac8a584a146105a5578063b1c16bf1146105cd578063b565b9ed146105f757610219565b80634b65613b116101a55780637fbb1949116101745780637fbb19491461048157806391d14854146104a95780639870d7fe146104e5578063a217fddf1461050d578063a44252ed1461053757610219565b80634b65613b146103dd5780634f902b141461040557806351845bf61461042f578063523b75df1461045957610219565b8063248a9ca3116101ec578063248a9ca3146102e95780632f2ff15d1461032557806330aa61e81461034d57806336568abe146103755780633a5c39f71461039d57610219565b806301afa8d01461021d57806301ffc9a71461024557806313f8fd4f146102815780631a32fc88146102c1575b5f5ffd5b348015610228575f5ffd5b50610243600480360381019061023e9190613f85565b610916565b005b348015610250575f5ffd5b5061026b60048036038101906102669190614005565b610a2f565b604051610278919061404a565b60405180910390f35b34801561028c575f5ffd5b506102a760048036038101906102a29190614063565b610aa8565b6040516102b895949392919061431c565b60405180910390f35b3480156102cc575f5ffd5b506102e760048036038101906102e291906143ba565b611078565b005b3480156102f4575f5ffd5b5061030f600480360381019061030a9190614418565b6110c8565b60405161031c9190614452565b60405180910390f35b348015610330575f5ffd5b5061034b6004803603810190610346919061446b565b6110e4565b005b348015610358575f5ffd5b50610373600480360381019061036e9190613f85565b611106565b005b348015610380575f5ffd5b5061039b6004803603810190610396919061446b565b6112bf565b005b3480156103a8575f5ffd5b506103c360048036038101906103be91906144a9565b61133a565b6040516103d49594939291906146eb565b60405180910390f35b3480156103e8575f5ffd5b5061040360048036038101906103fe91906143ba565b611a27565b005b348015610410575f5ffd5b50610419611a77565b60405161042691906147ba565b60405180910390f35b34801561043a575f5ffd5b50610443611a9c565b60405161045091906147e2565b60405180910390f35b348015610464575f5ffd5b5061047f600480360381019061047a91906147fb565b611ac1565b005b34801561048c575f5ffd5b506104a760048036038101906104a29190613f85565b611bd0565b005b3480156104b4575f5ffd5b506104cf60048036038101906104ca919061446b565b611c75565b6040516104dc919061404a565b60405180910390f35b3480156104f0575f5ffd5b5061050b600480360381019061050691906143ba565b611cd8565b005b348015610518575f5ffd5b50610521611d55565b60405161052e9190614452565b60405180910390f35b610551600480360381019061054c91906149c2565b611d5b565b60405161055e9190614abb565b60405180910390f35b348015610572575f5ffd5b5061058d60048036038101906105889190613f85565b611de4565b60405161059c93929190614b74565b60405180910390f35b3480156105b0575f5ffd5b506105cb60048036038101906105c691906143ba565b6120ac565b005b3480156105d8575f5ffd5b506105e1612129565b6040516105ee9190614bd0565b60405180910390f35b348015610602575f5ffd5b5061061d60048036038101906106189190614c13565b61214e565b005b34801561062a575f5ffd5b5061064560048036038101906106409190614c63565b612277565b005b348015610652575f5ffd5b5061066d600480360381019061066891906143ba565b6122b2565b005b34801561067a575f5ffd5b50610695600480360381019061069091906143ba565b612302565b6040516106a29190614abb565b60405180910390f35b3480156106b6575f5ffd5b506106bf6123ac565b6040516106cc9190614abb565b60405180910390f35b3480156106e0575f5ffd5b506106fb60048036038101906106f6919061446b565b6123b8565b005b348015610708575f5ffd5b50610723600480360381019061071e9190614cb3565b6123da565b6040516107309190614cf1565b60405180910390f35b348015610744575f5ffd5b5061075f600480360381019061075a9190614cb3565b612751565b60405161076d929190614d11565b60405180910390f35b348015610781575f5ffd5b5061079c60048036038101906107979190614d38565b6127b2565b6040516107a99190614abb565b60405180910390f35b6107cc60048036038101906107c791906149c2565b61286e565b6040516107d99190614abb565b60405180910390f35b3480156107ed575f5ffd5b5061080860048036038101906108039190613f85565b6128f2565b60405161081b9796959493929190614dd0565b60405180910390f35b34801561082f575f5ffd5b50610838612aa8565b6040516108459190614452565b60405180910390f35b348015610859575f5ffd5b50610874600480360381019061086f9190613f85565b612acc565b005b348015610881575f5ffd5b5061089c600480360381019061089791906147fb565b612bc3565b005b3480156108a9575f5ffd5b506108c460048036038101906108bf91906147fb565b612de4565b6040516108d1919061404a565b60405180910390f35b3480156108e5575f5ffd5b5061090060048036038101906108fb9190613f85565b612e0e565b60405161090d9190614abb565b60405180910390f35b5f73ffffffffffffffffffffffffffffffffffffffff1660025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16036109a5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161099c90614e95565b60405180910390fd5b60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166301afa8d0826040518263ffffffff1660e01b81526004016109ff9190614abb565b5f604051808303815f87803b158015610a16575f5ffd5b505af1158015610a28573d5f5f3e3d5ffd5b5050505050565b5f7f7965db0b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480610aa15750610aa082612e2b565b5b9050919050565b60608060608060605f5f90505f5f90505b600480549050811015610b3d578a60048281548110610adb57610ada614eb3565b5b905f5260205f2090600702016003015410158015610b1c57508960048281548110610b0957610b08614eb3565b5b905f5260205f2090600702016003015411155b15610b30578180610b2c90614f0d565b9250505b8080600101915050610ab9565b505f888211610b4c575f610b59565b8882610b589190614f54565b5b9050878111610b685780610b6a565b875b90508067ffffffffffffffff811115610b8657610b85614841565b5b604051908082528060200260200182016040528015610bb45781602001602082028036833780820191505090505b5096508067ffffffffffffffff811115610bd157610bd0614841565b5b604051908082528060200260200182016040528015610bff5781602001602082028036833780820191505090505b5095508067ffffffffffffffff811115610c1c57610c1b614841565b5b604051908082528060200260200182016040528015610c4a5781602001602082028036833780820191505090505b5094508067ffffffffffffffff811115610c6757610c66614841565b5b604051908082528060200260200182016040528015610c955781602001602082028036833780820191505090505b5093508067ffffffffffffffff811115610cb257610cb1614841565b5b604051908082528060200260200182016040528015610ce05781602001602082028036833780820191505090505b5092505f5f90505f5f90505f5f90505b60048054905081108015610d0357508383105b15611068578d60048281548110610d1d57610d1c614eb3565b5b905f5260205f2090600702016003015410158015610d5e57508c60048281548110610d4b57610d4a614eb3565b5b905f5260205f2090600702016003015411155b15611055578b821061104557808a8481518110610d7e57610d7d614eb3565b5b60200260200101818152505060048181548110610d9e57610d9d614eb3565b5b905f5260205f2090600702016004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16898481518110610de057610ddf614eb3565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505060048181548110610e2e57610e2d614eb3565b5b905f5260205f20906007020160030154888481518110610e5157610e50614eb3565b5b6020026020010181815250505f73ffffffffffffffffffffffffffffffffffffffff1660025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614610fdc575f5f5f60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632f99b231856040518263ffffffff1660e01b8152600401610f0e9190614abb565b60e060405180830381865afa158015610f29573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f4d9190614fd2565b5095505050935093505082421180610f89575060016002811115610f7457610f7361506f565b5b826002811115610f8757610f8661506f565b5b145b8a8781518110610f9c57610f9b614eb3565b5b60200260200101901515908115158152505080898781518110610fc257610fc1614eb3565b5b602002602001019015159081151581525050505050611032565b6001878481518110610ff157610ff0614eb3565b5b60200260200101901515908115158152505061100c81612e94565b86848151811061101f5761101e614eb3565b5b6020026020010190151590811515815250505b828061103d90614f0d565b935050611054565b818061105090614f0d565b9250505b5b808061106090614f0d565b915050610cf0565b5050505050945094509450945094565b5f5f1b61108481612fe3565b8160025f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b5f5f5f8381526020019081526020015f20600101549050919050565b6110ed826110c8565b6110f681612fe3565b6111008383612ff7565b50505050565b803373ffffffffffffffffffffffffffffffffffffffff166004828154811061113257611131614eb3565b5b905f5260205f2090600702016004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146111b7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ae9061510c565b60405180910390fd5b600482815481106111cb576111ca614eb3565b5b905f5260205f2090600702015f5f82015f9055600182015f6111ed9190613e6f565b600282015f6111fc9190613e6f565b600382015f9055600482015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600582015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600682015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550503373ffffffffffffffffffffffffffffffffffffffff16827fb5429b4807204ef014ef6a8143de5f24b3cd12e1c35608b6050adde1a36531a260405160405180910390a35050565b6112c76130e0565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161461132b576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61133582826130e7565b505050565b60608060608060605f5f90505f5f90505b6004805490508110156113cf578b6004828154811061136d5761136c614eb3565b5b905f5260205f20906007020160030154101580156113ae57508a6004828154811061139b5761139a614eb3565b5b905f5260205f2090600702016003015411155b156113c25781806113be90614f0d565b9250505b808060010191505061134b565b505f8882116113de575f6113eb565b88826113ea9190614f54565b5b90508781116113fa57806113fc565b875b90508067ffffffffffffffff81111561141857611417614841565b5b60405190808252806020026020018201604052801561145157816020015b61143e613eac565b8152602001906001900390816114365790505b5096508067ffffffffffffffff81111561146e5761146d614841565b5b60405190808252806020026020018201604052801561149c5781602001602082028036833780820191505090505b5095508067ffffffffffffffff8111156114b9576114b8614841565b5b6040519080825280602002602001820160405280156114e75781602001602082028036833780820191505090505b5094508067ffffffffffffffff81111561150457611503614841565b5b6040519080825280602002602001820160405280156115325781602001602082028036833780820191505090505b5093508067ffffffffffffffff81111561154f5761154e614841565b5b60405190808252806020026020018201604052801561157d5781602001602082028036833780820191505090505b5092505f5f90505f5f90505f60048054905090505b5f811180156115a057508383105b15611a15578e60046001836115b59190614f54565b815481106115c6576115c5614eb3565b5b905f5260205f209060070201600301541015801561161357508d60046001836115ef9190614f54565b81548110611600576115ff614eb3565b5b905f5260205f2090600702016003015411155b15611a02578b82106119f257600460018261162e9190614f54565b8154811061163f5761163e614eb3565b5b905f5260205f2090600702016040518060e00160405290815f820154815260200160018201805461166f90615157565b80601f016020809104026020016040519081016040528092919081815260200182805461169b90615157565b80156116e65780601f106116bd576101008083540402835291602001916116e6565b820191905f5260205f20905b8154815290600101906020018083116116c957829003601f168201915b505050505081526020016002820180546116ff90615157565b80601f016020809104026020016040519081016040528092919081815260200182805461172b90615157565b80156117765780601f1061174d57610100808354040283529160200191611776565b820191905f5260205f20905b81548152906001019060200180831161175957829003601f168201915b5050505050815260200160038201548152602001600482015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600582015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600682015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815250508a848151811061189b5761189a614eb3565b5b60200260200101819052506118bb6001826118b69190614f54565b6131d0565b8a85815181106118ce576118cd614eb3565b5b602002602001018a86815181106118e8576118e7614eb3565b5b602002602001018281525082815250505060065f6001836119099190614f54565b81526020019081526020015f205f8e73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff1687848151811061197157611970614eb3565b5b60200260200101901515908115158152505086838151811061199657611995614eb3565b5b6020026020010151156119df576119b96001826119b39190614f54565b8e613268565b8684815181106119cc576119cb614eb3565b5b6020026020010190151590811515815250505b82806119ea90614f0d565b935050611a01565b81806119fd90614f0d565b9250505b5b8080611a0d90615187565b915050611592565b50505050509550955095509550959050565b5f5f1b611a3381612fe3565b8160035f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b611aeb7f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92933611c75565b611b2a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b21906151f8565b60405180910390fd5b60065f8381526020019081526020015f205f8273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff16611bc2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611bb990615286565b60405180910390fd5b611bcc828261339b565b5050565b60065f8281526020019081526020015f205f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff16611c68576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c5f90615314565b60405180910390fd5b611c72813361339b565b50565b5f5f5f8481526020019081526020015f205f015f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff16905092915050565b5f5f1b611ce481612fe3565b611d0e7f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b929836110e4565b8173ffffffffffffffffffffffffffffffffffffffff167fac6fa858e9350a46cec16539926e0fde25b7629f84b5a72bffaae4df888ae86d60405160405180910390a25050565b5f5f1b81565b5f3373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614611dca576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611dc1906153a2565b60405180910390fd5b611dd887878787878761345e565b90509695505050505050565b611dec613eac565b5f5f6004805490508410611e35576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611e2c9061540a565b60405180910390fd5b60048481548110611e4957611e48614eb3565b5b905f5260205f2090600702016040518060e00160405290815f8201548152602001600182018054611e7990615157565b80601f0160208091040260200160405190810160405280929190818152602001828054611ea590615157565b8015611ef05780601f10611ec757610100808354040283529160200191611ef0565b820191905f5260205f20905b815481529060010190602001808311611ed357829003601f168201915b50505050508152602001600282018054611f0990615157565b80601f0160208091040260200160405190810160405280929190818152602001828054611f3590615157565b8015611f805780601f10611f5757610100808354040283529160200191611f80565b820191905f5260205f20905b815481529060010190602001808311611f6357829003601f168201915b5050505050815260200160038201548152602001600482015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600582015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600682015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681525050925061209d846131d0565b80925081935050509193909250565b5f5f1b6120b881612fe3565b6120e27f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b929836123b8565b8173ffffffffffffffffffffffffffffffffffffffff167f80c0b871b97b595b16a7741c1b06fed0c6f6f558639f18ccbce50724325dc40d60405160405180910390a25050565b60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6121787f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92933611c75565b6121b7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016121ae906151f8565b60405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff16600484815481106121e2576121e1614eb3565b5b905f5260205f2090600702016004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1603612267576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161225e90615472565b60405180910390fd5b612272838383613888565b505050565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016122a990615526565b60405180910390fd5b5f5f1b6122be81612fe3565b8160015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b5f5f5f90505b6004805490508110156123a6578273ffffffffffffffffffffffffffffffffffffffff16600482815481106123405761233f614eb3565b5b905f5260205f2090600702016004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff160361239957818061239590614f0d565b9250505b8080600101915050612308565b50919050565b5f600480549050905090565b6123c1826110c8565b6123ca81612fe3565b6123d483836130e7565b50505050565b60606004805490508310612423576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161241a9061558e565b60405180910390fd5b5f828461243091906155ac565b90506004805490508111156124485760048054905090505b83816124549190614f54565b67ffffffffffffffff81111561246d5761246c614841565b5b6040519080825280602002602001820160405280156124a657816020015b612493613eac565b81526020019060019003908161248b5790505b5091505f8490505b8181101561274957600481815481106124ca576124c9614eb3565b5b905f5260205f2090600702016040518060e00160405290815f82015481526020016001820180546124fa90615157565b80601f016020809104026020016040519081016040528092919081815260200182805461252690615157565b80156125715780601f1061254857610100808354040283529160200191612571565b820191905f5260205f20905b81548152906001019060200180831161255457829003601f168201915b5050505050815260200160028201805461258a90615157565b80601f01602080910402602001604051908101604052809291908181526020018280546125b690615157565b80156126015780601f106125d857610100808354040283529160200191612601565b820191905f5260205f20905b8154815290600101906020018083116125e457829003601f168201915b5050505050815260200160038201548152602001600482015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600582015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600682015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815250508386836127209190614f54565b8151811061273157612730614eb3565b5b602002602001018190525080806001019150506124ae565b505092915050565b6005602052815f5260405f20818154811061276a575f80fd5b905f5260205f20015f9150915050805f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690805f0160149054906101000a900460ff16905082565b5f5f5f90505f5f90505b6004805490508110156128405785600482815481106127de576127dd614eb3565b5b905f5260205f209060070201600301541015801561281f5750846004828154811061280c5761280b614eb3565b5b905f5260205f2090600702016003015411155b1561283357818061282f90614f0d565b9250505b80806001019150506127bc565b50826001848361285091906155ac565b61285a9190614f54565b612864919061560c565b9150509392505050565b5f6128997f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92933611c75565b6128d8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016128cf906151f8565b60405180910390fd5b6128e687878787878761345e565b90509695505050505050565b60048181548110612901575f80fd5b905f5260205f2090600702015f91509050805f01549080600101805461292690615157565b80601f016020809104026020016040519081016040528092919081815260200182805461295290615157565b801561299d5780601f106129745761010080835404028352916020019161299d565b820191905f5260205f20905b81548152906001019060200180831161298057829003601f168201915b5050505050908060020180546129b290615157565b80601f01602080910402602001604051908101604052809291908181526020018280546129de90615157565b8015612a295780601f10612a0057610100808354040283529160200191612a29565b820191905f5260205f20905b815481529060010190602001808311612a0c57829003601f168201915b505050505090806003015490806004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690806005015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690806006015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905087565b7f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92981565b5f5f1b612ad881612fe3565b6004805490508210612b1f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612b169061540a565b60405180910390fd5b5f612b2983612e94565b905060048381548110612b3f57612b3e614eb3565b5b905f5260205f2090600702016004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16837f2b04f5b936a447dff175af18ad4a7f2ed0867e7a83f4916937f859f360e3d1508342604051612bb692919061563c565b60405180910390a3505050565b612bed7f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92933611c75565b612c2c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612c23906151f8565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660048381548110612c5757612c56614eb3565b5b905f5260205f2090600702016004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612cdc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612cd3906156d3565b60405180910390fd5b60048281548110612cf057612cef614eb3565b5b905f5260205f2090600702015f5f82015f9055600182015f612d129190613e6f565b600282015f612d219190613e6f565b600382015f9055600482015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600582015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600682015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550508073ffffffffffffffffffffffffffffffffffffffff16827fb5429b4807204ef014ef6a8143de5f24b3cd12e1c35608b6050adde1a36531a260405160405180910390a35050565b6006602052815f5260405f20602052805f5260405f205f915091509054906101000a900460ff1681565b5f60055f8381526020019081526020015f20805490509050919050565b5f7f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b5f5f73ffffffffffffffffffffffffffffffffffffffff1660025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612fc6575f5f60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632f99b231856040518263ffffffff1660e01b8152600401612f459190614abb565b60e060405180830381865afa158015612f60573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f849190614fd2565b50955050509350505060016002811115612fa157612fa061506f565b5b826002811115612fb457612fb361506f565b5b03612fc3578092505050612fde565b50505b5f5f612fd1846131d0565b9150915080821015925050505b919050565b612ff481612fef6130e0565b613ba1565b50565b5f6130028383611c75565b6130d65760015f5f8581526020019081526020015f205f015f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055506130736130e0565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4600190506130da565b5f90505b92915050565b5f33905090565b5f6130f28383611c75565b156131c6575f5f5f8581526020019081526020015f205f015f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055506131636130e0565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16847ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a4600190506131ca565b5f90505b92915050565b5f5f5f5f90505b60055f8581526020019081526020015f20805490508110156132625760055f8581526020019081526020015f20818154811061321657613215614eb3565b5b905f5260205f20015f0160149054906101000a900460ff161561324657828061323e90614f0d565b935050613255565b818061325190614f0d565b9250505b80806001019150506131d7565b50915091565b5f5f5f90505b60055f8581526020019081526020015f2080549050811015613359578273ffffffffffffffffffffffffffffffffffffffff1660055f8681526020019081526020015f2082815481106132c4576132c3614eb3565b5b905f5260205f20015f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff160361334c5760055f8581526020019081526020015f20818154811061332c5761332b614eb3565b5b905f5260205f20015f0160149054906101000a900460ff16915050613395565b808060010191505061326e565b506040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161338c9061573b565b60405180910390fd5b92915050565b5f6133a68383613bf2565b90506133b28382613ceb565b5f60065f8581526020019081526020015f205f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff16837f6570a541a707009541b6b7f8f3c937278180f6169d5b6db44bea3d1426e6f8dd60405160405180910390a3505050565b5f60048054905090505f5f73ffffffffffffffffffffffffffffffffffffffff1660035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146135865760035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663271e75ff3484888a898960015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518863ffffffff1660e01b815260040161354296959493929190615795565b60206040518083038185885af115801561355e573d5f5f3e3d5ffd5b50505050506040513d601f19601f82011682018060405250810190613583919061580a565b90505b60046040518060e001604052808481526020018a81526020018981526020014281526020018773ffffffffffffffffffffffffffffffffffffffff1681526020013373ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff16815250908060018154018082558091505060019003905f5260205f2090600702015f909190919091505f820151815f0155602082015181600101908161364091906159cc565b50604082015181600201908161365691906159cc565b50606082015181600301556080820151816004015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060a0820151816005015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060c0820151816006015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050505f73ffffffffffffffffffffffffffffffffffffffff1660025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146138115760025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663df286f4c836040518263ffffffff1660e01b81526004016137e39190614abb565b5f604051808303815f87803b1580156137fa575f5ffd5b505af115801561380c573d5f5f3e3d5ffd5b505050505b8473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16837f0d4cd2468ea6d0f2bf4dd33a6419382b5e4dcb8d6ffcc581703619157d2b1ccc8b8b42876040516138759493929190615a9b565b60405180910390a4509695505050505050565b5f5f90505f5f90505b60055f8681526020019081526020015f2080549050811015613a1c578373ffffffffffffffffffffffffffffffffffffffff1660055f8781526020019081526020015f2082815481106138e7576138e6614eb3565b5b905f5260205f20015f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1603613a0f5782151560055f8781526020019081526020015f20828154811061395257613951614eb3565b5b905f5260205f20015f0160149054906101000a900460ff16151514613a06578260055f8781526020019081526020015f20828154811061399557613994614eb3565b5b905f5260205f20015f0160146101000a81548160ff0219169083151502179055508373ffffffffffffffffffffffffffffffffffffffff16857f5e211bd87f797087f36d0cc81a4dc3bbff6589b5674820fd67a53ab162291e74856040516139fd919061404a565b60405180910390a35b60019150613a1c565b8080600101915050613891565b5080613b9b5760055f8581526020019081526020015f2060405180604001604052808573ffffffffffffffffffffffffffffffffffffffff168152602001841515815250908060018154018082558091505060019003905f5260205f20015f909190919091505f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506020820151815f0160146101000a81548160ff0219169083151502179055505050600160065f8681526020019081526020015f205f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055508273ffffffffffffffffffffffffffffffffffffffff16847f5e211bd87f797087f36d0cc81a4dc3bbff6589b5674820fd67a53ab162291e7484604051613b92919061404a565b60405180910390a35b50505050565b613bab8282611c75565b613bee5780826040517fe2517d3f000000000000000000000000000000000000000000000000000000008152600401613be5929190615aec565b60405180910390fd5b5050565b5f5f5f90505b60055f8581526020019081526020015f2080549050811015613ca9578273ffffffffffffffffffffffffffffffffffffffff1660055f8681526020019081526020015f208281548110613c4e57613c4d614eb3565b5b905f5260205f20015f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1603613c9c5780915050613ce5565b8080600101915050613bf8565b506040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613cdc90615b5d565b60405180910390fd5b92915050565b5f600160055f8581526020019081526020015f2080549050613d0d9190614f54565b9050808214613dfe5760055f8481526020019081526020015f208181548110613d3957613d38614eb3565b5b905f5260205f200160055f8581526020019081526020015f208381548110613d6457613d63614eb3565b5b905f5260205f20015f82015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f820160149054906101000a900460ff16815f0160146101000a81548160ff0219169083151502179055509050505b60055f8481526020019081526020015f20805480613e1f57613e1e615b7b565b5b600190038181905f5260205f20015f5f82015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555f820160146101000a81549060ff021916905550509055505050565b508054613e7b90615157565b5f825580601f10613e8c5750613ea9565b601f0160209004905f5260205f2090810190613ea89190613f26565b5b50565b6040518060e001604052805f815260200160608152602001606081526020015f81526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f73ffffffffffffffffffffffffffffffffffffffff1681525090565b5b80821115613f3d575f815f905550600101613f27565b5090565b5f604051905090565b5f5ffd5b5f5ffd5b5f819050919050565b613f6481613f52565b8114613f6e575f5ffd5b50565b5f81359050613f7f81613f5b565b92915050565b5f60208284031215613f9a57613f99613f4a565b5b5f613fa784828501613f71565b91505092915050565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b613fe481613fb0565b8114613fee575f5ffd5b50565b5f81359050613fff81613fdb565b92915050565b5f6020828403121561401a57614019613f4a565b5b5f61402784828501613ff1565b91505092915050565b5f8115159050919050565b61404481614030565b82525050565b5f60208201905061405d5f83018461403b565b92915050565b5f5f5f5f6080858703121561407b5761407a613f4a565b5b5f61408887828801613f71565b945050602061409987828801613f71565b93505060406140aa87828801613f71565b92505060606140bb87828801613f71565b91505092959194509250565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b6140f981613f52565b82525050565b5f61410a83836140f0565b60208301905092915050565b5f602082019050919050565b5f61412c826140c7565b61413681856140d1565b9350614141836140e1565b805f5b8381101561417157815161415888826140ff565b975061416383614116565b925050600181019050614144565b5085935050505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6141d0826141a7565b9050919050565b6141e0816141c6565b82525050565b5f6141f183836141d7565b60208301905092915050565b5f602082019050919050565b5f6142138261417e565b61421d8185614188565b935061422883614198565b805f5b8381101561425857815161423f88826141e6565b975061424a836141fd565b92505060018101905061422b565b5085935050505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b61429781614030565b82525050565b5f6142a8838361428e565b60208301905092915050565b5f602082019050919050565b5f6142ca82614265565b6142d4818561426f565b93506142df8361427f565b805f5b8381101561430f5781516142f6888261429d565b9750614301836142b4565b9250506001810190506142e2565b5085935050505092915050565b5f60a0820190508181035f8301526143348188614122565b905081810360208301526143488187614209565b9050818103604083015261435c8186614122565b9050818103606083015261437081856142c0565b9050818103608083015261438481846142c0565b90509695505050505050565b614399816141c6565b81146143a3575f5ffd5b50565b5f813590506143b481614390565b92915050565b5f602082840312156143cf576143ce613f4a565b5b5f6143dc848285016143a6565b91505092915050565b5f819050919050565b6143f7816143e5565b8114614401575f5ffd5b50565b5f81359050614412816143ee565b92915050565b5f6020828403121561442d5761442c613f4a565b5b5f61443a84828501614404565b91505092915050565b61444c816143e5565b82525050565b5f6020820190506144655f830184614443565b92915050565b5f5f6040838503121561448157614480613f4a565b5b5f61448e85828601614404565b925050602061449f858286016143a6565b9150509250929050565b5f5f5f5f5f60a086880312156144c2576144c1613f4a565b5b5f6144cf88828901613f71565b95505060206144e088828901613f71565b94505060406144f1888289016143a6565b935050606061450288828901613f71565b925050608061451388828901613f71565b9150509295509295909350565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f61458b82614549565b6145958185614553565b93506145a5818560208601614563565b6145ae81614571565b840191505092915050565b5f60e083015f8301516145ce5f8601826140f0565b50602083015184820360208601526145e68282614581565b915050604083015184820360408601526146008282614581565b915050606083015161461560608601826140f0565b50608083015161462860808601826141d7565b5060a083015161463b60a08601826141d7565b5060c083015161464e60c08601826141d7565b508091505092915050565b5f61466483836145b9565b905092915050565b5f602082019050919050565b5f61468282614520565b61468c818561452a565b93508360208202850161469e8561453a565b805f5b858110156146d957848403895281516146ba8582614659565b94506146c58361466c565b925060208a019950506001810190506146a1565b50829750879550505050505092915050565b5f60a0820190508181035f8301526147038188614678565b905081810360208301526147178187614122565b9050818103604083015261472b8186614122565b9050818103606083015261473f81856142c0565b9050818103608083015261475381846142c0565b90509695505050505050565b5f819050919050565b5f61478261477d614778846141a7565b61475f565b6141a7565b9050919050565b5f61479382614768565b9050919050565b5f6147a482614789565b9050919050565b6147b48161479a565b82525050565b5f6020820190506147cd5f8301846147ab565b92915050565b6147dc816141c6565b82525050565b5f6020820190506147f55f8301846147d3565b92915050565b5f5f6040838503121561481157614810613f4a565b5b5f61481e85828601613f71565b925050602061482f858286016143a6565b9150509250929050565b5f5ffd5b5f5ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b61487782614571565b810181811067ffffffffffffffff8211171561489657614895614841565b5b80604052505050565b5f6148a8613f41565b90506148b4828261486e565b919050565b5f67ffffffffffffffff8211156148d3576148d2614841565b5b6148dc82614571565b9050602081019050919050565b828183375f83830152505050565b5f614909614904846148b9565b61489f565b9050828152602081018484840111156149255761492461483d565b5b6149308482856148e9565b509392505050565b5f82601f83011261494c5761494b614839565b5b813561495c8482602086016148f7565b91505092915050565b5f5ffd5b5f5ffd5b5f5f83601f84011261498257614981614839565b5b8235905067ffffffffffffffff81111561499f5761499e614965565b5b6020830191508360018202830111156149bb576149ba614969565b5b9250929050565b5f5f5f5f5f5f60a087890312156149dc576149db613f4a565b5b5f87013567ffffffffffffffff8111156149f9576149f8613f4e565b5b614a0589828a01614938565b965050602087013567ffffffffffffffff811115614a2657614a25613f4e565b5b614a3289828a01614938565b955050604087013567ffffffffffffffff811115614a5357614a52613f4e565b5b614a5f89828a01614938565b9450506060614a7089828a016143a6565b935050608087013567ffffffffffffffff811115614a9157614a90613f4e565b5b614a9d89828a0161496d565b92509250509295509295509295565b614ab581613f52565b82525050565b5f602082019050614ace5f830184614aac565b92915050565b5f60e083015f830151614ae95f8601826140f0565b5060208301518482036020860152614b018282614581565b91505060408301518482036040860152614b1b8282614581565b9150506060830151614b3060608601826140f0565b506080830151614b4360808601826141d7565b5060a0830151614b5660a08601826141d7565b5060c0830151614b6960c08601826141d7565b508091505092915050565b5f6060820190508181035f830152614b8c8186614ad4565b9050614b9b6020830185614aac565b614ba86040830184614aac565b949350505050565b5f614bba82614789565b9050919050565b614bca81614bb0565b82525050565b5f602082019050614be35f830184614bc1565b92915050565b614bf281614030565b8114614bfc575f5ffd5b50565b5f81359050614c0d81614be9565b92915050565b5f5f5f60608486031215614c2a57614c29613f4a565b5b5f614c3786828701613f71565b9350506020614c48868287016143a6565b9250506040614c5986828701614bff565b9150509250925092565b5f5f5f60608486031215614c7a57614c79613f4a565b5b5f614c8786828701613f71565b9350506020614c9886828701614bff565b9250506040614ca986828701613f71565b9150509250925092565b5f5f60408385031215614cc957614cc8613f4a565b5b5f614cd685828601613f71565b9250506020614ce785828601613f71565b9150509250929050565b5f6020820190508181035f830152614d098184614678565b905092915050565b5f604082019050614d245f8301856147d3565b614d31602083018461403b565b9392505050565b5f5f5f60608486031215614d4f57614d4e613f4a565b5b5f614d5c86828701613f71565b9350506020614d6d86828701613f71565b9250506040614d7e86828701613f71565b9150509250925092565b5f82825260208201905092915050565b5f614da282614549565b614dac8185614d88565b9350614dbc818560208601614563565b614dc581614571565b840191505092915050565b5f60e082019050614de35f83018a614aac565b8181036020830152614df58189614d98565b90508181036040830152614e098188614d98565b9050614e186060830187614aac565b614e2560808301866147d3565b614e3260a08301856147d3565b614e3f60c08301846147d3565b98975050505050505050565b7f4174746573746174696f6e206d616e61676572206e6f742073657400000000005f82015250565b5f614e7f601b83614d88565b9150614e8a82614e4b565b602082019050919050565b5f6020820190508181035f830152614eac81614e73565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f614f1782613f52565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614f4957614f48614ee0565b5b600182019050919050565b5f614f5e82613f52565b9150614f6983613f52565b9250828203905081811115614f8157614f80614ee0565b5b92915050565b5f81519050614f9581613f5b565b92915050565b60038110614fa7575f5ffd5b50565b5f81519050614fb881614f9b565b92915050565b5f81519050614fcc81614be9565b92915050565b5f5f5f5f5f5f5f60e0888a031215614fed57614fec613f4a565b5b5f614ffa8a828b01614f87565b975050602061500b8a828b01614f87565b965050604061501c8a828b01614faa565b955050606061502d8a828b01614f87565b945050608061503e8a828b01614f87565b93505060a061504f8a828b01614fbe565b92505060c06150608a828b01614f87565b91505092959891949750929550565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b7f43616c6c6572206973206e6f7420746865206f776e6572206f662074686973205f8201527f6c6f670000000000000000000000000000000000000000000000000000000000602082015250565b5f6150f6602383614d88565b91506151018261509c565b604082019050919050565b5f6020820190508181035f830152615123816150ea565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f600282049050600182168061516e57607f821691505b6020821081036151815761518061512a565b5b50919050565b5f61519182613f52565b91505f82036151a3576151a2614ee0565b5b600182039050919050565b7f43616c6c6572206973206e6f7420616e206f70657261746f72000000000000005f82015250565b5f6151e2601983614d88565b91506151ed826151ae565b602082019050919050565b5f6020820190508181035f83015261520f816151d6565b9050919050565b7f54617267657420686173206e6f7420617474657374656420746f2074686973205f8201527f6c6f670000000000000000000000000000000000000000000000000000000000602082015250565b5f615270602383614d88565b915061527b82615216565b604082019050919050565b5f6020820190508181035f83015261529d81615264565b9050919050565b7f43616c6c657220686173206e6f7420617474657374656420746f2074686973205f8201527f6c6f670000000000000000000000000000000000000000000000000000000000602082015250565b5f6152fe602383614d88565b9150615309826152a4565b604082019050919050565b5f6020820190508181035f83015261532b816152f2565b9050919050565b7f43616e206f6e6c79206c6f6720686f74646f677320666f7220796f757273656c5f8201527f6600000000000000000000000000000000000000000000000000000000000000602082015250565b5f61538c602183614d88565b915061539782615332565b604082019050919050565b5f6020820190508181035f8301526153b981615380565b9050919050565b7f4c6f6720646f6573206e6f7420657869737400000000000000000000000000005f82015250565b5f6153f4601283614d88565b91506153ff826153c0565b602082019050919050565b5f6020820190508181035f830152615421816153e8565b9050919050565b7f43616e6e6f742061747465737420746f206f776e206c6f6700000000000000005f82015250565b5f61545c601883614d88565b915061546782615428565b602082019050919050565b5f6020820190508181035f83015261548981615450565b9050919050565b7f506c656173652063616c6c20617474657374546f4c6f67206469726563746c795f8201527f206f6e20746865204174746573746174696f6e4d616e6167657220636f6e747260208201527f6163740000000000000000000000000000000000000000000000000000000000604082015250565b5f615510604383614d88565b915061551b82615490565b606082019050919050565b5f6020820190508181035f83015261553d81615504565b9050919050565b7f537461727420696e646578206f7574206f6620626f756e6473000000000000005f82015250565b5f615578601983614d88565b915061558382615544565b602082019050919050565b5f6020820190508181035f8301526155a58161556c565b9050919050565b5f6155b682613f52565b91506155c183613f52565b92508282019050808211156155d9576155d8614ee0565b5b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f61561682613f52565b915061562183613f52565b925082615631576156306155df565b5b828204905092915050565b5f60408201905061564f5f83018561403b565b61565c6020830184614aac565b9392505050565b7f546172676574206973206e6f7420746865206f776e6572206f662074686973205f8201527f6c6f670000000000000000000000000000000000000000000000000000000000602082015250565b5f6156bd602383614d88565b91506156c882615663565b604082019050919050565b5f6020820190508181035f8301526156ea816156b1565b9050919050565b7f55736572206174746573746174696f6e206e6f7420666f756e640000000000005f82015250565b5f615725601a83614d88565b9150615730826156f1565b602082019050919050565b5f6020820190508181035f83015261575281615719565b9050919050565b5f82825260208201905092915050565b5f6157748385615759565b93506157818385846148e9565b61578a83614571565b840190509392505050565b5f60a0820190506157a85f830189614aac565b6157b560208301886147d3565b81810360408301526157c78187614d98565b905081810360608301526157dc818587615769565b90506157eb60808301846147d3565b979650505050505050565b5f8151905061580481614390565b92915050565b5f6020828403121561581f5761581e613f4a565b5b5f61582c848285016157f6565b91505092915050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f600883026158917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82615856565b61589b8683615856565b95508019841693508086168417925050509392505050565b5f6158cd6158c86158c384613f52565b61475f565b613f52565b9050919050565b5f819050919050565b6158e6836158b3565b6158fa6158f2826158d4565b848454615862565b825550505050565b5f5f905090565b615911615902565b61591c8184846158dd565b505050565b5b8181101561593f576159345f82615909565b600181019050615922565b5050565b601f8211156159845761595581615835565b61595e84615847565b8101602085101561596d578190505b61598161597985615847565b830182615921565b50505b505050565b5f82821c905092915050565b5f6159a45f1984600802615989565b1980831691505092915050565b5f6159bc8383615995565b9150826002028217905092915050565b6159d582614549565b67ffffffffffffffff8111156159ee576159ed614841565b5b6159f88254615157565b615a03828285615943565b5f60209050601f831160018114615a34575f8415615a22578287015190505b615a2c85826159b1565b865550615a93565b601f198416615a4286615835565b5f5b82811015615a6957848901518255600182019150602085019450602081019050615a44565b86831015615a865784890151615a82601f891682615995565b8355505b6001600288020188555050505b505050505050565b5f6080820190508181035f830152615ab38187614d98565b90508181036020830152615ac78186614d98565b9050615ad66040830185614aac565b615ae360608301846147d3565b95945050505050565b5f604082019050615aff5f8301856147d3565b615b0c6020830184614443565b9392505050565b7f4174746573746174696f6e206e6f7420666f756e6400000000000000000000005f82015250565b5f615b47601583614d88565b9150615b5282615b13565b602082019050919050565b5f6020820190508181035f830152615b7481615b3b565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffdfea2646970667358221220cc485ad8bee3e437f7e7c4e4ebb1c8bf4e559600baabe3f4c019acd3a3530f8964736f6c634300081e0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000003de0ba94a1f291a7c44bb029b765adb2c487063f000000000000000000000000cbf054aa8feb4fd0484e45b766b502bc045076b8000000000000000000000000b249d4bb7400e8d6cff4e7824ee2014a89a2baaf
-----Decoded View---------------
Arg [0] : _platformReferrer (address): 0x3dE0ba94A1F291A7c44bb029b765ADB2C487063F
Arg [1] : _attestationManager (address): 0xcBf054aA8FEb4fd0484E45b766B502Bc045076B8
Arg [2] : _coinDeploymentManager (address): 0xB249d4BB7400e8d6cFf4E7824ee2014A89a2bAAf
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 0000000000000000000000003de0ba94a1f291a7c44bb029b765adb2c487063f
Arg [1] : 000000000000000000000000cbf054aa8feb4fd0484e45b766b502bc045076b8
Arg [2] : 000000000000000000000000b249d4bb7400e8d6cff4e7824ee2014a89a2baaf
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 32 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
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.