Skip to main content

df_hyperliquid_core/client/evm/precompiles/
assets.rs

1//! Market info: a perp's asset info, a spot pair, a token and its EVM contract,
2//! and a token's supply.
3
4use crate::client::evm::abi::AbiError;
5use crate::client::evm::precompiles::fields::{Fields, tokens};
6use crate::client::evm::precompiles::read::{Arg, Read, read};
7use crate::utils::Address;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct PerpAssetInfo {
11    pub coin: String,
12    pub margin_table_id: u32,
13    pub sz_decimals: u8,
14    pub max_leverage: u8,
15    pub only_isolated: bool,
16}
17
18/// `0x80a`: a perp's name, size decimals and leverage cap.
19#[must_use]
20pub fn perp_asset_info(perp: u32) -> Read<PerpAssetInfo> {
21    read(0x80a, &[Arg::Uint(u64::from(perp))], |answer| {
22        let tokens = tokens(0x80a, answer)?;
23        let mut fields = Fields::of("perpAssetInfo", &tokens)?;
24        Ok(PerpAssetInfo {
25            coin: fields.string()?,
26            margin_table_id: fields.u32()?,
27            sz_decimals: fields.u8()?,
28            max_leverage: fields.u8()?,
29            only_isolated: fields.bool()?,
30        })
31    })
32}
33
34#[derive(Debug, Clone, PartialEq, Eq)]
35pub struct SpotInfo {
36    pub name: String,
37    /// The base token's index, then the quote's.
38    pub tokens: [u64; 2],
39}
40
41/// `0x80b`: a spot pair and the two tokens it is between.
42#[must_use]
43pub fn spot_info(spot: u32) -> Read<SpotInfo> {
44    read(0x80b, &[Arg::Uint(u64::from(spot))], |answer| {
45        let tokens = tokens(0x80b, answer)?;
46        let mut fields = Fields::of("spotInfo", &tokens)?;
47        let name = fields.string()?;
48        let pair: [u64; 2] =
49            fields.u64s()?.try_into().map_err(|_| AbiError::Type("spotInfo: not two tokens".to_owned()))?;
50        Ok(SpotInfo { name, tokens: pair })
51    })
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
55pub struct TokenInfo {
56    pub name: String,
57    /// The spot pairs it trades in.
58    pub spots: Vec<u64>,
59    pub deployer_trading_fee_share: u64,
60    pub deployer: Address,
61    /// The linked ERC-20 on HyperEVM; zero when it has none.
62    pub evm_contract: Address,
63    pub sz_decimals: u8,
64    pub wei_decimals: u8,
65    /// How many more decimals the ERC-20 counts in than HyperCore does.
66    pub evm_extra_wei_decimals: i8,
67}
68
69/// `0x80c`: everything about a token, its EVM contract included.
70#[must_use]
71pub fn token_info(token: u32) -> Read<TokenInfo> {
72    read(0x80c, &[Arg::Uint(u64::from(token))], |answer| {
73        let tokens = tokens(0x80c, answer)?;
74        let mut fields = Fields::of("tokenInfo", &tokens)?;
75        Ok(TokenInfo {
76            name: fields.string()?,
77            spots: fields.u64s()?,
78            deployer_trading_fee_share: fields.u64()?,
79            deployer: fields.address()?,
80            evm_contract: fields.address()?,
81            sz_decimals: fields.u8()?,
82            wei_decimals: fields.u8()?,
83            evm_extra_wei_decimals: fields.i8()?,
84        })
85    })
86}
87
88/// An account holding non-circulating supply.
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub struct UserBalance {
91    pub user: Address,
92    pub balance: u64,
93}
94
95#[derive(Debug, Clone, PartialEq, Eq)]
96pub struct TokenSupply {
97    pub max_supply: u64,
98    pub total_supply: u64,
99    pub circulating_supply: u64,
100    pub future_emissions: u64,
101    pub non_circulating_user_balances: Vec<UserBalance>,
102}
103
104/// `0x80d`: a token's supply, and who holds the part that does not circulate.
105#[must_use]
106pub fn token_supply(token: u32) -> Read<TokenSupply> {
107    read(0x80d, &[Arg::Uint(u64::from(token))], |answer| {
108        let tokens = tokens(0x80d, answer)?;
109        let mut fields = Fields::of("tokenSupply", &tokens)?;
110        let (max_supply, total_supply, circulating_supply, future_emissions) =
111            (fields.u64()?, fields.u64()?, fields.u64()?, fields.u64()?);
112        let holders = fields
113            .items()?
114            .iter()
115            .map(|item| {
116                let mut holder = Fields::of("tokenSupply", std::slice::from_ref(item))?;
117                Ok(UserBalance { user: holder.address()?, balance: holder.u64()? })
118            })
119            .collect::<Result<_, AbiError>>()?;
120        Ok(TokenSupply {
121            max_supply,
122            total_supply,
123            circulating_supply,
124            future_emissions,
125            non_circulating_user_balances: holders,
126        })
127    })
128}
129
130#[cfg(test)]
131#[path = "tests/assets.rs"]
132mod tests;