Source Code
Overview
HYPE Balance
HYPE Value
$0.00Latest 20 from a total of 20 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Add Supported To... | 25644280 | 4 hrs ago | IN | 0 HYPE | 0.00047204 | ||||
| Deposit To Hyper... | 25091356 | 6 days ago | IN | 0 HYPE | 0.00001214 | ||||
| Deposit To Hyper... | 24977719 | 7 days ago | IN | 0 HYPE | 0.00001214 | ||||
| Deposit To Hyper... | 24947525 | 8 days ago | IN | 0 HYPE | 0.00001214 | ||||
| Deposit To Hyper... | 24687546 | 11 days ago | IN | 0 HYPE | 0.00001214 | ||||
| Deposit To Hyper... | 24637057 | 11 days ago | IN | 0 HYPE | 0.00001214 | ||||
| Deposit To Hyper... | 24294786 | 15 days ago | IN | 0 HYPE | 0.00001275 | ||||
| Deposit To Hyper... | 24071813 | 18 days ago | IN | 0 HYPE | 0.00001214 | ||||
| Deposit To Hyper... | 23740906 | 21 days ago | IN | 0 HYPE | 0.00001214 | ||||
| Deposit To Hyper... | 23740452 | 21 days ago | IN | 0 HYPE | 0.00001214 | ||||
| Deposit To Hyper... | 22259709 | 38 days ago | IN | 0 HYPE | 0.00001245 | ||||
| Deposit To Hyper... | 22125113 | 40 days ago | IN | 0 HYPE | 0.00001245 | ||||
| Deposit To Hyper... | 22096392 | 40 days ago | IN | 0 HYPE | 0.00012732 | ||||
| Deposit To Hyper... | 21905203 | 42 days ago | IN | 0 HYPE | 0.00001245 | ||||
| Deposit To Hyper... | 21900506 | 42 days ago | IN | 0 HYPE | 0.00011688 | ||||
| Deposit To Hyper... | 21900384 | 42 days ago | IN | 0 HYPE | 0.00011936 | ||||
| Sweep Donation B... | 21298861 | 49 days ago | IN | 0 HYPE | 0.00013933 | ||||
| Sweep Core Funds... | 20952176 | 53 days ago | IN | 0 HYPE | 0.00000324 | ||||
| Sweep Core Funds... | 20952138 | 53 days ago | IN | 0 HYPE | 0.00001333 | ||||
| Add Supported To... | 20301679 | 61 days ago | IN | 0 HYPE | 0.00005485 |
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 20301679 | 61 days ago | Contract Creation | 0 HYPE |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
HyperliquidDepositHandler
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 800 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.0;
import "../interfaces/SpokePoolMessageHandler.sol";
import "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts-v4/security/ReentrancyGuard.sol";
import { HyperCoreLib } from "../libraries/HyperCoreLib.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { DonationBox } from "../chain-adapters/DonationBox.sol";
/**
* @title Allows caller to bridge tokens from HyperEVM to Hypercore and send them to an end user's account
* on Hypercore.
* @dev This contract should only be deployed on HyperEVM.
* @dev This contract can replace a MulticallHandler on HyperEVM if the intent only wants to deposit tokens into
* Hypercore and bypass the other complex arbitrary calldata logic.
* @dev This contract can also be called directly to deposit tokens into Hypercore on behalf of an end user.
*/
contract HyperliquidDepositHandler is AcrossMessageHandler, ReentrancyGuard, Ownable {
using SafeERC20 for IERC20;
struct TokenInfo {
// HyperEVM token address.
address evmAddress;
// Hypercore token index.
uint64 tokenId;
// Activation fee in EVM units. e.g. 1000000 ($1) for USDH.
uint256 activationFeeEvm;
// coreDecimals - evmDecimals. e.g. -2 for USDH.
int8 decimalDiff;
}
// Stores hardcoded Hypercore configurations for tokens that this handler supports.
mapping(address => TokenInfo) public supportedTokens;
// Donation box contract to store funds for account activation fees.
DonationBox public immutable donationBox;
error InsufficientEvmAmountForActivation();
error TokenNotSupported();
event UserAccountActivated(address user, address indexed token, uint256 amountRequiredToActivate);
/**
* @notice Constructor.
* @dev Creates a new donation box contract owned by this contract.
*/
constructor() Ownable(msg.sender) {
donationBox = new DonationBox();
}
/**
* @notice Adds a new token to the supported tokens list.
* @dev Caller must be owner of this contract.
* @param evmAddress The address of the EVM token.
* @param tokenId The index of the Hypercore token.
* @param activationFeeEvm The activation fee in EVM units.
* @param decimalDiff The difference in decimals between the EVM and Hypercore tokens.
*/
function addSupportedToken(
address evmAddress,
uint64 tokenId,
uint256 activationFeeEvm,
int8 decimalDiff
) external onlyOwner {
supportedTokens[evmAddress] = TokenInfo({
evmAddress: evmAddress,
tokenId: tokenId,
activationFeeEvm: activationFeeEvm,
decimalDiff: decimalDiff
});
}
/**
* @notice Bridges tokens from HyperEVM to Hypercore and sends them to the end user's account on Hypercore.
* @dev Requires msg.sender to have approved this contract to spend the tokens.
* @param token The address of the token to deposit.
* @param amount The amount of tokens on HyperEVM to deposit.
* @param user The address of the user on Hypercore to send the tokens to.
*/
function depositToHypercore(address token, uint256 amount, address user) external nonReentrant {
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
_depositToHypercore(token, amount, user);
}
/**
* @notice Entrypoint function if this contract is called by the SpokePool contract following an intent fill.
* @dev Deposits tokens into Hypercore and sends them to the end user's account on Hypercore.
* @param token The address of the token sent.
* @param amount The amount of tokens received by this contract.
* @param message Encoded end user address.
*/
function handleV3AcrossMessage(
address token,
uint256 amount,
address /* relayer */,
bytes memory message
) external nonReentrant {
address user = abi.decode(message, (address));
_depositToHypercore(token, amount, user);
}
/**
* @notice Send Hypercore funds to a user from this contract's Hypercore account
* @dev The coreAmount parameter is specified in Hypercore units which often differs from the EVM units for the
* same token.
* @param token The token address
* @param coreAmount The amount of tokens on Hypercore to sweep
* @param user The address of the user to send the tokens to
*/
function sweepCoreFundsToUser(address token, uint64 coreAmount, address user) external onlyOwner nonReentrant {
uint64 tokenIndex = _getTokenInfo(token).tokenId;
HyperCoreLib.transferERC20CoreToCore(tokenIndex, user, coreAmount);
}
/**
* @notice Send donation box funds to a user from this contract's address on HyperEVM
* @param token The token address
* @param amount The amount of tokens to sweep
* @param user The address of the user to send the tokens to
*/
function sweepDonationBoxFundsToUser(address token, uint256 amount, address user) external onlyOwner nonReentrant {
donationBox.withdraw(IERC20(token), amount);
IERC20(token).safeTransfer(user, amount);
}
/**
* @notice Send ERC20 tokens to a user from this contract's address on HyperEVM
* @param token The token address
* @param evmAmount The amount of tokens to sweep
* @param user The address of the user to send the tokens to
*/
function sweepERC20ToUser(address token, uint256 evmAmount, address user) external onlyOwner nonReentrant {
IERC20(token).safeTransfer(user, evmAmount);
}
function _depositToHypercore(address token, uint256 evmAmount, address user) internal {
TokenInfo memory tokenInfo = _getTokenInfo(token);
uint64 tokenIndex = tokenInfo.tokenId;
int8 decimalDiff = tokenInfo.decimalDiff;
bool userExists = HyperCoreLib.coreUserExists(user);
if (!userExists) {
// To activate an account, we must pay the activation fee from this contract's core account and then send 1
// wei to the user's account, so we pull the activation fee + 1 wei from the donation box. This contract
// does not allow the end user subtracting part of their received amount to use for the activation fee.
uint256 activationFee = tokenInfo.activationFeeEvm;
uint256 amountRequiredToActivate = activationFee + 1;
donationBox.withdraw(IERC20(token), amountRequiredToActivate);
// Deposit the activation fee + 1 wei into this contract's core account to pay for the user's
// account activation.
HyperCoreLib.transferERC20EVMToSelfOnCore(token, tokenIndex, amountRequiredToActivate, decimalDiff);
HyperCoreLib.transferERC20CoreToCore(tokenIndex, user, 1);
emit UserAccountActivated(user, token, amountRequiredToActivate);
}
HyperCoreLib.transferERC20EVMToCore(token, tokenIndex, user, evmAmount, decimalDiff);
}
function _getTokenInfo(address evmAddress) internal view returns (TokenInfo memory) {
if (supportedTokens[evmAddress].evmAddress == address(0)) {
revert TokenNotSupported();
}
return supportedTokens[evmAddress];
}
// Native tokens are not supported by this contract, so there is no fallback function.
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// This interface is expected to be implemented by any contract that expects to receive messages from the SpokePool.
interface AcrossMessageHandler {
function handleV3AcrossMessage(
address tokenSent,
uint256 amount,
address relayer,
bytes memory message
) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol";
interface ICoreWriter {
function sendRawAction(bytes calldata data) external;
}
library HyperCoreLib {
using SafeERC20 for IERC20;
// Time-in-Force order types
enum Tif {
None, // invalid
ALO, // Add Liquidity Only
GTC, // Good-Till-Cancel
IOC // Immediate-or-Cancel
}
struct SpotBalance {
uint64 total;
uint64 hold; // Unused in this implementation
uint64 entryNtl; // Unused in this implementation
}
struct TokenInfo {
string name;
uint64[] spots;
uint64 deployerTradingFeeShare;
address deployer;
address evmContract;
uint8 szDecimals;
uint8 weiDecimals;
int8 evmExtraWeiDecimals;
}
struct CoreUserExists {
bool exists;
}
// Base asset bridge addresses
address public constant BASE_ASSET_BRIDGE_ADDRESS = 0x2000000000000000000000000000000000000000;
uint256 public constant BASE_ASSET_BRIDGE_ADDRESS_UINT256 = uint256(uint160(BASE_ASSET_BRIDGE_ADDRESS));
// Precompile addresses
address public constant SPOT_BALANCE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000801;
address public constant SPOT_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000808;
address public constant CORE_USER_EXISTS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000810;
address public constant TOKEN_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080C;
address public constant CORE_WRITER_PRECOMPILE_ADDRESS = 0x3333333333333333333333333333333333333333;
// CoreWriter action headers
bytes4 public constant LIMIT_ORDER_HEADER = 0x01000001; // version=1, action=1
bytes4 public constant SPOT_SEND_HEADER = 0x01000006; // version=1, action=6
bytes4 public constant CANCEL_BY_CLOID_HEADER = 0x0100000B; // version=1, action=11
// Errors
error LimitPxIsZero();
error OrderSizeIsZero();
error InvalidTif();
error CoreUserAlreadyExists();
error InsufficientCoreBalanceToActivateAccount();
error InsufficientEvmBalanceToActivateAccount();
error SpotBalancePrecompileCallFailed();
error CoreUserExistsPrecompileCallFailed();
error TokenInfoPrecompileCallFailed();
error SpotPxPrecompileCallFailed();
/**
* @notice Transfer `amountEVM` from HyperEVM to `to` on HyperCore.
* @dev Returns the amount credited on Core in Core units (post conversion).
* @param erc20EVMAddress The address of the ERC20 token on HyperEVM
* @param erc20CoreIndex The HyperCore index id of the token to transfer
* @param to The address to receive tokens on HyperCore
* @param amountEVM The amount to transfer on HyperEVM
* @param decimalDiff The decimal difference of evmDecimals - coreDecimals
* @return amountEVMSent The amount sent on HyperEVM
* @return amountCoreToReceive The amount credited on Core in Core units (post conversion)
*/
function transferERC20EVMToCore(
address erc20EVMAddress,
uint64 erc20CoreIndex,
address to,
uint256 amountEVM,
int8 decimalDiff
) internal returns (uint256 amountEVMSent, uint64 amountCoreToReceive) {
// if the transfer amount exceeds the bridge balance, this wil revert
(uint256 _amountEVMToSend, uint64 _amountCoreToReceive) = maximumEVMSendAmountToAmounts(amountEVM, decimalDiff);
if (_amountEVMToSend != 0) {
// Transfer the tokens to this contract's address on HyperCore
IERC20(erc20EVMAddress).safeTransfer(toAssetBridgeAddress(erc20CoreIndex), _amountEVMToSend);
// Transfer the tokens from this contract on HyperCore to the `to` address on HyperCore
transferERC20CoreToCore(erc20CoreIndex, to, _amountCoreToReceive);
}
return (_amountEVMToSend, _amountCoreToReceive);
}
/**
* @notice Bridges `amountEVM` of `erc20` from this address on HyperEVM to this address on HyperCore.
* @dev Returns the amount credited on Core in Core units (post conversion).
* @dev The decimal difference is evmDecimals - coreDecimals
* @param erc20EVMAddress The address of the ERC20 token on HyperEVM
* @param erc20CoreIndex The HyperCore index id of the token to transfer
* @param amountEVM The amount to transfer on HyperEVM
* @param decimalDiff The decimal difference of evmDecimals - coreDecimals
* @return amountEVMSent The amount sent on HyperEVM
* @return amountCoreToReceive The amount credited on Core in Core units (post conversion)
*/
function transferERC20EVMToSelfOnCore(
address erc20EVMAddress,
uint64 erc20CoreIndex,
uint256 amountEVM,
int8 decimalDiff
) internal returns (uint256 amountEVMSent, uint64 amountCoreToReceive) {
(uint256 _amountEVMToSend, uint64 _amountCoreToReceive) = maximumEVMSendAmountToAmounts(amountEVM, decimalDiff);
if (_amountEVMToSend != 0) {
// Transfer the tokens to this contract's address on HyperCore
IERC20(erc20EVMAddress).safeTransfer(toAssetBridgeAddress(erc20CoreIndex), _amountEVMToSend);
}
return (_amountEVMToSend, _amountCoreToReceive);
}
/**
* @notice Transfers tokens from this contract on HyperCore to the `to` address on HyperCore
* @param erc20CoreIndex The HyperCore index id of the token
* @param to The address to receive tokens on HyperCore
* @param amountCore The amount to transfer on HyperCore
*/
function transferERC20CoreToCore(uint64 erc20CoreIndex, address to, uint64 amountCore) internal {
bytes memory action = abi.encode(to, erc20CoreIndex, amountCore);
bytes memory payload = abi.encodePacked(SPOT_SEND_HEADER, action);
ICoreWriter(CORE_WRITER_PRECOMPILE_ADDRESS).sendRawAction(payload);
}
/**
* @notice Submit a limit order on HyperCore.
* @dev Expects price & size already scaled by 1e8 per HyperCore spec.
* @param asset The asset index of the order
* @param isBuy Whether the order is a buy order
* @param limitPriceX1e8 The limit price of the order scaled by 1e8
* @param sizeX1e8 The size of the order scaled by 1e8
* @param reduceOnly If true, only reduce existing position rather than opening a new opposing order
* @param tif Time-in-Force: ALO, GTC, IOC (None invalid)
* @param cloid The client order id of the order, 0 means no cloid
*/
function submitLimitOrder(
uint32 asset,
bool isBuy,
uint64 limitPriceX1e8,
uint64 sizeX1e8,
bool reduceOnly,
Tif tif,
uint128 cloid
) internal {
// Basic sanity checks
if (limitPriceX1e8 == 0) revert LimitPxIsZero();
if (sizeX1e8 == 0) revert OrderSizeIsZero();
if (tif == Tif.None || uint8(tif) > uint8(type(Tif).max)) revert InvalidTif();
// Encode the action
bytes memory encodedAction = abi.encode(asset, isBuy, limitPriceX1e8, sizeX1e8, reduceOnly, uint8(tif), cloid);
// Prefix with the limit-order header
bytes memory data = abi.encodePacked(LIMIT_ORDER_HEADER, encodedAction);
// Enqueue limit order to HyperCore via CoreWriter precompile
ICoreWriter(CORE_WRITER_PRECOMPILE_ADDRESS).sendRawAction(data);
}
/**
* @notice Enqueue a cancel-order-by-CLOID for a given asset.
* @param asset The asset index of the order
* @param cloid The client order id of the order
*/
function cancelOrderByCloid(uint32 asset, uint128 cloid) internal {
// Encode the action
bytes memory encodedAction = abi.encode(asset, cloid);
// Prefix with the cancel-by-cloid header
bytes memory data = abi.encodePacked(CANCEL_BY_CLOID_HEADER, encodedAction);
// Enqueue cancel order by CLOID to HyperCore via CoreWriter precompile
ICoreWriter(CORE_WRITER_PRECOMPILE_ADDRESS).sendRawAction(data);
}
/**
* @notice Get the balance of the specified ERC20 for `account` on HyperCore.
* @param account The address of the account to get the balance of
* @param token The token to get the balance of
* @return balance The balance of the specified ERC20 for `account` on HyperCore
*/
function spotBalance(address account, uint64 token) internal view returns (uint64 balance) {
(bool success, bytes memory result) = SPOT_BALANCE_PRECOMPILE_ADDRESS.staticcall(abi.encode(account, token));
if (!success) revert SpotBalancePrecompileCallFailed();
SpotBalance memory _spotBalance = abi.decode(result, (SpotBalance));
return _spotBalance.total;
}
/**
* @notice Checks if the user exists / has been activated on HyperCore.
* @param user The address of the user to check if they exist on HyperCore
* @return exists True if the user exists on HyperCore, false otherwise
*/
function coreUserExists(address user) internal view returns (bool) {
(bool success, bytes memory result) = CORE_USER_EXISTS_PRECOMPILE_ADDRESS.staticcall(abi.encode(user));
if (!success) revert CoreUserExistsPrecompileCallFailed();
CoreUserExists memory _coreUserExists = abi.decode(result, (CoreUserExists));
return _coreUserExists.exists;
}
/**
* @notice Get the spot price of the specified asset on HyperCore.
* @param index The asset index to get the spot price of
* @return spotPx The spot price of the specified asset on HyperCore scaled by 1e8
*/
function spotPx(uint32 index) internal view returns (uint64) {
(bool success, bytes memory result) = SPOT_PX_PRECOMPILE_ADDRESS.staticcall(abi.encode(index));
if (!success) revert SpotPxPrecompileCallFailed();
return abi.decode(result, (uint64));
}
/**
* @notice Get the info of the specified token on HyperCore.
* @param erc20CoreIndex The token to get the info of
* @return tokenInfo The info of the specified token on HyperCore
*/
function tokenInfo(uint32 erc20CoreIndex) internal view returns (TokenInfo memory) {
(bool success, bytes memory result) = TOKEN_INFO_PRECOMPILE_ADDRESS.staticcall(abi.encode(erc20CoreIndex));
if (!success) revert TokenInfoPrecompileCallFailed();
TokenInfo memory _tokenInfo = abi.decode(result, (TokenInfo));
return _tokenInfo;
}
/**
* @notice Checks if an amount is safe to bridge from HyperEVM to HyperCore
* @dev Verifies that the asset bridge has sufficient balance to cover the amount plus a buffer
* @param erc20CoreIndex The HyperCore index id of the token
* @param coreAmount The amount that the bridging should result in on HyperCore
* @param coreBufferAmount The minimum buffer amount that should remain on HyperCore after bridging
* @return True if the bridge has enough balance to safely bridge the amount, false otherwise
*/
function isCoreAmountSafeToBridge(
uint64 erc20CoreIndex,
uint64 coreAmount,
uint64 coreBufferAmount
) internal view returns (bool) {
address bridgeAddress = toAssetBridgeAddress(erc20CoreIndex);
uint64 currentBridgeBalance = spotBalance(bridgeAddress, erc20CoreIndex);
// Return true if currentBridgeBalance >= coreAmount + coreBufferAmount
return currentBridgeBalance >= coreAmount + coreBufferAmount;
}
/**
* @notice Converts a core index id to an asset bridge address
* @param erc20CoreIndex The core token index id to convert
* @return assetBridgeAddress The asset bridge address
*/
function toAssetBridgeAddress(uint64 erc20CoreIndex) internal pure returns (address) {
return address(uint160(BASE_ASSET_BRIDGE_ADDRESS_UINT256 + erc20CoreIndex));
}
/**
* @notice Converts an asset bridge address to a core index id
* @param assetBridgeAddress The asset bridge address to convert
* @return erc20CoreIndex The core token index id
*/
function toTokenId(address assetBridgeAddress) internal pure returns (uint64) {
return uint64(uint160(assetBridgeAddress) - BASE_ASSET_BRIDGE_ADDRESS_UINT256);
}
/**
* @notice Returns an amount to send on HyperEVM to receive AT LEAST the minimumCoreReceiveAmount on HyperCore
* @param minimumCoreReceiveAmount The minimum amount desired to receive on HyperCore
* @param decimalDiff The decimal difference of evmDecimals - coreDecimals
* @return amountEVMToSend The amount to send on HyperEVM to receive at least minimumCoreReceiveAmount on HyperCore
* @return amountCoreToReceive The amount that will be received on core if the amountEVMToSend is sent from HyperEVM
*/
function minimumCoreReceiveAmountToAmounts(
uint64 minimumCoreReceiveAmount,
int8 decimalDiff
) internal pure returns (uint256 amountEVMToSend, uint64 amountCoreToReceive) {
if (decimalDiff == 0) {
// Same decimals between HyperEVM and HyperCore
amountEVMToSend = uint256(minimumCoreReceiveAmount);
amountCoreToReceive = minimumCoreReceiveAmount;
} else if (decimalDiff > 0) {
// EVM token has more decimals than Core
// Scale up to represent the same value in higher-precision EVM units
amountEVMToSend = uint256(minimumCoreReceiveAmount) * (10 ** uint8(decimalDiff));
amountCoreToReceive = minimumCoreReceiveAmount;
} else {
// Core token has more decimals than EVM
// Scale down, rounding UP to avoid shortfall on Core
uint256 scaleDivisor = 10 ** uint8(-decimalDiff);
amountEVMToSend = (uint256(minimumCoreReceiveAmount) + scaleDivisor - 1) / scaleDivisor; // ceil division
amountCoreToReceive = uint64(amountEVMToSend * scaleDivisor);
}
}
/**
* @notice Converts a maximum EVM amount to send into an EVM amount to send to avoid loss to dust,
* @notice and the corresponding amount that will be recieved on Core.
* @param maximumEVMSendAmount The maximum amount to send on HyperEVM
* @param decimalDiff The decimal difference of evmDecimals - coreDecimals
* @return amountEVMToSend The amount to send on HyperEVM
* @return amountCoreToReceive The amount that will be received on HyperCore if the amountEVMToSend is sent
*/
function maximumEVMSendAmountToAmounts(
uint256 maximumEVMSendAmount,
int8 decimalDiff
) internal pure returns (uint256 amountEVMToSend, uint64 amountCoreToReceive) {
/// @dev HyperLiquid decimal conversion: Scale EVM (u256,evmDecimals) -> Core (u64,coreDecimals)
/// @dev Core amount is guaranteed to be within u64 range.
if (decimalDiff == 0) {
amountEVMToSend = maximumEVMSendAmount;
amountCoreToReceive = uint64(amountEVMToSend);
} else if (decimalDiff > 0) {
// EVM token has more decimals than Core
uint256 scale = 10 ** uint8(decimalDiff);
amountEVMToSend = maximumEVMSendAmount - (maximumEVMSendAmount % scale); // Safe: dustAmount = maximumEVMSendAmount % scale, so dust <= maximumEVMSendAmount
/// @dev Safe: Guaranteed to be in the range of [0, u64.max] because it is upperbounded by uint64 maxAmt
amountCoreToReceive = uint64(amountEVMToSend / scale);
} else {
// Core token has more decimals than EVM
uint256 scale = 10 ** uint8(-1 * decimalDiff);
amountEVMToSend = maximumEVMSendAmount;
/// @dev Safe: Guaranteed to be in the range of [0, u64.max] because it is upperbounded by uint64 maxAmt
amountCoreToReceive = uint64(amountEVMToSend * scale);
}
}
function convertCoreDecimalsSimple(
uint64 amountDecimalsFrom,
uint8 decimalsFrom,
uint8 decimalsTo
) internal pure returns (uint64) {
if (decimalsFrom == decimalsTo) {
return amountDecimalsFrom;
} else if (decimalsFrom < decimalsTo) {
return uint64(amountDecimalsFrom * 10 ** (decimalsTo - decimalsFrom));
} else {
// round down
return uint64(amountDecimalsFrom / 10 ** (decimalsFrom - decimalsTo));
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { Ownable } from "@openzeppelin/contracts-v4/access/Ownable.sol";
import { IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol";
/**
* @notice Users can donate tokens to this contract that only the owner can withdraw.
* @dev This contract is designed to be used as a convenience for the owner to store funds to pay for
* future transactions, such as donating custom gas tokens to pay for future retryable ticket messages
* to be sent via the Arbitrum_Adapter.
* @custom:security-contact [email protected]
*/
contract DonationBox is Ownable {
using SafeERC20 for IERC20;
/**
* @notice Withdraw tokens from the contract.
* @dev Only callable by owner, which should be set to the HubPool
* so that it can use these funds to pay for relaying messages to
* an Arbitrum L2 that uses custom gas tokens as the L1 payment currency,
* via the Arbitrum_CustomGasToken_Adapter.
* @param token Token to withdraw.
* @param amount Amount of tokens to withdraw.
*/
function withdraw(IERC20 token, uint256 amount) external onlyOwner {
token.safeTransfer(msg.sender, amount);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// 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 v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}{
"remappings": [
"@across-protocol/=node_modules/@across-protocol/",
"@ensdomains/=node_modules/@ensdomains/",
"@eth-optimism/=node_modules/@eth-optimism/",
"@gnosis.pm/=node_modules/@gnosis.pm/",
"@maticnetwork/=node_modules/@maticnetwork/",
"@matterlabs/=node_modules/@matterlabs/",
"@openzeppelin/=node_modules/@openzeppelin/",
"@scroll-tech/=node_modules/@scroll-tech/",
"@uniswap/=node_modules/@uniswap/",
"arb-bridge-eth/=node_modules/arb-bridge-eth/",
"arb-bridge-peripherals/=node_modules/arb-bridge-peripherals/",
"arbos-precompiles/=node_modules/arbos-precompiles/",
"base64-sol/=node_modules/base64-sol/",
"eth-gas-reporter/=node_modules/eth-gas-reporter/",
"hardhat-deploy/=node_modules/hardhat-deploy/",
"hardhat/=node_modules/hardhat/",
"@uma/=node_modules/@uma/",
"forge-std/=lib/forge-std/src/"
],
"optimizer": {
"enabled": true,
"runs": 800
},
"metadata": {
"useLiteralContent": true,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true,
"debug": {
"revertStrings": "strip"
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CoreUserExistsPrecompileCallFailed","type":"error"},{"inputs":[],"name":"InsufficientEvmAmountForActivation","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"TokenNotSupported","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountRequiredToActivate","type":"uint256"}],"name":"UserAccountActivated","type":"event"},{"inputs":[{"internalType":"address","name":"evmAddress","type":"address"},{"internalType":"uint64","name":"tokenId","type":"uint64"},{"internalType":"uint256","name":"activationFeeEvm","type":"uint256"},{"internalType":"int8","name":"decimalDiff","type":"int8"}],"name":"addSupportedToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"depositToHypercore","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"donationBox","outputs":[{"internalType":"contract DonationBox","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bytes","name":"message","type":"bytes"}],"name":"handleV3AcrossMessage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"supportedTokens","outputs":[{"internalType":"address","name":"evmAddress","type":"address"},{"internalType":"uint64","name":"tokenId","type":"uint64"},{"internalType":"uint256","name":"activationFeeEvm","type":"uint256"},{"internalType":"int8","name":"decimalDiff","type":"int8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint64","name":"coreAmount","type":"uint64"},{"internalType":"address","name":"user","type":"address"}],"name":"sweepCoreFundsToUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"sweepDonationBoxFundsToUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"evmAmount","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"sweepERC20ToUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60a080604052346100e15760015f5533156100cc5760018054336001600160a01b03198216811790925560405191906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a36103a38181016001600160401b038111838210176100b8578291610ed2833903905ff080156100ad57608052604051610dec90816100e682396080518181816101b70152818161026a01526108080152f35b6040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b631e4fbdf760e01b81525f6004820152602490fd5b5f80fdfe6040608081526004361015610012575f80fd5b5f905f3560e01c80633a5be8cb146105275780633eda20c61461045757806368c4ac26146103f2578063715018a61461038c5780637826a16e146102fa5780637bce887a146102385780638da5cb5b146102115780639b61d2d8146101db578063a4b672b614610198578063c48919d61461012c5763f2fde38b14610095575f80fd5b34610128576020366003190112610128576100ae6105df565b6100b66109f6565b6001600160a01b0380911691821561011157506001548273ffffffffffffffffffffffffffffffffffffffff19821617600155167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a380f35b51631e4fbdf760e01b815260048101849052602490fd5b5080fd5b8234610195576060366003190112610195576101466105df565b61014e610695565b6101566105f5565b61015e6109f6565b60025f54146101915767ffffffffffffffff602061018261018a9560025f55610a65565b015116610b1d565b6001815580f35b5f80fd5b80fd5b5034610128578160031936011261012857602090516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b8234610195576101ea366106ac565b6101f26109f6565b60025f5414610191576001600160a01b0361018a9360025f5516610a22565b50346101285781600319360112610128576020906001600160a01b03600154169051908152f35b50903461019157610248366106ac565b90916102526109f6565b60025f54146101915760025f556001600160a01b03807f000000000000000000000000000000000000000000000000000000000000000016911694813b1561019157805163f3fef3a360e01b81526001600160a01b038716600482015260248101859052915f908390604490829084905af19081156102f157506102dc575b5061018a9293610a22565b61018a93506102ea90610627565b5f926102d1565b513d5f823e3d90fd5b503461019157610309366106ac565b9160025f54146101915760025f5583516323b872dd60e01b60208201523360248201523060448201526064808201849052815260a08101949067ffffffffffffffff861181871017610378576103729561036d92526001600160a01b038316610c49565b610716565b60015f55005b634e487b7160e01b5f52604160045260245ffd5b34610191575f366003190112610191576103a46109f6565b5f6001600160a01b0360015473ffffffffffffffffffffffffffffffffffffffff198116600155167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b5034610191576020366003190112610191576080906001600160a01b0390816104196105df565b165f526002602052805f2080549167ffffffffffffffff600260018401549301545f0b938251958116865260a01c1660208501528301526060820152f35b5034610191576080366003190112610191576104716105df565b610479610695565b9060643592835f0b809403610191576002926104936109f6565b81519261049f8461060b565b6001600160a01b038091169283855267ffffffffffffffff6020860193168352808501936044358552606086019788525f52856020525f209351167bffffffffffffffff00000000000000000000000000000000000000008454925160a01b169163ffffffff60e01b161717825551600182015501905160ff198254169060ff161790555f80f35b5034610191576080366003190112610191576105416105df565b9061054a6105f5565b506064359067ffffffffffffffff8211610191573660238301121561019157816004013561058361057a82610679565b92519283610657565b8082526020820192366024838301011161019157815f9260246020930186378301015260025f54146101915760209060025f5580518101031261019157516001600160a01b038116809103610191576103729160243590610716565b600435906001600160a01b038216820361019157565b604435906001600160a01b038216820361019157565b6080810190811067ffffffffffffffff82111761037857604052565b67ffffffffffffffff811161037857604052565b6040810190811067ffffffffffffffff82111761037857604052565b90601f8019910116810190811067ffffffffffffffff82111761037857604052565b67ffffffffffffffff811161037857601f01601f191660200190565b6024359067ffffffffffffffff8216820361019157565b6060906003190112610191576001600160a01b03600435818116810361019157916024359160443590811681036101915790565b9073200000000000000000000000000000000000000091820180921161070257565b634e487b7160e01b5f52601160045260245ffd5b9161072083610a65565b67ffffffffffffffff926020908482840151169260608101515f0b926040805192828401985f806001600160a01b039b8c8c16978882528781526107638161063b565b51906108105afa610772610c0d565b90156109e557848180518101031261019157835191858301908111838210176103785784526107a2908501610c3c565b809152156107f3575b50505050906107b991610cef565b9390806107c9575b505050505050565b6107e895826107e3936107db866106e0565b169116610a22565b610b1d565b5f80808080806107c1565b810151916001830180931161070257898916937f00000000000000000000000000000000000000000000000000000000000000008a16803b1561019157835163f3fef3a360e01b81526001600160a01b038716600482015260248101869052905f908290604490829084905af180156109db576109cc575b506108768785610cef565b50806109b1575b50825191808301918252888484015260016060840152606083526108a08361060b565b6108d56024855180946108c5858301976280000360e11b895251809285850190610afc565b8101036004810185520183610657565b733333333333333333333333333333333333333333803b15610191575f92836044610924948851978896879586936317938e1360e01b8552600485015251809281602486015285850190610afc565b601f01601f191681010301925af180156109a757927f45b9d2d602535b50313ef0fa849df42dd31d8610fc42876e005a5b6806d3e8809261098b926107b998979695610998575b50516001600160a01b038a16815260208101919091529081906040820190565b0390a290915f80806107ab565b6109a190610627565b5f61096b565b82513d5f823e3d90fd5b6109c6908b6109bf8b6106e0565b1687610a22565b5f61087d565b6109d590610627565b5f61086b565b84513d5f823e3d90fd5b83516313dd7ccd60e31b8152600490fd5b6001600160a01b03600154163303610a0a57565b60405163118cdaa760e01b8152336004820152602490fd5b60405163a9059cbb60e01b60208201526001600160a01b03929092166024830152604480830193909352918152610a6391610a5e606483610657565b610c49565b565b60405f60608251610a758161060b565b828152826020820152828482015201526001600160a01b03809216805f52600260205282825f20541615610aeb57906002915f5281602052805f209067ffffffffffffffff815194610ac68661060b565b8354908116865260a01c16602085015260018201549084015201545f0b606082015290565b8151633dd1b30560e01b8152600490fd5b5f5b838110610b0d5750505f910152565b8181015183820152602001610afe565b604051926001600160a01b03602085019316835267ffffffffffffffff809216604085015216606083015260608252610b558261060b565b610b8c60246040518093610b7c60208301966280000360e11b885251809285850190610afc565b8101036004810184520182610657565b73333333333333333333333333333333333333333390813b15610191575f91610bde918360446040518097819682956317938e1360e01b84526020600485015251809281602486015285850190610afc565b601f01601f191681010301925af18015610c0257610bf95750565b610a6390610627565b6040513d5f823e3d90fd5b3d15610c37573d90610c1e82610679565b91610c2c6040519384610657565b82523d5f602084013e565b606090565b5190811515820361019157565b905f806001600160a01b03610ca69416927f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020604051610c898161063b565b818152015260208151910182855af1610ca0610c0d565b91610d8d565b8051908115918215610cbc575b50501561019157565b8192509060209181010312610191576020610cd79101610c3c565b5f80610cb3565b60ff16604d811161070257600a0a90565b9190805f0b9081155f14610d0d57505067ffffffffffffffff821690565b5f821315610d5757610d22915060ff16610cde565b8015610d4357808306830392831161070257820467ffffffffffffffff1690565b634e487b7160e01b5f52601260045260245ffd5b505f0380805f0b0361070257610d6f9060ff16610cde565b828181029181830414901517156107025767ffffffffffffffff1690565b9015610da757815115610d9e575090565b3b156101915790565b50805190811561019157602001fdfea2646970667358221220c0cba36db139425c71e4049e05ab727a287009647e4f3ca742370867c5d7d0ff64736f6c634300081800336080806040523461005a575f8054336001600160a01b0319821681178355916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3610344908161005f8239f35b5f80fdfe608060409080825260049081361015610016575f80fd5b5f3560e01c908163715018a61461026e5781638da5cb5b1461024c57508063f2fde38b146101d45763f3fef3a31461004c575f80fd5b346101545781600319360112610154578035916001600160a01b0383168093036101545760249261007b6102d2565b8151916020830163a9059cbb60e01b815233868501528535604485015260448452608084019367ffffffffffffffff94818110868211176101c25760c08201818110878211176101b0578452602090527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a0820152515f9182919082865af1923d1561019f573d9181831161018d57805195601f8401601f19908116603f011687019283118784101761017b575052835261013e93503d5f602085013e6102e5565b8051908115918215610158575b50501561015457005b5f80fd5b819250906020918101031261015457602001518015158103610154575f8061014b565b60418891634e487b7160e01b5f52525ffd5b86604187634e487b7160e01b5f52525ffd5b5050915061013e92506060916102e5565b88604189634e487b7160e01b5f52525ffd5b87604188634e487b7160e01b5f52525ffd5b503461015457602036600319011261015457356001600160a01b03808216809203610154576102016102d2565b8115610154575f548273ffffffffffffffffffffffffffffffffffffffff198216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b34610154575f366003190112610154576020906001600160a01b035f54168152f35b34610154575f366003190112610154576102866102d2565b5f6001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b6001600160a01b035f5416330361015457565b90156102ff578151156102f6575090565b3b156101545790565b50805190811561015457602001fdfea2646970667358221220c55691de465342d56d68c31160f5d7d661422ef39ebccaa5223872c823ad6d6964736f6c63430008180033
Deployed Bytecode
0x6040608081526004361015610012575f80fd5b5f905f3560e01c80633a5be8cb146105275780633eda20c61461045757806368c4ac26146103f2578063715018a61461038c5780637826a16e146102fa5780637bce887a146102385780638da5cb5b146102115780639b61d2d8146101db578063a4b672b614610198578063c48919d61461012c5763f2fde38b14610095575f80fd5b34610128576020366003190112610128576100ae6105df565b6100b66109f6565b6001600160a01b0380911691821561011157506001548273ffffffffffffffffffffffffffffffffffffffff19821617600155167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a380f35b51631e4fbdf760e01b815260048101849052602490fd5b5080fd5b8234610195576060366003190112610195576101466105df565b61014e610695565b6101566105f5565b61015e6109f6565b60025f54146101915767ffffffffffffffff602061018261018a9560025f55610a65565b015116610b1d565b6001815580f35b5f80fd5b80fd5b5034610128578160031936011261012857602090516001600160a01b037f00000000000000000000000059ee1342867c200fa8ac052faa5f3df8eef21a67168152f35b8234610195576101ea366106ac565b6101f26109f6565b60025f5414610191576001600160a01b0361018a9360025f5516610a22565b50346101285781600319360112610128576020906001600160a01b03600154169051908152f35b50903461019157610248366106ac565b90916102526109f6565b60025f54146101915760025f556001600160a01b03807f00000000000000000000000059ee1342867c200fa8ac052faa5f3df8eef21a6716911694813b1561019157805163f3fef3a360e01b81526001600160a01b038716600482015260248101859052915f908390604490829084905af19081156102f157506102dc575b5061018a9293610a22565b61018a93506102ea90610627565b5f926102d1565b513d5f823e3d90fd5b503461019157610309366106ac565b9160025f54146101915760025f5583516323b872dd60e01b60208201523360248201523060448201526064808201849052815260a08101949067ffffffffffffffff861181871017610378576103729561036d92526001600160a01b038316610c49565b610716565b60015f55005b634e487b7160e01b5f52604160045260245ffd5b34610191575f366003190112610191576103a46109f6565b5f6001600160a01b0360015473ffffffffffffffffffffffffffffffffffffffff198116600155167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b5034610191576020366003190112610191576080906001600160a01b0390816104196105df565b165f526002602052805f2080549167ffffffffffffffff600260018401549301545f0b938251958116865260a01c1660208501528301526060820152f35b5034610191576080366003190112610191576104716105df565b610479610695565b9060643592835f0b809403610191576002926104936109f6565b81519261049f8461060b565b6001600160a01b038091169283855267ffffffffffffffff6020860193168352808501936044358552606086019788525f52856020525f209351167bffffffffffffffff00000000000000000000000000000000000000008454925160a01b169163ffffffff60e01b161717825551600182015501905160ff198254169060ff161790555f80f35b5034610191576080366003190112610191576105416105df565b9061054a6105f5565b506064359067ffffffffffffffff8211610191573660238301121561019157816004013561058361057a82610679565b92519283610657565b8082526020820192366024838301011161019157815f9260246020930186378301015260025f54146101915760209060025f5580518101031261019157516001600160a01b038116809103610191576103729160243590610716565b600435906001600160a01b038216820361019157565b604435906001600160a01b038216820361019157565b6080810190811067ffffffffffffffff82111761037857604052565b67ffffffffffffffff811161037857604052565b6040810190811067ffffffffffffffff82111761037857604052565b90601f8019910116810190811067ffffffffffffffff82111761037857604052565b67ffffffffffffffff811161037857601f01601f191660200190565b6024359067ffffffffffffffff8216820361019157565b6060906003190112610191576001600160a01b03600435818116810361019157916024359160443590811681036101915790565b9073200000000000000000000000000000000000000091820180921161070257565b634e487b7160e01b5f52601160045260245ffd5b9161072083610a65565b67ffffffffffffffff926020908482840151169260608101515f0b926040805192828401985f806001600160a01b039b8c8c16978882528781526107638161063b565b51906108105afa610772610c0d565b90156109e557848180518101031261019157835191858301908111838210176103785784526107a2908501610c3c565b809152156107f3575b50505050906107b991610cef565b9390806107c9575b505050505050565b6107e895826107e3936107db866106e0565b169116610a22565b610b1d565b5f80808080806107c1565b810151916001830180931161070257898916937f00000000000000000000000059ee1342867c200fa8ac052faa5f3df8eef21a678a16803b1561019157835163f3fef3a360e01b81526001600160a01b038716600482015260248101869052905f908290604490829084905af180156109db576109cc575b506108768785610cef565b50806109b1575b50825191808301918252888484015260016060840152606083526108a08361060b565b6108d56024855180946108c5858301976280000360e11b895251809285850190610afc565b8101036004810185520183610657565b733333333333333333333333333333333333333333803b15610191575f92836044610924948851978896879586936317938e1360e01b8552600485015251809281602486015285850190610afc565b601f01601f191681010301925af180156109a757927f45b9d2d602535b50313ef0fa849df42dd31d8610fc42876e005a5b6806d3e8809261098b926107b998979695610998575b50516001600160a01b038a16815260208101919091529081906040820190565b0390a290915f80806107ab565b6109a190610627565b5f61096b565b82513d5f823e3d90fd5b6109c6908b6109bf8b6106e0565b1687610a22565b5f61087d565b6109d590610627565b5f61086b565b84513d5f823e3d90fd5b83516313dd7ccd60e31b8152600490fd5b6001600160a01b03600154163303610a0a57565b60405163118cdaa760e01b8152336004820152602490fd5b60405163a9059cbb60e01b60208201526001600160a01b03929092166024830152604480830193909352918152610a6391610a5e606483610657565b610c49565b565b60405f60608251610a758161060b565b828152826020820152828482015201526001600160a01b03809216805f52600260205282825f20541615610aeb57906002915f5281602052805f209067ffffffffffffffff815194610ac68661060b565b8354908116865260a01c16602085015260018201549084015201545f0b606082015290565b8151633dd1b30560e01b8152600490fd5b5f5b838110610b0d5750505f910152565b8181015183820152602001610afe565b604051926001600160a01b03602085019316835267ffffffffffffffff809216604085015216606083015260608252610b558261060b565b610b8c60246040518093610b7c60208301966280000360e11b885251809285850190610afc565b8101036004810184520182610657565b73333333333333333333333333333333333333333390813b15610191575f91610bde918360446040518097819682956317938e1360e01b84526020600485015251809281602486015285850190610afc565b601f01601f191681010301925af18015610c0257610bf95750565b610a6390610627565b6040513d5f823e3d90fd5b3d15610c37573d90610c1e82610679565b91610c2c6040519384610657565b82523d5f602084013e565b606090565b5190811515820361019157565b905f806001600160a01b03610ca69416927f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020604051610c898161063b565b818152015260208151910182855af1610ca0610c0d565b91610d8d565b8051908115918215610cbc575b50501561019157565b8192509060209181010312610191576020610cd79101610c3c565b5f80610cb3565b60ff16604d811161070257600a0a90565b9190805f0b9081155f14610d0d57505067ffffffffffffffff821690565b5f821315610d5757610d22915060ff16610cde565b8015610d4357808306830392831161070257820467ffffffffffffffff1690565b634e487b7160e01b5f52601260045260245ffd5b505f0380805f0b0361070257610d6f9060ff16610cde565b828181029181830414901517156107025767ffffffffffffffff1690565b9015610da757815115610d9e575090565b3b156101915790565b50805190811561019157602001fdfea2646970667358221220c0cba36db139425c71e4049e05ab727a287009647e4f3ca742370867c5d7d0ff64736f6c63430008180033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in HYPE
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.