Overview
S Balance
0 S
S Value
-More Info
Private Name Tags
ContractCreator
GENESIS at txn GENESIS_d100a01e00000000000000000000000000000001
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Source Code Verified (Genesis Bytecode Match Only)
Contract Name:
NodeDriver
Compiler Version
v0.8.27+commit.40a35a09
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.27; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {NodeDriverAuth} from "./NodeDriverAuth.sol"; import {IEVMWriter} from "../interfaces/IEVMWriter.sol"; import {INodeDriver} from "../interfaces/INodeDriver.sol"; /** * @title Node Driver Contract * @notice Ensures interaction of on-chain contracts with the Sonic client itself. * @dev Methods with onlyNode modifier are called by Sonic internal txs during epoch sealing. * @custom:security-contact [email protected] */ contract NodeDriver is OwnableUpgradeable, UUPSUpgradeable, INodeDriver { NodeDriverAuth internal backend; IEVMWriter internal evmWriter; error NotNode(); error NotBackend(); /// Callable only by NodeDriverAuth (which mediates calls from SFC and from admins) modifier onlyBackend() { if (msg.sender != address(backend)) { revert NotBackend(); } _; } event UpdateValidatorWeight(uint256 indexed validatorID, uint256 weight); event UpdateValidatorPubkey(uint256 indexed validatorID, bytes pubkey); event UpdateNetworkRules(bytes diff); event UpdateNetworkVersion(uint256 version); event AdvanceEpochs(uint256 num); /// Initialization is called only once, after the contract deployment. /// Because the contract code is written directly into genesis, constructor cannot be used. function initialize(address _backend, address _evmWriterAddress, address _owner) external initializer { __Ownable_init(_owner); __UUPSUpgradeable_init(); backend = NodeDriverAuth(_backend); evmWriter = IEVMWriter(_evmWriterAddress); } /// Override the upgrade authorization check to allow upgrades only from the owner. // solhint-disable-next-line no-empty-blocks function _authorizeUpgrade(address) internal override onlyOwner {} function setBalance(address acc, uint256 value) external onlyBackend { evmWriter.setBalance(acc, value); } function copyCode(address acc, address from) external onlyBackend { evmWriter.copyCode(acc, from); } function swapCode(address acc, address where) external onlyBackend { evmWriter.swapCode(acc, where); } function setStorage(address acc, bytes32 key, bytes32 value) external onlyBackend { evmWriter.setStorage(acc, key, value); } function incNonce(address acc, uint256 diff) external onlyBackend { evmWriter.incNonce(acc, diff); } /// Update network rules configuring the chain. /// Emitted event is being observed by Sonic client. function updateNetworkRules(bytes calldata diff) external onlyBackend { emit UpdateNetworkRules(diff); } /// Update advertised version of the network. /// Emitted event is being observed by Sonic client. function updateNetworkVersion(uint256 version) external onlyBackend { emit UpdateNetworkVersion(version); } /// Enforce sealing given number of epochs. /// Emitted event is being observed by Sonic client. function advanceEpochs(uint256 num) external onlyBackend { emit AdvanceEpochs(num); } /// Update weight of a validator. Used to propagate a stake change from SFC to the client. /// Emitted event is being observed by Sonic client. function updateValidatorWeight(uint256 validatorID, uint256 value) external onlyBackend { emit UpdateValidatorWeight(validatorID, value); } /// Update public key of a validator. Used to propagate a change from SFC to the client. /// Emitted event is being observed by Sonic client. function updateValidatorPubkey(uint256 validatorID, bytes calldata pubkey) external onlyBackend { emit UpdateValidatorPubkey(validatorID, pubkey); } /// Callable only from Sonic client itself as an internal tx. /// Used for propagating network event (validator doublesign, epoch sealing) from node to SFC. modifier onlyNode() { if (msg.sender != address(0)) { revert NotNode(); } _; } // Methods which are called only by the node /// Set an initial validator. Called only as part of network initialization/genesis file generating. function setGenesisValidator( address auth, uint256 validatorID, bytes calldata pubkey, uint256 createdTime ) external onlyNode { backend.setGenesisValidator(auth, validatorID, pubkey, createdTime); } /// Set an initial delegation. Called only as part of network initialization/genesis file generating. function setGenesisDelegation(address delegator, uint256 toValidatorID, uint256 stake) external onlyNode { backend.setGenesisDelegation(delegator, toValidatorID, stake); } /// Deactivate a validator. Called by network node when a double-sign of the given validator is registered. /// Is called before sealEpoch() call. function deactivateValidator(uint256 validatorID, uint256 status) external onlyNode { backend.deactivateValidator(validatorID, status); } /// Seal epoch. Called BEFORE epoch sealing made by the client itself. function sealEpoch( uint256[] calldata offlineTimes, uint256[] calldata offlineBlocks, uint256[] calldata uptimes, uint256[] calldata originatedTxsFee ) external onlyNode { backend.sealEpoch(offlineTimes, offlineBlocks, uptimes, originatedTxsFee); } /// Seal epoch. Called AFTER epoch sealing made by the client itself. function sealEpochValidators(uint256[] calldata nextValidatorIDs) external onlyNode { backend.sealEpochValidators(nextValidatorIDs); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) pragma solidity ^0.8.20; import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol"; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * The initial owner is set to the address provided by the deployer. This can * later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { /// @custom:storage-location erc7201:openzeppelin.storage.Ownable struct OwnableStorage { address _owner; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300; function _getOwnableStorage() private pure returns (OwnableStorage storage $) { assembly { $.slot := OwnableStorageLocation } } /** * @dev The caller account is not authorized to perform an operation. */ error OwnableUnauthorizedAccount(address account); /** * @dev The owner is not a valid owner account. (eg. `address(0)`) */ error OwnableInvalidOwner(address owner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the address provided by the deployer as the initial owner. */ function __Ownable_init(address initialOwner) internal onlyInitializing { __Ownable_init_unchained(initialOwner); } function __Ownable_init_unchained(address initialOwner) internal onlyInitializing { if (initialOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(initialOwner); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { OwnableStorage storage $ = _getOwnableStorage(); return $._owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { if (owner() != _msgSender()) { revert OwnableUnauthorizedAccount(_msgSender()); } } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { if (newOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { OwnableStorage storage $ = _getOwnableStorage(); address oldOwner = $._owner; $._owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.20; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Storage of the initializable contract. * * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions * when using with upgradeable contracts. * * @custom:storage-location erc7201:openzeppelin.storage.Initializable */ struct InitializableStorage { /** * @dev Indicates that the contract has been initialized. */ uint64 _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool _initializing; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00; /** * @dev The contract is already initialized. */ error InvalidInitialization(); /** * @dev The contract is not initializing. */ error NotInitializing(); /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint64 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in * production. * * Emits an {Initialized} event. */ modifier initializer() { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); // Cache values to avoid duplicated sloads bool isTopLevelCall = !$._initializing; uint64 initialized = $._initialized; // Allowed calls: // - initialSetup: the contract is not in the initializing state and no previous version was // initialized // - construction: the contract is initialized at version 1 (no reininitialization) and the // current contract is just being deployed bool initialSetup = initialized == 0 && isTopLevelCall; bool construction = initialized == 1 && address(this).code.length == 0; if (!initialSetup && !construction) { revert InvalidInitialization(); } $._initialized = 1; if (isTopLevelCall) { $._initializing = true; } _; if (isTopLevelCall) { $._initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint64 version) { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing || $._initialized >= version) { revert InvalidInitialization(); } $._initialized = version; $._initializing = true; _; $._initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { _checkInitializing(); _; } /** * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}. */ function _checkInitializing() internal view virtual { if (!_isInitializing()) { revert NotInitializing(); } } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing) { revert InvalidInitialization(); } if ($._initialized != type(uint64).max) { $._initialized = type(uint64).max; emit Initialized(type(uint64).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint64) { return _getInitializableStorage()._initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _getInitializableStorage()._initializing; } /** * @dev Returns a pointer to the storage namespace. */ // solhint-disable-next-line var-name-mixedcase function _getInitializableStorage() private pure returns (InitializableStorage storage $) { assembly { $.slot := INITIALIZABLE_STORAGE } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (proxy/utils/UUPSUpgradeable.sol) pragma solidity ^0.8.20; import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol"; import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; import {Initializable} from "./Initializable.sol"; /** * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy. * * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing * `UUPSUpgradeable` with a custom implementation of upgrades. * * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism. */ abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable { /// @custom:oz-upgrades-unsafe-allow state-variable-immutable address private immutable __self = address(this); /** * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)` * and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called, * while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string. * If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function * during an upgrade. */ string public constant UPGRADE_INTERFACE_VERSION = "5.0.0"; /** * @dev The call is from an unauthorized context. */ error UUPSUnauthorizedCallContext(); /** * @dev The storage `slot` is unsupported as a UUID. */ error UUPSUnsupportedProxiableUUID(bytes32 slot); /** * @dev Check that the execution is being performed through a delegatecall call and that the execution context is * a proxy contract with an implementation (as defined in ERC-1967) pointing to self. This should only be the case * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a * function through ERC-1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to * fail. */ modifier onlyProxy() { _checkProxy(); _; } /** * @dev Check that the execution is not being performed through a delegate call. This allows a function to be * callable on the implementing contract but not through proxies. */ modifier notDelegated() { _checkNotDelegated(); _; } function __UUPSUpgradeable_init() internal onlyInitializing { } function __UUPSUpgradeable_init_unchained() internal onlyInitializing { } /** * @dev Implementation of the ERC-1822 {proxiableUUID} function. This returns the storage slot used by the * implementation. It is used to validate the implementation's compatibility when performing an upgrade. * * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier. */ function proxiableUUID() external view virtual notDelegated returns (bytes32) { return ERC1967Utils.IMPLEMENTATION_SLOT; } /** * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call * encoded in `data`. * * Calls {_authorizeUpgrade}. * * Emits an {Upgraded} event. * * @custom:oz-upgrades-unsafe-allow-reachable delegatecall */ function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy { _authorizeUpgrade(newImplementation); _upgradeToAndCallUUPS(newImplementation, data); } /** * @dev Reverts if the execution is not performed via delegatecall or the execution * context is not of a proxy with an ERC-1967 compliant implementation pointing to self. * See {_onlyProxy}. */ function _checkProxy() internal view virtual { if ( address(this) == __self || // Must be called through delegatecall ERC1967Utils.getImplementation() != __self // Must be called through an active proxy ) { revert UUPSUnauthorizedCallContext(); } } /** * @dev Reverts if the execution is performed via delegatecall. * See {notDelegated}. */ function _checkNotDelegated() internal view virtual { if (address(this) != __self) { // Must not be called through delegatecall revert UUPSUnauthorizedCallContext(); } } /** * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by * {upgradeToAndCall}. * * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}. * * ```solidity * function _authorizeUpgrade(address) internal onlyOwner {} * ``` */ function _authorizeUpgrade(address newImplementation) internal virtual; /** * @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call. * * As a security check, {proxiableUUID} is invoked in the new implementation, and the return value * is expected to be the implementation slot in ERC-1967. * * Emits an {IERC1967-Upgraded} event. */ function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private { try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) { if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) { revert UUPSUnsupportedProxiableUUID(slot); } ERC1967Utils.upgradeToAndCall(newImplementation, data); } catch { // The implementation is not UUPS revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) pragma solidity ^0.8.20; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @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 ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } 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) (interfaces/draft-IERC1822.sol) pragma solidity ^0.8.20; /** * @dev ERC-1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified * proxy whose upgrades are fully controlled by the current implementation. */ interface IERC1822Proxiable { /** * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation * address. * * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this * function revert if invoked through a proxy. */ function proxiableUUID() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1967.sol) pragma solidity ^0.8.20; /** * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC. */ interface IERC1967 { /** * @dev Emitted when the implementation is upgraded. */ event Upgraded(address indexed implementation); /** * @dev Emitted when the admin account has changed. */ event AdminChanged(address previousAdmin, address newAdmin); /** * @dev Emitted when the beacon is changed. */ event BeaconUpgraded(address indexed beacon); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol) pragma solidity ^0.8.20; /** * @dev This is the interface that {BeaconProxy} expects of its beacon. */ interface IBeacon { /** * @dev Must return an address that can be used as a delegate call target. * * {UpgradeableBeacon} will check that this address is a contract. */ function implementation() external view returns (address); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (proxy/ERC1967/ERC1967Utils.sol) pragma solidity ^0.8.21; import {IBeacon} from "../beacon/IBeacon.sol"; import {IERC1967} from "../../interfaces/IERC1967.sol"; import {Address} from "../../utils/Address.sol"; import {StorageSlot} from "../../utils/StorageSlot.sol"; /** * @dev This library provides getters and event emitting update functions for * https://eips.ethereum.org/EIPS/eip-1967[ERC-1967] slots. */ library ERC1967Utils { /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1. */ // solhint-disable-next-line private-vars-leading-underscore bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @dev The `implementation` of the proxy is invalid. */ error ERC1967InvalidImplementation(address implementation); /** * @dev The `admin` of the proxy is invalid. */ error ERC1967InvalidAdmin(address admin); /** * @dev The `beacon` of the proxy is invalid. */ error ERC1967InvalidBeacon(address beacon); /** * @dev An upgrade function sees `msg.value > 0` that may be lost. */ error ERC1967NonPayable(); /** * @dev Returns the current implementation address. */ function getImplementation() internal view returns (address) { return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value; } /** * @dev Stores a new address in the ERC-1967 implementation slot. */ function _setImplementation(address newImplementation) private { if (newImplementation.code.length == 0) { revert ERC1967InvalidImplementation(newImplementation); } StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation; } /** * @dev Performs implementation upgrade with additional setup call if data is nonempty. * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected * to avoid stuck value in the contract. * * Emits an {IERC1967-Upgraded} event. */ function upgradeToAndCall(address newImplementation, bytes memory data) internal { _setImplementation(newImplementation); emit IERC1967.Upgraded(newImplementation); if (data.length > 0) { Address.functionDelegateCall(newImplementation, data); } else { _checkNonPayable(); } } /** * @dev Storage slot with the admin of the contract. * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1. */ // solhint-disable-next-line private-vars-leading-underscore bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; /** * @dev Returns the current admin. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by ERC-1967) using * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` */ function getAdmin() internal view returns (address) { return StorageSlot.getAddressSlot(ADMIN_SLOT).value; } /** * @dev Stores a new address in the ERC-1967 admin slot. */ function _setAdmin(address newAdmin) private { if (newAdmin == address(0)) { revert ERC1967InvalidAdmin(address(0)); } StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin; } /** * @dev Changes the admin of the proxy. * * Emits an {IERC1967-AdminChanged} event. */ function changeAdmin(address newAdmin) internal { emit IERC1967.AdminChanged(getAdmin(), newAdmin); _setAdmin(newAdmin); } /** * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. * This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1. */ // solhint-disable-next-line private-vars-leading-underscore bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; /** * @dev Returns the current beacon. */ function getBeacon() internal view returns (address) { return StorageSlot.getAddressSlot(BEACON_SLOT).value; } /** * @dev Stores a new beacon in the ERC-1967 beacon slot. */ function _setBeacon(address newBeacon) private { if (newBeacon.code.length == 0) { revert ERC1967InvalidBeacon(newBeacon); } StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon; address beaconImplementation = IBeacon(newBeacon).implementation(); if (beaconImplementation.code.length == 0) { revert ERC1967InvalidImplementation(beaconImplementation); } } /** * @dev Change the beacon and trigger a setup call if data is nonempty. * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected * to avoid stuck value in the contract. * * Emits an {IERC1967-BeaconUpgraded} event. * * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for * efficiency. */ function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal { _setBeacon(newBeacon); emit IERC1967.BeaconUpgraded(newBeacon); if (data.length > 0) { Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data); } else { _checkNonPayable(); } } /** * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract * if an upgrade doesn't perform an initialization call. */ function _checkNonPayable() private { if (msg.value > 0) { revert ERC1967NonPayable(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/Address.sol) pragma solidity ^0.8.20; import {Errors} from "./Errors.sol"; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert Errors.InsufficientBalance(address(this).balance, amount); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert Errors.FailedCall(); } } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {Errors.FailedCall} error. * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert Errors.InsufficientBalance(address(this).balance, value); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case * of an unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {Errors.FailedCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}. */ function _revert(bytes memory returndata) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly ("memory-safe") { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert Errors.FailedCall(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol) pragma solidity ^0.8.20; /** * @dev Collection of common custom errors used in multiple contracts * * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library. * It is recommended to avoid relying on the error API for critical functionality. * * _Available since v5.1._ */ library Errors { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error InsufficientBalance(uint256 balance, uint256 needed); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedCall(); /** * @dev The deployment failed. */ error FailedDeployment(); /** * @dev A necessary precompile is missing. */ error MissingPrecompile(address); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol) // This file was procedurally generated from scripts/generate/templates/StorageSlot.js. pragma solidity ^0.8.20; /** * @dev Library for reading and writing primitive types to specific storage slots. * * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. * This library helps with reading and writing to such slots without the need for inline assembly. * * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. * * Example usage to set ERC-1967 implementation slot: * ```solidity * contract ERC1967 { * // Define the slot. Alternatively, use the SlotDerivation library to derive the slot. * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; * * function _getImplementation() internal view returns (address) { * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; * } * * function _setImplementation(address newImplementation) internal { * require(newImplementation.code.length > 0); * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; * } * } * ``` * * TIP: Consider using this library along with {SlotDerivation}. */ library StorageSlot { struct AddressSlot { address value; } struct BooleanSlot { bool value; } struct Bytes32Slot { bytes32 value; } struct Uint256Slot { uint256 value; } struct Int256Slot { int256 value; } struct StringSlot { string value; } struct BytesSlot { bytes value; } /** * @dev Returns an `AddressSlot` with member `value` located at `slot`. */ function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `BooleanSlot` with member `value` located at `slot`. */ function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `Bytes32Slot` with member `value` located at `slot`. */ function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `Uint256Slot` with member `value` located at `slot`. */ function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `Int256Slot` with member `value` located at `slot`. */ function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `StringSlot` with member `value` located at `slot`. */ function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns an `StringSlot` representation of the string storage pointer `store`. */ function getStringSlot(string storage store) internal pure returns (StringSlot storage r) { assembly ("memory-safe") { r.slot := store.slot } } /** * @dev Returns a `BytesSlot` with member `value` located at `slot`. */ function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`. */ function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) { assembly ("memory-safe") { r.slot := store.slot } } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.27; /** * @custom:security-contact [email protected] */ library Decimal { // unit is used for decimals, e.g. 0.123456 function unit() internal pure returns (uint256) { return 1e18; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.27; /** * @title EVM Writer Interface * @notice Allows the NodeDriver contract to write into the state database. * @dev Implemented in the native code of the Sonic client. * @custom:security-contact [email protected] */ interface IEVMWriter { function setBalance(address acc, uint256 value) external; function copyCode(address acc, address from) external; function swapCode(address acc, address where) external; function setStorage(address acc, bytes32 key, bytes32 value) external; function incNonce(address acc, uint256 diff) external; }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.27; /** * @title Node Driver Contract Interface * @notice Ensures interaction of on-chain contracts with the Sonic client itself. * @dev Methods with onlyNode modifier are called by Sonic internal txs during epoch sealing. * @custom:security-contact [email protected] */ interface INodeDriver { /// Set an initial validator. Called only as part of network initialization/genesis file generating. function setGenesisValidator( address auth, uint256 validatorID, bytes calldata pubkey, uint256 createdTime ) external; /// Set an initial delegation. Called only as part of network initialization/genesis file generating. function setGenesisDelegation(address delegator, uint256 toValidatorID, uint256 stake) external; /// Deactivate a validator. Called by network node when a double-sign of the given validator is registered. /// Is called before sealEpoch() call. function deactivateValidator(uint256 validatorID, uint256 status) external; /// Seal epoch. Called BEFORE epoch sealing made by the client itself. function sealEpoch( uint256[] calldata offlineTimes, uint256[] calldata offlineBlocks, uint256[] calldata uptimes, uint256[] calldata originatedTxsFee ) external; /// Seal epoch. Called AFTER epoch sealing made by the client itself. function sealEpochValidators(uint256[] calldata nextValidatorIDs) external; }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.27; /** * @title Node Driver Executable * @notice A batch of operations to be executed with NodeDriver permissions. * @notice Contracts implementing this interface should be executed using NodeDriverAuth.execute() or mutExecute(). * @custom:security-contact [email protected] */ interface INodeDriverExecutable { function execute() external; }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.27; /** * @title Special Fee Contract for Sonic network * @notice The SFC maintains a list of validators and delegators and distributes rewards to them. * @custom:security-contact [email protected] */ interface ISFC { event CreatedValidator( uint256 indexed validatorID, address indexed auth, uint256 createdEpoch, uint256 createdTime ); event Delegated(address indexed delegator, uint256 indexed toValidatorID, uint256 amount); event Undelegated(address indexed delegator, uint256 indexed toValidatorID, uint256 indexed wrID, uint256 amount); event Withdrawn(address indexed delegator, uint256 indexed toValidatorID, uint256 indexed wrID, uint256 amount); event ClaimedRewards(address indexed delegator, uint256 indexed toValidatorID, uint256 rewards); event RestakedRewards(address indexed delegator, uint256 indexed toValidatorID, uint256 rewards); event BurntFTM(uint256 amount); event UpdatedSlashingRefundRatio(uint256 indexed validatorID, uint256 refundRatio); event RefundedSlashedLegacyDelegation(address indexed delegator, uint256 indexed validatorID, uint256 amount); event DeactivatedValidator(uint256 indexed validatorID, uint256 deactivatedEpoch, uint256 deactivatedTime); event ChangedValidatorStatus(uint256 indexed validatorID, uint256 status); event AnnouncedRedirection(address indexed from, address indexed to); function currentSealedEpoch() external view returns (uint256); function getEpochSnapshot( uint256 ) external view returns ( uint256 endTime, uint256 endBlock, uint256 epochFee, uint256 baseRewardPerSecond, uint256 totalStake, uint256 totalSupply ); function getStake(address, uint256) external view returns (uint256); function getValidator( uint256 ) external view returns ( uint256 status, uint256 receivedStake, address auth, uint256 createdEpoch, uint256 createdTime, uint256 deactivatedTime, uint256 deactivatedEpoch ); function getValidatorID(address) external view returns (uint256); function getValidatorPubkey(uint256) external view returns (bytes memory); function pubkeyAddressToValidatorID(address pubkeyAddress) external view returns (uint256); function getWithdrawalRequest( address, uint256, uint256 ) external view returns (uint256 epoch, uint256 time, uint256 amount); function isOwner() external view returns (bool); function lastValidatorID() external view returns (uint256); function minGasPrice() external view returns (uint256); function owner() external view returns (address); function renounceOwnership() external; function slashingRefundRatio(uint256) external view returns (uint256); function stashedRewardsUntilEpoch(address, uint256) external view returns (uint256); function totalActiveStake() external view returns (uint256); function totalStake() external view returns (uint256); function totalSupply() external view returns (uint256); function transferOwnership(address newOwner) external; function treasuryAddress() external view returns (address); function version() external pure returns (bytes3); function currentEpoch() external view returns (uint256); function updateConstsAddress(address v) external; function constsAddress() external view returns (address); function getEpochValidatorIDs(uint256 epoch) external view returns (uint256[] memory); function getEpochReceivedStake(uint256 epoch, uint256 validatorID) external view returns (uint256); function getEpochAccumulatedRewardPerToken(uint256 epoch, uint256 validatorID) external view returns (uint256); function getEpochAccumulatedUptime(uint256 epoch, uint256 validatorID) external view returns (uint256); function getEpochAverageUptime(uint256 epoch, uint256 validatorID) external view returns (uint32); function getEpochAccumulatedOriginatedTxsFee(uint256 epoch, uint256 validatorID) external view returns (uint256); function getEpochOfflineTime(uint256 epoch, uint256 validatorID) external view returns (uint256); function getEpochOfflineBlocks(uint256 epoch, uint256 validatorID) external view returns (uint256); function getEpochEndBlock(uint256 epoch) external view returns (uint256); function rewardsStash(address delegator, uint256 validatorID) external view returns (uint256); function createValidator(bytes calldata pubkey) external payable; function getSelfStake(uint256 validatorID) external view returns (uint256); function delegate(uint256 toValidatorID) external payable; function undelegate(uint256 toValidatorID, uint256 wrID, uint256 amount) external; function isSlashed(uint256 validatorID) external view returns (bool); function withdraw(uint256 toValidatorID, uint256 wrID) external; function deactivateValidator(uint256 validatorID, uint256 status) external; function pendingRewards(address delegator, uint256 toValidatorID) external view returns (uint256); function stashRewards(address delegator, uint256 toValidatorID) external; function claimRewards(uint256 toValidatorID) external; function restakeRewards(uint256 toValidatorID) external; function updateSlashingRefundRatio(uint256 validatorID, uint256 refundRatio) external; function updateTreasuryAddress(address v) external; function burnFTM(uint256 amount) external; function sealEpoch( uint256[] calldata offlineTime, uint256[] calldata offlineBlocks, uint256[] calldata uptimes, uint256[] calldata originatedTxsFee ) external; function sealEpochValidators(uint256[] calldata nextValidatorIDs) external; function initialize( uint256 sealedEpoch, uint256 _totalSupply, address nodeDriver, address consts, address _owner ) external; function setGenesisValidator( address auth, uint256 validatorID, bytes calldata pubkey, uint256 createdTime ) external; function setGenesisDelegation(address delegator, uint256 toValidatorID, uint256 stake) external; function updateStakeSubscriberAddress(address v) external; function stakeSubscriberAddress() external view returns (address); function setRedirectionAuthorizer(address v) external; function announceRedirection(address to) external; function initiateRedirection(address from, address to) external; function redirect(address to) external; }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.27; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {Decimal} from "../common/Decimal.sol"; /** * @custom:security-contact [email protected] */ contract ConstantsManager is OwnableUpgradeable { // Minimum amount of stake for a validator, i.e., 500000 FTM uint256 public minSelfStake; // Maximum ratio of delegations a validator can have, say, 15 times of self-stake uint256 public maxDelegatedRatio; // The commission fee in percentage a validator will get from a delegation, e.g., 15% uint256 public validatorCommission; // The percentage of fees to burn, e.g., 20% uint256 public burntFeeShare; // The percentage of fees to transfer to treasury address, e.g., 10% uint256 public treasuryFeeShare; // the number of epochs that undelegated stake is locked for uint256 public withdrawalPeriodEpochs; // the number of seconds that undelegated stake is locked for uint256 public withdrawalPeriodTime; uint256 public baseRewardPerSecond; uint256 public offlinePenaltyThresholdBlocksNum; uint256 public offlinePenaltyThresholdTime; uint256 public targetGasPowerPerSecond; uint256 public gasPriceBalancingCounterweight; // The number of epochs to calculate the average uptime ratio from, acceptable bound [10, 87600]. // Is also the minimum number of epochs necessary for deactivation of offline validators. uint32 public averageUptimeEpochWindow; // Minimum average uptime ratio in fixed-point format; acceptable bounds [0,0.9]. // Zero to disable validators deactivation by this metric. uint64 public minAverageUptime; /** * @dev Given value is too small */ error ValueTooSmall(); /** * @dev Given value is too large */ error ValueTooLarge(); constructor(address owner) initializer { __Ownable_init(owner); } function updateMinSelfStake(uint256 v) external virtual onlyOwner { if (v < 100000 * 1e18) { revert ValueTooSmall(); } if (v > 10000000 * 1e18) { revert ValueTooLarge(); } minSelfStake = v; } function updateMaxDelegatedRatio(uint256 v) external virtual onlyOwner { if (v < Decimal.unit()) { revert ValueTooSmall(); } if (v > 31 * Decimal.unit()) { revert ValueTooLarge(); } maxDelegatedRatio = v; } function updateValidatorCommission(uint256 v) external virtual onlyOwner { if (v > Decimal.unit() / 2) { revert ValueTooLarge(); } validatorCommission = v; } function updateBurntFeeShare(uint256 v) external virtual onlyOwner { if (v > Decimal.unit() / 2) { revert ValueTooLarge(); } burntFeeShare = v; } function updateTreasuryFeeShare(uint256 v) external virtual onlyOwner { if (v > Decimal.unit() / 2) { revert ValueTooLarge(); } treasuryFeeShare = v; } function updateWithdrawalPeriodEpochs(uint256 v) external virtual onlyOwner { if (v < 2) { revert ValueTooSmall(); } if (v > 100) { revert ValueTooLarge(); } withdrawalPeriodEpochs = v; } function updateWithdrawalPeriodTime(uint256 v) external virtual onlyOwner { if (v < 86400) { revert ValueTooSmall(); } if (v > 30 * 86400) { revert ValueTooLarge(); } withdrawalPeriodTime = v; } function updateBaseRewardPerSecond(uint256 v) external virtual onlyOwner { if (v < 0.5 * 1e18) { revert ValueTooSmall(); } if (v > 32 * 1e18) { revert ValueTooLarge(); } baseRewardPerSecond = v; } function updateOfflinePenaltyThresholdTime(uint256 v) external virtual onlyOwner { if (v < 86400) { revert ValueTooSmall(); } if (v > 10 * 86400) { revert ValueTooLarge(); } offlinePenaltyThresholdTime = v; } function updateOfflinePenaltyThresholdBlocksNum(uint256 v) external virtual onlyOwner { if (v < 100) { revert ValueTooSmall(); } if (v > 1000000) { revert ValueTooLarge(); } offlinePenaltyThresholdBlocksNum = v; } function updateTargetGasPowerPerSecond(uint256 v) external virtual onlyOwner { if (v < 1000000) { revert ValueTooSmall(); } if (v > 500000000) { revert ValueTooLarge(); } targetGasPowerPerSecond = v; } function updateGasPriceBalancingCounterweight(uint256 v) external virtual onlyOwner { if (v < 100) { revert ValueTooSmall(); } if (v > 10 * 86400) { revert ValueTooLarge(); } gasPriceBalancingCounterweight = v; } function updateAverageUptimeEpochWindow(uint32 v) external virtual onlyOwner { if (v < 10) { // needs to be long enough to allow permissible downtime for validators maintenance revert ValueTooSmall(); } if (v > 87600) { revert ValueTooLarge(); } averageUptimeEpochWindow = v; } function updateMinAverageUptime(uint64 v) external virtual onlyOwner { if (v > ((Decimal.unit() * 9) / 10)) { revert ValueTooLarge(); } minAverageUptime = v; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.27; import {ISFC} from "../interfaces/ISFC.sol"; import {NodeDriver} from "./NodeDriver.sol"; import {NodeDriverAuth} from "./NodeDriverAuth.sol"; import {ConstantsManager} from "./ConstantsManager.sol"; import {Decimal} from "../common/Decimal.sol"; /** * @custom:security-contact [email protected] */ contract NetworkInitializer { // Initialize NodeDriverAuth, NodeDriver and SFC in one call to allow fewer genesis transactions function initializeAll( uint256 sealedEpoch, uint256 totalSupply, address payable _sfc, address _auth, address _driver, address _evmWriter, address _owner ) external { NodeDriver(_driver).initialize(_auth, _evmWriter, _owner); NodeDriverAuth(_auth).initialize(_sfc, _driver, _owner); ConstantsManager consts = new ConstantsManager(address(this)); consts.updateMinSelfStake(500000 * 1e18); consts.updateMaxDelegatedRatio(16 * Decimal.unit()); consts.updateValidatorCommission((15 * Decimal.unit()) / 100); consts.updateBurntFeeShare((20 * Decimal.unit()) / 100); consts.updateTreasuryFeeShare((10 * Decimal.unit()) / 100); consts.updateWithdrawalPeriodEpochs(3); consts.updateWithdrawalPeriodTime(60 * 60 * 24 * 7); consts.updateBaseRewardPerSecond(2668658453701531600); consts.updateOfflinePenaltyThresholdTime(5 days); consts.updateOfflinePenaltyThresholdBlocksNum(1000); consts.updateTargetGasPowerPerSecond(2000000); consts.updateGasPriceBalancingCounterweight(3600); consts.updateAverageUptimeEpochWindow(100); consts.updateMinAverageUptime(0); // check disabled by default consts.transferOwnership(_owner); ISFC(_sfc).initialize(sealedEpoch, totalSupply, _auth, address(consts), _owner); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.27; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {ISFC} from "../interfaces/ISFC.sol"; import {NodeDriver} from "./NodeDriver.sol"; import {INodeDriverExecutable} from "../interfaces/INodeDriverExecutable.sol"; /** * @custom:security-contact [email protected] */ contract NodeDriverAuth is OwnableUpgradeable, UUPSUpgradeable { ISFC internal sfc; NodeDriver internal driver; error NotSFC(); error NotDriver(); error NotContract(); error SelfCodeHashMismatch(); error DriverCodeHashMismatch(); error RecipientNotSFC(); // Initialize NodeDriverAuth, NodeDriver and SFC in one call to allow fewer genesis transactions function initialize(address payable _sfc, address _driver, address _owner) external initializer { __Ownable_init(_owner); __UUPSUpgradeable_init(); driver = NodeDriver(_driver); sfc = ISFC(_sfc); } /// Override the upgrade authorization check to allow upgrades only from the owner. // solhint-disable-next-line no-empty-blocks function _authorizeUpgrade(address) internal override onlyOwner {} /// Callable only by SFC contract. modifier onlySFC() { if (msg.sender != address(sfc)) { revert NotSFC(); } _; } /// Callable only by NodeDriver (mediates messages from the network client) modifier onlyDriver() { if (msg.sender != address(driver)) { revert NotDriver(); } _; } function _execute(address executable, address newOwner, bytes32 selfCodeHash, bytes32 driverCodeHash) internal { _transferOwnership(executable); INodeDriverExecutable(executable).execute(); _transferOwnership(newOwner); //require(driver.backend() == address(this), "ownership of driver is lost"); if (_getCodeHash(address(this)) != selfCodeHash) { revert SelfCodeHashMismatch(); } if (_getCodeHash(address(driver)) != driverCodeHash) { revert DriverCodeHashMismatch(); } } /// Execute a batch update of network configuration. /// Run given contract with a permission of the NodeDriverAuth owner. /// Does not allow changing NodeDriver and NodeDriverAuth code. function execute(address executable) external onlyOwner { _execute(executable, owner(), _getCodeHash(address(this)), _getCodeHash(address(driver))); } /// Execute a batch update of network configuration. /// Run given contract with a permission of the NodeDriverAuth owner. /// Allows changing NodeDriver and NodeDriverAuth code. function mutExecute( address executable, address newOwner, bytes32 selfCodeHash, bytes32 driverCodeHash ) external onlyOwner { _execute(executable, newOwner, selfCodeHash, driverCodeHash); } /// Mint native token. To be used by SFC for minting validators rewards. function incBalance(address acc, uint256 diff) external onlySFC { if (acc != address(sfc)) { revert RecipientNotSFC(); } driver.setBalance(acc, address(acc).balance + diff); } /// Upgrade code of given contract by coping it from other deployed contract. /// Avoids setting code to an external address. function upgradeCode(address acc, address from) external onlyOwner { if (!isContract(acc) || !isContract(from)) { revert NotContract(); } driver.copyCode(acc, from); } /// Upgrade code of given contract by coping it from other deployed contract. /// Does not avoid setting code to an external address. (DANGEROUS!) function copyCode(address acc, address from) external onlyOwner { driver.copyCode(acc, from); } /// Increment nonce of the given account. function incNonce(address acc, uint256 diff) external onlyOwner { driver.incNonce(acc, diff); } /// Update network rules by providing a JSON patch. function updateNetworkRules(bytes calldata diff) external onlyOwner { driver.updateNetworkRules(diff); } /// Update advertised network version. function updateNetworkVersion(uint256 version) external onlyOwner { driver.updateNetworkVersion(version); } /// Enforce sealing given number of epochs. function advanceEpochs(uint256 num) external onlyOwner { driver.advanceEpochs(num); } /// Update weight of a validator. Used to propagate a stake change from SFC to the client. function updateValidatorWeight(uint256 validatorID, uint256 value) external onlySFC { driver.updateValidatorWeight(validatorID, value); } /// Update public key of a validator. Used to propagate a change from SFC to the client. function updateValidatorPubkey(uint256 validatorID, bytes calldata pubkey) external onlySFC { driver.updateValidatorPubkey(validatorID, pubkey); } /// Set an initial validator into SFC. Called only as part of network initialization/genesis file generating. function setGenesisValidator( address auth, uint256 validatorID, bytes calldata pubkey, uint256 createdTime ) external onlyDriver { sfc.setGenesisValidator(auth, validatorID, pubkey, createdTime); } /// Set an initial delegation. Called only as part of network initialization/genesis file generating. function setGenesisDelegation(address delegator, uint256 toValidatorID, uint256 stake) external onlyDriver { sfc.setGenesisDelegation(delegator, toValidatorID, stake); } /// Deactivate a validator. Called by network node when a double-sign of the given validator is registered. /// Is called before sealEpoch() call. function deactivateValidator(uint256 validatorID, uint256 status) external onlyDriver { sfc.deactivateValidator(validatorID, status); } /// Seal epoch. Called BEFORE epoch sealing made by the client itself. function sealEpoch( uint256[] calldata offlineTimes, uint256[] calldata offlineBlocks, uint256[] calldata uptimes, uint256[] calldata originatedTxsFee ) external onlyDriver { sfc.sealEpoch(offlineTimes, offlineBlocks, uptimes, originatedTxsFee); } /// Seal epoch. Called AFTER epoch sealing made by the client itself. function sealEpochValidators(uint256[] calldata nextValidatorIDs) external onlyDriver { sfc.sealEpochValidators(nextValidatorIDs); } function isContract(address account) internal view returns (bool) { uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } function _getCodeHash(address addr) internal view returns (bytes32) { bytes32 codeHash; assembly { codeHash := extcodehash(addr) } return codeHash; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.27; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {Decimal} from "../common/Decimal.sol"; import {NodeDriverAuth} from "./NodeDriverAuth.sol"; import {ConstantsManager} from "./ConstantsManager.sol"; import {Version} from "../version/Version.sol"; /** * @title Special Fee Contract for Sonic network * @notice The SFC maintains a list of validators and delegators and distributes rewards to them. * @custom:security-contact [email protected] */ contract SFC is OwnableUpgradeable, UUPSUpgradeable, Version { uint256 internal constant OK_STATUS = 0; uint256 internal constant WITHDRAWN_BIT = 1; uint256 internal constant OFFLINE_BIT = 1 << 3; uint256 internal constant OFFLINE_AVG_BIT = 1 << 4; uint256 internal constant DOUBLESIGN_BIT = 1 << 7; uint256 internal constant CHEATER_MASK = DOUBLESIGN_BIT; /** * @dev The staking for validation */ struct Validator { uint256 status; uint256 receivedStake; // from all delegators (weight of the validator) address auth; // self-stake delegator uint256 createdEpoch; uint256 createdTime; uint256 deactivatedTime; uint256 deactivatedEpoch; } NodeDriverAuth internal node; // last sealed epoch (currentEpoch - 1) uint256 public currentSealedEpoch; mapping(uint256 validatorID => Validator) public getValidator; mapping(address auth => uint256 validatorID) public getValidatorID; mapping(uint256 validatorID => bytes pubkey) public getValidatorPubkey; uint256 public lastValidatorID; // total stake of all validators - includes slashed/offline validators uint256 public totalStake; // total stake of active (OK_STATUS) validators (total weight) uint256 public totalActiveStake; // delegator => validator ID => stashed rewards (to be claimed/restaked) mapping(address delegator => mapping(uint256 validatorID => uint256 stashedRewards)) internal _rewardsStash; // delegator => validator ID => last epoch number for which were rewards stashed mapping(address delegator => mapping(uint256 validatorID => uint256 epoch)) public stashedRewardsUntilEpoch; struct WithdrawalRequest { uint256 epoch; // epoch where undelegated uint256 time; // when undelegated uint256 amount; } // delegator => validator ID => withdrawal ID => withdrawal request mapping(address delegator => mapping(uint256 validatorID => mapping(uint256 wrID => WithdrawalRequest))) public getWithdrawalRequest; // delegator => validator ID => current stake mapping(address delegator => mapping(uint256 validatorID => uint256 stake)) public getStake; // data structure to compute average uptime for each active validator struct AverageUptime { // average uptime ratio as a value between 0 and 1e18 uint64 averageUptime; // remainder from the division in the average calculation uint32 remainder; // number of epochs in the average (at most averageUptimeEpochsWindow) uint32 epochs; } struct EpochSnapshot { // validator ID => validator weight in the epoch mapping(uint256 => uint256) receivedStake; // validator ID => accumulated ( delegatorsReward * 1e18 / receivedStake ) mapping(uint256 => uint256) accumulatedRewardPerToken; // validator ID => accumulated online time mapping(uint256 => uint256) accumulatedUptime; // validator ID => average uptime as a percentage mapping(uint256 => AverageUptime) averageUptime; // validator ID => gas fees from txs originated by the validator mapping(uint256 => uint256) accumulatedOriginatedTxsFee; mapping(uint256 => uint256) offlineTime; mapping(uint256 => uint256) offlineBlocks; uint256[] validatorIDs; uint256 endTime; uint256 endBlock; uint256 epochFee; // gas fees from txs in the epoch uint256 baseRewardPerSecond; // the base reward to divide among validators for each second of the epoch uint256 totalStake; // total weight of all validators uint256 totalSupply; // total supply of native tokens } // the total supply of native tokens in the chain uint256 public totalSupply; // epoch id => epoch snapshot mapping(uint256 epoch => EpochSnapshot) public getEpochSnapshot; // validator ID -> slashing refund ratio (allows to withdraw slashed stake) mapping(uint256 validatorID => uint256 refundRatio) public slashingRefundRatio; // the treasure contract (receives unlock penalties and a part of epoch fees) address public treasuryAddress; ConstantsManager internal c; // the contract subscribed to stake changes notifications address public stakeSubscriberAddress; // address derived from the validator pubkey => validator id mapping(address pubkeyAddress => uint256 validatorID) public pubkeyAddressToValidatorID; // address authorized to initiate redirection address public redirectionAuthorizer; // delegator => withdrawals receiver mapping(address delegator => address receiver) public getRedirectionRequest; // delegator => withdrawals receiver mapping(address delegator => address receiver) public getRedirection; struct SealEpochRewardsCtx { uint256[] baseRewardWeights; uint256 totalBaseRewardWeight; uint256[] txRewardWeights; uint256 totalTxRewardWeight; uint256 epochFee; } // auth error NotDriverAuth(); error NotAuthorized(); // addresses error ZeroAddress(); error SameAddress(); // values error ZeroAmount(); error ZeroRewards(); // pubkeys error PubkeyUsedByOtherValidator(); error MalformedPubkey(); // redirections error AlreadyRedirected(); error SameRedirectionAuthorizer(); error Redirected(); // validators error ValidatorNotExists(); error ValidatorExists(); error ValidatorNotActive(); error ValidatorDelegationLimitExceeded(); error NotDeactivatedStatus(); // requests error RequestExists(); error RequestNotExists(); // transfers error TransfersNotAllowed(); error TransferFailed(); // stake changes subscriber error StakeSubscriberFailed(); // staking error InsufficientSelfStake(); error NotEnoughTimePassed(); error NotEnoughEpochsPassed(); error StakeIsFullySlashed(); // stashing error NothingToStash(); // slashing error ValidatorNotSlashed(); error RefundRatioTooHigh(); event DeactivatedValidator(uint256 indexed validatorID, uint256 deactivatedEpoch, uint256 deactivatedTime); event ChangedValidatorStatus(uint256 indexed validatorID, uint256 status); event CreatedValidator( uint256 indexed validatorID, address indexed auth, uint256 createdEpoch, uint256 createdTime ); event Delegated(address indexed delegator, uint256 indexed toValidatorID, uint256 amount); event Undelegated(address indexed delegator, uint256 indexed toValidatorID, uint256 indexed wrID, uint256 amount); event Withdrawn(address indexed delegator, uint256 indexed toValidatorID, uint256 indexed wrID, uint256 amount); event ClaimedRewards(address indexed delegator, uint256 indexed toValidatorID, uint256 rewards); event RestakedRewards(address indexed delegator, uint256 indexed toValidatorID, uint256 rewards); event BurntFTM(uint256 amount); event UpdatedSlashingRefundRatio(uint256 indexed validatorID, uint256 refundRatio); event RefundedSlashedLegacyDelegation(address indexed delegator, uint256 indexed validatorID, uint256 amount); event AnnouncedRedirection(address indexed from, address indexed to); modifier onlyDriver() { if (!isNode(msg.sender)) { revert NotDriverAuth(); } _; } /// Initialization is called only once, after the contract deployment. /// Because the contract code is written directly into genesis, constructor cannot be used. function initialize( uint256 sealedEpoch, uint256 _totalSupply, address nodeDriver, address _c, address owner ) external initializer { __Ownable_init(owner); __UUPSUpgradeable_init(); currentSealedEpoch = sealedEpoch; node = NodeDriverAuth(nodeDriver); c = ConstantsManager(_c); totalSupply = _totalSupply; getEpochSnapshot[sealedEpoch].endTime = _now(); } /// Override the upgrade authorization check to allow upgrades only from the owner. // solhint-disable-next-line no-empty-blocks function _authorizeUpgrade(address) internal override onlyOwner {} /// Receive fallback to revert transfers. receive() external payable { revert TransfersNotAllowed(); } /// Set admin address responsible for initiating redirections. function setRedirectionAuthorizer(address v) external onlyOwner { if (redirectionAuthorizer == v) { revert SameRedirectionAuthorizer(); } redirectionAuthorizer = v; } /// Announce redirection of address to be called by validator whose auth key was compromised. /// Produced events are used to notify redirect authorizer about redirection request. /// Redirect authorizer then initiates creating of appropriate redirect by calling initiateRedirection(). function announceRedirection(address to) external { emit AnnouncedRedirection(msg.sender, to); } /// Initiate redirection of withdrawals/claims for a compromised validator account. /// Needs to be accepted by validator key holder before the redirect is active. function initiateRedirection(address from, address to) external { if (msg.sender != redirectionAuthorizer) { revert NotAuthorized(); } if (getRedirection[from] == to) { revert AlreadyRedirected(); } if (from == to) { revert SameAddress(); } getRedirectionRequest[from] = to; } /// Accept redirection proposal. /// Redirection must by accepted by the validator key holder before it start to be applied. function redirect(address to) external { address from = msg.sender; if (to == address(0)) { revert ZeroAddress(); } if (getRedirectionRequest[from] != to) { revert RequestNotExists(); } getRedirection[from] = to; getRedirectionRequest[from] = address(0); } /// Seal current epoch - deactivate validators who were offline too long, create an epoch snapshot /// for the current epoch (provides information for rewards calculation), calculate new minimal gas price. /// This method is called BEFORE the epoch sealing made by the client itself. function sealEpoch( uint256[] calldata offlineTime, uint256[] calldata offlineBlocks, uint256[] calldata uptimes, uint256[] calldata originatedTxsFee ) external onlyDriver { EpochSnapshot storage snapshot = getEpochSnapshot[currentEpoch()]; uint256[] memory validatorIDs = snapshot.validatorIDs; _sealEpochOffline(snapshot, validatorIDs, offlineTime, offlineBlocks); { EpochSnapshot storage prevSnapshot = getEpochSnapshot[currentSealedEpoch]; uint256 epochDuration = 1; if (_now() > prevSnapshot.endTime) { epochDuration = _now() - prevSnapshot.endTime; } _sealEpochRewards(epochDuration, snapshot, prevSnapshot, validatorIDs, uptimes, originatedTxsFee); _sealEpochAverageUptime(epochDuration, snapshot, prevSnapshot, validatorIDs, uptimes); } currentSealedEpoch = currentEpoch(); snapshot.endTime = _now(); snapshot.endBlock = block.number; snapshot.baseRewardPerSecond = c.baseRewardPerSecond(); snapshot.totalSupply = totalSupply; } /// Finish epoch sealing - store validators of the new epoch into a snapshot. /// This method is called AFTER the epoch sealing made by the client itself. function sealEpochValidators(uint256[] calldata nextValidatorIDs) external onlyDriver { EpochSnapshot storage snapshot = getEpochSnapshot[currentEpoch()]; // fill data for the next snapshot for (uint256 i = 0; i < nextValidatorIDs.length; i++) { uint256 validatorID = nextValidatorIDs[i]; uint256 receivedStake = getValidator[validatorID].receivedStake; snapshot.receivedStake[validatorID] = receivedStake; snapshot.totalStake = snapshot.totalStake + receivedStake; } snapshot.validatorIDs = nextValidatorIDs; } /// Set an initial validator. /// Called only as part of network initialization/genesis file generating. function setGenesisValidator( address auth, uint256 validatorID, bytes calldata pubkey, uint256 createdTime ) external onlyDriver { _rawCreateValidator( auth, validatorID, pubkey, OK_STATUS, 0, // createdEpoch createdTime, 0, // deactivatedEpoch - not deactivated 0 // deactivatedTime - not deactivated ); if (validatorID > lastValidatorID) { lastValidatorID = validatorID; } } /// Set an initial delegation. /// Called only as part of network initialization/genesis file generating. function setGenesisDelegation(address delegator, uint256 toValidatorID, uint256 stake) external onlyDriver { _rawDelegate(delegator, toValidatorID, stake, false); _mintNativeToken(stake); } /// Create a validator with a given public key while using attached value as the validator's self-stake. function createValidator(bytes calldata pubkey) external payable { if (msg.value < c.minSelfStake()) { revert InsufficientSelfStake(); } if (pubkey.length != 66 || pubkey[0] != 0xc0) { revert MalformedPubkey(); } if (pubkeyAddressToValidatorID[_pubkeyToAddress(pubkey)] != 0) { revert PubkeyUsedByOtherValidator(); } _createValidator(msg.sender, pubkey); _delegate(msg.sender, lastValidatorID, msg.value); } /// Update slashing refund ratio for a validator. /// The refund ratio is used to calculate the amount of stake that can be withdrawn after slashing. function updateSlashingRefundRatio(uint256 validatorID, uint256 refundRatio) external onlyOwner { if (!isSlashed(validatorID)) { revert ValidatorNotSlashed(); } if (refundRatio > Decimal.unit()) { revert RefundRatioTooHigh(); } slashingRefundRatio[validatorID] = refundRatio; emit UpdatedSlashingRefundRatio(validatorID, refundRatio); } /// Delegate stake to a validator. function delegate(uint256 toValidatorID) external payable { _delegate(msg.sender, toValidatorID, msg.value); } /// Withdraw stake from a validator after its un-delegation. /// Un-delegated stake is locked for a certain period of time. function withdraw(uint256 toValidatorID, uint256 wrID) public { _withdraw(msg.sender, toValidatorID, wrID, _receiverOf(msg.sender)); } /// Deactivate a validator. /// Called by the chain client when a client misbehavior is observed. function deactivateValidator(uint256 validatorID, uint256 status) external onlyDriver { if (status == OK_STATUS) { revert NotDeactivatedStatus(); } _setValidatorDeactivated(validatorID, status); _syncValidator(validatorID, false); address validatorAddr = getValidator[validatorID].auth; _notifyStakeSubscriber(validatorAddr, validatorAddr, false); } /// Stash rewards for a delegator. function stashRewards(address delegator, uint256 toValidatorID) external { if (!_stashRewards(delegator, toValidatorID)) { revert NothingToStash(); } } /// burnFTM allows SFC to burn an arbitrary amount of FTM tokens. function burnFTM(uint256 amount) external onlyOwner { _burnFTM(amount); } /// Update treasury address. function updateTreasuryAddress(address v) external onlyOwner { treasuryAddress = v; } /// Update consts address. function updateConstsAddress(address v) external onlyOwner { c = ConstantsManager(v); } /// Update voteBook address. function updateStakeSubscriberAddress(address v) external onlyOwner { stakeSubscriberAddress = v; } /// Get consts address. function constsAddress() external view returns (address) { return address(c); } /// Claim rewards for stake delegated to a validator. function claimRewards(uint256 toValidatorID) public { address delegator = msg.sender; uint256 rewards = _claimRewards(delegator, toValidatorID); // It's important that we transfer after erasing (protection against Re-Entrancy) (bool sent, ) = _receiverOf(delegator).call{value: rewards}(""); if (!sent) { revert TransferFailed(); } emit ClaimedRewards(delegator, toValidatorID, rewards); } /// Get amount of currently stashed rewards. function rewardsStash(address delegator, uint256 validatorID) public view returns (uint256) { return _rewardsStash[delegator][validatorID]; } /// Un-delegate stake from a validator. function undelegate(uint256 toValidatorID, uint256 wrID, uint256 amount) public { address delegator = msg.sender; _stashRewards(delegator, toValidatorID); if (amount == 0) { revert ZeroAmount(); } if (getWithdrawalRequest[delegator][toValidatorID][wrID].amount != 0) { revert RequestExists(); } _rawUndelegate(delegator, toValidatorID, amount, true, false, true); getWithdrawalRequest[delegator][toValidatorID][wrID].amount = amount; getWithdrawalRequest[delegator][toValidatorID][wrID].epoch = currentEpoch(); getWithdrawalRequest[delegator][toValidatorID][wrID].time = _now(); _syncValidator(toValidatorID, false); emit Undelegated(delegator, toValidatorID, wrID, amount); } /// Re-stake rewards - claim rewards for staking and delegate it immediately /// to the same validator - add it to the current stake. function restakeRewards(uint256 toValidatorID) public { address delegator = msg.sender; uint256 rewards = _claimRewards(delegator, toValidatorID); _delegate(delegator, toValidatorID, rewards); emit RestakedRewards(delegator, toValidatorID, rewards); } /// Get the current epoch number. function currentEpoch() public view returns (uint256) { return currentSealedEpoch + 1; } /// Get self-stake of a validator. function getSelfStake(uint256 validatorID) public view returns (uint256) { return getStake[getValidator[validatorID].auth][validatorID]; } /// Get validator IDs for given epoch. function getEpochValidatorIDs(uint256 epoch) public view returns (uint256[] memory) { return getEpochSnapshot[epoch].validatorIDs; } /// Get received stake for a validator in a given epoch. function getEpochReceivedStake(uint256 epoch, uint256 validatorID) public view returns (uint256) { return getEpochSnapshot[epoch].receivedStake[validatorID]; } /// Get accumulated reward per token for a validator in a given epoch. function getEpochAccumulatedRewardPerToken(uint256 epoch, uint256 validatorID) public view returns (uint256) { return getEpochSnapshot[epoch].accumulatedRewardPerToken[validatorID]; } /// Get accumulated uptime for a validator in a given epoch. function getEpochAccumulatedUptime(uint256 epoch, uint256 validatorID) public view returns (uint256) { return getEpochSnapshot[epoch].accumulatedUptime[validatorID]; } /// Get average uptime for a validator in a given epoch. function getEpochAverageUptime(uint256 epoch, uint256 validatorID) public view returns (uint64) { return getEpochSnapshot[epoch].averageUptime[validatorID].averageUptime; } /// Get accumulated originated txs fee for a validator in a given epoch. function getEpochAccumulatedOriginatedTxsFee(uint256 epoch, uint256 validatorID) public view returns (uint256) { return getEpochSnapshot[epoch].accumulatedOriginatedTxsFee[validatorID]; } /// Get offline time for a validator in a given epoch. function getEpochOfflineTime(uint256 epoch, uint256 validatorID) public view returns (uint256) { return getEpochSnapshot[epoch].offlineTime[validatorID]; } /// Get offline blocks for a validator in a given epoch. function getEpochOfflineBlocks(uint256 epoch, uint256 validatorID) public view returns (uint256) { return getEpochSnapshot[epoch].offlineBlocks[validatorID]; } /// Get end block for a given epoch. function getEpochEndBlock(uint256 epoch) public view returns (uint256) { return getEpochSnapshot[epoch].endBlock; } /// Check whether the given validator is slashed - the stake (or its part) cannot /// be withdrawn because of misbehavior (double-sign) of the validator. function isSlashed(uint256 validatorID) public view returns (bool) { return getValidator[validatorID].status & CHEATER_MASK != 0; } /// Get the amount of rewards which can be currently claimed by the given delegator for the given validator. function pendingRewards(address delegator, uint256 toValidatorID) public view returns (uint256) { uint256 reward = _newRewards(delegator, toValidatorID); return _rewardsStash[delegator][toValidatorID] + reward; } /// Check whether the self-stake covers the required fraction of all delegations for the given validator. function _checkDelegatedStakeLimit(uint256 validatorID) internal view returns (bool) { return getValidator[validatorID].receivedStake <= (getSelfStake(validatorID) * c.maxDelegatedRatio()) / Decimal.unit(); } /// Check if an address is the NodeDriverAuth contract. function isNode(address addr) internal view virtual returns (bool) { return addr == address(node); } /// Delegate stake to a validator. function _delegate(address delegator, uint256 toValidatorID, uint256 amount) internal { if (!_validatorExists(toValidatorID)) { revert ValidatorNotExists(); } if (getValidator[toValidatorID].status != OK_STATUS) { revert ValidatorNotActive(); } _rawDelegate(delegator, toValidatorID, amount, true); if (!_checkDelegatedStakeLimit(toValidatorID)) { revert ValidatorDelegationLimitExceeded(); } } /// Delegate stake to a validator without checking delegation limit. function _rawDelegate(address delegator, uint256 toValidatorID, uint256 amount, bool strict) internal { if (amount == 0) { revert ZeroAmount(); } _stashRewards(delegator, toValidatorID); getStake[delegator][toValidatorID] = getStake[delegator][toValidatorID] + amount; uint256 origStake = getValidator[toValidatorID].receivedStake; getValidator[toValidatorID].receivedStake = origStake + amount; totalStake = totalStake + amount; if (getValidator[toValidatorID].status == OK_STATUS) { totalActiveStake = totalActiveStake + amount; } _syncValidator(toValidatorID, origStake == 0); emit Delegated(delegator, toValidatorID, amount); _notifyStakeSubscriber(delegator, getValidator[toValidatorID].auth, strict); } /// Un-delegate stake from a validator. function _rawUndelegate( address delegator, uint256 toValidatorID, uint256 amount, bool strict, bool forceful, bool checkDelegatedStake ) internal { getStake[delegator][toValidatorID] -= amount; getValidator[toValidatorID].receivedStake = getValidator[toValidatorID].receivedStake - amount; totalStake = totalStake - amount; if (getValidator[toValidatorID].status == OK_STATUS) { totalActiveStake = totalActiveStake - amount; } uint256 selfStakeAfterwards = getSelfStake(toValidatorID); if (selfStakeAfterwards != 0 && getValidator[toValidatorID].status == OK_STATUS) { if (!(selfStakeAfterwards >= c.minSelfStake())) { if (forceful) { revert InsufficientSelfStake(); } else { _setValidatorDeactivated(toValidatorID, WITHDRAWN_BIT); } } if (checkDelegatedStake && !_checkDelegatedStakeLimit(toValidatorID)) { revert ValidatorDelegationLimitExceeded(); } } else { _setValidatorDeactivated(toValidatorID, WITHDRAWN_BIT); } _notifyStakeSubscriber(delegator, getValidator[toValidatorID].auth, strict); } /// Get slashing penalty for a stake. function getSlashingPenalty( uint256 amount, bool isCheater, uint256 refundRatio ) internal pure returns (uint256 penalty) { if (!isCheater || refundRatio >= Decimal.unit()) { return 0; } // round penalty upwards (ceiling) to prevent dust amount attacks penalty = (amount * (Decimal.unit() - refundRatio)) / Decimal.unit() + 1; if (penalty > amount) { return amount; } return penalty; } /// Withdraw stake from a validator. /// The stake must be undelegated first. function _withdraw(address delegator, uint256 toValidatorID, uint256 wrID, address payable receiver) private { WithdrawalRequest memory request = getWithdrawalRequest[delegator][toValidatorID][wrID]; if (request.epoch == 0) { revert RequestNotExists(); } uint256 requestTime = request.time; uint256 requestEpoch = request.epoch; if ( getValidator[toValidatorID].deactivatedTime != 0 && getValidator[toValidatorID].deactivatedTime < requestTime ) { requestTime = getValidator[toValidatorID].deactivatedTime; requestEpoch = getValidator[toValidatorID].deactivatedEpoch; } if (_now() < requestTime + c.withdrawalPeriodTime()) { revert NotEnoughTimePassed(); } if (currentEpoch() < requestEpoch + c.withdrawalPeriodEpochs()) { revert NotEnoughEpochsPassed(); } uint256 amount = getWithdrawalRequest[delegator][toValidatorID][wrID].amount; bool isCheater = isSlashed(toValidatorID); uint256 penalty = getSlashingPenalty(amount, isCheater, slashingRefundRatio[toValidatorID]); delete getWithdrawalRequest[delegator][toValidatorID][wrID]; if (amount <= penalty) { revert StakeIsFullySlashed(); } // It's important that we transfer after erasing (protection against Re-Entrancy) (bool sent, ) = receiver.call{value: amount - penalty}(""); if (!sent) { revert TransferFailed(); } _burnFTM(penalty); emit Withdrawn(delegator, toValidatorID, wrID, amount); } /// Get highest epoch for which can be claimed rewards for the given validator. // If the validator is deactivated, the highest payable epoch is the deactivation epoch // or the current epoch, whichever is lower function _highestPayableEpoch(uint256 validatorID) internal view returns (uint256) { if (getValidator[validatorID].deactivatedEpoch != 0) { if (currentSealedEpoch < getValidator[validatorID].deactivatedEpoch) { return currentSealedEpoch; } return getValidator[validatorID].deactivatedEpoch; } return currentSealedEpoch; } /// Get new rewards for a delegator. /// The rewards are calculated from the last stashed epoch until the highest payable epoch. function _newRewards(address delegator, uint256 toValidatorID) internal view returns (uint256) { uint256 stashedUntil = stashedRewardsUntilEpoch[delegator][toValidatorID]; uint256 payableUntil = _highestPayableEpoch(toValidatorID); uint256 wholeStake = getStake[delegator][toValidatorID]; uint256 fullReward = _newRewardsOf(wholeStake, toValidatorID, stashedUntil, payableUntil); return fullReward; } /// Get new rewards for a delegator for a given stake amount and epoch range. function _newRewardsOf( uint256 stakeAmount, uint256 toValidatorID, uint256 fromEpoch, uint256 toEpoch ) internal view returns (uint256) { if (fromEpoch >= toEpoch) { return 0; } uint256 stashedRate = getEpochSnapshot[fromEpoch].accumulatedRewardPerToken[toValidatorID]; uint256 currentRate = getEpochSnapshot[toEpoch].accumulatedRewardPerToken[toValidatorID]; return ((currentRate - stashedRate) * stakeAmount) / Decimal.unit(); } /// Stash rewards for a delegator. function _stashRewards(address delegator, uint256 toValidatorID) internal returns (bool updated) { uint256 nonStashedReward = _newRewards(delegator, toValidatorID); stashedRewardsUntilEpoch[delegator][toValidatorID] = _highestPayableEpoch(toValidatorID); _rewardsStash[delegator][toValidatorID] += nonStashedReward; return nonStashedReward != 0; } /// Claim rewards for a delegator. function _claimRewards(address delegator, uint256 toValidatorID) internal returns (uint256) { _stashRewards(delegator, toValidatorID); uint256 rewards = _rewardsStash[delegator][toValidatorID]; if (rewards == 0) { revert ZeroRewards(); } delete _rewardsStash[delegator][toValidatorID]; // It's important that we mint after erasing (protection against Re-Entrancy) _mintNativeToken(rewards); return rewards; } /// Burn FTM tokens. /// The tokens are sent to the zero address. function _burnFTM(uint256 amount) internal { if (amount != 0) { payable(address(0)).transfer(amount); emit BurntFTM(amount); } } /// Get epoch end time. function epochEndTime(uint256 epoch) internal view returns (uint256) { return getEpochSnapshot[epoch].endTime; } /// Check if an address is redirected. function _redirected(address addr) internal view returns (bool) { return getRedirection[addr] != address(0); } /// Get address which should receive rewards and withdrawn stake for the given delegator. /// The delegator is usually the receiver, unless a redirection is created. function _receiverOf(address addr) internal view returns (address payable) { address to = getRedirection[addr]; if (to == address(0)) { return payable(address(uint160(addr))); } return payable(address(uint160(to))); } /// Seal epoch - sync validators. function _sealEpochOffline( EpochSnapshot storage snapshot, uint256[] memory validatorIDs, uint256[] memory offlineTime, uint256[] memory offlineBlocks ) internal { // mark offline nodes for (uint256 i = 0; i < validatorIDs.length; i++) { if ( offlineBlocks[i] > c.offlinePenaltyThresholdBlocksNum() && offlineTime[i] >= c.offlinePenaltyThresholdTime() ) { _setValidatorDeactivated(validatorIDs[i], OFFLINE_BIT); _syncValidator(validatorIDs[i], false); } // log data snapshot.offlineTime[validatorIDs[i]] = offlineTime[i]; snapshot.offlineBlocks[validatorIDs[i]] = offlineBlocks[i]; } } /// Seal epoch - calculate rewards. function _sealEpochRewards( uint256 epochDuration, EpochSnapshot storage snapshot, EpochSnapshot storage prevSnapshot, uint256[] memory validatorIDs, uint256[] memory uptimes, uint256[] memory accumulatedOriginatedTxsFee ) internal { SealEpochRewardsCtx memory ctx = SealEpochRewardsCtx( new uint256[](validatorIDs.length), 0, new uint256[](validatorIDs.length), 0, 0 ); for (uint256 i = 0; i < validatorIDs.length; i++) { uint256 prevAccumulatedTxsFee = prevSnapshot.accumulatedOriginatedTxsFee[validatorIDs[i]]; uint256 originatedTxsFee = 0; if (accumulatedOriginatedTxsFee[i] > prevAccumulatedTxsFee) { originatedTxsFee = accumulatedOriginatedTxsFee[i] - prevAccumulatedTxsFee; } // txRewardWeight = {originatedTxsFee} * {uptime} // originatedTxsFee is roughly proportional to {uptime} * {stake}, so the whole formula is roughly // {stake} * {uptime} ^ 2 ctx.txRewardWeights[i] = (originatedTxsFee * uptimes[i]) / epochDuration; ctx.totalTxRewardWeight = ctx.totalTxRewardWeight + ctx.txRewardWeights[i]; ctx.epochFee = ctx.epochFee + originatedTxsFee; } for (uint256 i = 0; i < validatorIDs.length; i++) { // baseRewardWeight = {stake} * {uptime ^ 2} ctx.baseRewardWeights[i] = (((snapshot.receivedStake[validatorIDs[i]] * uptimes[i]) / epochDuration) * uptimes[i]) / epochDuration; ctx.totalBaseRewardWeight = ctx.totalBaseRewardWeight + ctx.baseRewardWeights[i]; } for (uint256 i = 0; i < validatorIDs.length; i++) { uint256 rawReward = _calcRawValidatorEpochBaseReward( epochDuration, c.baseRewardPerSecond(), ctx.baseRewardWeights[i], ctx.totalBaseRewardWeight ); rawReward = rawReward + _calcRawValidatorEpochTxReward(ctx.epochFee, ctx.txRewardWeights[i], ctx.totalTxRewardWeight); uint256 validatorID = validatorIDs[i]; address validatorAddr = getValidator[validatorID].auth; // accounting validator's commission uint256 commissionRewardFull = _calcValidatorCommission(rawReward, c.validatorCommission()); uint256 selfStake = getStake[validatorAddr][validatorID]; if (selfStake != 0) { _rewardsStash[validatorAddr][validatorID] += commissionRewardFull; } // accounting reward per token for delegators uint256 delegatorsReward = rawReward - commissionRewardFull; // note: use latest stake for the sake of rewards distribution accuracy, not snapshot.receivedStake uint256 receivedStake = getValidator[validatorID].receivedStake; uint256 rewardPerToken = 0; if (receivedStake != 0) { rewardPerToken = (delegatorsReward * Decimal.unit()) / receivedStake; } snapshot.accumulatedRewardPerToken[validatorID] = prevSnapshot.accumulatedRewardPerToken[validatorID] + rewardPerToken; snapshot.accumulatedOriginatedTxsFee[validatorID] = accumulatedOriginatedTxsFee[i]; snapshot.accumulatedUptime[validatorID] = prevSnapshot.accumulatedUptime[validatorID] + uptimes[i]; } snapshot.epochFee = ctx.epochFee; if (totalSupply > snapshot.epochFee) { totalSupply -= snapshot.epochFee; } else { totalSupply = 0; } // transfer 10% of fees to treasury if (treasuryAddress != address(0)) { uint256 feeShare = (ctx.epochFee * c.treasuryFeeShare()) / Decimal.unit(); _mintNativeToken(feeShare); (bool success, ) = treasuryAddress.call{value: feeShare, gas: 1000000}(""); // solhint-disable-next-line no-empty-blocks if (!success) { // ignore treasury transfer failure // the treasury failure must not endanger the epoch sealing } } } /// Seal epoch - recalculate average uptime time of validators function _sealEpochAverageUptime( uint256 epochDuration, EpochSnapshot storage snapshot, EpochSnapshot storage prevSnapshot, uint256[] memory validatorIDs, uint256[] memory uptimes ) internal { for (uint256 i = 0; i < validatorIDs.length; i++) { uint256 validatorID = validatorIDs[i]; // compute normalised uptime as a percentage in the fixed-point format uint256 normalisedUptime = (uptimes[i] * Decimal.unit()) / epochDuration; if (normalisedUptime > Decimal.unit()) { normalisedUptime = Decimal.unit(); } AverageUptime memory previous = prevSnapshot.averageUptime[validatorID]; AverageUptime memory current = _addElementIntoAverageUptime(uint64(normalisedUptime), previous); snapshot.averageUptime[validatorID] = current; // remove validator if average uptime drops below min average uptime // (by setting minAverageUptime to zero, this check is ignored) if (current.averageUptime < c.minAverageUptime() && current.epochs >= c.averageUptimeEpochWindow()) { _setValidatorDeactivated(validatorID, OFFLINE_AVG_BIT); _syncValidator(validatorID, false); } } } function _addElementIntoAverageUptime( uint64 newValue, AverageUptime memory prev ) private view returns (AverageUptime memory) { AverageUptime memory cur; if (prev.epochs == 0) { cur.averageUptime = newValue; // the only element for the average cur.epochs = 1; return cur; } // the number of elements the average is calculated from uint128 n = prev.epochs + 1; // add new value into the average uint128 tmp = (n - 1) * uint128(prev.averageUptime) + uint128(newValue) + prev.remainder; cur.averageUptime = uint64(tmp / n); cur.remainder = uint32(tmp % n); if (cur.averageUptime > Decimal.unit()) { cur.averageUptime = uint64(Decimal.unit()); } if (prev.epochs < c.averageUptimeEpochWindow()) { cur.epochs = prev.epochs + 1; } else { cur.epochs = prev.epochs; } return cur; } /// Create a new validator. function _createValidator(address auth, bytes calldata pubkey) internal { uint256 validatorID = ++lastValidatorID; _rawCreateValidator(auth, validatorID, pubkey, OK_STATUS, currentEpoch(), _now(), 0, 0); } /// Create a new validator without incrementing lastValidatorID. function _rawCreateValidator( address auth, uint256 validatorID, bytes calldata pubkey, uint256 status, uint256 createdEpoch, uint256 createdTime, uint256 deactivatedEpoch, uint256 deactivatedTime ) internal { if (getValidatorID[auth] != 0) { revert ValidatorExists(); } getValidatorID[auth] = validatorID; getValidator[validatorID].status = status; getValidator[validatorID].createdEpoch = createdEpoch; getValidator[validatorID].createdTime = createdTime; getValidator[validatorID].deactivatedTime = deactivatedTime; getValidator[validatorID].deactivatedEpoch = deactivatedEpoch; getValidator[validatorID].auth = auth; getValidatorPubkey[validatorID] = pubkey; pubkeyAddressToValidatorID[_pubkeyToAddress(pubkey)] = validatorID; emit CreatedValidator(validatorID, auth, createdEpoch, createdTime); if (deactivatedEpoch != 0) { emit DeactivatedValidator(validatorID, deactivatedEpoch, deactivatedTime); } if (status != 0) { emit ChangedValidatorStatus(validatorID, status); } } /// Calculate raw validator epoch transaction reward. function _calcRawValidatorEpochTxReward( uint256 epochFee, uint256 txRewardWeight, uint256 totalTxRewardWeight ) internal view returns (uint256) { if (txRewardWeight == 0) { return 0; } uint256 txReward = (epochFee * txRewardWeight) / totalTxRewardWeight; // fee reward except burntFeeShare and treasuryFeeShare return (txReward * (Decimal.unit() - c.burntFeeShare() - c.treasuryFeeShare())) / Decimal.unit(); } /// Calculate raw validator epoch base reward. function _calcRawValidatorEpochBaseReward( uint256 epochDuration, uint256 _baseRewardPerSecond, uint256 baseRewardWeight, uint256 totalBaseRewardWeight ) internal pure returns (uint256) { if (baseRewardWeight == 0) { return 0; } uint256 totalReward = epochDuration * _baseRewardPerSecond; return (totalReward * baseRewardWeight) / totalBaseRewardWeight; } /// Mint native token. function _mintNativeToken(uint256 amount) internal { // balance will be increased after the transaction is processed node.incBalance(address(this), amount); totalSupply = totalSupply + amount; } /// Notify stake subscriber about staking changes. /// Used to recount votes from delegators in the governance contract. function _notifyStakeSubscriber(address delegator, address validatorAuth, bool strict) internal { if (stakeSubscriberAddress != address(0)) { // Don't allow announceStakeChange to use up all the gas // solhint-disable-next-line avoid-low-level-calls (bool success, ) = stakeSubscriberAddress.call{gas: 8000000}( abi.encodeWithSignature("announceStakeChange(address,address)", delegator, validatorAuth) ); // Don't revert if announceStakeChange failed unless strict mode enabled if (!success && strict) { revert StakeSubscriberFailed(); } } } /// Set validator deactivated status. function _setValidatorDeactivated(uint256 validatorID, uint256 status) internal { if (getValidator[validatorID].status == OK_STATUS && status != OK_STATUS) { totalActiveStake = totalActiveStake - getValidator[validatorID].receivedStake; } // status as a number is proportional to severity if (status > getValidator[validatorID].status) { getValidator[validatorID].status = status; if (getValidator[validatorID].deactivatedEpoch == 0) { getValidator[validatorID].deactivatedEpoch = currentEpoch(); getValidator[validatorID].deactivatedTime = _now(); emit DeactivatedValidator( validatorID, getValidator[validatorID].deactivatedEpoch, getValidator[validatorID].deactivatedTime ); } emit ChangedValidatorStatus(validatorID, status); } } /// Sync validator with node. function _syncValidator(uint256 validatorID, bool syncPubkey) public { if (!_validatorExists(validatorID)) { revert ValidatorNotExists(); } // emit special log for node uint256 weight = getValidator[validatorID].receivedStake; if (getValidator[validatorID].status != OK_STATUS) { weight = 0; } node.updateValidatorWeight(validatorID, weight); if (syncPubkey && weight != 0) { node.updateValidatorPubkey(validatorID, getValidatorPubkey[validatorID]); } } /// Check if a validator exists. function _validatorExists(uint256 validatorID) internal view returns (bool) { return getValidator[validatorID].createdTime != 0; } /// Calculate validator commission. function _calcValidatorCommission(uint256 rawReward, uint256 commission) internal pure returns (uint256) { return (rawReward * commission) / Decimal.unit(); } /// Derive address from validator private key function _pubkeyToAddress(bytes calldata pubkey) private pure returns (address) { return address(uint160(uint256(keccak256(pubkey[2:])))); } /// Get current time. function _now() internal view virtual returns (uint256) { return block.timestamp; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.27; import {IEVMWriter} from "../interfaces/IEVMWriter.sol"; contract StubEvmWriter is IEVMWriter { function setBalance(address acc, uint256 value) external {} function copyCode(address acc, address from) external {} function swapCode(address acc, address where) external {} function setStorage(address acc, bytes32 key, bytes32 value) external {} function incNonce(address acc, uint256 diff) external {} }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.27; import {ConstantsManager} from "../sfc/ConstantsManager.sol"; contract UnitTestConstantsManager is ConstantsManager { constructor(address owner) ConstantsManager(owner) {} function updateMinSelfStake(uint256 v) external override onlyOwner { minSelfStake = v; } function updateBaseRewardPerSecond(uint256 v) external override onlyOwner { baseRewardPerSecond = v; } function updateGasPriceBalancingCounterweight(uint256 v) external override onlyOwner { gasPriceBalancingCounterweight = v; } function updateOfflinePenaltyThresholdTime(uint256 v) external override onlyOwner { offlinePenaltyThresholdTime = v; } function updateTargetGasPowerPerSecond(uint256 v) external override onlyOwner { targetGasPowerPerSecond = v; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.27; import {Decimal} from "../common/Decimal.sol"; import {SFC} from "../sfc/SFC.sol"; import {ISFC} from "../interfaces/ISFC.sol"; import {NodeDriverAuth} from "../sfc/NodeDriverAuth.sol"; import {NodeDriver} from "../sfc/NodeDriver.sol"; import {UnitTestConstantsManager} from "./UnitTestConstantsManager.sol"; contract UnitTestSFC is SFC { uint256 internal time; bool public allowedNonNodeCalls; function rebaseTime() external { time = block.timestamp; } function advanceTime(uint256 diff) external { time += diff; } function getTime() external view returns (uint256) { return time; } function getBlockTime() external view returns (uint256) { return block.timestamp; } function enableNonNodeCalls() external { allowedNonNodeCalls = true; } function disableNonNodeCalls() external { allowedNonNodeCalls = false; } function _now() internal view override returns (uint256) { return time; } function isNode(address addr) internal view override returns (bool) { if (allowedNonNodeCalls) { return true; } return SFC.isNode(addr); } } contract UnitTestNetworkInitializer { function initializeAll( uint256 sealedEpoch, uint256 totalSupply, address payable _sfc, address _auth, address _driver, address _evmWriter, address _owner ) external { NodeDriver(_driver).initialize(_auth, _evmWriter, _owner); NodeDriverAuth(_auth).initialize(_sfc, _driver, _owner); UnitTestConstantsManager consts = new UnitTestConstantsManager(address(this)); consts.updateMinSelfStake(0.3175000 * 1e18); consts.updateMaxDelegatedRatio(16 * Decimal.unit()); consts.updateValidatorCommission((15 * Decimal.unit()) / 100); consts.updateBurntFeeShare((20 * Decimal.unit()) / 100); consts.updateTreasuryFeeShare((10 * Decimal.unit()) / 100); consts.updateWithdrawalPeriodEpochs(3); consts.updateWithdrawalPeriodTime(60 * 60 * 24 * 7); consts.updateBaseRewardPerSecond(6183414351851851852); consts.updateOfflinePenaltyThresholdTime(3 days); consts.updateOfflinePenaltyThresholdBlocksNum(1000); consts.updateTargetGasPowerPerSecond(2000000); consts.updateGasPriceBalancingCounterweight(6 * 60 * 60); consts.updateAverageUptimeEpochWindow(10); consts.updateMinAverageUptime(0); // check disabled by default consts.transferOwnership(_owner); ISFC(_sfc).initialize(sealedEpoch, totalSupply, _auth, address(consts), _owner); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.27; /** * @dev Version contract gives the versioning information of the implementation contract */ contract Version { /** * @dev Returns the version of the SFC contract */ function version() public pure returns (bytes3) { return 0x040000; // version 4.0.0 } }
{ "evmVersion": "cancun", "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"ERC1967InvalidImplementation","type":"error"},{"inputs":[],"name":"ERC1967NonPayable","type":"error"},{"inputs":[],"name":"FailedCall","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NotBackend","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"NotNode","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"UUPSUnauthorizedCallContext","type":"error"},{"inputs":[{"internalType":"bytes32","name":"slot","type":"bytes32"}],"name":"UUPSUnsupportedProxiableUUID","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"num","type":"uint256"}],"name":"AdvanceEpochs","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"diff","type":"bytes"}],"name":"UpdateNetworkRules","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"version","type":"uint256"}],"name":"UpdateNetworkVersion","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"validatorID","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"pubkey","type":"bytes"}],"name":"UpdateValidatorPubkey","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"validatorID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"weight","type":"uint256"}],"name":"UpdateValidatorWeight","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"UPGRADE_INTERFACE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"num","type":"uint256"}],"name":"advanceEpochs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"acc","type":"address"},{"internalType":"address","name":"from","type":"address"}],"name":"copyCode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"validatorID","type":"uint256"},{"internalType":"uint256","name":"status","type":"uint256"}],"name":"deactivateValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"acc","type":"address"},{"internalType":"uint256","name":"diff","type":"uint256"}],"name":"incNonce","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_backend","type":"address"},{"internalType":"address","name":"_evmWriterAddress","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"offlineTimes","type":"uint256[]"},{"internalType":"uint256[]","name":"offlineBlocks","type":"uint256[]"},{"internalType":"uint256[]","name":"uptimes","type":"uint256[]"},{"internalType":"uint256[]","name":"originatedTxsFee","type":"uint256[]"}],"name":"sealEpoch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"nextValidatorIDs","type":"uint256[]"}],"name":"sealEpochValidators","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"acc","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"internalType":"uint256","name":"stake","type":"uint256"}],"name":"setGenesisDelegation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"auth","type":"address"},{"internalType":"uint256","name":"validatorID","type":"uint256"},{"internalType":"bytes","name":"pubkey","type":"bytes"},{"internalType":"uint256","name":"createdTime","type":"uint256"}],"name":"setGenesisValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"acc","type":"address"},{"internalType":"bytes32","name":"key","type":"bytes32"},{"internalType":"bytes32","name":"value","type":"bytes32"}],"name":"setStorage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"acc","type":"address"},{"internalType":"address","name":"where","type":"address"}],"name":"swapCode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"diff","type":"bytes"}],"name":"updateNetworkRules","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"version","type":"uint256"}],"name":"updateNetworkVersion","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"validatorID","type":"uint256"},{"internalType":"bytes","name":"pubkey","type":"bytes"}],"name":"updateValidatorPubkey","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"validatorID","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"updateValidatorWeight","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"}]
Contract Creation Code
608060405260043610610131575f3560e01c80638da5cb5b116100a8578063c0c53b8b1161006d578063c0c53b8b1461035d578063d6a0c7af1461037c578063e08d7e661461039b578063e30443bc146103ba578063ebdf104c146103d9578063f2fde38b146103f8575f5ffd5b80638da5cb5b1461027d578063a4066fbe146102c3578063a8ab09ba146102e2578063ad3cb1cc14610301578063b9cc6b1c1461033e575f5ffd5b806339e503ab116100f957806339e503ab146101d25780634f1ef286146101f157806352d1902d14610204578063715018a61461022b57806376fed43a1461023f57806379bead381461025e575f5ffd5b806307690b2a146101355780630aeeca00146101565780631e702f8314610175578063242a6e3f14610194578063267ab446146101b3575b5f5ffd5b348015610140575f5ffd5b5061015461014f366004611129565b610417565b005b348015610161575f5ffd5b5061015461017036600461115a565b6104a8565b348015610180575f5ffd5b5061015461018f366004611171565b610509565b34801561019f575f5ffd5b506101546101ae3660046111d5565b61055f565b3480156101be575f5ffd5b506101546101cd36600461115a565b6105c8565b3480156101dd575f5ffd5b506101546101ec36600461121c565b610622565b6101546101ff366004611260565b6106ba565b34801561020f575f5ffd5b506102186106d9565b6040519081526020015b60405180910390f35b348015610236575f5ffd5b506101546106f4565b34801561024a575f5ffd5b50610154610259366004611321565b610707565b348015610269575f5ffd5b5061015461027836600461137d565b610791565b348015610288575f5ffd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546040516001600160a01b039091168152602001610222565b3480156102ce575f5ffd5b506101546102dd366004611171565b6107f4565b3480156102ed575f5ffd5b506101546102fc36600461121c565b61085c565b34801561030c575f5ffd5b50610331604051806040016040528060058152602001640352e302e360dc1b81525081565b60405161022291906113a5565b348015610349575f5ffd5b506101546103583660046113da565b6108ba565b348015610368575f5ffd5b50610154610377366004611418565b610921565b348015610387575f5ffd5b50610154610396366004611129565b610a66565b3480156103a6575f5ffd5b506101546103b5366004611498565b610aca565b3480156103c5575f5ffd5b506101546103d436600461137d565b610b1a565b3480156103e4575f5ffd5b506101546103f33660046114ca565b610b7d565b348015610403575f5ffd5b50610154610412366004611594565b610c10565b5f546001600160a01b0316331461044157604051630a31c3dd60e41b815260040160405180910390fd5b6001546040516303b4859560e11b81526001600160a01b0384811660048301528381166024830152909116906307690b2a906044015b5f604051808303815f87803b15801561048e575f5ffd5b505af11580156104a0573d5f5f3e3d5ffd5b505050505050565b5f546001600160a01b031633146104d257604051630a31c3dd60e41b815260040160405180910390fd5b6040518181527f0151256d62457b809bbc891b1f81c6dd0b9987552c70ce915b519750cd434dd1906020015b60405180910390a150565b331561052857604051630b9a4d6d60e31b815260040160405180910390fd5b5f54604051631e702f8360e01b815260048101849052602481018390526001600160a01b0390911690631e702f8390604401610477565b5f546001600160a01b0316331461058957604051630a31c3dd60e41b815260040160405180910390fd5b827f0f0ef1ab97439def0a9d2c6d9dc166207f1b13b99e62b442b2993d6153c63a6e83836040516105bb9291906115d5565b60405180910390a2505050565b5f546001600160a01b031633146105f257604051630a31c3dd60e41b815260040160405180910390fd5b6040518181527f2ccdfd47cf0c1f1069d949f1789bb79b2f12821f021634fc835af1de66ea2feb906020016104fe565b5f546001600160a01b0316331461064c57604051630a31c3dd60e41b815260040160405180910390fd5b6001546040516339e503ab60e01b81526001600160a01b0385811660048301526024820185905260448201849052909116906339e503ab906064015b5f604051808303815f87803b15801561069f575f5ffd5b505af11580156106b1573d5f5f3e3d5ffd5b50505050505050565b6106c2610c52565b6106cb82610cf6565b6106d58282610cfe565b5050565b5f6106e2610dbf565b505f5160206116f95f395f51905f5290565b6106fc610e08565b6107055f610e63565b565b331561072657604051630b9a4d6d60e31b815260040160405180910390fd5b5f54604051633b7f6a1d60e11b81526001600160a01b03909116906376fed43a9061075d90889088908890889088906004016115f0565b5f604051808303815f87803b158015610774575f5ffd5b505af1158015610786573d5f5f3e3d5ffd5b505050505050505050565b5f546001600160a01b031633146107bb57604051630a31c3dd60e41b815260040160405180910390fd5b600154604051630f37d5a760e31b81526001600160a01b03848116600483015260248201849052909116906379bead3890604401610477565b5f546001600160a01b0316331461081e57604051630a31c3dd60e41b815260040160405180910390fd5b817fb975807576e3b1461be7de07ebf7d20e4790ed802d7a0c4fdd0a1a13df72a9358260405161085091815260200190565b60405180910390a25050565b331561087b57604051630b9a4d6d60e31b815260040160405180910390fd5b5f5460405163545584dd60e11b81526001600160a01b03858116600483015260248201859052604482018490529091169063a8ab09ba90606401610688565b5f546001600160a01b031633146108e457604051630a31c3dd60e41b815260040160405180910390fd5b7f47d10eed096a44e3d0abc586c7e3a5d6cb5358cc90e7d437cd0627f7e765fb9982826040516109159291906115d5565b60405180910390a15050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156109655750825b90505f826001600160401b031660011480156109805750303b155b90508115801561098e575080155b156109ac5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156109d657845460ff60401b1916600160401b1785555b6109df86610ed3565b6109e7610ee4565b5f80546001600160a01b03808b166001600160a01b03199283161790925560018054928a16929091169190911790558315610a5c57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b5f546001600160a01b03163314610a9057604051630a31c3dd60e41b815260040160405180910390fd5b60015460405163d6a0c7af60e01b81526001600160a01b03848116600483015283811660248301529091169063d6a0c7af90604401610477565b3315610ae957604051630b9a4d6d60e31b815260040160405180910390fd5b5f54604051637046bf3360e11b81526001600160a01b039091169063e08d7e66906104779085908590600401611659565b5f546001600160a01b03163314610b4457604051630a31c3dd60e41b815260040160405180910390fd5b6001546040516338c110ef60e21b81526001600160a01b038481166004830152602482018490529091169063e30443bc90604401610477565b3315610b9c57604051630b9a4d6d60e31b815260040160405180910390fd5b5f54604051633af7c41360e21b81526001600160a01b039091169063ebdf104c90610bd9908b908b908b908b908b908b908b908b9060040161166c565b5f604051808303815f87803b158015610bf0575f5ffd5b505af1158015610c02573d5f5f3e3d5ffd5b505050505050505050505050565b610c18610e08565b6001600160a01b038116610c4657604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b610c4f81610e63565b50565b306001600160a01b037f000000000000000000000000d100a01e00000000000000000000000000000001161480610cd857507f000000000000000000000000d100a01e000000000000000000000000000000016001600160a01b0316610ccc5f5160206116f95f395f51905f52546001600160a01b031690565b6001600160a01b031614155b156107055760405163703e46dd60e11b815260040160405180910390fd5b610c4f610e08565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015610d58575060408051601f3d908101601f19168201909252610d55918101906116cb565b60015b610d8057604051634c9c8ce360e01b81526001600160a01b0383166004820152602401610c3d565b5f5160206116f95f395f51905f528114610db057604051632a87526960e21b815260048101829052602401610c3d565b610dba8383610eec565b505050565b306001600160a01b037f000000000000000000000000d100a01e0000000000000000000000000000000116146107055760405163703e46dd60e11b815260040160405180910390fd5b33610e3a7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b0316146107055760405163118cdaa760e01b8152336004820152602401610c3d565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b610edb610f41565b610c4f81610f8a565b610705610f41565b610ef582610f92565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a2805115610f3957610dba8282610ff5565b6106d5611067565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661070557604051631afcd79f60e31b815260040160405180910390fd5b610c18610f41565b806001600160a01b03163b5f03610fc757604051634c9c8ce360e01b81526001600160a01b0382166004820152602401610c3d565b5f5160206116f95f395f51905f5280546001600160a01b0319166001600160a01b0392909216919091179055565b60605f5f846001600160a01b03168460405161101191906116e2565b5f60405180830381855af49150503d805f8114611049576040519150601f19603f3d011682016040523d82523d5f602084013e61104e565b606091505b509150915061105e858383611086565b95945050505050565b34156107055760405163b398979f60e01b815260040160405180910390fd5b60608261109b57611096826110e5565b6110de565b81511580156110b257506001600160a01b0384163b155b156110db57604051639996b31560e01b81526001600160a01b0385166004820152602401610c3d565b50805b9392505050565b8051156110f55780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b80356001600160a01b0381168114611124575f5ffd5b919050565b5f5f6040838503121561113a575f5ffd5b6111438361110e565b91506111516020840161110e565b90509250929050565b5f6020828403121561116a575f5ffd5b5035919050565b5f5f60408385031215611182575f5ffd5b50508035926020909101359150565b5f5f83601f8401126111a1575f5ffd5b5081356001600160401b038111156111b7575f5ffd5b6020830191508360208285010111156111ce575f5ffd5b9250929050565b5f5f5f604084860312156111e7575f5ffd5b8335925060208401356001600160401b03811115611203575f5ffd5b61120f86828701611191565b9497909650939450505050565b5f5f5f6060848603121561122e575f5ffd5b6112378461110e565b95602085013595506040909401359392505050565b634e487b7160e01b5f52604160045260245ffd5b5f5f60408385031215611271575f5ffd5b61127a8361110e565b915060208301356001600160401b03811115611294575f5ffd5b8301601f810185136112a4575f5ffd5b80356001600160401b038111156112bd576112bd61124c565b604051601f8201601f19908116603f011681016001600160401b03811182821017156112eb576112eb61124c565b604052818152828201602001871015611302575f5ffd5b816020840160208301375f602083830101528093505050509250929050565b5f5f5f5f5f60808688031215611335575f5ffd5b61133e8661110e565b94506020860135935060408601356001600160401b0381111561135f575f5ffd5b61136b88828901611191565b96999598509660600135949350505050565b5f5f6040838503121561138e575f5ffd5b6113978361110e565b946020939093013593505050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f5f602083850312156113eb575f5ffd5b82356001600160401b03811115611400575f5ffd5b61140c85828601611191565b90969095509350505050565b5f5f5f6060848603121561142a575f5ffd5b6114338461110e565b92506114416020850161110e565b915061144f6040850161110e565b90509250925092565b5f5f83601f840112611468575f5ffd5b5081356001600160401b0381111561147e575f5ffd5b6020830191508360208260051b85010111156111ce575f5ffd5b5f5f602083850312156114a9575f5ffd5b82356001600160401b038111156114be575f5ffd5b61140c85828601611458565b5f5f5f5f5f5f5f5f6080898b0312156114e1575f5ffd5b88356001600160401b038111156114f6575f5ffd5b6115028b828c01611458565b90995097505060208901356001600160401b03811115611520575f5ffd5b61152c8b828c01611458565b90975095505060408901356001600160401b0381111561154a575f5ffd5b6115568b828c01611458565b90955093505060608901356001600160401b03811115611574575f5ffd5b6115808b828c01611458565b999c989b5096995094979396929594505050565b5f602082840312156115a4575f5ffd5b6110de8261110e565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b602081525f6115e86020830184866115ad565b949350505050565b60018060a01b0386168152846020820152608060408201525f6116176080830185876115ad565b90508260608301529695505050505050565b8183525f6001600160fb1b03831115611640575f5ffd5b8260051b80836020870137939093016020019392505050565b602081525f6115e8602083018486611629565b608081525f61167f608083018a8c611629565b828103602084015261169281898b611629565b905082810360408401526116a7818789611629565b905082810360608401526116bc818587611629565b9b9a5050505050505050505050565b5f602082840312156116db575f5ffd5b5051919050565b5f82518060208501845e5f92019182525091905056fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca2646970667358221220bdc88220ee10109405383043d276b2402778177b22ef92e76a2321370bbd62fd64736f6c634300081b0033
Deployed Bytecode
0x608060405260043610610131575f3560e01c80638da5cb5b116100a8578063c0c53b8b1161006d578063c0c53b8b1461035d578063d6a0c7af1461037c578063e08d7e661461039b578063e30443bc146103ba578063ebdf104c146103d9578063f2fde38b146103f8575f5ffd5b80638da5cb5b1461027d578063a4066fbe146102c3578063a8ab09ba146102e2578063ad3cb1cc14610301578063b9cc6b1c1461033e575f5ffd5b806339e503ab116100f957806339e503ab146101d25780634f1ef286146101f157806352d1902d14610204578063715018a61461022b57806376fed43a1461023f57806379bead381461025e575f5ffd5b806307690b2a146101355780630aeeca00146101565780631e702f8314610175578063242a6e3f14610194578063267ab446146101b3575b5f5ffd5b348015610140575f5ffd5b5061015461014f366004611129565b610417565b005b348015610161575f5ffd5b5061015461017036600461115a565b6104a8565b348015610180575f5ffd5b5061015461018f366004611171565b610509565b34801561019f575f5ffd5b506101546101ae3660046111d5565b61055f565b3480156101be575f5ffd5b506101546101cd36600461115a565b6105c8565b3480156101dd575f5ffd5b506101546101ec36600461121c565b610622565b6101546101ff366004611260565b6106ba565b34801561020f575f5ffd5b506102186106d9565b6040519081526020015b60405180910390f35b348015610236575f5ffd5b506101546106f4565b34801561024a575f5ffd5b50610154610259366004611321565b610707565b348015610269575f5ffd5b5061015461027836600461137d565b610791565b348015610288575f5ffd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546040516001600160a01b039091168152602001610222565b3480156102ce575f5ffd5b506101546102dd366004611171565b6107f4565b3480156102ed575f5ffd5b506101546102fc36600461121c565b61085c565b34801561030c575f5ffd5b50610331604051806040016040528060058152602001640352e302e360dc1b81525081565b60405161022291906113a5565b348015610349575f5ffd5b506101546103583660046113da565b6108ba565b348015610368575f5ffd5b50610154610377366004611418565b610921565b348015610387575f5ffd5b50610154610396366004611129565b610a66565b3480156103a6575f5ffd5b506101546103b5366004611498565b610aca565b3480156103c5575f5ffd5b506101546103d436600461137d565b610b1a565b3480156103e4575f5ffd5b506101546103f33660046114ca565b610b7d565b348015610403575f5ffd5b50610154610412366004611594565b610c10565b5f546001600160a01b0316331461044157604051630a31c3dd60e41b815260040160405180910390fd5b6001546040516303b4859560e11b81526001600160a01b0384811660048301528381166024830152909116906307690b2a906044015b5f604051808303815f87803b15801561048e575f5ffd5b505af11580156104a0573d5f5f3e3d5ffd5b505050505050565b5f546001600160a01b031633146104d257604051630a31c3dd60e41b815260040160405180910390fd5b6040518181527f0151256d62457b809bbc891b1f81c6dd0b9987552c70ce915b519750cd434dd1906020015b60405180910390a150565b331561052857604051630b9a4d6d60e31b815260040160405180910390fd5b5f54604051631e702f8360e01b815260048101849052602481018390526001600160a01b0390911690631e702f8390604401610477565b5f546001600160a01b0316331461058957604051630a31c3dd60e41b815260040160405180910390fd5b827f0f0ef1ab97439def0a9d2c6d9dc166207f1b13b99e62b442b2993d6153c63a6e83836040516105bb9291906115d5565b60405180910390a2505050565b5f546001600160a01b031633146105f257604051630a31c3dd60e41b815260040160405180910390fd5b6040518181527f2ccdfd47cf0c1f1069d949f1789bb79b2f12821f021634fc835af1de66ea2feb906020016104fe565b5f546001600160a01b0316331461064c57604051630a31c3dd60e41b815260040160405180910390fd5b6001546040516339e503ab60e01b81526001600160a01b0385811660048301526024820185905260448201849052909116906339e503ab906064015b5f604051808303815f87803b15801561069f575f5ffd5b505af11580156106b1573d5f5f3e3d5ffd5b50505050505050565b6106c2610c52565b6106cb82610cf6565b6106d58282610cfe565b5050565b5f6106e2610dbf565b505f5160206116f95f395f51905f5290565b6106fc610e08565b6107055f610e63565b565b331561072657604051630b9a4d6d60e31b815260040160405180910390fd5b5f54604051633b7f6a1d60e11b81526001600160a01b03909116906376fed43a9061075d90889088908890889088906004016115f0565b5f604051808303815f87803b158015610774575f5ffd5b505af1158015610786573d5f5f3e3d5ffd5b505050505050505050565b5f546001600160a01b031633146107bb57604051630a31c3dd60e41b815260040160405180910390fd5b600154604051630f37d5a760e31b81526001600160a01b03848116600483015260248201849052909116906379bead3890604401610477565b5f546001600160a01b0316331461081e57604051630a31c3dd60e41b815260040160405180910390fd5b817fb975807576e3b1461be7de07ebf7d20e4790ed802d7a0c4fdd0a1a13df72a9358260405161085091815260200190565b60405180910390a25050565b331561087b57604051630b9a4d6d60e31b815260040160405180910390fd5b5f5460405163545584dd60e11b81526001600160a01b03858116600483015260248201859052604482018490529091169063a8ab09ba90606401610688565b5f546001600160a01b031633146108e457604051630a31c3dd60e41b815260040160405180910390fd5b7f47d10eed096a44e3d0abc586c7e3a5d6cb5358cc90e7d437cd0627f7e765fb9982826040516109159291906115d5565b60405180910390a15050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156109655750825b90505f826001600160401b031660011480156109805750303b155b90508115801561098e575080155b156109ac5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156109d657845460ff60401b1916600160401b1785555b6109df86610ed3565b6109e7610ee4565b5f80546001600160a01b03808b166001600160a01b03199283161790925560018054928a16929091169190911790558315610a5c57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b5f546001600160a01b03163314610a9057604051630a31c3dd60e41b815260040160405180910390fd5b60015460405163d6a0c7af60e01b81526001600160a01b03848116600483015283811660248301529091169063d6a0c7af90604401610477565b3315610ae957604051630b9a4d6d60e31b815260040160405180910390fd5b5f54604051637046bf3360e11b81526001600160a01b039091169063e08d7e66906104779085908590600401611659565b5f546001600160a01b03163314610b4457604051630a31c3dd60e41b815260040160405180910390fd5b6001546040516338c110ef60e21b81526001600160a01b038481166004830152602482018490529091169063e30443bc90604401610477565b3315610b9c57604051630b9a4d6d60e31b815260040160405180910390fd5b5f54604051633af7c41360e21b81526001600160a01b039091169063ebdf104c90610bd9908b908b908b908b908b908b908b908b9060040161166c565b5f604051808303815f87803b158015610bf0575f5ffd5b505af1158015610c02573d5f5f3e3d5ffd5b505050505050505050505050565b610c18610e08565b6001600160a01b038116610c4657604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b610c4f81610e63565b50565b306001600160a01b037f000000000000000000000000d100a01e00000000000000000000000000000001161480610cd857507f000000000000000000000000d100a01e000000000000000000000000000000016001600160a01b0316610ccc5f5160206116f95f395f51905f52546001600160a01b031690565b6001600160a01b031614155b156107055760405163703e46dd60e11b815260040160405180910390fd5b610c4f610e08565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015610d58575060408051601f3d908101601f19168201909252610d55918101906116cb565b60015b610d8057604051634c9c8ce360e01b81526001600160a01b0383166004820152602401610c3d565b5f5160206116f95f395f51905f528114610db057604051632a87526960e21b815260048101829052602401610c3d565b610dba8383610eec565b505050565b306001600160a01b037f000000000000000000000000d100a01e0000000000000000000000000000000116146107055760405163703e46dd60e11b815260040160405180910390fd5b33610e3a7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b0316146107055760405163118cdaa760e01b8152336004820152602401610c3d565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b610edb610f41565b610c4f81610f8a565b610705610f41565b610ef582610f92565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a2805115610f3957610dba8282610ff5565b6106d5611067565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661070557604051631afcd79f60e31b815260040160405180910390fd5b610c18610f41565b806001600160a01b03163b5f03610fc757604051634c9c8ce360e01b81526001600160a01b0382166004820152602401610c3d565b5f5160206116f95f395f51905f5280546001600160a01b0319166001600160a01b0392909216919091179055565b60605f5f846001600160a01b03168460405161101191906116e2565b5f60405180830381855af49150503d805f8114611049576040519150601f19603f3d011682016040523d82523d5f602084013e61104e565b606091505b509150915061105e858383611086565b95945050505050565b34156107055760405163b398979f60e01b815260040160405180910390fd5b60608261109b57611096826110e5565b6110de565b81511580156110b257506001600160a01b0384163b155b156110db57604051639996b31560e01b81526001600160a01b0385166004820152602401610c3d565b50805b9392505050565b8051156110f55780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b80356001600160a01b0381168114611124575f5ffd5b919050565b5f5f6040838503121561113a575f5ffd5b6111438361110e565b91506111516020840161110e565b90509250929050565b5f6020828403121561116a575f5ffd5b5035919050565b5f5f60408385031215611182575f5ffd5b50508035926020909101359150565b5f5f83601f8401126111a1575f5ffd5b5081356001600160401b038111156111b7575f5ffd5b6020830191508360208285010111156111ce575f5ffd5b9250929050565b5f5f5f604084860312156111e7575f5ffd5b8335925060208401356001600160401b03811115611203575f5ffd5b61120f86828701611191565b9497909650939450505050565b5f5f5f6060848603121561122e575f5ffd5b6112378461110e565b95602085013595506040909401359392505050565b634e487b7160e01b5f52604160045260245ffd5b5f5f60408385031215611271575f5ffd5b61127a8361110e565b915060208301356001600160401b03811115611294575f5ffd5b8301601f810185136112a4575f5ffd5b80356001600160401b038111156112bd576112bd61124c565b604051601f8201601f19908116603f011681016001600160401b03811182821017156112eb576112eb61124c565b604052818152828201602001871015611302575f5ffd5b816020840160208301375f602083830101528093505050509250929050565b5f5f5f5f5f60808688031215611335575f5ffd5b61133e8661110e565b94506020860135935060408601356001600160401b0381111561135f575f5ffd5b61136b88828901611191565b96999598509660600135949350505050565b5f5f6040838503121561138e575f5ffd5b6113978361110e565b946020939093013593505050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f5f602083850312156113eb575f5ffd5b82356001600160401b03811115611400575f5ffd5b61140c85828601611191565b90969095509350505050565b5f5f5f6060848603121561142a575f5ffd5b6114338461110e565b92506114416020850161110e565b915061144f6040850161110e565b90509250925092565b5f5f83601f840112611468575f5ffd5b5081356001600160401b0381111561147e575f5ffd5b6020830191508360208260051b85010111156111ce575f5ffd5b5f5f602083850312156114a9575f5ffd5b82356001600160401b038111156114be575f5ffd5b61140c85828601611458565b5f5f5f5f5f5f5f5f6080898b0312156114e1575f5ffd5b88356001600160401b038111156114f6575f5ffd5b6115028b828c01611458565b90995097505060208901356001600160401b03811115611520575f5ffd5b61152c8b828c01611458565b90975095505060408901356001600160401b0381111561154a575f5ffd5b6115568b828c01611458565b90955093505060608901356001600160401b03811115611574575f5ffd5b6115808b828c01611458565b999c989b5096995094979396929594505050565b5f602082840312156115a4575f5ffd5b6110de8261110e565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b602081525f6115e86020830184866115ad565b949350505050565b60018060a01b0386168152846020820152608060408201525f6116176080830185876115ad565b90508260608301529695505050505050565b8183525f6001600160fb1b03831115611640575f5ffd5b8260051b80836020870137939093016020019392505050565b602081525f6115e8602083018486611629565b608081525f61167f608083018a8c611629565b828103602084015261169281898b611629565b905082810360408401526116a7818789611629565b905082810360608401526116bc818587611629565b9b9a5050505050505050505050565b5f602082840312156116db575f5ffd5b5051919050565b5f82518060208501845e5f92019182525091905056fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca2646970667358221220bdc88220ee10109405383043d276b2402778177b22ef92e76a2321370bbd62fd64736f6c634300081b0033
Deployed Bytecode Sourcemap
708:5183:18:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2312:114;;;;;;;;;;-1:-1:-1;2312:114:18;;;;;:::i;:::-;;:::i;:::-;;3260:97;;;;;;;;;;-1:-1:-1;3260:97:18;;;;;:::i;:::-;;:::i;5136:149::-;;;;;;;;;;-1:-1:-1;5136:149:18;;;;;:::i;:::-;;:::i;3822:160::-;;;;;;;;;;-1:-1:-1;3822:160:18;;;;;:::i;:::-;;:::i;3030:119::-;;;;;;;;;;-1:-1:-1;3030:119:18;;;;;:::i;:::-;;:::i;2432:136::-;;;;;;;;;;-1:-1:-1;2432:136:18;;;;;:::i;:::-;;:::i;4161:214:2:-;;;;;;:::i;:::-;;:::i;3708:134::-;;;;;;;;;;;;;:::i;:::-;;;3645:25:25;;;3633:2;3618:18;3708:134:2;;;;;;;;3155:101:0;;;;;;;;;;;;;:::i;4432:248:18:-;;;;;;;;;;-1:-1:-1;4432:248:18;;;;;:::i;:::-;;:::i;2574:112::-;;;;;;;;;;-1:-1:-1;2574:112:18;;;;;:::i;:::-;;:::i;2441:144:0:-;;;;;;;;;;-1:-1:-1;1313:22:0;2570:8;2441:144;;-1:-1:-1;;;;;2570:8:0;;;4855:51:25;;4843:2;4828:18;2441:144:0;4709:203:25;3515:151:18;;;;;;;;;;-1:-1:-1;3515:151:18;;;;;:::i;:::-;;:::i;4792:183::-;;;;;;;;;;-1:-1:-1;4792:183:18;;;;;:::i;:::-;;:::i;1819:58:2:-;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;1819:58:2;;;;;;;;;;;;:::i;2801:116:18:-;;;;;;;;;;-1:-1:-1;2801:116:18;;;;;:::i;:::-;;:::i;1585:270::-;;;;;;;;;;-1:-1:-1;1585:270:18;;;;;:::i;:::-;;:::i;2194:112::-;;;;;;;;;;-1:-1:-1;2194:112:18;;;;;:::i;:::-;;:::i;5743:146::-;;;;;;;;;;-1:-1:-1;5743:146:18;;;;;:::i;:::-;;:::i;2070:118::-;;;;;;;;;;-1:-1:-1;2070:118:18;;;;;:::i;:::-;;:::i;5366:297::-;;;;;;;;;;-1:-1:-1;5366:297:18;;;;;:::i;:::-;;:::i;3405:215:0:-;;;;;;;;;;-1:-1:-1;3405:215:0;;;;;:::i;:::-;;:::i;2312:114:18:-;1052:7;;-1:-1:-1;;;;;1052:7:18;1030:10;:30;1026:80;;1083:12;;-1:-1:-1;;;1083:12:18;;;;;;;;;;;1026:80;2389:9:::1;::::0;:30:::1;::::0;-1:-1:-1;;;2389:30:18;;-1:-1:-1;;;;;9151:32:25;;;2389:30:18::1;::::0;::::1;9133:51:25::0;9220:32;;;9200:18;;;9193:60;2389:9:18;;::::1;::::0;:18:::1;::::0;9106::25;;2389:30:18::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2312:114:::0;;:::o;3260:97::-;1052:7;;-1:-1:-1;;;;;1052:7:18;1030:10;:30;1026:80;;1083:12;;-1:-1:-1;;;1083:12:18;;;;;;;;;;;1026:80;3332:18:::1;::::0;3645:25:25;;;3332:18:18::1;::::0;3633:2:25;3618:18;3332::18::1;;;;;;;;3260:97:::0;:::o;5136:149::-;4187:10;:24;4183:71;;4234:9;;-1:-1:-1;;;4234:9:18;;;;;;;;;;;4183:71;5230:7:::1;::::0;:48:::1;::::0;-1:-1:-1;;;5230:48:18;;::::1;::::0;::::1;9620:25:25::0;;;9661:18;;;9654:34;;;-1:-1:-1;;;;;5230:7:18;;::::1;::::0;:27:::1;::::0;9593:18:25;;5230:48:18::1;9446:248:25::0;3822:160:18;1052:7;;-1:-1:-1;;;;;1052:7:18;1030:10;:30;1026:80;;1083:12;;-1:-1:-1;;;1083:12:18;;;;;;;;;;;1026:80;3955:11:::1;3933:42;3968:6;;3933:42;;;;;;;:::i;:::-;;;;;;;;3822:160:::0;;;:::o;3030:119::-;1052:7;;-1:-1:-1;;;;;1052:7:18;1030:10;:30;1026:80;;1083:12;;-1:-1:-1;;;1083:12:18;;;;;;;;;;;1026:80;3113:29:::1;::::0;3645:25:25;;;3113:29:18::1;::::0;3633:2:25;3618:18;3113:29:18::1;3499:177:25::0;2432:136:18;1052:7;;-1:-1:-1;;;;;1052:7:18;1030:10;:30;1026:80;;1083:12;;-1:-1:-1;;;1083:12:18;;;;;;;;;;;1026:80;2524:9:::1;::::0;:37:::1;::::0;-1:-1:-1;;;2524:37:18;;-1:-1:-1;;;;;10439:32:25;;;2524:37:18::1;::::0;::::1;10421:51:25::0;10488:18;;;10481:34;;;10531:18;;;10524:34;;;2524:9:18;;::::1;::::0;:20:::1;::::0;10394:18:25;;2524:37:18::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2432:136:::0;;;:::o;4161:214:2:-;2655:13;:11;:13::i;:::-;4276:36:::1;4294:17;4276;:36::i;:::-;4322:46;4344:17;4363:4;4322:21;:46::i;:::-;4161:214:::0;;:::o;3708:134::-;3777:7;2926:20;:18;:20::i;:::-;-1:-1:-1;;;;;;;;;;;;3708:134:2;:::o;3155:101:0:-;2334:13;:11;:13::i;:::-;3219:30:::1;3246:1;3219:18;:30::i;:::-;3155:101::o:0;4432:248:18:-;4187:10;:24;4183:71;;4234:9;;-1:-1:-1;;;4234:9:18;;;;;;;;;;;4183:71;4606:7:::1;::::0;:67:::1;::::0;-1:-1:-1;;;4606:67:18;;-1:-1:-1;;;;;4606:7:18;;::::1;::::0;:27:::1;::::0;:67:::1;::::0;4634:4;;4640:11;;4653:6;;;;4661:11;;4606:67:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4432:248:::0;;;;;:::o;2574:112::-;1052:7;;-1:-1:-1;;;;;1052:7:18;1030:10;:30;1026:80;;1083:12;;-1:-1:-1;;;1083:12:18;;;;;;;;;;;1026:80;2650:9:::1;::::0;:29:::1;::::0;-1:-1:-1;;;2650:29:18;;-1:-1:-1;;;;;11251:32:25;;;2650:29:18::1;::::0;::::1;11233:51:25::0;11300:18;;;11293:34;;;2650:9:18;;::::1;::::0;:18:::1;::::0;11206::25;;2650:29:18::1;11059:274:25::0;3515:151:18;1052:7;;-1:-1:-1;;;;;1052:7:18;1030:10;:30;1026:80;;1083:12;;-1:-1:-1;;;1083:12:18;;;;;;;;;;;1026:80;3640:11:::1;3618:41;3653:5;3618:41;;;;3645:25:25::0;;3633:2;3618:18;;3499:177;3618:41:18::1;;;;;;;;3515:151:::0;;:::o;4792:183::-;4187:10;:24;4183:71;;4234:9;;-1:-1:-1;;;4234:9:18;;;;;;;;;;;4183:71;4907:7:::1;::::0;:61:::1;::::0;-1:-1:-1;;;4907:61:18;;-1:-1:-1;;;;;10439:32:25;;;4907:61:18::1;::::0;::::1;10421:51:25::0;10488:18;;;10481:34;;;10531:18;;;10524:34;;;4907:7:18;;::::1;::::0;:28:::1;::::0;10394:18:25;;4907:61:18::1;10219:345:25::0;2801:116:18;1052:7;;-1:-1:-1;;;;;1052:7:18;1030:10;:30;1026:80;;1083:12;;-1:-1:-1;;;1083:12:18;;;;;;;;;;;1026:80;2886:24:::1;2905:4;;2886:24;;;;;;;:::i;:::-;;;;;;;;2801:116:::0;;:::o;1585:270::-;8870:21:1;4302:15;;-1:-1:-1;;;4302:15:1;;;;4301:16;;-1:-1:-1;;;;;4348:14:1;4158:30;4726:16;;:34;;;;;4746:14;4726:34;4706:54;;4770:17;4790:11;-1:-1:-1;;;;;4790:16:1;4805:1;4790:16;:50;;;;-1:-1:-1;4818:4:1;4810:25;:30;4790:50;4770:70;;4856:12;4855:13;:30;;;;;4873:12;4872:13;4855:30;4851:91;;;4908:23;;-1:-1:-1;;;4908:23:1;;;;;;;;;;;4851:91;4951:18;;-1:-1:-1;;4951:18:1;4968:1;4951:18;;;4979:67;;;;5013:22;;-1:-1:-1;;;;5013:22:1;-1:-1:-1;;;5013:22:1;;;4979:67;1697:22:18::1;1712:6;1697:14;:22::i;:::-;1729:24;:22;:24::i;:::-;1763:7;:34:::0;;-1:-1:-1;;;;;1763:34:18;;::::1;-1:-1:-1::0;;;;;;1763:34:18;;::::1;;::::0;;;;1807:41;;;;::::1;::::0;;;::::1;::::0;;;::::1;::::0;;5066:101:1;;;;5100:23;;-1:-1:-1;;;;5100:23:1;;;5142:14;;-1:-1:-1;11841:50:25;;5142:14:1;;11829:2:25;11814:18;5142:14:1;;;;;;;5066:101;4092:1081;;;;;1585:270:18;;;:::o;2194:112::-;1052:7;;-1:-1:-1;;;;;1052:7:18;1030:10;:30;1026:80;;1083:12;;-1:-1:-1;;;1083:12:18;;;;;;;;;;;1026:80;2270:9:::1;::::0;:29:::1;::::0;-1:-1:-1;;;2270:29:18;;-1:-1:-1;;;;;9151:32:25;;;2270:29:18::1;::::0;::::1;9133:51:25::0;9220:32;;;9200:18;;;9193:60;2270:9:18;;::::1;::::0;:18:::1;::::0;9106::25;;2270:29:18::1;8959:300:25::0;5743:146:18;4187:10;:24;4183:71;;4234:9;;-1:-1:-1;;;4234:9:18;;;;;;;;;;;4183:71;5837:7:::1;::::0;:45:::1;::::0;-1:-1:-1;;;5837:45:18;;-1:-1:-1;;;;;5837:7:18;;::::1;::::0;:27:::1;::::0;:45:::1;::::0;5865:16;;;;5837:45:::1;;;:::i;2070:118::-:0;1052:7;;-1:-1:-1;;;;;1052:7:18;1030:10;:30;1026:80;;1083:12;;-1:-1:-1;;;1083:12:18;;;;;;;;;;;1026:80;2149:9:::1;::::0;:32:::1;::::0;-1:-1:-1;;;2149:32:18;;-1:-1:-1;;;;;11251:32:25;;;2149::18::1;::::0;::::1;11233:51:25::0;11300:18;;;11293:34;;;2149:9:18;;::::1;::::0;:20:::1;::::0;11206:18:25;;2149:32:18::1;11059:274:25::0;5366:297:18;4187:10;:24;4183:71;;4234:9;;-1:-1:-1;;;4234:9:18;;;;;;;;;;;4183:71;5583:7:::1;::::0;:73:::1;::::0;-1:-1:-1;;;5583:73:18;;-1:-1:-1;;;;;5583:7:18;;::::1;::::0;:17:::1;::::0;:73:::1;::::0;5601:12;;;;5615:13;;;;5630:7;;;;5639:16;;;;5583:73:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5366:297:::0;;;;;;;;:::o;3405:215:0:-;2334:13;:11;:13::i;:::-;-1:-1:-1;;;;;3489:22:0;::::1;3485:91;;3534:31;::::0;-1:-1:-1;;;3534:31:0;;3562:1:::1;3534:31;::::0;::::1;4855:51:25::0;4828:18;;3534:31:0::1;;;;;;;;3485:91;3585:28;3604:8;3585:18;:28::i;:::-;3405:215:::0;:::o;4603:312:2:-;4683:4;-1:-1:-1;;;;;4692:6:2;4675:23;;;:120;;;4789:6;-1:-1:-1;;;;;4753:42:2;:32;-1:-1:-1;;;;;;;;;;;1519:53:6;-1:-1:-1;;;;;1519:53:6;;1441:138;4753:32:2;-1:-1:-1;;;;;4753:42:2;;;4675:120;4658:251;;;4869:29;;-1:-1:-1;;;4869:29:2;;;;;;;;;;;1998:66:18;2334:13:0;:11;:13::i;6057:538:2:-;6174:17;-1:-1:-1;;;;;6156:50:2;;:52;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;6156:52:2;;;;;;;;-1:-1:-1;;6156:52:2;;;;;;;;;;;;:::i;:::-;;;6152:437;;6518:60;;-1:-1:-1;;;6518:60:2;;-1:-1:-1;;;;;4873:32:25;;6518:60:2;;;4855:51:25;4828:18;;6518:60:2;4709:203:25;6152:437:2;-1:-1:-1;;;;;;;;;;;6250:40:2;;6246:120;;6317:34;;-1:-1:-1;;;6317:34:2;;;;;3645:25:25;;;3618:18;;6317:34:2;3499:177:25;6246:120:2;6379:54;6409:17;6428:4;6379:29;:54::i;:::-;6209:235;6057:538;;:::o;5032:213::-;5106:4;-1:-1:-1;;;;;5115:6:2;5098:23;;5094:145;;5199:29;;-1:-1:-1;;;5199:29:2;;;;;;;;;;;2658:162:0;966:10:3;2717:7:0;1313:22;2570:8;-1:-1:-1;;;;;2570:8:0;;2441:144;2717:7;-1:-1:-1;;;;;2717:23:0;;2713:101;;2763:40;;-1:-1:-1;;;2763:40:0;;966:10:3;2763:40:0;;;4855:51:25;4828:18;;2763:40:0;4709:203:25;3774:248:0;1313:22;3923:8;;-1:-1:-1;;;;;;3941:19:0;;-1:-1:-1;;;;;3941:19:0;;;;;;;;3975:40;;3923:8;;;;;3975:40;;3847:24;;3975:40;3837:185;;3774:248;:::o;1847:127::-;6931:20:1;:18;:20::i;:::-;1929:38:0::1;1954:12;1929:24;:38::i;2970:67:2:-:0;6931:20:1;:18;:20::i;2264:344:6:-;2355:37;2374:17;2355:18;:37::i;:::-;2407:36;;-1:-1:-1;;;;;2407:36:6;;;;;;;;2458:11;;:15;2454:148;;2489:53;2518:17;2537:4;2489:28;:53::i;2454:148::-;2573:18;:16;:18::i;7084:141:1:-;8870:21;8560:40;-1:-1:-1;;;8560:40:1;;;;7146:73;;7191:17;;-1:-1:-1;;;7191:17:1;;;;;;;;;;;1980:235:0;6931:20:1;:18;:20::i;1671:281:6:-;1748:17;-1:-1:-1;;;;;1748:29:6;;1781:1;1748:34;1744:119;;1805:47;;-1:-1:-1;;;1805:47:6;;-1:-1:-1;;;;;4873:32:25;;1805:47:6;;;4855:51:25;4828:18;;1805:47:6;4709:203:25;1744:119:6;-1:-1:-1;;;;;;;;;;;1872:73:6;;-1:-1:-1;;;;;;1872:73:6;-1:-1:-1;;;;;1872:73:6;;;;;;;;;;1671:281::o;3900:253:8:-;3983:12;4008;4022:23;4049:6;-1:-1:-1;;;;;4049:19:8;4069:4;4049:25;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4007:67;;;;4091:55;4118:6;4126:7;4135:10;4091:26;:55::i;:::-;4084:62;3900:253;-1:-1:-1;;;;;3900:253:8:o;6113:122:6:-;6163:9;:13;6159:70;;6199:19;;-1:-1:-1;;;6199:19:6;;;;;;;;;;;4421:582:8;4565:12;4594:7;4589:408;;4617:19;4625:10;4617:7;:19::i;:::-;4589:408;;;4841:17;;:22;:49;;;;-1:-1:-1;;;;;;4867:18:8;;;:23;4841:49;4837:119;;;4917:24;;-1:-1:-1;;;4917:24:8;;-1:-1:-1;;;;;4873:32:25;;4917:24:8;;;4855:51:25;4828:18;;4917:24:8;4709:203:25;4837:119:8;-1:-1:-1;4976:10:8;4589:408;4421:582;;;;;:::o;5543:487::-;5674:17;;:21;5670:354;;5871:10;5865:17;5927:15;5914:10;5910:2;5906:19;5899:44;5670:354;5994:19;;-1:-1:-1;;;5994:19:8;;;;;;;;;;;14:173:25;82:20;;-1:-1:-1;;;;;131:31:25;;121:42;;111:70;;177:1;174;167:12;111:70;14:173;;;:::o;192:260::-;260:6;268;321:2;309:9;300:7;296:23;292:32;289:52;;;337:1;334;327:12;289:52;360:29;379:9;360:29;:::i;:::-;350:39;;408:38;442:2;431:9;427:18;408:38;:::i;:::-;398:48;;192:260;;;;;:::o;457:226::-;516:6;569:2;557:9;548:7;544:23;540:32;537:52;;;585:1;582;575:12;537:52;-1:-1:-1;630:23:25;;457:226;-1:-1:-1;457:226:25:o;688:346::-;756:6;764;817:2;805:9;796:7;792:23;788:32;785:52;;;833:1;830;823:12;785:52;-1:-1:-1;;878:23:25;;;998:2;983:18;;;970:32;;-1:-1:-1;688:346:25:o;1039:347::-;1090:8;1100:6;1154:3;1147:4;1139:6;1135:17;1131:27;1121:55;;1172:1;1169;1162:12;1121:55;-1:-1:-1;1195:20:25;;-1:-1:-1;;;;;1227:30:25;;1224:50;;;1270:1;1267;1260:12;1224:50;1307:4;1299:6;1295:17;1283:29;;1359:3;1352:4;1343:6;1335;1331:19;1327:30;1324:39;1321:59;;;1376:1;1373;1366:12;1321:59;1039:347;;;;;:::o;1391:523::-;1470:6;1478;1486;1539:2;1527:9;1518:7;1514:23;1510:32;1507:52;;;1555:1;1552;1545:12;1507:52;1600:23;;;-1:-1:-1;1698:2:25;1683:18;;1670:32;-1:-1:-1;;;;;1714:30:25;;1711:50;;;1757:1;1754;1747:12;1711:50;1796:58;1846:7;1837:6;1826:9;1822:22;1796:58;:::i;:::-;1391:523;;1873:8;;-1:-1:-1;1770:84:25;;-1:-1:-1;;;;1391:523:25:o;1919:420::-;1996:6;2004;2012;2065:2;2053:9;2044:7;2040:23;2036:32;2033:52;;;2081:1;2078;2071:12;2033:52;2104:29;2123:9;2104:29;:::i;:::-;2094:39;2202:2;2187:18;;2174:32;;-1:-1:-1;2303:2:25;2288:18;;;2275:32;;1919:420;-1:-1:-1;;;1919:420:25:o;2344:127::-;2405:10;2400:3;2396:20;2393:1;2386:31;2436:4;2433:1;2426:15;2460:4;2457:1;2450:15;2476:1018;2553:6;2561;2614:2;2602:9;2593:7;2589:23;2585:32;2582:52;;;2630:1;2627;2620:12;2582:52;2653:29;2672:9;2653:29;:::i;:::-;2643:39;;2733:2;2722:9;2718:18;2705:32;-1:-1:-1;;;;;2752:6:25;2749:30;2746:50;;;2792:1;2789;2782:12;2746:50;2815:22;;2868:4;2860:13;;2856:27;-1:-1:-1;2846:55:25;;2897:1;2894;2887:12;2846:55;2937:2;2924:16;-1:-1:-1;;;;;2955:6:25;2952:30;2949:56;;;2985:18;;:::i;:::-;3034:2;3028:9;3126:2;3088:17;;-1:-1:-1;;3084:31:25;;;3117:2;3080:40;3076:54;3064:67;;-1:-1:-1;;;;;3146:34:25;;3182:22;;;3143:62;3140:88;;;3208:18;;:::i;:::-;3244:2;3237:22;3268;;;3309:15;;;3326:2;3305:24;3302:37;-1:-1:-1;3299:57:25;;;3352:1;3349;3342:12;3299:57;3408:6;3403:2;3399;3395:11;3390:2;3382:6;3378:15;3365:50;3461:1;3456:2;3447:6;3439;3435:19;3431:28;3424:39;3482:6;3472:16;;;;;2476:1018;;;;;:::o;3681:718::-;3778:6;3786;3794;3802;3810;3863:3;3851:9;3842:7;3838:23;3834:33;3831:53;;;3880:1;3877;3870:12;3831:53;3903:29;3922:9;3903:29;:::i;:::-;3893:39;-1:-1:-1;4001:2:25;3986:18;;3973:32;;-1:-1:-1;4080:2:25;4065:18;;4052:32;-1:-1:-1;;;;;4096:30:25;;4093:50;;;4139:1;4136;4129:12;4093:50;4178:58;4228:7;4219:6;4208:9;4204:22;4178:58;:::i;:::-;3681:718;;;;-1:-1:-1;4255:8:25;4363:2;4348:18;4335:32;;3681:718;-1:-1:-1;;;;3681:718:25:o;4404:300::-;4472:6;4480;4533:2;4521:9;4512:7;4508:23;4504:32;4501:52;;;4549:1;4546;4539:12;4501:52;4572:29;4591:9;4572:29;:::i;:::-;4562:39;4670:2;4655:18;;;;4642:32;;-1:-1:-1;;;4404:300:25:o;5342:418::-;5491:2;5480:9;5473:21;5454:4;5523:6;5517:13;5566:6;5561:2;5550:9;5546:18;5539:34;5625:6;5620:2;5612:6;5608:15;5603:2;5592:9;5588:18;5582:50;5681:1;5676:2;5667:6;5656:9;5652:22;5648:31;5641:42;5751:2;5744;5740:7;5735:2;5727:6;5723:15;5719:29;5708:9;5704:45;5700:54;5692:62;;;5342:418;;;;:::o;5765:409::-;5835:6;5843;5896:2;5884:9;5875:7;5871:23;5867:32;5864:52;;;5912:1;5909;5902:12;5864:52;5952:9;5939:23;-1:-1:-1;;;;;5977:6:25;5974:30;5971:50;;;6017:1;6014;6007:12;5971:50;6056:58;6106:7;6097:6;6086:9;6082:22;6056:58;:::i;:::-;6133:8;;6030:84;;-1:-1:-1;5765:409:25;-1:-1:-1;;;;5765:409:25:o;6179:334::-;6256:6;6264;6272;6325:2;6313:9;6304:7;6300:23;6296:32;6293:52;;;6341:1;6338;6331:12;6293:52;6364:29;6383:9;6364:29;:::i;:::-;6354:39;;6412:38;6446:2;6435:9;6431:18;6412:38;:::i;:::-;6402:48;;6469:38;6503:2;6492:9;6488:18;6469:38;:::i;:::-;6459:48;;6179:334;;;;;:::o;6518:367::-;6581:8;6591:6;6645:3;6638:4;6630:6;6626:17;6622:27;6612:55;;6663:1;6660;6653:12;6612:55;-1:-1:-1;6686:20:25;;-1:-1:-1;;;;;6718:30:25;;6715:50;;;6761:1;6758;6751:12;6715:50;6798:4;6790:6;6786:17;6774:29;;6858:3;6851:4;6841:6;6838:1;6834:14;6826:6;6822:27;6818:38;6815:47;6812:67;;;6875:1;6872;6865:12;6890:437;6976:6;6984;7037:2;7025:9;7016:7;7012:23;7008:32;7005:52;;;7053:1;7050;7043:12;7005:52;7093:9;7080:23;-1:-1:-1;;;;;7118:6:25;7115:30;7112:50;;;7158:1;7155;7148:12;7112:50;7197:70;7259:7;7250:6;7239:9;7235:22;7197:70;:::i;7332:1431::-;7526:6;7534;7542;7550;7558;7566;7574;7582;7635:3;7623:9;7614:7;7610:23;7606:33;7603:53;;;7652:1;7649;7642:12;7603:53;7692:9;7679:23;-1:-1:-1;;;;;7717:6:25;7714:30;7711:50;;;7757:1;7754;7747:12;7711:50;7796:70;7858:7;7849:6;7838:9;7834:22;7796:70;:::i;:::-;7885:8;;-1:-1:-1;7770:96:25;-1:-1:-1;;7973:2:25;7958:18;;7945:32;-1:-1:-1;;;;;7989:32:25;;7986:52;;;8034:1;8031;8024:12;7986:52;8073:72;8137:7;8126:8;8115:9;8111:24;8073:72;:::i;:::-;8164:8;;-1:-1:-1;8047:98:25;-1:-1:-1;;8252:2:25;8237:18;;8224:32;-1:-1:-1;;;;;8268:32:25;;8265:52;;;8313:1;8310;8303:12;8265:52;8352:72;8416:7;8405:8;8394:9;8390:24;8352:72;:::i;:::-;8443:8;;-1:-1:-1;8326:98:25;-1:-1:-1;;8531:2:25;8516:18;;8503:32;-1:-1:-1;;;;;8547:32:25;;8544:52;;;8592:1;8589;8582:12;8544:52;8631:72;8695:7;8684:8;8673:9;8669:24;8631:72;:::i;:::-;7332:1431;;;;-1:-1:-1;7332:1431:25;;-1:-1:-1;7332:1431:25;;;;;;8722:8;-1:-1:-1;;;7332:1431:25:o;8768:186::-;8827:6;8880:2;8868:9;8859:7;8855:23;8851:32;8848:52;;;8896:1;8893;8886:12;8848:52;8919:29;8938:9;8919:29;:::i;9699:266::-;9787:6;9782:3;9775:19;9839:6;9832:5;9825:4;9820:3;9816:14;9803:43;-1:-1:-1;9891:1:25;9866:16;;;9884:4;9862:27;;;9855:38;;;;9947:2;9926:15;;;-1:-1:-1;;9922:29:25;9913:39;;;9909:50;;9699:266::o;9970:244::-;10127:2;10116:9;10109:21;10090:4;10147:61;10204:2;10193:9;10189:18;10181:6;10173;10147:61;:::i;:::-;10139:69;9970:244;-1:-1:-1;;;;9970:244:25:o;10569:485::-;10839:1;10835;10830:3;10826:11;10822:19;10814:6;10810:32;10799:9;10792:51;10879:6;10874:2;10863:9;10859:18;10852:34;10922:3;10917:2;10906:9;10902:18;10895:31;10773:4;10943:62;11000:3;10989:9;10985:19;10977:6;10969;10943:62;:::i;:::-;10935:70;;11041:6;11036:2;11025:9;11021:18;11014:34;10569:485;;;;;;;;:::o;11902:311::-;11990:19;;;11972:3;-1:-1:-1;;;;;12021:31:25;;12018:51;;;12065:1;12062;12055:12;12018:51;12101:6;12098:1;12094:14;12153:8;12146:5;12139:4;12134:3;12130:14;12117:45;12182:18;;;;12202:4;12178:29;;11902:311;-1:-1:-1;;;11902:311:25:o;12218:288::-;12407:2;12396:9;12389:21;12370:4;12427:73;12496:2;12485:9;12481:18;12473:6;12465;12427:73;:::i;12511:983::-;12964:3;12953:9;12946:22;12927:4;12991:74;13060:3;13049:9;13045:19;13037:6;13029;12991:74;:::i;:::-;13113:9;13105:6;13101:22;13096:2;13085:9;13081:18;13074:50;13147:61;13201:6;13193;13185;13147:61;:::i;:::-;13133:75;;13256:9;13248:6;13244:22;13239:2;13228:9;13224:18;13217:50;13290:61;13344:6;13336;13328;13290:61;:::i;:::-;13276:75;;13399:9;13391:6;13387:22;13382:2;13371:9;13367:18;13360:50;13427:61;13481:6;13473;13465;13427:61;:::i;:::-;13419:69;12511:983;-1:-1:-1;;;;;;;;;;;12511:983:25:o;13499:184::-;13569:6;13622:2;13610:9;13601:7;13597:23;13593:32;13590:52;;;13638:1;13635;13628:12;13590:52;-1:-1:-1;13661:16:25;;13499:184;-1:-1:-1;13499:184:25:o;13688:301::-;13817:3;13855:6;13849:13;13901:6;13894:4;13886:6;13882:17;13877:3;13871:37;13963:1;13927:16;;13952:13;;;-1:-1:-1;13927:16:25;13688:301;-1:-1:-1;13688:301:25:o
Swarm Source
ipfs://bdc88220ee10109405383043d276b2402778177b22ef92e76a2321370bbd62fd
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.