Source Code
Overview
S Balance
More Info
ContractCreator
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
PayrollFactory
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol"; import {PayrollManagement} from "./PayrollManagement.sol"; contract PayrollFactory is AccessControl { // Custom errors error PayrollExists(); // Thrown when a payroll contract already exists for an employer error InvalidAddress(); // Thrown when an invalid address is provided error PayrollNotFound(); // Thrown when no payroll contract is found for an employer error NotApprovedEmployer(); // Thrown when an employer is not approved by the admin error InvalidPayrollContract(); // Thrown when an invalid payroll contract address is provided error MaxContractsReached(); // Thrown when an employer tries to create more than the allowed number of contracts // Roles bytes32 public constant FACTORY_ADMIN = keccak256("FACTORY_ADMIN"); // Role for factory admin // Structs struct EmployerContracts { address[] payrollContracts; uint256 contractCount; bool isApproved; } // State variables mapping(address => EmployerContracts) public employers; // employer => EmployerContracts struct mapping(address => bool) public isPayrollContract; // contract address => whether it is a payroll contract address public immutable usdcToken; // USDC token address address[] private allEmployers; // Events event PayrollContractCreated( address indexed employer, address indexed payrollContract ); // Emitted when a new payroll contract is created event EmployerApproved(address indexed employer); // Emitted when an employer is approved by the admin event AIAgentRoleGranted(address indexed payrollContract, address indexed aiAgent); // Emitted when AI_AGENT_ROLE is granted event PayrollContractPaused(address indexed payrollContract); // Emitted when a payroll contract is paused event PayrollContractUnpaused(address indexed payrollContract); // Emitted when a payroll contract is unpaused /** * @dev Constructor to initialize the factory. * @param _usdcToken Address of the USDC token contract. */ constructor(address _usdcToken) { if (_usdcToken == address(0)) revert InvalidAddress(); // Ensure USDC token address is valid // Grant roles to the deployer _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); _grantRole(FACTORY_ADMIN, msg.sender); usdcToken = _usdcToken; // Initialize USDC token address } /** * @dev Approves an employer to deploy their payroll contract. * @param _employer Address of the employer to approve. */ function approveEmployer(address _employer) external onlyRole(FACTORY_ADMIN) { if (_employer == address(0)) revert InvalidAddress(); employers[_employer].isApproved = true; allEmployers.push(_employer); emit EmployerApproved(_employer); } /** * @dev Returns an array of all approved employer addresses * @return address[] Array of employer addresses */ function getAllEmployers() external view returns (address[] memory) { return allEmployers; } /** * @dev Deploys a new PayrollManagement contract for an employer. * @return address The address of the deployed payroll contract. */ function createPayrollContract() external returns (address) { // Checks if (!employers[msg.sender].isApproved) revert NotApprovedEmployer(); if (employers[msg.sender].contractCount >= 3) revert MaxContractsReached(); // Effects PayrollManagement payrollContract = new PayrollManagement(usdcToken); address payrollAddress = address(payrollContract); employers[msg.sender].payrollContracts.push(payrollAddress); employers[msg.sender].contractCount++; isPayrollContract[payrollAddress] = true; // Events emit PayrollContractCreated(msg.sender, payrollAddress); // Interactions payrollContract.grantRole(payrollContract.ADMIN_ROLE(), address(this)); payrollContract.grantRole(payrollContract.EMPLOYER_ROLE(), msg.sender); return payrollAddress; } /** * @dev Grants the AI_AGENT_ROLE to an address in a specific payroll contract. * @param _payrollContract Address of the payroll contract. * @param _aiAgent Address of the AI agent. */ function grantAIAgentRole(address _payrollContract, address _aiAgent) external onlyRole(FACTORY_ADMIN) { // Checks if (_payrollContract == address(0) || _aiAgent == address(0)) revert InvalidAddress(); if (!isPayrollContract[_payrollContract]) revert InvalidPayrollContract(); // Events emit AIAgentRoleGranted(_payrollContract, _aiAgent); // Interactions PayrollManagement payrollContract = PayrollManagement(_payrollContract); payrollContract.grantRole(payrollContract.AI_AGENT_ROLE(), _aiAgent); } /** * @dev Retrieves the payroll contract address for a given employer. * @param _employer Address of the employer. * @return address The address of the payroll contract. */ function getPayrollContract(address _employer) external view returns (address) { if (_employer == address(0)) revert InvalidAddress(); // Validate employer address if (employers[_employer].payrollContracts.length == 0) revert PayrollNotFound(); // Ensure payroll contract exists return employers[_employer].payrollContracts[0]; // Return the first contract (or modify logic as needed) } /** * @dev Checks if an address is a valid payroll contract. * @param _contract Address to verify. * @return bool True if the address is a valid payroll contract. */ function isValidPayrollContract(address _contract) external view returns (bool) { return isPayrollContract[_contract]; // Return whether the address is a payroll contract } /** * @dev Pauses a payroll contract (only callable by the factory admin). * @param _payrollContract Address of the payroll contract to pause. */ function pausePayrollContract(address _payrollContract) external onlyRole(FACTORY_ADMIN) { // Checks if (_payrollContract == address(0)) revert InvalidAddress(); if (!isPayrollContract[_payrollContract]) revert InvalidPayrollContract(); // Events emit PayrollContractPaused(_payrollContract); // Interactions PayrollManagement payrollContract = PayrollManagement(_payrollContract); payrollContract.pause(); } /** * @dev Unpauses a payroll contract (only callable by the factory admin). * @param _payrollContract Address of the payroll contract to unpause. */ function unpausePayrollContract(address _payrollContract) external onlyRole(FACTORY_ADMIN) { // Checks if (_payrollContract == address(0)) revert InvalidAddress(); if (!isPayrollContract[_payrollContract]) revert InvalidPayrollContract(); // Events emit PayrollContractUnpaused(_payrollContract); // Interactions PayrollManagement payrollContract = PayrollManagement(_payrollContract); payrollContract.unpause(); } /** * @dev Retrieves the list of employees for a given employer. * @param _employer Address of the employer. * @return uint256[] Array of employee IDs. */ function getEmployerEmployees(address _employer) external view returns (uint256[] memory) { if (_employer == address(0)) revert InvalidAddress(); // Validate employer address if (employers[_employer].payrollContracts.length == 0) revert PayrollNotFound(); // Ensure payroll contract exists address payrollContract = employers[_employer].payrollContracts[0]; // Use the first contract (or modify logic as needed) return PayrollManagement(payrollContract).getEmployerEmployees(_employer); } /** * @dev Retrieves details of a specific employee for a given employer. * @param _employer Address of the employer. * @param _employeeId ID of the employee. * @return employeeId Employee ID. * @return walletAddress Employee wallet address. * @return salary Employee salary. * @return joiningDate Employee joining date. * @return isActive Whether the employee is active. */ function getEmployeeDetails(address _employer, uint256 _employeeId) external view returns ( uint256 employeeId, address walletAddress, uint256 salary, uint256 joiningDate, bool isActive ) { if (_employer == address(0)) revert InvalidAddress(); if (employers[_employer].payrollContracts.length == 0) revert PayrollNotFound(); address payrollContract = employers[_employer].payrollContracts[0]; // Use the first contract (or modify logic as needed) (employeeId, walletAddress, salary, joiningDate, isActive) = PayrollManagement(payrollContract).getEmployeeDetails(_employer, _employeeId); } /** * @dev Retrieves the balance of a given employer. * @param _employer Address of the employer. * @return uint256 Employer's balance. */ function getEmployerBalance(address _employer) external view returns (uint256) { if (_employer == address(0)) revert InvalidAddress(); // Validate employer address if (employers[_employer].payrollContracts.length == 0) revert PayrollNotFound(); // Ensure payroll contract exists address payrollContract = employers[_employer].payrollContracts[0]; // Use the first contract (or modify logic as needed) return PayrollManagement(payrollContract).getEmployerBalance(_employer); } /** * @dev Retrieves the payroll interval details for a specific employee. * @param _employer Address of the employer. * @param _employeeId ID of the employee. * @return cycleLength The length of the payroll cycle. * @return lastProcessedDate The last processed payroll date. * @return isActive Whether the payroll interval is active. */ function getPayrollInterval(address _employer, uint256 _employeeId) external view returns (uint256 cycleLength, uint256 lastProcessedDate, bool isActive) { if (_employer == address(0)) revert InvalidAddress(); if (employers[_employer].payrollContracts.length == 0) revert PayrollNotFound(); address payrollContract = employers[_employer].payrollContracts[0]; // Use the first contract (or modify logic as needed) return PayrollManagement(payrollContract).getPayrollInterval(_employer, _employeeId); } /** * @dev Retrieves the next payroll date for a specific employee. * @param _employer Address of the employer. * @param _employeeId ID of the employee. * @return nextPaymentDate The next payroll date. */ function getNextPayrollDate(address _employer, uint256 _employeeId) external view returns (uint256 nextPaymentDate) { if (_employer == address(0)) revert InvalidAddress(); if (employers[_employer].payrollContracts.length == 0) revert PayrollNotFound(); address payrollContract = employers[_employer].payrollContracts[0]; // Use the first contract (or modify logic as needed) return PayrollManagement(payrollContract).getNextPayrollDate(_employer, _employeeId); } /** * @dev Checks if the employer has sufficient balance to cover the total payroll liability. * @param _employer Address of the employer. * @return bool True if the balance is sufficient, false otherwise. */ function hasSufficientBalanceForPayroll(address _employer) external view returns (bool) { if (_employer == address(0)) revert InvalidAddress(); if (employers[_employer].payrollContracts.length == 0) revert PayrollNotFound(); address payrollContract = employers[_employer].payrollContracts[0]; // Use the first contract (or modify logic as needed) return PayrollManagement(payrollContract).hasSufficientBalanceForPayroll(_employer); } /** * @dev Calculates the total payroll liability for an employer. * @param _employer Address of the employer. * @return uint256 Total payroll liability in USDC. */ function getTotalPayrollLiability(address _employer) external view returns (uint256) { if (_employer == address(0)) revert InvalidAddress(); if (employers[_employer].payrollContracts.length == 0) revert PayrollNotFound(); address payrollContract = employers[_employer].payrollContracts[0]; // Use the first contract (or modify logic as needed) return PayrollManagement(payrollContract).getTotalPayrollLiability(_employer); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol) pragma solidity ^0.8.20; import {IAccessControl} from "./IAccessControl.sol"; import {Context} from "../utils/Context.sol"; import {ERC165} from "../utils/introspection/ERC165.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ```solidity * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ```solidity * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules} * to enforce additional security measures for this role. */ abstract contract AccessControl is Context, IAccessControl, ERC165 { struct RoleData { mapping(address account => bool) hasRole; bytes32 adminRole; } mapping(bytes32 role => RoleData) private _roles; bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Modifier that checks that an account has a specific role. Reverts * with an {AccessControlUnauthorizedAccount} error including the required role. */ modifier onlyRole(bytes32 role) { _checkRole(role); _; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view virtual returns (bool) { return _roles[role].hasRole[account]; } /** * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()` * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier. */ function _checkRole(bytes32 role) internal view virtual { _checkRole(role, _msgSender()); } /** * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account` * is missing `role`. */ function _checkRole(bytes32 role, address account) internal view virtual { if (!hasRole(role, account)) { revert AccessControlUnauthorizedAccount(account, role); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) { return _roles[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleGranted} event. */ function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleRevoked} event. */ function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been revoked `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `callerConfirmation`. * * May emit a {RoleRevoked} event. */ function renounceRole(bytes32 role, address callerConfirmation) public virtual { if (callerConfirmation != _msgSender()) { revert AccessControlBadConfirmation(); } _revokeRole(role, callerConfirmation); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { bytes32 previousAdminRole = getRoleAdmin(role); _roles[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted. * * Internal function without access restriction. * * May emit a {RoleGranted} event. */ function _grantRole(bytes32 role, address account) internal virtual returns (bool) { if (!hasRole(role, account)) { _roles[role].hasRole[account] = true; emit RoleGranted(role, account, _msgSender()); return true; } else { return false; } } /** * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked. * * Internal function without access restriction. * * May emit a {RoleRevoked} event. */ function _revokeRole(bytes32 role, address account) internal virtual returns (bool) { if (hasRole(role, account)) { _roles[role].hasRole[account] = false; emit RoleRevoked(role, account, _msgSender()); return true; } else { return false; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.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 signaling this. */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call. 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.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.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.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.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.2.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 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 // 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/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.0.0) (utils/Pausable.sol) pragma solidity ^0.8.20; import {Context} from "../utils/Context.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract Pausable is Context { bool private _paused; /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); /** * @dev The operation failed because the contract is paused. */ error EnforcedPause(); /** * @dev The operation failed because the contract is not paused. */ error ExpectedPause(); /** * @dev Initializes the contract in unpaused state. */ constructor() { _paused = false; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { _requireNotPaused(); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { _requirePaused(); _; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Throws if the contract is paused. */ function _requireNotPaused() internal view virtual { if (paused()) { revert EnforcedPause(); } } /** * @dev Throws if the contract is not paused. */ function _requirePaused() internal view virtual { if (!paused()) { revert ExpectedPause(); } } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; // Importing necessary OpenZeppelin contracts import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol"; import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; // PayrollManagement contract inherits from AccessControl and Pausable contract PayrollManagement is AccessControl, Pausable { using SafeERC20 for IERC20; // Custom errors for better error handling error InvalidAddress(); error EmployeeAlreadyExists(); error EmployeeNotFound(); error UnauthorizedAccess(); error InvalidPayrollCycle(); error PayrollCycleNotCompleted(); error InsufficientBalance(); error TransferFailed(); error PayrollAlreadyProcessed(); error ZeroAmount(); error InsufficientAllowance(); error WithdrawalLocked(uint256 nextPayrollDate); // Roles for access control bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); bytes32 public constant EMPLOYER_ROLE = keccak256("EMPLOYER_ROLE"); bytes32 public constant AI_AGENT_ROLE = keccak256("AI_AGENT_ROLE"); // Immutable USDC token contract IERC20 public immutable usdcToken; // Structs for employee and payroll management struct Employee { uint256 employeeId; address walletAddress; uint256 salary; uint256 joiningDate; bool isActive; } struct PayrollCycle { uint256 cycleId; uint256 startDate; uint256 endDate; bool isProcessed; } struct PayrollInterval { uint256 cycleLength; uint256 lastProcessedDate; bool isActive; } // State variables for storing employee and payroll data mapping(address => mapping(uint256 => Employee)) public employeesByEmployer; mapping(address => uint256[]) public employerEmployees; mapping(address => mapping(uint256 => PayrollCycle[])) public employeePayrollCycles; mapping(address => uint256) public employeeCounter; mapping(address => uint256) public employerBalances; mapping(address => mapping(uint256 => PayrollInterval)) public employeePayrollIntervals; // Events for logging important contract actions event EmployeeAdded(address indexed employer, uint256 indexed employeeId, address walletAddress); event EmployeeUpdated(address indexed employer, uint256 indexed employeeId); event EmployeeDeactivated(address indexed employer, uint256 indexed employeeId); event PayrollCycleCreated(address indexed employer, uint256 indexed employeeId, uint256 cycleId, uint256 startDate, uint256 endDate); event SalaryPaid(address indexed employer, uint256 indexed employeeId, uint256 amount); event FundsAdded(address indexed employer, uint256 amount); event FundsWithdrawn(address indexed employer, uint256 amount); event PayrollIntervalSet(address indexed employer, uint256 indexed employeeId, uint256 cycleLength); // Constructor to initialize the contract with the USDC token address constructor(address _usdcToken) { if (_usdcToken == address(0)) revert InvalidAddress(); usdcToken = IERC20(_usdcToken); _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); } // Modifiers for access control and validation modifier onlyEmployerOf(uint256 _employeeId) { if (employeesByEmployer[msg.sender][_employeeId].walletAddress == address(0)) revert EmployeeNotFound(); _; } modifier onlyEmployerOrAgent() { if (!hasRole(EMPLOYER_ROLE, msg.sender) && !hasRole(AI_AGENT_ROLE, msg.sender)) revert UnauthorizedAccess(); _; } // Function to add a new employee function addEmployee(address _walletAddress, uint256 _salary) external whenNotPaused onlyRole(EMPLOYER_ROLE) returns (uint256) { if (_walletAddress == address(0)) revert InvalidAddress(); if (_salary == 0) revert ZeroAmount(); // Check if employee already exists for (uint256 i = 0; i < employerEmployees[msg.sender].length; i++) { if (employeesByEmployer[msg.sender][employerEmployees[msg.sender][i]].walletAddress == _walletAddress) { revert EmployeeAlreadyExists(); } } // Create new employee uint256 newEmployeeId = ++employeeCounter[msg.sender]; Employee storage newEmployee = employeesByEmployer[msg.sender][newEmployeeId]; newEmployee.employeeId = newEmployeeId; newEmployee.walletAddress = _walletAddress; newEmployee.salary = _salary; newEmployee.joiningDate = block.timestamp; newEmployee.isActive = true; employerEmployees[msg.sender].push(newEmployeeId); emit EmployeeAdded(msg.sender, newEmployeeId, _walletAddress); return newEmployeeId; } // Function to create a new payroll cycle for an employee function createPayrollCycle(uint256 _employeeId, uint256 _startDate, uint256 _endDate) external whenNotPaused onlyRole(EMPLOYER_ROLE) onlyEmployerOf(_employeeId) { Employee storage employee = employeesByEmployer[msg.sender][_employeeId]; if (!employee.isActive) revert EmployeeNotFound(); if (_startDate >= _endDate) revert InvalidPayrollCycle(); // Check for overlapping payroll cycles PayrollCycle[] storage cycles = employeePayrollCycles[msg.sender][_employeeId]; for (uint256 i = 0; i < cycles.length; i++) { if (_startDate < cycles[i].endDate && _endDate > cycles[i].startDate) { revert InvalidPayrollCycle(); } } // Add new payroll cycle uint256 cycleId = cycles.length; cycles.push(PayrollCycle({ cycleId: cycleId, startDate: _startDate, endDate: _endDate, isProcessed: false })); emit PayrollCycleCreated(msg.sender, _employeeId, cycleId, _startDate, _endDate); } // Function to set the payroll interval for an employee function setPayrollInterval(uint256 _employeeId, uint256 _cycleLength) external whenNotPaused onlyRole(EMPLOYER_ROLE) onlyEmployerOf(_employeeId) { employeePayrollIntervals[msg.sender][_employeeId] = PayrollInterval({ cycleLength: _cycleLength, lastProcessedDate: block.timestamp, isActive: true }); emit PayrollIntervalSet(msg.sender, _employeeId, _cycleLength); } // Function to add funds to the employer's balance function addFunds(uint256 _amount) external whenNotPaused onlyRole(EMPLOYER_ROLE) { if (_amount == 0) revert ZeroAmount(); uint256 allowance = usdcToken.allowance(msg.sender, address(this)); if (allowance < _amount) revert InsufficientAllowance(); usdcToken.safeTransferFrom(msg.sender, address(this), _amount); employerBalances[msg.sender] += _amount; emit FundsAdded(msg.sender, _amount); } // Function to process all payrolls for the employer function processAllPayrolls() external whenNotPaused onlyEmployerOrAgent { uint256[] memory employeeIds = employerEmployees[msg.sender]; if (employeeIds.length == 0) revert EmployeeNotFound(); uint256 totalPayout = 0; // Calculate total payout for all employees for (uint256 i = 0; i < employeeIds.length; i++) { Employee storage employee = employeesByEmployer[msg.sender][employeeIds[i]]; PayrollInterval storage interval = employeePayrollIntervals[msg.sender][employee.employeeId]; if (!interval.isActive || !employee.isActive) continue; uint256 nextPaymentDate = interval.lastProcessedDate + interval.cycleLength; if (block.timestamp < nextPaymentDate) continue; totalPayout += employee.salary; } if (employerBalances[msg.sender] < totalPayout) revert InsufficientBalance(); // Process payroll for each employee for (uint256 i = 0; i < employeeIds.length; i++) { Employee storage employee = employeesByEmployer[msg.sender][employeeIds[i]]; PayrollInterval storage interval = employeePayrollIntervals[msg.sender][employee.employeeId]; if (!interval.isActive || !employee.isActive) continue; uint256 nextPaymentDate = interval.lastProcessedDate + interval.cycleLength; if (block.timestamp < nextPaymentDate) continue; interval.lastProcessedDate = block.timestamp; employerBalances[msg.sender] -= employee.salary; usdcToken.safeTransfer(employee.walletAddress, employee.salary); emit SalaryPaid(msg.sender, employee.employeeId, employee.salary); } } // Function to update an employee's salary function updateEmployeeSalary(uint256 _employeeId, uint256 _newSalary) external whenNotPaused onlyRole(EMPLOYER_ROLE) onlyEmployerOf(_employeeId) { if (_newSalary == 0) revert ZeroAmount(); employeesByEmployer[msg.sender][_employeeId].salary = _newSalary; emit EmployeeUpdated(msg.sender, _employeeId); } // Function to withdraw funds from the employer's balance function withdrawFunds(uint256 _amount) external whenNotPaused onlyRole(EMPLOYER_ROLE) { if (_amount == 0) revert ZeroAmount(); if (employerBalances[msg.sender] < _amount) revert InsufficientBalance(); // Check if withdrawal is locked due to upcoming payroll uint256 nextPayrollDate = 0; for (uint256 i = 0; i < employerEmployees[msg.sender].length; i++) { uint256 employeeId = employerEmployees[msg.sender][i]; PayrollInterval storage interval = employeePayrollIntervals[msg.sender][employeeId]; if (interval.isActive) { uint256 nextPaymentDate = interval.lastProcessedDate + interval.cycleLength; if (nextPaymentDate > nextPayrollDate) { nextPayrollDate = nextPaymentDate; } } } if (nextPayrollDate > 0 && block.timestamp >= nextPayrollDate - 3 days) { revert WithdrawalLocked(nextPayrollDate); } employerBalances[msg.sender] -= _amount; usdcToken.safeTransfer(msg.sender, _amount); emit FundsWithdrawn(msg.sender, _amount); } // Function to deactivate an employee function deactivateEmployee(uint256 _employeeId) external whenNotPaused onlyRole(EMPLOYER_ROLE) onlyEmployerOf(_employeeId) { employeesByEmployer[msg.sender][_employeeId].isActive = false; emit EmployeeDeactivated(msg.sender, _employeeId); } // Function to get the employer's balance function getEmployerBalance(address _employer) external view returns (uint256) { return employerBalances[_employer]; } // Function to get the list of employees for an employer function getEmployerEmployees(address _employer) external view returns (uint256[] memory) { return employerEmployees[_employer]; } // Function to get the total payroll liability for an employer function getTotalPayrollLiability(address _employer) external view returns (uint256 totalLiability) { uint256[] memory employeeIds = employerEmployees[_employer]; for (uint256 i = 0; i < employeeIds.length; i++) { Employee memory employee = employeesByEmployer[_employer][employeeIds[i]]; if (employee.isActive) { totalLiability += employee.salary; } } return totalLiability; } // Function to get the payroll interval for an employee function getPayrollInterval(address _employer, uint256 _employeeId) external view returns (uint256 cycleLength, uint256 lastProcessedDate, bool isActive) { PayrollInterval memory interval = employeePayrollIntervals[_employer][_employeeId]; return (interval.cycleLength, interval.lastProcessedDate, interval.isActive); } // Function to get the next payroll date for an employee function getNextPayrollDate(address _employer, uint256 _employeeId) external view returns (uint256 nextPaymentDate) { PayrollInterval memory interval = employeePayrollIntervals[_employer][_employeeId]; if (!interval.isActive) return 0; return interval.lastProcessedDate + interval.cycleLength; } // Function to check if the employer has sufficient balance for payroll function hasSufficientBalanceForPayroll(address _employer) external view returns (bool) { uint256 totalLiability = this.getTotalPayrollLiability(_employer); return employerBalances[_employer] >= totalLiability; } // Function to get all employees with details for an employer function getAllEmployeesWithDetails(address _employer) external view returns (Employee[] memory) { uint256[] memory employeeIds = employerEmployees[_employer]; Employee[] memory employeeList = new Employee[](employeeIds.length); for (uint256 i = 0; i < employeeIds.length; i++) { employeeList[i] = employeesByEmployer[_employer][employeeIds[i]]; } return employeeList; } // Function to get details of a specific employee function getEmployeeDetails(address _employer, uint256 _employeeId) external view returns (uint256 employeeId, address walletAddress, uint256 salary, uint256 joiningDate, bool isActive) { Employee memory emp = employeesByEmployer[_employer][_employeeId]; return (emp.employeeId, emp.walletAddress, emp.salary, emp.joiningDate, emp.isActive); } // Function to pause the contract (only callable by admin) function pause() external onlyRole(ADMIN_ROLE) { _pause(); } // Function to unpause the contract (only callable by admin) function unpause() external onlyRole(ADMIN_ROLE) { _unpause(); } }
{ "optimizer": { "enabled": true, "runs": 200 }, "evmVersion": "paris", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
[{"inputs":[{"internalType":"address","name":"_usdcToken","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidPayrollContract","type":"error"},{"inputs":[],"name":"MaxContractsReached","type":"error"},{"inputs":[],"name":"NotApprovedEmployer","type":"error"},{"inputs":[],"name":"PayrollExists","type":"error"},{"inputs":[],"name":"PayrollNotFound","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"payrollContract","type":"address"},{"indexed":true,"internalType":"address","name":"aiAgent","type":"address"}],"name":"AIAgentRoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"employer","type":"address"}],"name":"EmployerApproved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"employer","type":"address"},{"indexed":true,"internalType":"address","name":"payrollContract","type":"address"}],"name":"PayrollContractCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"payrollContract","type":"address"}],"name":"PayrollContractPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"payrollContract","type":"address"}],"name":"PayrollContractUnpaused","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":"FACTORY_ADMIN","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_employer","type":"address"}],"name":"approveEmployer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"createPayrollContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"employers","outputs":[{"internalType":"uint256","name":"contractCount","type":"uint256"},{"internalType":"bool","name":"isApproved","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllEmployers","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_employer","type":"address"},{"internalType":"uint256","name":"_employeeId","type":"uint256"}],"name":"getEmployeeDetails","outputs":[{"internalType":"uint256","name":"employeeId","type":"uint256"},{"internalType":"address","name":"walletAddress","type":"address"},{"internalType":"uint256","name":"salary","type":"uint256"},{"internalType":"uint256","name":"joiningDate","type":"uint256"},{"internalType":"bool","name":"isActive","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_employer","type":"address"}],"name":"getEmployerBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_employer","type":"address"}],"name":"getEmployerEmployees","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_employer","type":"address"},{"internalType":"uint256","name":"_employeeId","type":"uint256"}],"name":"getNextPayrollDate","outputs":[{"internalType":"uint256","name":"nextPaymentDate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_employer","type":"address"}],"name":"getPayrollContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_employer","type":"address"},{"internalType":"uint256","name":"_employeeId","type":"uint256"}],"name":"getPayrollInterval","outputs":[{"internalType":"uint256","name":"cycleLength","type":"uint256"},{"internalType":"uint256","name":"lastProcessedDate","type":"uint256"},{"internalType":"bool","name":"isActive","type":"bool"}],"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":"address","name":"_employer","type":"address"}],"name":"getTotalPayrollLiability","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_payrollContract","type":"address"},{"internalType":"address","name":"_aiAgent","type":"address"}],"name":"grantAIAgentRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_employer","type":"address"}],"name":"hasSufficientBalanceForPayroll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isPayrollContract","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"}],"name":"isValidPayrollContract","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_payrollContract","type":"address"}],"name":"pausePayrollContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_payrollContract","type":"address"}],"name":"unpausePayrollContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"usdcToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60a060405234801561001057600080fd5b50604051613de2380380613de283398101604081905261002f9161014a565b6001600160a01b0381166100565760405163e6c4247b60e01b815260040160405180910390fd5b61006160003361009e565b5061008c7f334e4ff29b2b33ea01e68e72a933a3c413941d8539341b73a9bd86a2ae4d12f63361009e565b506001600160a01b031660805261017a565b6000828152602081815260408083206001600160a01b038516845290915281205460ff16610140576000838152602081815260408083206001600160a01b03861684529091529020805460ff191660011790556100f83390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610144565b5060005b92915050565b60006020828403121561015c57600080fd5b81516001600160a01b038116811461017357600080fd5b9392505050565b608051613c4661019c6000396000818161022c0152610cdf0152613c466000f3fe608060405234801561001057600080fd5b506004361061018e5760003560e01c8063754e884e116100de578063d547741f11610097578063ed332bf011610071578063ed332bf014610408578063f26d55f71461044d578063f57cc1d314610460578063fd9139a31461047357600080fd5b8063d547741f146103da578063e751b5b4146103ed578063ec255746146103f557600080fd5b8063754e884e146103395780638ca098a31461035c57806391d148541461038c578063a217fddf1461039f578063c9ac5d1f146103a7578063cd3dc1b8146103c757600080fd5b806315e1bbc41161014b5780632e9db8d7116101255780632e9db8d7146102d45780632f2ff15d146102e757806336568abe146102fa57806371a4a62e1461030d57600080fd5b806315e1bbc41461027b578063248a9ca31461029c57806328a854e6146102bf57600080fd5b806301ffc9a714610193578063040f7f94146101bb57806308d0eb34146101ce5780630be435bd146101e357806311eac8551461022757806314c671d614610266575b600080fd5b6101a66101a136600461165e565b610486565b60405190151581526020015b60405180910390f35b6101a66101c936600461169d565b6104bd565b6101e16101dc36600461169d565b6105c7565b005b6102126101f136600461169d565b60016020819052600091825260409091209081015460029091015460ff1682565b604080519283529015156020830152016101b2565b61024e7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101b2565b61026e6106d0565b6040516101b291906116ba565b61028e61028936600461169d565b610732565b6040519081526020016101b2565b61028e6102aa366004611706565b60009081526020819052604090206001015490565b61028e600080516020613bf183398151915281565b61028e6102e236600461169d565b610836565b6101e16102f536600461171f565b6108fd565b6101e161030836600461171f565b610928565b6101a661031b36600461169d565b6001600160a01b031660009081526002602052604090205460ff1690565b6101a661034736600461169d565b60026020526000908152604090205460ff1681565b61036f61036a36600461174f565b610960565b6040805193845260208401929092521515908201526060016101b2565b6101a661039a36600461171f565b610a7a565b61028e600081565b6103ba6103b536600461169d565b610aa3565b6040516101b2919061177b565b61024e6103d536600461169d565b610baa565b6101e16103e836600461171f565b610c4f565b61024e610c74565b6101e161040336600461169d565b610f6f565b61041b61041636600461174f565b61103e565b604080519586526001600160a01b0390941660208601529284019190915260608301521515608082015260a0016101b2565b6101e161045b3660046117b3565b611160565b61028e61046e36600461174f565b611308565b6101e161048136600461169d565b61141a565b60006001600160e01b03198216637965db0b60e01b14806104b757506301ffc9a760e01b6001600160e01b03198316145b92915050565b60006001600160a01b0382166104e65760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b038216600090815260016020526040812054900361051e57604051633bf7330b60e21b815260040160405180910390fd5b6001600160a01b038216600090815260016020526040812080548290610546576105466117e1565b600091825260209091200154604051630103dfe560e21b81526001600160a01b0385811660048301529091169150819063040f7f9490602401602060405180830381865afa15801561059c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105c0919061180c565b9392505050565b600080516020613bf18339815191526105df81611506565b6001600160a01b0382166106065760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b03821660009081526002602052604090205460ff1661063f5760405163b3b752eb60e01b815260040160405180910390fd5b6040516001600160a01b038316907f8386b23e7b5e56defef4896578831a4c8858978cfc3daa72ca9930769e021cb190600090a26000829050806001600160a01b0316638456cb596040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156106b357600080fd5b505af11580156106c7573d6000803e3d6000fd5b50505050505050565b6060600380548060200260200160405190810160405280929190818152602001828054801561072857602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161070a575b5050505050905090565b60006001600160a01b03821661075b5760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b038216600090815260016020526040812054900361079357604051633bf7330b60e21b815260040160405180910390fd5b6001600160a01b0382166000908152600160205260408120805482906107bb576107bb6117e1565b6000918252602090912001546040516305786ef160e21b81526001600160a01b038581166004830152909116915081906315e1bbc4906024015b602060405180830381865afa158015610812573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105c09190611827565b60006001600160a01b03821661085f5760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b038216600090815260016020526040812054900361089757604051633bf7330b60e21b815260040160405180910390fd5b6001600160a01b0382166000908152600160205260408120805482906108bf576108bf6117e1565b600091825260209091200154604051632e9db8d760e01b81526001600160a01b03858116600483015290911691508190632e9db8d7906024016107f5565b60008281526020819052604090206001015461091881611506565b6109228383611513565b50505050565b6001600160a01b03811633146109515760405163334bd91960e11b815260040160405180910390fd5b61095b82826115a5565b505050565b600080806001600160a01b03851661098b5760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b03851660009081526001602052604081205490036109c357604051633bf7330b60e21b815260040160405180910390fd5b6001600160a01b0385166000908152600160205260408120805482906109eb576109eb6117e1565b600091825260209091200154604051638ca098a360e01b81526001600160a01b0388811660048301526024820188905290911691508190638ca098a390604401606060405180830381865afa158015610a48573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a6c9190611840565b935093509350509250925092565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b60606001600160a01b038216610acc5760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b0382166000908152600160205260408120549003610b0457604051633bf7330b60e21b815260040160405180910390fd5b6001600160a01b038216600090815260016020526040812080548290610b2c57610b2c6117e1565b60009182526020909120015460405163c9ac5d1f60e01b81526001600160a01b0385811660048301529091169150819063c9ac5d1f90602401600060405180830381865afa158015610b82573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526105c0919081019061188c565b60006001600160a01b038216610bd35760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b0382166000908152600160205260408120549003610c0b57604051633bf7330b60e21b815260040160405180910390fd5b6001600160a01b03821660009081526001602052604081208054909190610c3457610c346117e1565b6000918252602090912001546001600160a01b031692915050565b600082815260208190526040902060010154610c6a81611506565b61092283836115a5565b3360009081526001602052604081206002015460ff16610ca75760405163cf11a12160e01b815260040160405180910390fd5b3360009081526001602081905260409091200154600311610cdb57604051637c14665360e01b815260040160405180910390fd5b60007f0000000000000000000000000000000000000000000000000000000000000000604051610d0a90611651565b6001600160a01b039091168152602001604051809103906000f080158015610d36573d6000803e3d6000fd5b5033600081815260016020818152604083208054808401825581855291842090910180546001600160a01b0319166001600160a01b038716179055928252919091018054929350839291610d8983611959565b90915550506001600160a01b038116600081815260026020526040808220805460ff191660011790555133917fe6b90854f2f854b3d6bd3a78791efaf8f5c4b16e72adee672b2495d5171ea97f91a3816001600160a01b0316632f2ff15d836001600160a01b03166375b238fc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e499190611827565b6040516001600160e01b031960e084901b1681526004810191909152306024820152604401600060405180830381600087803b158015610e8857600080fd5b505af1158015610e9c573d6000803e3d6000fd5b50505050816001600160a01b0316632f2ff15d836001600160a01b0316632426bb386040518163ffffffff1660e01b8152600401602060405180830381865afa158015610eed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f119190611827565b6040516001600160e01b031960e084901b1681526004810191909152336024820152604401600060405180830381600087803b158015610f5057600080fd5b505af1158015610f64573d6000803e3d6000fd5b509295945050505050565b600080516020613bf1833981519152610f8781611506565b6001600160a01b038216610fae5760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b0382166000818152600160208190526040808320600201805460ff19168317905560038054928301815583527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b90910180546001600160a01b03191684179055517f0d884eb0b7cbf3f44571b21ba60ea7df041f15b92935f82ff78c00e1ea376c659190a25050565b6000808080806001600160a01b03871661106b5760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b03871660009081526001602052604081205490036110a357604051633bf7330b60e21b815260040160405180910390fd5b6001600160a01b0387166000908152600160205260408120805482906110cb576110cb6117e1565b600091825260209091200154604051630ed332bf60e41b81526001600160a01b038a81166004830152602482018a90529091169150819063ed332bf09060440160a060405180830381865afa158015611128573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114c9190611980565b939c929b5090995097509095509350505050565b600080516020613bf183398151915261117881611506565b6001600160a01b038316158061119557506001600160a01b038216155b156111b35760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b03831660009081526002602052604090205460ff166111ec5760405163b3b752eb60e01b815260040160405180910390fd5b816001600160a01b0316836001600160a01b03167feabd3b45e0004ece10559102bfc26fdbd57a4201accc3bc1de60627362f5489d60405160405180910390a36000839050806001600160a01b0316632f2ff15d826001600160a01b03166325b8e7eb6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561127e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112a29190611827565b6040516001600160e01b031960e084901b16815260048101919091526001600160a01b0386166024820152604401600060405180830381600087803b1580156112ea57600080fd5b505af11580156112fe573d6000803e3d6000fd5b5050505050505050565b60006001600160a01b0383166113315760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b038316600090815260016020526040812054900361136957604051633bf7330b60e21b815260040160405180910390fd5b6001600160a01b038316600090815260016020526040812080548290611391576113916117e1565b60009182526020909120015460405163f57cc1d360e01b81526001600160a01b038681166004830152602482018690529091169150819063f57cc1d390604401602060405180830381865afa1580156113ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114129190611827565b949350505050565b600080516020613bf183398151915261143281611506565b6001600160a01b0382166114595760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b03821660009081526002602052604090205460ff166114925760405163b3b752eb60e01b815260040160405180910390fd5b6040516001600160a01b038316907fad335d75fc1702677f623fbb30a0148eea2fda954bfcaf97a05353d72b641f1a90600090a26000829050806001600160a01b0316633f4ba83a6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156106b357600080fd5b6115108133611610565b50565b600061151f8383610a7a565b61159d576000838152602081815260408083206001600160a01b03861684529091529020805460ff191660011790556115553390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45060016104b7565b5060006104b7565b60006115b18383610a7a565b1561159d576000838152602081815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45060016104b7565b61161a8282610a7a565b61164d5760405163e2517d3f60e01b81526001600160a01b03821660048201526024810183905260440160405180910390fd5b5050565b61221c806119d583390190565b60006020828403121561167057600080fd5b81356001600160e01b0319811681146105c057600080fd5b6001600160a01b038116811461151057600080fd5b6000602082840312156116af57600080fd5b81356105c081611688565b602080825282518282018190526000918401906040840190835b818110156116fb5783516001600160a01b03168352602093840193909201916001016116d4565b509095945050505050565b60006020828403121561171857600080fd5b5035919050565b6000806040838503121561173257600080fd5b82359150602083013561174481611688565b809150509250929050565b6000806040838503121561176257600080fd5b823561176d81611688565b946020939093013593505050565b602080825282518282018190526000918401906040840190835b818110156116fb578351835260209384019390920191600101611795565b600080604083850312156117c657600080fd5b82356117d181611688565b9150602083013561174481611688565b634e487b7160e01b600052603260045260246000fd5b8051801515811461180757600080fd5b919050565b60006020828403121561181e57600080fd5b6105c0826117f7565b60006020828403121561183957600080fd5b5051919050565b60008060006060848603121561185557600080fd5b83516020850151909350915061186d604085016117f7565b90509250925092565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561189e57600080fd5b815167ffffffffffffffff8111156118b557600080fd5b8201601f810184136118c657600080fd5b805167ffffffffffffffff8111156118e0576118e0611876565b8060051b604051601f19603f830116810181811067ffffffffffffffff8211171561190d5761190d611876565b60405291825260208184018101929081018784111561192b57600080fd5b6020850194505b8385101561194e57845180825260209586019590935001611932565b509695505050505050565b60006001820161197957634e487b7160e01b600052601160045260246000fd5b5060010190565b600080600080600060a0868803121561199857600080fd5b855160208701519095506119ab81611688565b60408701516060880151919550935091506119c8608087016117f7565b9050929550929590935056fe60a060405234801561001057600080fd5b5060405161221c38038061221c83398101604081905261002f9161012b565b6001805460ff191690556001600160a01b0381166100605760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b03811660805261007860003361007f565b505061015b565b6000828152602081815260408083206001600160a01b038516845290915281205460ff16610121576000838152602081815260408083206001600160a01b03861684529091529020805460ff191660011790556100d93390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610125565b5060005b92915050565b60006020828403121561013d57600080fd5b81516001600160a01b038116811461015457600080fd5b9392505050565b60805161208a610192600039600081816102610152818161094d015281816113b701528181611488015261152b015261208a6000f3fe608060405234801561001057600080fd5b506004361061021c5760003560e01c8063769f6df211610125578063be999705116100ad578063e7fd9a131161007c578063e7fd9a1314610637578063ed332bf01461064a578063f57cc1d3146106c9578063f91575f0146106dc578063fa85b26a146106ef57600080fd5b8063be999705146105de578063c9ac5d1f146105f1578063d1299e3a14610611578063d547741f1461062457600080fd5b80638ca098a3116100f45780638ca098a31461054657806391d14854146105a8578063a217fddf146105bb578063ad76d766146105c3578063bb97eab7146105d657600080fd5b8063769f6df2146104a45780637ca987fb146104c45780638456cb591461051e57806385e06e081461052657600080fd5b80632e9db8d7116101a85780633f4ba83a116101775780633f4ba83a146103b2578063509f2868146103ba5780635c975abb146103ef5780636f4e5f3f146103fa57806375b238fc1461047d57600080fd5b80632e9db8d7146103435780632f2909f31461036c5780632f2ff15d1461038c57806336568abe1461039f57600080fd5b806315e1bbc4116101ef57806315e1bbc4146102b0578063233d98d3146102d15780632426bb38146102e4578063248a9ca3146102f957806325b8e7eb1461031c57600080fd5b806301ffc9a714610221578063040f7f941461024957806311eac8551461025c578063155dd5ee1461029b575b600080fd5b61023461022f366004611d8f565b610702565b60405190151581526020015b60405180910390f35b610234610257366004611ddc565b610739565b6102837f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610240565b6102ae6102a9366004611df7565b6107cb565b005b6102c36102be366004611ddc565b6109af565b604051908152602001610240565b6102ae6102df366004611e10565b610ad9565b6102c360008051602061203583398151915281565b6102c3610307366004611df7565b60009081526020819052604090206001015490565b6102c37ffaa623ff490ff9197b5ad863cd7f72b33e2e131994d09bd7260d7cd680faad7981565b6102c3610351366004611ddc565b6001600160a01b031660009081526006602052604090205490565b6102c361037a366004611ddc565b60066020526000908152604090205481565b6102ae61039a366004611e32565b610bae565b6102ae6103ad366004611e32565b610bd9565b6102ae610c11565b6103cd6103c8366004611e5e565b610c46565b6040805194855260208501939093529183015215156060820152608001610240565b60015460ff16610234565b61044b610408366004611e91565b600260208181526000938452604080852090915291835291208054600182015492820154600383015460049093015491936001600160a01b031692909160ff1685565b604080519586526001600160a01b0390941660208601529284019190915260608301521515608082015260a001610240565b6102c37fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177581565b6104b76104b2366004611ddc565b610c9e565b6040516102409190611ebb565b6105016104d2366004611e91565b600760209081526000928352604080842090915290825290208054600182015460029092015490919060ff1683565b604080519384526020840192909252151590820152606001610240565b6102ae610e5d565b6102c3610534366004611ddc565b60056020526000908152604090205481565b610501610554366004611e91565b6001600160a01b038216600090815260076020908152604080832084845282529182902082516060810184528154808252600183015493820184905260029092015460ff1615159301839052919250925092565b6102346105b6366004611e32565b610e8f565b6102c3600081565b6102ae6105d1366004611f36565b610eb8565b6102ae6110cf565b6102ae6105ec366004611df7565b611429565b6106046105ff366004611ddc565b6115a9565b6040516102409190611f62565b6102ae61061f366004611e10565b611615565b6102ae610632366004611e32565b61170a565b6102c3610645366004611e91565b61172f565b61044b610658366004611e91565b6001600160a01b03918216600090815260026020818152604080842094845293815291839020835160a081018552815480825260018301549096169381018490529181015493820184905260038101546060830181905260049091015460ff16151560809092018290529394919391565b6102c36106d7366004611e91565b611912565b6102ae6106ea366004611df7565b611983565b6102c36106fd366004611e91565b611a3b565b60006001600160e01b03198216637965db0b60e01b148061073357506301ffc9a760e01b6001600160e01b03198316145b92915050565b6040516305786ef160e21b81526001600160a01b0382166004820152600090819030906315e1bbc490602401602060405180830381865afa158015610782573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a69190611f9a565b6001600160a01b03909316600090815260066020526040902054929092101592915050565b6107d3611a6c565b6000805160206120358339815191526107eb81611a92565b8160000361080c57604051631f2a200560e01b815260040160405180910390fd5b3360009081526006602052604090205482111561083c57604051631e9acf1760e31b815260040160405180910390fd5b6000805b336000908152600360205260409020548110156108d85733600090815260036020526040812080548390811061087857610878611fb3565b6000918252602080832090910154338352600782526040808420828552909252912060028101549192509060ff16156108ce57805460018201546000916108be91611fdf565b9050848111156108cc578094505b505b5050600101610840565b506000811180156108f557506108f16203f48082611ff2565b4210155b1561091b5760405163213bea0b60e11b8152600481018290526024015b60405180910390fd5b336000908152600660205260408120805485929061093a908490611ff2565b9091555061097490506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163385611a9c565b60405183815233907feaff4b37086828766ad3268786972c0cd24259d4c87a80f9d3963a3c3d999b0d906020015b60405180910390a2505050565b6001600160a01b038116600090815260036020908152604080832080548251818502810185019093528083528493830182828015610a0c57602002820191906000526020600020905b8154815260200190600101908083116109f8575b5050505050905060005b8151811015610ad2576001600160a01b038416600090815260026020526040812083518290859085908110610a4d57610a4d611fb3565b6020908102919091018101518252818101929092526040908101600020815160a0810183528154815260018201546001600160a01b031693810193909352600281015491830191909152600381015460608301526004015460ff161580156080830152909150610ac9576040810151610ac69085611fdf565b93505b50600101610a16565b5050919050565b610ae1611a6c565b600080516020612035833981519152610af981611a92565b33600090815260026020908152604080832086845290915290206001015483906001600160a01b0316610b3f57604051635d70aba760e11b815260040160405180910390fd5b82600003610b6057604051631f2a200560e01b815260040160405180910390fd5b336000818152600260208181526040808420898552909152808320909101869055518692917f1ee4ba310539bf61900eae83f6f369673ce6a17c2acae70119382e9ad727311091a350505050565b600082815260208190526040902060010154610bc981611a92565b610bd38383611afb565b50505050565b6001600160a01b0381163314610c025760405163334bd91960e11b815260040160405180910390fd5b610c0c8282611b8d565b505050565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775610c3b81611a92565b610c43611bf8565b50565b60046020528260005260406000206020528160005260406000208181548110610c6e57600080fd5b60009182526020909120600490910201805460018201546002830154600390930154919550935090915060ff1684565b6001600160a01b0381166000908152600360209081526040808320805482518185028101850190935280835260609493830182828015610cfd57602002820191906000526020600020905b815481526020019060010190808311610ce9575b505050505090506000815167ffffffffffffffff811115610d2057610d20612005565b604051908082528060200260200182016040528015610d8e57816020015b610d7b6040518060a001604052806000815260200160006001600160a01b0316815260200160008152602001600081526020016000151581525090565b815260200190600190039081610d3e5790505b50905060005b8251811015610e55576001600160a01b03851660009081526002602052604081208451909190859084908110610dcc57610dcc611fb3565b6020908102919091018101518252818101929092526040908101600020815160a0810183528154815260018201546001600160a01b031693810193909352600281015491830191909152600381015460608301526004015460ff16151560808201528251839083908110610e4257610e42611fb3565b6020908102919091010152600101610d94565b509392505050565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775610e8781611a92565b610c43611c4a565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b610ec0611a6c565b600080516020612035833981519152610ed881611a92565b33600090815260026020908152604080832087845290915290206001015484906001600160a01b0316610f1e57604051635d70aba760e11b815260040160405180910390fd5b3360009081526002602090815260408083208884529091529020600481015460ff16610f5d57604051635d70aba760e11b815260040160405180910390fd5b838510610f7d57604051634fe2f5b960e01b815260040160405180910390fd5b3360009081526004602090815260408083208984529091528120905b815481101561101c57818181548110610fb457610fb4611fb3565b90600052602060002090600402016002015487108015610ff65750818181548110610fe157610fe1611fb3565b90600052602060002090600402016001015486115b1561101457604051634fe2f5b960e01b815260040160405180910390fd5b600101610f99565b5080546040805160808101825282815260208082018a81528284018a8152600060608086018281526001808a018b558a845292869020965160048a029097019687559351918601919091559051600285015590516003909301805460ff19169315159390931790925582518481529081018a9052918201889052899133917fd153b8ef43cb85b04d95d02215fc9f3a7f1cb8aad9e916299ac8cf0f3c69d323910160405180910390a35050505050505050565b6110d7611a6c565b6110ef60008051602061203583398151915233610e8f565b15801561112357506111217ffaa623ff490ff9197b5ad863cd7f72b33e2e131994d09bd7260d7cd680faad7933610e8f565b155b1561114157604051631a27eac360e11b815260040160405180910390fd5b3360009081526003602090815260408083208054825181850281018501909352808352919290919083018282801561119857602002820191906000526020600020905b815481526020019060010190808311611184575b5050505050905080516000036111c157604051635d70aba760e11b815260040160405180910390fd5b6000805b825181101561128f57336000908152600260205260408120845182908690859081106111f3576111f3611fb3565b6020908102919091018101518252818101929092526040908101600090812033825260078452828220815483529093522060028101549192509060ff1615806112415750600482015460ff16155b1561124d575050611287565b8054600182015460009161126091611fdf565b90508042101561127257505050611287565b60028301546112819086611fdf565b94505050505b6001016111c5565b50336000908152600660205260409020548111156112c057604051631e9acf1760e31b815260040160405180910390fd5b60005b8251811015610c0c57336000908152600260205260408120845182908690859081106112f1576112f1611fb3565b6020908102919091018101518252818101929092526040908101600090812033825260078452828220815483529093522060028101549192509060ff16158061133f5750600482015460ff16155b1561134b575050611421565b8054600182015460009161135e91611fdf565b90508042101561137057505050611421565b4260018301556002830154336000908152600660205260408120805490919061139a908490611ff2565b9091555050600183015460028401546113e1916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811692911690611a9c565b8254600284015460405190815233907fe6784566ea810f71a9cd63f82925ef440935e26c9e28e52a099a00ff7af3bec89060200160405180910390a35050505b6001016112c3565b611431611a6c565b60008051602061203583398151915261144981611a92565b8160000361146a57604051631f2a200560e01b815260040160405180910390fd5b604051636eb1769f60e11b81523360048201523060248201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063dd62ed3e90604401602060405180830381865afa1580156114d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114fb9190611f9a565b90508281101561151e576040516313be252b60e01b815260040160405180910390fd5b6115536001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016333086611c85565b3360009081526006602052604081208054859290611572908490611fdf565b909155505060405183815233907f8fe10ae416f22f5e5220b0018a6c1d4ff534d6aa3a471f2a20cb7747fe63e5b9906020016109a2565b6001600160a01b03811660009081526003602090815260409182902080548351818402810184019094528084526060939283018282801561160957602002820191906000526020600020905b8154815260200190600101908083116115f5575b50505050509050919050565b61161d611a6c565b60008051602061203583398151915261163581611a92565b33600090815260026020908152604080832086845290915290206001015483906001600160a01b031661167b57604051635d70aba760e11b815260040160405180910390fd5b604080516060810182528481524260208083019182526001838501818152336000818152600785528781208c8252855287902095518655935191850191909155516002909301805460ff19169315159390931790925591518581528692917fc904c76b999e0c1747ab28b6bd1d0f5f595e88a5a155b943e62a597a605db908910160405180910390a350505050565b60008281526020819052604090206001015461172581611a92565b610bd38383611b8d565b6000611739611a6c565b60008051602061203583398151915261175181611a92565b6001600160a01b0384166117785760405163e6c4247b60e01b815260040160405180910390fd5b8260000361179957604051631f2a200560e01b815260040160405180910390fd5b60005b33600090815260036020526040902054811015611839573360009081526002602090815260408083206003909252822080546001600160a01b038916939190859081106117eb576117eb611fb3565b600091825260208083209091015483528201929092526040019020600101546001600160a01b0316036118315760405163117059fd60e21b815260040160405180910390fd5b60010161179c565b50336000908152600560205260408120805482906118569061201b565b91829055503360008181526002602081815260408084208685528252808420868155600180820180546001600160a01b0319166001600160a01b038f169081179091559482018c90554260038084019190915560048301805460ff1916831790558787528452828620805491820181558652948390209094018690555191825293945090928492917f660a2dcf876162ac968b6610cd2548fdb0e619e8296ef931e2ca1392e7fe1a4a910160405180910390a350949350505050565b6001600160a01b038216600090815260076020908152604080832084845282528083208151606081018352815481526001820154938101939093526002015460ff16151590820181905261196a576000915050610733565b8051602082015161197b9190611fdf565b949350505050565b61198b611a6c565b6000805160206120358339815191526119a381611a92565b33600090815260026020908152604080832085845290915290206001015482906001600160a01b03166119e957604051635d70aba760e11b815260040160405180910390fd5b336000818152600260209081526040808320878452909152808220600401805460ff19169055518592917ffab1e92dfdb794e4d1cafcbe46c62e8043dae2bb9d7b99d1ecf78ee5e03755aa91a3505050565b60036020528160005260406000208181548110611a5757600080fd5b90600052602060002001600091509150505481565b60015460ff1615611a905760405163d93c066560e01b815260040160405180910390fd5b565b610c438133611cbe565b6040516001600160a01b03838116602483015260448201839052610c0c91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050611cfb565b6000611b078383610e8f565b611b85576000838152602081815260408083206001600160a01b03861684529091529020805460ff19166001179055611b3d3390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610733565b506000610733565b6000611b998383610e8f565b15611b85576000838152602081815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610733565b611c00611d6c565b6001805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b611c52611a6c565b6001805460ff1916811790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833611c2d565b6040516001600160a01b038481166024830152838116604483015260648201839052610bd39186918216906323b872dd90608401611ac9565b611cc88282610e8f565b611cf75760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401610912565b5050565b600080602060008451602086016000885af180611d1e576040513d6000823e3d81fd5b50506000513d91508115611d36578060011415611d43565b6001600160a01b0384163b155b15610bd357604051635274afe760e01b81526001600160a01b0385166004820152602401610912565b60015460ff16611a9057604051638dfc202b60e01b815260040160405180910390fd5b600060208284031215611da157600080fd5b81356001600160e01b031981168114611db957600080fd5b9392505050565b80356001600160a01b0381168114611dd757600080fd5b919050565b600060208284031215611dee57600080fd5b611db982611dc0565b600060208284031215611e0957600080fd5b5035919050565b60008060408385031215611e2357600080fd5b50508035926020909101359150565b60008060408385031215611e4557600080fd5b82359150611e5560208401611dc0565b90509250929050565b600080600060608486031215611e7357600080fd5b611e7c84611dc0565b95602085013595506040909401359392505050565b60008060408385031215611ea457600080fd5b611ead83611dc0565b946020939093013593505050565b602080825282518282018190526000918401906040840190835b81811015611f2b578351805184526020808201516001600160a01b03168186015260408083015190860152606080830151908601526080918201511515918501919091529093019260a090920191600101611ed5565b509095945050505050565b600080600060608486031215611f4b57600080fd5b505081359360208301359350604090920135919050565b602080825282518282018190526000918401906040840190835b81811015611f2b578351835260209384019390920191600101611f7c565b600060208284031215611fac57600080fd5b5051919050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b8082018082111561073357610733611fc9565b8181038181111561073357610733611fc9565b634e487b7160e01b600052604160045260246000fd5b60006001820161202d5761202d611fc9565b506001019056fefc7c36207174f786648d2e624616a4b40e15fd7f0268b4e36af5057f0c43c835a2646970667358221220d4accc1ebddba917b0eeec3a9201f97b8ea440800b22de6e27fb60ff6a72e3a864736f6c634300081c0033334e4ff29b2b33ea01e68e72a933a3c413941d8539341b73a9bd86a2ae4d12f6a264697066735822122076edf83eb16d7aa73f7eba2a7135799f637fc1f803d559245f0e20b52a3f535c64736f6c634300081c00330000000000000000000000003b0d3d4766c708d51a84ba33b926823d92a08026
Deployed Bytecode
0x608060405234801561001057600080fd5b506004361061018e5760003560e01c8063754e884e116100de578063d547741f11610097578063ed332bf011610071578063ed332bf014610408578063f26d55f71461044d578063f57cc1d314610460578063fd9139a31461047357600080fd5b8063d547741f146103da578063e751b5b4146103ed578063ec255746146103f557600080fd5b8063754e884e146103395780638ca098a31461035c57806391d148541461038c578063a217fddf1461039f578063c9ac5d1f146103a7578063cd3dc1b8146103c757600080fd5b806315e1bbc41161014b5780632e9db8d7116101255780632e9db8d7146102d45780632f2ff15d146102e757806336568abe146102fa57806371a4a62e1461030d57600080fd5b806315e1bbc41461027b578063248a9ca31461029c57806328a854e6146102bf57600080fd5b806301ffc9a714610193578063040f7f94146101bb57806308d0eb34146101ce5780630be435bd146101e357806311eac8551461022757806314c671d614610266575b600080fd5b6101a66101a136600461165e565b610486565b60405190151581526020015b60405180910390f35b6101a66101c936600461169d565b6104bd565b6101e16101dc36600461169d565b6105c7565b005b6102126101f136600461169d565b60016020819052600091825260409091209081015460029091015460ff1682565b604080519283529015156020830152016101b2565b61024e7f0000000000000000000000003b0d3d4766c708d51a84ba33b926823d92a0802681565b6040516001600160a01b0390911681526020016101b2565b61026e6106d0565b6040516101b291906116ba565b61028e61028936600461169d565b610732565b6040519081526020016101b2565b61028e6102aa366004611706565b60009081526020819052604090206001015490565b61028e600080516020613bf183398151915281565b61028e6102e236600461169d565b610836565b6101e16102f536600461171f565b6108fd565b6101e161030836600461171f565b610928565b6101a661031b36600461169d565b6001600160a01b031660009081526002602052604090205460ff1690565b6101a661034736600461169d565b60026020526000908152604090205460ff1681565b61036f61036a36600461174f565b610960565b6040805193845260208401929092521515908201526060016101b2565b6101a661039a36600461171f565b610a7a565b61028e600081565b6103ba6103b536600461169d565b610aa3565b6040516101b2919061177b565b61024e6103d536600461169d565b610baa565b6101e16103e836600461171f565b610c4f565b61024e610c74565b6101e161040336600461169d565b610f6f565b61041b61041636600461174f565b61103e565b604080519586526001600160a01b0390941660208601529284019190915260608301521515608082015260a0016101b2565b6101e161045b3660046117b3565b611160565b61028e61046e36600461174f565b611308565b6101e161048136600461169d565b61141a565b60006001600160e01b03198216637965db0b60e01b14806104b757506301ffc9a760e01b6001600160e01b03198316145b92915050565b60006001600160a01b0382166104e65760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b038216600090815260016020526040812054900361051e57604051633bf7330b60e21b815260040160405180910390fd5b6001600160a01b038216600090815260016020526040812080548290610546576105466117e1565b600091825260209091200154604051630103dfe560e21b81526001600160a01b0385811660048301529091169150819063040f7f9490602401602060405180830381865afa15801561059c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105c0919061180c565b9392505050565b600080516020613bf18339815191526105df81611506565b6001600160a01b0382166106065760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b03821660009081526002602052604090205460ff1661063f5760405163b3b752eb60e01b815260040160405180910390fd5b6040516001600160a01b038316907f8386b23e7b5e56defef4896578831a4c8858978cfc3daa72ca9930769e021cb190600090a26000829050806001600160a01b0316638456cb596040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156106b357600080fd5b505af11580156106c7573d6000803e3d6000fd5b50505050505050565b6060600380548060200260200160405190810160405280929190818152602001828054801561072857602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161070a575b5050505050905090565b60006001600160a01b03821661075b5760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b038216600090815260016020526040812054900361079357604051633bf7330b60e21b815260040160405180910390fd5b6001600160a01b0382166000908152600160205260408120805482906107bb576107bb6117e1565b6000918252602090912001546040516305786ef160e21b81526001600160a01b038581166004830152909116915081906315e1bbc4906024015b602060405180830381865afa158015610812573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105c09190611827565b60006001600160a01b03821661085f5760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b038216600090815260016020526040812054900361089757604051633bf7330b60e21b815260040160405180910390fd5b6001600160a01b0382166000908152600160205260408120805482906108bf576108bf6117e1565b600091825260209091200154604051632e9db8d760e01b81526001600160a01b03858116600483015290911691508190632e9db8d7906024016107f5565b60008281526020819052604090206001015461091881611506565b6109228383611513565b50505050565b6001600160a01b03811633146109515760405163334bd91960e11b815260040160405180910390fd5b61095b82826115a5565b505050565b600080806001600160a01b03851661098b5760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b03851660009081526001602052604081205490036109c357604051633bf7330b60e21b815260040160405180910390fd5b6001600160a01b0385166000908152600160205260408120805482906109eb576109eb6117e1565b600091825260209091200154604051638ca098a360e01b81526001600160a01b0388811660048301526024820188905290911691508190638ca098a390604401606060405180830381865afa158015610a48573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a6c9190611840565b935093509350509250925092565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b60606001600160a01b038216610acc5760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b0382166000908152600160205260408120549003610b0457604051633bf7330b60e21b815260040160405180910390fd5b6001600160a01b038216600090815260016020526040812080548290610b2c57610b2c6117e1565b60009182526020909120015460405163c9ac5d1f60e01b81526001600160a01b0385811660048301529091169150819063c9ac5d1f90602401600060405180830381865afa158015610b82573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526105c0919081019061188c565b60006001600160a01b038216610bd35760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b0382166000908152600160205260408120549003610c0b57604051633bf7330b60e21b815260040160405180910390fd5b6001600160a01b03821660009081526001602052604081208054909190610c3457610c346117e1565b6000918252602090912001546001600160a01b031692915050565b600082815260208190526040902060010154610c6a81611506565b61092283836115a5565b3360009081526001602052604081206002015460ff16610ca75760405163cf11a12160e01b815260040160405180910390fd5b3360009081526001602081905260409091200154600311610cdb57604051637c14665360e01b815260040160405180910390fd5b60007f0000000000000000000000003b0d3d4766c708d51a84ba33b926823d92a08026604051610d0a90611651565b6001600160a01b039091168152602001604051809103906000f080158015610d36573d6000803e3d6000fd5b5033600081815260016020818152604083208054808401825581855291842090910180546001600160a01b0319166001600160a01b038716179055928252919091018054929350839291610d8983611959565b90915550506001600160a01b038116600081815260026020526040808220805460ff191660011790555133917fe6b90854f2f854b3d6bd3a78791efaf8f5c4b16e72adee672b2495d5171ea97f91a3816001600160a01b0316632f2ff15d836001600160a01b03166375b238fc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e499190611827565b6040516001600160e01b031960e084901b1681526004810191909152306024820152604401600060405180830381600087803b158015610e8857600080fd5b505af1158015610e9c573d6000803e3d6000fd5b50505050816001600160a01b0316632f2ff15d836001600160a01b0316632426bb386040518163ffffffff1660e01b8152600401602060405180830381865afa158015610eed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f119190611827565b6040516001600160e01b031960e084901b1681526004810191909152336024820152604401600060405180830381600087803b158015610f5057600080fd5b505af1158015610f64573d6000803e3d6000fd5b509295945050505050565b600080516020613bf1833981519152610f8781611506565b6001600160a01b038216610fae5760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b0382166000818152600160208190526040808320600201805460ff19168317905560038054928301815583527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b90910180546001600160a01b03191684179055517f0d884eb0b7cbf3f44571b21ba60ea7df041f15b92935f82ff78c00e1ea376c659190a25050565b6000808080806001600160a01b03871661106b5760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b03871660009081526001602052604081205490036110a357604051633bf7330b60e21b815260040160405180910390fd5b6001600160a01b0387166000908152600160205260408120805482906110cb576110cb6117e1565b600091825260209091200154604051630ed332bf60e41b81526001600160a01b038a81166004830152602482018a90529091169150819063ed332bf09060440160a060405180830381865afa158015611128573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114c9190611980565b939c929b5090995097509095509350505050565b600080516020613bf183398151915261117881611506565b6001600160a01b038316158061119557506001600160a01b038216155b156111b35760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b03831660009081526002602052604090205460ff166111ec5760405163b3b752eb60e01b815260040160405180910390fd5b816001600160a01b0316836001600160a01b03167feabd3b45e0004ece10559102bfc26fdbd57a4201accc3bc1de60627362f5489d60405160405180910390a36000839050806001600160a01b0316632f2ff15d826001600160a01b03166325b8e7eb6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561127e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112a29190611827565b6040516001600160e01b031960e084901b16815260048101919091526001600160a01b0386166024820152604401600060405180830381600087803b1580156112ea57600080fd5b505af11580156112fe573d6000803e3d6000fd5b5050505050505050565b60006001600160a01b0383166113315760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b038316600090815260016020526040812054900361136957604051633bf7330b60e21b815260040160405180910390fd5b6001600160a01b038316600090815260016020526040812080548290611391576113916117e1565b60009182526020909120015460405163f57cc1d360e01b81526001600160a01b038681166004830152602482018690529091169150819063f57cc1d390604401602060405180830381865afa1580156113ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114129190611827565b949350505050565b600080516020613bf183398151915261143281611506565b6001600160a01b0382166114595760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b03821660009081526002602052604090205460ff166114925760405163b3b752eb60e01b815260040160405180910390fd5b6040516001600160a01b038316907fad335d75fc1702677f623fbb30a0148eea2fda954bfcaf97a05353d72b641f1a90600090a26000829050806001600160a01b0316633f4ba83a6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156106b357600080fd5b6115108133611610565b50565b600061151f8383610a7a565b61159d576000838152602081815260408083206001600160a01b03861684529091529020805460ff191660011790556115553390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45060016104b7565b5060006104b7565b60006115b18383610a7a565b1561159d576000838152602081815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45060016104b7565b61161a8282610a7a565b61164d5760405163e2517d3f60e01b81526001600160a01b03821660048201526024810183905260440160405180910390fd5b5050565b61221c806119d583390190565b60006020828403121561167057600080fd5b81356001600160e01b0319811681146105c057600080fd5b6001600160a01b038116811461151057600080fd5b6000602082840312156116af57600080fd5b81356105c081611688565b602080825282518282018190526000918401906040840190835b818110156116fb5783516001600160a01b03168352602093840193909201916001016116d4565b509095945050505050565b60006020828403121561171857600080fd5b5035919050565b6000806040838503121561173257600080fd5b82359150602083013561174481611688565b809150509250929050565b6000806040838503121561176257600080fd5b823561176d81611688565b946020939093013593505050565b602080825282518282018190526000918401906040840190835b818110156116fb578351835260209384019390920191600101611795565b600080604083850312156117c657600080fd5b82356117d181611688565b9150602083013561174481611688565b634e487b7160e01b600052603260045260246000fd5b8051801515811461180757600080fd5b919050565b60006020828403121561181e57600080fd5b6105c0826117f7565b60006020828403121561183957600080fd5b5051919050565b60008060006060848603121561185557600080fd5b83516020850151909350915061186d604085016117f7565b90509250925092565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561189e57600080fd5b815167ffffffffffffffff8111156118b557600080fd5b8201601f810184136118c657600080fd5b805167ffffffffffffffff8111156118e0576118e0611876565b8060051b604051601f19603f830116810181811067ffffffffffffffff8211171561190d5761190d611876565b60405291825260208184018101929081018784111561192b57600080fd5b6020850194505b8385101561194e57845180825260209586019590935001611932565b509695505050505050565b60006001820161197957634e487b7160e01b600052601160045260246000fd5b5060010190565b600080600080600060a0868803121561199857600080fd5b855160208701519095506119ab81611688565b60408701516060880151919550935091506119c8608087016117f7565b9050929550929590935056fe60a060405234801561001057600080fd5b5060405161221c38038061221c83398101604081905261002f9161012b565b6001805460ff191690556001600160a01b0381166100605760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b03811660805261007860003361007f565b505061015b565b6000828152602081815260408083206001600160a01b038516845290915281205460ff16610121576000838152602081815260408083206001600160a01b03861684529091529020805460ff191660011790556100d93390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610125565b5060005b92915050565b60006020828403121561013d57600080fd5b81516001600160a01b038116811461015457600080fd5b9392505050565b60805161208a610192600039600081816102610152818161094d015281816113b701528181611488015261152b015261208a6000f3fe608060405234801561001057600080fd5b506004361061021c5760003560e01c8063769f6df211610125578063be999705116100ad578063e7fd9a131161007c578063e7fd9a1314610637578063ed332bf01461064a578063f57cc1d3146106c9578063f91575f0146106dc578063fa85b26a146106ef57600080fd5b8063be999705146105de578063c9ac5d1f146105f1578063d1299e3a14610611578063d547741f1461062457600080fd5b80638ca098a3116100f45780638ca098a31461054657806391d14854146105a8578063a217fddf146105bb578063ad76d766146105c3578063bb97eab7146105d657600080fd5b8063769f6df2146104a45780637ca987fb146104c45780638456cb591461051e57806385e06e081461052657600080fd5b80632e9db8d7116101a85780633f4ba83a116101775780633f4ba83a146103b2578063509f2868146103ba5780635c975abb146103ef5780636f4e5f3f146103fa57806375b238fc1461047d57600080fd5b80632e9db8d7146103435780632f2909f31461036c5780632f2ff15d1461038c57806336568abe1461039f57600080fd5b806315e1bbc4116101ef57806315e1bbc4146102b0578063233d98d3146102d15780632426bb38146102e4578063248a9ca3146102f957806325b8e7eb1461031c57600080fd5b806301ffc9a714610221578063040f7f941461024957806311eac8551461025c578063155dd5ee1461029b575b600080fd5b61023461022f366004611d8f565b610702565b60405190151581526020015b60405180910390f35b610234610257366004611ddc565b610739565b6102837f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610240565b6102ae6102a9366004611df7565b6107cb565b005b6102c36102be366004611ddc565b6109af565b604051908152602001610240565b6102ae6102df366004611e10565b610ad9565b6102c360008051602061203583398151915281565b6102c3610307366004611df7565b60009081526020819052604090206001015490565b6102c37ffaa623ff490ff9197b5ad863cd7f72b33e2e131994d09bd7260d7cd680faad7981565b6102c3610351366004611ddc565b6001600160a01b031660009081526006602052604090205490565b6102c361037a366004611ddc565b60066020526000908152604090205481565b6102ae61039a366004611e32565b610bae565b6102ae6103ad366004611e32565b610bd9565b6102ae610c11565b6103cd6103c8366004611e5e565b610c46565b6040805194855260208501939093529183015215156060820152608001610240565b60015460ff16610234565b61044b610408366004611e91565b600260208181526000938452604080852090915291835291208054600182015492820154600383015460049093015491936001600160a01b031692909160ff1685565b604080519586526001600160a01b0390941660208601529284019190915260608301521515608082015260a001610240565b6102c37fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177581565b6104b76104b2366004611ddc565b610c9e565b6040516102409190611ebb565b6105016104d2366004611e91565b600760209081526000928352604080842090915290825290208054600182015460029092015490919060ff1683565b604080519384526020840192909252151590820152606001610240565b6102ae610e5d565b6102c3610534366004611ddc565b60056020526000908152604090205481565b610501610554366004611e91565b6001600160a01b038216600090815260076020908152604080832084845282529182902082516060810184528154808252600183015493820184905260029092015460ff1615159301839052919250925092565b6102346105b6366004611e32565b610e8f565b6102c3600081565b6102ae6105d1366004611f36565b610eb8565b6102ae6110cf565b6102ae6105ec366004611df7565b611429565b6106046105ff366004611ddc565b6115a9565b6040516102409190611f62565b6102ae61061f366004611e10565b611615565b6102ae610632366004611e32565b61170a565b6102c3610645366004611e91565b61172f565b61044b610658366004611e91565b6001600160a01b03918216600090815260026020818152604080842094845293815291839020835160a081018552815480825260018301549096169381018490529181015493820184905260038101546060830181905260049091015460ff16151560809092018290529394919391565b6102c36106d7366004611e91565b611912565b6102ae6106ea366004611df7565b611983565b6102c36106fd366004611e91565b611a3b565b60006001600160e01b03198216637965db0b60e01b148061073357506301ffc9a760e01b6001600160e01b03198316145b92915050565b6040516305786ef160e21b81526001600160a01b0382166004820152600090819030906315e1bbc490602401602060405180830381865afa158015610782573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a69190611f9a565b6001600160a01b03909316600090815260066020526040902054929092101592915050565b6107d3611a6c565b6000805160206120358339815191526107eb81611a92565b8160000361080c57604051631f2a200560e01b815260040160405180910390fd5b3360009081526006602052604090205482111561083c57604051631e9acf1760e31b815260040160405180910390fd5b6000805b336000908152600360205260409020548110156108d85733600090815260036020526040812080548390811061087857610878611fb3565b6000918252602080832090910154338352600782526040808420828552909252912060028101549192509060ff16156108ce57805460018201546000916108be91611fdf565b9050848111156108cc578094505b505b5050600101610840565b506000811180156108f557506108f16203f48082611ff2565b4210155b1561091b5760405163213bea0b60e11b8152600481018290526024015b60405180910390fd5b336000908152600660205260408120805485929061093a908490611ff2565b9091555061097490506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163385611a9c565b60405183815233907feaff4b37086828766ad3268786972c0cd24259d4c87a80f9d3963a3c3d999b0d906020015b60405180910390a2505050565b6001600160a01b038116600090815260036020908152604080832080548251818502810185019093528083528493830182828015610a0c57602002820191906000526020600020905b8154815260200190600101908083116109f8575b5050505050905060005b8151811015610ad2576001600160a01b038416600090815260026020526040812083518290859085908110610a4d57610a4d611fb3565b6020908102919091018101518252818101929092526040908101600020815160a0810183528154815260018201546001600160a01b031693810193909352600281015491830191909152600381015460608301526004015460ff161580156080830152909150610ac9576040810151610ac69085611fdf565b93505b50600101610a16565b5050919050565b610ae1611a6c565b600080516020612035833981519152610af981611a92565b33600090815260026020908152604080832086845290915290206001015483906001600160a01b0316610b3f57604051635d70aba760e11b815260040160405180910390fd5b82600003610b6057604051631f2a200560e01b815260040160405180910390fd5b336000818152600260208181526040808420898552909152808320909101869055518692917f1ee4ba310539bf61900eae83f6f369673ce6a17c2acae70119382e9ad727311091a350505050565b600082815260208190526040902060010154610bc981611a92565b610bd38383611afb565b50505050565b6001600160a01b0381163314610c025760405163334bd91960e11b815260040160405180910390fd5b610c0c8282611b8d565b505050565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775610c3b81611a92565b610c43611bf8565b50565b60046020528260005260406000206020528160005260406000208181548110610c6e57600080fd5b60009182526020909120600490910201805460018201546002830154600390930154919550935090915060ff1684565b6001600160a01b0381166000908152600360209081526040808320805482518185028101850190935280835260609493830182828015610cfd57602002820191906000526020600020905b815481526020019060010190808311610ce9575b505050505090506000815167ffffffffffffffff811115610d2057610d20612005565b604051908082528060200260200182016040528015610d8e57816020015b610d7b6040518060a001604052806000815260200160006001600160a01b0316815260200160008152602001600081526020016000151581525090565b815260200190600190039081610d3e5790505b50905060005b8251811015610e55576001600160a01b03851660009081526002602052604081208451909190859084908110610dcc57610dcc611fb3565b6020908102919091018101518252818101929092526040908101600020815160a0810183528154815260018201546001600160a01b031693810193909352600281015491830191909152600381015460608301526004015460ff16151560808201528251839083908110610e4257610e42611fb3565b6020908102919091010152600101610d94565b509392505050565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775610e8781611a92565b610c43611c4a565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b610ec0611a6c565b600080516020612035833981519152610ed881611a92565b33600090815260026020908152604080832087845290915290206001015484906001600160a01b0316610f1e57604051635d70aba760e11b815260040160405180910390fd5b3360009081526002602090815260408083208884529091529020600481015460ff16610f5d57604051635d70aba760e11b815260040160405180910390fd5b838510610f7d57604051634fe2f5b960e01b815260040160405180910390fd5b3360009081526004602090815260408083208984529091528120905b815481101561101c57818181548110610fb457610fb4611fb3565b90600052602060002090600402016002015487108015610ff65750818181548110610fe157610fe1611fb3565b90600052602060002090600402016001015486115b1561101457604051634fe2f5b960e01b815260040160405180910390fd5b600101610f99565b5080546040805160808101825282815260208082018a81528284018a8152600060608086018281526001808a018b558a845292869020965160048a029097019687559351918601919091559051600285015590516003909301805460ff19169315159390931790925582518481529081018a9052918201889052899133917fd153b8ef43cb85b04d95d02215fc9f3a7f1cb8aad9e916299ac8cf0f3c69d323910160405180910390a35050505050505050565b6110d7611a6c565b6110ef60008051602061203583398151915233610e8f565b15801561112357506111217ffaa623ff490ff9197b5ad863cd7f72b33e2e131994d09bd7260d7cd680faad7933610e8f565b155b1561114157604051631a27eac360e11b815260040160405180910390fd5b3360009081526003602090815260408083208054825181850281018501909352808352919290919083018282801561119857602002820191906000526020600020905b815481526020019060010190808311611184575b5050505050905080516000036111c157604051635d70aba760e11b815260040160405180910390fd5b6000805b825181101561128f57336000908152600260205260408120845182908690859081106111f3576111f3611fb3565b6020908102919091018101518252818101929092526040908101600090812033825260078452828220815483529093522060028101549192509060ff1615806112415750600482015460ff16155b1561124d575050611287565b8054600182015460009161126091611fdf565b90508042101561127257505050611287565b60028301546112819086611fdf565b94505050505b6001016111c5565b50336000908152600660205260409020548111156112c057604051631e9acf1760e31b815260040160405180910390fd5b60005b8251811015610c0c57336000908152600260205260408120845182908690859081106112f1576112f1611fb3565b6020908102919091018101518252818101929092526040908101600090812033825260078452828220815483529093522060028101549192509060ff16158061133f5750600482015460ff16155b1561134b575050611421565b8054600182015460009161135e91611fdf565b90508042101561137057505050611421565b4260018301556002830154336000908152600660205260408120805490919061139a908490611ff2565b9091555050600183015460028401546113e1916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811692911690611a9c565b8254600284015460405190815233907fe6784566ea810f71a9cd63f82925ef440935e26c9e28e52a099a00ff7af3bec89060200160405180910390a35050505b6001016112c3565b611431611a6c565b60008051602061203583398151915261144981611a92565b8160000361146a57604051631f2a200560e01b815260040160405180910390fd5b604051636eb1769f60e11b81523360048201523060248201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063dd62ed3e90604401602060405180830381865afa1580156114d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114fb9190611f9a565b90508281101561151e576040516313be252b60e01b815260040160405180910390fd5b6115536001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016333086611c85565b3360009081526006602052604081208054859290611572908490611fdf565b909155505060405183815233907f8fe10ae416f22f5e5220b0018a6c1d4ff534d6aa3a471f2a20cb7747fe63e5b9906020016109a2565b6001600160a01b03811660009081526003602090815260409182902080548351818402810184019094528084526060939283018282801561160957602002820191906000526020600020905b8154815260200190600101908083116115f5575b50505050509050919050565b61161d611a6c565b60008051602061203583398151915261163581611a92565b33600090815260026020908152604080832086845290915290206001015483906001600160a01b031661167b57604051635d70aba760e11b815260040160405180910390fd5b604080516060810182528481524260208083019182526001838501818152336000818152600785528781208c8252855287902095518655935191850191909155516002909301805460ff19169315159390931790925591518581528692917fc904c76b999e0c1747ab28b6bd1d0f5f595e88a5a155b943e62a597a605db908910160405180910390a350505050565b60008281526020819052604090206001015461172581611a92565b610bd38383611b8d565b6000611739611a6c565b60008051602061203583398151915261175181611a92565b6001600160a01b0384166117785760405163e6c4247b60e01b815260040160405180910390fd5b8260000361179957604051631f2a200560e01b815260040160405180910390fd5b60005b33600090815260036020526040902054811015611839573360009081526002602090815260408083206003909252822080546001600160a01b038916939190859081106117eb576117eb611fb3565b600091825260208083209091015483528201929092526040019020600101546001600160a01b0316036118315760405163117059fd60e21b815260040160405180910390fd5b60010161179c565b50336000908152600560205260408120805482906118569061201b565b91829055503360008181526002602081815260408084208685528252808420868155600180820180546001600160a01b0319166001600160a01b038f169081179091559482018c90554260038084019190915560048301805460ff1916831790558787528452828620805491820181558652948390209094018690555191825293945090928492917f660a2dcf876162ac968b6610cd2548fdb0e619e8296ef931e2ca1392e7fe1a4a910160405180910390a350949350505050565b6001600160a01b038216600090815260076020908152604080832084845282528083208151606081018352815481526001820154938101939093526002015460ff16151590820181905261196a576000915050610733565b8051602082015161197b9190611fdf565b949350505050565b61198b611a6c565b6000805160206120358339815191526119a381611a92565b33600090815260026020908152604080832085845290915290206001015482906001600160a01b03166119e957604051635d70aba760e11b815260040160405180910390fd5b336000818152600260209081526040808320878452909152808220600401805460ff19169055518592917ffab1e92dfdb794e4d1cafcbe46c62e8043dae2bb9d7b99d1ecf78ee5e03755aa91a3505050565b60036020528160005260406000208181548110611a5757600080fd5b90600052602060002001600091509150505481565b60015460ff1615611a905760405163d93c066560e01b815260040160405180910390fd5b565b610c438133611cbe565b6040516001600160a01b03838116602483015260448201839052610c0c91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050611cfb565b6000611b078383610e8f565b611b85576000838152602081815260408083206001600160a01b03861684529091529020805460ff19166001179055611b3d3390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610733565b506000610733565b6000611b998383610e8f565b15611b85576000838152602081815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610733565b611c00611d6c565b6001805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b611c52611a6c565b6001805460ff1916811790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833611c2d565b6040516001600160a01b038481166024830152838116604483015260648201839052610bd39186918216906323b872dd90608401611ac9565b611cc88282610e8f565b611cf75760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401610912565b5050565b600080602060008451602086016000885af180611d1e576040513d6000823e3d81fd5b50506000513d91508115611d36578060011415611d43565b6001600160a01b0384163b155b15610bd357604051635274afe760e01b81526001600160a01b0385166004820152602401610912565b60015460ff16611a9057604051638dfc202b60e01b815260040160405180910390fd5b600060208284031215611da157600080fd5b81356001600160e01b031981168114611db957600080fd5b9392505050565b80356001600160a01b0381168114611dd757600080fd5b919050565b600060208284031215611dee57600080fd5b611db982611dc0565b600060208284031215611e0957600080fd5b5035919050565b60008060408385031215611e2357600080fd5b50508035926020909101359150565b60008060408385031215611e4557600080fd5b82359150611e5560208401611dc0565b90509250929050565b600080600060608486031215611e7357600080fd5b611e7c84611dc0565b95602085013595506040909401359392505050565b60008060408385031215611ea457600080fd5b611ead83611dc0565b946020939093013593505050565b602080825282518282018190526000918401906040840190835b81811015611f2b578351805184526020808201516001600160a01b03168186015260408083015190860152606080830151908601526080918201511515918501919091529093019260a090920191600101611ed5565b509095945050505050565b600080600060608486031215611f4b57600080fd5b505081359360208301359350604090920135919050565b602080825282518282018190526000918401906040840190835b81811015611f2b578351835260209384019390920191600101611f7c565b600060208284031215611fac57600080fd5b5051919050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b8082018082111561073357610733611fc9565b8181038181111561073357610733611fc9565b634e487b7160e01b600052604160045260246000fd5b60006001820161202d5761202d611fc9565b506001019056fefc7c36207174f786648d2e624616a4b40e15fd7f0268b4e36af5057f0c43c835a2646970667358221220d4accc1ebddba917b0eeec3a9201f97b8ea440800b22de6e27fb60ff6a72e3a864736f6c634300081c0033334e4ff29b2b33ea01e68e72a933a3c413941d8539341b73a9bd86a2ae4d12f6a264697066735822122076edf83eb16d7aa73f7eba2a7135799f637fc1f803d559245f0e20b52a3f535c64736f6c634300081c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000003b0d3d4766c708d51a84ba33b926823d92a08026
-----Decoded View---------------
Arg [0] : _usdcToken (address): 0x3B0d3D4766C708d51A84BA33B926823d92a08026
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000003b0d3d4766c708d51a84ba33b926823d92a08026
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.