Skip to content

Commit 9be22c2

Browse files
authored
hlp+ci - hyperliquid node + tests using new node (#1653)
Signed-off-by: shankar <shankar@layerzerolabs.org>
1 parent 253612c commit 9be22c2

18 files changed

Lines changed: 825 additions & 599 deletions

File tree

.changeset/cyan-squids-kick.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@layerzerolabs/hyperliquid-composer": patch
3+
"@layerzerolabs/oft-hyperliquid-example": patch
4+
---
5+
6+
change fork fuzzing to works off a forked anvil

.github/workflows/reusable-test.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ jobs:
119119
# LZ_DEVTOOLS_ENABLE_SOLANA_TESTS: 1
120120
RPC_URL_SOLANA_MAINNET: ${{ secrets.RPC_URL_SOLANA_MAINNET || 'https://rpc.ankr.com/solana' }}
121121
RPC_URL_SOLANA_TESTNET: ${{ secrets.RPC_URL_SOLANA_TESTNET || 'https://api.devnet.solana.com' }}
122+
FOUNDRY_PROFILE: "ci"
123+
RPC_URL_HYPERLIQUID: "http://127.0.0.1:8546"
124+
RPC_URL_HYPERLIQUID_UPSTREAM: ${{ secrets.RPC_URL_HYPERLIQUID_UPSTREAM || 'https://rpc.hyperliquid.xyz/evm' }}
122125

123126
# We'll collect the docker compose logs from all containers on failure
124127
- name: Collect docker logs on failure

Dockerfile.hyperliquid

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# syntax=docker/dockerfile:1.4
2+
FROM ghcr.io/foundry-rs/foundry:latest
3+
4+
WORKDIR /anvil
5+
6+
# Install curl for healthcheck
7+
USER root
8+
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
9+
10+
ENTRYPOINT ["sh", "-c", "anvil --fork-url $FORK_URL --host $HOST --port $PORT"]
11+
12+
EXPOSE 8545

docker-compose.local.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,7 @@ services:
3232
network-tango:
3333
ports:
3434
- "10003:8545"
35+
36+
network-hyperliquid:
37+
ports:
38+
- "8546:8546"

docker-compose.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,29 @@ services:
5151
file: docker-compose.templates.yaml
5252
service: ton-node
5353

54+
# ~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~
55+
#
56+
# The HyperLiquid local fork node
57+
#
58+
# .oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.
59+
network-hyperliquid:
60+
build:
61+
context: .
62+
dockerfile: Dockerfile.hyperliquid
63+
environment:
64+
- FORK_URL=${RPC_URL_HYPERLIQUID_UPSTREAM:-https://rpc.hyperliquid.xyz/evm}
65+
- HOST=0.0.0.0
66+
- PORT=8545
67+
- FOUNDRY_DISABLE_NIGHTLY_WARNING=1
68+
ports:
69+
- "8546:8545"
70+
healthcheck:
71+
test: ["CMD", "sh", "-c", "curl -X POST -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"method\":\"eth_chainId\",\"params\":[],\"id\":1}' http://localhost:8545"]
72+
interval: 10s
73+
timeout: 5s
74+
retries: 10
75+
start_period: 30s
76+
5477
# ~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~
5578
#
5679
# The actual tests
@@ -69,6 +92,8 @@ services:
6992
condition: service_healthy
7093
network-ton:
7194
condition: service_healthy
95+
network-hyperliquid:
96+
condition: service_healthy
7297
logging:
7398
driver: none
7499
# The default containerized setup will specify the network URLs
@@ -85,11 +110,14 @@ services:
85110
- NETWORK_URL_VENGABOYS=http://network-vengaboys:8545
86111
- NETWORK_URL_TANGO=http://network-tango:8545
87112
- NETWORK_URL_TON=http://network-ton:8081/jsonRPC
113+
- NETWORK_URL_HYPERLIQUID=http://network-hyperliquid:8546
88114
- DOCKER_COMPOSE_RUN_TESTS_TURBO_ARGS=$DOCKER_COMPOSE_RUN_TESTS_TURBO_ARGS
89115
# Passing environment variables that control the solana tests
90116
- RPC_URL_SOLANA_MAINNET=${RPC_URL_SOLANA_MAINNET}
91117
- RPC_URL_SOLANA_TESTNET=${RPC_URL_SOLANA_TESTNET}
92118
- LZ_DEVTOOLS_ENABLE_SOLANA_TESTS=${LZ_DEVTOOLS_ENABLE_SOLANA_TESTS}
119+
- FOUNDRY_PROFILE=${FOUNDRY_PROFILE}
120+
- RPC_URL_HYPERLIQUID=${RPC_URL_HYPERLIQUID}
93121
volumes:
94122
- ./node_modules/.cache/turbo:/app/node_modules/.cache/turbo
95123
# Hardhat has an issue with caching compilers inside a docker container,

examples/oft-hyperliquid/test/foundry/ComposerCodec/ComposeMessage.t.sol

Lines changed: 8 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,57 +5,21 @@ import { OFTComposeMsgCodec } from "@layerzerolabs/oft-evm/contracts/libs/OFTCom
55

66
import { HyperLiquidComposerCodec } from "@layerzerolabs/hyperliquid-composer/contracts/library/HyperLiquidComposerCodec.sol";
77

8-
import { IHyperLiquidComposerErrors, ErrorMessagePayload } from "@layerzerolabs/hyperliquid-composer/contracts/interfaces/IHyperLiquidComposerErrors.sol";
9-
import { IHyperAsset, IHyperLiquidComposerCore } from "@layerzerolabs/hyperliquid-composer/contracts/interfaces/IHyperLiquidComposerCore.sol";
8+
import { IHyperLiquidComposerErrors } from "@layerzerolabs/hyperliquid-composer/contracts/interfaces/IHyperLiquidComposerErrors.sol";
109

1110
import { HyperLiquidComposer } from "@layerzerolabs/hyperliquid-composer/contracts/HyperLiquidComposer.sol";
12-
import { MyOFT } from "../../../contracts/MyOFT.sol";
1311

14-
import { Test, console } from "forge-std/Test.sol";
12+
import { HyperliquidBaseTest } from "@layerzerolabs/hyperliquid-composer/test/HyperliquidBase.t.sol";
1513

16-
contract ComposeMessageTest is Test {
14+
import { console } from "forge-std/Test.sol";
15+
16+
contract ComposeMessageTest is HyperliquidBaseTest {
1717
using HyperLiquidComposerCodec for bytes;
1818

1919
uint256 public constant DEFAULT_AMOUNT = 1e18;
2020

21-
IHyperAsset public ALICE;
22-
IHyperAsset public HYPE;
23-
address public constant HL_LZ_ENDPOINT_V2 = 0xf9e1815F151024bDE4B7C10BAC10e8Ba9F6b53E1;
24-
25-
MyOFT public oft;
26-
HyperLiquidComposer public hyperLiquidComposer;
27-
28-
address public sender;
29-
30-
function setUp() public {
31-
// Skip test if fork fails
32-
try vm.createSelectFork("https://rpc.hyperliquid-testnet.xyz/evm") {} catch {
33-
console.log("Forking testnet https://rpc.hyperliquid-testnet.xyz/evm failed");
34-
vm.skip(true);
35-
}
36-
37-
sender = makeAddr("sender");
38-
39-
ALICE = IHyperAsset({
40-
assetBridgeAddress: HyperLiquidComposerCodec.into_assetBridgeAddress(1231),
41-
coreIndexId: 1231,
42-
decimalDiff: 18 - 6
43-
});
44-
45-
HYPE = IHyperAsset({
46-
assetBridgeAddress: 0x2222222222222222222222222222222222222222,
47-
coreIndexId: 1105,
48-
decimalDiff: 18 - 10
49-
});
50-
51-
oft = new MyOFT("test", "test", HL_LZ_ENDPOINT_V2, msg.sender);
52-
53-
hyperLiquidComposer = new HyperLiquidComposer(
54-
HL_LZ_ENDPOINT_V2,
55-
address(oft),
56-
ALICE.coreIndexId,
57-
ALICE.decimalDiff
58-
);
21+
function setUp() public override {
22+
super.setUp();
5923
}
6024

6125
function test_validateAndDecodeMessage_EncodingVariants(address _receiver, bool _useEncodePacked) public view {
@@ -77,7 +41,7 @@ contract ComposeMessageTest is Test {
7741
uint256 _amount,
7842
bytes memory _noise
7943
) internal view returns (bytes memory) {
80-
bytes memory payload = bytes.concat(addressToBytes32(sender), _receiver, _noise);
44+
bytes memory payload = bytes.concat(addressToBytes32(userA), _receiver, _noise);
8145
return OFTComposeMsgCodec.encode(0, 0, _amount, payload);
8246
}
8347

examples/oft-hyperliquid/test/foundry/HyperLiquidComposer.t.sol

Lines changed: 43 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,134 +1,83 @@
11
// SPDX-License-Identifier: UNLICENSED
22
pragma solidity ^0.8.0;
33

4-
import { Test, console } from "forge-std/Test.sol";
5-
6-
import { ILayerZeroEndpointV2 } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
7-
import { OFTComposeMsgCodec } from "@layerzerolabs/oft-evm/contracts/libs/OFTComposeMsgCodec.sol";
84
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
95

10-
import { HyperLiquidComposer, IHyperAsset } from "@layerzerolabs/hyperliquid-composer/contracts/HyperLiquidComposer.sol";
11-
import { ICoreWriter } from "@layerzerolabs/hyperliquid-composer/contracts/interfaces/ICoreWriter.sol";
12-
13-
import { HyperLiquidComposerCodec } from "@layerzerolabs/hyperliquid-composer/contracts/library/HyperLiquidComposerCodec.sol";
6+
import { OFTComposeMsgCodec } from "@layerzerolabs/oft-evm/contracts/libs/OFTComposeMsgCodec.sol";
147

8+
import { IHyperAsset } from "@layerzerolabs/hyperliquid-composer/contracts/interfaces/IHyperLiquidComposerCore.sol";
159
import { IHYPEPrecompile } from "@layerzerolabs/hyperliquid-composer/contracts/interfaces/IHYPEPrecompile.sol";
16-
import { HypePrecompileMock } from "@layerzerolabs/hyperliquid-composer/test/mocks/HypePrecompileMock.sol";
17-
import { SpotBalancePrecompileMock } from "@layerzerolabs/hyperliquid-composer/test/mocks/SpotBalancePrecompileMock.sol";
10+
import { ICoreWriter } from "@layerzerolabs/hyperliquid-composer/contracts/interfaces/ICoreWriter.sol";
1811

1912
import { MyOFT } from "../../contracts/MyOFT.sol";
13+
import { SpotBalancePrecompileMock } from "@layerzerolabs/hyperliquid-composer/test/mocks/SpotBalancePrecompileMock.sol";
2014

21-
contract HyperLiquidComposerTest is Test {
22-
IHyperAsset public ALICE;
23-
IHyperAsset public HYPE;
24-
address public constant HL_LZ_ENDPOINT_V2 = 0xf9e1815F151024bDE4B7C10BAC10e8Ba9F6b53E1;
25-
26-
address public constant HL_LZ_ENDPOINT_V2_TESTNET = 0xf9e1815F151024bDE4B7C10BAC10e8Ba9F6b53E1;
27-
address public constant HL_LZ_ENDPOINT_V2_MAINNET = 0x3A73033C0b1407574C76BdBAc67f126f6b4a9AA9;
28-
29-
address public constant HLP_CORE_WRITER = 0x3333333333333333333333333333333333333333;
30-
address public constant HLP_PRECOMPILE_READ_SPOT_BALANCE = 0x0000000000000000000000000000000000000801;
31-
// Ethereum Sepolia
32-
uint32 public constant SRC_EID = 40161;
33-
// HyperLiquid Testnet
34-
uint32 public DST_EID;
35-
36-
MyOFT public oft;
37-
HyperLiquidComposer public hyperLiquidComposer;
38-
39-
address public userA = makeAddr("userA");
40-
address public userB = makeAddr("userB");
41-
42-
uint64 public constant AMOUNT_TO_SEND = 1e18;
43-
uint64 public constant AMOUNT_TO_FUND = 100 gwei;
44-
uint64 public constant DUST = 1 wei;
45-
46-
function setUp() public {
47-
// Skip test if fork fails
48-
try vm.createSelectFork("https://rpc.hyperliquid-testnet.xyz/evm") {} catch {
49-
console.log("Forking testnet https://rpc.hyperliquid-testnet.xyz/evm failed");
50-
vm.skip(true);
51-
}
52-
53-
ALICE = IHyperAsset({
54-
assetBridgeAddress: HyperLiquidComposerCodec.into_assetBridgeAddress(1231),
55-
coreIndexId: 1231,
56-
decimalDiff: 18 - 6
57-
});
58-
59-
HYPE = IHyperAsset({
60-
assetBridgeAddress: 0x2222222222222222222222222222222222222222,
61-
coreIndexId: 1105,
62-
decimalDiff: 18 - 10
63-
});
64-
65-
vm.etch(HLP_PRECOMPILE_READ_SPOT_BALANCE, address(new SpotBalancePrecompileMock()).code);
66-
SpotBalancePrecompileMock(HLP_PRECOMPILE_READ_SPOT_BALANCE).setSpotBalance(
67-
ALICE.assetBridgeAddress,
68-
ALICE.coreIndexId,
69-
type(uint64).max
70-
);
71-
72-
SpotBalancePrecompileMock(HLP_PRECOMPILE_READ_SPOT_BALANCE).setSpotBalance(
73-
HYPE.assetBridgeAddress,
74-
HYPE.coreIndexId,
75-
type(uint64).max
76-
);
77-
78-
oft = new MyOFT("test", "test", HL_LZ_ENDPOINT_V2, msg.sender);
15+
import { HyperLiquidComposer } from "@layerzerolabs/hyperliquid-composer/contracts/HyperLiquidComposer.sol";
7916

80-
hyperLiquidComposer = new HyperLiquidComposer(
81-
HL_LZ_ENDPOINT_V2,
82-
address(oft),
83-
ALICE.coreIndexId,
84-
ALICE.decimalDiff
85-
);
86-
DST_EID = oft.endpoint().eid();
17+
import { HyperliquidBaseTest } from "@layerzerolabs/hyperliquid-composer/test/HyperliquidBase.t.sol";
18+
import { console } from "forge-std/Test.sol";
8719

88-
vm.deal(HL_LZ_ENDPOINT_V2, 100 ether);
20+
contract HyperLiquidComposerTest is HyperliquidBaseTest {
21+
function setUp() public override {
22+
super.setUp();
8923
}
9024

9125
function test_deployment() public view {
9226
IHyperAsset memory hypeAsset = hyperLiquidComposer.getHypeAsset();
9327
assertEq(hypeAsset.assetBridgeAddress, 0x2222222222222222222222222222222222222222);
94-
assertEq(hypeAsset.coreIndexId, 1105);
28+
uint256 expectedHypeCoreIndexId = block.chainid == 998 ? 1105 : 150;
29+
assertEq(hypeAsset.coreIndexId, expectedHypeCoreIndexId);
9530
assertEq(hypeAsset.decimalDiff, 10);
9631

9732
IHyperAsset memory oftAsset = hyperLiquidComposer.getOFTAsset();
98-
assertEq(oftAsset.assetBridgeAddress, ALICE.assetBridgeAddress);
99-
assertEq(oftAsset.coreIndexId, ALICE.coreIndexId);
100-
assertEq(oftAsset.decimalDiff, ALICE.decimalDiff);
33+
assertEq(oftAsset.assetBridgeAddress, ERC20.assetBridgeAddress);
34+
assertEq(oftAsset.coreIndexId, ERC20.coreIndexId);
35+
assertEq(oftAsset.decimalDiff, ERC20.decimalDiff);
10136
}
10237

10338
function test_hypeIndexByChainId_testnet() public {
104-
try vm.createSelectFork("https://rpc.hyperliquid-testnet.xyz/evm") {} catch {
105-
console.log("Forking testnet https://rpc.hyperliquid-testnet.xyz/evm failed");
39+
string memory rpcUrl = "https://rpc.hyperliquid-testnet.xyz/evm";
40+
try vm.envString("RPC_URL_HYPERLIQUID_TESTNET") returns (string memory _rpcUrl) {
41+
rpcUrl = _rpcUrl;
42+
} catch {
43+
console.log("Using default mainnet RPC URL");
44+
}
45+
// Skip test if fork fails
46+
try vm.createSelectFork(rpcUrl) {} catch {
47+
console.log("Forking testnet ", rpcUrl, " failed");
10648
vm.skip(true);
10749
}
10850

10951
MyOFT oftTestnet = new MyOFT("test", "test", HL_LZ_ENDPOINT_V2_TESTNET, msg.sender);
11052
HyperLiquidComposer hypeComposerTestnet = new HyperLiquidComposer(
11153
HL_LZ_ENDPOINT_V2_TESTNET,
11254
address(oftTestnet),
113-
ALICE.coreIndexId,
114-
ALICE.decimalDiff
55+
ERC20.coreIndexId,
56+
ERC20.decimalDiff
11557
);
11658

11759
assertEq(hypeComposerTestnet.hypeIndexByChainId(998), 1105);
11860
}
11961

12062
function test_hypeIndexByChainId_mainnet() public {
121-
try vm.createSelectFork("https://rpc.hyperliquid.xyz/evm") {} catch {
122-
console.log("Forking mainnet https://rpc.hyperliquid.xyz/evm failed");
63+
string memory rpcUrl = "https://rpc.hyperliquid.xyz/evm";
64+
try vm.envString("RPC_URL_HYPERLIQUID_MAINNET") returns (string memory _rpcUrl) {
65+
rpcUrl = _rpcUrl;
66+
} catch {
67+
console.log("Using default mainnet RPC URL");
68+
}
69+
// Skip test if fork fails
70+
try vm.createSelectFork(rpcUrl) {} catch {
71+
console.log("Forking mainnet ", rpcUrl, " failed");
12372
vm.skip(true);
12473
}
12574

12675
MyOFT oftMainnet = new MyOFT("test", "test", HL_LZ_ENDPOINT_V2_MAINNET, msg.sender);
12776
HyperLiquidComposer hypeComposerMainnet = new HyperLiquidComposer(
12877
HL_LZ_ENDPOINT_V2_MAINNET,
12978
address(oftMainnet),
130-
ALICE.coreIndexId,
131-
ALICE.decimalDiff
79+
ERC20.coreIndexId,
80+
ERC20.decimalDiff
13281
);
13382

13483
assertEq(hypeComposerMainnet.hypeIndexByChainId(999), 150);
@@ -140,7 +89,7 @@ contract HyperLiquidComposerTest is Test {
14089
// Build composerMsg similar to the outcome of OFTCore.send()
14190
bytes memory composerMsg_ = OFTComposeMsgCodec.encode(
14291
0,
143-
SRC_EID,
92+
ETH_EID,
14493
AMOUNT_TO_SEND + DUST,
14594
abi.encodePacked(addressToBytes32(userA), composeMsg)
14695
);
@@ -151,10 +100,10 @@ contract HyperLiquidComposerTest is Test {
151100

152101
// Expect the Transfer event to be emitted
153102
vm.expectEmit(address(oft));
154-
emit IERC20.Transfer(address(hyperLiquidComposer), ALICE.assetBridgeAddress, AMOUNT_TO_SEND);
103+
emit IERC20.Transfer(address(hyperLiquidComposer), ERC20.assetBridgeAddress, AMOUNT_TO_SEND);
155104

156105
uint64 coreAmount = hyperLiquidComposer.quoteHyperCoreAmount(AMOUNT_TO_SEND, true).core;
157-
bytes memory action = abi.encode(userB, ALICE.coreIndexId, coreAmount);
106+
bytes memory action = abi.encode(userB, ERC20.coreIndexId, coreAmount);
158107
bytes memory payload = abi.encodePacked(hyperLiquidComposer.SPOT_SEND_HEADER(), action);
159108
vm.expectEmit(HLP_CORE_WRITER);
160109
emit ICoreWriter.RawAction(address(hyperLiquidComposer), payload);
@@ -175,7 +124,7 @@ contract HyperLiquidComposerTest is Test {
175124
// Build composerMsg similar to the outcome of OFTCore.send()
176125
bytes memory composerMsg_ = OFTComposeMsgCodec.encode(
177126
0,
178-
SRC_EID,
127+
ETH_EID,
179128
AMOUNT_TO_SEND,
180129
abi.encodePacked(addressToBytes32(userA), composeMsg)
181130
);
@@ -189,10 +138,10 @@ contract HyperLiquidComposerTest is Test {
189138

190139
// Expect the Transfer event to be emitted
191140
vm.expectEmit(address(oft));
192-
emit IERC20.Transfer(address(hyperLiquidComposer), ALICE.assetBridgeAddress, AMOUNT_TO_SEND);
141+
emit IERC20.Transfer(address(hyperLiquidComposer), ERC20.assetBridgeAddress, AMOUNT_TO_SEND);
193142

194143
uint64 coreAmount = hyperLiquidComposer.quoteHyperCoreAmount(AMOUNT_TO_SEND, true).core;
195-
bytes memory action = abi.encode(userB, ALICE.coreIndexId, coreAmount);
144+
bytes memory action = abi.encode(userB, ERC20.coreIndexId, coreAmount);
196145
bytes memory payload = abi.encodePacked(hyperLiquidComposer.SPOT_SEND_HEADER(), action);
197146
vm.expectEmit(HLP_CORE_WRITER);
198147
emit ICoreWriter.RawAction(address(hyperLiquidComposer), payload);

packages/hyperliquid-composer/artifacts/HyperLiquidComposer.sol/HyperLiquidComposer.json

Lines changed: 2 additions & 2 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)