From cb61ceadafdffb497f41c52cf60b2e461bdaa03e Mon Sep 17 00:00:00 2001
From: Satoshi Otomakan <satoshiotomakan8@gmail.com>
Date: Mon, 8 Jan 2024 17:22:12 +0700
Subject: [PATCH] [WalletConnect/BNB]: Add `TWWalletConnectRequestParse` C FFI

* Improve WalletConnect.proto interface
---
 .../TrustWalletCore/TWWalletConnectRequest.h  | 27 ++++++++++
 rust/chains/tw_binance/Cargo.toml             |  2 +-
 .../src/modules/wallet_connect/connector.rs   | 14 +++--
 .../src/modules/wallet_connect/methods.rs     |  7 ---
 .../src/modules/wallet_connect/mod.rs         |  1 -
 .../src/ffi/tw_wallet_connect_request.rs      |  4 +-
 .../src/test_utils/wallet_connect_utils.rs    | 29 +++-------
 .../chains/binance/binance_wallet_connect.rs  | 12 +++--
 rust/tw_cosmos_sdk/Cargo.toml                 |  2 +-
 src/interface/TWWalletConnectRequest.cpp      | 24 +++++++++
 src/proto/WalletConnect.proto                 | 22 +++++---
 .../chains/Binance/TWWalletConnectSigning.cpp | 54 +++++++++++++++++++
 .../Binance/TransactionCompilerTests.cpp      |  1 -
 13 files changed, 146 insertions(+), 53 deletions(-)
 create mode 100644 include/TrustWalletCore/TWWalletConnectRequest.h
 delete mode 100644 rust/chains/tw_binance/src/modules/wallet_connect/methods.rs
 create mode 100644 src/interface/TWWalletConnectRequest.cpp
 create mode 100644 tests/chains/Binance/TWWalletConnectSigning.cpp

diff --git a/include/TrustWalletCore/TWWalletConnectRequest.h b/include/TrustWalletCore/TWWalletConnectRequest.h
new file mode 100644
index 00000000000..a65e7d9ed3d
--- /dev/null
+++ b/include/TrustWalletCore/TWWalletConnectRequest.h
@@ -0,0 +1,27 @@
+// Copyright © 2017-2024 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.
+
+#pragma once
+
+#include "TWBase.h"
+#include "TWCoinType.h"
+#include "TWData.h"
+
+TW_EXTERN_C_BEGIN
+
+/// Represents a WalletConnect signing request.
+TW_EXPORT_CLASS
+struct TWWalletConnectRequest;
+
+/// Parses the WalletConnect signing request as a `SigningInput`.
+///
+/// \param coin The given coin type to plan the transaction for.
+/// \param input The serialized data of a `WalletConnect::Proto::ParseRequestInput` proto object.
+/// \return The serialized data of `WalletConnect::Proto::ParseRequestOutput` proto object.
+TW_EXPORT_STATIC_METHOD
+TWData* _Nonnull TWWalletConnectRequestParse(enum TWCoinType coin, TWData* _Nonnull input);
+
+TW_EXTERN_C_END
diff --git a/rust/chains/tw_binance/Cargo.toml b/rust/chains/tw_binance/Cargo.toml
index 9a1d5003651..e5c9a10aa2c 100644
--- a/rust/chains/tw_binance/Cargo.toml
+++ b/rust/chains/tw_binance/Cargo.toml
@@ -17,5 +17,5 @@ tw_evm = { path = "../../tw_evm" }
 tw_hash = { path = "../../tw_hash" }
 tw_keypair = { path = "../../tw_keypair" }
 tw_memory = { path = "../../tw_memory" }
-tw_misc = { path = "../../tw_misc" }
+tw_misc = { path = "../../tw_misc", features = ["serde"] }
 tw_proto = { path = "../../tw_proto" }
diff --git a/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs b/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs
index 48f9afc763b..13a9a95de7c 100644
--- a/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs
+++ b/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs
@@ -5,14 +5,14 @@
 // file LICENSE at the root of the source code distribution tree.
 
 use crate::modules::tx_builder::TxBuilder;
