use crate::client::evm::tx::Call;
use crate::client::evm::u256::{BadAmount, NATIVE_DECIMALS, U256};
use crate::transfer::erc20;
use crate::utils::{Address, Dec};
pub const HYPE_SYSTEM: Address = Address::from_bytes([0x22; 20]);
#[must_use]
pub fn system_address(index: u64) -> Address {
let mut bytes = [0u8; 20];
bytes[0] = 0x20;
bytes[12..].copy_from_slice(&index.to_be_bytes());
Address::from_bytes(bytes)
}
#[must_use]
pub fn is_system_address(address: Address) -> bool {
address == HYPE_SYSTEM || system_token(address).is_some()
}
#[must_use]
pub fn system_token(address: Address) -> Option<u64> {
let bytes = address.bytes();
if bytes[0] != 0x20 || bytes[1..12].iter().any(|byte| *byte != 0) {
return None;
}
let mut index = [0u8; 8];
index.copy_from_slice(&bytes[12..]);
Some(u64::from_be_bytes(index))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Linked {
pub index: u64,
pub contract: Option<Address>,
pub wei_decimals: u32,
pub evm_extra_wei_decimals: i32,
}
impl Linked {
#[must_use]
pub const fn hype(wei_decimals: u32) -> Linked {
Linked { index: 0, contract: None, wei_decimals, evm_extra_wei_decimals: 0 }
}
#[must_use]
pub const fn erc20(index: u64, contract: Address, wei_decimals: u32, evm_extra_wei_decimals: i32) -> Linked {
Linked { index, contract: Some(contract), wei_decimals, evm_extra_wei_decimals }
}
#[must_use]
pub fn system(&self) -> Address {
match self.contract {
None => HYPE_SYSTEM,
Some(_) => system_address(self.index),
}
}
#[must_use]
pub fn evm_decimals(&self) -> Option<u32> {
match self.contract {
None => Some(NATIVE_DECIMALS),
Some(_) => u32::try_from(i64::from(self.wei_decimals) + i64::from(self.evm_extra_wei_decimals)).ok(),
}
}
#[must_use]
pub fn core_places(&self) -> Option<u32> {
self.evm_decimals().map(|evm| evm.min(self.wei_decimals))
}
pub fn to_core(&self, amount: Dec) -> Result<Call, BadAmount> {
let places = self.core_places().ok_or(BadAmount::TooLarge)?;
let decimals = self.evm_decimals().ok_or(BadAmount::TooLarge)?;
if !amount.is_positive() {
return Err(BadAmount::NotPositive(amount));
}
if amount.places() > places {
return Err(BadAmount::TooFine { value: amount, decimals: places });
}
let units = U256::from_dec(amount, decimals)?;
Ok(match self.contract {
None => Call { to: Some(HYPE_SYSTEM), value: units, data: Vec::new() },
Some(contract) => {
Call { to: Some(contract), value: U256::ZERO, data: erc20::transfer(self.system(), units) }
}
})
}
}
#[cfg(test)]
#[path = "tests/evm.rs"]
mod tests;