Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ Legend: ✅ implemented — ⬜ not yet implemented.
| General | `subAccounts2` | ⬜ | |
| General | `twapHistory` | ✅ | `RestApi::twapHistory` |
| General | `usdcRouting` | ⬜ | |
| General | `userBorrowLendInterest` | ⬜ | |
| General | `userBorrowLendInterest` | ✅ | `RestApi::userBorrowLendInterest` |
| General | `userToMultiSigSigners` | ⬜ | |
| General | `userTwapSliceFillsByTime` | ✅ | `RestApi::userTwapSliceFillsByTime` |
| General | `validatorL1Votes` | ⬜ | |
Expand All @@ -146,7 +146,7 @@ Legend: ✅ implemented — ⬜ not yet implemented.
| Perpetuals | `perpAnnotation` | ✅ | `RestApi::perpAnnotation` |
| Perpetuals | `perpCategories` | ✅ | `RestApi::perpCategories` |
| Perpetuals | `perpConciseAnnotations` | ✅ | `RestApi::perpConciseAnnotations` |
| Perpetuals | `liquidatable` | ⬜ | |
| Perpetuals | `liquidatable` | ✅ | `RestApi::liquidatable` |
| Perpetuals | `marginTable` | ⬜ | |
| Perpetuals | `maxMarketOrderNtls` | ⬜ | |
| Perpetuals | `recentTrades` | ✅ | `RestApi::recentTrades` |
Expand All @@ -161,7 +161,7 @@ Legend: ✅ implemented — ⬜ not yet implemented.
| Spot / Outcomes | `outcomeDeployerLimits` | ⬜ | |
| Spot / Outcomes | `outcomeTemplates` | ⬜ | |

56 of 78 documented info endpoints implemented. One (`tokenDetails`) has a `RestEndpointType` enum value reserved but no request builder or method yet.
58 of 78 documented info endpoints implemented. One (`tokenDetails`) has a `RestEndpointType` enum value reserved but no request builder or method yet.

### Exchange actions (`/exchange`)

Expand Down
14 changes: 14 additions & 0 deletions examples/rest_borrow_lend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <hyperliquid/config/Config.h>
#include <spdlog/spdlog.h>

#include <chrono>

int main() {
auto wallet = loadWalletFromConfig();
hyperliquid::setLogLevel(hyperliquid::LogLevel::Debug);
Expand Down Expand Up @@ -39,5 +41,17 @@ int main() {
position.token, position.supply.value, position.borrow.value);
}

spdlog::info("=== userBorrowLendInterest(own wallet, last 30d) ===");
uint64_t now = static_cast<uint64_t>(
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count());
uint64_t thirtyDaysAgo = now - 30ULL * 24ULL * 3600ULL * 1000ULL;
auto interest = api.userBorrowLendInterest(wallet.accountAddress, thirtyDaysAgo);
spdlog::info("{} interest entries", interest.interest.size());
for (const auto& entry : interest.interest) {
spdlog::info(" time={} token={} borrow={} supply={}",
entry.time, entry.token, entry.borrow, entry.supply);
}

return 0;
}
7 changes: 7 additions & 0 deletions examples/rest_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ int main()
auto mids = api.allMids();
spdlog::info("{} mids", mids.mids.size());

spdlog::info("=== liquidatable ===");
auto liquidatable = api.liquidatable();
spdlog::info("{} liquidatable positions", liquidatable.positions.size());
for (const auto& p : liquidatable.positions)
spdlog::info(" user={} isolatedAsset={} marginAvailable=[{}, {}]",
p.user, p.isolatedAsset, p.marginAvailable[0], p.marginAvailable[1]);

