Skip to content

Commit 4e9c0e7

Browse files
committed
Info API: Added UserRole to auto set vaultAddress on startup
1 parent b2f2b2d commit 4e9c0e7

File tree

10 files changed

+399
-191
lines changed

10 files changed

+399
-191
lines changed

hyperliquid/client.go

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ type IClient interface {
2222
IAPIService
2323
SetPrivateKey(privateKey string) error
2424
SetAccountAddress(address string)
25+
SetVaultAddress(address string)
26+
SetUserRole(role Role)
2527
AccountAddress() string
28+
VaultAddress() string
2629
SetDebugActive()
2730
IsMainnet() bool
2831
}
@@ -33,14 +36,16 @@ type IClient interface {
3336
// the network type, the private key, and the logger.
3437
// The debug method prints the debug messages.
3538
type Client struct {
36-
baseUrl string // Base URL of the HyperLiquid API
39+
baseURL string // Base URL of the HyperLiquid API
3740
privateKey string // Private key for the client
38-
defualtAddress string // Default address for the client
41+
defaultAddress string // Default address for the client
3942
isMainnet bool // Network type
4043
Debug bool // Debug mode
4144
httpClient *http.Client // HTTP client
4245
keyManager *PKeyManager // Private key manager
4346
Logger *log.Logger // Logger for debug messages
47+
role Role // Role of the client,
48+
vaultAddress string // Vault address
4449
}
4550

4651
// Returns the private key manager connected to the API.
@@ -60,19 +65,21 @@ func getURL(isMainnet bool) string {
6065
// NewClient returns a new instance of the Client struct.
6166
func NewClient(isMainnet bool) *Client {
6267
logger := log.New()
68+
logger.SetLevel(log.DebugLevel)
6369
logger.SetFormatter(&log.TextFormatter{
6470
FullTimestamp: true,
6571
PadLevelText: true,
72+
ForceColors: true,
6673
})
6774
logger.SetOutput(os.Stdout)
6875
logger.SetLevel(log.DebugLevel)
6976
return &Client{
70-
baseUrl: getURL(isMainnet),
77+
baseURL: getURL(isMainnet),
7178
httpClient: http.DefaultClient,
7279
Debug: false,
7380
isMainnet: isMainnet,
7481
privateKey: "",
75-
defualtAddress: "",
82+
defaultAddress: "",
7683
Logger: logger,
7784
keyManager: nil,
7885
}
@@ -100,12 +107,30 @@ func (client *Client) SetPrivateKey(privateKey string) error {
100107
// In case you use PKeyManager from API section https://app.hyperliquid.xyz/API
101108
// Then you can use this method to set the address.
102109
func (client *Client) SetAccountAddress(address string) {
103-
client.defualtAddress = address
110+
client.defaultAddress = address
104111
}
105112

106113
// Returns the public address connected to the API.
107114
func (client *Client) AccountAddress() string {
108-
return client.defualtAddress
115+
return client.defaultAddress
116+
}
117+
118+
// VaultAddress returns the vault address for the client.
119+
func (client *Client) VaultAddress() string {
120+
return client.vaultAddress
121+
}
122+
123+
// SetVaultAddress sets the vault address for the client.
124+
func (client *Client) SetVaultAddress(vaultAddress string) {
125+
client.vaultAddress = vaultAddress
126+
}
127+
128+
// SetUserRole sets the user role for the client.
129+
func (client *Client) SetUserRole(role Role) {
130+
client.role = role
131+
if role.IsVaultOrSubAccount() {
132+
client.vaultAddress = client.AccountAddress()
133+
}
109134
}
110135

111136
// IsMainnet returns true if the client is connected to the mainnet.
@@ -121,7 +146,7 @@ func (client *Client) SetDebugActive() {
121146
// Request sends a POST request to the HyperLiquid API.
122147
func (client *Client) Request(endpoint string, payload any) ([]byte, error) {
123148
endpoint = strings.TrimPrefix(endpoint, "/") // Remove leading slash if present
124-
url := fmt.Sprintf("%s/%s", client.baseUrl, endpoint)
149+
url := fmt.Sprintf("%s/%s", client.baseURL, endpoint)
125150
client.debug("Request to %s", url)
126151
jsonPayload, err := json.Marshal(payload)
127152
if err != nil {

hyperliquid/convert.go

Lines changed: 94 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,21 @@ func HexToBytes(addr string) []byte {
3434
return b
3535
}
3636
}
37+
func HexToInt(hexString string) (*big.Int, error) {
38+
value := new(big.Int)
39+
if len(hexString) > 1 && hexString[:2] == "0x" {
40+
hexString = hexString[2:]
41+
}
42+
_, success := value.SetString(hexString, 16)
43+
if !success {
44+
return nil, fmt.Errorf("invalid hexadecimal string: %s", hexString)
45+
}
46+
return value, nil
47+
}
48+
49+
func IntToHex(value *big.Int) string {
50+
return "0x" + value.Text(16)
51+
}
3752

3853
func OrderWiresToOrderAction(orders []OrderWire, grouping Grouping) PlaceOrderAction {
3954
return PlaceOrderAction{
@@ -43,19 +58,33 @@ func OrderWiresToOrderAction(orders []OrderWire, grouping Grouping) PlaceOrderAc
4358
}
4459
}
4560

46-
func OrderRequestToWire(req OrderRequest, meta map[string]AssetInfo, isSpot bool) OrderWire {
61+
func (req *OrderRequest) isSpot() bool {
62+
return strings.ContainsAny(req.Coin, "@-")
63+
}
64+
65+
// ToWire (OrderRequest) converts an OrderRequest to an OrderWire using the provided metadata.
66+
// https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/asset-ids
67+
func (req *OrderRequest) ToWireMeta(meta map[string]AssetInfo) OrderWire {
4768
info := meta[req.Coin]
48-
var assetId, maxDecimals int
49-
if isSpot {
50-
// https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/asset-ids
51-
assetId = info.AssetId + 10000
69+
return req.ToWire(info)
70+
}
71+
72+
func (req *OrderRequest) ToModifyWire(info AssetInfo) ModifyOrderWire {
73+
return ModifyOrderWire{
74+
OrderID: *req.OrderID,
75+
Order: req.ToWire(info),
76+
}
77+
78+
}
79+
func (req *OrderRequest) ToWire(info AssetInfo) OrderWire {
80+
var assetID = info.AssetID
81+
var maxDecimals = PERP_MAX_DECIMALS
82+
if req.isSpot() {
83+
assetID = info.AssetID + 10000 // https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/asset-ids
5284
maxDecimals = SPOT_MAX_DECIMALS
53-
} else {
54-
assetId = info.AssetId
55-
maxDecimals = PERP_MAX_DECIMALS
5685
}
5786
return OrderWire{
58-
Asset: assetId,
87+
Asset: assetID,
5988
IsBuy: req.IsBuy,
6089
LimitPx: PriceToWire(req.LimitPx, maxDecimals, info.SzDecimals),
6190
SizePx: SizeToWire(req.Sz, info.SzDecimals),
@@ -65,29 +94,7 @@ func OrderRequestToWire(req OrderRequest, meta map[string]AssetInfo, isSpot bool
6594
}
6695
}
6796

68-
func ModifyOrderRequestToWire(req ModifyOrderRequest, meta map[string]AssetInfo, isSpot bool) ModifyOrderWire {
69-
info := meta[req.Coin]
70-
var assetId, maxDecimals int
71-
if isSpot {
72-
// https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/asset-ids
73-
assetId = info.AssetId + 10000
74-
maxDecimals = SPOT_MAX_DECIMALS
75-
} else {
76-
assetId = info.AssetId
77-
maxDecimals = PERP_MAX_DECIMALS
78-
}
79-
return ModifyOrderWire{
80-
OrderId: req.OrderId,
81-
Order: OrderWire{
82-
Asset: assetId,
83-
IsBuy: req.IsBuy,
84-
LimitPx: PriceToWire(req.LimitPx, maxDecimals, info.SzDecimals),
85-
SizePx: SizeToWire(req.Sz, info.SzDecimals),
86-
ReduceOnly: req.ReduceOnly,
87-
OrderType: OrderTypeToWire(req.OrderType),
88-
},
89-
}
90-
}
97+
// ToWire (ModifyOrderRequest) converts a ModifyOrderRequest to a ModifyOrderWire using the provided metadata.
9198

9299
func OrderTypeToWire(orderType OrderType) OrderTypeWire {
93100
if orderType.Limit != nil {
@@ -110,8 +117,26 @@ func OrderTypeToWire(orderType OrderType) OrderTypeWire {
110117
return OrderTypeWire{}
111118
}
112119

113-
// Format the float with custom decimal places, default is 6 (perp), 8 (spot).
114-
// https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/tick-and-lot-size
120+
/**
121+
* FloatToWire converts a float64 to a string representation following Hyperliquid's decimal rules.
122+
* FloatToWire converts a float64 to a string representation following Hyperliquid's decimal rules.
123+
*
124+
* The conversion adheres to market-specific decimal place constraints:
125+
* - Perpetual markets: Maximum 6 decimal places
126+
* - Spot markets: Maximum 8 decimal places
127+
*
128+
* The function dynamically adjusts decimal precision based on:
129+
* 1. Integer part magnitude
130+
* 2. Maximum allowed decimals (maxDecimals)
131+
* 3. Size decimal precision (szDecimals)
132+
*
133+
* Output formatting:
134+
* - Removes trailing zeros
135+
* - Trims unnecessary decimal points
136+
* - Maintains tick size precision requirements
137+
*
138+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/tick-and-lot-size
139+
*/
115140
func FloatToWire(x float64, maxDecimals int, szDecimals int) string {
116141
bigf := big.NewFloat(x)
117142
var maxDecSz uint
@@ -228,3 +253,38 @@ func StructToMap(strct any) (res map[string]interface{}, err error) {
228253
json.Unmarshal(a, &res)
229254
return res, nil
230255
}
256+
257+
// RoundOrderSize rounds the order size to the nearest tick size
258+
func RoundOrderSize(x float64, szDecimals int) string {
259+
newX := math.Round(x*math.Pow10(szDecimals)) / math.Pow10(szDecimals)
260+
// TODO: add rounding
261+
return big.NewFloat(newX).Text('f', szDecimals)
262+
}
263+
264+
// RoundOrderPrice rounds the order price to the nearest tick size
265+
func RoundOrderPrice(x float64, szDecimals int, maxDecimals int) string {
266+
maxSignFigures := 5
267+
allowedDecimals := maxDecimals - szDecimals
268+
numberOfDigitsInIntegerPart := len(strconv.Itoa(int(x)))
269+
if numberOfDigitsInIntegerPart >= maxSignFigures {
270+
return RoundOrderSize(x, 0)
271+
}
272+
allowedSignFigures := maxSignFigures - numberOfDigitsInIntegerPart
273+
if x >= 1 {
274+
return RoundOrderSize(x, min(allowedSignFigures, allowedDecimals))
275+
}
276+
277+
text := RoundOrderSize(x, allowedDecimals)
278+
startSignFigures := false
279+
for i := 2; i < len(text); i++ {
280+
if text[i] == '0' && !startSignFigures {
281+
continue
282+
}
283+
startSignFigures = true
284+
allowedSignFigures--
285+
if allowedSignFigures == 0 {
286+
return text[:i+1]
287+
}
288+
}
289+
return text
290+
}

0 commit comments

Comments
 (0)