estonlabs.cxtl.exchanges.hyperliquid.api.v0.util.HyperliquidSigner Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cxtl Show documentation
Show all versions of cxtl Show documentation
CXTL – Crypto eXchange Trading Library
package estonlabs.cxtl.exchanges.hyperliquid.api.v0.util;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.msgpack.jackson.dataformat.MessagePackFactory;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.Hash;
import org.web3j.crypto.Sign;
import org.web3j.crypto.StructuredDataEncoder;
import org.web3j.utils.Numeric;
import com.fasterxml.jackson.databind.ObjectMapper;
public class HyperliquidSigner {
public static HyperliquidSignature signL1Action(String privateKey, Object action, String activePool, long nonce, boolean isMainnet) throws Exception {
Credentials wallet = Credentials.create(privateKey);
byte[] hash = actionHash(action, activePool, nonce);
Map phantomAgent = constructPhantomAgent(hash, isMainnet);
ObjectMapper mapper = new ObjectMapper();
Map data = new HashMap<>();
data.put("domain", Map.of("chainId", 1337, "name", "Exchange", "verifyingContract",
"0x0000000000000000000000000000000000000000", "version", "1"));
data.put("types", Map.of("Agent",
List.of(Map.of("name", "source", "type", "string"), Map.of("name", "connectionId", "type", "bytes32")),
"EIP712Domain",
List.of(Map.of("name", "name", "type", "string"), Map.of("name", "version", "type", "string"),
Map.of("name", "chainId", "type", "uint256"),
Map.of("name", "verifyingContract", "type", "address"))));
data.put("primaryType", "Agent");
data.put("message", phantomAgent);
String json = mapper.writeValueAsString(data);
return signInner(wallet, json);
}
private static Map constructPhantomAgent(byte[] hash, boolean isMainnet) {
Map phantomAgent = new HashMap<>();
phantomAgent.put("source", isMainnet ? "a" : "b");
phantomAgent.put("connectionId", Numeric.toHexString(hash));
return phantomAgent;
}
private static HyperliquidSignature signInner(Credentials wallet, String jsonData) throws Exception {
StructuredDataEncoder dataEncoder = new StructuredDataEncoder(jsonData);
byte[] structuredData = dataEncoder.hashStructuredData();
Sign.SignatureData signature = Sign.signMessage(structuredData, wallet.getEcKeyPair(), false);
String r = Numeric.toHexString(signature.getR());
String s = Numeric.toHexString(signature.getS());
int v = signature.getV()[0];
Map signatureMap = new HashMap<>();
signatureMap.put("r", r);
signatureMap.put("s", s);
signatureMap.put("v", v);
return new HyperliquidSignature(r, s, v);
}
private static byte[] addressToBytes(String address) {
return new BigInteger(address.startsWith("0x") ? address.substring(2) : address, 16).toByteArray();
}
private static byte[] actionHash(Object action, String vaultAddress, long nonce) throws Exception {
ObjectMapper mapper = new ObjectMapper(new MessagePackFactory());
byte[] data = mapper.writeValueAsBytes(action);
data = combine(data, longToBytes(nonce, 8));
if (vaultAddress == null) {
data = combine(data, new byte[] { 0x00 });
} else {
data = combine(data, new byte[] { 0x01 });
data = combine(data, addressToBytes(vaultAddress));
}
return Hash.sha3(data);
}
private static byte[] longToBytes(long value, int byteSize) {
ByteBuffer buffer = ByteBuffer.allocate(byteSize);
buffer.order(ByteOrder.BIG_ENDIAN);
buffer.putLong(value);
return buffer.array();
}
private static byte[] combine(byte[] a, byte[] b) {
byte[] combined = new byte[a.length + b.length];
System.arraycopy(a, 0, combined, 0, a.length);
System.arraycopy(b, 0, combined, a.length, b.length);
return combined;
}
}