Skip to main content

Overview

Melt is a cross-chain token bridge built on LayerZero V2. It enables:
  1. Bridge In — Lock ERC-20 tokens on a source chain, receive wrapped tokens on HyperEVM.
  2. Bridge In to HyperCore — Same as above, but tokens land directly in your HyperCore spot balance for immediate trading.
  3. Bridge Out — Burn wrapped tokens on HyperEVM, receive original tokens on the source chain.
  4. Wrap / Unwrap — Convert between native HyperEVM tokens and their Melt-wrapped equivalents (same-chain only).
Source Chain                         HyperEVM
┌──────────────┐    LayerZero V2    ┌──────────────┐
│  MeltBridge  │ ────────────────►  │   MeltHub    │
│  (lock/release) │ ◄──────────────── │  (mint/burn) │
└──────────────┘                    └──────┬───────┘

                                          │ toCore = true

                                   ┌──────────────┐
                                   │  HyperCore   │
                                   │ (spot balance)│
                                   └──────────────┘
See Deployment Addresses for all contract addresses.

Core Concepts

Config Key

Every token is identified by a configKey — a hash of the original token address and its source chain:
configKey = keccak256(abi.encodePacked(originalToken, sourceEid))
  • sourceEid is the LayerZero Endpoint ID of the source chain (0 for same-chain HyperEVM tokens).
  • Use MeltHub.getConfigKey(originalToken, sourceEid) to compute it on-chain.

LayerZero Endpoint IDs

ChainEID
Ethereum30101
HyperEVM30367

Recipient Encoding

Bridge functions accept recipients as bytes32. To convert an address:
bytes32 recipient = bytes32(uint256(uint160(recipientAddress)));

Fee Directions

Melt fees are directional:
  • IN — applied on wrap() and bridge-in (source → HyperEVM). Deducted from the amount received.
  • OUT — applied on unwrap() and bridgeOut() (HyperEVM → source). Deducted from the amount sent.

Flows

Bridge In (Source → HyperEVM)

Bridge tokens from a source chain to receive wrapped tokens on HyperEVM.
1

Approve

Approve the MeltBridge contract to spend your tokens.
2

Quote LayerZero fee

MessagingFee memory fee = meltBridge.quoteBridge(token, amount, recipient, false);
3

Bridge

MessagingReceipt memory receipt = meltBridge.bridge{value: fee.nativeFee}(
    token,
    amount,
    recipient, // bytes32-encoded destination address on HyperEVM
    false      // toCore = false
);
4

Wait for delivery

LayerZero delivery takes ~1–5 minutes. Track using the guid from the receipt.
5

Receive

Wrapped tokens arrive on HyperEVM. Amount received = amount - fee (IN direction fee).
Events to monitor:
ChainEventMeaning
SourceBridgeInitiated(guid, sender, token, amount, recipient)Bridge tx submitted
HyperEVMBridgedIn(guid, recipient, wrappedToken, amount, srcEid)Wrapped tokens minted

Bridge In to HyperCore

Bridge tokens directly into your HyperCore spot balance for immediate trading.
Your recipient address must be activated on HyperCore. The transaction reverts with CoreUserNotActivated otherwise. The token must also have HyperCore configured (coreLinked = true).
1

Approve

Approve the MeltBridge contract to spend your tokens.
2

Quote LayerZero fee (2x gas for toCore)

MessagingFee memory fee = meltBridge.quoteBridge(token, amount, recipient, true);
3

Bridge with toCore = true

MessagingReceipt memory receipt = meltBridge.bridge{value: fee.nativeFee}(
    token,
    amount,
    recipient, // bytes32-encoded HyperEVM address (must be activated on Core)
    true       // toCore = true
);
4

Receive on HyperCore

Tokens land in your HyperCore spot balance. The amount is converted from EVM decimals to Core decimals using the token’s decimalDiff.
Events to monitor:
ChainEventMeaning
SourceBridgeInitiated(guid, sender, token, amount, recipient)Bridge tx submitted
HyperEVMBridgedToCore(guid, recipient, wrappedToken, evmAmount, coreAmount)Tokens delivered to HyperCore

Bridge Out (HyperEVM → Source)

Burn wrapped tokens on HyperEVM to receive original tokens on the source chain.
1

Approve

Approve MeltHub to spend your wrapped tokens.
2

Quote LayerZero fee

MessagingFee memory fee = meltHub.quoteBridgeOut(wrappedToken, amount, recipient);
3

Bridge out

MessagingReceipt memory receipt = meltHub.bridgeOut{value: fee.nativeFee}(
    wrappedToken,
    amount,
    recipient // bytes32-encoded destination address on source chain
);
4

Receive on source chain