-use crate::modules::wallet_connect::methods::COSMOS_SIGN_AMINO;
 use crate::modules::wallet_connect::types::SignAminoRequest;
 use tw_coin_entry::coin_context::CoinContext;
 use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult};
 use tw_coin_entry::modules::wallet_connect_signer::WalletConnector;
 use tw_coin_entry::signing_output_error;
-use tw_proto::serialize;
-use tw_proto::WalletConnect::Proto as WCProto;
+use tw_proto::WalletConnect::Proto::{
+    self as WCProto, mod_ParseRequestOutput::OneOfsigning_input_oneof as SigningInputEnum,
+};
 
 pub struct BinanceWalletConnector;
 
@@ -32,8 +32,8 @@ impl BinanceWalletConnector {
         coin: &dyn CoinContext,
         request: WCProto::ParseRequestInput<'_>,
     ) -> SigningResult<WCProto::ParseRequestOutput<'static>> {
-        match request.method.as_ref() {
-            COSMOS_SIGN_AMINO => Self::parse_sign_amino_request(coin, request),
+        match request.method {
+            WCProto::Method::CosmosSignAmino => Self::parse_sign_amino_request(coin, request),
             _ => Err(SigningError(SigningErrorType::Error_not_supported)),
         }
     }
@@ -47,11 +47,9 @@ impl BinanceWalletConnector {
 
         // Parse a `SigningInput` from the given `signDoc`.
         let signing_input = TxBuilder::unsigned_tx_to_proto(&amino_req.sign_doc)?;
-        let signing_input_bytes = serialize(&signing_input)
-            .map_err(|_| SigningError(SigningErrorType::Error_internal))?;
 
         Ok(WCProto::ParseRequestOutput {
-            signing_input: signing_input_bytes.into(),
+            signing_input_oneof: SigningInputEnum::binance(signing_input),
             ..WCProto::ParseRequestOutput::default()
         })
     }
diff --git a/rust/chains/tw_binance/src/modules/wallet_connect/methods.rs b/rust/chains/tw_binance/src/modules/wallet_connect/methods.rs
deleted file mode 100644
index a57ace82339..00000000000
--- a/rust/chains/tw_binance/src/modules/wallet_connect/methods.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-// 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.
-
-pub const COSMOS_SIGN_AMINO: &str = "cosmos_signAmino";
diff --git a/rust/chains/tw_binance/src/modules/wallet_connect/mod.rs b/rust/chains/tw_binance/src/modules/wallet_connect/mod.rs
index 2227ef977f7..204b40aaa32 100644
--- a/rust/chains/tw_binance/src/modules/wallet_connect/mod.rs
+++ b/rust/chains/tw_binance/src/modules/wallet_connect/mod.rs
@@ -5,5 +5,4 @@
 // file LICENSE at the root of the source code distribution tree.
 
 pub mod connector;
-pub mod methods;
 pub mod types;
diff --git a/rust/tw_any_coin/src/ffi/tw_wallet_connect_request.rs b/rust/tw_any_coin/src/ffi/tw_wallet_connect_request.rs
index 637bf92a7af..6d97e358ec9 100644
--- a/rust/tw_any_coin/src/ffi/tw_wallet_connect_request.rs
+++ b/rust/tw_any_coin/src/ffi/tw_wallet_connect_request.rs
@@ -15,8 +15,8 @@ use tw_misc::try_or_else;
 /// Parses the WalletConnect signing request as a `SigningInput`.
 ///
 /// \param coin The given coin type to plan the transaction for.
