Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x15ef807A...CC5042902 The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
CelerFeeHubFacet
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 99999 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.19; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { IERC20 } from "@solidstate/contracts/interfaces/IERC20.sol"; import { SafeERC20 } from "@solidstate/contracts/utils/SafeERC20.sol"; import { IFeeDistributorFacet } from "./../interfaces/IFeeDistributorFacet.sol"; import { ICelerFeeHubFacet } from "./../interfaces/ICelerFeeHubFacet.sol"; import { IFeeStoreFacet } from "./../interfaces/IFeeStoreFacet.sol"; import { IRelayerCeler } from "./../interfaces/IRelayerCeler.sol"; import { LibAccessControlEnumerable } from "./../libraries/LibAccessControlEnumerable.sol"; import { LibFeeManagerStorage } from "./../libraries/LibFeeManagerStorage.sol"; import { LibFeeManager } from "./../libraries/LibFeeManager.sol"; import { LibFeeStore } from "./../libraries/LibFeeStore.sol"; import { LibDiamond } from "./../libraries/LibDiamond.sol"; import { FeeConfig, FeeConfigSyncDTO, FeeConfigSyncHomeDTO, FeeSyncQueue } from "./../helpers/Structs.sol"; import { AddressZero, AlreadyInitialized, NotAllowed, WrongChain, ZeroValueNotAllowed } from "./../helpers/GenericErrors.sol"; import { addressZeroCheck } from "./../helpers/Functions.sol"; import { FeeDeployState } from "./../helpers/Enums.sol"; import { Constants } from "./../helpers/Constants.sol"; /// @title Celer Fee Hub Facet /// @author Daniel <[email protected]> /// @notice This contract provides the functionality to interact with the celer services through a defined relayer /// @custom:version 1.0.0 contract CelerFeeHubFacet is ICelerFeeHubFacet { using SafeERC20 for IERC20; using Address for address payable; bytes32 constant STORAGE_NAMESPACE = keccak256("degenx.celer-fee-hub.storage.v1"); address immutable relayer; event QueueProcessed(); event FeesSent(); event UpdateThreshold(uint256 amount); event UpdateSendFeesWei(uint256 amount); event UpdateDeployFeesWei(uint256 amount); event RefundCollected(address asset, address receiver, uint256 amount); event RefundForwarded(address asset, address receiver, uint256 amount); event RelayerForChainAdded(address relayer, uint256 chainId); event RelayerForChainUpdated(address relayer, uint256 chainId); event RelayerForChainRemoved(uint256 chainId); event DeploymentSuccessful(uint64 indexed chainId); error QueueEmpty(); error NoChainsConfigured(); error RefundFailed(); error ChainExisting(uint256 chainId); error ChainNotExisting(uint256 chainId); error RelayerExists(address relayer); error ThresholdNotMet(); error InsufficientFundsSent(); error InsufficientFundsForGas(); /// @param sendFeesThreshold defines threshold when it is allowed to send fees to home chain /// @param sendFeesWei defines the funds in wei that are needed to initiate the process. leftovers are moved back to sender /// @param deployFeesWei defines the funds in wei that are needed to initiate the process. leftovers are moved back to sender /// @param chainIdToRelayer a map of chain ids and their matching relayer address struct Storage { uint256 sendFeesThreshold; uint256 sendFeesWei; uint256 deployFeesWei; mapping(uint256 => address) chainIdToRelayer; } /// Constructor /// @param _relayer address of the relayer constructor(address _relayer) { relayer = _relayer; } /// Adds a relayer for a specific chain id /// @param _relayer address of a relayer /// @param _chainId chain id of the relayer /// @dev this can only be executed by the FEE_MANAGER_ROLE, which is the DAO and the owner function addRelayerForChain(address _relayer, uint256 _chainId) external { LibAccessControlEnumerable.checkRole(Constants.FEE_MANAGER_ROLE); addressZeroCheck(_relayer); if (_chainId == 0) revert ZeroValueNotAllowed(); Storage storage s = _store(); if (s.chainIdToRelayer[_chainId] != address(0)) revert ChainExisting(_chainId); s.chainIdToRelayer[_chainId] = _relayer; emit RelayerForChainAdded(_relayer, _chainId); } /// Updates a relayer for a specific chain id /// @param _relayer address of a relayer /// @param _chainId chain id of the relayer /// @dev this can only be executed by the FEE_MANAGER_ROLE, which is the DAO and the owner function updateRelayerOnChain(address _relayer, uint256 _chainId) external { LibAccessControlEnumerable.checkRole(Constants.FEE_MANAGER_ROLE); addressZeroCheck(_relayer); if (_chainId == 0) revert ZeroValueNotAllowed(); Storage storage s = _store(); if (s.chainIdToRelayer[_chainId] == address(0)) revert ChainNotExisting(_chainId); if (s.chainIdToRelayer[_chainId] == _relayer) revert RelayerExists(_relayer); s.chainIdToRelayer[_chainId] = _relayer; emit RelayerForChainUpdated(_relayer, _chainId); } /// Removes a relayer for a specific chain id /// @param _chainId chain id of the relayer /// @dev this can only be executed by the FEE_MANAGER_ROLE, which is the DAO and the owner function removeRelayerOnChain(uint256 _chainId) external { LibAccessControlEnumerable.checkRole(Constants.FEE_MANAGER_ROLE); Storage storage s = _store(); if (s.chainIdToRelayer[_chainId] == address(0)) revert ChainNotExisting(_chainId); delete s.chainIdToRelayer[_chainId]; emit RelayerForChainRemoved(_chainId); } /// Sets the threshold a total fee can be sent to the home chain /// @param _amount threshold amount function updateSendFeesThreshold(uint256 _amount) external { LibAccessControlEnumerable.checkRole(Constants.FEE_MANAGER_ROLE); Storage storage s = _store(); s.sendFeesThreshold = _amount; emit UpdateThreshold(_amount); } /// Sets the amount of fees that is being used to initiate the send fees process /// @param _wei amount of fees function updateSendFeesWei(uint256 _wei) external { LibAccessControlEnumerable.checkRole(Constants.FEE_MANAGER_ROLE); Storage storage s = _store(); s.sendFeesWei = _wei; emit UpdateSendFeesWei(_wei); } /// Sets the amount of fees that is being used to initiate the deploy fees process /// @param _wei amount of fees function updateDeployFeesWei(uint256 _wei) external { LibAccessControlEnumerable.checkRole(Constants.FEE_MANAGER_ROLE); Storage storage s = _store(); s.deployFeesWei = _wei; emit UpdateDeployFeesWei(_wei); } /// @notice This method deploys added, updated or removed fee configuration to desired chains through CELER IM. It is executable by everyone (DeFi things obv) /// @dev Once the queue of the fee manager is filled with configs, it'll be processable. It creates an array of dtos which are being processed by the target chain and its relayer. function deployFeesWithCeler() external payable { LibFeeManagerStorage.FeeManagerStorage storage _managerStore = LibFeeManagerStorage.feeManagerStorage(); Storage storage s = _store(); uint256 _providedWei = msg.value; if (_providedWei == 0 || s.deployFeesWei > _providedWei) revert InsufficientFundsSent(); uint256[] memory _chainIds = _managerStore.chainIds; if (_chainIds.length == 0) revert NoChainsConfigured(); bool _sync = false; mapping(uint256 => FeeSyncQueue[]) storage _queue = _managerStore.feeSyncQueue; for (uint256 i = 0; i < _chainIds.length; i++) { if (_queue[_chainIds[i]].length == 0) continue; if (!_sync) _sync = true; uint256 chainId = _chainIds[i]; addressZeroCheck(s.chainIdToRelayer[chainId]); FeeConfigSyncDTO[] memory _dto = new FeeConfigSyncDTO[](_queue[chainId].length); for (uint256 j = 0; j < _queue[chainId].length; j++) { bytes32 feeId = _queue[chainId][j].id; FeeConfig storage _config = _managerStore.feeConfigs[feeId]; _dto[j] = FeeConfigSyncDTO({ id: feeId, fee: _config.fee, action: _queue[chainId][j].action, target: _managerStore.chainTargets[chainId] }); _managerStore.feeDeployState[chainId][feeId] = FeeDeployState.Pending; } address _target = LibFeeManager.getChainTarget(chainId); bytes memory _message = abi.encodeWithSelector(IFeeStoreFacet.syncFees.selector, _dto); uint256 _fee = IRelayerCeler(relayer).deployFeesFeeCalc(_target, _message); if (_fee > _providedWei) revert InsufficientFundsForGas(); _providedWei -= _fee; IRelayerCeler(relayer).deployFees{ value: _fee }(s.chainIdToRelayer[chainId], _target, chainId, _message); delete _managerStore.feeSyncQueue[_chainIds[i]]; } if (_sync) { if (_providedWei > 0) payable(msg.sender).sendValue(_providedWei); emit QueueProcessed(); } else revert QueueEmpty(); } /// @inheritdoc ICelerFeeHubFacet function deployFeesWithCelerConfirm(uint64 _chainId, bytes calldata _message) external { if (relayer != msg.sender) revert NotAllowed(); LibFeeManagerStorage.FeeManagerStorage storage _managerStore = LibFeeManagerStorage.feeManagerStorage(); FeeConfigSyncDTO[] memory _dto = abi.decode(_message[4:], (FeeConfigSyncDTO[])); for (uint256 i = 0; i < _dto.length; i++) { _managerStore.feeDeployState[_chainId][_dto[i].id] = FeeDeployState.Deployed; } } /// Sends fees stored on the FeeStore back to the home chain, respecting a bounty receiver /// @param minMaxSlippage external defined minimal max slippage by the estimation api of CELER /// @param _bountyReceiver address of the bounty receiver on the home chain /// @dev The bounty receiver is set because you can't relay on the initiator in the consuming /// contract on the home chain, because contracts can execute this method without having /// the same address on the home chain. It also transfers the tokens to the relayer which /// then bridges the tokens and sends the message to the CELER IM service /// @notice Can be executed by everyone. Its success is dependend on the sendFeesThreshold being met function sendFeesWithCeler(uint32 minMaxSlippage, address _bountyReceiver) external payable { Storage storage s = _store(); FeeConfigSyncHomeDTO memory _dto = LibFeeStore.prepareToSendFees(); // implicit zero fee check here if (_dto.totalFees < s.sendFeesThreshold) revert ThresholdNotMet(); uint256 _providedWei = msg.value; if (_providedWei == 0 || s.sendFeesWei > _providedWei) revert InsufficientFundsSent(); if (_bountyReceiver == address(0) || _bountyReceiver == address(0xdead)) revert AddressZero(); _dto.bountyReceiver = _bountyReceiver; IERC20(LibFeeStore.getIntermediateAsset()).safeTransfer(relayer, _dto.totalFees); bytes memory _message = abi.encode(_dto); uint256 _fee = IRelayerCeler(relayer).sendFeesFeeCalc(_message); if (_fee > _providedWei) revert InsufficientFundsForGas(); _providedWei -= _fee; IRelayerCeler(relayer).sendFees{ value: _fee }(LibFeeStore.getIntermediateAsset(), _dto.totalFees, minMaxSlippage, _message); if (_providedWei > 0) payable(msg.sender).sendValue(_providedWei); emit FeesSent(); } /// viewables function celerFeeHubSendFeesWei() external view returns (uint256 _wei) { Storage storage s = _store(); _wei = s.sendFeesWei; } function celerFeeHubDeployFeesWei() external view returns (uint256 _wei) { Storage storage s = _store(); _wei = s.deployFeesWei; } /// internals /// Store function _store() internal pure returns (Storage storage s) { bytes32 position = STORAGE_NAMESPACE; assembly { s.slot := position } } }
// 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 Address { /** * @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.9.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) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 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. If 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)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.0; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; import "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _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) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toString(int256 value) internal pure returns (string memory) { return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value)))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { 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] = _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); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. pragma solidity ^0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ```solidity * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure * unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an * array of EnumerableSet. * ==== */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { bytes32 lastValue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastValue; // Update the index for the moved value set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { bytes32[] memory store = _values(set._inner); bytes32[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values in the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20Internal } from './IERC20Internal.sol'; /** * @title ERC20 interface * @dev see https://eips.ethereum.org/EIPS/eip-20 */ interface IERC20 is IERC20Internal { /** * @notice query the total minted token supply * @return token supply */ function totalSupply() external view returns (uint256); /** * @notice query the token balance of given account * @param account address to query * @return token balance */ function balanceOf(address account) external view returns (uint256); /** * @notice query the allowance granted from given holder to given spender * @param holder approver of allowance * @param spender recipient of allowance * @return token allowance */ function allowance( address holder, address spender ) external view returns (uint256); /** * @notice grant approval to spender to spend tokens * @dev prefer ERC20Extended functions to avoid transaction-ordering vulnerability (see https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729) * @param spender recipient of allowance * @param amount quantity of tokens approved for spending * @return success status (always true; otherwise function should revert) */ function approve(address spender, uint256 amount) external returns (bool); /** * @notice transfer tokens to given recipient * @param recipient beneficiary of token transfer * @param amount quantity of tokens to transfer * @return success status (always true; otherwise function should revert) */ function transfer( address recipient, uint256 amount ) external returns (bool); /** * @notice transfer tokens to given recipient on behalf of given holder * @param holder holder of tokens prior to transfer * @param recipient beneficiary of token transfer * @param amount quantity of tokens to transfer * @return success status (always true; otherwise function should revert) */ function transferFrom( address holder, address recipient, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; /** * @title Partial ERC20 interface needed by internal functions */ interface IERC20Internal { event Transfer(address indexed from, address indexed to, uint256 value); event Approval( address indexed owner, address indexed spender, uint256 value ); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20MetadataInternal } from './IERC20MetadataInternal.sol'; /** * @title ERC20 metadata interface */ interface IERC20Metadata is IERC20MetadataInternal { /** * @notice return token name * @return token name */ function name() external view returns (string memory); /** * @notice return token symbol * @return token symbol */ function symbol() external view returns (string memory); /** * @notice return token decimals, generally used only for display purposes * @return token decimals */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; /** * @title ERC20 metadata internal interface */ interface IERC20MetadataInternal { }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { UintUtils } from './UintUtils.sol'; library AddressUtils { using UintUtils for uint256; error AddressUtils__InsufficientBalance(); error AddressUtils__NotContract(); error AddressUtils__SendValueFailed(); function toString(address account) internal pure returns (string memory) { return uint256(uint160(account)).toHexString(20); } function isContract(address account) internal view returns (bool) { uint256 size; assembly { size := extcodesize(account) } return size > 0; } function sendValue(address payable account, uint256 amount) internal { (bool success, ) = account.call{ value: amount }(''); if (!success) revert AddressUtils__SendValueFailed(); } function functionCall( address target, bytes memory data ) internal returns (bytes memory) { return functionCall(target, data, 'AddressUtils: failed low-level call'); } function functionCall( address target, bytes memory data, string memory error ) internal returns (bytes memory) { return _functionCallWithValue(target, data, 0, error); } function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue( target, data, value, 'AddressUtils: failed low-level call with value' ); } function functionCallWithValue( address target, bytes memory data, uint256 value, string memory error ) internal returns (bytes memory) { if (value > address(this).balance) revert AddressUtils__InsufficientBalance(); return _functionCallWithValue(target, data, value, error); } /** * @notice execute arbitrary external call with limited gas usage and amount of copied return data * @dev derived from https://github.com/nomad-xyz/ExcessivelySafeCall (MIT License) * @param target recipient of call * @param gasAmount gas allowance for call * @param value native token value to include in call * @param maxCopy maximum number of bytes to copy from return data * @param data encoded call data * @return success whether call is successful * @return returnData copied return data */ function excessivelySafeCall( address target, uint256 gasAmount, uint256 value, uint16 maxCopy, bytes memory data ) internal returns (bool success, bytes memory returnData) { returnData = new bytes(maxCopy); assembly { // execute external call via assembly to avoid automatic copying of return data success := call( gasAmount, target, value, add(data, 0x20), mload(data), 0, 0 ) // determine whether to limit amount of data to copy let toCopy := returndatasize() if gt(toCopy, maxCopy) { toCopy := maxCopy } // store the length of the copied bytes mstore(returnData, toCopy) // copy the bytes from returndata[0:toCopy] returndatacopy(add(returnData, 0x20), 0, toCopy) } } function _functionCallWithValue( address target, bytes memory data, uint256 value, string memory error ) private returns (bytes memory) { if (!isContract(target)) revert AddressUtils__NotContract(); (bool success, bytes memory returnData) = target.call{ value: value }( data ); if (success) { return returnData; } else if (returnData.length > 0) { assembly { let returnData_size := mload(returnData) revert(add(32, returnData), returnData_size) } } else { revert(error); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20 } from '../interfaces/IERC20.sol'; import { AddressUtils } from './AddressUtils.sol'; /** * @title Safe ERC20 interaction library * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license) */ library SafeERC20 { using AddressUtils for address; error SafeERC20__ApproveFromNonZeroToNonZero(); error SafeERC20__DecreaseAllowanceBelowZero(); error SafeERC20__OperationFailed(); function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transfer.selector, to, value) ); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value) ); } /** * @dev safeApprove (like approve) should only be called when setting an initial allowance or when resetting it to zero; otherwise prefer safeIncreaseAllowance and safeDecreaseAllowance */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { if ((value != 0) && (token.allowance(address(this), spender) != 0)) revert SafeERC20__ApproveFromNonZeroToNonZero(); _callOptionalReturn( token, abi.encodeWithSelector(token.approve.selector, spender, value) ); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn( token, abi.encodeWithSelector( token.approve.selector, spender, newAllowance ) ); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); if (oldAllowance < value) revert SafeERC20__DecreaseAllowanceBelowZero(); uint256 newAllowance = oldAllowance - value; _callOptionalReturn( token, abi.encodeWithSelector( token.approve.selector, spender, newAllowance ) ); } } /** * @notice send transaction data and check validity of return value, if present * @param token ERC20 token interface * @param data transaction data */ function _callOptionalReturn(IERC20 token, bytes memory data) private { bytes memory returndata = address(token).functionCall( data, 'SafeERC20: low-level call failed' ); if (returndata.length > 0) { if (!abi.decode(returndata, (bool))) revert SafeERC20__OperationFailed(); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; /** * @title utility functions for uint256 operations * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license) */ library UintUtils { error UintUtils__InsufficientHexLength(); bytes16 private constant HEX_SYMBOLS = '0123456789abcdef'; function add(uint256 a, int256 b) internal pure returns (uint256) { return b < 0 ? sub(a, -b) : a + uint256(b); } function sub(uint256 a, int256 b) internal pure returns (uint256) { return b < 0 ? add(a, -b) : a - uint256(b); } function toString(uint256 value) internal pure returns (string memory) { 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); } function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return '0x00'; } uint256 length = 0; for (uint256 temp = value; temp != 0; temp >>= 8) { unchecked { length++; } } return toHexString(value, 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'; unchecked { for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = HEX_SYMBOLS[value & 0xf]; value >>= 4; } } if (value != 0) revert UintUtils__InsufficientHexLength(); return string(buffer); } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; library Constants { /*-------------------------------- Role --------------------------------*/ // 0x0000000000000000000000000000000000000000000000000000000000000000 bytes32 constant DEFAULT_ADMIN_ROLE = 0x00; // 0xa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775 bytes32 constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); // 0xfc425f2263d0df187444b70e47283d622c70181c5baebb1306a01edba1ce184c bytes32 constant DEPLOYER_ROLE = keccak256("DEPLOYER_ROLE"); // 0x6c0757dc3e6b28b2580c03fd9e96c274acf4f99d91fbec9b418fa1d70604ff1c bytes32 constant FEE_MANAGER_ROLE = keccak256("FEE_MANAGER_ROLE"); // 0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6 bytes32 constant MINTER_ROLE = keccak256("MINTER_ROLE"); // 0x3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a848 bytes32 constant BURNER_ROLE = keccak256("BURNER_ROLE"); // 0x63eb04268b235ac1afacf3bcf4b19c5c175d0417a1555fb3ff79ae190f71ee7c bytes32 constant FEE_STORE_MANAGER_ROLE = keccak256("FEE_STORE_MANAGER_ROLE"); // 0x77f52ccf2f32e71a0cff8f14ad8c8303b7d2e4c7609b8fba963114f4db2af767 bytes32 constant FEE_DISTRIBUTOR_PUSH_ROLE = keccak256("FEE_DISTRIBUTOR_PUSH_ROLE"); // 0xe85d5f1f8338cb18f500856d1568d0f3b0d0971f25b3ccd134475e991354edbf bytes32 constant FEE_DISTRIBUTOR_MANAGER = keccak256("FEE_DISTRIBUTOR_MANAGER"); /*----------------------------------------------------------------------*/ /*------------------------------- Fee ID -------------------------------*/ // 0xacfc432e98ad100d9f8c385f3782bc88a17e1de7e53f69678cbcc41e8ffe72b0 bytes32 constant ERC20_MARKETING_FEE = keccak256("ERC20_MARKETING_FEE"); // 0x6b78196f16f828b24a5a6584d4a1bcc5ce2f3154ba57839db273e6a4ebbe92c2 bytes32 constant ERC20_REWARD_FEE = keccak256("ERC20_REWARD_FEE"); // 0x6e3678bee6f77c8a6179922c9a518b08407e6d9d2593ac683a87c979c8b31a12 bytes32 constant ERC20_PLATFORM_FEE = keccak256("ERC20_PLATFORM_FEE"); // 0x6e2178bb28988b4c92cd3092e9e342e7639bfda2f68a02ac478cb084759607cf bytes32 constant ERC20_DEVELOPER_FEE = keccak256("ERC20_DEVELOPER_FEE"); /*----------------------------------------------------------------------*/ /*--------------------------- Relayer Actions --------------------------*/ // 0xf145583e6e33d9da99af75b579493b11db4229a339336b82c748312f152b29a9 bytes32 constant RELAYER_ACTION_DEPLOY_FEES = keccak256("RELAYER_ACTION_DEPLOY_FEES"); // 0xf375f410a0dc135af0d9a16e273eac999064981d8813a68af762e93567a43aac bytes32 constant RELAYER_ACTION_DEPLOY_FEES_CONFIRM = keccak256("RELAYER_ACTION_DEPLOY_FEES_CONFIRM"); // 0x9d62257b25ea052fe7cd5123fd6b791268b8673b073aae5de4a823c4dc7d7607 bytes32 constant RELAYER_ACTION_SEND_FEES = keccak256("RELAYER_ACTION_SEND_FEES"); /*----------------------------------------------------------------------*/ }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; /// enums enum FeeCurrency { Null, // L1 Native, // different asset Token } enum FeeType { Null, // absolute/onetime Default, // buy/sell depending on target From, // buy/sell depending on target To } enum FeeSyncAction { Null, // adding a fee Add, // updating a fee Update, // removing a fee Delete } enum FeeDeployState { Null, // a fee is recently added, updated or removed Queued, // a fee config is deployed Pending, // a fee gets receives information about being deployed Deployed }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.17; import { AddressZero } from "./GenericErrors.sol"; function addressZeroCheck(address _candidate) pure { if (_candidate == address(0)) revert AddressZero(); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; error AddressZero(); error ZeroValueNotAllowed(); error InsufficientAllowance(); error InsufficientBalance(); error InsufficientFunds(); error NotAllowed(); error AlreadyInitialized(); error NoReetrancy(); error WrongChain(); error UnexpectedBalance(); error FailToSendNative();
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; import { FeeCurrency, FeeType, FeeSyncAction, FeeDeployState } from "./Enums.sol"; /// General Fee Config struct FeeConfig { // relative: 10000 = 1% or 100 = 0.01% // absolute: 10000 = 1 or 1 = 0.0001 uint256 fee; // Assets are always going to the fee distributor on the home chain. This config is necessary to define which receiver gets this asset. // It's purpose can be overwritten by the FeeDistributor. So it will serve as a fallback. address receiver; // defines the type. It does not have a purpose yet but may have in the future // see {Enums->FeeType} FeeType ftype; // type of how the fees should be handles // see {Enums->FeeCurrency} FeeCurrency currency; // // Deploy state of a fee config // // see {Enums->FeeDeployState} // FeeDeployState deployState; } /// Fee Management struct AddFeeConfigParams { // fee id which can be defined elsewhere but needs to be a bytes32 bytes32 id; // see {struct FeeConfig->fee} uint256 fee; // see {struct FeeConfig->receiver} address receiver; // see {struct FeeConfig->ftype} FeeType ftype; // see {struct FeeConfig->currency} FeeCurrency currency; } struct UpdateFeeConfigParams { // see {struct AddFeeConfigParams->id} bytes32 id; // see {struct FeeConfig->fee} uint256 fee; // see {struct FeeConfig->fee} address receiver; } struct RemoveFeeConfigParams { // see {struct AddFeeConfigParams->id} bytes32 id; } /// Chain Management struct AddChainParams { // chain id uint256 chainId; // address of the participant, most likely the diamon address of the target chain address target; } struct RemoveChainParams { // chain id uint256 chainId; } /// Fee & Chain Management struct AssignFeeConfigToChainParams { // fee config id bytes32 id; // chain id to assign the fee config id to uint256 chainId; } struct UnassignFeeConfigFromChainParams { // fee config id bytes32 id; // chain id to unassign the fee config id from uint256 chainId; } struct UnassignFeeConfigFromAllChainsParams { // fee config id bytes32 id; } /// Syncing struct FeeSyncQueue { // fee config id bytes32 id; // chain id uint256 chainId; // action to execute on the target chain FeeSyncAction action; } struct FeeConfigDeployState { bytes32 id; FeeDeployState state; } /// Data Transfer Objects struct FeeConfigSyncDTO { // fee config id bytes32 id; // fee value uint256 fee; // address to make conditional charged based on a specific token // a contract can decide by itself whether to it or not // if defined and used, this fee should be restricted and charged onto a specific token address target; // desired action to execute on the target chain FeeSyncAction action; } struct FeeConfigSyncHomeFees { // fee config id bytes32 id; // amount of the collected fees of this if uint256 amount; } struct FeeConfigSyncHomeDTO { // total amount of collected fees uint256 totalFees; // address of the bounty receiver on the home chain address bountyReceiver; // containing fee information that will moved to the home chain FeeConfigSyncHomeFees[] fees; } struct CelerRelayerData { // bytes32 hash which defined the action that should be taken bytes32 what; // address of the contract which what is being executed to address target; // encoded message of the desired scope bytes message; } /// Fee Store struct FeeStoreConfig { // fee config id bytes32 id; // fee uint256 fee; // address of the contract which what is being executed to address target; // flag for being markes as deleted bool deleted; } /// Fee Distributor struct AddReceiverParams { // public name for the receiver // can be "Staking", "Liquidity Backing" or whatever string name; // potion of share in points. Points will be summarized in the distribution to calculate the relative share uint64 points; // address of the contract/account that receives the share address account; // swap path in case a share receiver expects another token then the intermediate token of the bridge address[] swapPath; }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.17; /// @title CelerFeeHubFacet Interface /// @author Daniel <[email protected]> interface ICelerFeeHubFacet { /// Registers the successful deployment of the fees to the given chain /// @param _chainId chain id /// @param _message encoded message function deployFeesWithCelerConfirm(uint64 _chainId, bytes memory _message) external; }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; /******************************************************************************\ * Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen) * EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535 /******************************************************************************/ interface IDiamondCut { enum FacetCutAction { Add, Replace, Remove } // Add=0, Replace=1, Remove=2 struct FacetCut { address facetAddress; FacetCutAction action; bytes4[] functionSelectors; } /// @notice Add/replace/remove any number of functions and optionally execute /// a function with delegatecall /// @param _diamondCut Contains the facet addresses and function selectors /// @param _init The address of the contract or facet to execute _calldata /// @param _calldata A function call, including function selector and arguments /// _calldata is executed with delegatecall on _init function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external; }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.17; import { FeeConfigSyncHomeDTO } from "./../helpers/Structs.sol"; /// @title Fee Distributor Interface /// @author Daniel <[email protected]> interface IFeeDistributorFacet { // this is guarateed to get the tokens before being executed /// Pushes the fee to the desired receivers /// @param _token the token address being received /// @param _amount amount of tokens being received /// @param _dto the dto of the fee store to determine the split of _amount /// @dev an updated dto needs to be created since the receiving amount is not /// matching the sent amount anymore. The contract will 100% receive the /// _token _amount before being executed /// @dev only available to FEE_DISTRIBUTOR_PUSH_ROLE role /// @dev if the token doesn't match, it will fail. function pushFees(address _token, uint256 _amount, FeeConfigSyncHomeDTO calldata _dto) external payable; }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.17; import { FeeStoreConfig, FeeConfigSyncDTO, FeeConfigSyncHomeDTO } from "./../helpers/Structs.sol"; /// @title Fee Store Facet Interface /// @author Daniel <[email protected]> interface IFeeStoreFacet { /// Synchronizes fee configs /// @param _feeConfigSyncDTO array of fee configs to process in the fee store function syncFees(FeeConfigSyncDTO[] calldata _feeConfigSyncDTO) external payable; /// Restores fees which are actually intended to be sent to the home chain /// @param _dto data which is primarily used for sending fees to the home chain function restoreFeesFromSendFees(FeeConfigSyncHomeDTO memory _dto) external payable; }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.17; /// @title Relayer Celer Interface /// @author Daniel <[email protected]> interface IRelayerCeler { /// Deploys the fees to the desired chain /// @param _receiver relayer on target chain /// @param _target diamond address on target chain /// @param _chainId target chain id /// @param _message message to send to the message bus function deployFees(address _receiver, address _target, uint256 _chainId, bytes calldata _message) external payable; /// Pre calculates upcoming fees for deploying fees /// @param _target diamond address on target chain /// @param _message message to send to the message bus function deployFeesFeeCalc(address _target, bytes calldata _message) external view returns (uint256 _wei); /// Sends the fees to the home chain /// @param _asset asset that get send /// @param _amount amount of assets that gets send /// @param minMaxSlippage calculated slippage by celer /// @param _message message to send to the message bus function sendFees(address _asset, uint256 _amount, uint32 minMaxSlippage, bytes calldata _message) external payable; /// Pre calculates upcoming fees for sending fees /// @param _message message to send to the message bus function sendFeesFeeCalc(bytes calldata _message) external view returns (uint256 _wei); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import "@openzeppelin/contracts/utils/Strings.sol"; import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; library LibAccessControlEnumerable { using EnumerableSet for EnumerableSet.AddressSet; bytes32 constant ACCESS_CONTROL_STORAGE_POSITION = keccak256("degenx.access.control.storage"); event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); struct RoleData { mapping(address => bool) members; bytes32 adminRole; } struct AccessControlStorage { mapping(bytes32 => RoleData) roles; mapping(bytes32 => EnumerableSet.AddressSet) roleMembers; mapping(bytes4 => bool) supportedInterfaces; } function accessControlStorage() internal pure returns (AccessControlStorage storage acs) { bytes32 position = ACCESS_CONTROL_STORAGE_POSITION; assembly { acs.slot := position } } function checkRole(bytes32 role) internal view { checkRole(role, msg.sender); } function checkRole(bytes32 role, address account) internal view { if (!hasRole(role, account)) { revert( string( abi.encodePacked( "AccessControl: account ", Strings.toHexString(account), " is missing role ", Strings.toHexString(uint256(role), 32) ) ) ); } } function hasRole(bytes32 role, address account) internal view returns (bool) { AccessControlStorage storage acs = accessControlStorage(); return acs.roles[role].members[account]; } function grantRole(bytes32 role, address account) internal { AccessControlStorage storage acs = accessControlStorage(); if (!hasRole(role, account)) { acs.roles[role].members[account] = true; emit RoleGranted(role, account, msg.sender); acs.roleMembers[role].add(account); } } function revokeRole(bytes32 role, address account) internal { AccessControlStorage storage acs = accessControlStorage(); if (hasRole(role, account)) { acs.roles[role].members[account] = false; emit RoleRevoked(role, account, msg.sender); acs.roleMembers[role].remove(account); } } function setRoleAdmin(bytes32 role, bytes32 adminRole) internal { AccessControlStorage storage acs = accessControlStorage(); bytes32 previousAdminRole = acs.roles[role].adminRole; acs.roles[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /******************************************************************************\ * Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen) * EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535 /******************************************************************************/ import { IDiamondCut } from "../interfaces/IDiamondCut.sol"; // Remember to add the loupe functions from DiamondLoupeFacet to the diamond. // The loupe functions are required by the EIP2535 Diamonds standard error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); library LibDiamond { bytes32 constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage"); struct FacetAddressAndPosition { address facetAddress; uint96 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array } struct FacetFunctionSelectors { bytes4[] functionSelectors; uint256 facetAddressPosition; // position of facetAddress in facetAddresses array } struct DiamondStorage { // maps function selector to the facet address and // the position of the selector in the facetFunctionSelectors.selectors array mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition; // maps facet addresses to function selectors mapping(address => FacetFunctionSelectors) facetFunctionSelectors; // facet addresses address[] facetAddresses; // Used to query if a contract implements an interface. // Used to implement ERC-165. mapping(bytes4 => bool) supportedInterfaces; // owner of the contract address contractOwner; } function diamondStorage() internal pure returns (DiamondStorage storage ds) { bytes32 position = DIAMOND_STORAGE_POSITION; assembly { ds.slot := position } } event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); function setContractOwner(address _newOwner) internal { DiamondStorage storage ds = diamondStorage(); address previousOwner = ds.contractOwner; ds.contractOwner = _newOwner; emit OwnershipTransferred(previousOwner, _newOwner); } function contractOwner() internal view returns (address contractOwner_) { contractOwner_ = diamondStorage().contractOwner; } function enforceIsContractOwner() internal view { require(msg.sender == diamondStorage().contractOwner, "LibDiamond: Must be contract owner"); } event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata); // Internal function version of diamondCut function diamondCut(IDiamondCut.FacetCut[] memory _diamondCut, address _init, bytes memory _calldata) internal { for (uint256 facetIndex; facetIndex < _diamondCut.length; facetIndex++) { IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action; if (action == IDiamondCut.FacetCutAction.Add) { addFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors); } else if (action == IDiamondCut.FacetCutAction.Replace) { replaceFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors); } else if (action == IDiamondCut.FacetCutAction.Remove) { removeFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors); } else { revert("LibDiamondCut: Incorrect FacetCutAction"); } } emit DiamondCut(_diamondCut, _init, _calldata); initializeDiamondCut(_init, _calldata); } function addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); DiamondStorage storage ds = diamondStorage(); require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)"); uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length); // add new facet address if it does not exist if (selectorPosition == 0) { addFacet(ds, _facetAddress); } for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; require(oldFacetAddress == address(0), "LibDiamondCut: Can't add function that already exists"); addFunction(ds, selector, selectorPosition, _facetAddress); selectorPosition++; } } function replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); DiamondStorage storage ds = diamondStorage(); require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)"); uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length); // add new facet address if it does not exist if (selectorPosition == 0) { addFacet(ds, _facetAddress); } for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; require(oldFacetAddress != _facetAddress, "LibDiamondCut: Can't replace function with same function"); removeFunction(ds, oldFacetAddress, selector); addFunction(ds, selector, selectorPosition, _facetAddress); selectorPosition++; } } function removeFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); DiamondStorage storage ds = diamondStorage(); // if function does not exist then do nothing and return require(_facetAddress == address(0), "LibDiamondCut: Remove facet address must be address(0)"); for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; removeFunction(ds, oldFacetAddress, selector); } } function addFacet(DiamondStorage storage ds, address _facetAddress) internal { enforceHasContractCode(_facetAddress, "LibDiamondCut: New facet has no code"); ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = ds.facetAddresses.length; ds.facetAddresses.push(_facetAddress); } function addFunction(DiamondStorage storage ds, bytes4 _selector, uint96 _selectorPosition, address _facetAddress) internal { ds.selectorToFacetAndPosition[_selector].functionSelectorPosition = _selectorPosition; ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(_selector); ds.selectorToFacetAndPosition[_selector].facetAddress = _facetAddress; } function removeFunction(DiamondStorage storage ds, address _facetAddress, bytes4 _selector) internal { require(_facetAddress != address(0), "LibDiamondCut: Can't remove function that doesn't exist"); // an immutable function is a function defined directly in a diamond require(_facetAddress != address(this), "LibDiamondCut: Can't remove immutable function"); // replace selector with last selector, then delete last selector uint256 selectorPosition = ds.selectorToFacetAndPosition[_selector].functionSelectorPosition; uint256 lastSelectorPosition = ds.facetFunctionSelectors[_facetAddress].functionSelectors.length - 1; // if not the same then replace _selector with lastSelector if (selectorPosition != lastSelectorPosition) { bytes4 lastSelector = ds.facetFunctionSelectors[_facetAddress].functionSelectors[lastSelectorPosition]; ds.facetFunctionSelectors[_facetAddress].functionSelectors[selectorPosition] = lastSelector; ds.selectorToFacetAndPosition[lastSelector].functionSelectorPosition = uint96(selectorPosition); } // delete the last selector ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop(); delete ds.selectorToFacetAndPosition[_selector]; // if no more selectors for facet address then delete the facet address if (lastSelectorPosition == 0) { // replace facet address with last facet address and delete last facet address uint256 lastFacetAddressPosition = ds.facetAddresses.length - 1; uint256 facetAddressPosition = ds.facetFunctionSelectors[_facetAddress].facetAddressPosition; if (facetAddressPosition != lastFacetAddressPosition) { address lastFacetAddress = ds.facetAddresses[lastFacetAddressPosition]; ds.facetAddresses[facetAddressPosition] = lastFacetAddress; ds.facetFunctionSelectors[lastFacetAddress].facetAddressPosition = facetAddressPosition; } ds.facetAddresses.pop(); delete ds.facetFunctionSelectors[_facetAddress].facetAddressPosition; } } function initializeDiamondCut(address _init, bytes memory _calldata) internal { if (_init == address(0)) { return; } enforceHasContractCode(_init, "LibDiamondCut: _init address has no code"); (bool success, bytes memory error) = _init.delegatecall(_calldata); if (!success) { if (error.length > 0) { // bubble up error /// @solidity memory-safe-assembly assembly { let returndata_size := mload(error) revert(add(32, error), returndata_size) } } else { revert InitializationFunctionReverted(_init, _calldata); } } } function enforceHasContractCode(address _contract, string memory _errorMessage) internal view { uint256 contractSize; assembly { contractSize := extcodesize(_contract) } require(contractSize > 0, _errorMessage); } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.19; import "./LibFeeManagerStorage.sol"; import { FeeConfig, FeeSyncQueue } from "./../helpers/Structs.sol"; import { FeeType, FeeSyncAction, FeeDeployState } from "./../helpers/Enums.sol"; /// @title Lib Fee Manager /// @author Daniel <[email protected]> /// @notice Helper functions for the Fee Manager Facet library LibFeeManager { /// viewables /// Checks whether a fee config exsists or not /// @param _id fee config id function exists(bytes32 _id) internal view returns (bool _exists) { _exists = LibFeeManagerStorage.feeManagerStorage().feeConfigs[_id].ftype != FeeType.Null; } /// Checks whether a fee config is in use on a specific chain or not /// @param _id fee config id function isFeeConfigInUse(bytes32 _id) internal view returns (bool _exists) { for (uint256 i = 0; i < store().chainIds.length; i++) { for (uint256 j = 0; j < store().chainIdFeeConfigMap[store().chainIds[i]].length; j++) { if (store().chainIdFeeConfigMap[store().chainIds[i]][j] == _id) { _exists = true; break; } } } } /// Gets the target address for a specific chain /// @param _chainId chain id /// @dev normally the address of the diamond on the target chain function getChainTarget(uint256 _chainId) internal view returns (address _target) { _target = store().chainTargets[_chainId]; } /// Gets the fee config by a given id /// @param _id fee config id function getFeeConfigById(bytes32 _id) internal view returns (FeeConfig memory _feeConfig) { LibFeeManagerStorage.FeeManagerStorage storage s = LibFeeManagerStorage.feeManagerStorage(); _feeConfig = s.feeConfigs[_id]; } /// internals /// Queues up a specific fee config for a specific chain with a specific action /// @param _id fee config id /// @param _chainId chain id /// @param _action action to execute on the target chain function queue(bytes32 _id, uint256 _chainId, FeeSyncAction _action) internal { LibFeeManagerStorage.FeeManagerStorage storage s = LibFeeManagerStorage.feeManagerStorage(); bool alreadInQueue = false; for (uint256 i = 0; i < s.feeSyncQueue[_chainId].length; i++) alreadInQueue = alreadInQueue || (s.feeSyncQueue[_chainId][i].id == _id && s.feeSyncQueue[_chainId][i].chainId == _chainId); if (!alreadInQueue) { s.feeSyncQueue[_chainId].push(FeeSyncQueue({ id: _id, chainId: _chainId, action: _action })); s.feeDeployState[_chainId][_id] = FeeDeployState.Queued; } } /// Simple archiving of fee configs /// @param _id fee config id /// will be called on update and delete of a fee config function archiveFeeConfig(bytes32 _id) internal { FeeConfig storage feeConfigToArchive = LibFeeManagerStorage.feeManagerStorage().feeConfigs[_id]; LibFeeManagerStorage.feeManagerStorage().feeConfigsArchive[_id].push(feeConfigToArchive); } /// store function store() internal pure returns (LibFeeManagerStorage.FeeManagerStorage storage _store) { _store = LibFeeManagerStorage.feeManagerStorage(); } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.19; import { FeeDeployState } from "./../helpers/Enums.sol"; import { FeeConfig, FeeSyncQueue } from "./../helpers/Structs.sol"; /// @title Lib Fee Manager Storage /// @author Daniel <[email protected]> /// @notice Storage for the Fee Manager Facet library LibFeeManagerStorage { bytes32 constant FEE_MANAGER_STORAGE_POSITION = keccak256("degenx.fee-manager.storage.v1"); struct FeeManagerStorage { // all available chains uint256[] chainIds; // all available configs bytes32[] feeConfigIds; // contract to chain assignments // chainId => contract mapping(uint256 => address) chainTargets; // fee config to chain assignment to store which config should be available on which chain // chainId => list of fee config ids mapping(uint256 => bytes32[]) chainIdFeeConfigMap; // flags for quick checks to avoid looping through chainIdFeeConfigMap // chainId => feeConfigId mapping(uint256 => mapping(bytes32 => bool)) chainIdFeeConfig; // flag if a specific chain is being supported // chainId => true/false mapping(uint256 => bool) isChainSupported; // fee config id to fee config mapping. The fee config itself doesn't need to know its id // feeConfigId => FeeConfig mapping(bytes32 => FeeConfig) feeConfigs; // fee config archive of recent fee config settings to a specific fee config id // feeConfigId => list of fee config variants mapping(bytes32 => FeeConfig[]) feeConfigsArchive; // queue for syncing configs with the target contracts // chainId => list of fee sync data mapping(uint256 => FeeSyncQueue[]) feeSyncQueue; // deployment state per chain per fee config id // chainId => fee config id => deployment state of a fee config mapping(uint256 => mapping(bytes32 => FeeDeployState)) feeDeployState; } /// store function feeManagerStorage() internal pure returns (FeeManagerStorage storage fms) { bytes32 position = FEE_MANAGER_STORAGE_POSITION; assembly { fms.slot := position } } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.17; import { IERC20Metadata } from "@solidstate/contracts/token/ERC20/metadata/IERC20Metadata.sol"; import { SafeERC20 } from "@solidstate/contracts/utils/SafeERC20.sol"; import { IERC20 } from "@solidstate/contracts/interfaces/IERC20.sol"; import { LibFeeStoreStorage } from "./LibFeeStoreStorage.sol"; import { ZeroValueNotAllowed, NotAllowed } from "./../helpers/GenericErrors.sol"; import { FeeStoreConfig, FeeConfigSyncHomeDTO, FeeConfigSyncHomeFees } from "./../helpers/Structs.sol"; /// @title Fee Store Library /// @author Daniel <[email protected]> /// @notice Functions to help with the fee store for other instances library LibFeeStore { using SafeERC20 for IERC20; uint256 constant DENOMINATOR_RELATIVE = 10 ** 5; // bps denominator uint256 constant DENOMINATOR_ABSOLUTE = 10 ** 4; error ZeroFees(); error FeeNotExisting(bytes32 id); error FeeExists(bytes32 id); event FeeConfigAdded(bytes32 indexed id); event FeeConfigUpdated(bytes32 indexed id); event FeeConfigDeleted(bytes32 indexed id); event FeeConfigMarkedAsDeleted(bytes32 indexed id); event FeesPrepared(uint256 amount, FeeConfigSyncHomeDTO candidate); /// Store a specific amount of fees in the store /// @param _feeConfigId fee config id /// @param _amount amount of tokens function putFees(bytes32 _feeConfigId, uint256 _amount) internal { if (_amount == 0) revert ZeroValueNotAllowed(); LibFeeStoreStorage.FeeStoreStorage storage s = LibFeeStoreStorage.feeStoreStorage(); FeeStoreConfig memory _config = s.feeConfigs[_feeConfigId]; if (_config.id == bytes32("")) revert NotAllowed(); s.collectedFees[_config.id] += _amount; s.collectedFeesTotal += _amount; } /// Prepares the fees collected on the store to be send to the home chain /// @return _dto the dto that will be used on the home chain for receiving and process fees /// @dev this method will also clean up every fee collected and sets it to 0 function prepareToSendFees() internal returns (FeeConfigSyncHomeDTO memory _dto) { LibFeeStoreStorage.FeeStoreStorage storage s = LibFeeStoreStorage.feeStoreStorage(); if (s.collectedFeesTotal == 0) revert ZeroFees(); uint256 _feeIndex = 0; uint256 _noOfExpectedFees = 0; // get how many fees need to get sent for (uint256 i = 0; i < s.feeConfigIds.length; ) { if (s.collectedFees[s.feeConfigIds[i]] > 0) _noOfExpectedFees++; unchecked { i++; } } // collect amounts and gathers configs _dto.fees = new FeeConfigSyncHomeFees[](_noOfExpectedFees); for (uint256 i = 0; i < s.feeConfigIds.length; ) { bytes32 _id = s.feeConfigIds[i]; if (s.collectedFees[_id] > 0) { uint256 _amount = s.collectedFees[_id]; s.collectedFees[_id] = 0; if (s.feeConfigs[_id].deleted) deleteFee(_id); _dto.totalFees += _amount; _dto.fees[_feeIndex] = FeeConfigSyncHomeFees({ id: _id, amount: _amount }); unchecked { _feeIndex++; } } unchecked { i++; } } s.collectedFeesTotal = 0; emit FeesPrepared(_dto.totalFees, _dto); } /// Removes a fee from the store /// @param _id fee id /// @dev if a fee is still in use, it will be marked as deleted. Once fees get moved to home chain, it will be deleted properly function deleteFee(bytes32 _id) internal { LibFeeStoreStorage.FeeStoreStorage storage s = LibFeeStoreStorage.feeStoreStorage(); if (s.feeConfigs[_id].id == bytes32(0)) revert FeeNotExisting(_id); if (s.collectedFees[_id] > 0) { s.feeConfigs[_id].deleted = true; emit FeeConfigMarkedAsDeleted(_id); } else { delete s.collectedFees[_id]; delete s.feeConfigs[_id]; for (uint256 i = 0; i < s.feeConfigIds.length; ) { if (s.feeConfigIds[i] == _id) { s.feeConfigIds[i] = s.feeConfigIds[s.feeConfigIds.length - 1]; break; } unchecked { i++; } } s.feeConfigIds.pop(); emit FeeConfigDeleted(_id); } } /// Adds a fee to the store /// @param _id fee id /// @param _fee fee value /// @param _target the target address function addFee(bytes32 _id, uint256 _fee, address _target) internal { LibFeeStoreStorage.FeeStoreStorage storage s = LibFeeStoreStorage.feeStoreStorage(); if (s.feeConfigs[_id].id != bytes32(0)) revert FeeExists(_id); s.feeConfigs[_id] = FeeStoreConfig({ id: _id, fee: _fee, target: _target, deleted: false }); s.feeConfigIds.push(_id); emit FeeConfigAdded(_id); } /// Updates a fee on the store /// @param _id fee id /// @param _fee fee value /// @param _target the target address function updateFee(bytes32 _id, uint256 _fee, address _target) internal { LibFeeStoreStorage.FeeStoreStorage storage s = LibFeeStoreStorage.feeStoreStorage(); if (s.feeConfigs[_id].id == bytes32(0)) revert FeeNotExisting(_id); s.feeConfigs[_id] = FeeStoreConfig({ id: _id, fee: _fee, target: _target, deleted: false }); emit FeeConfigUpdated(_id); } /// viewables /// Calculates the relative fee based on the inserted amount /// @param _feeConfigId fee config id /// @param _asset address of the token /// @param _amount amount that fees are based on /// @return _amountNet amount excluding fee /// @return _fee amount of fee /// @return _feePoints fee value that is applied function calcFeesRelative( bytes32 _feeConfigId, address _asset, uint256 _amount ) internal view returns (uint256 _amountNet, uint256 _fee, uint256 _feePoints) { return calcFees(_feeConfigId, _asset, _amount, false); } /// Calculates the absolute fee based on the inserted amount /// @param _feeConfigId fee config id /// @param _asset address of the token /// @param _amount amount that fees are based on /// @return _amountNet amount excluding fee /// @return _fee amount of fee /// @return _feePoints fee value that is applied function calcFeesAbsolute( bytes32 _feeConfigId, address _asset, uint256 _amount ) internal view returns (uint256 _amountNet, uint256 _fee, uint256 _feePoints) { return calcFees(_feeConfigId, _asset, _amount, true); } /// Calculates the relative or absolute fees based on the inserted amount /// @param _feeConfigId fee config id /// @param _asset address of the token /// @param _amount amount that fees are based on /// @param _absolute whether a calculation is relative or absolute /// @return _amountNet amount excluding fee /// @return _fee amount of fee /// @return _feePoints fee value that is applied function calcFees( bytes32 _feeConfigId, address _asset, uint256 _amount, bool _absolute ) internal view returns (uint256 _amountNet, uint256 _fee, uint256 _feePoints) { if (_amount == 0) revert ZeroValueNotAllowed(); LibFeeStoreStorage.FeeStoreStorage storage s = LibFeeStoreStorage.feeStoreStorage(); FeeStoreConfig memory _config = s.feeConfigs[_feeConfigId]; if (_config.id == bytes32("")) return (_amount, 0, 0); _feePoints = _config.fee; _fee = _absolute ? ((_feePoints * (10 ** IERC20Metadata(_asset).decimals())) / DENOMINATOR_ABSOLUTE) : ((_amount * _feePoints) / DENOMINATOR_RELATIVE); _amountNet = _amount - _fee; } function getOperator() internal view returns (address _operator) { LibFeeStoreStorage.FeeStoreStorage storage s = LibFeeStoreStorage.feeStoreStorage(); _operator = s.operator; } function setOperator(address _operator) internal { LibFeeStoreStorage.FeeStoreStorage storage s = LibFeeStoreStorage.feeStoreStorage(); s.operator = _operator; } function getIntermediateAsset() internal view returns (address _intermediateAsset) { LibFeeStoreStorage.FeeStoreStorage storage s = LibFeeStoreStorage.feeStoreStorage(); _intermediateAsset = s.intermediateAsset; } function setIntermediateAsset(address _intermediateAsset) internal { LibFeeStoreStorage.FeeStoreStorage storage s = LibFeeStoreStorage.feeStoreStorage(); s.intermediateAsset = _intermediateAsset; } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.19; import { FeeStoreConfig } from "./../helpers/Structs.sol"; library LibFeeStoreStorage { bytes32 constant FEE_STORE_STORAGE_POSITION = keccak256("degenx.fee-store.storage.v1"); struct FeeStoreStorage { // feeConfigId => FeeStoreConfig mapping(bytes32 => FeeStoreConfig) feeConfigs; // feeConfigId => amount of fees collected mapping(bytes32 => uint256) collectedFees; // represents a sum of each amount in collectedFees uint256 collectedFeesTotal; bytes32[] feeConfigIds; address operator; address intermediateAsset; } function feeStoreStorage() internal pure returns (FeeStoreStorage storage fss) { bytes32 position = FEE_STORE_STORAGE_POSITION; assembly { fss.slot := position } } }
{ "optimizer": { "enabled": true, "runs": 99999 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_relayer","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AddressUtils__NotContract","type":"error"},{"inputs":[],"name":"AddressZero","type":"error"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"}],"name":"ChainExisting","type":"error"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"}],"name":"ChainNotExisting","type":"error"},{"inputs":[{"internalType":"bytes32","name":"id","type":"bytes32"}],"name":"FeeNotExisting","type":"error"},{"inputs":[],"name":"InsufficientFundsForGas","type":"error"},{"inputs":[],"name":"InsufficientFundsSent","type":"error"},{"inputs":[],"name":"NoChainsConfigured","type":"error"},{"inputs":[],"name":"NotAllowed","type":"error"},{"inputs":[],"name":"QueueEmpty","type":"error"},{"inputs":[],"name":"RefundFailed","type":"error"},{"inputs":[{"internalType":"address","name":"relayer","type":"address"}],"name":"RelayerExists","type":"error"},{"inputs":[],"name":"SafeERC20__OperationFailed","type":"error"},{"inputs":[],"name":"ThresholdNotMet","type":"error"},{"inputs":[],"name":"ZeroFees","type":"error"},{"inputs":[],"name":"ZeroValueNotAllowed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"chainId","type":"uint64"}],"name":"DeploymentSuccessful","type":"event"},{"anonymous":false,"inputs":[],"name":"FeesSent","type":"event"},{"anonymous":false,"inputs":[],"name":"QueueProcessed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RefundCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RefundForwarded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"relayer","type":"address"},{"indexed":false,"internalType":"uint256","name":"chainId","type":"uint256"}],"name":"RelayerForChainAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"chainId","type":"uint256"}],"name":"RelayerForChainRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"relayer","type":"address"},{"indexed":false,"internalType":"uint256","name":"chainId","type":"uint256"}],"name":"RelayerForChainUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"UpdateDeployFeesWei","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"UpdateSendFeesWei","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"UpdateThreshold","type":"event"},{"inputs":[{"internalType":"address","name":"_relayer","type":"address"},{"internalType":"uint256","name":"_chainId","type":"uint256"}],"name":"addRelayerForChain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"celerFeeHubDeployFeesWei","outputs":[{"internalType":"uint256","name":"_wei","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"celerFeeHubSendFeesWei","outputs":[{"internalType":"uint256","name":"_wei","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deployFeesWithCeler","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint64","name":"_chainId","type":"uint64"},{"internalType":"bytes","name":"_message","type":"bytes"}],"name":"deployFeesWithCelerConfirm","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chainId","type":"uint256"}],"name":"removeRelayerOnChain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"minMaxSlippage","type":"uint32"},{"internalType":"address","name":"_bountyReceiver","type":"address"}],"name":"sendFeesWithCeler","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_wei","type":"uint256"}],"name":"updateDeployFeesWei","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_relayer","type":"address"},{"internalType":"uint256","name":"_chainId","type":"uint256"}],"name":"updateRelayerOnChain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"updateSendFeesThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_wei","type":"uint256"}],"name":"updateSendFeesWei","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Deployed Bytecode
0x6080604052600436106100bc5760003560e01c80637a1371da11610074578063a473b6f71161004e578063a473b6f7146101c0578063c822ba77146101f4578063ded23a7d1461021457600080fd5b80637a1371da1461014b5780637c4d3baa1461015e57806389a0062e1461017e57600080fd5b806329e2dced116100a557806329e2dced146100eb57806333c5d8281461010b5780633479024c1461012b57600080fd5b8063109e55db146100c157806320820526146100cb575b600080fd5b6100c9610234565b005b3480156100d757600080fd5b506100c96100e6366004612251565b610983565b3480156100f757600080fd5b506100c9610106366004612293565b610a2a565b34801561011757600080fd5b506100c9610126366004612251565b610bcc565b34801561013757600080fd5b506100c96101463660046122bd565b610c6b565b6100c9610159366004612349565b610dc4565b34801561016a57600080fd5b506100c9610179366004612251565b6111f7565b34801561018a57600080fd5b507f24f78524ef4056b808e3f69d6b6961a56fc684a855ba89c52cb8f85660055cff545b60405190815260200160405180910390f35b3480156101cc57600080fd5b507f24f78524ef4056b808e3f69d6b6961a56fc684a855ba89c52cb8f85660055d00546101ae565b34801561020057600080fd5b506100c961020f366004612293565b611327565b34801561022057600080fd5b506100c961022f366004612251565b611534565b7f9c0d1aa28706b015335cfcad9f64557dc37cf2a3cb8f83623896252dbb48dbd67f24f78524ef4056b808e3f69d6b6961a56fc684a855ba89c52cb8f85660055cfe348015806102875750808260020154115b156102be576040517f5941c78a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008360000180548060200260200160405190810160405280929190818152602001828054801561030e57602002820191906000526020600020905b8154815260200190600101908083116102fa575b505050505090508051600003610350576040517f5a60568500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600060088501815b83518110156109045781600085838151811061037657610376612387565b6020026020010151815260200190815260200160002080549050600003156108f257826103a257600192505b60008482815181106103b6576103b6612387565b602090810291909101810151600081815260038a019092526040909120549091506103f69073ffffffffffffffffffffffffffffffffffffffff166115b1565b60008181526020849052604081205467ffffffffffffffff81111561041d5761041d6123b6565b60405190808252806020026020018201604052801561047657816020015b6104636040805160808101825260008082526020820181905291810182905290606082015290565b81526020019060019003908161043b5790505b50905060005b6000838152602086905260409020548110156106105760008381526020869052604081208054839081106104b2576104b2612387565b906000526020600020906003020160000154905060008b600601600083815260200190815260200160002090506040518060800160405280838152602001826000015481526020018d600201600088815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001886000888152602001908152602001600020858154811061056e5761056e612387565b600091825260209091206003918202016002015460ff1690811115610595576105956123e5565b8152508484815181106105aa576105aa612387565b602090810291909101810191909152600086815260098e0182526040808220948252939091529190912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166002179055508061060881612443565b91505061047c565b5060008281527f9c0d1aa28706b015335cfcad9f64557dc37cf2a3cb8f83623896252dbb48dbd8602052604081205473ffffffffffffffffffffffffffffffffffffffff169050600063a2ffce2d60e01b83604051602401610672919061247b565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290517fa111002d00000000000000000000000000000000000000000000000000000000815290915060009073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000083cbcd2a68e8479d98a5bf173a8fe124612f31e3169063a111002d90610768908690869060040161259b565b602060405180830381865afa158015610785573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a991906125ca565b9050898111156107e5576040517f929c816700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107ef818b6125e3565b600086815260038d016020526040908190205490517ffed39ad3000000000000000000000000000000000000000000000000000000008152919b5073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000083cbcd2a68e8479d98a5bf173a8fe124612f31e381169263fed39ad39285926108809291169088908b9089906004016125f6565b6000604051808303818588803b15801561089957600080fd5b505af11580156108ad573d6000803e3d6000fd5b50505050508b60080160008a88815181106108ca576108ca612387565b6020026020010151815260200190815260200160002060006108ec91906121ec565b50505050505b806108fc81612443565b915050610358565b50811561094957831561091b5761091b3385611601565b6040517f99d80d9c5a59a573dfcc791e04e7b201c0d0b256e90cca6361c13d4a4eebca9890600090a161097b565b6040517f75e52f4f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050565b6109ac7f6c0757dc3e6b28b2580c03fd9e96c274acf4f99d91fbec9b418fa1d70604ff1c611760565b7f24f78524ef4056b808e3f69d6b6961a56fc684a855ba89c52cb8f85660055d008190556040518181527f24f78524ef4056b808e3f69d6b6961a56fc684a855ba89c52cb8f85660055cfe907fb58b26187f60774f93d6d34d2450f9d52f6b79fa3ba8b0278a048ccd7b72f89d906020015b60405180910390a15050565b610a537f6c0757dc3e6b28b2580c03fd9e96c274acf4f99d91fbec9b418fa1d70604ff1c611760565b610a5c826115b1565b80600003610a96576040517f9cf8540c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181527f24f78524ef4056b808e3f69d6b6961a56fc684a855ba89c52cb8f85660055d0160205260409020547f24f78524ef4056b808e3f69d6b6961a56fc684a855ba89c52cb8f85660055cfe9073ffffffffffffffffffffffffffffffffffffffff1615610b3b576040517f67ba2f65000000000000000000000000000000000000000000000000000000008152600481018390526024015b60405180910390fd5b600082815260038201602090815260409182902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff871690811790915582519081529081018490527fb3dd29c09100314d215560265c31aa486d6cbc91268851d11954941e6acfda9891015b60405180910390a1505050565b610bf57f6c0757dc3e6b28b2580c03fd9e96c274acf4f99d91fbec9b418fa1d70604ff1c611760565b7f24f78524ef4056b808e3f69d6b6961a56fc684a855ba89c52cb8f85660055cff8190556040518181527f24f78524ef4056b808e3f69d6b6961a56fc684a855ba89c52cb8f85660055cfe907fb93f0bd235e1dea016e9feb4ea4f2a3708be4da464517330fc53d0af332844e390602001610a1e565b7f00000000000000000000000083cbcd2a68e8479d98a5bf173a8fe124612f31e373ffffffffffffffffffffffffffffffffffffffff163314610cda576040517f3d693ada00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f9c0d1aa28706b015335cfcad9f64557dc37cf2a3cb8f83623896252dbb48dbd66000610d0a836004818761263f565b810190610d1791906126e1565b905060005b815181101561097b5760038360090160008867ffffffffffffffff1681526020019081526020016000206000848481518110610d5a57610d5a612387565b60209081029190910181015151825281019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001836003811115610dad57610dad6123e5565b021790555080610dbc81612443565b915050610d1c565b7f24f78524ef4056b808e3f69d6b6961a56fc684a855ba89c52cb8f85660055cfe6000610def61176a565b825481519192501115610e2e576040517f59fa4a9300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b34801580610e3f5750808360010154115b15610e76576040517f5941c78a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84161580610eb0575073ffffffffffffffffffffffffffffffffffffffff841661dead145b15610ee7576040517f9fabe1c100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff841660208301528151610f86907f00000000000000000000000083cbcd2a68e8479d98a5bf173a8fe124612f31e390610f697f1ef17e9a165a4e1a08bf7c7a70090c7711ef4b97027937554becb896058c668e5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff169190611a0d565b600082604051602001610f999190612859565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290527f42584071000000000000000000000000000000000000000000000000000000008252915060009073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000083cbcd2a68e8479d98a5bf173a8fe124612f31e3169063425840719061103c90859060040161286c565b602060405180830381865afa158015611059573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061107d91906125ca565b9050828111156110b9576040517f929c816700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110c381846125e3565b92507f00000000000000000000000083cbcd2a68e8479d98a5bf173a8fe124612f31e373ffffffffffffffffffffffffffffffffffffffff16639dd004d8826111407f1ef17e9a165a4e1a08bf7c7a70090c7711ef4b97027937554becb896058c668e5473ffffffffffffffffffffffffffffffffffffffff1690565b87516040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b1681526111809291908d90899060040161287f565b6000604051808303818588803b15801561119957600080fd5b505af11580156111ad573d6000803e3d6000fd5b505050505060008311156111c5576111c53384611601565b6040517f39482fcdcea66858b1c12e7c5884615e8c741c88dad07351a5f0a76199006e6490600090a150505050505050565b6112207f6c0757dc3e6b28b2580c03fd9e96c274acf4f99d91fbec9b418fa1d70604ff1c611760565b60008181527f24f78524ef4056b808e3f69d6b6961a56fc684a855ba89c52cb8f85660055d0160205260409020547f24f78524ef4056b808e3f69d6b6961a56fc684a855ba89c52cb8f85660055cfe9073ffffffffffffffffffffffffffffffffffffffff166112bf576040517f03d939b400000000000000000000000000000000000000000000000000000000815260048101839052602401610b32565b60008281526003820160205260409081902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055517fa697396417171fd2c05f8319c533f9f0f18be6c2e94ae78c7e2bfc15b05c272c90610a1e9084815260200190565b6113507f6c0757dc3e6b28b2580c03fd9e96c274acf4f99d91fbec9b418fa1d70604ff1c611760565b611359826115b1565b80600003611393576040517f9cf8540c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181527f24f78524ef4056b808e3f69d6b6961a56fc684a855ba89c52cb8f85660055d0160205260409020547f24f78524ef4056b808e3f69d6b6961a56fc684a855ba89c52cb8f85660055cfe9073ffffffffffffffffffffffffffffffffffffffff16611432576040517f03d939b400000000000000000000000000000000000000000000000000000000815260048101839052602401610b32565b600082815260038201602052604090205473ffffffffffffffffffffffffffffffffffffffff8085169116036114ac576040517f308e624200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152602401610b32565b600082815260038201602090815260409182902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff871690811790915582519081529081018490527f01f1a56eefb94264a7cdc1ad53ed314ed7ee4546fcc3d18b21dac910e0daa4339101610bbf565b61155d7f6c0757dc3e6b28b2580c03fd9e96c274acf4f99d91fbec9b418fa1d70604ff1c611760565b7f24f78524ef4056b808e3f69d6b6961a56fc684a855ba89c52cb8f85660055cfe8181556040518281527fcfbc3e24bcdbf875f7af2b101605919fdbd3cb0b217ae54c372ae49417c58d0190602001610a1e565b73ffffffffffffffffffffffffffffffffffffffff81166115fe576040517f9fabe1c100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b8047101561166b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610b32565b60008273ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d80600081146116c5576040519150601f19603f3d011682016040523d82523d6000602084013e6116ca565b606091505b505090508061175b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610b32565b505050565b6115fe8133611a9a565b6040805160608082018352600080835260208301819052928201527f1ef17e9a165a4e1a08bf7c7a70090c7711ef4b97027937554becb896058c668b5490917f1ef17e9a165a4e1a08bf7c7a70090c7711ef4b97027937554becb896058c6689919003611803576040517f4716db3500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060005b600384015481101561186857600084600101600086600301848154811061183257611832612387565b90600052602060002001548152602001908152602001600020541115611860578161185c81612443565b9250505b600101611809565b508067ffffffffffffffff811115611882576118826123b6565b6040519080825280602002602001820160405280156118c757816020015b60408051808201909152600080825260208201528152602001906001900390816118a05790505b50604085015260005b60038401548110156119c55760008460030182815481106118f3576118f3612387565b9060005260206000200154905060008560010160008381526020019081526020016000205411156119bc5760008181526001860160209081526040808320805490849055918890529091206002015474010000000000000000000000000000000000000000900460ff161561196b5761196b82611b2f565b808760000181815161197d91906128c0565b90525060408051808201825283815260208101839052908801518051879081106119a9576119a9612387565b6020908102919091010152506001909301925b506001016118d0565b506000600284015583516040517fb2850f52591cabccaf2c842c91203cdb1fb29d1dcc7d565d5ec824772f224ef5916119ff9187906128d3565b60405180910390a150505090565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb0000000000000000000000000000000000000000000000000000000017905261175b908490611d60565b611aa48282611e16565b611b2b57611ab181611e6f565b611abc836020611e8e565b604051602001611acd9291906128ec565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290527f08c379a0000000000000000000000000000000000000000000000000000000008252610b329160040161286c565b5050565b60008181527f1ef17e9a165a4e1a08bf7c7a70090c7711ef4b97027937554becb896058c66896020819052604090912054611b99576040517f967a306300000000000000000000000000000000000000000000000000000000815260048101839052602401610b32565b600082815260018201602052604090205415611c285760008281526020829052604080822060020180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790555183917fbd3861da79e4ee6609c96a5104039bd715225aa8ac69760ca3320d5d115d3d0891a25050565b600082815260018083016020908152604080842084905590849052822082815590810182905560020180547fffffffffffffffffffffff0000000000000000000000000000000000000000001690555b6003820154811015611d055782826003018281548110611c9a57611c9a612387565b906000526020600020015403611cfd57600382018054611cbc906001906125e3565b81548110611ccc57611ccc612387565b9060005260206000200154826003018281548110611cec57611cec612387565b600091825260209091200155611d05565b600101611c78565b5080600301805480611d1957611d1961296d565b60019003818190600052602060002001600090559055817f9a58bb2e15d0792adb797ae6cd2b8be9f03d3a5410b824cd14bc44c67630c44660405160405180910390a25050565b6000611dc2826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166120d89092919063ffffffff16565b80519091501561175b5780806020019051810190611de0919061299c565b61175b576040517fdb59782000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281527ff72ca309d9a3d085eca8abc0d0f1efd7a9682e454e7a123eb852dac23bbd2afa6020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff165b92915050565b6060611e6973ffffffffffffffffffffffffffffffffffffffff831660145b60606000611e9d8360026129be565b611ea89060026128c0565b67ffffffffffffffff811115611ec057611ec06123b6565b6040519080825280601f01601f191660200182016040528015611eea576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110611f2157611f21612387565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110611f8457611f84612387565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506000611fc08460026129be565b611fcb9060016128c0565b90505b6001811115612068577f303132333435363738396162636465660000000000000000000000000000000085600f166010811061200c5761200c612387565b1a60f81b82828151811061202257612022612387565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c93612061816129d5565b9050611fce565b5083156120d1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610b32565b9392505050565b60606120e784846000856120ef565b949350505050565b6060843b612129576040517f89c35afc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516121529190612a0a565b60006040518083038185875af1925050503d806000811461218f576040519150601f19603f3d011682016040523d82523d6000602084013e612194565b606091505b509150915081156121a85791506120e79050565b8051156121b85780518082602001fd5b836040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b32919061286c565b50805460008255600302906000526020600020908101906115fe91905b8082111561224d57600080825560018201556002810180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055600301612209565b5090565b60006020828403121561226357600080fd5b5035919050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461228e57600080fd5b919050565b600080604083850312156122a657600080fd5b6122af8361226a565b946020939093013593505050565b6000806000604084860312156122d257600080fd5b833567ffffffffffffffff80821682146122eb57600080fd5b9093506020850135908082111561230157600080fd5b818601915086601f83011261231557600080fd5b81358181111561232457600080fd5b87602082850101111561233657600080fd5b6020830194508093505050509250925092565b6000806040838503121561235c57600080fd5b823563ffffffff8116811461237057600080fd5b915061237e6020840161226a565b90509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361247457612474612414565b5060010190565b60208082528251828201819052600091906040908185019086840185805b8381101561251f5782518051865287810151888701528681015173ffffffffffffffffffffffffffffffffffffffff1687870152606090810151906004808310612509577f4e487b7100000000000000000000000000000000000000000000000000000000855260218152602485fd5b5086015260809094019391860191600101612499565b509298975050505050505050565b60005b83811015612548578181015183820152602001612530565b50506000910152565b6000815180845261256981602086016020860161252d565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b73ffffffffffffffffffffffffffffffffffffffff831681526040602082015260006120e76040830184612551565b6000602082840312156125dc57600080fd5b5051919050565b81810381811115611e6957611e69612414565b600073ffffffffffffffffffffffffffffffffffffffff8087168352808616602084015250836040830152608060608301526126356080830184612551565b9695505050505050565b6000808585111561264f57600080fd5b8386111561265c57600080fd5b5050820193919092039150565b6040516080810167ffffffffffffffff8111828210171561268c5761268c6123b6565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156126d9576126d96123b6565b604052919050565b600060208083850312156126f457600080fd5b823567ffffffffffffffff8082111561270c57600080fd5b818501915085601f83011261272057600080fd5b813581811115612732576127326123b6565b612740848260051b01612692565b818152848101925060079190911b83018401908782111561276057600080fd5b928401925b818410156127cd576080848903121561277e5760008081fd5b612786612669565b843581528585013586820152604061279f81870161226a565b90820152606085810135600481106127b75760008081fd5b9082015283526080939093019291840191612765565b979650505050505050565b60006060830182518452602073ffffffffffffffffffffffffffffffffffffffff8185015116818601526040808501516060828801528381518086526080890191508483019550600092505b8083101561284d5785518051835285015185830152948401946001929092019190830190612824565b50979650505050505050565b6020815260006120d160208301846127d8565b6020815260006120d16020830184612551565b73ffffffffffffffffffffffffffffffffffffffff8516815283602082015263ffffffff831660408201526080606082015260006126356080830184612551565b80820180821115611e6957611e69612414565b8281526040602082015260006120e760408301846127d8565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161292481601785016020880161252d565b7f206973206d697373696e6720726f6c6520000000000000000000000000000000601791840191820152835161296181602884016020880161252d565b01602801949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6000602082840312156129ae57600080fd5b815180151581146120d157600080fd5b8082028115828204841417611e6957611e69612414565b6000816129e4576129e4612414565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b60008251612a1c81846020870161252d565b919091019291505056fea264697066735822122039ffd62b0d0b552e00d4d3b9e8f10a142e33358904b875b38c302d1e6a8bd51e64736f6c63430008130033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.