Original tokens are released from the bridge. Amount received = amount - fee (OUT direction fee).
Events to monitor:
ChainEventMeaning
HyperEVMBridgedOut(guid, sender, wrappedToken, amount, dstEid, recipient)Burn + LZ message sent
SourceBridgeCompleted(recipient, token, amount)Original tokens released

Wrap / Unwrap (Same-Chain)

For tokens native to HyperEVM that have a Melt-wrapped version (identified by sourceEid = 0). Wrap (original → wrapped):
  1. Approve MeltHub to spend the original token.
  2. Call:
    uint256 amountOut = meltHub.wrap(configKey, amount);
    
  3. Receive amountOut = amount - fee (IN direction).
Unwrap (wrapped → original):
  1. Approve MeltHub to spend the wrapped token.
  2. Call:
    uint256 amountOut = meltHub.unwrap(configKey, amount);
    
  3. Receive amountOut = amount - fee (OUT direction).

Errors Reference

ErrorContractCause
TokenNotSupportedMeltBridgeToken is not whitelisted on this bridge
InsufficientAmountBothAmount is zero
InsufficientLiquidityMeltBridgeBridge doesn’t hold enough locked tokens for release
TokenNotRegisteredMeltHubToken config key not found
TokenPausedMeltHubToken or hub is paused
InvalidSourceChainMeltHubSource chain mismatch on bridge-out
CoreNotConfiguredMeltHubtoCore=true but HyperCore not linked for this token
CoreUserNotActivatedMeltHubRecipient is not activated on HyperCore
InsufficientCoreBridgeCapacityMeltHubAsset bridge doesn’t have enough capacity

Fee Structure

  • Fees are charged in basis points (1 bps = 0.01%), up to a maximum of 1,000 bps (10%).
  • Fee calculation: fee = amount * feeBps / 10_000.
  • Fees are directional — IN and OUT fees may differ.
  • The protocol can configure per-address fee overrides via MeltFeeController. If your address has an override, it takes precedence over the token’s default fee.
  • Fees are collected as wrapped tokens held by MeltHub.

Checking Your Fee

// Get the token's default fee
(, , , uint256 feeBps, ,) = meltHub.tokenConfigs(configKey);

// If a fee controller is set, your actual fee may differ
IMeltFeeController controller = meltHub.feeController();
if (address(controller) != address(0)) {
    uint256 fee = controller.getFee(configKey, yourAddress, amount, feeBps, isOut);
}

Function Signatures

MeltBridge (Source Chain)

// Bridge tokens to HyperEVM. Set toCore=true to deliver to HyperCore spot balance.
function bridge(address token, uint256 amount, bytes32 recipient, bool toCore)
    external payable returns (MessagingReceipt memory);

// Quote the LayerZero fee for a bridge transaction.
function quoteBridge(address token, uint256 amount, bytes32 recipient, bool toCore)
    external view returns (MessagingFee memory);

// Check if a token is supported.
function isTokenSupported(address token) external view returns (bool);

// Get the locked balance for a token.
function getLockedBalance(address token) external view returns (uint256);

MeltHub (HyperEVM)

// Wrap native HyperEVM tokens into Melt-wrapped tokens.
function wrap(bytes32 configKey, uint256 amount) external returns (uint256 amountOut);

// Unwrap Melt-wrapped tokens back to native HyperEVM tokens.
function unwrap(bytes32 configKey, uint256 amount) external returns (uint256 amountOut);

// Bridge wrapped tokens back to the source chain.
function bridgeOut(address wrappedToken, uint256 amount, bytes32 recipient)
    external payable returns (MessagingReceipt memory);

// Quote the LayerZero fee for a bridge-out transaction.
function quoteBridgeOut(address wrappedToken, uint256 amount, bytes32 recipient)
    external view returns (MessagingFee memory);

// Compute the config key for a token.
function getConfigKey(address originalToken, uint32 sourceEid)
    external pure returns (bytes32);

// Get full token config.
function getTokenConfig(bytes32 configKey)
    external view returns (TokenConfig memory);

// Get HyperCore config for a token.
function coreConfigs(bytes32 configKey)
    external view returns (uint64 coreIndexId, int8 decimalDiff, bool isConfigured);

// Get config key from wrapped token address.
function wrappedToConfigKey(address wrappedToken) external view returns (bytes32);

// Get the fee controller (address(0) if none set).
function feeController() external view returns (IMeltFeeController);

LayerZero Types

struct MessagingFee {
    uint256 nativeFee;  // Gas fee in native token (ETH on Ethereum, HYPE on HyperEVM)
    uint256 lzTokenFee; // Optional fee in LZ token (usually 0)
}

struct MessagingReceipt {
    bytes32 guid;       // Unique message ID — use to track delivery
    uint64 nonce;
    MessagingFee fee;
}