df_elysium_core/client/
read.rs1use df_hyperliquid_core::client::Http;
6use df_hyperliquid_core::client::evm::abi::{AbiError, Token};
7
8use crate::client::method::{Arg, Method};
9use crate::client::rpc::{BlockTag, CallRequest, Rpc, RpcError};
10use crate::utils::Address;
11use crate::{B256, U256};
12
13#[derive(Debug, Clone)]
15pub struct Read<T> {
16 pub call: CallRequest,
17 decode: fn(&[u8]) -> Result<T, AbiError>,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
21pub enum ReadError {
22 #[error(transparent)]
23 Rpc(#[from] RpcError),
24 #[error("the answer does not fit what the contract declares: {0}")]
25 Abi(#[from] AbiError),
26}
27
28impl ReadError {
29 #[must_use]
31 pub fn is_lagging(&self) -> bool {
32 matches!(self, ReadError::Rpc(error) if crate::client::rpc::lagging(error))
33 }
34}
35
36impl<T> Read<T> {
37 #[must_use]
39 pub fn new(at: Address, method: &Method, args: &[Arg<'_>], decode: fn(&[u8]) -> Result<T, AbiError>) -> Read<T> {
40 Read { call: CallRequest::to(at, method.calldata(args)), decode }
41 }
42
43 #[must_use]
45 pub fn from(mut self, from: Address) -> Read<T> {
46 self.call.from = Some(from);
47 self
48 }
49
50 #[must_use]
52 pub fn with_value(mut self, value: U256) -> Read<T> {
53 self.call.value = Some(value).filter(|value| !value.is_zero());
54 self
55 }
56
57 pub fn decode(&self, answer: &[u8]) -> Result<T, AbiError> {
59 (self.decode)(answer)
60 }
61
62 pub async fn on<H: Http>(&self, rpc: &Rpc<H>, block: BlockTag) -> Result<T, ReadError> {
64 let answer = rpc.call(&self.call, block).await?;
65 Ok(self.decode(&answer)?)
66 }
67}
68
69pub(crate) struct Fields<'a> {
71 name: &'static str,
72 items: std::slice::Iter<'a, Token>,
73}
74
75impl<'a> Fields<'a> {
76 pub(crate) fn of(name: &'static str, tokens: &'a [Token]) -> Fields<'a> {
78 Fields { name, items: tokens.iter() }
79 }
80
81 pub(crate) fn tuple(name: &'static str, tokens: &'a [Token]) -> Result<Fields<'a>, AbiError> {
83 match tokens {
84 [Token::Tuple(items)] => Ok(Fields { name, items: items.iter() }),
85 _ => Err(AbiError::Type(format!("{name}: not one struct"))),
86 }
87 }
88
89 fn take<T>(&mut self, read: impl FnOnce(&'a Token) -> Option<T>) -> Result<T, AbiError> {
90 self.items
91 .next()
92 .and_then(read)
93 .ok_or_else(|| AbiError::Type(format!("{}: a field of another type", self.name)))
94 }
95
96 pub(crate) fn uint(&mut self) -> Result<U256, AbiError> {
97 self.take(Token::as_u256)
98 }
99
100 pub(crate) fn u64(&mut self) -> Result<u64, AbiError> {
101 self.take(Token::as_u64)
102 }
103
104 pub(crate) fn u16(&mut self) -> Result<u16, AbiError> {
105 self.take(Token::as_u16)
106 }
107
108 pub(crate) fn u8(&mut self) -> Result<u8, AbiError> {
109 self.take(Token::as_u8)
110 }
111
112 pub(crate) fn bool(&mut self) -> Result<bool, AbiError> {
113 self.take(Token::as_bool)
114 }
115
116 pub(crate) fn address(&mut self) -> Result<Address, AbiError> {
117 self.take(Token::as_address)
118 }
119
120 pub(crate) fn word(&mut self) -> Result<B256, AbiError> {
121 self.take(|token| <[u8; 32]>::try_from(token.as_bytes()?).ok().map(B256))
122 }
123
124 pub(crate) fn bytes(&mut self) -> Result<Vec<u8>, AbiError> {
125 self.take(|token| token.as_bytes().map(<[u8]>::to_vec))
126 }
127
128 pub(crate) fn items(&mut self) -> Result<&'a [Token], AbiError> {
129 self.take(Token::as_items)
130 }
131
132 pub(crate) fn addresses(&mut self) -> Result<Vec<Address>, AbiError> {
133 let name = self.name;
134 self.items()?
135 .iter()
136 .map(|item| item.as_address().ok_or_else(|| AbiError::Type(format!("{name}: not a list of addresses"))))
137 .collect()
138 }
139
140 pub(crate) fn words(&mut self) -> Result<Vec<B256>, AbiError> {
141 let name = self.name;
142 self.items()?
143 .iter()
144 .map(|item| {
145 item.as_bytes()
146 .and_then(|bytes| <[u8; 32]>::try_from(bytes).ok())
147 .map(B256)
148 .ok_or_else(|| AbiError::Type(format!("{name}: not a list of words")))
149 })
150 .collect()
151 }
152}
153
154pub(crate) fn one<T>(method: &Method, answer: &[u8], read: impl FnOnce(&Token) -> Option<T>) -> Result<T, AbiError> {
156 let tokens = method.answer(answer)?;
157 match tokens.as_slice() {
158 [token] => read(token).ok_or_else(|| AbiError::Type(format!("{}: another type", method.signature))),
159 _ => Err(AbiError::Type(format!("{}: not one value", method.signature))),
160 }
161}
162
163pub(crate) fn one_word(method: &Method, answer: &[u8]) -> Result<B256, AbiError> {
165 one(method, answer, |token| token.as_bytes().and_then(|bytes| <[u8; 32]>::try_from(bytes).ok()).map(B256))
166}
167
168#[cfg(test)]
169#[path = "tests/read.rs"]
170mod tests;