Skip to content

Commit

Permalink
save dev state - WalletConnect client impl
Browse files Browse the repository at this point in the history
  • Loading branch information
borngraced committed Sep 9, 2024
1 parent ca3f558 commit c3dc6c6
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 147 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 12 additions & 11 deletions mm2src/kdf_walletconnect/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,25 @@ use derive_more::Display;
use pairing_api::PairingClientError;
use relay_client::error::{ClientError, Error};
use relay_rpc::rpc::PublishError;
use serde::{Deserialize, Serialize};

#[derive(Debug, Display)]
#[derive(Debug, Display, Serialize, Deserialize)]
pub enum WalletConnectClientError {
PairingError(PairingClientError),
PairingError(String),
EncodeError(String),
PublishError(Error<PublishError>),
ClientError(ClientError),
PairingNotFound(String)
PublishError(String),
ClientError(String),
PairingNotFound(String),
}

impl From<PairingClientError> for WalletConnectClientError {
fn from(value: PairingClientError) -> Self {
WalletConnectClientError::PairingError(value)
}
fn from(error: PairingClientError) -> Self { WalletConnectClientError::PairingError(error.to_string()) }
}

impl From<ClientError> for WalletConnectClientError {
fn from(value: ClientError) -> Self {
WalletConnectClientError::ClientError(value)
}
fn from(error: ClientError) -> Self { WalletConnectClientError::ClientError(error.to_string()) }
}

impl From<Error<PublishError>> for WalletConnectClientError {
fn from(error: Error<PublishError>) -> Self { WalletConnectClientError::PublishError(format!("{error:?}")) }
}
10 changes: 5 additions & 5 deletions mm2src/kdf_walletconnect/src/handler.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use std::time::Duration;

use futures::channel::mpsc::UnboundedSender;
use relay_client::{error::ClientError, websocket::{CloseFrame, ConnectionHandler, PublishedMessage}, ConnectionOptions};
use common::log::info;
use futures::channel::mpsc::UnboundedSender;
use relay_client::{error::ClientError,
websocket::{CloseFrame, ConnectionHandler, PublishedMessage},
ConnectionOptions};
use relay_rpc::auth::{ed25519_dalek::SigningKey, AuthToken};

