diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 8a3f3ff..f2eae59 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -11,3 +11,4 @@ pub mod invoke; pub mod publish; pub mod telemetry; pub mod test; +pub mod wallet_connect; \ No newline at end of file diff --git a/src/commands/wallet_connect.rs b/src/commands/wallet_connect.rs new file mode 100644 index 0000000..18748eb --- /dev/null +++ b/src/commands/wallet_connect.rs @@ -0,0 +1,71 @@ +use crate::config::{ProfileConfig, RootConfig}; +use clap::Args as ClapArgs; +use miette::IntoDiagnostic; +use std::net::SocketAddr; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::net::TcpListener; + +const HTML_TEMPLATE: &str = include_str!("../../templates/wallet_connect.html"); + +#[derive(ClapArgs)] +pub struct Args { + /// Port to serve wallet connect on. + #[arg(long, default_value_t = 3030)] + port: u16, + + /// Optional CBOR hex to pre-fill + #[arg(long)] + cbor: Option, +} + +pub async fn run(args: Args, config: &RootConfig, profile: &ProfileConfig) -> miette::Result<()> { + #[cfg(feature = "unstable")] + { + _run(args, config, profile).await + } + #[cfg(not(feature = "unstable"))] + { + Err(miette::miette!( + "The wallet-connect command is currently unstable and requires the `unstable` feature to be enabled." + )) + } +} + +async fn _run(args: Args, config: &RootConfig, profile: &ProfileConfig) -> miette::Result<()> { + let network = config.resolve_profile_network(&profile.name)?; + + let addr = SocketAddr::from(([127, 0, 0, 1], args.port)); + let listener = TcpListener::bind(addr).await.into_diagnostic()?; + + println!("Serving wallet connect on port {}", args.port); + println!("http://localhost:{}", args.port); + + let cbor_value = args.cbor.unwrap_or_default(); + let headers_json = serde_json::to_string(&network.trp.headers).into_diagnostic()?; + + let body = HTML_TEMPLATE + .replace("__CBOR_PLACEHOLDER__", &cbor_value) + .replace("__TRP_URL_PLACEHOLDER__", &network.trp.url) + .replace("__TRP_HEADERS_PLACEHOLDER__", &headers_json); + + let response = format!( + "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: {}\r\n\r\n{}", + body.len(), + body + ); + + loop { + let (mut socket, _) = listener.accept().await.into_diagnostic()?; + let response = response.clone(); + + tokio::spawn(async move { + let mut buf = [0; 1024]; + // Leemos la solicitud (aunque la ignoramos para este ejemplo simple) + let _ = socket.read(&mut buf).await; + + if let Err(e) = socket.write_all(response.as_bytes()).await { + eprintln!("failed to write to socket; err = {:?}", e); + } + }); + } +} diff --git a/src/main.rs b/src/main.rs index 5ad2ee8..e1e4ee9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -63,6 +63,10 @@ enum Commands { /// Manage crypographic identities Identities(cmds::identities::Args), + /// Connect to wallets via Wallet Connect protocol (UNSTABLE - This feature is experimental and may change) + #[command(hide = true)] + WalletConnect(cmds::wallet_connect::Args), + /// Publish a Tx3 package into the registry (UNSTABLE - This feature is experimental and may change) #[command(hide = true)] Publish(cmds::publish::Args), @@ -111,6 +115,7 @@ async fn run_scoped_command(cli: Cli, config: RootConfig) -> Result<()> { Commands::Identities(args) => cmds::identities::run(args, &config, &profile), Commands::Publish(args) => cmds::publish::run(args, &config), Commands::Telemetry(args) => cmds::telemetry::run(args), + Commands::WalletConnect(args) => cmds::wallet_connect::run(args, &config, &profile).await, }; if let Some(handle) = metric { diff --git a/templates/wallet_connect.html b/templates/wallet_connect.html new file mode 100644 index 0000000..b47f088 --- /dev/null +++ b/templates/wallet_connect.html @@ -0,0 +1,374 @@ + + + + + + + Tx3 Wallet Connect + + + + +
+

Sign Transaction

+

Connect your Cardano wallet to sign the transaction below.

+ + + +
+ + + +
+ +
Ready to connect
+
+ + + + + + + + + + \ No newline at end of file