refactor: HL EVM signing via sonirico/go-hyperliquid SDK
This commit is contained in:
@@ -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);
|
||||
});
|
||||
Reference in New Issue
Block a user