refactor: HL EVM signing via sonirico/go-hyperliquid SDK

This commit is contained in:
jackyu66git
2026-05-04 16:15:51 +08:00
parent c5c4c7394f
commit 8b8d4140f9
1974 changed files with 270551 additions and 156 deletions
+94
View File
@@ -0,0 +1,94 @@
const https = require('https');
// Read args: [action_json, nonce, private_key, address, isMainnet]
const [actionJson, nonceStr, privKey, address, isMainnetStr] = process.argv.slice(2);
if (!actionJson || !nonceStr || !privKey) {
console.log(JSON.stringify({ error: "Usage: node hl_sign.js '<action_json>' <nonce> <priv_key> [address] [isMainnet]" }));
process.exit(1);
}
const action = JSON.parse(actionJson);
const nonce = parseInt(nonceStr);
const isMainnet = isMainnetStr === 'true';
// ---- Build the EIP-712 typed data ----
const { ethers } = require('ethers');
async function main() {
const wallet = new ethers.Wallet(privKey);
const apiAddr = wallet.address;
// 1. Normalize action (remove trailing zeros)
let normalizedAction = JSON.parse(JSON.stringify(action));
function normalize(obj) {
if (!obj || typeof obj !== 'object') return;
if (Array.isArray(obj)) {
for (const item of obj) normalize(item);
} else {
for (const key of Object.keys(obj)) {
if ((key === 'p' || key === 's') && typeof obj[key] === 'string') {
obj[key] = obj[key].replace(/\.?0+$/, '').replace(/^-0$/, '0');
} else {
normalize(obj[key]);
}
}
}
}
normalize(normalizedAction);
// 2. msgpack encode the action
const { encode } = require('@msgpack/msgpack');
const msgBytes = encode(normalizedAction);
// 3. Build hash: msgpack + nonce(8 bytes big endian) + flag(1 byte)
const totalLen = msgBytes.length + 8 + 1;
const data = new Uint8Array(totalLen);
data.set(msgBytes, 0);
const view = new DataView(data.buffer);
view.setBigUint64(msgBytes.length, BigInt(nonce), false);
view.setUint8(msgBytes.length + 8, 0); // no vault address
const hash = ethers.keccak256(data);
// 4. EIP-712 signing
const source = isMainnet ? 'a' : 'b';
const domain = {
name: 'Exchange',
version: '1',
chainId: 1337,
verifyingContract: '0x0000000000000000000000000000000000000000',
};
const types = {
Agent: [
{ name: 'source', type: 'string' },
{ name: 'connectionId', type: 'bytes32' },
],
};
const message = { source, connectionId: hash };
const signature = await wallet.signTypedData(domain, types, message);
const sig = ethers.Signature.from(signature);
// 5. Build result
const result = {
action: action,
nonce: nonce,
signature: {
r: sig.r,
s: sig.s,
v: sig.v,
},
};
// If address is provided and != wallet.address, it's the main account
// The SDK automatically handles this via the accountAddress constructor param
// For order placement via exchange API, we just send the signature
console.log(JSON.stringify(result));
}
main().catch(err => {
console.log(JSON.stringify({ error: err.message }));
process.exit(1);
});