use df_hyperliquid_core::client::evm::abi::{AbiError, Token};
use crate::client::read::{Fields, one};
use crate::client::{Arg, Call, Method, Read};
use crate::gas::Estimate;
use crate::nitro::table::NODE_INTERFACE;
use crate::utils::Address;
use crate::{B256, U256};
pub const GAS_ESTIMATE_COMPONENTS: Method = Method::new(
"gasEstimateComponents(address,bool,bytes)",
"uint64 gasEstimate, uint64 gasEstimateForL1, uint256 baseFee, uint256 l1BaseFeeEstimate",
);
pub const GAS_ESTIMATE_L1_COMPONENT: Method = Method::new(
"gasEstimateL1Component(address,bool,bytes)",
"uint64 gasEstimateForL1, uint256 baseFee, uint256 l1BaseFeeEstimate",
);
pub const CONSTRUCT_OUTBOX_PROOF: Method =
Method::new("constructOutboxProof(uint64,uint64)", "bytes32 send, bytes32 root, bytes32[] proof");
pub const FIND_BATCH_CONTAINING_BLOCK: Method = Method::new("findBatchContainingBlock(uint64)", "uint64 batch");
pub const GET_L1_CONFIRMATIONS: Method = Method::new("getL1Confirmations(bytes32)", "uint64 confirmations");
pub const ESTIMATE_RETRYABLE_TICKET: Method =
Method::new("estimateRetryableTicket(address,uint256,address,uint256,address,address,bytes)", "");
pub const NITRO_GENESIS_BLOCK: Method = Method::new("nitroGenesisBlock()", "uint256 number");
pub const BLOCK_L1_NUM: Method = Method::new("blockL1Num(uint64)", "uint64 l1BlockNum");
pub const L2_BLOCK_RANGE_FOR_L1: Method =
Method::new("l2BlockRangeForL1(uint64)", "uint64 firstBlock, uint64 lastBlock");
pub const METHODS: &[Method] = &[
GAS_ESTIMATE_COMPONENTS,
GAS_ESTIMATE_L1_COMPONENT,
CONSTRUCT_OUTBOX_PROOF,
FIND_BATCH_CONTAINING_BLOCK,
GET_L1_CONFIRMATIONS,
ESTIMATE_RETRYABLE_TICKET,
NITRO_GENESIS_BLOCK,
BLOCK_L1_NUM,
L2_BLOCK_RANGE_FOR_L1,
];
#[must_use]
pub fn gas_estimate_components(from: Option<Address>, call: &Call) -> Read<Estimate> {
let to = call.to.unwrap_or(Address::ZERO);
let args = [Arg::Address(to), Arg::Bool(call.to.is_none()), Arg::Bytes(&call.data)];
let read = Read::new(NODE_INTERFACE, &GAS_ESTIMATE_COMPONENTS, &args, |answer| {
let tokens = GAS_ESTIMATE_COMPONENTS.answer(answer)?;
let mut fields = Fields::of("gasEstimateComponents", &tokens);
Ok(Estimate {
gas: fields.u64()?,
gas_for_l1: fields.u64()?,
base_fee: fields.uint()?,
l1_base_fee: fields.uint()?,
})
})
.with_value(call.value);
match from {
Some(from) => read.from(from),
None => read,
}
}
#[must_use]
pub fn gas_estimate_l1_component(call: &Call) -> Read<(u64, U256, U256)> {
let to = call.to.unwrap_or(Address::ZERO);
let args = [Arg::Address(to), Arg::Bool(call.to.is_none()), Arg::Bytes(&call.data)];
Read::new(NODE_INTERFACE, &GAS_ESTIMATE_L1_COMPONENT, &args, |answer| {
let tokens = GAS_ESTIMATE_L1_COMPONENT.answer(answer)?;
let mut fields = Fields::of("gasEstimateL1Component", &tokens);
Ok((fields.u64()?, fields.uint()?, fields.uint()?))
})
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OutboxProof {
pub send: B256,
pub root: B256,
pub proof: Vec<B256>,
}
#[must_use]
pub fn construct_outbox_proof(size: u64, leaf: u64) -> Read<OutboxProof> {
Read::new(NODE_INTERFACE, &CONSTRUCT_OUTBOX_PROOF, &[Arg::u64(size), Arg::u64(leaf)], |answer| {
let tokens = CONSTRUCT_OUTBOX_PROOF.answer(answer)?;
let mut fields = Fields::of("constructOutboxProof", &tokens);
Ok(OutboxProof { send: fields.word()?, root: fields.word()?, proof: fields.words()? })
})
}
#[must_use]
pub fn find_batch_containing_block(number: u64) -> Read<u64> {
Read::new(NODE_INTERFACE, &FIND_BATCH_CONTAINING_BLOCK, &[Arg::u64(number)], |answer| {
one(&FIND_BATCH_CONTAINING_BLOCK, answer, Token::as_u64)
})
}
#[must_use]
pub fn l1_confirmations(block_hash: B256) -> Read<u64> {
Read::new(NODE_INTERFACE, &GET_L1_CONFIRMATIONS, &[Arg::Word(block_hash)], |answer| {
one(&GET_L1_CONFIRMATIONS, answer, Token::as_u64)
})
}
#[must_use]
pub fn nitro_genesis_block() -> Read<U256> {
Read::new(NODE_INTERFACE, &NITRO_GENESIS_BLOCK, &[], |answer| one(&NITRO_GENESIS_BLOCK, answer, Token::as_u256))
}
#[must_use]
pub fn block_l1_num(number: u64) -> Read<u64> {
Read::new(NODE_INTERFACE, &BLOCK_L1_NUM, &[Arg::u64(number)], |answer| one(&BLOCK_L1_NUM, answer, Token::as_u64))
}
#[must_use]
pub fn l2_block_range_for_l1(l1_block: u64) -> Read<(u64, u64)> {
Read::new(NODE_INTERFACE, &L2_BLOCK_RANGE_FOR_L1, &[Arg::u64(l1_block)], |answer| {
let tokens = L2_BLOCK_RANGE_FOR_L1.answer(answer)?;
let mut fields = Fields::of("l2BlockRangeForL1", &tokens);
Ok((fields.u64()?, fields.u64()?))
})
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RetryableEstimate {
pub sender: Address,
pub deposit: U256,
pub to: Address,
pub l2_call_value: U256,
pub excess_fee_refund_address: Address,
pub call_value_refund_address: Address,
pub data: Vec<u8>,
}
#[must_use]
pub fn estimate_retryable_ticket(ticket: &RetryableEstimate) -> Call {
let data = ESTIMATE_RETRYABLE_TICKET.calldata(&[
Arg::Address(ticket.sender),
Arg::Uint(ticket.deposit),
Arg::Address(ticket.to),
Arg::Uint(ticket.l2_call_value),
Arg::Address(ticket.excess_fee_refund_address),
Arg::Address(ticket.call_value_refund_address),
Arg::Bytes(&ticket.data),
]);
Call { to: Some(NODE_INTERFACE), value: U256::ZERO, data }
}
pub fn read_outbox_proof(answer: &[u8]) -> Result<OutboxProof, AbiError> {
construct_outbox_proof(0, 0).decode(answer)
}
#[cfg(test)]
#[path = "tests/node.rs"]
mod tests;