use alloy_primitives::{Address, Bytes, B256, U256, keccak256};
use alloy_rlp::Encodable;
pub const TX_TYPE_USD_MULTI_TOKEN: u8 = 0x7a;
pub const FEE_PAYER_SIGNATURE_MAGIC_BYTE: u8 = 0x7b;
#[derive(Debug, Clone)]
pub struct TxBuilder {
pub chain_id: u64,
pub nonce: u64,
pub gas_limit: u64,
pub to: Option<Address>,
pub value: U256,
pub data: Bytes,
pub fee_token: Address,
pub fee_payer: Option<Address>,
pub max_fee_per_gas_usd: Option<u128>,
pub fee_payer_signature: Option<Bytes>,
}
impl Default for TxBuilder {
fn default() -> Self {
Self::new()
}
}
impl TxBuilder {
pub fn new() -> Self {
Self {
chain_id: 0,
nonce: 0,
gas_limit: 21000,
to: None,
value: U256::ZERO,
data: Bytes::new(),
fee_token: Address::ZERO,
fee_payer: None,
max_fee_per_gas_usd: None,
fee_payer_signature: None,
}
}
pub fn chain_id(mut self, chain_id: u64) -> Self {
self.chain_id = chain_id;
self
}
pub fn nonce(mut self, nonce: u64) -> Self {
self.nonce = nonce;
self
}
pub fn gas_limit(mut self, gas_limit: u64) -> Self {
self.gas_limit = gas_limit;
self
}
pub fn to(mut self, to: Option<Address>) -> Self {
self.to = to;
self
}
pub fn value(mut self, value: U256) -> Self {
self.value = value;
self
}
pub fn data(mut self, data: Bytes) -> Self {
self.data = data;
self
}
pub fn fee_token(mut self, fee_token: Address) -> Self {
self.fee_token = fee_token;
self
}
pub fn fee_payer(mut self, fee_payer: Option<Address>) -> Self {
self.fee_payer = fee_payer;
self
}
pub fn max_fee_per_gas_usd(mut self, max_fee_per_gas_usd: u128) -> Self {
self.max_fee_per_gas_usd = Some(max_fee_per_gas_usd);
self
}
pub fn fee_payer_signature(mut self, signature: Bytes) -> Self {
self.fee_payer_signature = Some(signature);
self
}
pub fn erc20_transfer(mut self, token: Address, to: Address, amount: U256) -> Self {
let mut data = vec![0xa9, 0x05, 0x9c, 0xbb];
let mut recipient = [0u8; 32];
recipient[12..].copy_from_slice(to.as_slice());
data.extend_from_slice(&recipient);
let mut amount_padded = [0u8; 32];
let amount_bytes: [u8; 32] = amount.to_le_bytes();
amount_padded.copy_from_slice(&amount_bytes);
data.extend_from_slice(&amount_padded);
self.to = Some(token);
self.data = Bytes::from(data);
self
}
pub fn build(&self) -> TxFields {
TxFields {
chain_id: self.chain_id,
nonce: self.nonce,
gas_limit: self.gas_limit,
to: self.to,
value: self.value,
data: self.data.clone(),
fee_token: self.fee_token,
fee_payer: self.fee_payer.unwrap_or(Address::ZERO),
max_fee_per_gas_usd: self.max_fee_per_gas_usd.unwrap_or(0),
}
}
}
#[derive(Debug, Clone)]
pub struct TxFields {
pub chain_id: u64,
pub nonce: u64,
pub gas_limit: u64,
pub to: Option<Address>,
pub value: U256,
pub data: Bytes,
pub fee_token: Address,
pub fee_payer: Address,
pub max_fee_per_gas_usd: u128,
}
pub fn fee_payer_signature_hash(
chain_id: u64,
nonce: u64,
gas_limit: u64,
fee_token: Address,
fee_payer: Address,
max_fee_per_gas_usd: u128,
sender: Address,
) -> B256 {
let mut buf = Vec::new();
buf.push(FEE_PAYER_SIGNATURE_MAGIC_BYTE);
chain_id.encode(&mut buf);
nonce.encode(&mut buf);
gas_limit.encode(&mut buf);
fee_token.encode(&mut buf);
fee_payer.encode(&mut buf);
max_fee_per_gas_usd.encode(&mut buf);
sender.encode(&mut buf);
keccak256(&buf)
}
pub fn create_transaction() -> TxBuilder {
TxBuilder::new()
}