df_hyperliquid_core/transfer/
evm.rs1use crate::client::evm::tx::Call;
8use crate::client::evm::u256::{BadAmount, NATIVE_DECIMALS, U256};
9use crate::transfer::erc20;
10use crate::utils::{Address, Dec};
11
12pub const HYPE_SYSTEM: Address = Address::from_bytes([0x22; 20]);
14
15#[must_use]
18pub fn system_address(index: u64) -> Address {
19 let mut bytes = [0u8; 20];
20 bytes[0] = 0x20;
21 bytes[12..].copy_from_slice(&index.to_be_bytes());
22 Address::from_bytes(bytes)
23}
24
25#[must_use]
28pub fn is_system_address(address: Address) -> bool {
29 address == HYPE_SYSTEM || system_token(address).is_some()
30}
31
32#[must_use]
35pub fn system_token(address: Address) -> Option<u64> {
36 let bytes = address.bytes();
37 if bytes[0] != 0x20 || bytes[1..12].iter().any(|byte| *byte != 0) {
38 return None;
39 }
40 let mut index = [0u8; 8];
41 index.copy_from_slice(&bytes[12..]);
42 Some(u64::from_be_bytes(index))
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub struct Linked {
49 pub index: u64,
50 pub contract: Option<Address>,
52 pub wei_decimals: u32,
54 pub evm_extra_wei_decimals: i32,
56}
57
58impl Linked {
59 #[must_use]
61 pub const fn hype(wei_decimals: u32) -> Linked {
62 Linked { index: 0, contract: None, wei_decimals, evm_extra_wei_decimals: 0 }
63 }
64
65 #[must_use]
67 pub const fn erc20(index: u64, contract: Address, wei_decimals: u32, evm_extra_wei_decimals: i32) -> Linked {
68 Linked { index, contract: Some(contract), wei_decimals, evm_extra_wei_decimals }
69 }
70
71 #[must_use]
74 pub fn system(&self) -> Address {
75 match self.contract {
76 None => HYPE_SYSTEM,
77 Some(_) => system_address(self.index),
78 }
79 }
80
81 #[must_use]
83 pub fn evm_decimals(&self) -> Option<u32> {
84 match self.contract {
85 None => Some(NATIVE_DECIMALS),
86 Some(_) => u32::try_from(i64::from(self.wei_decimals) + i64::from(self.evm_extra_wei_decimals)).ok(),
87 }
88 }
89
90 #[must_use]
93 pub fn core_places(&self) -> Option<u32> {
94 self.evm_decimals().map(|evm| evm.min(self.wei_decimals))
95 }
96
97 pub fn to_core(&self, amount: Dec) -> Result<Call, BadAmount> {
101 let places = self.core_places().ok_or(BadAmount::TooLarge)?;
102 let decimals = self.evm_decimals().ok_or(BadAmount::TooLarge)?;
103 if !amount.is_positive() {
104 return Err(BadAmount::NotPositive(amount));
105 }
106 if amount.places() > places {
107 return Err(BadAmount::TooFine { value: amount, decimals: places });
108 }
109 let units = U256::from_dec(amount, decimals)?;
110 Ok(match self.contract {
111 None => Call { to: Some(HYPE_SYSTEM), value: units, data: Vec::new() },
112 Some(contract) => {
113 Call { to: Some(contract), value: U256::ZERO, data: erc20::transfer(self.system(), units) }
114 }
115 })
116 }
117}
118
119#[cfg(test)]
120#[path = "tests/evm.rs"]
121mod tests;