pub struct Handler {
Expand All @@ -11,9 +13,7 @@ pub struct Handler {
}

impl Handler {
pub fn new(name: &'static str, sender: UnboundedSender<PublishedMessage>) -> Self {
Self { name, sender }
}
pub fn new(name: &'static str, sender: UnboundedSender<PublishedMessage>) -> Self { Self { name, sender } }
}

impl ConnectionHandler for Handler {
Expand Down
249 changes: 138 additions & 111 deletions mm2src/kdf_walletconnect/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,39 @@
use std::{collections::HashMap, sync::Arc, time::Duration};
mod error;
mod handler;
mod session_key;

use common::log::info;
use error::WalletConnectClientError;
use futures::{channel::mpsc::{unbounded, UnboundedReceiver}, lock::Mutex, StreamExt};
use futures::{channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender},
lock::Mutex,
StreamExt};
use handler::Handler;
use mm2_err_handle::prelude::MmResult;
use mm2_err_handle::prelude::*;
use pairing_api::{Methods, PairingClient};
use relay_client::{websocket::{Client, PublishedMessage}, ConnectionOptions, MessageIdGenerator};
use relay_rpc::{domain::{MessageId, SubscriptionId, Topic}, rpc::{params::{IrnMetadata, Metadata}, Params, Payload, Request, Response, SuccessfulResponse, JSON_RPC_VERSION_STR}};
use relay_client::{websocket::{Client, PublishedMessage},
ConnectionOptions, MessageIdGenerator};
use relay_rpc::{domain::{MessageId, SubscriptionId, Topic},
rpc::{params::{IrnMetadata, Metadata},
Params, Payload, Request, Response, SuccessfulResponse, JSON_RPC_VERSION_STR}};
use session_key::SessionKey;
use mm2_err_handle::prelude::*;
use std::{collections::HashMap, sync::Arc, time::Duration};
use wc_common::{encrypt_and_encode, EnvelopeType};
use common::log::info;


mod session_key;
mod handler;
mod error;

pub const RELAY_ADDRESS: &str = "wss://relay.walletconnect.com";
pub const PROJECT_ID: &str = "86e916bcbacee7f98225dde86b697f5b";
const RELAY_ADDRESS: &str = "wss://relay.walletconnect.com";
const PROJECT_ID: &str = "86e916bcbacee7f98225dde86b697f5b";
const SUPPORTED_PROTOCOL: &str = "irn";
const SUPPORTED_METHODS: &[&str] = &[
"eth_sendTransaction",
"eth_signTransaction",
"eth_sign",
"personal_sign",
"eth_signTypedData",
"eth_signTypedData_v4",
];
const SUPPORTED_CHAINS: &[&str] = &["eip155:1", "eip155:5"];
const SUPPORTED_EVENTS: &[&str] = &["chainChanged", "accountsChanged"];
const SUPPORTED_ACCOUNTS: &[&str] = &["eip155:5:0xBA5BA3955463ADcc7aa3E33bbdfb8A68e0933dD8"];

#[derive(Debug)]
pub struct Session {
Expand All @@ -28,136 +43,148 @@ pub struct Session {
pub session_key: SessionKey,
}

#[derive(Debug)]
pub struct WalletConnectClient {
pub client: Arc<Client>,
pub pairing: Arc<PairingClient>,
pub sessions: HashMap<Topic, Session>,
pub handler: Arc<Mutex<UnboundedReceiver<PublishedMessage>>>
pub client: Client,
pub pairing: PairingClient,
pub sessions: Arc<Mutex<HashMap<Topic, Session>>>,
pub handler: Arc<Mutex<UnboundedReceiver<PublishedMessage>>>,
pub rpc_handler: Arc<Mutex<UnboundedReceiver<Params>>>,
pub rpc_sender: UnboundedSender<Params>,
}

impl Default for WalletConnectClient {
fn default() -> Self {
Self::new()
}
fn default() -> Self { Self::new() }
}

impl WalletConnectClient {
pub fn new() -> Self {
let (msg_sender, msg_receiver) = unbounded();
let (rpc_sender, rpc_receiver) = unbounded::<Params>();

let pairing = PairingClient::new();
let client = Arc::new(Client::new(Handler::new("Komodefi", msg_sender)));
let client = Client::new(Handler::new("Komodefi", msg_sender));

Self {
client,
pairing,
sessions: HashMap::new(),
handler: Arc::new(Mutex::new(msg_receiver))
rpc_sender,
sessions: Arc::new(Mutex::new(HashMap::new())),
handler: Arc::new(Mutex::new(msg_receiver)),
rpc_handler: Arc::new(Mutex::new(rpc_receiver)),
}
}

pub async fn create_pairing(&self, metadata: Metadata, methods: Option<Methods>) -> MmResult<(Topic, String), WalletConnectClientError> {
Ok(self.pairing.create(metadata, methods, &self.client).await?)
pub async fn create_pairing(
&self,
metadata: Metadata,
methods: Option<Methods>,
) -> MmResult<(Topic, String), WalletConnectClientError> {
Ok(self.pairing.create(metadata, methods, &self.client).await?)
}

pub async fn connect_to_pairing(&self, url: &str, activate: bool) -> MmResult<Topic, WalletConnectClientError> {
Ok(self.pairing.pair(url, activate, &self.client).await?)
Ok(self.pairing.pair(url, activate, &self.client).await?)
}

pub async fn connect(&self, opts: &ConnectionOptions) -> MmResult<(), WalletConnectClientError> {
Ok(self.client.connect(opts).await?)
}


/// Private function to publish a request.
async fn publish_request(
&self,
topic: &str,
params: Params,
irn_metadata: IrnMetadata
) -> MmResult<(), WalletConnectClientError> {
let message_id = MessageIdGenerator::new().next();
let request = Request::new(message_id, params);
// Publish the encrypted message
self.publish_payload(topic, irn_metadata, Payload::Request(request))
.await?;

info!("Otbound request sent!\n");

Ok(())
}

/// Private function to publish a request response.
async fn publish_response(
&self,
topic: &str,
params: Params,
irn_metadata: IrnMetadata,
message_id: MessageId,
) -> MmResult<(), WalletConnectClientError> {
let response = Response::Success(SuccessfulResponse {
id: message_id,
jsonrpc: JSON_RPC_VERSION_STR.into(),
result: serde_json::to_value(params)
.map_to_mm(|err| WalletConnectClientError::EncodeError(err.to_string()))?,
});

// Publish the encrypted message
self.publish_payload(topic, irn_metadata, Payload::Response(response))
.await?;

println!("\nOutbound request sent!");

Ok(())
}


/// Private function to publish a payload.
async fn publish_payload(
&self,
topic: &str,
irn_metadata: IrnMetadata,
payload: Payload,
) -> MmResult<(), WalletConnectClientError> {
// try to extend session before updating local store.
let sym_key = {
let pairings = self.pairing.pairings.lock().await;
let pairing = pairings
.get(topic)
.ok_or_else(|| WalletConnectClientError::PairingNotFound(format!("Pariring not found for topic:{topic}")))?;
hex::decode(pairing.sym_key.clone()).map_to_mm(|err| {
WalletConnectClientError::EncodeError(format!("Failed to decode sym_key: {:?}", err))
})?
};

let payload = serde_json::to_string(&payload)
.map_to_mm(|err| WalletConnectClientError::EncodeError(err.to_string()))?;
let message = encrypt_and_encode(EnvelopeType::Type0, payload, &sym_key)
.map_to_mm(|err| WalletConnectClientError::EncodeError(err.to_string()))?;

// Publish the encrypted message
{
self.client
.publish(
topic.into(),
message,
None,
irn_metadata.tag,
Duration::from_secs(irn_metadata.ttl),
irn_metadata.prompt,
)
.await
.map_to_mm(WalletConnectClientError::PublishError)?;
};

Ok(())
}
async fn publish_request(
&self,
topic: &str,
params: Params,
irn_metadata: IrnMetadata,
) -> MmResult<(), WalletConnectClientError> {
let message_id = MessageIdGenerator::new().next();
let request = Request::new(message_id, params);
// Publish the encrypted message
self.publish_payload(topic, irn_metadata, Payload::Request(request))
.await?;

info!("Otbound request sent!\n");

Ok(())
}

/// Private function to publish a request response.
async fn publish_response(
&self,
topic: &str,
params: Params,
irn_metadata: IrnMetadata,
message_id: MessageId,
) -> MmResult<(), WalletConnectClientError> {
let response = Response::Success(SuccessfulResponse {
id: message_id,
jsonrpc: JSON_RPC_VERSION_STR.into(),
result: serde_json::to_value(params)
.map_to_mm(|err| WalletConnectClientError::EncodeError(err.to_string()))?,
});

// Publish the encrypted message
self.publish_payload(topic, irn_metadata, Payload::Response(response))
.await?;

println!("\nOutbound request sent!");

Ok(())
}

/// Private function to publish a payload.
async fn publish_payload(
&self,
topic: &str,
irn_metadata: IrnMetadata,
payload: Payload,
) -> MmResult<(), WalletConnectClientError> {
// try to extend session before updating local store.
let sym_key = {
let pairings = self.pairing.pairings.lock().await;
let pairing = pairings.get(topic).ok_or_else(|| {
WalletConnectClientError::PairingNotFound(format!("Pariring not found for topic:{topic}"))
})?;
hex::decode(pairing.sym_key.clone()).map_to_mm(|err| {
WalletConnectClientError::EncodeError(format!("Failed to decode sym_key: {:?}", err))
})?
};

let payload =
serde_json::to_string(&payload).map_to_mm(|err| WalletConnectClientError::EncodeError(err.to_string()))?;
let message = encrypt_and_encode(EnvelopeType::Type0, payload, &sym_key)
.map_to_mm(|err| WalletConnectClientError::EncodeError(err.to_string()))?;

// Publish the encrypted message
{
self.client
.publish(
topic.into(),
message,
None,
irn_metadata.tag,
Duration::from_secs(irn_metadata.ttl),
irn_metadata.prompt,
)
.await?;
};

Ok(())
}
}

pub async fn published_message_event_loop(client: Arc<WalletConnectClient>) {
let mut recv = client.handler.lock().await;
while let Some(data) = recv.next().await {
while let Some(msg) = recv.next().await {
info!("Pulished Message: {msg:?}");
todo!()
}
}

pub async fn wc_rpc_event_loop(receiver: Arc<Mutex<UnboundedReceiver<Params>>>) {
let mut recv = receiver.lock().await;
while let Some(param) = recv.next().await {
info!("Params: {param:?}");
todo!()
}
}
Loading

0 comments on commit c3dc6c6

Please sign in to comment.