use df_hyperliquid_core::client::Http;
use df_hyperliquid_core::client::evm::{AbiError, BlockTag, CallRequest, Function, Rpc, RpcError, Token};
use df_hyperliquid_core::utils::Address;
use crate::client::deployment::{DEPOSIT_PROXY_FACTORY, EXCHANGE, PAPER_STAKING};
pub struct Read<T> {
pub to: Address,
pub data: Vec<u8>,
decode: fn(&[Token]) -> Option<T>,
function: Function,
}
impl<T> std::fmt::Debug for Read<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Read").field("to", &self.to).field("function", &self.function.signature()).finish()
}
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum ReadError {
#[error(transparent)]
Rpc(#[from] RpcError),
#[error("the answer does not fit the function's outputs: {0}")]
Abi(#[from] AbiError),
#[error("the answer is not the value the function returns")]
Shape,
}
impl<T> Read<T> {
fn new(to: Address, signature: &str, args: &[Token], decode: fn(&[Token]) -> Option<T>) -> Result<Self, AbiError> {
let function = Function::parse(signature)?;
Ok(Read { to, data: function.encode(args)?, decode, function })
}
#[must_use]
pub fn request(&self) -> CallRequest {
CallRequest::to(self.to, self.data.clone())
}
pub fn decode(&self, answer: &[u8]) -> Result<T, ReadError> {
let tokens = self.function.decode_output(answer)?;
(self.decode)(&tokens).ok_or(ReadError::Shape)
}
pub async fn run<H: Http>(&self, rpc: &Rpc<H>) -> Result<T, ReadError> {
let answer = rpc.call(&self.request(), BlockTag::Latest).await?;
self.decode(&answer)
}
}
pub fn deposit_address(wallet: Address) -> Result<Read<Address>, AbiError> {
Read::new(
DEPOSIT_PROXY_FACTORY,
"computeProxyAddress(address user) view returns (address)",
&[Token::address(wallet)],
|tokens| tokens.first()?.as_address(),
)
}
pub fn win_fee_rate() -> Result<Read<u128>, AbiError> {
Read::new(EXCHANGE, "winFeeRate() view returns (uint256)", &[], |tokens| tokens.first()?.as_u128())
}
pub fn pending_reward(wallet: Address) -> Result<Read<u128>, AbiError> {
Read::new(
PAPER_STAKING,
"pendingReward(address user) view returns (uint256)",
&[Token::address(wallet)],
|tokens| tokens.first()?.as_u128(),
)
}
#[cfg(test)]
#[path = "tests/chain.rs"]
mod tests;