package ccxt import ( "fmt" "math/big" "strings" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/mitchellh/mapstructure" "github.com/vmihailenco/msgpack/v5" ) // ===================================== Hyperliquid Structs ===================================== // // OrderMessage Struct // { // "brokerCode": 1, // "grouping": "na", // "orders": [ // // { // "a": 159, // "b": true, // "p": "26.25", // "r": false, // "s": "1000", // "t": { // "limit": { // "tif": "Ioc" // } // } // } // // ], // "type": "order" // } type TimeInForce struct { TIF string `mapstructure:"tif" msgpack:"tif"` } type Limit struct { TimeInForce TimeInForce `mapstructure:"limit" msgpack:"limit"` } type OrderHyperliquid struct { A int `mapstructure:"a" msgpack:"a"` B bool `mapstructure:"b" msgpack:"b"` P string `mapstructure:"p" msgpack:"p"` S string `mapstructure:"s" msgpack:"s"` R bool `mapstructure:"r" msgpack:"r"` T Limit `mapstructure:"t" msgpack:"t"` } type OrderMessage struct { Type string `mapstructure:"type" msgpack:"type"` Orders []OrderHyperliquid `mapstructure:"orders" msgpack:"orders"` Grouping string `mapstructure:"grouping" msgpack:"grouping"` BrokerCode int `mapstructure:"brokerCode" msgpack:"brokerCode"` } // cancel // {"type":"cancel","cancels":[{"a":10000,"o":9078231563}]} type Cancel struct { A int `mapstructure:"a" msgpack:"a"` O int `mapstructure:"o" msgpack:"o"` } type CancelMessage struct { Type string `mapstructure:"type" msgpack:"type"` Cancels []Cancel `mapstructure:"cancels" msgpack:"cancels"` } // Transfer // {"hyperliquidChain":"Mainnet","signatureChainId":"0x66eee","type":"usdClassTransfer","amount":"100000","toPerp":false,"nonce":1737458035944} type TransferMessage struct { HyperliquidChain string `mapstructure:"hyperliquidChain" msgpack:"hyperliquidChain"` SignatureChainID string `mapstructure:"signatureChainId" msgpack:"signatureChainId"` Type string `mapstructure:"type" msgpack:"type"` Amount string `mapstructure:"amount" msgpack:"amount"` ToPerp bool `mapstructure:"toPerp" msgpack:"toPerp"` Nonce int64 `mapstructure:"nonce" msgpack:"nonce"` } // withdraw // {"hyperliquidChain":"Mainnet","signatureChainId":"0x66eee","destination":"0xc950889d14a3717f541ec246bc253d7a9e98c78f","amount":"100000","time":1737458231937,"type":"withdraw3"} type WithdrawMessage struct { HyperliquidChain string `mapstructure:"hyperliquidChain" msgpack:"hyperliquidChain"` SignatureChainID string `mapstructure:"signatureChainId" msgpack:"signatureChainId"` Destination string `mapstructure:"destination" msgpack:"destination"` Amount string `mapstructure:"amount" msgpack:"amount"` Time int64 `mapstructure:"time" msgpack:"time"` Type string `mapstructure:"type" msgpack:"type"` } // editOrder // {"type":"batchModify","modifies":[{"oid":8553833906,"order":{"a":5,"b":true,"p":"151","s":"0.2","r":false,"t":{"limit":{"tif":"Gtc"}}}}]} type Modify struct { OID int64 `mapstructure:"oid" msgpack:"oid"` Order Order `mapstructure:"order" msgpack:"order"` } // EditOrderMessage represents the batch modification message. type EditOrderMessage struct { Type string `mapstructure:"type" msgpack:"type"` Modifies []Modify `mapstructure:"modifies" msgpack:"modifies"` } // ===================================== Hyperliquid Structs ===================================== // func ethEncodeStructuredData(primaryType string, domain apitypes.TypedDataDomain, messageTypes map[string][]apitypes.Type, messageData map[string]interface{}) (string, error) { // domain {"chainId":1337,"name":"Exchange","verifyingContract":"0x0000000000000000000000000000000000000000","version":"1"} // agent: {"Agent":[{"name":"source","type":"string"},{"name":"connectionId","type":"bytes32"}]} // phantom: {"source":"a","connectionId":{"0":81,"1":132,"2":60,"3":100,"4":202,"5":146,"6":114,"7":128,"8":99,"9":200,"10":106,"11":37,"12":220,"13":61,"14":150,"15":236,"16":173,"17":119,"18":83,"19":11,"20":205,"21":91,"22":222,"23":149,"24":201,"25":182,"26":71,"27":103,"28":74,"29":0,"30":223,"31":202}} var domainTypesList []apitypes.Type = []apitypes.Type{ {Name: "name", Type: "string"}, {Name: "version", Type: "string"}, {Name: "chainId", Type: "uint256"}, {Name: "verifyingContract", Type: "address"}, // {Name: "salt", Type: "bytes32"}, } messageTypes["EIP712Domain"] = domainTypesList typedData := apitypes.TypedData{ Domain: domain, Types: messageTypes, PrimaryType: primaryType, // Set this to the primary type used in the message Message: messageData, } encodedDomain, err := typedData.HashStruct("EIP712Domain", domain.Map()) if err != nil { return "", err } encodedData, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) if err != nil { return "", err } domainHex := hexutil.Encode(encodedDomain) // comes with 0x, remove domainHex = strings.TrimPrefix(domainHex, "0x") dataHex := hexutil.Encode(encodedData) dataHex = strings.TrimPrefix(dataHex, "0x") encodedHex := "1901" + domainHex + dataHex return encodedHex, nil } func (this *Exchange) EthEncodeStructuredData(domain2 interface{}, messageTypes2 interface{}, messageData2 interface{}) []uint8 { // domain {"chainId":1337,"name":"Exchange","verifyingContract":"0x0000000000000000000000000000000000000000","version":"1"} // agent: {"Agent":[{"name":"source","type":"string"},{"name":"connectionId","type":"bytes32"}]} // phantom: {"source":"a","connectionId":{"0":81,"1":132,"2":60,"3":100,"4":202,"5":146,"6":114,"7":128,"8":99,"9":200,"10":106,"11":37,"12":220,"13":61,"14":150,"15":236,"16":173,"17":119,"18":83,"19":11,"20":205,"21":91,"22":222,"23":149,"24":201,"25":182,"26":71,"27":103,"28":74,"29":0,"30":223,"31":202}} if this.Id != "hyperliquid" { return []uint8{} } domain := domain2.(map[string]interface{}) messageTypes := messageTypes2.(map[string]interface{}) messageData := messageData2.(map[string]interface{}) val, ok := messageData["nonce"] if ok { // messageData["nonce"] = uint64(val.(int64)) messageData["nonce"] = (*math.HexOrDecimal256)(big.NewInt(val.(int64))) } val, ok = messageData["time"] if ok { // messageData["time"] = uint64(val.(int64)) messageData["time"] = (*math.HexOrDecimal256)(big.NewInt(val.(int64))) } domainTyped := apitypes.TypedDataDomain{ Name: this.SafeString(domain, "name", "").(string), Version: this.SafeString(domain, "version", "").(string), ChainId: (*math.HexOrDecimal256)(big.NewInt(this.SafeInteger(domain, "chainId").(int64))), VerifyingContract: this.SafeString(domain, "verifyingContract", "").(string), } messageTypesTyped := map[string][]apitypes.Type{} primaryType := "" // check this what is the primary type for key, value := range messageTypes { primaryType = key types := value.([]interface{}) messageTypesTyped[key] = make([]apitypes.Type, len(types)) for i, type_ := range types { typeMap := type_.(map[string]interface{}) messageTypesTyped[key][i] = apitypes.Type{ Name: typeMap["name"].(string), Type: typeMap["type"].(string), } } } hexData, err := ethEncodeStructuredData(primaryType, domainTyped, messageTypesTyped, messageData) if err != nil { // log.Fatalf("Error encoding data: %v", err) str, _ := fmt.Printf("Binary Data: %x\n", hexData) panic(str) } return this.Base16ToBinary(hexData) } func ConvertInt64ToBigInt(data interface{}) interface{} { switch v := data.(type) { case map[string]interface{}: for key, value := range v { v[key] = ConvertInt64ToBigInt(value) } return v case []interface{}: for i, item := range v { v[i] = ConvertInt64ToBigInt(item) } return v case int64: return uint8(v) default: return v // Leave other types unchanged } } func ConvertInt64ToInt(data interface{}) interface{} { switch v := data.(type) { case map[string]interface{}: for key, value := range v { v[key] = ConvertInt64ToInt(value) } return v case []interface{}: for i, item := range v { v[i] = ConvertInt64ToInt(item) } return v case int64: return int(v) default: return v // Leave other types unchanged } } func (this *Exchange) Packb(data interface{}) []uint8 { converted := ConvertInt64ToBigInt(data) if this.Id != "hyperliquid" { p, err := msgpack.Marshal(converted) if err != nil { panic(err) } return p } typeA := this.SafeString(converted, "type", "").(string) if typeA == "order" { var orderMsg OrderMessage err := mapstructure.Decode(converted, &orderMsg) if err != nil { panic(err) } packed, err := msgpack.Marshal(orderMsg) if err != nil { panic(err) } return packed } else if typeA == "cancel" { var cancelMsg CancelMessage err := mapstructure.Decode(converted, &cancelMsg) if err != nil { panic(err) } packed, err := msgpack.Marshal(cancelMsg) if err != nil { panic(err) } return packed } else if typeA == "withdraw3" { var withdrawMsg WithdrawMessage err := mapstructure.Decode(converted, &withdrawMsg) if err != nil { panic(err) } packed, err := msgpack.Marshal(withdrawMsg) if err != nil { panic(err) } return packed } else if typeA == "batchModify" { var editMsg EditOrderMessage err := mapstructure.Decode(converted, &editMsg) if err != nil { panic(err) } packed, err := msgpack.Marshal(editMsg) if err != nil { panic(err) } return packed } return nil }