#[cfg(feature = "sign")]
use crate::account::{LocalKey, recover};
use crate::account::{SignError, Signature};
use crate::client::evm::rlp;
use crate::client::evm::u256::{B256, U256};
use crate::utils::Address;
use crate::utils::sign::keccak256;
use serde::Deserialize;
pub const EIP1559: u8 = 0x02;
pub const GAS_HEADROOM: u64 = 130;
const BASE_FEE_TIMES: u64 = 2;
#[derive(Debug, Clone, PartialEq, Eq, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AccessItem {
pub address: Address,
pub storage_keys: Vec<B256>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Eip1559 {
pub chain_id: u64,
pub nonce: u64,
pub max_priority_fee: U256,
pub max_fee: U256,
pub gas: u64,
pub to: Option<Address>,
pub value: U256,
pub data: Vec<u8>,
pub access_list: Vec<AccessItem>,
}
impl Eip1559 {
fn fields(&self) -> Vec<Vec<u8>> {
let to = match &self.to {
Some(to) => rlp::bytes(to.bytes()),
None => rlp::bytes(&[]),
};
let access: Vec<Vec<u8>> = self
.access_list
.iter()
.map(|item| {
let keys: Vec<Vec<u8>> = item.storage_keys.iter().map(|key| rlp::bytes(&key.0)).collect();
rlp::list(&[rlp::bytes(item.address.bytes()), rlp::list(&keys)])
})
.collect();
vec![
rlp::uint(u128::from(self.chain_id)),
rlp::uint(u128::from(self.nonce)),
rlp::u256(self.max_priority_fee),
rlp::u256(self.max_fee),
rlp::uint(u128::from(self.gas)),
to,
rlp::u256(self.value),
rlp::bytes(&self.data),
rlp::list(&access),
]
}
#[must_use]
pub fn unsigned(&self) -> Vec<u8> {
typed(&self.fields())
}
#[must_use]
pub fn signing_hash(&self) -> B256 {
B256(keccak256(&self.unsigned()))
}
pub fn signed(&self, signature: &Signature) -> Result<Vec<u8>, SignError> {
let parity = match signature.recovery_id() {
Some(parity @ (0 | 1)) => parity,
_ => return Err(SignError::BadV(signature.v())),
};
let mut fields = self.fields();
fields.push(rlp::uint(u128::from(parity)));
fields.push(rlp::word(signature.r()));
fields.push(rlp::word(signature.s()));
Ok(typed(&fields))
}
#[cfg(feature = "sign")]
pub fn sign(&self, key: &LocalKey) -> Result<Vec<u8>, SignError> {
self.signed(&key.sign_digest(&self.signing_hash().0)?)
}
#[cfg(feature = "sign")]
pub fn signer(&self, signature: &Signature) -> Result<Address, SignError> {
recover(&self.signing_hash().0, signature)
}
#[must_use]
pub fn created(&self, from: Address) -> Option<Address> {
self.to.is_none().then(|| create_address(from, self.nonce))
}
#[must_use]
pub fn gas_ceiling(&self) -> U256 {
self.max_fee.saturating_mul(U256::from_u64(self.gas))
}
#[must_use]
pub fn most_spent(&self) -> Option<U256> {
self.max_fee.checked_mul(U256::from_u64(self.gas))?.checked_add(self.value)
}
}
fn typed(fields: &[Vec<u8>]) -> Vec<u8> {
let mut out = vec![EIP1559];
out.extend(rlp::list(fields));
out
}
#[must_use]
pub fn tx_hash(raw: &[u8]) -> B256 {
B256(keccak256(raw))
}
#[must_use]
pub fn create_address(from: Address, nonce: u64) -> Address {
let hash = keccak256(&rlp::list(&[rlp::bytes(from.bytes()), rlp::uint(u128::from(nonce))]));
let mut bytes = [0u8; 20];
bytes.copy_from_slice(&hash[12..]);
Address::from_bytes(bytes)
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Call {
pub to: Option<Address>,
pub value: U256,
pub data: Vec<u8>,
}
impl Call {
#[must_use]
pub fn transaction(self, chain_id: u64, nonce: u64, gas: u64, fees: Fees) -> Eip1559 {
Eip1559 {
chain_id,
nonce,
max_priority_fee: fees.max_priority_fee,
max_fee: fees.max_fee,
gas,
to: self.to,
value: self.value,
data: self.data,
access_list: Vec::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Fees {
pub max_priority_fee: U256,
pub max_fee: U256,
}
impl Fees {
#[must_use]
pub fn over_base(base: U256, tip: U256) -> Fees {
let max_fee = base.saturating_mul(U256::from_u64(BASE_FEE_TIMES)).saturating_add(tip);
Fees { max_priority_fee: tip, max_fee }
}
}
#[must_use]
pub fn with_headroom(gas: u64) -> u64 {
let wide = u128::from(gas) * u128::from(GAS_HEADROOM) / 100;
u64::try_from(wide).unwrap_or(u64::MAX)
}
#[cfg(test)]
#[path = "tests/tx.rs"]
mod tests;