Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
Shares
Compiler Version
v0.8.15+commit.e14f2714
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BSL 1.1 - Copyright 2024 MetaLayer Labs Ltd.
pragma solidity 0.8.15;
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { Semver } from "src/universal/Semver.sol";
import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
import { Blast, YieldMode, GasMode } from "src/L2/Blast.sol";
/// @custom:predeploy 0x4300000000000000000000000000000000000000
/// @title SharesBase
/// @notice Base contract to track share rebasing and yield reporting.
abstract contract SharesBase is Initializable {
/// @notice Approved yield reporter.
address public immutable REPORTER;
/// @notice Share price. This value can only increase.
uint256 public price;
/// @notice Accumulated yield that has not been distributed
/// to the share price.
uint256 public pending;
/// @notice Reserve extra slots (to a total of 50) in the storage layout for future upgrades.
/// A gap size of 48 was chosen here, so that the first slot used in a child contract
/// would be a multiple of 50.
uint256[48] private __gap;
/// @notice Emitted when a new share price is set after a yield event.
event NewPrice(uint256 price);
error InvalidReporter();
error DistributeFailed(uint256 count, uint256 pending);
error PriceIsInitialized();
/// @param _reporter Address of the approved yield reporter.
constructor(address _reporter) {
REPORTER = _reporter;
}
/// @notice Initializer.
/// @param _price Initial share price.
// solhint-disable-next-line func-name-mixedcase
function __SharesBase_init(uint256 _price) internal onlyInitializing {
if (price != 0) {
revert PriceIsInitialized();
}
price = _price;
}
/// @notice Get the total number of shares. Needs to be
/// overridden by the child contract.
/// @return Total number of shares.
function count() public view virtual returns (uint256);
/// @notice Report a yield event and update the share price.
/// @param value Amount of new yield
function addValue(uint256 value) external {
_addValue(value);
}
function _addValue(uint256 value) internal virtual {
if (AddressAliasHelper.undoL1ToL2Alias(msg.sender) != REPORTER) {
revert InvalidReporter();
}
if (value > 0) {
pending += value;
}
_tryDistributePending();
}
/// @notice Attempt to distribute pending yields if there
/// are sufficient pending yields to increase the
/// share price.
/// @return True if there were sufficient pending yields to
/// increase the share price.
function _tryDistributePending() internal returns (bool) {
if (pending < count() || count() == 0) {
return false;
}
price += pending / count();
pending = pending % count();
emit NewPrice(price);
return true;
}
}
/// @custom:predeploy 0x4300000000000000000000000000000000000000
/// @title Shares
/// @notice Integrated EVM contract to manage native ether share
/// rebasing from yield reports.
contract Shares is SharesBase, Semver {
/// @notice Total number of shares. This value is modified directly
/// by the sequencer EVM.
uint256 private _count;
/// @notice _reporter Address of approved yield reporter.
constructor(address _reporter) SharesBase(_reporter) Semver(1, 0, 0) {
_disableInitializers();
}
/// @notice Initializer.
function initialize(uint256 _price) public initializer {
__SharesBase_init({ _price: _price });
Blast(Predeploys.BLAST).configureContract(
address(this),
YieldMode.VOID,
GasMode.VOID,
address(0xdead) /// don't set a governor
);
}
/// @inheritdoc SharesBase
function count() public view override returns (uint256) {
return _count;
}
function _addValue(uint256 value) internal override {
super._addValue(value);
SharesBase(Predeploys.WETH_REBASING).addValue(value);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @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 Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 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 functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_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 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_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() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @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 {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}// SPDX-License-Identifier: BSL 1.1 - Copyright 2024 MetaLayer Labs Ltd.
pragma solidity ^0.8.0;
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
/// @title Semver
/// @notice Semver is a simple contract for managing contract versions.
contract Semver {
/// @notice Contract version number (major).
uint256 private immutable MAJOR_VERSION;
/// @notice Contract version number (minor).
uint256 private immutable MINOR_VERSION;
/// @notice Contract version number (patch).
uint256 private immutable PATCH_VERSION;
/// @param _major Version number (major).
/// @param _minor Version number (minor).
/// @param _patch Version number (patch).
constructor(uint256 _major, uint256 _minor, uint256 _patch) {
MAJOR_VERSION = _major;
MINOR_VERSION = _minor;
PATCH_VERSION = _patch;
}
/// @notice Returns the full semver contract version.
/// @return Semver contract version as a string.
function version() public view returns (string memory) {
return string(
abi.encodePacked(
Strings.toString(MAJOR_VERSION),
".",
Strings.toString(MINOR_VERSION),
".",
Strings.toString(PATCH_VERSION)
)
);
}
}// SPDX-License-Identifier: Apache-2.0
/*
* Copyright 2019-2021, Offchain Labs, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
pragma solidity ^0.8.0;
library AddressAliasHelper {
uint160 constant offset = uint160(0x1111000000000000000000000000000000001111);
/// @notice Utility function that converts the address in the L1 that submitted a tx to
/// the inbox to the msg.sender viewed in the L2
/// @param l1Address the address in the L1 that triggered the tx to L2
/// @return l2Address L2 address as viewed in msg.sender
function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) {
unchecked {
l2Address = address(uint160(l1Address) + offset);
}
}
/// @notice Utility function that converts the msg.sender viewed in the L2 to the
/// address in the L1 that submitted a tx to the inbox
/// @param l2Address L2 address as viewed in msg.sender
/// @return l1Address the address in the L1 that triggered the tx to L2
function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) {
unchecked {
l1Address = address(uint160(l2Address) - offset);
}
}
}// SPDX-License-Identifier: BSL 1.1 - Copyright 2024 MetaLayer Labs Ltd.
pragma solidity ^0.8.0;
/// @title Predeploys
/// @notice Contains constant addresses for contracts that are pre-deployed to the L2 system.
library Predeploys {
/// @notice Address of the L2ToL1MessagePasser predeploy.
address internal constant L2_TO_L1_MESSAGE_PASSER = 0x4200000000000000000000000000000000000016;
/// @notice Address of the L2CrossDomainMessenger predeploy.
address internal constant L2_CROSS_DOMAIN_MESSENGER = 0x4200000000000000000000000000000000000007;
/// @notice Address of the L2StandardBridge predeploy.
address internal constant L2_STANDARD_BRIDGE = 0x4200000000000000000000000000000000000010;
/// @notice Address of the L2ERC721Bridge predeploy.
address internal constant L2_ERC721_BRIDGE = 0x4200000000000000000000000000000000000014;
//// @notice Address of the SequencerFeeWallet predeploy.
address internal constant SEQUENCER_FEE_WALLET = 0x4200000000000000000000000000000000000011;
/// @notice Address of the OptimismMintableERC20Factory predeploy.
address internal constant OPTIMISM_MINTABLE_ERC20_FACTORY = 0x4200000000000000000000000000000000000012;
/// @notice Address of the OptimismMintableERC721Factory predeploy.
address internal constant OPTIMISM_MINTABLE_ERC721_FACTORY = 0x4200000000000000000000000000000000000017;
/// @notice Address of the L1Block predeploy.
address internal constant L1_BLOCK_ATTRIBUTES = 0x4200000000000000000000000000000000000015;
/// @notice Address of the GasPriceOracle predeploy. Includes fee information
/// and helpers for computing the L1 portion of the transaction fee.
address internal constant GAS_PRICE_ORACLE = 0x420000000000000000000000000000000000000F;
/// @custom:legacy
/// @notice Address of the L1MessageSender predeploy. Deprecated. Use L2CrossDomainMessenger
/// or access tx.origin (or msg.sender) in a L1 to L2 transaction instead.
address internal constant L1_MESSAGE_SENDER = 0x4200000000000000000000000000000000000001;
/// @custom:legacy
/// @notice Address of the DeployerWhitelist predeploy. No longer active.
address internal constant DEPLOYER_WHITELIST = 0x4200000000000000000000000000000000000002;
/// @custom:legacy
/// @notice Address of the LegacyERC20ETH predeploy. Deprecated. Balances are migrated to the
/// state trie as of the Bedrock upgrade. Contract has been locked and write functions
/// can no longer be accessed.
address internal constant LEGACY_ERC20_ETH = 0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000;
/// @custom:legacy
/// @notice Address of the L1BlockNumber predeploy. Deprecated. Use the L1Block predeploy
/// instead, which exposes more information about the L1 state.
address internal constant L1_BLOCK_NUMBER = 0x4200000000000000000000000000000000000013;
/// @custom:legacy
/// @notice Address of the LegacyMessagePasser predeploy. Deprecate. Use the updated
/// L2ToL1MessagePasser contract instead.
address internal constant LEGACY_MESSAGE_PASSER = 0x4200000000000000000000000000000000000000;
/// @notice Address of the ProxyAdmin predeploy.
address internal constant PROXY_ADMIN = 0x4200000000000000000000000000000000000018;
/// @notice Address of the BaseFeeVault predeploy.
address internal constant BASE_FEE_VAULT = 0x4200000000000000000000000000000000000019;
/// @notice Address of the L1FeeVault predeploy.
address internal constant L1_FEE_VAULT = 0x420000000000000000000000000000000000001A;
/// @notice Address of the GovernanceToken predeploy.
address internal constant GOVERNANCE_TOKEN = 0x4200000000000000000000000000000000000042;
/// @notice Address of the SchemaRegistry predeploy.
address internal constant SCHEMA_REGISTRY = 0x4200000000000000000000000000000000000020;
/// @notice Address of the EAS predeploy.
address internal constant EAS = 0x4200000000000000000000000000000000000021;
/// @notice Address of the Shares predeploy.
address internal constant SHARES = 0x4300000000000000000000000000000000000000;
/// @notice Address of the Gas predeploy.
address internal constant GAS = 0x4300000000000000000000000000000000000001;
/// @notice Address of the Blast predeploy.
address internal constant BLAST = 0x4300000000000000000000000000000000000002;
/// @notice Address of the USDB predeploy.
address internal constant USDB = 0x4300000000000000000000000000000000000003;
/// @notice Address of the WETH predeploy.
address internal constant WETH_REBASING = 0x4300000000000000000000000000000000000004;
/// @notice Address of the L2BlastBridge predeploy.
address internal constant L2_BLAST_BRIDGE = 0x4300000000000000000000000000000000000005;
}// SPDX-License-Identifier: BSL 1.1 - Copyright 2024 MetaLayer Labs Ltd.
pragma solidity 0.8.15;
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { Semver } from "src/universal/Semver.sol";
import { GasMode, IGas } from "src/L2/Gas.sol";
enum YieldMode {
AUTOMATIC,
VOID,
CLAIMABLE
}
interface IYield {
function configure(address contractAddress, uint8 flags) external returns (uint256);
function claim(address contractAddress, address recipientOfYield, uint256 desiredAmount) external returns (uint256);
function getClaimableAmount(address contractAddress) external view returns (uint256);
function getConfiguration(address contractAddress) external view returns (uint8);
}
interface IBlast{
// configure
function configureContract(address contractAddress, YieldMode _yield, GasMode gasMode, address governor) external;
function configure(YieldMode _yield, GasMode gasMode, address governor) external;
// base configuration options
function configureClaimableYield() external;
function configureClaimableYieldOnBehalf(address contractAddress) external;
function configureAutomaticYield() external;
function configureAutomaticYieldOnBehalf(address contractAddress) external;
function configureVoidYield() external;
function configureVoidYieldOnBehalf(address contractAddress) external;
function configureClaimableGas() external;
function configureClaimableGasOnBehalf(address contractAddress) external;
function configureVoidGas() external;
function configureVoidGasOnBehalf(address contractAddress) external;
function configureGovernor(address _governor) external;
function configureGovernorOnBehalf(address _newGovernor, address contractAddress) external;
// claim yield
function claimYield(address contractAddress, address recipientOfYield, uint256 amount) external returns (uint256);
function claimAllYield(address contractAddress, address recipientOfYield) external returns (uint256);
// claim gas
function claimAllGas(address contractAddress, address recipientOfGas) external returns (uint256);
// NOTE: can be off by 1 bip
function claimGasAtMinClaimRate(address contractAddress, address recipientOfGas, uint256 minClaimRateBips) external returns (uint256);
function claimMaxGas(address contractAddress, address recipientOfGas) external returns (uint256);
function claimGas(address contractAddress, address recipientOfGas, uint256 gasToClaim, uint256 gasSecondsToConsume) external returns (uint256);
// read functions
function readClaimableYield(address contractAddress) external view returns (uint256);
function readYieldConfiguration(address contractAddress) external view returns (uint8);
function readGasParams(address contractAddress) external view returns (uint256 etherSeconds, uint256 etherBalance, uint256 lastUpdated, GasMode);
}
/// @custom:predeploy 0x4300000000000000000000000000000000000002
/// @title Blast
contract Blast is IBlast, Initializable, Semver {
address public immutable YIELD_CONTRACT;
address public immutable GAS_CONTRACT;
mapping(address => address) public governorMap;
constructor(address _gasContract, address _yieldContract) Semver(1, 0, 0) {
GAS_CONTRACT = _gasContract;
YIELD_CONTRACT = _yieldContract;
_disableInitializers();
}
function initialize() public initializer {}
/**
* @notice Checks if the caller is the governor of the contract
* @param contractAddress The address of the contract
* @return A boolean indicating if the caller is the governor
*/
function isGovernor(address contractAddress) public view returns (bool) {
return msg.sender == governorMap[contractAddress];
}
/**
* @notice Checks if the governor is not set for the contract
* @param contractAddress The address of the contract
* @return boolean indicating if the governor is not set
*/
function governorNotSet(address contractAddress) internal view returns (bool) {
return governorMap[contractAddress] == address(0);
}
/**
* @notice Checks if the caller is authorized
* @param contractAddress The address of the contract
* @return A boolean indicating if the caller is authorized
*/
function isAuthorized(address contractAddress) public view returns (bool) {
return isGovernor(contractAddress) || (governorNotSet(contractAddress) && msg.sender == contractAddress);
}
/**
* @notice contract configures its yield and gas modes and sets the governor. called by contract
* @param _yieldMode The yield mode to be set
* @param _gasMode The gas mode to be set
* @param governor The address of the governor to be set
*/
function configure(YieldMode _yieldMode, GasMode _gasMode, address governor) external {
// requires that no governor is set for contract
require(isAuthorized(msg.sender), "not authorized to configure contract");
// set governor
governorMap[msg.sender] = governor;
// set gas mode
IGas(GAS_CONTRACT).setGasMode(msg.sender, _gasMode);
// set yield mode
IYield(YIELD_CONTRACT).configure(msg.sender, uint8(_yieldMode));
}
/**
* @notice Configures the yield and gas modes and sets the governor for a specific contract. called by authorized user
* @param contractAddress The address of the contract to be configured
* @param _yieldMode The yield mode to be set
* @param _gasMode The gas mode to be set
* @param _newGovernor The address of the new governor to be set
*/
function configureContract(address contractAddress, YieldMode _yieldMode, GasMode _gasMode, address _newGovernor) external {
// only allow governor, or if no governor is set, the contract itself to configure
require(isAuthorized(contractAddress), "not authorized to configure contract");
// set governor
governorMap[contractAddress] = _newGovernor;
// set gas mode
IGas(GAS_CONTRACT).setGasMode(contractAddress, _gasMode);
// set yield mode
IYield(YIELD_CONTRACT).configure(contractAddress, uint8(_yieldMode));
}
/**
* @notice Configures the yield mode to CLAIMABLE for the contract that calls this function
*/
function configureClaimableYield() external {
require(isAuthorized(msg.sender), "not authorized to configure contract");
IYield(YIELD_CONTRACT).configure(msg.sender, uint8(YieldMode.CLAIMABLE));
}
/**
* @notice Configures the yield mode to CLAIMABLE for a specific contract. Called by an authorized user
* @param contractAddress The address of the contract to be configured
*/
function configureClaimableYieldOnBehalf(address contractAddress) external {
require(isAuthorized(contractAddress), "not authorized to configure contract");
IYield(YIELD_CONTRACT).configure(contractAddress, uint8(YieldMode.CLAIMABLE));
}
/**
* @notice Configures the yield mode to AUTOMATIC for the contract that calls this function
*/
function configureAutomaticYield() external {
require(isAuthorized(msg.sender), "not authorized to configure contract");
IYield(YIELD_CONTRACT).configure(msg.sender, uint8(YieldMode.AUTOMATIC));
}
/**
* @notice Configures the yield mode to AUTOMATIC for a specific contract. Called by an authorized user
* @param contractAddress The address of the contract to be configured
*/
function configureAutomaticYieldOnBehalf(address contractAddress) external {
require(isAuthorized(contractAddress), "not authorized to configure contract");
IYield(YIELD_CONTRACT).configure(contractAddress, uint8(YieldMode.AUTOMATIC));
}
/**
* @notice Configures the yield mode to VOID for the contract that calls this function
*/
function configureVoidYield() external {
require(isAuthorized(msg.sender), "not authorized to configure contract");
IYield(YIELD_CONTRACT).configure(msg.sender, uint8(YieldMode.VOID));
}
/**
* @notice Configures the yield mode to VOID for a specific contract. Called by an authorized user
* @param contractAddress The address of the contract to be configured
*/
function configureVoidYieldOnBehalf(address contractAddress) external {
require(isAuthorized(contractAddress), "not authorized to configure contract");
IYield(YIELD_CONTRACT).configure(contractAddress, uint8(YieldMode.VOID));
}
/**
* @notice Configures the gas mode to CLAIMABLE for the contract that calls this function
*/
function configureClaimableGas() external {
require(isAuthorized(msg.sender), "not authorized to configure contract");
IGas(GAS_CONTRACT).setGasMode(msg.sender, GasMode.CLAIMABLE);
}
/**
* @notice Configures the gas mode to CLAIMABLE for a specific contract. Called by an authorized user
* @param contractAddress The address of the contract to be configured
*/
function configureClaimableGasOnBehalf(address contractAddress) external {
require(isAuthorized(contractAddress), "not authorized to configure contract");
IGas(GAS_CONTRACT).setGasMode(contractAddress, GasMode.CLAIMABLE);
}
/**
* @notice Configures the gas mode to VOID for the contract that calls this function
*/
function configureVoidGas() external {
require(isAuthorized(msg.sender), "not authorized to configure contract");
IGas(GAS_CONTRACT).setGasMode(msg.sender, GasMode.VOID);
}
/**
* @notice Configures the gas mode to void for a specific contract. Called by an authorized user
* @param contractAddress The address of the contract to be configured
*/
function configureVoidGasOnBehalf(address contractAddress) external {
require(isAuthorized(contractAddress), "not authorized to configure contract");
IGas(GAS_CONTRACT).setGasMode(contractAddress, GasMode.VOID);
}
/**
* @notice Configures the governor for the contract that calls this function
*/
function configureGovernor(address _governor) external {
require(isAuthorized(msg.sender), "not authorized to configure contract");
governorMap[msg.sender] = _governor;
}
/**
* @notice Configures the governor for a specific contract. Called by an authorized user
* @param contractAddress The address of the contract to be configured
*/
function configureGovernorOnBehalf(address _newGovernor, address contractAddress) external {
require(isAuthorized(contractAddress), "not authorized to configure contract");
governorMap[contractAddress] = _newGovernor;
}
// claim methods
/**
* @notice Claims yield for a specific contract. Called by an authorized user
* @param contractAddress The address of the contract for which yield is to be claimed
* @param recipientOfYield The address of the recipient of the yield
* @param amount The amount of yield to be claimed
* @return The amount of yield that was claimed
*/
function claimYield(address contractAddress, address recipientOfYield, uint256 amount) external returns (uint256) {
require(isAuthorized(contractAddress), "Not authorized to claim yield");
return IYield(YIELD_CONTRACT).claim(contractAddress, recipientOfYield, amount);
}
/**
* @notice Claims all yield for a specific contract. Called by an authorized user
* @param contractAddress The address of the contract for which all yield is to be claimed
* @param recipientOfYield The address of the recipient of the yield
* @return The amount of yield that was claimed
*/
function claimAllYield(address contractAddress, address recipientOfYield) external returns (uint256) {
require(isAuthorized(contractAddress), "Not authorized to claim yield");
uint256 amount = IYield(YIELD_CONTRACT).getClaimableAmount(contractAddress);
return IYield(YIELD_CONTRACT).claim(contractAddress, recipientOfYield, amount);
}
/**
* @notice Claims all gas for a specific contract. Called by an authorized user
* @param contractAddress The address of the contract for which all gas is to be claimed
* @param recipientOfGas The address of the recipient of the gas
* @return The amount of gas that was claimed
*/
function claimAllGas(address contractAddress, address recipientOfGas) external returns (uint256) {
require(isAuthorized(contractAddress), "Not allowed to claim all gas");
return IGas(GAS_CONTRACT).claimAll(contractAddress, recipientOfGas);
}
/**
* @notice Claims gas at a minimum claim rate for a specific contract, with error rate '1'. Called by an authorized user
* @param contractAddress The address of the contract for which gas is to be claimed
* @param recipientOfGas The address of the recipient of the gas
* @param minClaimRateBips The minimum claim rate in basis points
* @return The amount of gas that was claimed
*/
function claimGasAtMinClaimRate(address contractAddress, address recipientOfGas, uint256 minClaimRateBips) external returns (uint256) {
require(isAuthorized(contractAddress), "Not allowed to claim gas at min claim rate");
return IGas(GAS_CONTRACT).claimGasAtMinClaimRate(contractAddress, recipientOfGas, minClaimRateBips);
}
/**
* @notice Claims gas available to be claimed at max claim rate for a specific contract. Called by an authorized user
* @param contractAddress The address of the contract for which maximum gas is to be claimed
* @param recipientOfGas The address of the recipient of the gas
* @return The amount of gas that was claimed
*/
function claimMaxGas(address contractAddress, address recipientOfGas) external returns (uint256) {
require(isAuthorized(contractAddress), "Not allowed to claim max gas");
return IGas(GAS_CONTRACT).claimMax(contractAddress, recipientOfGas);
}
/**
* @notice Claims a specific amount of gas for a specific contract. claim rate governed by integral of gas over time
* @param contractAddress The address of the contract for which gas is to be claimed
* @param recipientOfGas The address of the recipient of the gas
* @param gasToClaim The amount of gas to be claimed
* @param gasSecondsToConsume The amount of gas seconds to consume
* @return The amount of gas that was claimed
*/
function claimGas(address contractAddress, address recipientOfGas, uint256 gasToClaim, uint256 gasSecondsToConsume) external returns (uint256) {
require(isAuthorized(contractAddress), "Not allowed to claim gas");
return IGas(GAS_CONTRACT).claim(contractAddress, recipientOfGas, gasToClaim, gasSecondsToConsume);
}
/**
* @notice Reads the claimable yield for a specific contract
* @param contractAddress The address of the contract for which the claimable yield is to be read
* @return claimable yield
*/
function readClaimableYield(address contractAddress) public view returns (uint256) {
return IYield(YIELD_CONTRACT).getClaimableAmount(contractAddress);
}
/**
* @notice Reads the yield configuration for a specific contract
* @param contractAddress The address of the contract for which the yield configuration is to be read
* @return uint8 representing yield enum
*/
function readYieldConfiguration(address contractAddress) public view returns (uint8) {
return IYield(YIELD_CONTRACT).getConfiguration(contractAddress);
}
/**
* @notice Reads the gas parameters for a specific contract
* @param contractAddress The address of the contract for which the gas parameters are to be read
* @return uint256 representing the accumulated ether seconds
* @return uint256 representing ether balance
* @return uint256 representing last update timestamp
* @return GasMode representing the gas mode (VOID, CLAIMABLE)
*/
function readGasParams(address contractAddress) public view returns (uint256, uint256, uint256, GasMode) {
return IGas(GAS_CONTRACT).readGasParams(contractAddress);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @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.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @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, it is bubbled up by this
* function (like regular Solidity function calls).
*
* 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.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @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`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) 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
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
}// SPDX-License-Identifier: BSL 1.1 - Copyright 2024 MetaLayer Labs Ltd.
pragma solidity 0.8.15;
import { SafeTransferLib } from "solmate/utils/SafeTransferLib.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { Semver } from "src/universal/Semver.sol";
enum GasMode {
VOID,
CLAIMABLE
}
interface IGas {
function readGasParams(address contractAddress) external view returns (uint256, uint256, uint256, GasMode);
function setGasMode(address contractAddress, GasMode mode) external;
function claimGasAtMinClaimRate(address contractAddress, address recipient, uint256 minClaimRateBips) external returns (uint256);
function claimAll(address contractAddress, address recipient) external returns (uint256);
function claimMax(address contractAddress, address recipient) external returns (uint256);
function claim(address contractAddress, address recipient, uint256 gasToClaim, uint256 gasSecondsToConsume) external returns (uint256);
}
/// @custom:predeploy 0x4300000000000000000000000000000000000001
/// @title Gas
contract Gas is IGas, Initializable, Semver {
address public immutable admin;
// Blast.sol --> controls all dAPP accesses to Gas.sol
address public immutable blastConfigurationContract;
// BaseFeeVault.sol -> fees from gas claims directed here
address public immutable blastFeeVault;
// zero claim rate in bps -> percent of gas user is able to claim
// without consuming any gas seconds
uint256 public zeroClaimRate; // bps
// base claim rate in bps -> percent of gas user is able to claim
// by consuming base gas seconds
uint256 public baseGasSeconds;
uint256 public baseClaimRate; // bps
// ceil claim rate in bps -> percent of gas user is able to claim
// by consuming ceil gas seconds or more
uint256 public ceilGasSeconds;
uint256 public ceilClaimRate; // bps
/**
* @notice Constructs the blast gas contract.
* @param _admin The address of the admin.
* @param _blastConfigurationContract The address of the Blast configuration contract.
* @param _blastFeeVault The address of the Blast fee vault.
*/
constructor (
address _admin,
address _blastConfigurationContract,
address _blastFeeVault
) Semver(1, 0, 0) {
admin = _admin;
blastConfigurationContract = _blastConfigurationContract;
blastFeeVault = _blastFeeVault;
_disableInitializers();
}
/**
* @notice Initializer.
* @param _zeroClaimRate The zero claim rate.
* @param _baseGasSeconds The base gas seconds.
* @param _baseClaimRate The base claim rate.
* @param _ceilGasSeconds The ceiling gas seconds.
* @param _ceilClaimRate The ceiling claim rate.
*/
function initialize(
uint256 _zeroClaimRate,
uint256 _baseGasSeconds,
uint256 _baseClaimRate,
uint256 _ceilGasSeconds,
uint256 _ceilClaimRate
) public initializer {
require(_zeroClaimRate < _baseClaimRate, "zero claim rate must be < base claim rate");
require(_baseClaimRate < _ceilClaimRate, "base claim rate must be < ceil claim rate");
require(_baseGasSeconds < _ceilGasSeconds, "base gas seconds must be < ceil gas seconds");
require(_baseGasSeconds > 0, "base gas seconds must be > 0");
require(_ceilClaimRate <= 10000, "ceil claim rate must be less than or equal to 10_000 bips");
// admin vars
zeroClaimRate = _zeroClaimRate;
baseGasSeconds = _baseGasSeconds;
baseClaimRate = _baseClaimRate;
ceilGasSeconds = _ceilGasSeconds;
ceilClaimRate = _ceilClaimRate;
}
/**
* @notice Allows only the admin to call a function
*/
modifier onlyAdmin() {
require(msg.sender == admin, "Caller is not the admin");
_;
}
/**
* @notice Allows only the Blast Configuration Contract to call a function
*/
modifier onlyBlastConfigurationContract() {
require(msg.sender == blastConfigurationContract, "Caller must be blast configuration contract");
_;
}
/**
* @notice Allows the admin to update the parameters
* @param _zeroClaimRate The new zero claim rate
* @param _baseGasSeconds The new base gas seconds
* @param _baseClaimRate The new base claim rate
* @param _ceilGasSeconds The new ceiling gas seconds
* @param _ceilClaimRate The new ceiling claim rate
*/
function updateAdminParameters(
uint256 _zeroClaimRate,
uint256 _baseGasSeconds,
uint256 _baseClaimRate,
uint256 _ceilGasSeconds,
uint256 _ceilClaimRate
) external onlyAdmin {
require(_zeroClaimRate < _baseClaimRate, "zero claim rate must be < base claim rate");
require(_baseClaimRate < _ceilClaimRate, "base claim rate must be < ceil claim rate");
require(_baseGasSeconds < _ceilGasSeconds, "base gas seconds must be < ceil gas seconds");
require(_baseGasSeconds > 0, "base gas seconds must be > 0");
require(_ceilClaimRate <= 10000, "ceil claim rate must be less than or equal to 10_000 bips");
zeroClaimRate = _zeroClaimRate;
baseGasSeconds = _baseGasSeconds;
baseClaimRate = _baseClaimRate;
ceilGasSeconds = _ceilGasSeconds;
ceilClaimRate = _ceilClaimRate;
}
/**
* @notice Allows the admin to claim the gas of any address
* @param contractAddress The address of the contract
* @return The amount of ether balance claimed
*/
function adminClaimGas(address contractAddress) external onlyAdmin returns (uint256) {
(, uint256 etherBalance,,) = readGasParams(contractAddress);
_updateGasParams(contractAddress, 0, 0, GasMode.VOID);
SafeTransferLib.safeTransferETH(blastFeeVault, etherBalance);
return etherBalance;
}
/**
* @notice Allows an authorized user to set the gas mode for a contract via the BlastConfigurationContract
* @param contractAddress The address of the contract
* @param mode The new gas mode for the contract
*/
function setGasMode(address contractAddress, GasMode mode) external onlyBlastConfigurationContract {
// retrieve gas params
(uint256 etherSeconds, uint256 etherBalance,,) = readGasParams(contractAddress);
_updateGasParams(contractAddress, etherSeconds, etherBalance, mode);
}
/**
* @notice Allows a user to claim gas at a minimum claim rate (error = 1 bip)
* @param contractAddress The address of the contract
* @param recipientOfGas The address of the recipient of the gas
* @param minClaimRateBips The minimum claim rate in basis points
* @return The amount of gas claimed
*/
function claimGasAtMinClaimRate(address contractAddress, address recipientOfGas, uint256 minClaimRateBips) public returns (uint256) {
require(minClaimRateBips <= ceilClaimRate, "desired claim rate exceeds maximum");
(uint256 etherSeconds, uint256 etherBalance,,) = readGasParams(contractAddress);
if (minClaimRateBips <= zeroClaimRate) {
return claimAll(contractAddress, recipientOfGas);
}
// set minClaimRate to baseClaimRate in this case
if (minClaimRateBips < baseClaimRate) {
minClaimRateBips = baseClaimRate;
}
uint256 bipsDiff = minClaimRateBips - baseClaimRate;
uint256 secondsDiff = ceilGasSeconds - baseGasSeconds;
uint256 rateDiff = ceilClaimRate - baseClaimRate;
uint256 minSecondsStaked = baseGasSeconds + Math.ceilDiv(bipsDiff * secondsDiff, rateDiff);
uint256 maxEtherClaimable = etherSeconds / minSecondsStaked;
if (maxEtherClaimable > etherBalance) {
maxEtherClaimable = etherBalance;
}
uint256 secondsToConsume = maxEtherClaimable * minSecondsStaked;
return claim(contractAddress, recipientOfGas, maxEtherClaimable, secondsToConsume);
}
/**
* @notice Allows a contract to claim all gas
* @param contractAddress The address of the contract
* @param recipientOfGas The address of the recipient of the gas
* @return The amount of gas claimed
*/
function claimAll(address contractAddress, address recipientOfGas) public returns (uint256) {
(uint256 etherSeconds, uint256 etherBalance,,) = readGasParams(contractAddress);
return claim(contractAddress, recipientOfGas, etherBalance, etherSeconds);
}
/**
* @notice Allows a contract to claim all gas at the highest possible claim rate
* @param contractAddress The address of the contract
* @param recipientOfGas The address of the recipient of the gas
* @return The amount of gas claimed
*/
function claimMax(address contractAddress, address recipientOfGas) public returns (uint256) {
return claimGasAtMinClaimRate(contractAddress, recipientOfGas, ceilClaimRate);
}
/**
* @notice Allows a contract to claim a specified amount of gas, at a claim rate set by the number of gas seconds
* @param contractAddress The address of the contract
* @param recipientOfGas The address of the recipient of the gas
* @param gasToClaim The amount of gas to claim
* @param gasSecondsToConsume The amount of gas seconds to consume
* @return The amount of gas claimed (gasToClaim - penalty)
*/
function claim(address contractAddress, address recipientOfGas, uint256 gasToClaim, uint256 gasSecondsToConsume) public onlyBlastConfigurationContract() returns (uint256) {
// retrieve gas params
(uint256 etherSeconds, uint256 etherBalance,, GasMode mode) = readGasParams(contractAddress);
// check validity requirements
require(gasToClaim > 0, "must withdraw non-zero amount");
require(gasToClaim <= etherBalance, "too much to withdraw");
require(gasSecondsToConsume <= etherSeconds, "not enough gas seconds");
// get claim rate
(uint256 claimRate, uint256 gasSecondsToConsumeNormalized) = getClaimRateBps(gasSecondsToConsume, gasToClaim);
// calculate tax
uint256 userEther = gasToClaim * claimRate / 10_000;
uint256 penalty = gasToClaim - userEther;
_updateGasParams(contractAddress, etherSeconds - gasSecondsToConsumeNormalized, etherBalance - gasToClaim, mode);
SafeTransferLib.safeTransferETH(recipientOfGas, userEther);
if (penalty > 0) {
SafeTransferLib.safeTransferETH(blastFeeVault, penalty);
}
return userEther;
}
/**
* @notice Calculates the claim rate in basis points based on gasSeconds, gasToClaim
* @param gasSecondsToConsume The amount of gas seconds to consume
* @param gasToClaim The amount of gas to claim
* @return claimRate The calculated claim rate in basis points
* @return gasSecondsToConsume The normalized gas seconds to consume (<= gasSecondsToConsume)
*/
function getClaimRateBps(uint256 gasSecondsToConsume, uint256 gasToClaim) public view returns (uint256, uint256) {
uint256 secondsStaked = gasSecondsToConsume / gasToClaim;
if (secondsStaked < baseGasSeconds) {
return (zeroClaimRate, 0);
}
if (secondsStaked >= ceilGasSeconds) {
uint256 gasToConsumeNormalized = gasToClaim * ceilGasSeconds;
return (ceilClaimRate, gasToConsumeNormalized);
}
uint256 rateDiff = ceilClaimRate - baseClaimRate;
uint256 secondsDiff = ceilGasSeconds - baseGasSeconds;
uint256 secondsStakedDiff = secondsStaked - baseGasSeconds;
uint256 additionalClaimRate = rateDiff * secondsStakedDiff / secondsDiff;
uint256 claimRate = baseClaimRate + additionalClaimRate;
return (claimRate, gasSecondsToConsume);
}
/**
* @notice Reads the gas parameters for a given user
* @param user The address of the user
* @return etherSeconds The integral of ether over time (ether * seconds vested)
* @return etherBalance The total ether balance for the user
* @return lastUpdated The last updated timestamp for the user's gas parameters
* @return mode The current gas mode for the user
*/
function readGasParams(address user) public view returns (uint256 etherSeconds, uint256 etherBalance, uint256 lastUpdated, GasMode mode) {
bytes32 paramsHash = keccak256(abi.encodePacked(user, "parameters"));
bytes32 packedParams;
// read params
assembly {
packedParams := sload(paramsHash)
}
// unpack params
// - The first byte (most significant byte) represents the mode
// - The next 12 bytes represent the etherBalance
// - The following 15 bytes represent the etherSeconds
// - The last 4 bytes (least significant bytes) represent the lastUpdated timestamp
mode = GasMode(uint8(packedParams[0]));
etherBalance = uint256((packedParams << (1 * 8)) >> ((32 - 12) * 8));
etherSeconds = uint256((packedParams << ((1 + 12) * 8)) >> ((32 - 15) * 8));
lastUpdated = uint256((packedParams << ((1 + 12 + 15) * 8)) >> ((32 - 4) * 8));
// update ether seconds
etherSeconds = etherSeconds + etherBalance * (block.timestamp - lastUpdated);
}
/**
* @notice Updates the gas parameters for a given contract address
* @param contractAddress The address of the contract
* @param etherSeconds The integral of ether over time (ether * seconds vested)
* @param etherBalance The total ether balance for the contract
*/
function _updateGasParams(address contractAddress, uint256 etherSeconds, uint256 etherBalance, GasMode mode) internal {
if (
etherBalance >= 1 << (12 * 8) ||
etherSeconds >= 1 << (15 * 8)
) {
revert("Unexpected packing issue due to overflow");
}
uint256 updatedTimestamp = block.timestamp; // Known to fit in 4 bytes
bytes32 paramsHash = keccak256(abi.encodePacked(contractAddress, "parameters"));
bytes32 packedParams;
packedParams = (
(bytes32(uint256(mode)) << ((12 + 15 + 4) * 8)) | // Shift mode to the most significant byte
(bytes32(etherBalance) << ((15 + 4) * 8)) | // Shift etherBalance to start after 1 byte of mode
(bytes32(etherSeconds) << (4 * 8)) | // Shift etherSeconds to start after mode and etherBalance
bytes32(updatedTimestamp) // Keep updatedTimestamp in the least significant bytes
);
assembly {
sstore(paramsHash, packedParams)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller.
library SafeTransferLib {
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address to, uint256 amount) internal {
bool success;
assembly {
// Transfer the ETH and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
assembly {
// We'll write our calldata to this slot below, but restore it later.
let memPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(4, from) // Append the "from" argument.
mstore(36, to) // Append the "to" argument.
mstore(68, amount) // Append the "amount" argument.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because that's the total length of our calldata (4 + 32 * 3)
// Counterintuitively, this call() must be positioned after the or() in the
// surrounding and() because and() evaluates its arguments from right to left.
call(gas(), token, 0, 0, 100, 0, 32)
)
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, memPointer) // Restore the memPointer.
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
// We'll write our calldata to this slot below, but restore it later.
let memPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(0, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(4, to) // Append the "to" argument.
mstore(36, amount) // Append the "amount" argument.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because that's the total length of our calldata (4 + 32 * 2)
// Counterintuitively, this call() must be positioned after the or() in the
// surrounding and() because and() evaluates its arguments from right to left.
call(gas(), token, 0, 0, 68, 0, 32)
)
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, memPointer) // Restore the memPointer.
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
// We'll write our calldata to this slot below, but restore it later.
let memPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(0, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(4, to) // Append the "to" argument.
mstore(36, amount) // Append the "amount" argument.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because that's the total length of our calldata (4 + 32 * 2)
// Counterintuitively, this call() must be positioned after the or() in the
// surrounding and() because and() evaluates its arguments from right to left.
call(gas(), token, 0, 0, 68, 0, 32)
)
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, memPointer) // Restore the memPointer.
}
require(success, "APPROVE_FAILED");
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. It the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`.
// We also know that `k`, the position of the most significant bit, is such that `msb(a) = 2**k`.
// This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`.
// Using an algorithm similar to the msb conmputation, we are able to compute `result = 2**(k/2)` which is a
// good first aproximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1;
uint256 x = a;
if (x >> 128 > 0) {
x >>= 128;
result <<= 64;
}
if (x >> 64 > 0) {
x >>= 64;
result <<= 32;
}
if (x >> 32 > 0) {
x >>= 32;
result <<= 16;
}
if (x >> 16 > 0) {
x >>= 16;
result <<= 8;
}
if (x >> 8 > 0) {
x >>= 8;
result <<= 4;
}
if (x >> 4 > 0) {
x >>= 4;
result <<= 2;
}
if (x >> 2 > 0) {
result <<= 1;
}
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
uint256 result = sqrt(a);
if (rounding == Rounding.Up && result * result < a) {
result += 1;
}
return result;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}{
"remappings": [
"@cwia/=lib/clones-with-immutable-args/src/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@rari-capital/solmate/=lib/solmate/",
"clones-with-immutable-args/=lib/clones-with-immutable-args/src/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/",
"safe-contracts/=lib/safe-contracts/contracts/",
"solmate/=lib/solmate/src/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"bytecodeHash": "none"
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
}
},
"evmVersion": "london",
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_reporter","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"count","type":"uint256"},{"internalType":"uint256","name":"pending","type":"uint256"}],"name":"DistributeFailed","type":"error"},{"inputs":[],"name":"InvalidReporter","type":"error"},{"inputs":[],"name":"PriceIsInitialized","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"}],"name":"NewPrice","type":"event"},{"inputs":[],"name":"REPORTER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"addValue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"count","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pending","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"price","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]Contract Creation Code
608060405234801561001057600080fd5b506004361061007d5760003560e01c80637ae556b51161005b5780637ae556b5146100c3578063a035b1fe14610102578063e20ccec31461010b578063fe4b84df1461011457600080fd5b806306661abd1461008257806354fd4d50146100995780635b9af12b146100ae575b600080fd5b6033545b6040519081526020015b60405180910390f35b6100a1610127565b60405161009091906106a6565b6100c16100bc3660046106d9565b6101ca565b005b6100ea7f00000000000000000000000098078db053902644191f93988341e31289e1c8fe81565b6040516001600160a01b039091168152602001610090565b61008660015481565b61008660025481565b6100c16101223660046106d9565b6101d6565b60606101527f0000000000000000000000000000000000000000000000000000000000000001610354565b61017b7f0000000000000000000000000000000000000000000000000000000000000000610354565b6101a47f0000000000000000000000000000000000000000000000000000000000000000610354565b6040516020016101b6939291906106f2565b604051602081830303815290604052905090565b6101d38161045d565b50565b600054610100900460ff16158080156101f65750600054600160ff909116105b806102105750303b158015610210575060005460ff166001145b6102785760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff19166001179055801561029b576000805461ff0019166101001790555b6102a4826104c1565b60405163099005e760e31b81526002604360981b0190634c802f38906102d890309060019060009061dead90600401610762565b600060405180830381600087803b1580156102f257600080fd5b505af1158015610306573d6000803e3d6000fd5b505050508015610350576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b60608160000361037b5750506040805180820190915260018152600360fc1b602082015290565b8160005b81156103a5578061038f816107c8565b915061039e9050600a836107f7565b915061037f565b60008167ffffffffffffffff8111156103c0576103c061080b565b6040519080825280601f01601f1916602001820160405280156103ea576020820181803683370190505b5090505b8415610455576103ff600183610821565b915061040c600a86610838565b61041790603061084c565b60f81b81838151811061042c5761042c610864565b60200101906001600160f81b031916908160001a90535061044e600a866107f7565b94506103ee565b949350505050565b61046681610552565b604051635b9af12b60e01b8152600481018290526004604360981b0190635b9af12b90602401600060405180830381600087803b1580156104a657600080fd5b505af11580156104ba573d6000803e3d6000fd5b5050505050565b600054610100900460ff1661052c5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840161026f565b6001541561054d5760405163131cb46d60e21b815260040160405180910390fd5b600155565b6001600160a01b037f00000000000000000000000098078db053902644191f93988341e31289e1c8fe167311110000000000000000000000000000000011101933016001600160a01b0316146105bb57604051631d73770560e11b815260040160405180910390fd5b80156105d95780600260008282546105d3919061084c565b90915550505b61035060006105e760335490565b60025410806105f65750603354155b156106015750600090565b60335460025461061191906107f7565b60016000828254610622919061084c565b90915550506033546002546106379190610838565b6002556001546040519081527f270b316b51ab2cf3a3bb8ca4d22e76a327d05e762fcaa8bd6afaf8cfde9270b79060200160405180910390a150600190565b60005b83811015610691578181015183820152602001610679565b838111156106a0576000848401525b50505050565b60208152600082518060208401526106c5816040850160208701610676565b601f01601f19169190910160400192915050565b6000602082840312156106eb57600080fd5b5035919050565b60008451610704818460208901610676565b8083019050601760f91b8082528551610724816001850160208a01610676565b6001920191820152835161073f816002840160208801610676565b0160020195945050505050565b634e487b7160e01b600052602160045260246000fd5b6001600160a01b0385811682526080820190600386106107845761078461074c565b8560208401526002851061079a5761079a61074c565b84604084015280841660608401525095945050505050565b634e487b7160e01b600052601160045260246000fd5b6000600182016107da576107da6107b2565b5060010190565b634e487b7160e01b600052601260045260246000fd5b600082610806576108066107e1565b500490565b634e487b7160e01b600052604160045260246000fd5b600082821015610833576108336107b2565b500390565b600082610847576108476107e1565b500690565b6000821982111561085f5761085f6107b2565b500190565b634e487b7160e01b600052603260045260246000fdfea164736f6c634300080f000a
Deployed Bytecode
0x608060405234801561001057600080fd5b506004361061007d5760003560e01c80637ae556b51161005b5780637ae556b5146100c3578063a035b1fe14610102578063e20ccec31461010b578063fe4b84df1461011457600080fd5b806306661abd1461008257806354fd4d50146100995780635b9af12b146100ae575b600080fd5b6033545b6040519081526020015b60405180910390f35b6100a1610127565b60405161009091906106a6565b6100c16100bc3660046106d9565b6101ca565b005b6100ea7f00000000000000000000000098078db053902644191f93988341e31289e1c8fe81565b6040516001600160a01b039091168152602001610090565b61008660015481565b61008660025481565b6100c16101223660046106d9565b6101d6565b60606101527f0000000000000000000000000000000000000000000000000000000000000001610354565b61017b7f0000000000000000000000000000000000000000000000000000000000000000610354565b6101a47f0000000000000000000000000000000000000000000000000000000000000000610354565b6040516020016101b6939291906106f2565b604051602081830303815290604052905090565b6101d38161045d565b50565b600054610100900460ff16158080156101f65750600054600160ff909116105b806102105750303b158015610210575060005460ff166001145b6102785760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff19166001179055801561029b576000805461ff0019166101001790555b6102a4826104c1565b60405163099005e760e31b81526002604360981b0190634c802f38906102d890309060019060009061dead90600401610762565b600060405180830381600087803b1580156102f257600080fd5b505af1158015610306573d6000803e3d6000fd5b505050508015610350576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b60608160000361037b5750506040805180820190915260018152600360fc1b602082015290565b8160005b81156103a5578061038f816107c8565b915061039e9050600a836107f7565b915061037f565b60008167ffffffffffffffff8111156103c0576103c061080b565b6040519080825280601f01601f1916602001820160405280156103ea576020820181803683370190505b5090505b8415610455576103ff600183610821565b915061040c600a86610838565b61041790603061084c565b60f81b81838151811061042c5761042c610864565b60200101906001600160f81b031916908160001a90535061044e600a866107f7565b94506103ee565b949350505050565b61046681610552565b604051635b9af12b60e01b8152600481018290526004604360981b0190635b9af12b90602401600060405180830381600087803b1580156104a657600080fd5b505af11580156104ba573d6000803e3d6000fd5b5050505050565b600054610100900460ff1661052c5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840161026f565b6001541561054d5760405163131cb46d60e21b815260040160405180910390fd5b600155565b6001600160a01b037f00000000000000000000000098078db053902644191f93988341e31289e1c8fe167311110000000000000000000000000000000011101933016001600160a01b0316146105bb57604051631d73770560e11b815260040160405180910390fd5b80156105d95780600260008282546105d3919061084c565b90915550505b61035060006105e760335490565b60025410806105f65750603354155b156106015750600090565b60335460025461061191906107f7565b60016000828254610622919061084c565b90915550506033546002546106379190610838565b6002556001546040519081527f270b316b51ab2cf3a3bb8ca4d22e76a327d05e762fcaa8bd6afaf8cfde9270b79060200160405180910390a150600190565b60005b83811015610691578181015183820152602001610679565b838111156106a0576000848401525b50505050565b60208152600082518060208401526106c5816040850160208701610676565b601f01601f19169190910160400192915050565b6000602082840312156106eb57600080fd5b5035919050565b60008451610704818460208901610676565b8083019050601760f91b8082528551610724816001850160208a01610676565b6001920191820152835161073f816002840160208801610676565b0160020195945050505050565b634e487b7160e01b600052602160045260246000fd5b6001600160a01b0385811682526080820190600386106107845761078461074c565b8560208401526002851061079a5761079a61074c565b84604084015280841660608401525095945050505050565b634e487b7160e01b600052601160045260246000fd5b6000600182016107da576107da6107b2565b5060010190565b634e487b7160e01b600052601260045260246000fd5b600082610806576108066107e1565b500490565b634e487b7160e01b600052604160045260246000fd5b600082821015610833576108336107b2565b500390565b600082610847576108476107e1565b500690565b6000821982111561085f5761085f6107b2565b500190565b634e487b7160e01b600052603260045260246000fdfea164736f6c634300080f000a
Deployed Bytecode Sourcemap
3409:1009:8:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4161:88;4235:6;;4161:88;;;160:25:12;;;148:2;133:18;4161:88:8;;;;;;;;1011:338:10;;;:::i;:::-;;;;;;;:::i;2275:77:8:-;;;;;;:::i;:::-;;:::i;:::-;;709:33;;;;;;;;-1:-1:-1;;;;;1196:32:12;;;1178:51;;1166:2;1151:18;709:33:8;1032:203:12;811:20:8;;;;;;942:22;;;;;;3808:313;;;;;;:::i;:::-;;:::i;1011:338:10:-;1051:13;1140:31;1157:13;1140:16;:31::i;:::-;1212;1229:13;1212:16;:31::i;:::-;1284;1301:13;1284:16;:31::i;:::-;1105:225;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;1077:264;;1011:338;:::o;2275:77:8:-;2328:16;2338:5;2328:9;:16::i;:::-;2275:77;:::o;3808:313::-;3363:19:0;3386:13;;;;;;3385:14;;3433:34;;;;-1:-1:-1;3451:12:0;;3466:1;3451:12;;;;:16;3433:34;3432:108;;;-1:-1:-1;3512:4:0;1757:19:1;:23;;;3473:66:0;;-1:-1:-1;3522:12:0;;;;;:17;3473:66;3410:204;;;;-1:-1:-1;;;3410:204:0;;2407:2:12;3410:204:0;;;2389:21:12;2446:2;2426:18;;;2419:30;2485:34;2465:18;;;2458:62;-1:-1:-1;;;2536:18:12;;;2529:44;2590:19;;3410:204:0;;;;;;;;;3625:12;:16;;-1:-1:-1;;3625:16:0;3640:1;3625:16;;;3652:67;;;;3687:13;:20;;-1:-1:-1;;3687:20:0;;;;;3652:67;3874:37:8::1;3902:6;3874:17;:37::i;:::-;3922:191;::::0;-1:-1:-1;;;3922:191:8;;-1:-1:-1;;;;;4496:42:9;3922:41:8::1;::::0;:191:::1;::::0;3986:4:::1;::::0;4006:14:::1;::::0;4035:12:::1;::::0;4070:6:::1;::::0;3922:191:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;3745:14:0::0;3741:102;;;3792:5;3776:21;;-1:-1:-1;;3776:21:0;;;3817:14;;-1:-1:-1;3490:36:12;;3817:14:0;;3478:2:12;3463:18;3817:14:0;;;;;;;3741:102;3352:498;3808:313:8;:::o;407:723:2:-;463:13;684:5;693:1;684:10;680:53;;-1:-1:-1;;711:10:2;;;;;;;;;;;;-1:-1:-1;;;711:10:2;;;;;407:723::o;680:53::-;758:5;743:12;799:78;806:9;;799:78;;832:8;;;;:::i;:::-;;-1:-1:-1;855:10:2;;-1:-1:-1;863:2:2;855:10;;:::i;:::-;;;799:78;;;887:19;919:6;909:17;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;909:17:2;;887:39;;937:154;944:10;;937:154;;971:11;981:1;971:11;;:::i;:::-;;-1:-1:-1;1040:10:2;1048:2;1040:5;:10;:::i;:::-;1027:24;;:2;:24;:::i;:::-;1014:39;;997:6;1004;997:14;;;;;;;;:::i;:::-;;;;:56;-1:-1:-1;;;;;997:56:2;;;;;;;;-1:-1:-1;1068:11:2;1077:2;1068:11;;:::i;:::-;;;937:154;;;1115:6;407:723;-1:-1:-1;;;;407:723:2:o;4257:158:8:-;4320:22;4336:5;4320:15;:22::i;:::-;4355:52;;-1:-1:-1;;;4355:52:8;;;;;160:25:12;;;-1:-1:-1;;;;;4769:42:9;4355:45:8;;133:18:12;;4355:52:8;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4257:158;:::o;1761:182::-;5506:13:0;;;;;;;5498:69;;;;-1:-1:-1;;;5498:69:0;;4912:2:12;5498:69:0;;;4894:21:12;4951:2;4931:18;;;4924:30;4990:34;4970:18;;;4963:62;-1:-1:-1;;;5041:18:12;;;5034:41;5092:19;;5498:69:0;4710:407:12;5498:69:0;1845:5:8::1;::::0;:10;1841:70:::1;;1879:20;;-1:-1:-1::0;;;1879:20:8::1;;;;;;;;;;;1841:70;1921:5;:14:::0;1761:182::o;2360:290::-;-1:-1:-1;;;;;2476:8:8;2426:58;-1:-1:-1;;2461:10:8;1724:27:11;-1:-1:-1;;;;;2426:58:8;;2422:115;;2508:17;;-1:-1:-1;;;2508:17:8;;;;;;;;;;;2422:115;2553:9;;2549:58;;2590:5;2579:7;;:16;;;;;;;:::i;:::-;;;;-1:-1:-1;;2549:58:8;2619:23;2973:4;3004:7;4235:6;;;4161:88;3004:7;2994;;:17;:33;;;-1:-1:-1;4235:6:8;;3015:12;2994:33;2990:78;;;-1:-1:-1;3051:5:8;;2922:287::o;2990:78::-;4235:6;;3089:7;;:17;;;;:::i;:::-;3080:5;;:26;;;;;;;:::i;:::-;;;;-1:-1:-1;;4235:6:8;;3127:7;;:17;;;;:::i;:::-;3117:7;:27;3171:5;;3162:15;;160:25:12;;;3162:15:8;;148:2:12;133:18;3162:15:8;;;;;;;-1:-1:-1;3197:4:8;;2922:287::o;196:258:12:-;268:1;278:113;292:6;289:1;286:13;278:113;;;368:11;;;362:18;349:11;;;342:39;314:2;307:10;278:113;;;409:6;406:1;403:13;400:48;;;444:1;435:6;430:3;426:16;419:27;400:48;;196:258;;;:::o;459:383::-;608:2;597:9;590:21;571:4;640:6;634:13;683:6;678:2;667:9;663:18;656:34;699:66;758:6;753:2;742:9;738:18;733:2;725:6;721:15;699:66;:::i;:::-;826:2;805:15;-1:-1:-1;;801:29:12;786:45;;;;833:2;782:54;;459:383;-1:-1:-1;;459:383:12:o;847:180::-;906:6;959:2;947:9;938:7;934:23;930:32;927:52;;;975:1;972;965:12;927:52;-1:-1:-1;998:23:12;;847:180;-1:-1:-1;847:180:12:o;1240:960::-;1669:3;1707:6;1701:13;1723:53;1769:6;1764:3;1757:4;1749:6;1745:17;1723:53;:::i;:::-;1807:6;1802:3;1798:16;1785:29;;-1:-1:-1;;;1859:2:12;1852:5;1845:17;1893:6;1887:13;1909:65;1965:8;1961:1;1954:5;1950:13;1943:4;1935:6;1931:17;1909:65;:::i;:::-;2037:1;1993:20;;2029:10;;;2022:22;2069:13;;2091:62;2069:13;2140:1;2132:10;;2125:4;2113:17;;2091:62;:::i;:::-;2173:17;2192:1;2169:25;;1240:960;-1:-1:-1;;;;;1240:960:12:o;2620:127::-;2681:10;2676:3;2672:20;2669:1;2662:31;2712:4;2709:1;2702:15;2736:4;2733:1;2726:15;2752:581;-1:-1:-1;;;;;3061:15:12;;;3043:34;;2992:3;2977:19;;;3107:1;3096:13;;3086:47;;3113:18;;:::i;:::-;3169:6;3164:2;3153:9;3149:18;3142:34;3206:1;3198:6;3195:13;3185:47;;3212:18;;:::i;:::-;3268:6;3263:2;3252:9;3248:18;3241:34;3323:2;3315:6;3311:15;3306:2;3295:9;3291:18;3284:43;;2752:581;;;;;;;:::o;3537:127::-;3598:10;3593:3;3589:20;3586:1;3579:31;3629:4;3626:1;3619:15;3653:4;3650:1;3643:15;3669:135;3708:3;3729:17;;;3726:43;;3749:18;;:::i;:::-;-1:-1:-1;3796:1:12;3785:13;;3669:135::o;3809:127::-;3870:10;3865:3;3861:20;3858:1;3851:31;3901:4;3898:1;3891:15;3925:4;3922:1;3915:15;3941:120;3981:1;4007;3997:35;;4012:18;;:::i;:::-;-1:-1:-1;4046:9:12;;3941:120::o;4066:127::-;4127:10;4122:3;4118:20;4115:1;4108:31;4158:4;4155:1;4148:15;4182:4;4179:1;4172:15;4198:125;4238:4;4266:1;4263;4260:8;4257:34;;;4271:18;;:::i;:::-;-1:-1:-1;4308:9:12;;4198:125::o;4328:112::-;4360:1;4386;4376:35;;4391:18;;:::i;:::-;-1:-1:-1;4425:9:12;;4328:112::o;4445:128::-;4485:3;4516:1;4512:6;4509:1;4506:13;4503:39;;;4522:18;;:::i;:::-;-1:-1:-1;4558:9:12;;4445:128::o;4578:127::-;4639:10;4634:3;4630:20;4627:1;4620:31;4670:4;4667:1;4660:15;4694:4;4691:1;4684:15
Swarm Source
none
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.