I am trying to sign and send an order request to Hyperliquid. And no matter what I try, and I tried a lot of different things, I get the response:
L1 error: User or API Wallet 0xb37035fd36b3fc08b42969b806f6d01b9edce5ff does not exist.
The Hash is always random. I assume it should show my address every time if code would do the right thing.
I watched SDK of Hyperliquid platform and tried convert their code from Python and TS to C#. Nothing helped including CGPT.
public static EthECDSASignature GetSignature<T>(string privateKey, T action,
long nonce,
string? vaultAddress,
bool isMainNet = true)
{
var bytes = EncodeLOneAction(action, nonce, vaultAddress, isMainNet);
var signature = new EthECKey(privateKey).SignAndCalculateV(bytes);
return signature;
}
public static byte[] EncodeLOneAction<T>(
T action,
long nonce,
string? vaultAddress,
bool isMainNet = true)
{
var actionHash = ActionHash(action, nonce, vaultAddress);
var domainSchema = GetDomainSchema1(actionHash, isMainNet);
var signer = new Eip712TypedDataSigner();
var encoder1 = signer.EncodeTypedData(domainSchema);
byte[] encodedDataHash1 = Sha3Keccack.Current.CalculateHash(encoder1);
return encodedDataHash1;
}
private static byte[] ActionHash<T>(T action, long nonce, string? vaultAddress)
{
var data = MessagePackSerializer.Serialize(action);
var nonceBytes = BitConverter.GetBytes(nonce);
data = [.. data, .. nonceBytes];
if (string.IsNullOrEmpty(vaultAddress))
{
// Add end byte
data = [.. data, .. new byte[] { 0x00 }];
}
else
{
// Add new line byte before adding address
data = [.. data, .. new byte[] { 0x01 }, .. AddressToBytes(vaultAddress)];
}
return Sha3Keccack.Current.CalculateHash(data);
}
private static TypedData<Domain> GetDomainSchema1(byte[] hash, bool isMainNet)
{
var typedData = new TypedData<Domain>()
{
Domain = new Domain
{
ChainId = 1337,
Name = "Exchange",
VerifyingContract = "0x0000000000000000000000000000000000000000",
Version = "1"
},
PrimaryType = "Agent",
Types = MemberDescriptionFactory.GetTypesMemberDescription(typeof(Domain), typeof(AgentType))
};
typedData.SetMessage(new AgentType
{
Source = (isMainNet ? "a" : "b"),
ConnectionId = hash
});
return typedData;
}
[Struct("Agent")]
private class AgentType
{
[Parameter("string", "source", 1)]
public virtual string Source { get; set; } = null!;
[Parameter("bytes32", "connectionId", 2)]
public virtual byte[] ConnectionId { get; set; } = null!;
}
My serialized post request after all the jazz before sending to Hyperliquid looks like this
{
"method": "post",
"id": 0,
"request":
{
"type": "action",
"payload":
{
"action":
{
"type": "order",
"orders": [
{
"a": 1,
"b": true,
"p": "0.2",
"s": "1100",
"r": false,
"t":
{
"limit":
{
"tif": "Gtc"
}
}
}],
"grouping": "na"
},
"nonce": 1726578116356,
"signature":
{
"r": "***a6d70929151805870921825f219",
"s": "***d3090bb540e015a69226a35ae8d",
"v": 28
}
}
}
}
At this point I am completely out of ideas what could be wrong and asking for help.