use df_hyperliquid_core::client::evm::abi::Token;
use crate::client::read::{one, one_word};
use crate::client::rpc::RpcError;
use crate::client::{Arg, Call, Event, Method, Read, ReadError};
use crate::nitro::table::ARB_RETRYABLE_TX;
use crate::utils::{Address, keccak256};
use crate::{B256, U256};
pub const REDEEM: Method = Method::new("redeem(bytes32)", "bytes32");
pub const GET_LIFETIME: Method = Method::new("getLifetime()", "uint256");
pub const GET_TIMEOUT: Method = Method::new("getTimeout(bytes32)", "uint256");
pub const KEEPALIVE: Method = Method::new("keepalive(bytes32)", "uint256");
pub const GET_BENEFICIARY: Method = Method::new("getBeneficiary(bytes32)", "address");
pub const CANCEL: Method = Method::new("cancel(bytes32)", "");
pub const GET_CURRENT_REDEEMER: Method = Method::new("getCurrentRedeemer()", "address");
pub const METHODS: &[Method] =
&[REDEEM, GET_LIFETIME, GET_TIMEOUT, KEEPALIVE, GET_BENEFICIARY, CANCEL, GET_CURRENT_REDEEMER];
pub const TICKET_CREATED: Event = Event::new("TicketCreated(bytes32)", "bytes32 indexed ticketId");
pub const LIFETIME_EXTENDED: Event =
Event::new("LifetimeExtended(bytes32,uint256)", "bytes32 indexed ticketId, uint256 newTimeout");
pub const REDEEM_SCHEDULED: Event = Event::new(
"RedeemScheduled(bytes32,bytes32,uint64,uint64,address,uint256,uint256)",
"bytes32 indexed ticketId, bytes32 indexed retryTxHash, uint64 indexed sequenceNum, uint64 donatedGas, \
address gasDonor, uint256 maxRefund, uint256 submissionFeeRefund",
);
pub const CANCELED: Event = Event::new("Canceled(bytes32)", "bytes32 indexed ticketId");
pub const NO_TICKET_WITH_ID: &str = "NoTicketWithID()";
#[must_use]
pub fn lifetime() -> Read<U256> {
Read::new(ARB_RETRYABLE_TX, &GET_LIFETIME, &[], |answer| one(&GET_LIFETIME, answer, Token::as_u256))
}
#[must_use]
pub fn timeout(ticket: B256) -> Read<U256> {
Read::new(ARB_RETRYABLE_TX, &GET_TIMEOUT, &[Arg::Word(ticket)], |answer| one(&GET_TIMEOUT, answer, Token::as_u256))
}
#[must_use]
pub fn beneficiary(ticket: B256) -> Read<Address> {
Read::new(ARB_RETRYABLE_TX, &GET_BENEFICIARY, &[Arg::Word(ticket)], |answer| {
one(&GET_BENEFICIARY, answer, Token::as_address)
})
}
#[must_use]
pub fn current_redeemer() -> Read<Address> {
Read::new(ARB_RETRYABLE_TX, &GET_CURRENT_REDEEMER, &[], |answer| {
one(&GET_CURRENT_REDEEMER, answer, Token::as_address)
})
}
#[must_use]
pub fn redeem(ticket: B256) -> Call {
Call { to: Some(ARB_RETRYABLE_TX), value: U256::ZERO, data: REDEEM.calldata(&[Arg::Word(ticket)]) }
}
#[must_use]
pub fn keepalive(ticket: B256) -> Call {
Call { to: Some(ARB_RETRYABLE_TX), value: U256::ZERO, data: KEEPALIVE.calldata(&[Arg::Word(ticket)]) }
}
#[must_use]
pub fn cancel(ticket: B256) -> Call {
Call { to: Some(ARB_RETRYABLE_TX), value: U256::ZERO, data: CANCEL.calldata(&[Arg::Word(ticket)]) }
}
#[must_use]
pub fn no_ticket_selector() -> [u8; 4] {
let hash = keccak256(NO_TICKET_WITH_ID.as_bytes());
[hash[0], hash[1], hash[2], hash[3]]
}
#[must_use]
pub fn no_ticket(error: &ReadError) -> bool {
let ReadError::Rpc(RpcError::Node { error, .. }) = error else { return false };
let data = error.data.as_deref().and_then(|data| crate::utils::hex::decode(data).ok());
data.as_deref().and_then(|data| data.get(..4)) == Some(&no_ticket_selector()[..])
}
#[must_use]
pub fn redeem_hash(answer: &[u8]) -> Option<B256> {
one_word(&REDEEM, answer).ok()
}
#[cfg(test)]
#[path = "tests/retryable.rs"]
mod tests;