-/// \param input The serialized data of a signing input.
-/// \return The serialized data of a `TransactionPlan` proto object.
+/// \param input The serialized data of a `WalletConnect::Proto::ParseRequestInput` proto object.
+/// \return The serialized data of `WalletConnect::Proto::ParseRequestOutput` proto object.
 #[no_mangle]
 pub unsafe extern "C" fn tw_wallet_connect_request_parse(
     coin: u32,
diff --git a/rust/tw_any_coin/src/test_utils/wallet_connect_utils.rs b/rust/tw_any_coin/src/test_utils/wallet_connect_utils.rs
index 2f19724ca51..7d6efd0fffa 100644
--- a/rust/tw_any_coin/src/test_utils/wallet_connect_utils.rs
+++ b/rust/tw_any_coin/src/test_utils/wallet_connect_utils.rs
@@ -5,43 +5,30 @@
 // file LICENSE at the root of the source code distribution tree.
 
 use crate::ffi::tw_wallet_connect_request::tw_wallet_connect_request_parse;
-use std::marker::PhantomData;
 use tw_coin_registry::coin_type::CoinType;
 use tw_memory::test_utils::tw_data_helper::TWDataHelper;
 use tw_memory::Data;
-use tw_proto::{
-    deserialize, serialize, Common::Proto as CommonProto, MessageRead,
-    WalletConnect::Proto as WCProto,
-};
+use tw_proto::{deserialize, serialize, WalletConnect::Proto as WCProto};
 
 #[derive(Default)]
-pub struct WalletConnectRequestHelper<'a, SigningInput: MessageRead<'a>> {
-    signing_input_data: Data,
-    _signing_input_type: PhantomData<&'a SigningInput>,
+pub struct WalletConnectRequestHelper {
+    output_data: Data,
 }
 
-impl<'a, SigningInput: MessageRead<'a>> WalletConnectRequestHelper<'a, SigningInput> {
-    pub fn parse(
+impl WalletConnectRequestHelper {
+    pub fn parse<'a>(
         &'a mut self,
         coin_type: CoinType,
         input: &WCProto::ParseRequestInput,
-    ) -> Result<SigningInput, CommonProto::SigningError> {
+    ) -> WCProto::ParseRequestOutput<'a> {
         let input_data = TWDataHelper::create(serialize(input).unwrap());
 
-        let output_data = TWDataHelper::wrap(unsafe {
+        self.output_data = TWDataHelper::wrap(unsafe {
             tw_wallet_connect_request_parse(coin_type as u32, input_data.ptr())
         })
         .to_vec()
         .expect("!tw_wallet_connect_request_parse returned nullptr");
 
-        let output: WCProto::ParseRequestOutput = deserialize(&output_data).unwrap();
-
-        if output.error != CommonProto::SigningError::OK {
-            return Err(output.error);
-        }
-
-        self.signing_input_data = output.signing_input.to_vec();
-        let signing_input: SigningInput = deserialize(&self.signing_input_data).unwrap();
-        Ok(signing_input)
+        deserialize(&self.output_data).unwrap()
     }
 }
diff --git a/rust/tw_any_coin/tests/chains/binance/binance_wallet_connect.rs b/rust/tw_any_coin/tests/chains/binance/binance_wallet_connect.rs
index 694b5e8af8e..9d10a12486b 100644
--- a/rust/tw_any_coin/tests/chains/binance/binance_wallet_connect.rs
+++ b/rust/tw_any_coin/tests/chains/binance/binance_wallet_connect.rs
@@ -20,15 +20,17 @@ const WC_SIGN_REQUEST_CASE_1: &str = include_str!("data/wc_sign_request_case_1.j
 fn test_binance_sign_wallet_connect_case_1() {
     let input = WCProto::ParseRequestInput {
         protocol: WCProto::Protocol::V2,
-        method: "cosmos_signAmino".into(),
+        method: WCProto::Method::CosmosSignAmino,
         payload: WC_SIGN_REQUEST_CASE_1.to_string().into(),
     };
 
-    let mut parser = WalletConnectRequestHelper::<Proto::SigningInput>::default();
+    let mut parser = WalletConnectRequestHelper::default();
+    let parsing_output = parser.parse(CoinType::Binance, &input);
 
-    let mut signing_input = parser
-        .parse(CoinType::Binance, &input)
-        .expect("Unexpected error on parsing WalletConnect request");
+    let mut signing_input = match parsing_output.signing_input_oneof {
+        WCProto::mod_ParseRequestOutput::OneOfsigning_input_oneof::binance(input) => input,
+        _ => unreachable!(),
+    };
 
     // bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2
     let expected_from_addr_key_hash = "40c2979694bbc961023d1d27be6fc4d21a9febe6";
diff --git a/rust/tw_cosmos_sdk/Cargo.toml b/rust/tw_cosmos_sdk/Cargo.toml
index 3e209b4f6df..ffd6abf455b 100644
--- a/rust/tw_cosmos_sdk/Cargo.toml
+++ b/rust/tw_cosmos_sdk/Cargo.toml
@@ -17,7 +17,7 @@ tw_hash = { path = "../tw_hash" }
 tw_keypair = { path = "../tw_keypair" }
 tw_memory = { path = "../tw_memory" }
 tw_misc = { path = "../tw_misc" }
-tw_number = { path = "../tw_number", features = ["serde"] }
+tw_number = { path = "../tw_number" }
 tw_proto = { path = "../tw_proto" }
 
 [dev-dependencies]
diff --git a/src/interface/TWWalletConnectRequest.cpp b/src/interface/TWWalletConnectRequest.cpp
new file mode 100644
index 00000000000..0630536f6b9
--- /dev/null
+++ b/src/interface/TWWalletConnectRequest.cpp
@@ -0,0 +1,24 @@
+// Copyright © 2017-2024 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.
+
+#include <TrustWalletCore/TWWalletConnectRequest.h>
+
+#include "rust/Wrapper.h"
+
+using namespace TW;
+
+TWData* _Nonnull TWWalletConnectRequestParse(enum TWCoinType coin, TWData* _Nonnull input) {
+    try {
+        const Data& inputData = *reinterpret_cast<const Data*>(input);
+        Rust::TWDataWrapper twInputData = inputData;
+
+        Rust::TWDataWrapper twOutputData = Rust::tw_wallet_connect_request_parse(static_cast<uint32_t>(coin), twInputData.get());
+        auto outputData = twOutputData.toDataOrDefault();
+        return TWDataCreateWithBytes(outputData.data(), outputData.size());
+    } catch (...) {
+        return nullptr;
+    }
+}
diff --git a/src/proto/WalletConnect.proto b/src/proto/WalletConnect.proto
index 0cf4726d8ab..37b8a9796c0 100644
--- a/src/proto/WalletConnect.proto
+++ b/src/proto/WalletConnect.proto
@@ -3,6 +3,7 @@ syntax = "proto3";
 package TW.WalletConnect.Proto;
 option java_package = "wallet.core.jni.proto";
 
+import "Binance.proto";
 import "Common.proto";
 
 // The transaction protocol may differ from version to version.
@@ -10,12 +11,19 @@ enum Protocol {
     V2 = 0;
 }
 
+// WalletConnect request method.
+enum Method {
+    Unknown = 0;
+    // cosmos_signAmino
+    CosmosSignAmino = 1;
+}
+
 message ParseRequestInput {
     // A protocol version.
     Protocol protocol = 1;
 
     // A signing method like "cosmos_signAmino" or "eth_signTransaction".
-    string method = 2;
+    Method method = 2;
 
     // Transaction payload to sign.
     // Basically, a JSON object.
@@ -23,12 +31,14 @@ message ParseRequestInput {
 }
 
 message ParseRequestOutput {
-    // Prepared unsigned transaction input needs to be parsed accordingly.
-    bytes signing_input = 1;
-
     // OK (=0) or other codes in case of error
-    Common.Proto.SigningError error = 2;
+    Common.Proto.SigningError error = 1;
 
     // error description in case of error
-    string error_message = 3;
+    string error_message = 2;
+
+    // Prepared unsigned transaction input, on the source chain. Some fields must be completed, and it has to be signed.
+    oneof signing_input_oneof {
+        Binance.Proto.SigningInput binance = 3;
+    }
 }
diff --git a/tests/chains/Binance/TWWalletConnectSigning.cpp b/tests/chains/Binance/TWWalletConnectSigning.cpp
new file mode 100644
index 00000000000..85161072322
--- /dev/null
+++ b/tests/chains/Binance/TWWalletConnectSigning.cpp
@@ -0,0 +1,54 @@
+// 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.
+
+#include "HexCoding.h"
+#include "proto/Binance.pb.h"
+#include "proto/WalletConnect.pb.h"
+#include "Coin.h"
+#include <TrustWalletCore/TWAnySigner.h>
+#include <TrustWalletCore/TWWalletConnectRequest.h>
+
+#include "TestUtilities.h"
+#include <gtest/gtest.h>
+
+namespace TW::Binance {
+
+TEST(TWWalletConnectSign, SendOrder) {
+    auto private_key = parse_hex("95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832");
+    const auto payload = R"({"signerAddress":"bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2","signDoc":{"account_number":"19","chain_id":"chain-bnb","memo":"","data":null,"msgs":[{"inputs":[{"address":"bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2","coins":[{"amount":1001000000,"denom":"BNB"}]}],"outputs":[{"address":"bnb13zeh6hs97d5eu2s5qerguhv8ewwue6u4ywa6yf","coins":[{"amount":1001000000,"denom":"BNB"}]}]}],"sequence":"23","source":"1"}})";
+
+    WalletConnect::Proto::ParseRequestInput parsingInput;
+    parsingInput.set_method(WalletConnect::Proto::Method::CosmosSignAmino);
+    parsingInput.set_payload(payload);
+
+    const auto parsinginputData = parsingInput.SerializeAsString();
+    const auto parsingInputDataPtr = WRAPD(TWDataCreateWithBytes(reinterpret_cast<const uint8_t *>(parsinginputData.c_str()), parsinginputData.size()));
+
+    const auto outputDataPtr = WRAPD(TWWalletConnectRequestParse(TWCoinTypeBinance, parsingInputDataPtr.get()));
+
+    WalletConnect::Proto::ParseRequestOutput parsingOutput;
+    parsingOutput.ParseFromArray(
+        TWDataBytes(outputDataPtr.get()),
+        static_cast<int>(TWDataSize(outputDataPtr.get()))
+    );
+
+    EXPECT_EQ(parsingOutput.error(), Common::Proto::SigningError::OK);
+
+    // Step 2: Set missing fields.
+    ASSERT_TRUE(parsingOutput.has_binance());
+    Proto::SigningInput signingInput = parsingOutput.binance();
+
+    signingInput.set_private_key(private_key.data(), private_key.size());
+
+    Proto::SigningOutput output;
+    ANY_SIGN(signingInput, TWCoinTypeBinance);
+
+    EXPECT_EQ(output.error(), Common::Proto::SigningError::OK);
+    EXPECT_EQ(hex(output.signature()), "3c24c784c6bbf99d54ffabb153edcb6d3c4a774936df5c72a5d32897256f8e062f320fb4753302fb0a96f08c475974d20edfd1a27bbeeda73587f58ddc958975");
+    EXPECT_EQ(output.signature_json(), R"({"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"Amo1kgCI2Yw4iMpoxT38k/RWRgJgbLuH8P5e5TPbOOUC"},"signature":"PCTHhMa7+Z1U/6uxU+3LbTxKd0k231xypdMolyVvjgYvMg+0dTMC+wqW8IxHWXTSDt/Ronu+7ac1h/WN3JWJdQ=="})");
+}
+
+} // namespace TW::Binance
diff --git a/tests/chains/Binance/TransactionCompilerTests.cpp b/tests/chains/Binance/TransactionCompilerTests.cpp
index 5b5a4e7ef19..efcb375ac30 100644
--- a/tests/chains/Binance/TransactionCompilerTests.cpp
+++ b/tests/chains/Binance/TransactionCompilerTests.cpp
@@ -82,7 +82,6 @@ TEST(BinanceCompiler, CompileWithSignatures) {
         "253cf264c69180ec31814929b5de62088c0c5a45e8a816d1208fc5366bb8b041781a6771248550d04094c3d7a5"
         "04f9e8310679";
     {
-        EXPECT_EQ(outputData.size(), 189ul);
         Binance::Proto::SigningOutput output;
         ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size()));