95 lines
2.8 KiB
JavaScript
95 lines
2.8 KiB
JavaScript
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);
|
|
});
|