df_hyperliquid_core/transfer/
erc20.rs1use crate::client::evm::abi::{self, AbiError, Token};
6use crate::client::evm::u256::{B256, U256};
7use crate::utils::Address;
8
9pub const TRANSFER: [u8; 4] = [0xa9, 0x05, 0x9c, 0xbb];
11pub const BALANCE_OF: [u8; 4] = [0x70, 0xa0, 0x82, 0x31];
13pub const APPROVE: [u8; 4] = [0x09, 0x5e, 0xa7, 0xb3];
15pub const DECIMALS: [u8; 4] = [0x31, 0x3c, 0xe5, 0x67];
17pub const SYMBOL: [u8; 4] = [0x95, 0xd8, 0x9b, 0x41];
19
20fn call(selector: [u8; 4], words: &[[u8; 32]]) -> Vec<u8> {
21 let mut out = selector.to_vec();
22 for word in words {
23 out.extend_from_slice(word);
24 }
25 out
26}
27
28#[must_use]
30pub fn transfer(to: Address, amount: U256) -> Vec<u8> {
31 call(TRANSFER, &[B256::from_address(to).0, amount.to_be_bytes()])
32}
33
34#[must_use]
36pub fn balance_of(owner: Address) -> Vec<u8> {
37 call(BALANCE_OF, &[B256::from_address(owner).0])
38}
39
40#[must_use]
42pub fn approve(spender: Address, amount: U256) -> Vec<u8> {
43 call(APPROVE, &[B256::from_address(spender).0, amount.to_be_bytes()])
44}
45
46#[must_use]
48pub fn decimals() -> Vec<u8> {
49 DECIMALS.to_vec()
50}
51
52#[must_use]
54pub fn symbol() -> Vec<u8> {
55 SYMBOL.to_vec()
56}
57
58fn one(shape: &str, answer: &[u8]) -> Result<Token, AbiError> {
59 abi::decode(&abi::params(shape)?, answer)?.into_iter().next().ok_or(AbiError::Short(answer.len()))
60}
61
62pub fn read_amount(answer: &[u8]) -> Result<U256, AbiError> {
64 one("uint256", answer)?.as_u256().ok_or(AbiError::Short(answer.len()))
65}
66
67pub fn read_decimals(answer: &[u8]) -> Result<u8, AbiError> {
69 one("uint8", answer)?.as_u8().ok_or(AbiError::Short(answer.len()))
70}
71
72pub fn read_symbol(answer: &[u8]) -> Result<String, AbiError> {
74 match one("string", answer)? {
75 Token::String(text) => Ok(text),
76 _ => Err(AbiError::Short(answer.len())),
77 }
78}
79
80#[cfg(test)]
81#[path = "tests/erc20.rs"]
82mod tests;