uint64_t now = static_cast<uint64_t>(
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count());
Expand Down
8 changes: 8 additions & 0 deletions include/hyperliquid/rest/RestApi.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ class RestApi {
BorrowLendUserStateResponse borrowLendUserState(const std::string& user);
BorrowLendReserveState borrowLendReserveState(int token);
AllBorrowLendReserveStatesResponse allBorrowLendReserveStates();
UserBorrowLendInterestResponse userBorrowLendInterest(const std::string& user,
uint64_t startTime,
const std::optional<uint64_t>& endTime = std::nullopt);
LiquidatableResponse liquidatable();
UserDexAbstractionResponse userDexAbstractionState(const std::string& user);
UserAbstractionResponse userAbstraction(const std::string& user);
PlaceOrderResponse placeOrder(const std::vector<OrderRequest>& orders,
Expand Down Expand Up @@ -251,6 +255,10 @@ class RestApi {
void borrowLendUserStateAsync(const std::string& user);
void borrowLendReserveStateAsync(int token);
void allBorrowLendReserveStatesAsync();
void userBorrowLendInterestAsync(const std::string& user,
uint64_t startTime,
const std::optional<uint64_t>& endTime = std::nullopt);
void liquidatableAsync();
void userDexAbstractionStateAsync(const std::string& user);
void userAbstractionAsync(const std::string& user);
void placeOrderAsync(const std::vector<OrderRequest>& orders,
Expand Down
2 changes: 2 additions & 0 deletions include/hyperliquid/rest/RestApiMessageParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ namespace hyperliquid
BorrowLendUserStateResponse parseBorrowLendUserState(const std::string& message);
BorrowLendReserveState parseBorrowLendReserveState(const std::string& message);
AllBorrowLendReserveStatesResponse parseAllBorrowLendReserveStates(const std::string& message);
UserBorrowLendInterestResponse parseUserBorrowLendInterest(const std::string& message);
LiquidatableResponse parseLiquidatable(const std::string& message);
PlaceOrderResponse parsePlaceOrder(const std::string& message);
CancelOrderResponse parseCancelOrder(const std::string& message);
ModifyOrderResponse parseModifyOrder(const std::string& message);
Expand Down
2 changes: 2 additions & 0 deletions include/hyperliquid/rest/RestEndpointListener.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ class RestEndpointListener {
virtual void onBorrowLendUserState(const BorrowLendUserStateResponse&, std::optional<uint64_t> = std::nullopt) {}
virtual void onBorrowLendReserveState(const BorrowLendReserveState&, std::optional<uint64_t> = std::nullopt) {}
virtual void onAllBorrowLendReserveStates(const AllBorrowLendReserveStatesResponse&, std::optional<uint64_t> = std::nullopt) {}
virtual void onUserBorrowLendInterest(const UserBorrowLendInterestResponse&, std::optional<uint64_t> = std::nullopt) {}
virtual void onLiquidatable(const LiquidatableResponse&, std::optional<uint64_t> = std::nullopt) {}
virtual void onUserDexAbstractionState(const UserDexAbstractionResponse&, std::optional<uint64_t> = std::nullopt) {}
virtual void onUserAbstraction(const UserAbstractionResponse&, std::optional<uint64_t> = std::nullopt) {}
};
Expand Down
6 changes: 6 additions & 0 deletions include/hyperliquid/types/RequestTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ namespace hyperliquid
BorrowLendUserState,
BorrowLendReserveState,
AllBorrowLendReserveStates,
UserBorrowLendInterest,
Liquidatable,
UserDexAbstractionState,
UserAbstraction,

Expand Down Expand Up @@ -312,6 +314,8 @@ namespace hyperliquid
case RestEndpointType::BorrowLendUserState: return "borrowLendUserState";
case RestEndpointType::BorrowLendReserveState: return "borrowLendReserveState";
case RestEndpointType::AllBorrowLendReserveStates: return "allBorrowLendReserveStates";
case RestEndpointType::UserBorrowLendInterest: return "userBorrowLendInterest";
case RestEndpointType::Liquidatable: return "liquidatable";
case RestEndpointType::UserDexAbstractionState: return "userDexAbstraction";
case RestEndpointType::UserAbstraction: return "userAbstraction";

Expand Down Expand Up @@ -418,6 +422,8 @@ namespace hyperliquid
case RestEndpointType::BorrowLendUserState: return false;
case RestEndpointType::BorrowLendReserveState: return false;
case RestEndpointType::AllBorrowLendReserveStates: return false;
case RestEndpointType::UserBorrowLendInterest: return false;
case RestEndpointType::Liquidatable: return false;
case RestEndpointType::UserDexAbstractionState: return false;
case RestEndpointType::UserAbstraction: return false;

Expand Down
26 changes: 26 additions & 0 deletions include/hyperliquid/types/ResponseTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1701,6 +1701,32 @@ namespace hyperliquid
std::optional<double> healthFactor;
};

struct UserBorrowLendInterestEntry
{
uint64_t time = 0;
std::string token;
double borrow = 0.0;
double supply = 0.0;
};

struct UserBorrowLendInterestResponse
{
std::vector<UserBorrowLendInterestEntry> interest;
};

struct LiquidatableEntry
{
std::string user;
int isolatedAsset = 0;
// Meaning of the two values is not documented upstream.
std::array<double, 2> marginAvailable{};
};

struct LiquidatableResponse
{
std::vector<LiquidatableEntry> positions;
};

// --- HIP-3 deployer (perp dex abstraction) ---

struct PerpDexLimitsCoinCap
Expand Down
5 changes: 5 additions & 0 deletions include/hyperliquid/websocket/WebsocketApi.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ namespace hyperliquid
void borrowLendUserState(const std::string& user, std::optional<uint64_t> correlationId = std::nullopt);
void borrowLendReserveState(int token, std::optional<uint64_t> correlationId = std::nullopt);
void allBorrowLendReserveStates(std::optional<uint64_t> correlationId = std::nullopt);
void userBorrowLendInterest(const std::string& user,
uint64_t startTime,
const std::optional<uint64_t>& endTime = std::nullopt,
std::optional<uint64_t> correlationId = std::nullopt);
void liquidatable(std::optional<uint64_t> correlationId = std::nullopt);

void spotDeployState(const std::string& user, std::optional<uint64_t> correlationId = std::nullopt);
void spotPairDeployAuctionStatus(std::optional<uint64_t> correlationId = std::nullopt);
Expand Down
19 changes: 19 additions & 0 deletions src/messages/InfoRequestBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,25 @@ nlohmann::ordered_json InfoRequestBuilder::allBorrowLendReserveStates()
return body;
}

nlohmann::ordered_json InfoRequestBuilder::userBorrowLendInterest(const std::string& user,
uint64_t startTime,
const std::optional<uint64_t>& endTime)
{
nlohmann::ordered_json body;
body["type"] = toString(RestEndpointType::UserBorrowLendInterest);
body["user"] = user;
body["startTime"] = startTime;
if (endTime) body["endTime"] = *endTime;
return body;
}

nlohmann::ordered_json InfoRequestBuilder::liquidatable()
{
nlohmann::ordered_json body;
body["type"] = toString(RestEndpointType::Liquidatable);
return body;
}

nlohmann::ordered_json InfoRequestBuilder::userDexAbstractionState(const std::string& user)
{
nlohmann::ordered_json body;
Expand Down
4 changes: 4 additions & 0 deletions src/messages/InfoRequestBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ class InfoRequestBuilder {
static nlohmann::ordered_json borrowLendUserState(const std::string& user);
static nlohmann::ordered_json borrowLendReserveState(int token);
static nlohmann::ordered_json allBorrowLendReserveStates();
static nlohmann::ordered_json userBorrowLendInterest(const std::string& user,
uint64_t startTime,
const std::optional<uint64_t>& endTime = std::nullopt);
static nlohmann::ordered_json liquidatable();

static nlohmann::ordered_json userDexAbstractionState(const std::string& user);
static nlohmann::ordered_json userAbstraction(const std::string& user);
Expand Down
28 changes: 28 additions & 0 deletions src/rest/RestApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,21 @@ AllBorrowLendReserveStatesResponse RestApi::allBorrowLendReserveStates()
InfoRequestBuilder::allBorrowLendReserveStates()));
}

UserBorrowLendInterestResponse RestApi::userBorrowLendInterest(const std::string& user,
uint64_t startTime,
const std::optional<uint64_t>& endTime)
{
return RestApiMessageParser().parseUserBorrowLendInterest(
impl_->signAndSendSync(RestEndpointType::UserBorrowLendInterest,
InfoRequestBuilder::userBorrowLendInterest(user, startTime, endTime)));
}

LiquidatableResponse RestApi::liquidatable()
{
return RestApiMessageParser().parseLiquidatable(
impl_->signAndSendSync(RestEndpointType::Liquidatable, InfoRequestBuilder::liquidatable()));
}

UserDexAbstractionResponse RestApi::userDexAbstractionState(const std::string& user)
{
return RestApiMessageParser().parseUserDexAbstractionState(
Expand Down Expand Up @@ -1072,6 +1087,19 @@ void RestApi::allBorrowLendReserveStatesAsync()
impl_->signAndSend(RestEndpointType::AllBorrowLendReserveStates, InfoRequestBuilder::allBorrowLendReserveStates());
}

void RestApi::userBorrowLendInterestAsync(const std::string& user,
uint64_t startTime,
const std::optional<uint64_t>& endTime)
{
impl_->signAndSend(RestEndpointType::UserBorrowLendInterest,
InfoRequestBuilder::userBorrowLendInterest(user, startTime, endTime));
}

void RestApi::liquidatableAsync()
{
impl_->signAndSend(RestEndpointType::Liquidatable, InfoRequestBuilder::liquidatable());
}

void RestApi::userDexAbstractionStateAsync(const std::string& user)
{
impl_->signAndSend(RestEndpointType::UserDexAbstractionState, InfoRequestBuilder::userDexAbstractionState(user));
Expand Down
90 changes: 90 additions & 0 deletions src/rest/RestApiMessageParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,12 @@ namespace hyperliquid
case RestEndpointType::AllBorrowLendReserveStates:
listener.onAllBorrowLendReserveStates(parseAllBorrowLendReserveStates(message), correlationId);
break;
case RestEndpointType::UserBorrowLendInterest:
listener.onUserBorrowLendInterest(parseUserBorrowLendInterest(message), correlationId);
break;
case RestEndpointType::Liquidatable:
listener.onLiquidatable(parseLiquidatable(message), correlationId);
break;
case RestEndpointType::PlaceOrder:
listener.onPlaceOrder(parsePlaceOrder(message), correlationId);
break;
Expand Down Expand Up @@ -1898,6 +1904,80 @@ namespace hyperliquid
return response;
}

UserBorrowLendInterestResponse parseUserBorrowLendInterest(const std::string& message)
{
UserBorrowLendInterestResponse response;
padded = simdjson::padded_string(message.data(), message.size());
auto doc = parser.iterate(padded);

try
{
validateStructure(message);

auto arr = doc.get_array().value();
for (auto entry : arr)
{
auto obj = entry.get_object().value();
UserBorrowLendInterestEntry item{};
item.time = obj["time"].get_uint64().value();
item.token = std::string(obj["token"].get_string().value());
item.borrow = parseNumberField(obj, "borrow");
item.supply = parseNumberField(obj, "supply");
response.interest.push_back(std::move(item));
}
}
catch (const simdjson::simdjson_error& err)
{
getLogger()->error("RestMessageParser: parse error in userBorrowLendInterest: {}\n raw: {}", err.what(), message);
}

return response;
}

LiquidatableResponse parseLiquidatable(const std::string& message)
{
LiquidatableResponse response;
padded = simdjson::padded_string(message.data(), message.size());
auto doc = parser.iterate(padded);

try
{
validateStructure(message);

auto arr = doc.get_array().value();
for (auto entry : arr)
{
auto obj = entry.get_object().value();
LiquidatableEntry item{};
item.user = std::string(obj["user"].get_string().value());

auto positionIndexObj = obj["positionIndex"].get_object().value();
auto isolatedObj = positionIndexObj["isolated"].get_object().value();
item.isolatedAsset = static_cast<int>(isolatedObj["asset"].get_int64().value());

auto marginArr = obj["marginAvailable"].get_array().value();
auto iter = marginArr.begin();
if (iter != marginArr.end())
{
item.marginAvailable[0] = (*iter).get_double().value();
++iter;
if (iter != marginArr.end())
{
item.marginAvailable[1] = (*iter).get_double().value();
}
}

response.positions.push_back(std::move(item));
}
}
catch (const simdjson::simdjson_error& err)
{
getLogger()->error("RestMessageParser: parse error in liquidatable: {}\n raw: {}", err.what(), message);
}

return response;
}

ClearinghouseState parseClearinghouseStateObj(simdjson::ondemand::object& doc)
{
ClearinghouseState response{};
Expand Down Expand Up @@ -3446,6 +3526,16 @@ namespace hyperliquid
return impl_->parseAllBorrowLendReserveStates(message);
}

UserBorrowLendInterestResponse RestApiMessageParser::parseUserBorrowLendInterest(const std::string& message)
{
return impl_->parseUserBorrowLendInterest(message);
}

LiquidatableResponse RestApiMessageParser::parseLiquidatable(const std::string& message)
{
return impl_->parseLiquidatable(message);
}

PlaceOrderResponse RestApiMessageParser::parsePlaceOrder(const std::string& message)
{
return impl_->parsePlaceOrder(message);
Expand Down
14 changes: 14 additions & 0 deletions src/websocket/WebsocketApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,20 @@ namespace hyperliquid
std::nullopt, std::nullopt, correlationId);
}

void WebsocketApi::userBorrowLendInterest(const std::string& user, uint64_t startTime,
const std::optional<uint64_t>& endTime, std::optional<uint64_t> correlationId)
{
return impl_->signAndSend(RestEndpointType::UserBorrowLendInterest,
InfoRequestBuilder::userBorrowLendInterest(user, startTime, endTime),
std::nullopt, std::nullopt, correlationId);
}

void WebsocketApi::liquidatable(std::optional<uint64_t> correlationId)
{
return impl_->signAndSend(RestEndpointType::Liquidatable, InfoRequestBuilder::liquidatable(),
std::nullopt, std::nullopt, correlationId);
}

void WebsocketApi::placeOrder(const std::vector<OrderRequest>& orders,
Grouping grouping,
const std::optional<Builder>& builder,
Expand Down
Loading
Loading