diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs b/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs index afbada1d43e..13d61c5525c 100644 --- a/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs @@ -15,6 +15,7 @@ use tw_coin_entry::error::AddressResult; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::wallet_connect_signer::NoWalletConnectSigner; use tw_coin_entry::prefix::NoPrefix; use tw_keypair::tw::PublicKey; use tw_proto::{BLOCKCHAIN}::Proto; @@ -33,6 +34,7 @@ impl CoinEntry for {BLOCKCHAIN}Entry { type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; + type WalletConnectSigner = NoWalletConnectSigner; #[inline] fn parse_address( diff --git a/rust/chains/tw_binance/src/entry.rs b/rust/chains/tw_binance/src/entry.rs index bda948c9900..393c1910e63 100644 --- a/rust/chains/tw_binance/src/entry.rs +++ b/rust/chains/tw_binance/src/entry.rs @@ -16,6 +16,7 @@ use tw_coin_entry::error::AddressResult; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::wallet_connect_signer::NoWalletConnectSigner; use tw_keypair::tw::PublicKey; use tw_proto::Binance::Proto; use tw_proto::TxCompiler::Proto as CompilerProto; @@ -33,6 +34,7 @@ impl CoinEntry for BinanceEntry { type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; + type WalletConnectSigner = NoWalletConnectSigner; #[inline] fn parse_address( diff --git a/rust/chains/tw_cosmos/src/entry.rs b/rust/chains/tw_cosmos/src/entry.rs index ca2d84efcf7..f777ea0a292 100644 --- a/rust/chains/tw_cosmos/src/entry.rs +++ b/rust/chains/tw_cosmos/src/entry.rs @@ -12,6 +12,7 @@ use tw_coin_entry::error::AddressResult; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::wallet_connect_signer::NoWalletConnectSigner; use tw_cosmos_sdk::address::{Address, Bech32Prefix}; use tw_cosmos_sdk::context::StandardCosmosContext; use tw_cosmos_sdk::modules::compiler::tw_compiler::TWTransactionCompiler; @@ -31,6 +32,7 @@ impl CoinEntry for CosmosEntry { type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; + type WalletConnectSigner = NoWalletConnectSigner; #[inline] fn parse_address( diff --git a/rust/chains/tw_greenfield/src/entry.rs b/rust/chains/tw_greenfield/src/entry.rs index c78823b9b27..1803226c318 100644 --- a/rust/chains/tw_greenfield/src/entry.rs +++ b/rust/chains/tw_greenfield/src/entry.rs @@ -15,6 +15,7 @@ use tw_coin_entry::error::{AddressError, AddressResult}; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::wallet_connect_signer::NoWalletConnectSigner; use tw_coin_entry::prefix::NoPrefix; use tw_keypair::tw::PublicKey; use tw_proto::Greenfield::Proto; @@ -33,6 +34,7 @@ impl CoinEntry for GreenfieldEntry { type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; + type WalletConnectSigner = NoWalletConnectSigner; #[inline] fn parse_address( diff --git a/rust/chains/tw_native_evmos/src/entry.rs b/rust/chains/tw_native_evmos/src/entry.rs index eb25d7ea816..daa75eba7e5 100644 --- a/rust/chains/tw_native_evmos/src/entry.rs +++ b/rust/chains/tw_native_evmos/src/entry.rs @@ -13,6 +13,7 @@ use tw_coin_entry::error::AddressResult; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::wallet_connect_signer::NoWalletConnectSigner; use tw_cosmos_sdk::address::{Address, Bech32Prefix}; use tw_cosmos_sdk::modules::compiler::tw_compiler::TWTransactionCompiler; use tw_cosmos_sdk::modules::signer::tw_signer::TWSigner; @@ -31,6 +32,7 @@ impl CoinEntry for NativeEvmosEntry { type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; + type WalletConnectSigner = NoWalletConnectSigner; #[inline] fn parse_address( diff --git a/rust/chains/tw_native_injective/src/entry.rs b/rust/chains/tw_native_injective/src/entry.rs index b11d0036c87..ff6137da4a8 100644 --- a/rust/chains/tw_native_injective/src/entry.rs +++ b/rust/chains/tw_native_injective/src/entry.rs @@ -13,6 +13,7 @@ use tw_coin_entry::error::AddressResult; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::wallet_connect_signer::NoWalletConnectSigner; use tw_cosmos_sdk::address::{Address, Bech32Prefix, CosmosAddress}; use tw_cosmos_sdk::modules::compiler::tw_compiler::TWTransactionCompiler; use tw_cosmos_sdk::modules::signer::tw_signer::TWSigner; @@ -31,6 +32,7 @@ impl CoinEntry for NativeInjectiveEntry { type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; + type WalletConnectSigner = NoWalletConnectSigner; #[inline] fn parse_address( diff --git a/rust/chains/tw_thorchain/src/entry.rs b/rust/chains/tw_thorchain/src/entry.rs index 81f8bf562f1..7caf4126c30 100644 --- a/rust/chains/tw_thorchain/src/entry.rs +++ b/rust/chains/tw_thorchain/src/entry.rs @@ -14,6 +14,7 @@ use tw_coin_entry::error::AddressResult; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::wallet_connect_signer::NoWalletConnectSigner; use tw_cosmos_sdk::address::{Address, Bech32Prefix, CosmosAddress}; use tw_keypair::tw; use tw_proto::Cosmos::Proto; @@ -30,6 +31,7 @@ impl CoinEntry for ThorchainEntry { type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; + type WalletConnectSigner = NoWalletConnectSigner; #[inline] fn parse_address( diff --git a/rust/tw_any_coin/src/any_signer.rs b/rust/tw_any_coin/src/any_signer.rs index eea6859517b..df34ebb1f4b 100644 --- a/rust/tw_any_coin/src/any_signer.rs +++ b/rust/tw_any_coin/src/any_signer.rs @@ -27,4 +27,12 @@ impl AnySigner { let (ctx, entry) = coin_dispatcher(coin)?; entry.plan(&ctx, input) } + + /// Signs a transaction in WalletConnect format. + /// It is optional. Returns an error if the chain does not support WalletConnect signing. + #[inline] + pub fn sign_wallet_connect(input: &[u8], coin: CoinType) -> SigningResult { + let (ctx, entry) = coin_dispatcher(coin)?; + entry.sign_wallet_connect(&ctx, input) + } } diff --git a/rust/tw_aptos/src/entry.rs b/rust/tw_aptos/src/entry.rs index e2a16cd0ac8..d0cdafb38f0 100644 --- a/rust/tw_aptos/src/entry.rs +++ b/rust/tw_aptos/src/entry.rs @@ -15,6 +15,7 @@ use tw_coin_entry::error::{AddressError, AddressResult}; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::wallet_connect_signer::NoWalletConnectSigner; use tw_coin_entry::prefix::NoPrefix; use tw_keypair::tw::PublicKey; use tw_proto::Aptos::Proto; @@ -33,6 +34,7 @@ impl CoinEntry for AptosEntry { type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; + type WalletConnectSigner = NoWalletConnectSigner; #[inline] fn parse_address( diff --git a/rust/tw_bitcoin/src/entry.rs b/rust/tw_bitcoin/src/entry.rs index a847c4a0690..409e513bf14 100644 --- a/rust/tw_bitcoin/src/entry.rs +++ b/rust/tw_bitcoin/src/entry.rs @@ -11,6 +11,7 @@ use tw_coin_entry::derivation::Derivation; use tw_coin_entry::error::{AddressError, AddressResult}; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; +use tw_coin_entry::modules::wallet_connect_signer::NoWalletConnectSigner; use tw_coin_entry::prefix::NoPrefix; use tw_coin_entry::signing_output_error; use tw_keypair::tw::PublicKey; @@ -45,6 +46,7 @@ impl CoinEntry for BitcoinEntry { type JsonSigner = NoJsonSigner; type PlanBuilder = BitcoinPlanBuilder; type MessageSigner = NoMessageSigner; + type WalletConnectSigner = NoWalletConnectSigner; #[inline] fn parse_address( diff --git a/rust/tw_coin_entry/src/coin_entry.rs b/rust/tw_coin_entry/src/coin_entry.rs index 1ec5e590f48..ead5af20f5a 100644 --- a/rust/tw_coin_entry/src/coin_entry.rs +++ b/rust/tw_coin_entry/src/coin_entry.rs @@ -16,6 +16,7 @@ use tw_memory::Data; use tw_proto::{MessageRead, MessageWrite}; use crate::modules::message_signer::MessageSigner; +use crate::modules::wallet_connect_signer::WalletConnectSigner; pub use tw_proto::{ProtoError, ProtoResult}; pub type PrivateKeyBytes = Data; @@ -60,6 +61,10 @@ pub trait CoinEntry { /// /// **Optional**. Use `NoMessageSigner` if the blockchain does not support message signing. type MessageSigner: MessageSigner; + /// WalletConnect Signer - the module allows to sign a transaction received in WalletConnect format. + /// + /// **Optional**. Use `NoWalletConnectSigner` if the blockchain does not support WalletConnect transactions. + type WalletConnectSigner: WalletConnectSigner; /// Tries to parse `Self::Address` from the given `address` string by `coin` type and address `prefix`. fn parse_address( @@ -125,4 +130,11 @@ pub trait CoinEntry { fn message_signer(&self) -> Option { None } + + /// It is optional, Signing WalletConnect input with private key. + /// Returns `Ok(None)` if the blockchain does not support WalletConnect transactions. + #[inline] + fn wallet_connect_signer(&self) -> Option { + None + } } diff --git a/rust/tw_coin_entry/src/coin_entry_ext.rs b/rust/tw_coin_entry/src/coin_entry_ext.rs index d3daf007c4a..286035cf33a 100644 --- a/rust/tw_coin_entry/src/coin_entry_ext.rs +++ b/rust/tw_coin_entry/src/coin_entry_ext.rs @@ -12,6 +12,7 @@ use crate::error::{AddressResult, SigningError, SigningErrorType}; use crate::modules::json_signer::JsonSigner; use crate::modules::message_signer::MessageSigner; use crate::modules::plan_builder::PlanBuilder; +use crate::modules::wallet_connect_signer::WalletConnectSigner; use crate::prefix::AddressPrefix; use tw_keypair::tw::{PrivateKey, PublicKey}; use tw_memory::Data; @@ -84,6 +85,9 @@ pub trait CoinEntryExt { /// Verifies a signature for a message. fn verify_message(&self, coin: &dyn CoinContext, input: &[u8]) -> SigningResult; + + /// Signs a transaction in WalletConnect format. + fn sign_wallet_connect(&self, coin: &dyn CoinContext, input: &[u8]) -> SigningResult; } impl CoinEntryExt for T @@ -209,4 +213,15 @@ where deserialize(input)?; Ok(message_signer.verify_message(coin, input)) } + + fn sign_wallet_connect(&self, coin: &dyn CoinContext, input: &[u8]) -> SigningResult { + let Some(wc_signer) = self.wallet_connect_signer() else { + return Err(SigningError(SigningErrorType::Error_not_supported)); + }; + + let input: ::SigningInput<'_> = + deserialize(input)?; + let output = wc_signer.sign(coin, input); + serialize(&output).map_err(SigningError::from) + } } diff --git a/rust/tw_coin_entry/src/modules/mod.rs b/rust/tw_coin_entry/src/modules/mod.rs index 6f8b8b891bf..959969d9d62 100644 --- a/rust/tw_coin_entry/src/modules/mod.rs +++ b/rust/tw_coin_entry/src/modules/mod.rs @@ -9,3 +9,4 @@ pub mod json_signer; pub mod message_signer; pub mod plan_builder; +pub mod wallet_connect_signer; diff --git a/rust/tw_coin_entry/src/modules/wallet_connect_signer.rs b/rust/tw_coin_entry/src/modules/wallet_connect_signer.rs new file mode 100644 index 00000000000..ac7be14c87f --- /dev/null +++ b/rust/tw_coin_entry/src/modules/wallet_connect_signer.rs @@ -0,0 +1,28 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::coin_context::CoinContext; +use tw_proto::{DummyMessage, MessageRead, MessageWrite, NoMessage}; + +pub trait WalletConnectSigner { + type SigningInput<'a>: MessageRead<'a>; + type SigningOutput: MessageWrite; + + /// Signs a transaction in WalletConnect format. + fn sign(&self, coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput; +} + +/// `NoWalletConnectSigner` can't be created since there are no enum variants. +pub enum NoWalletConnectSigner {} + +impl WalletConnectSigner for NoWalletConnectSigner { + type SigningInput<'a> = DummyMessage; + type SigningOutput = NoMessage; + + fn sign(&self, _coin: &dyn CoinContext, _input: Self::SigningInput<'_>) -> Self::SigningOutput { + panic!("`NoWalletConnectSigner` should never be constructed and used") + } +} diff --git a/rust/tw_ethereum/src/entry.rs b/rust/tw_ethereum/src/entry.rs index f69893fd63f..6a36bbf92ae 100644 --- a/rust/tw_ethereum/src/entry.rs +++ b/rust/tw_ethereum/src/entry.rs @@ -10,6 +10,7 @@ use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; use tw_coin_entry::error::{AddressError, AddressResult}; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::wallet_connect_signer::NoWalletConnectSigner; use tw_coin_entry::prefix::NoPrefix; use tw_evm::address::Address; use tw_evm::evm_context::StandardEvmContext; @@ -35,6 +36,7 @@ impl CoinEntry for EthereumEntry { type JsonSigner = EthJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = EthMessageSigner; + type WalletConnectSigner = NoWalletConnectSigner; #[inline] fn parse_address( diff --git a/rust/tw_internet_computer/src/entry.rs b/rust/tw_internet_computer/src/entry.rs index 6fb47ba161c..594d5ef94e8 100644 --- a/rust/tw_internet_computer/src/entry.rs +++ b/rust/tw_internet_computer/src/entry.rs @@ -12,6 +12,7 @@ use tw_coin_entry::{ error::{AddressError, AddressResult, SigningError}, modules::{ json_signer::NoJsonSigner, message_signer::NoMessageSigner, plan_builder::NoPlanBuilder, + wallet_connect_signer::NoWalletConnectSigner, }, prefix::NoPrefix, signing_output_error, @@ -43,6 +44,8 @@ impl CoinEntry for InternetComputerEntry { type MessageSigner = NoMessageSigner; + type WalletConnectSigner = NoWalletConnectSigner; + #[inline] fn parse_address( &self, diff --git a/rust/tw_ronin/src/entry.rs b/rust/tw_ronin/src/entry.rs index 36bb8fb96b2..88e9c1f30d0 100644 --- a/rust/tw_ronin/src/entry.rs +++ b/rust/tw_ronin/src/entry.rs @@ -12,6 +12,7 @@ use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; use tw_coin_entry::error::{AddressError, AddressResult}; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::wallet_connect_signer::NoWalletConnectSigner; use tw_coin_entry::prefix::NoPrefix; use tw_evm::evm_entry::EvmEntry; use tw_evm::modules::compiler::Compiler; @@ -35,6 +36,7 @@ impl CoinEntry for RoninEntry { type JsonSigner = EthJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = EthMessageSigner; + type WalletConnectSigner = NoWalletConnectSigner; #[inline] fn parse_address( diff --git a/src/proto/WalletConnect.proto b/src/proto/WalletConnect.proto new file mode 100644 index 00000000000..bfccefad5fa --- /dev/null +++ b/src/proto/WalletConnect.proto @@ -0,0 +1,35 @@ +syntax = "proto3"; + +package TW.WalletConnect.Proto; +option java_package = "wallet.core.jni.proto"; + +import "Common.proto"; + +// The transaction protocol may differ from version to version. +enum Protocol { + V2 = 0; +} + +message SigningInput { + // A signing method like "cosmos_signAmino" or "eth_signTransaction". + string method = 1; + + // Wallet's private key. + bytes private_key = 2; + + // Transaction payload to sign. + // Basically, a JSON object. + string payload = 3; +} + +message SigningOutput { + // A signing result in string representation. + // It can be a serialized signed transaction in Base64, hex etc, or a JSON object. + string result = 1; + + // OK (=0) or other codes in case of error + Common.Proto.SigningError error = 2; + + // error description in case of error + string error_message = 3; +}