altius-tx-sdk 0.1.6

SDK for signing and sending Altius USD multi-token transactions
Documentation
//! Transaction building for Altius USD multi-token transactions.
//!
//! Supports the 0x7a transaction type.

use alloy_primitives::{Address, Bytes, B256, U256, keccak256};
use alloy_rlp::Encodable;

/// Transaction type for USD Multi-Token (EIP-2718)
pub const TX_TYPE_USD_MULTI_TOKEN: u8 = 0x7a;

/// Magic byte for fee payer signature
pub const FEE_PAYER_SIGNATURE_MAGIC_BYTE: u8 = 0x7b;

/// TxBuilder for building USD Multi-Token transactions
#[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
    }

    /// Build ERC20 transfer transaction
    pub fn erc20_transfer(mut self, token: Address, to: Address, amount: U256) -> Self {
        // ERC20 transfer selector: 0xa9059cbb
        let mut data = vec![0xa9, 0x05, 0x9c, 0xbb];
        // Add recipient (padded to 32 bytes)
        let mut recipient = [0u8; 32];
        recipient[12..].copy_from_slice(to.as_slice());
        data.extend_from_slice(&recipient);
        // Add amount (padded to 32 bytes)
        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
    }

    /// Build the transaction fields for signing
    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),
        }
    }
}

/// Transaction fields for encoding
#[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,
}

/// Compute fee payer signature hash
///
/// This creates the message that the fee payer signs to authorize
/// the transaction fees to be paid from their account.
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)
}

/// Create a new transaction (convenience function)
pub fn create_transaction() -> TxBuilder {
    TxBuilder::new()
}