Skip to content

Commit

Permalink
feat: chain abstraction client
Browse files Browse the repository at this point in the history
  • Loading branch information
chris13524 committed Nov 12, 2024
1 parent 04554d3 commit 92dcf52
Show file tree
Hide file tree
Showing 8 changed files with 619 additions and 0 deletions.
20 changes: 20 additions & 0 deletions crates/yttrium/src/chain_abstraction/api/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use alloy::primitives::Address;
use serde::{Deserialize, Serialize};

pub mod route;
pub mod status;

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Transaction {
pub from: Address,
pub to: Address,
pub value: String,
pub gas: String,
pub gas_price: String,
pub data: String,
pub nonce: String,
pub max_fee_per_gas: String,
pub max_priority_fee_per_gas: String,
pub chain_id: String,
}
73 changes: 73 additions & 0 deletions crates/yttrium/src/chain_abstraction/api/route.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use super::Transaction;
use alloy::primitives::Address;
use relay_rpc::domain::ProjectId;
use serde::{Deserialize, Serialize};

pub const ROUTE_ENDPOINT_PATH: &str = "/v1/ca/orchestrator/route";

#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct RouteQueryParams {
pub project_id: ProjectId,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RouteRequest {
pub transaction: Transaction,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Metadata {
pub funding_from: Vec<FundingMetadata>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FundingMetadata {
pub chain_id: String,
pub token_contract: Address,
pub symbol: String,
pub amount: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RouteResponseSuccess {
pub orchestration_id: Option<String>,
pub transactions: Vec<Transaction>,
pub metadata: Option<Metadata>,
}

/// Bridging check error response that should be returned as a normal HTTP 200
/// response
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RouteResponseError {
pub error: BridgingError,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum BridgingError {
NoRoutesAvailable,
InsufficientFunds,
InsufficientGasFunds,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RouteResponse {
Success(RouteResponseSuccess),
Error(RouteResponseError),
}

impl RouteResponse {
pub fn into_result(
self,
) -> Result<RouteResponseSuccess, RouteResponseError> {
match self {
Self::Success(success) => Ok(success),
Self::Error(error) => Err(error),
}
}
}
42 changes: 42 additions & 0 deletions crates/yttrium/src/chain_abstraction/api/status.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use relay_rpc::domain::ProjectId;
use serde::{Deserialize, Serialize};

pub const STATUS_ENDPOINT_PATH: &str = "/v1/ca/orchestrator/status";

#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct StatusQueryParams {
pub project_id: ProjectId,
pub orchestration_id: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum BridgingStatus {
Pending,
Completed,
Error,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct StatusResponseSuccess {
status: BridgingStatus,
created_at: usize,
error_reason: Option<String>,
/// Polling interval in ms for the client
check_in: Option<usize>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct StatusResponseError {
pub error: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum StatusResponse {
Success(StatusResponseSuccess),
Error(StatusResponseError),
}
67 changes: 67 additions & 0 deletions crates/yttrium/src/chain_abstraction/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use super::{
api::{
route::{
RouteQueryParams, RouteRequest, RouteResponse, ROUTE_ENDPOINT_PATH,
},
status::{StatusQueryParams, StatusResponse, STATUS_ENDPOINT_PATH},
Transaction,
},
error::RouteError,
};
use relay_rpc::domain::ProjectId;
use reqwest::{Client as ReqwestClient, Url};

pub struct Client {
client: ReqwestClient,
base_url: Url,
project_id: ProjectId,
}

impl Client {
pub fn new(project_id: ProjectId) -> Self {
Self {
client: ReqwestClient::new(),
base_url: "https://rpc.walletconnect.com".parse().unwrap(),
project_id,
}
}

pub async fn route(
&self,
transaction: Transaction,
) -> Result<RouteResponse, RouteError> {
let response = self
.client
.post(self.base_url.join(ROUTE_ENDPOINT_PATH).unwrap())
.json(&RouteRequest { transaction })
.query(&RouteQueryParams { project_id: self.project_id.clone() })
.send()
.await
.map_err(RouteError::Request)?;
if response.status().is_success() {
response.json().await.map_err(RouteError::Request)
} else {
Err(RouteError::RequestFailed(response.text().await))
}
}

pub async fn status(
&self,
orchestration_id: String,
) -> Result<StatusResponse, RouteError> {
self.client
.get(self.base_url.join(STATUS_ENDPOINT_PATH).unwrap())
.query(&StatusQueryParams {
project_id: self.project_id.clone(),
orchestration_id,
})
.send()
.await
.map_err(RouteError::Request)?
.error_for_status()
.map_err(RouteError::Request)?
.json()
.await
.map_err(RouteError::Request)
}
}
10 changes: 10 additions & 0 deletions crates/yttrium/src/chain_abstraction/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#[derive(thiserror::Error, Debug)]
pub enum RouteError {
/// Retryable error
#[error("HTTP request: {0}")]
Request(reqwest::Error),

/// Retryable error
#[error("HTTP request failed: {0:?}")]
RequestFailed(Result<String, reqwest::Error>),
}
7 changes: 7 additions & 0 deletions crates/yttrium/src/chain_abstraction/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pub mod api;
pub mod client;
pub mod error;

#[cfg(test)]
#[cfg(feature = "test_blockchain_api")]
mod tests;
Loading

0 comments on commit 92dcf52

Please sign in to comment.