trident-sdk 0.2.0

End-user SDK for Trident deposits and cross-chain spends
Documentation
use std::fmt::{Display, Formatter};
use std::str::FromStr;

use alloy_primitives::Address;
use alloy_provider::network::{AnyNetwork, EthereumWallet};
use alloy_provider::{Provider, ProviderBuilder};
use alloy_signer_local::PrivateKeySigner;
use tonic::transport::Endpoint;
use trident_sdk::proto::delivery_v1;

pub(crate) type ExampleResult<T = ()> = Result<T, Box<dyn std::error::Error + Send + Sync>>;

#[derive(Debug)]
struct ExampleError(String);

impl Display for ExampleError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_str(&self.0)
    }
}

impl std::error::Error for ExampleError {}

fn example_error(message: impl Into<String>) -> Box<dyn std::error::Error + Send + Sync> {
    Box::new(ExampleError(message.into()))
}

pub(crate) fn env_var(name: &str) -> ExampleResult<String> {
    std::env::var(name)
        .map_err(|_| example_error(format!("missing required environment variable {name}")))
}

pub(crate) fn env_parse<T>(name: &str) -> ExampleResult<T>
where
    T: FromStr,
    T::Err: Display,
{
    let raw = env_var(name)?;
    raw.parse()
        .map_err(|error| example_error(format!("failed to parse {name}: {error}")))
}

pub(crate) fn grpc_url() -> ExampleResult<String> {
    env_var("TRIDENT_GRPC_URL")
}

pub(crate) fn grpc_endpoint() -> ExampleResult<Endpoint> {
    Ok(Endpoint::from_shared(grpc_url()?)?)
}

pub(crate) fn signer_from_env() -> ExampleResult<PrivateKeySigner> {
    env_parse("TRIDENT_PRIVATE_KEY")
}

pub(crate) async fn provider_from_env(
    rpc_var: &str,
    signer: &PrivateKeySigner,
) -> ExampleResult<impl Provider<AnyNetwork> + Clone + use<>> {
    let rpc_url = env_var(rpc_var)?;
    let wallet = EthereumWallet::new(signer.clone());
    Ok(ProviderBuilder::default()
        .with_recommended_fillers()
        .wallet(wallet)
        .connect(rpc_url.as_str())
        .await?
        .erased())
}

#[allow(dead_code)]
pub(crate) async fn deposit_contract_for_domain(domain: u32) -> ExampleResult<Address> {
    let channel = grpc_endpoint()?.connect().await?;
    let mut delivery = delivery_v1::delivery_service_client::DeliveryServiceClient::new(channel);
    let deployments = delivery
        .list_deployments(delivery_v1::ListDeploymentsRequest {})
        .await?
        .into_inner();

    let deployment = deployments
        .deployments
        .into_iter()
        .find(|deployment| deployment.domain == domain)
        .ok_or_else(|| example_error(format!("no deployment returned for domain {domain}")))?;

    let deposit_contract = deployment.deposit_contract.ok_or_else(|| {
        example_error(format!(
            "deployment for domain {domain} is missing deposit_contract"
        ))
    })?;

    Ok(Address::try_from(deposit_contract)?)
}