pub mod evm;
pub mod http;
#[cfg(feature = "native")]
pub mod native;
pub mod network;
#[cfg(feature = "web")]
pub mod web;
use crate::account::SignError;
#[cfg(feature = "sign")]
use crate::account::Signer;
use crate::client::http::Api;
#[cfg(feature = "sign")]
use crate::utils::sign::{Domain, L1Action, UserAction};
use crate::utils::{Address, NonceClock};
use std::sync::{Mutex, PoisonError};
pub use http::{
Accepted, ApiError, ExchangeResponse, Http, Method, Request, Response, Spent, Status, Transport, TransportError,
exchange, info, info_request, user_request,
};
pub use network::{
L1_CHAIN_ID, MAINNET_HYPE, MAINNET_USDC, Network, TESTNET_HYPE, TESTNET_USDC, Token, USDC_INDEX, UnknownNetwork,
};
pub type Clock = fn() -> u64;
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum SendError {
#[error(transparent)]
Sign(#[from] SignError),
#[error(transparent)]
Api(#[from] ApiError),
}
pub struct Client<H, S = ()> {
api: Api<H>,
signer: S,
vault: Option<Address>,
clock: Clock,
nonces: Mutex<NonceClock>,
}
impl<H: Http> Client<H> {
pub fn reader(http: H, network: Network, clock: Clock) -> Self {
Client::new(http, network, clock, ())
}
}
impl<H: Http, S> Client<H, S> {
pub fn new(http: H, network: Network, clock: Clock, signer: S) -> Self {
Client {
api: Api::new(http, network, clock),
signer,
vault: None,
clock,
nonces: Mutex::new(NonceClock::new()),
}
}
#[must_use]
pub fn acting_for(mut self, vault: Address) -> Self {
self.vault = Some(vault);
self
}
#[must_use]
pub const fn network(&self) -> Network {
self.api.network()
}
#[must_use]
pub const fn http(&self) -> &H {
self.api.http()
}
#[must_use]
pub const fn signer(&self) -> &S {
&self.signer
}
pub fn spent(&self) -> Spent {
self.api.spent()
}
pub fn next_nonce(&self) -> u64 {
self.nonces.lock().unwrap_or_else(PoisonError::into_inner).next((self.clock)())
}
}
#[cfg(feature = "sign")]
impl<H: Http, S: Signer> Client<H, S> {
pub async fn send(&self, action: &impl L1Action) -> Result<Accepted, SendError> {
self.send_at(action, self.next_nonce()).await
}
pub async fn send_at(&self, action: &impl L1Action, nonce: u64) -> Result<Accepted, SendError> {
let typed = action.typed(nonce, self.vault, self.network());
let signature = self.signer.sign_typed(&Domain::EXCHANGE, &typed).await?;
self.check(&typed.digest(&Domain::EXCHANGE), &signature)?;
Ok(exchange(&self.api, action.body(nonce, &signature, self.vault)).await?)
}
pub async fn send_user(&self, action: &impl UserAction) -> Result<Accepted, SendError> {
let network = self.network();
let domain = Domain::user_signed(network);
let typed = action.typed(network);
let signature = self.signer.sign_typed(&domain, &typed).await?;
self.check(&typed.digest(&domain), &signature)?;
Ok(exchange(&self.api, action.body(&signature, network)).await?)
}
fn check(&self, digest: &[u8; 32], signature: &crate::account::Signature) -> Result<(), SignError> {
crate::account::verify(digest, signature, self.signer.address())
}
}
impl<H: Http, S> Transport for Client<H, S> {
fn post(&self, path: &str, body: String) -> impl Future<Output = Result<String, TransportError>> {
self.api.post(path, body)
}
}