use alloy::{
dyn_abi::TypedData,
network::TransactionBuilder,
primitives::{Address, B256, ChainId, Signature, U256},
rpc::types::TransactionRequest,
signers::Signer,
};
use async_trait::async_trait;
use std::sync::Arc;
use thiserror::Error;
use crate::{AdminRelayerClient, ApiSdkError, RelayerClient};
use rrelayer_core::{
common_types::EvmAddress,
relayer::RelayerId,
transaction::api::RelayTransactionRequest,
transaction::types::{TransactionData, TransactionValue},
};
use std::str::FromStr;
#[derive(Error, Debug)]
pub enum RelayerSignerError {
#[error("Relayer API error: {0}")]
ApiError(#[from] ApiSdkError),
#[error("Invalid signature format")]
InvalidSignature,
#[error("Address conversion error")]
AddressConversion,
}
#[derive(Debug, Clone)]
pub struct RelayerSigner {
client_type: RelayerClientType,
address: Address,
chain_id: Option<ChainId>,
}
#[derive(Debug, Clone)]
pub enum RelayerClientType {
Relayer(Arc<RelayerClient>),
Admin(Arc<AdminRelayerClient>),
}
impl RelayerSigner {
pub fn from_relayer_client(
client: Arc<RelayerClient>,
address: Address,
chain_id: Option<ChainId>,
) -> Self {
Self { client_type: RelayerClientType::Relayer(client), address, chain_id }
}
pub fn from_admin_client(
client: Arc<AdminRelayerClient>,
address: Address,
chain_id: Option<ChainId>,
) -> Self {
Self { client_type: RelayerClientType::Admin(client), address, chain_id }
}
pub async fn from_relayer_client_auto_address(
client: Arc<RelayerClient>,
chain_id: Option<ChainId>,
) -> Result<Self, RelayerSignerError> {
let relayer_address = client.address().await?;
let address = relayer_address.into_address();
Ok(Self::from_relayer_client(client, address, chain_id))
}
pub async fn from_admin_client_auto_address(
client: Arc<AdminRelayerClient>,
chain_id: Option<ChainId>,
) -> Result<Self, RelayerSignerError> {
let relayer_address = client.address().await?;
let address = relayer_address.into_address();
Ok(Self::from_admin_client(client, address, chain_id))
}
pub fn relayer_id(&self) -> &RelayerId {
match &self.client_type {
RelayerClientType::Relayer(client) => client.id(),
RelayerClientType::Admin(client) => client.id(),
}
}
pub fn client(&self) -> &RelayerClientType {
&self.client_type
}
pub fn address(&self) -> &Address {
&self.address
}
}
#[async_trait]
impl Signer for RelayerSigner {
fn address(&self) -> Address {
self.address
}
fn chain_id(&self) -> Option<ChainId> {
self.chain_id
}
fn set_chain_id(&mut self, chain_id: Option<ChainId>) {
self.chain_id = chain_id;
}
async fn sign_hash(&self, hash: &B256) -> alloy::signers::Result<Signature> {
let hash_hex = format!("0x{}", hex::encode(hash));
let sign_result = match &self.client_type {
RelayerClientType::Relayer(client) => client.sign().text(&hash_hex, None).await,
RelayerClientType::Admin(client) => client.sign().text(&hash_hex, None).await,
}
.map_err(alloy::signers::Error::other)?;
Ok(sign_result.signature)
}
async fn sign_dynamic_typed_data(
&self,
payload: &TypedData,
) -> alloy::signers::Result<Signature> {
let sign_result = match &self.client_type {
RelayerClientType::Relayer(client) => client.sign().typed_data(payload, None).await,
RelayerClientType::Admin(client) => client.sign().typed_data(payload, None).await,
}
.map_err(alloy::signers::Error::other)?;
Ok(sign_result.signature)
}
}
#[derive(Debug, Clone)]
pub struct RelayerProvider<P> {
inner: P,
relayer_signer: RelayerSigner,
}
impl<P> RelayerProvider<P> {
pub fn new(provider: P, relayer_signer: RelayerSigner) -> Self {
Self { inner: provider, relayer_signer }
}
pub fn inner(&self) -> &P {
&self.inner
}
pub fn relayer_signer(&self) -> &RelayerSigner {
&self.relayer_signer
}
pub async fn send_transaction(
&self,
tx_request: &TransactionRequest,
) -> Result<String, RelayerSignerError> {
println!("🔄 Sending transaction via relayer!");
let relay_request = self.convert_transaction_request(tx_request)?;
let result = match &self.relayer_signer.client_type {
RelayerClientType::Relayer(client) => {
client.transaction().send(&relay_request, None).await
}
RelayerClientType::Admin(client) => {
client.transaction().send(&relay_request, None).await
}
}?;
Ok(result.id.to_string())
}
pub async fn send_transaction_via_relayer(
&self,
to: Address,
value: u64,
) -> Result<String, RelayerSignerError> {
let tx_request = TransactionRequest::default().with_to(to).with_value(U256::from(value));
self.send_transaction(&tx_request).await
}
fn convert_transaction_request(
&self,
tx_request: &TransactionRequest,
) -> Result<RelayTransactionRequest, RelayerSignerError> {
let to = match &tx_request.to {
Some(to_addr) => {
#[allow(irrefutable_let_patterns)]
if let Ok(addr_str) = format!("{:?}", to_addr)
.strip_prefix("0x")
.unwrap_or(&format!("{:?}", to_addr))
.parse::<String>()
{
EvmAddress::from_str(&addr_str)
.map_err(|_| RelayerSignerError::AddressConversion)?
} else {
return Err(RelayerSignerError::AddressConversion);
}
}
None => return Err(RelayerSignerError::InvalidSignature),
};
let value =
tx_request.value.map(TransactionValue::new).unwrap_or_else(TransactionValue::zero);
let data = match tx_request.input.input() {
Some(input_bytes) if !input_bytes.is_empty() => {
TransactionData::new(input_bytes.clone())
}
_ => TransactionData::empty(),
};
Ok(RelayTransactionRequest {
to,
value,
data,
speed: None, external_id: None,
blobs: None, })
}
}
pub fn with_relayer<P>(provider: P, relayer_signer: RelayerSigner) -> RelayerProvider<P> {
RelayerProvider::new(provider, relayer_signer)
}
#[cfg(test)]
mod tests {
use super::*;
use alloy::primitives::address;
#[test]
fn test_relayer_signer_creation() {
let test_address = address!("742d35cc6634c0532925a3b8d67e8000c942b1b5");
let _would_create = |client: Arc<RelayerClient>| {
RelayerSigner::from_relayer_client(client, test_address, Some(1))
};
}
#[test]
fn test_provider_wrapper() {
let _test_fn = |signer: RelayerSigner| {
let mock_provider = (); with_relayer(mock_provider, signer)
};
}
}