Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 30 additions & 117 deletions broadcast/DeployHyperliquidDepositHandler.s.sol/999/run-latest.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions broadcast/deployed-addresses.json
Original file line number Diff line number Diff line change
Expand Up @@ -515,9 +515,9 @@
"transaction_hash": "0x7ce545552d4a517bc380951ec35bd5eb86f84e0364a7ac9417aba605b8bf6309"
},
"HyperliquidDepositHandler": {
"address": "0x861e127036b28d32f3777b4676f6bbb9e007d195",
"block_number": 20301679,
"transaction_hash": "0x187b45f39be413aff2ff526946c64f76ed98763129e7e2ffb7d2d4c5bd997519"
"address": "0xbfb53e9c8acce6d6ac54885a8e33a7aec95427d5",
"block_number": 27144816,
"transaction_hash": "0xf7f1b77559935ec04f3a3a89601b621f21d4cf38c2701e8df18e10f333b24c7c"
},
"SponsoredCCTPDstPeriphery": {
"address": "0x1c709fd0db6a6b877ddb19ae3d485b7b4add879f",
Expand Down
2 changes: 1 addition & 1 deletion broadcast/deployed-addresses.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ This file contains the latest deployed smart contract addresses from the broadca
| DonationBox | [0x3D589D40312Bf2d20f13cD0AF26A11144a9cA844](https://hyperevmscan.io//address/0x3D589D40312Bf2d20f13cD0AF26A11144a9cA844) |
| DonationBox | [0x039d62C549F27ead0eB9B567d8776289e5020583](https://hyperevmscan.io//address/0x039d62C549F27ead0eB9B567d8776289e5020583) |
| DstOFTHandler | [0xc8786D517b4e224bB43985A38dBeF8588D7354CD](https://hyperevmscan.io//address/0xc8786D517b4e224bB43985A38dBeF8588D7354CD) |
| HyperliquidDepositHandler | [0x861E127036B28D32f3777B4676F6bbb9e007d195](https://hyperevmscan.io//address/0x861E127036B28D32f3777B4676F6bbb9e007d195) |
| HyperliquidDepositHandler | [0xbFB53E9C8acCe6D6aC54885A8e33A7Aec95427D5](https://hyperevmscan.io//address/0xbFB53E9C8acCe6D6aC54885A8e33A7Aec95427D5) |
| MulticallHandler | [0x5E7840E06fAcCb6d1c3b5F5E0d1d3d07F2829bba](https://hyperevmscan.io//address/0x5E7840E06fAcCb6d1c3b5F5E0d1d3d07F2829bba) |
| PermissionedMulticallHandler | [0xfD0876712DD9003D014CDCd8e5140B4EFAC9BFCC](https://hyperevmscan.io//address/0xfD0876712DD9003D014CDCd8e5140B4EFAC9BFCC) |
| SP1Helios | [0xc19B7EF43a6eBd393446F401d1eCFac01B181ac0](https://hyperevmscan.io//address/0xc19B7EF43a6eBd393446F401d1eCFac01B181ac0) |
Expand Down
165 changes: 124 additions & 41 deletions script/DeployHyperliquidDepositHandler.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,143 @@
pragma solidity ^0.8.0;

import { Script } from "forge-std/Script.sol";
import { Test } from "forge-std/Test.sol";
import { console } from "forge-std/console.sol";
import { HyperliquidDepositHandler } from "../contracts/handlers/HyperliquidDepositHandler.sol";
import { HyperCoreLib } from "../contracts/libraries/HyperCoreLib.sol";
import { IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol";
import { ReadHCoreTokenInfoUtil } from "./mintburn/ReadHCoreTokenInfoUtil.s.sol";
import { DeployedAddresses } from "./utils/DeployedAddresses.sol";
import { SafeCast } from "@openzeppelin/contracts-v4/utils/math/SafeCast.sol";

// How to run:
// forge script script/DeployHyperliquidDepositHandler.s.sol:DeployHyperliquidDepositHandler --rpc-url hyperevm -vvvv
/*
How to run:

contract DeployHyperliquidDepositHandler is Script, Test {
function run() external {
string memory deployerMnemonic = vm.envString("MNEMONIC");
uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0);
// Set the initial signer to the deployer's address.
address signer = vm.addr(deployerPrivateKey);
TOKENS_JSON='["usdt0","usdc","usdh"]'
TOKENS_ENCODED=$(cast abi-encode "f(string[])" "$TOKENS_JSON")

address spokePool = 0x35E63eA3eb0fb7A3bc543C71FB66412e1F6B0E04;
# signer defaults to deployer
forge script script/DeployHyperliquidDepositHandler.s.sol:DeployHyperliquidDepositHandler \
--sig "run(string)" "$TOKENS_ENCODED" --rpc-url hyperevm -vvvv --broadcast --verify

// Get the current chain ID
uint256 chainId = block.chainid;
# explicit signer override (pass zero address to use deployer)
SIGNER=0x1111111111111111111111111111111111111111
forge script script/DeployHyperliquidDepositHandler.s.sol:DeployHyperliquidDepositHandler \
--sig "run(string,address)" "$TOKENS_ENCODED" $SIGNER --rpc-url hyperevm -vvvv --broadcast --verify
*/

// Set up USDH as a supported token for this handler.
IERC20 usdh = IERC20(0x111111a1a0667d36bD57c0A9f569b98057111111);
uint32 usdhTokenIndex = 360;
uint64 usdhActivationFeeCore = 10000; // 1 USDH in Core units (4 szDecimals)
uint64 usdhBridgeSafetyBufferCore = 0; // No buffer for now
contract DeployHyperliquidDepositHandler is Script, DeployedAddresses {
using SafeCast for uint256;

vm.startBroadcast(deployerPrivateKey);
string internal constant SPOKE_POOL_NAME = "SpokePool";
string internal constant ADD_SUPPORTED_TOKEN_SIG = "addSupportedToken(address,uint32,bool,uint64,uint64)";

HyperliquidDepositHandler hyperliquidDepositHandler = new HyperliquidDepositHandler(signer, spokePool);
struct TokenConfig {
string symbol;
address evmAddress;
uint32 coreIndex;
bool canBeUsedForAccountActivation;
uint64 accountActivationFeeCore;
uint64 bridgeSafetyBufferCore;
}

function run() external pure {
revert("Missing args. Use run(string encodedTokenSymbols[, address signer])");
}

function run(string memory encodedTokenSymbols) external {
_run(vm.parseBytes(encodedTokenSymbols), address(0));
}

function run(string memory encodedTokenSymbols, address signerOverride) external {
_run(vm.parseBytes(encodedTokenSymbols), signerOverride);
}

function _run(bytes memory encodedTokenSymbols, address signerOverride) internal {
string[] memory tokenSymbols = abi.decode(encodedTokenSymbols, (string[]));
require(tokenSymbols.length != 0, "token symbols required");

string memory deployerMnemonic = vm.envString("MNEMONIC");
uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0);
address deployer = vm.addr(deployerPrivateKey);
address signer = signerOverride == address(0) ? deployer : signerOverride;
uint256 chainId = block.chainid;
address spokePool = getAddress(chainId, SPOKE_POOL_NAME);
require(spokePool != address(0), "SpokePool missing in broadcast/deployed-addresses.json");

ReadHCoreTokenInfoUtil reader = new ReadHCoreTokenInfoUtil();
TokenConfig[] memory tokenConfigs = new TokenConfig[](tokenSymbols.length);
for (uint256 i = 0; i < tokenSymbols.length; i++) {
string memory tokenSymbol = tokenSymbols[i];
require(bytes(tokenSymbol).length != 0, "empty token symbol");
ReadHCoreTokenInfoUtil.TokenJson memory info = reader.readToken(tokenSymbol);
tokenConfigs[i] = TokenConfig({
symbol: tokenSymbol,
evmAddress: reader.resolveEvmAddress(info),
coreIndex: info.index.toUint32(),
canBeUsedForAccountActivation: info.canBeUsedForAccountActivation,
accountActivationFeeCore: info.accountActivationFeeCore.toUint64(),
bridgeSafetyBufferCore: info.bridgeSafetyBufferCore.toUint64()
});
}

// Activate Handler account so it can write to CoreWriter by sending 1 core wei.
HyperCoreLib.transferERC20CoreToCore(
usdhTokenIndex,
address(hyperliquidDepositHandler),
1,
HyperCoreLib.CORE_SPOT_DEX_ID,
HyperCoreLib.CORE_SPOT_DEX_ID
);
hyperliquidDepositHandler.addSupportedToken(
address(usdh),
usdhTokenIndex,
true, // canBeUsedForAccountActivation
usdhActivationFeeCore,
usdhBridgeSafetyBufferCore
);

// Log the deployed addresses
console.log("Chain ID:", chainId);
console.log("HyperliquidDepositHandler deployed to:", address(hyperliquidDepositHandler));
console.log("SpokePool:", spokePool);
console.log("Deployer:", deployer);
console.log("Signer required to sign payloads for handleV3AcrossMessage:", signer);
console.log("USDH token index:", usdhTokenIndex);
console.log("USDH activation fee (core):", usdhActivationFeeCore);

vm.startBroadcast(deployerPrivateKey);
HyperliquidDepositHandler hyperliquidDepositHandler = new HyperliquidDepositHandler(signer, spokePool);
vm.stopBroadcast();

address deployedHandler = address(hyperliquidDepositHandler);
console.log("HyperliquidDepositHandler deployed to:", deployedHandler);
console.log("Configured token count:", tokenConfigs.length);
_printPostDeploySteps(deployedHandler, tokenConfigs);
}

function _printPostDeploySteps(address deployedHandler, TokenConfig[] memory tokenConfigs) internal pure {
console.log("POST-DEPLOY STEPS (manual):");
console.log("Foundry scripts cannot currently execute HyperCore precompile-dependent setup flows.");
console.log("TODO: when precompile simulation support is available, inline these setup calls in this script.");
console.log("1) Activate HyperCore account for the deployed handler.");
console.log(" Handler:", deployedHandler);
console.log(" Use Hyperliquid UI/API to send 1 core wei to this address.");
console.log(" Suggested activation token:", tokenConfigs[0].symbol);

for (uint256 i = 0; i < tokenConfigs.length; i++) {
TokenConfig memory cfg = tokenConfigs[i];
uint256 stepNum = i + 2;
string memory canActivate = cfg.canBeUsedForAccountActivation ? "true" : "false";

console.log(
string(abi.encodePacked(vm.toString(stepNum), ") Configure ", cfg.symbol, " via addSupportedToken"))
);
console.log(" Function:", ADD_SUPPORTED_TOKEN_SIG);
console.log(" token:", cfg.evmAddress);
console.log(" coreIndex:", uint256(cfg.coreIndex));
console.log(" canBeUsedForAccountActivation:", cfg.canBeUsedForAccountActivation);
console.log(" accountActivationFeeCore:", uint256(cfg.accountActivationFeeCore));
console.log(" bridgeSafetyBufferCore:", uint256(cfg.bridgeSafetyBufferCore));
console.log(" Command:");
console.log(
string(
abi.encodePacked(
" cast send ",
vm.toString(deployedHandler),
' "',
ADD_SUPPORTED_TOKEN_SIG,
'" ',
vm.toString(cfg.evmAddress),
" ",
vm.toString(cfg.coreIndex),
" ",
canActivate,
" ",
vm.toString(cfg.accountActivationFeeCore),
" ",
vm.toString(cfg.bridgeSafetyBufferCore),
" --rpc-url hyperevm --account <ACCOUNT>"
)
)
);
}
}
}
26 changes: 26 additions & 0 deletions script/mintburn/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
## Hypercore token metadata notes

- To get `hypercore-tokens.json` info:

```
curl -s 'https://api.hyperliquid.xyz/info' \
-H 'content-type: application/json' \
--data '{"type":"spotMeta"}' \
| jq '.tokens[]
| select((.name|ascii_upcase)=="USDT0"
or (.name|ascii_upcase)=="USDC"
or (.name|ascii_upcase)=="USDH")'
```

These 3 fields added manually to each entry:

```
"canBeUsedForAccountActivation": true,
"accountActivationFeeCore": 100000000,
"bridgeSafetyBufferCore": 100000000000000000
```

- `script/mintburn/hypercore-tokens.json` is read by `script/mintburn/ReadHCoreTokenInfoUtil.s.sol`.
- USDC is a special case: `evmContract.address` in that JSON is **not** the real HyperEVM USDC ERC20 address.
- Read utils hard-override USDC to `0xb88339CB7199b77E23DB6E890353E22632Ba630f` for all script configuration paths.
- Same address is documented in `script/mintburn/oft/README.md`.
45 changes: 31 additions & 14 deletions script/mintburn/ReadHCoreTokenInfoUtil.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,48 @@
pragma solidity ^0.8.0;

import { Script } from "forge-std/Script.sol";
import { console } from "forge-std/console.sol";
import { Constants } from "../utils/Constants.sol";

contract ReadHCoreTokenInfoUtil is Script {
string internal constant HCORE_JSON_PATH = "./script/mintburn/hypercore-tokens.json";
address internal constant HYPEREVM_USDC = 0xb88339CB7199b77E23DB6E890353E22632Ba630f;

struct TokenJson {
uint256 index;
address evmAddress;
bool canBeUsedForAccountActivation;
uint256 accountActivationFeeCore;
uint256 bridgeSafetyBufferCore;
bool isUsdc;
}

function readToken(string memory tokenName) public view returns (TokenJson memory info) {
function readToken(string memory tokenKey) public view returns (TokenJson memory info) {
string memory json = vm.readFile(HCORE_JSON_PATH);
string memory base = string.concat(".", tokenName);
string memory base = string.concat(".", tokenKey);

info.index = vm.parseJsonUint(json, string.concat(base, ".index"));
info.isUsdc = _isUsdc(tokenKey);

// evmAddress can be null in JSON; parseJsonAddress would revert. Try/catch and leave zero if unset.
try this._parseAddress(json, string.concat(base, ".evmAddress")) returns (address a) {
// evmContract.address can be null in JSON; parseJsonAddress would revert.
try this._parseAddress(json, string.concat(base, ".evmContract.address")) returns (address a) {
info.evmAddress = a;
} catch {
info.evmAddress = address(0);
}

// Required fields for CoreTokenInfo
info.canBeUsedForAccountActivation = vm.parseJsonBool(
json,
string.concat(base, ".canBeUsedForAccountActivation")
);
info.accountActivationFeeCore = vm.parseJsonUint(json, string.concat(base, ".accountActivationFeeCore"));
info.bridgeSafetyBufferCore = vm.parseJsonUint(json, string.concat(base, ".bridgeSafetyBufferCore"));
// Optional fields for CoreTokenInfo. Leave defaults if absent.
try this._parseBool(json, string.concat(base, ".canBeUsedForAccountActivation")) returns (bool canActivate) {
info.canBeUsedForAccountActivation = canActivate;
} catch {}
try this._parseUint(json, string.concat(base, ".accountActivationFeeCore")) returns (uint256 activationFee) {
info.accountActivationFeeCore = activationFee;
} catch {}
try this._parseUint(json, string.concat(base, ".bridgeSafetyBufferCore")) returns (uint256 bridgeSafetyBuffer) {
info.bridgeSafetyBufferCore = bridgeSafetyBuffer;
} catch {}
}

function resolveEvmAddress(TokenJson memory info, uint256 /* chainId */) public pure returns (address evm) {
function resolveEvmAddress(TokenJson memory info) public pure returns (address evm) {
if (info.isUsdc) return HYPEREVM_USDC;
require(info.evmAddress != address(0), "evmAddress required in JSON");
return info.evmAddress;
}
Expand All @@ -47,4 +52,16 @@ contract ReadHCoreTokenInfoUtil is Script {
function _parseAddress(string memory json, string memory key) external pure returns (address) {
return vm.parseJsonAddress(json, key);
}

function _parseBool(string memory json, string memory key) external pure returns (bool) {
return vm.parseJsonBool(json, key);
}

function _parseUint(string memory json, string memory key) external pure returns (uint256) {
return vm.parseJsonUint(json, key);
}

function _isUsdc(string memory tokenKey) internal pure returns (bool) {
return keccak256(bytes(tokenKey)) == keccak256(bytes("usdc"));
}
}
33 changes: 24 additions & 9 deletions script/mintburn/hypercore-tokens.json
Original file line number Diff line number Diff line change
@@ -1,36 +1,51 @@
{
"usdc": {
"name": "USDC",
"index": 0,
"tokenId": "0x6d1e7cde53ba9467b783cb7c530ce054",
"szDecimals": 8,
"weiDecimals": 8,
"index": 0,
"tokenId": "0x6d1e7cde53ba9467b783cb7c530ce054",
"isCanonical": true,
"evmAddress": null,
"evmContract": {
"address": "0x6b9e773128f453f5c2c60935ee2de2cbc5390a24",
"evm_extra_wei_decimals": -2
},
"fullName": null,
"deployerTradingFeeShare": "0.0",
"canBeUsedForAccountActivation": true,
"accountActivationFeeCore": 100000000,
"bridgeSafetyBufferCore": 100000000000000000
},
"usdt0": {
"name": "USDT0",
"index": 268,
"tokenId": "0x25faedc3f054130dbb4e4203aca63567",
"szDecimals": 2,
"weiDecimals": 8,
"index": 268,
"tokenId": "0x25faedc3f054130dbb4e4203aca63567",
"isCanonical": false,
"evmAddress": "0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb",
"evmContract": {
"address": "0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb",
"evm_extra_wei_decimals": -2
},
"fullName": "USDT0",
"deployerTradingFeeShare": "0.0",
"canBeUsedForAccountActivation": true,
"accountActivationFeeCore": 100000000,
"bridgeSafetyBufferCore": 100000000000000000
},
"usdh": {
"name": "USDH",
"index": 360,
"tokenId": "0x54e00a5988577cb0b0c9ab0cb6ef7f4b",
"szDecimals": 2,
"weiDecimals": 8,
"index": 360,
"tokenId": "0x54e00a5988577cb0b0c9ab0cb6ef7f4b",
"isCanonical": false,
"evmAddress": "0x111111a1a0667d36bd57c0a9f569b98057111111",
"evmContract": {
"address": "0x111111a1a0667d36bd57c0a9f569b98057111111",
"evm_extra_wei_decimals": -2
},
"fullName": "USDH",
"deployerTradingFeeShare": "0.0",
"canBeUsedForAccountActivation": true,
"accountActivationFeeCore": 100000000,
"bridgeSafetyBufferCore": 100000000000000000
Expand Down
Loading
Loading