diff --git a/.github/workflows/codegen-v2.yml b/.github/workflows/codegen-v2.yml index 108b8e42448..f8ced68b019 100644 --- a/.github/workflows/codegen-v2.yml +++ b/.github/workflows/codegen-v2.yml @@ -13,6 +13,7 @@ on: jobs: test: runs-on: ubuntu-latest + if: github.event.pull_request.draft == false steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/binance/TestBinanceWalletConnectSigning.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/binance/TestBinanceWalletConnectSigning.kt new file mode 100644 index 00000000000..a66c2dc9776 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/binance/TestBinanceWalletConnectSigning.kt @@ -0,0 +1,46 @@ +package com.trustwallet.core.app.blockchains.binance + +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.toHexBytes +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.proto.Binance.SigningOutput +import wallet.core.jni.proto.WalletConnect +import wallet.core.jni.* +import wallet.core.jni.CoinType.BINANCE +import wallet.core.java.AnySigner +import wallet.core.jni.proto.Common + +class TestBinanceWalletConnectSigning { + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testSignBinanceTransactionFromWalletConnectRequest() { + // Step 1: Parse a signing request received through WalletConnect. + + val parsingInput = WalletConnect.ParseRequestInput.newBuilder().apply { + method = WalletConnect.Method.CosmosSignAmino + payload = "{\"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\"}}" + }.build() + + val parsingOutputBytes = WalletConnectRequest.parse(BINANCE, parsingInput.toByteArray()) + val parsingOutput = WalletConnect.ParseRequestOutput.parseFrom(parsingOutputBytes) + + assertEquals(parsingOutput.error, Common.SigningError.OK) + + // Step 2: Set missing fields. + + val signingInput = parsingOutput.binance.toBuilder().apply { + privateKey = ByteString.copyFrom("95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832".toHexBytes()) + }.build() + + // Step 3: Sign the transaction. + + val output = AnySigner.sign(signingInput, BINANCE, SigningOutput.parser()) + + assertEquals(output.error, Common.SigningError.OK) + assertEquals(output.signatureJson, "{\"pub_key\":{\"type\":\"tendermint/PubKeySecp256k1\",\"value\":\"Amo1kgCI2Yw4iMpoxT38k/RWRgJgbLuH8P5e5TPbOOUC\"},\"signature\":\"PCTHhMa7+Z1U/6uxU+3LbTxKd0k231xypdMolyVvjgYvMg+0dTMC+wqW8IxHWXTSDt/Ronu+7ac1h/WN3JWJdQ==\"}") + } +} \ No newline at end of file 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..a33b00322b5 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_connector::NoWalletConnector; 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 WalletConnector = NoWalletConnector; #[inline] fn parse_address( diff --git a/codegen-v2/src/tests/samples/class.output.swift b/codegen-v2/src/tests/samples/class.output.swift index 0b5b7fbb3a1..bd9024b04df 100644 --- a/codegen-v2/src/tests/samples/class.output.swift +++ b/codegen-v2/src/tests/samples/class.output.swift @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 diff --git a/codegen-v2/src/tests/samples/enum.output.swift b/codegen-v2/src/tests/samples/enum.output.swift index ebc0cd0b491..d8d9a3d3e72 100644 --- a/codegen-v2/src/tests/samples/enum.output.swift +++ b/codegen-v2/src/tests/samples/enum.output.swift @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 diff --git a/codegen-v2/src/tests/samples/enum_extension.output.swift b/codegen-v2/src/tests/samples/enum_extension.output.swift index afdd1d794cc..77a42623f8c 100644 --- a/codegen-v2/src/tests/samples/enum_extension.output.swift +++ b/codegen-v2/src/tests/samples/enum_extension.output.swift @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 diff --git a/codegen-v2/src/tests/samples/enum_private.output.swift b/codegen-v2/src/tests/samples/enum_private.output.swift index b1f199054fe..bf583ef1ce5 100644 --- a/codegen-v2/src/tests/samples/enum_private.output.swift +++ b/codegen-v2/src/tests/samples/enum_private.output.swift @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 diff --git a/codegen-v2/src/tests/samples/non-associated.output.swift b/codegen-v2/src/tests/samples/non-associated.output.swift index d4e8ee82477..89c0a9dbb63 100644 --- a/codegen-v2/src/tests/samples/non-associated.output.swift +++ b/codegen-v2/src/tests/samples/non-associated.output.swift @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 diff --git a/codegen-v2/src/tests/samples/optional.output.swift b/codegen-v2/src/tests/samples/optional.output.swift index e2cef180240..504a396ebd3 100644 --- a/codegen-v2/src/tests/samples/optional.output.swift +++ b/codegen-v2/src/tests/samples/optional.output.swift @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 diff --git a/codegen-v2/src/tests/samples/private_class.output.swift b/codegen-v2/src/tests/samples/private_class.output.swift index 0ce8618172f..31897047db4 100644 --- a/codegen-v2/src/tests/samples/private_class.output.swift +++ b/codegen-v2/src/tests/samples/private_class.output.swift @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 diff --git a/codegen-v2/src/tests/samples/struct.output.swift b/codegen-v2/src/tests/samples/struct.output.swift index 927ffd1eac1..9cf7a2fbd19 100644 --- a/codegen-v2/src/tests/samples/struct.output.swift +++ b/codegen-v2/src/tests/samples/struct.output.swift @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 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/address.rs b/rust/chains/tw_binance/src/address.rs index 9bc164e0395..6fecbd6f3ac 100644 --- a/rust/chains/tw_binance/src/address.rs +++ b/rust/chains/tw_binance/src/address.rs @@ -1,10 +1,10 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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. -use serde::Serialize; +use serde::{Deserialize, Serialize}; use std::fmt; use std::str::FromStr; use tw_bech32_address::bech32_prefix::Bech32Prefix; @@ -12,6 +12,7 @@ use tw_bech32_address::Bech32Address; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_cosmos_sdk::address::CosmosAddress; use tw_keypair::tw::PublicKey; use tw_memory::Data; @@ -21,7 +22,7 @@ const BNB_KNOWN_HRPS: [&str; 2] = [ "bca", ]; -#[derive(Serialize)] +#[derive(Deserialize, PartialEq, Serialize)] pub struct BinanceAddress(Bech32Address); impl CoinAddress for BinanceAddress { @@ -31,6 +32,16 @@ impl CoinAddress for BinanceAddress { } } +impl CosmosAddress for BinanceAddress { + fn from_str_with_coin(coin: &dyn CoinContext, addr: &str) -> AddressResult + where + Self: Sized, + { + let prefix = None; + Self::from_str_with_coin_and_prefix(coin, addr.to_string(), prefix) + } +} + impl BinanceAddress { pub const VALIDATOR_HRP: &'static str = "bva"; diff --git a/rust/chains/tw_binance/src/amino.rs b/rust/chains/tw_binance/src/amino.rs index 0c891586b9e..048b0bdedc1 100644 --- a/rust/chains/tw_binance/src/amino.rs +++ b/rust/chains/tw_binance/src/amino.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 diff --git a/rust/chains/tw_binance/src/compiler.rs b/rust/chains/tw_binance/src/compiler.rs index 1ee1e23dd15..0a289ec1c3f 100644 --- a/rust/chains/tw_binance/src/compiler.rs +++ b/rust/chains/tw_binance/src/compiler.rs @@ -1,9 +1,10 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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. +use crate::context::BinanceContext; use crate::modules::preimager::{JsonPreimager, JsonTxPreimage}; use crate::modules::serializer::BinanceAminoSerializer; use crate::modules::tx_builder::TxBuilder; @@ -12,9 +13,12 @@ use crate::transaction::SignerInfo; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; use tw_coin_entry::common::compile_input::SingleSignaturePubkey; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_coin_entry::signing_output_error; -use tw_keypair::ecdsa::secp256k1; +use tw_cosmos_sdk::modules::serializer::json_serializer::JsonSerializer; +use tw_cosmos_sdk::public_key::secp256k1::Secp256PublicKey; +use tw_cosmos_sdk::public_key::CosmosPublicKey; +use tw_misc::traits::ToBytesVec; use tw_proto::Binance::Proto; use tw_proto::TxCompiler::Proto as CompilerProto; @@ -69,7 +73,13 @@ impl BinanceCompiler { public_key, } = SingleSignaturePubkey::from_sign_pubkey_list(signatures, public_keys)?; let signature = BinanceSignature::try_from(signature.as_slice())?; - let public_key = secp256k1::PublicKey::try_from(public_key.as_slice())?; + let public_key = Secp256PublicKey::from_bytes(coin, public_key.as_slice())?; + + let signature_bytes = signature.to_vec(); + let signature_json = JsonSerializer::::serialize_signature( + &public_key, + signature_bytes.clone(), + ); let unsigned_tx = TxBuilder::unsigned_tx_from_proto(coin, &input)?; let signed_tx = unsigned_tx.into_signed(SignerInfo { @@ -79,8 +89,12 @@ impl BinanceCompiler { let encoded_tx = BinanceAminoSerializer::serialize_signed_tx(&signed_tx)?; + let signature_json = serde_json::to_string(&signature_json) + .map_err(|_| SigningError(SigningErrorType::Error_internal))?; Ok(Proto::SigningOutput { encoded: encoded_tx.into(), + signature: signature_bytes.into(), + signature_json: signature_json.into(), ..Proto::SigningOutput::default() }) } diff --git a/rust/chains/tw_binance/src/context.rs b/rust/chains/tw_binance/src/context.rs new file mode 100644 index 00000000000..a70977bcd46 --- /dev/null +++ b/rust/chains/tw_binance/src/context.rs @@ -0,0 +1,23 @@ +// 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. + +use crate::address::BinanceAddress; +use tw_cosmos_sdk::context::CosmosContext; +use tw_cosmos_sdk::hasher::sha256_hasher::Sha256Hasher; +use tw_cosmos_sdk::private_key::secp256k1::Secp256PrivateKey; +use tw_cosmos_sdk::public_key::secp256k1::Secp256PublicKey; +use tw_cosmos_sdk::signature::secp256k1::Secp256k1Signature; + +pub struct BinanceContext; + +impl CosmosContext for BinanceContext { + type Address = BinanceAddress; + /// Binance Beacon chain uses `sha256` hash. + type TxHasher = Sha256Hasher; + type PrivateKey = Secp256PrivateKey; + type PublicKey = Secp256PublicKey; + type Signature = Secp256k1Signature; +} diff --git a/rust/chains/tw_binance/src/entry.rs b/rust/chains/tw_binance/src/entry.rs index bda948c9900..4589e2d31a8 100644 --- a/rust/chains/tw_binance/src/entry.rs +++ b/rust/chains/tw_binance/src/entry.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 @@ -6,6 +6,7 @@ use crate::address::BinanceAddress; use crate::compiler::BinanceCompiler; +use crate::modules::wallet_connect::connector::BinanceWalletConnector; use crate::signer::BinanceSigner; use std::str::FromStr; use tw_bech32_address::bech32_prefix::Bech32Prefix; @@ -33,6 +34,7 @@ impl CoinEntry for BinanceEntry { type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; + type WalletConnector = BinanceWalletConnector; #[inline] fn parse_address( @@ -88,4 +90,9 @@ impl CoinEntry for BinanceEntry { ) -> Self::SigningOutput { BinanceCompiler::compile(coin, input, signatures, public_keys) } + + #[inline] + fn wallet_connector(&self) -> Option { + Some(BinanceWalletConnector) + } } diff --git a/rust/chains/tw_binance/src/lib.rs b/rust/chains/tw_binance/src/lib.rs index 0bd07a91bbf..606d6f3000d 100644 --- a/rust/chains/tw_binance/src/lib.rs +++ b/rust/chains/tw_binance/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 @@ -7,6 +7,7 @@ pub mod address; pub mod amino; pub mod compiler; +pub mod context; pub mod entry; pub mod modules; pub mod signature; diff --git a/rust/chains/tw_binance/src/modules/mod.rs b/rust/chains/tw_binance/src/modules/mod.rs index f27e84a2506..5300755a36f 100644 --- a/rust/chains/tw_binance/src/modules/mod.rs +++ b/rust/chains/tw_binance/src/modules/mod.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 @@ -7,3 +7,4 @@ pub mod preimager; pub mod serializer; pub mod tx_builder; +pub mod wallet_connect; diff --git a/rust/chains/tw_binance/src/modules/preimager.rs b/rust/chains/tw_binance/src/modules/preimager.rs index 18e2d47b5b5..cbb4969d139 100644 --- a/rust/chains/tw_binance/src/modules/preimager.rs +++ b/rust/chains/tw_binance/src/modules/preimager.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 diff --git a/rust/chains/tw_binance/src/modules/serializer.rs b/rust/chains/tw_binance/src/modules/serializer.rs index 4716ffd9ab9..d77e85d05f3 100644 --- a/rust/chains/tw_binance/src/modules/serializer.rs +++ b/rust/chains/tw_binance/src/modules/serializer.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 @@ -8,7 +8,7 @@ use crate::amino::AminoEncoder; use crate::transaction::SignedTransaction; use std::borrow::Cow; use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; -use tw_hash::H264; +use tw_cosmos_sdk::public_key::CosmosPublicKey; use tw_memory::Data; use tw_misc::traits::ToBytesVec; use tw_proto::serialize; @@ -27,7 +27,7 @@ impl BinanceAminoSerializer { .unsigned .msgs .iter() - .map(|msg| msg.to_amino_protobuf().map(Cow::from)) + .map(|msg| msg.as_ref().to_amino_protobuf().map(Cow::from)) .collect::>>()?; let signature = Self::serialize_signature(tx)?; @@ -43,7 +43,7 @@ impl BinanceAminoSerializer { .encode_size_prefixed()?) } - pub fn serialize_public_key(public_key: H264) -> Data { + pub fn serialize_public_key(public_key: Data) -> Data { let public_key_len = public_key.len() as u8; AminoEncoder::new(&PUBLIC_KEY_PREFIX) // Push the length of the public key. @@ -54,7 +54,7 @@ impl BinanceAminoSerializer { pub fn serialize_signature(signed: &SignedTransaction) -> SigningResult { let sign_msg = Proto::Signature { - pub_key: Self::serialize_public_key(signed.signer.public_key.compressed()).into(), + pub_key: Self::serialize_public_key(signed.signer.public_key.to_bytes()).into(), signature: signed.signer.signature.to_vec().into(), account_number: signed.unsigned.account_number, sequence: signed.unsigned.sequence, diff --git a/rust/chains/tw_binance/src/modules/tx_builder.rs b/rust/chains/tw_binance/src/modules/tx_builder.rs index a355df60dc4..e5b47fbedf5 100644 --- a/rust/chains/tw_binance/src/modules/tx_builder.rs +++ b/rust/chains/tw_binance/src/modules/tx_builder.rs @@ -1,17 +1,14 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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. -use crate::address::BinanceAddress; -use crate::transaction::message::Token; -use crate::transaction::message::{BinanceMessage, BinanceMessageBox}; +use crate::transaction::message::{BinanceMessageEnum, TWBinanceProto}; use crate::transaction::UnsignedTransaction; +use std::borrow::Cow; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; -use tw_evm::address::Address as EthereumAddress; -use tw_hash::H160; use tw_proto::Binance::Proto; pub struct TxBuilder; @@ -21,8 +18,7 @@ impl TxBuilder { coin: &dyn CoinContext, input: &Proto::SigningInput<'_>, ) -> SigningResult { - let msg = Self::msg_from_proto(coin, input)?; - + let msg = BinanceMessageEnum::from_tw_proto(coin, &input.order_oneof)?; Ok(UnsignedTransaction { account_number: input.account_number, chain_id: input.chain_id.to_string(), @@ -34,468 +30,26 @@ impl TxBuilder { }) } - pub fn msg_from_proto( - coin: &dyn CoinContext, - input: &Proto::SigningInput<'_>, - ) -> SigningResult { - use Proto::mod_SigningInput::OneOforder_oneof as OrderEnum; - - match input.order_oneof { - OrderEnum::trade_order(ref new_order) => Self::trade_order_from_proto(coin, new_order), - OrderEnum::cancel_trade_order(ref cancel_order) => { - Self::cancel_order_from_proto(coin, cancel_order) - }, - OrderEnum::send_order(ref send_order) => Self::send_order_from_proto(coin, send_order), - OrderEnum::freeze_order(ref freeze_order) => { - Self::freeze_order_from_proto(coin, freeze_order) - }, - OrderEnum::unfreeze_order(ref unfreeze_order) => { - Self::unfreeze_order_from_proto(coin, unfreeze_order) - }, - OrderEnum::htlt_order(ref htlt_order) => Self::htlt_order_from_proto(coin, htlt_order), - OrderEnum::depositHTLT_order(ref deposit_htlt) => { - Self::deposit_htlt_order_from_proto(coin, deposit_htlt) - }, - OrderEnum::claimHTLT_order(ref claim_htlt) => { - Self::claim_htlt_order_from_proto(coin, claim_htlt) - }, - OrderEnum::refundHTLT_order(ref refund_htlt) => { - Self::refund_htlt_order_from_proto(coin, refund_htlt) - }, - OrderEnum::issue_order(ref issue_order) => { - Self::issue_order_from_proto(coin, issue_order) - }, - OrderEnum::mint_order(ref mint_order) => Self::mint_order_from_proto(coin, mint_order), - OrderEnum::burn_order(ref burn_order) => Self::burn_order_from_proto(coin, burn_order), - OrderEnum::transfer_out_order(ref transfer_out) => { - Self::transfer_out_order_from_proto(coin, transfer_out) - }, - OrderEnum::side_delegate_order(ref side_delegate) => { - Self::side_delegate_order_from_proto(coin, side_delegate) - }, - OrderEnum::side_redelegate_order(ref side_redelegate) => { - Self::side_redelegate_order_from_proto(coin, side_redelegate) - }, - OrderEnum::side_undelegate_order(ref side_undelegate) => { - Self::side_undelegate_order_from_proto(coin, side_undelegate) - }, - OrderEnum::time_lock_order(ref time_lock) => { - Self::time_lock_order_from_proto(coin, time_lock) - }, - OrderEnum::time_relock_order(ref time_relock) => { - Self::time_relock_order_from_proto(coin, time_relock) - }, - OrderEnum::time_unlock_order(ref time_unlock) => { - Self::time_unlock_order_from_proto(coin, time_unlock) - }, - OrderEnum::None => Err(SigningError(SigningErrorType::Error_invalid_params)), - } - } - - pub fn trade_order_from_proto( - coin: &dyn CoinContext, - new_order: &Proto::TradeOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::trade_order::NewTradeOrder; - use crate::transaction::message::trade_order::OrderType; - - let order_type = OrderType::from_repr(new_order.ordertype) - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; - let sender = BinanceAddress::from_key_hash_with_coin(coin, new_order.sender.to_vec())?; - - Ok(NewTradeOrder { - id: new_order.id.to_string(), - order_type, - price: new_order.price, - quantity: new_order.quantity, - sender, - side: new_order.side, - symbol: new_order.symbol.to_string(), - time_in_force: new_order.timeinforce, - } - .into_boxed()) - } - - pub fn cancel_order_from_proto( - coin: &dyn CoinContext, - cancel_order: &Proto::CancelTradeOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::trade_order::CancelTradeOrder; - - let sender = BinanceAddress::from_key_hash_with_coin(coin, cancel_order.sender.to_vec())?; - Ok(CancelTradeOrder { - sender, - symbol: cancel_order.symbol.to_string(), - refid: cancel_order.refid.to_string(), - } - .into_boxed()) - } - - pub fn send_order_from_proto( - coin: &dyn CoinContext, - send_order: &Proto::SendOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::send_order::{InOut, SendOrder}; - - fn in_out_from_proto( - coin: &dyn CoinContext, - address_key_hash: &[u8], - coins: &[Proto::mod_SendOrder::Token], - ) -> SigningResult { - let address = BinanceAddress::from_key_hash_with_coin(coin, address_key_hash.to_vec())?; - let coins = coins.iter().map(TxBuilder::token_from_proto).collect(); - - Ok(InOut { address, coins }) - } - - let inputs = send_order - .inputs - .iter() - .map(|input| in_out_from_proto(coin, &input.address, &input.coins)) - .collect::>>()?; - - let outputs = send_order - .outputs - .iter() - .map(|output| in_out_from_proto(coin, &output.address, &output.coins)) - .collect::>>()?; - - Ok(SendOrder { inputs, outputs }.into_boxed()) - } - - pub fn freeze_order_from_proto( - coin: &dyn CoinContext, - freeze_order: &Proto::TokenFreezeOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::token_order::TokenFreezeOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, freeze_order.from.to_vec())?; - Ok(TokenFreezeOrder { - from, - symbol: freeze_order.symbol.to_string(), - amount: freeze_order.amount, - } - .into_boxed()) - } - - pub fn unfreeze_order_from_proto( - coin: &dyn CoinContext, - unfreeze_order: &Proto::TokenUnfreezeOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::token_order::TokenUnfreezeOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, unfreeze_order.from.to_vec())?; - Ok(TokenUnfreezeOrder { - from, - symbol: unfreeze_order.symbol.to_string(), - amount: unfreeze_order.amount, - } - .into_boxed()) - } - - pub fn htlt_order_from_proto( - coin: &dyn CoinContext, - htlt_order: &Proto::HTLTOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::htlt_order::HTLTOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, htlt_order.from.to_vec())?; - let to = BinanceAddress::from_key_hash_with_coin(coin, htlt_order.to.to_vec())?; - - let amount = htlt_order - .amount - .iter() - .map(Self::token_from_proto) - .collect(); - - Ok(HTLTOrder { - from, - to, - recipient_other_chain: htlt_order.recipient_other_chain.to_string(), - sender_other_chain: htlt_order.sender_other_chain.to_string(), - random_number_hash: htlt_order.random_number_hash.to_vec(), - timestamp: htlt_order.timestamp, - amount, - expected_income: htlt_order.expected_income.to_string(), - height_span: htlt_order.height_span, - cross_chain: htlt_order.cross_chain, - } - .into_boxed()) - } - - pub fn deposit_htlt_order_from_proto( - coin: &dyn CoinContext, - deposit_htlt: &Proto::DepositHTLTOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::htlt_order::DepositHTLTOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, deposit_htlt.from.to_vec())?; - - let amount = deposit_htlt - .amount - .iter() - .map(Self::token_from_proto) - .collect(); - - Ok(DepositHTLTOrder { - from, - amount, - swap_id: deposit_htlt.swap_id.to_vec(), - } - .into_boxed()) - } - - pub fn claim_htlt_order_from_proto( - coin: &dyn CoinContext, - claim_htlt: &Proto::ClaimHTLOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::htlt_order::ClaimHTLTOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, claim_htlt.from.to_vec())?; - - Ok(ClaimHTLTOrder { - from, - swap_id: claim_htlt.swap_id.to_vec(), - random_number: claim_htlt.random_number.to_vec(), - } - .into_boxed()) - } - - pub fn refund_htlt_order_from_proto( - coin: &dyn CoinContext, - refund_htlt: &Proto::RefundHTLTOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::htlt_order::RefundHTLTOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, refund_htlt.from.to_vec())?; - let swap_id = refund_htlt.swap_id.to_vec(); - - Ok(RefundHTLTOrder { from, swap_id }.into_boxed()) - } - - pub fn issue_order_from_proto( - coin: &dyn CoinContext, - issue_order: &Proto::TokenIssueOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::token_order::TokenIssueOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, issue_order.from.to_vec())?; - Ok(TokenIssueOrder { - from, - name: issue_order.name.to_string(), - symbol: issue_order.symbol.to_string(), - total_supply: issue_order.total_supply, - mintable: issue_order.mintable, - } - .into_boxed()) - } - - pub fn mint_order_from_proto( - coin: &dyn CoinContext, - mint_order: &Proto::TokenMintOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::token_order::TokenMintOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, mint_order.from.to_vec())?; - Ok(TokenMintOrder { - from, - symbol: mint_order.symbol.to_string(), - amount: mint_order.amount, - } - .into_boxed()) - } - - pub fn burn_order_from_proto( - coin: &dyn CoinContext, - burn_order: &Proto::TokenBurnOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::token_order::TokenBurnOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, burn_order.from.to_vec())?; - Ok(TokenBurnOrder { - from, - symbol: burn_order.symbol.to_string(), - amount: burn_order.amount, - } - .into_boxed()) - } - - pub fn transfer_out_order_from_proto( - coin: &dyn CoinContext, - transfer_out: &Proto::TransferOut<'_>, - ) -> SigningResult { - use crate::transaction::message::tranfer_out_order::TransferOutOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, transfer_out.from.to_vec())?; - - let to_bytes = H160::try_from(transfer_out.to.as_ref()) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_address))?; - let to = EthereumAddress::from_bytes(to_bytes); - - let amount_proto = transfer_out - .amount - .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; - - Ok(TransferOutOrder { - from, - to, - amount: Self::token_from_proto(amount_proto), - expire_time: transfer_out.expire_time, - } - .into_boxed()) - } - - pub fn side_delegate_order_from_proto( - coin: &dyn CoinContext, - side_delegate: &Proto::SideChainDelegate<'_>, - ) -> SigningResult { - use crate::transaction::message::side_chain_delegate::SideDelegateOrder; - - let delegator_addr = - BinanceAddress::from_key_hash_with_coin(coin, side_delegate.delegator_addr.to_vec())?; - let validator_addr = - BinanceAddress::new_validator_addr(side_delegate.validator_addr.to_vec())?; - - let delegation = side_delegate - .delegation - .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; - - Ok(SideDelegateOrder { - delegator_addr, - validator_addr, - delegation: Self::token_from_proto(delegation), - side_chain_id: side_delegate.chain_id.to_string(), - } - .into_boxed()) - } - - pub fn side_redelegate_order_from_proto( - coin: &dyn CoinContext, - side_redelegate: &Proto::SideChainRedelegate<'_>, - ) -> SigningResult { - use crate::transaction::message::side_chain_delegate::SideRedelegateOrder; - - let delegator_addr = - BinanceAddress::from_key_hash_with_coin(coin, side_redelegate.delegator_addr.to_vec())?; - let validator_src_addr = - BinanceAddress::new_validator_addr(side_redelegate.validator_src_addr.to_vec())?; - let validator_dst_addr = - BinanceAddress::new_validator_addr(side_redelegate.validator_dst_addr.to_vec())?; - - let amount = side_redelegate - .amount - .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; - - Ok(SideRedelegateOrder { - delegator_addr, - validator_src_addr, - validator_dst_addr, - amount: Self::token_from_proto(amount), - side_chain_id: side_redelegate.chain_id.to_string(), - } - .into_boxed()) - } - - pub fn side_undelegate_order_from_proto( - coin: &dyn CoinContext, - side_undelegate: &Proto::SideChainUndelegate<'_>, - ) -> SigningResult { - use crate::transaction::message::side_chain_delegate::SideUndelegateOrder; - - let delegator_addr = - BinanceAddress::from_key_hash_with_coin(coin, side_undelegate.delegator_addr.to_vec())?; - let validator_addr = - BinanceAddress::new_validator_addr(side_undelegate.validator_addr.to_vec())?; - - let amount = side_undelegate - .amount - .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; - - Ok(SideUndelegateOrder { - delegator_addr, - validator_addr, - amount: Self::token_from_proto(amount), - side_chain_id: side_undelegate.chain_id.to_string(), - } - .into_boxed()) - } - - pub fn time_lock_order_from_proto( - coin: &dyn CoinContext, - time_lock: &Proto::TimeLockOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::time_lock_order::TimeLockOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, time_lock.from_address.to_vec())?; - let amount = time_lock - .amount - .iter() - .map(Self::token_from_proto) - .collect(); - - Ok(TimeLockOrder { - from, - description: time_lock.description.to_string(), - amount, - lock_time: time_lock.lock_time, - } - .into_boxed()) - } - - pub fn time_relock_order_from_proto( - coin: &dyn CoinContext, - time_relock: &Proto::TimeRelockOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::time_lock_order::TimeRelockOrder; - - let from = - BinanceAddress::from_key_hash_with_coin(coin, time_relock.from_address.to_vec())?; - - let amount = if time_relock.amount.is_empty() { - None - } else { - Some( - time_relock - .amount - .iter() - .map(Self::token_from_proto) - .collect(), - ) - }; - - Ok(TimeRelockOrder { - from, - time_lock_id: time_relock.id, - description: time_relock.description.to_string(), - amount, - lock_time: time_relock.lock_time, - } - .into_boxed()) - } - - pub fn time_unlock_order_from_proto( - coin: &dyn CoinContext, - time_unlock: &Proto::TimeUnlockOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::time_lock_order::TimeUnlockOrder; - - let from = - BinanceAddress::from_key_hash_with_coin(coin, time_unlock.from_address.to_vec())?; - Ok(TimeUnlockOrder { - from, - time_lock_id: time_unlock.id, - } - .into_boxed()) - } - - fn token_from_proto(token: &Proto::mod_SendOrder::Token) -> Token { - Token { - denom: token.denom.to_string(), - amount: token.amount, - } + pub fn unsigned_tx_to_proto( + unsigned: &UnsignedTransaction, + ) -> SigningResult> { + if unsigned.msgs.len() != 1 { + return Err(SigningError(SigningErrorType::Error_invalid_params)); + } + let msg = unsigned + .msgs + .first() + .expect("There should be exactly one message") + .to_tw_proto(); + + Ok(Proto::SigningInput { + chain_id: unsigned.chain_id.clone().into(), + account_number: unsigned.account_number, + sequence: unsigned.sequence, + source: unsigned.source, + memo: unsigned.memo.clone().into(), + private_key: Cow::default(), + order_oneof: msg, + }) } } diff --git a/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs b/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs new file mode 100644 index 00000000000..893b408488b --- /dev/null +++ b/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs @@ -0,0 +1,56 @@ +// 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. + +use crate::modules::tx_builder::TxBuilder; +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_connector::WalletConnector; +use tw_coin_entry::signing_output_error; +use tw_proto::WalletConnect::Proto::{ + self as WCProto, mod_ParseRequestOutput::OneOfsigning_input_oneof as SigningInputEnum, +}; + +pub struct BinanceWalletConnector; + +impl WalletConnector for BinanceWalletConnector { + fn parse_request( + &self, + coin: &dyn CoinContext, + request: WCProto::ParseRequestInput<'_>, + ) -> WCProto::ParseRequestOutput<'static> { + Self::parse_request_impl(coin, request) + .unwrap_or_else(|e| signing_output_error!(WCProto::ParseRequestOutput, e)) + } +} + +impl BinanceWalletConnector { + fn parse_request_impl( + coin: &dyn CoinContext, + request: WCProto::ParseRequestInput<'_>, + ) -> SigningResult> { + match request.method { + WCProto::Method::CosmosSignAmino => Self::parse_sign_amino_request(coin, request), + _ => Err(SigningError(SigningErrorType::Error_not_supported)), + } + } + + pub fn parse_sign_amino_request( + _coin: &dyn CoinContext, + request: WCProto::ParseRequestInput<'_>, + ) -> SigningResult> { + let amino_req: SignAminoRequest = serde_json::from_str(&request.payload) + .map_err(|_| SigningError(SigningErrorType::Error_input_parse))?; + + // Parse a `SigningInput` from the given `signDoc`. + let signing_input = TxBuilder::unsigned_tx_to_proto(&amino_req.sign_doc)?; + + Ok(WCProto::ParseRequestOutput { + signing_input_oneof: SigningInputEnum::binance(signing_input), + ..WCProto::ParseRequestOutput::default() + }) + } +} diff --git a/rust/chains/tw_binance/src/modules/wallet_connect/mod.rs b/rust/chains/tw_binance/src/modules/wallet_connect/mod.rs new file mode 100644 index 00000000000..9e2233feaa6 --- /dev/null +++ b/rust/chains/tw_binance/src/modules/wallet_connect/mod.rs @@ -0,0 +1,8 @@ +// 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. + +pub mod connector; +pub mod types; diff --git a/rust/chains/tw_binance/src/modules/wallet_connect/types.rs b/rust/chains/tw_binance/src/modules/wallet_connect/types.rs new file mode 100644 index 00000000000..e2b2f599b6e --- /dev/null +++ b/rust/chains/tw_binance/src/modules/wallet_connect/types.rs @@ -0,0 +1,14 @@ +// 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. + +use crate::transaction::UnsignedTransaction; +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct SignAminoRequest { + #[serde(rename = "signDoc")] + pub sign_doc: UnsignedTransaction, +} diff --git a/rust/chains/tw_binance/src/signature.rs b/rust/chains/tw_binance/src/signature.rs index 5e58a2967a1..eda4adbf4c7 100644 --- a/rust/chains/tw_binance/src/signature.rs +++ b/rust/chains/tw_binance/src/signature.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 diff --git a/rust/chains/tw_binance/src/signer.rs b/rust/chains/tw_binance/src/signer.rs index b8b1c02739e..8913a9c5e6a 100644 --- a/rust/chains/tw_binance/src/signer.rs +++ b/rust/chains/tw_binance/src/signer.rs @@ -1,19 +1,23 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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. +use crate::context::BinanceContext; use crate::modules::preimager::{JsonPreimager, JsonTxPreimage}; use crate::modules::serializer::BinanceAminoSerializer; use crate::modules::tx_builder::TxBuilder; use crate::signature::BinanceSignature; use crate::transaction::SignerInfo; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_coin_entry::signing_output_error; +use tw_cosmos_sdk::modules::serializer::json_serializer::JsonSerializer; +use tw_cosmos_sdk::public_key::secp256k1::Secp256PublicKey; use tw_keypair::ecdsa::secp256k1; use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; +use tw_misc::traits::ToBytesVec; use tw_proto::Binance::Proto; pub struct BinanceSigner; @@ -37,7 +41,13 @@ impl BinanceSigner { let key_pair = secp256k1::KeyPair::try_from(input.private_key.as_ref())?; let signature = BinanceSignature::from(key_pair.sign(tx_hash)?); - let public_key = key_pair.public().clone(); + let public_key = Secp256PublicKey::from_secp256k1_public_key(coin, key_pair.public())?; + + let signature_bytes = signature.to_vec(); + let signature_json = JsonSerializer::::serialize_signature( + &public_key, + signature_bytes.clone(), + ); let signed_tx = unsigned_tx.into_signed(SignerInfo { public_key, @@ -45,8 +55,12 @@ impl BinanceSigner { }); let encoded_tx = BinanceAminoSerializer::serialize_signed_tx(&signed_tx)?; + let signature_json = serde_json::to_string(&signature_json) + .map_err(|_| SigningError(SigningErrorType::Error_internal))?; Ok(Proto::SigningOutput { encoded: encoded_tx.into(), + signature: signature_bytes.into(), + signature_json: signature_json.into(), ..Proto::SigningOutput::default() }) } diff --git a/rust/chains/tw_binance/src/transaction/message/htlt_order.rs b/rust/chains/tw_binance/src/transaction/message/htlt_order.rs index c0969bb305d..2d5677ccaa5 100644 --- a/rust/chains/tw_binance/src/transaction/message/htlt_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/htlt_order.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 @@ -6,28 +6,28 @@ use crate::address::BinanceAddress; use crate::amino::AminoEncoder; -use crate::transaction::message::{message_to_json, BinanceMessage, Token}; -use serde::Serialize; -use serde_json::Value as Json; +use crate::transaction::message::{BinanceMessage, TWBinanceProto, Token}; +use serde::{Deserialize, Serialize}; +use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; use tw_coin_entry::error::SigningResult; use tw_encoding::hex::as_hex; use tw_memory::Data; use tw_proto::Binance::Proto; -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct HTLTOrder { + pub amount: Vec, + pub cross_chain: bool, + pub expected_income: String, pub from: BinanceAddress, - pub to: BinanceAddress, - pub recipient_other_chain: String, - pub sender_other_chain: String, + pub height_span: i64, #[serde(serialize_with = "as_hex")] pub random_number_hash: Data, + pub recipient_other_chain: String, + pub sender_other_chain: String, pub timestamp: i64, - pub amount: Vec, - pub expected_income: String, - pub height_span: i64, - pub cross_chain: bool, + pub to: BinanceAddress, } impl HTLTOrder { @@ -36,34 +36,56 @@ impl HTLTOrder { } impl BinanceMessage for HTLTOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::HTLTOrder { +impl TWBinanceProto for HTLTOrder { + type Proto<'a> = Proto::HTLTOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; + let to = BinanceAddress::from_key_hash_with_coin(coin, msg.to.to_vec())?; + + let amount = msg.amount.iter().map(Token::from_tw_proto).collect(); + + Ok(HTLTOrder { + from, + to, + recipient_other_chain: msg.recipient_other_chain.to_string(), + sender_other_chain: msg.sender_other_chain.to_string(), + random_number_hash: msg.random_number_hash.to_vec(), + timestamp: msg.timestamp, + amount, + expected_income: msg.expected_income.to_string(), + height_span: msg.height_span, + cross_chain: msg.cross_chain, + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::HTLTOrder { from: self.from.data().into(), to: self.to.data().into(), recipient_other_chain: self.recipient_other_chain.clone().into(), sender_other_chain: self.sender_other_chain.clone().into(), random_number_hash: self.random_number_hash.clone().into(), timestamp: self.timestamp, - amount: self.amount.iter().map(Token::to_proto).collect(), + amount: self.amount.iter().map(Token::to_tw_proto).collect(), expected_income: self.expected_income.clone().into(), height_span: self.height_span, cross_chain: self.cross_chain, - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct DepositHTLTOrder { - pub from: BinanceAddress, pub amount: Vec, + pub from: BinanceAddress, #[serde(serialize_with = "as_hex")] pub swap_id: Data, } @@ -74,30 +96,43 @@ impl DepositHTLTOrder { } impl BinanceMessage for DepositHTLTOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::DepositHTLTOrder { +impl TWBinanceProto for DepositHTLTOrder { + type Proto<'a> = Proto::DepositHTLTOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; + let amount = msg.amount.iter().map(Token::from_tw_proto).collect(); + + Ok(DepositHTLTOrder { + from, + amount, + swap_id: msg.swap_id.to_vec(), + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::DepositHTLTOrder { from: self.from.data().into(), - amount: self.amount.iter().map(Token::to_proto).collect(), + amount: self.amount.iter().map(Token::to_tw_proto).collect(), swap_id: self.swap_id.clone().into(), - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct ClaimHTLTOrder { pub from: BinanceAddress, #[serde(serialize_with = "as_hex")] - pub swap_id: Data, - #[serde(serialize_with = "as_hex")] pub random_number: Data, + #[serde(serialize_with = "as_hex")] + pub swap_id: Data, } impl ClaimHTLTOrder { @@ -106,24 +141,36 @@ impl ClaimHTLTOrder { } impl BinanceMessage for ClaimHTLTOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::ClaimHTLOrder { +impl TWBinanceProto for ClaimHTLTOrder { + type Proto<'a> = Proto::ClaimHTLOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; + + Ok(ClaimHTLTOrder { + from, + swap_id: msg.swap_id.to_vec(), + random_number: msg.random_number.to_vec(), + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::ClaimHTLOrder { from: self.from.data().into(), swap_id: self.swap_id.clone().into(), random_number: self.random_number.clone().into(), - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct RefundHTLTOrder { pub from: BinanceAddress, #[serde(serialize_with = "as_hex")] @@ -135,19 +182,28 @@ impl RefundHTLTOrder { pub const PREFIX: [u8; 4] = [0x34, 0x54, 0xA2, 0x7C]; } -impl BinanceMessage for RefundHTLTOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) +impl TWBinanceProto for RefundHTLTOrder { + type Proto<'a> = Proto::RefundHTLTOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; + let swap_id = msg.swap_id.to_vec(); + + Ok(RefundHTLTOrder { from, swap_id }) } - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::RefundHTLTOrder { + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::RefundHTLTOrder { from: self.from.data().into(), swap_id: self.swap_id.clone().into(), - }; + } + } +} +impl BinanceMessage for RefundHTLTOrder { + fn to_amino_protobuf(&self) -> SigningResult { Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? + .extend_with_msg(&self.to_tw_proto())? .encode()) } } diff --git a/rust/chains/tw_binance/src/transaction/message/mod.rs b/rust/chains/tw_binance/src/transaction/message/mod.rs index 6729a4276e4..78518de6b15 100644 --- a/rust/chains/tw_binance/src/transaction/message/mod.rs +++ b/rust/chains/tw_binance/src/transaction/message/mod.rs @@ -1,15 +1,14 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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. -use serde::ser::Error as SerError; -use serde::{Serialize, Serializer}; -use serde_json::Value as Json; +use serde::{Deserialize, Serialize, Serializer}; +use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_memory::Data; -use tw_proto::Binance::Proto; +use tw_proto::Binance::Proto::{self, mod_SigningInput::OneOforder_oneof as BinanceMessageProto}; pub mod htlt_order; pub mod send_order; @@ -19,66 +18,249 @@ pub mod token_order; pub mod trade_order; pub mod tranfer_out_order; -pub type BinanceMessageBox = Box; - pub trait BinanceMessage { - fn into_boxed(self) -> BinanceMessageBox - where - Self: 'static + Sized, - { - Box::new(self) - } + fn to_amino_protobuf(&self) -> SigningResult; +} - fn to_json(&self) -> SigningResult; +/// A Binance message represented as a Trust Wallet Core Protobuf message. +pub trait TWBinanceProto: Sized { + type Proto<'a>; - fn to_amino_protobuf(&self) -> SigningResult; + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult; + + fn to_tw_proto(&self) -> Self::Proto<'static>; } -impl Serialize for dyn BinanceMessage { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - self.to_json() - .map_err(|e| SerError::custom(format!("{e:?}")))? - .serialize(serializer) +/// Please note that some of the fields are typped such as `SideDelegateOrder`. +#[derive(Deserialize, Serialize)] +#[serde(untagged)] +pub enum BinanceMessageEnum { + HTLTOrder(htlt_order::HTLTOrder), + DepositHTLTOrder(htlt_order::DepositHTLTOrder), + ClaimHTLTOrder(htlt_order::ClaimHTLTOrder), + RefundHTLTOrder(htlt_order::RefundHTLTOrder), + SendOrder(send_order::SendOrder), + SideDelegateOrder(side_chain_delegate::SideDelegateOrder), + SideRedelegateOrder(side_chain_delegate::SideRedelegateOrder), + SideUndelegateOrder(side_chain_delegate::SideUndelegateOrder), + TimeLockOrder(time_lock_order::TimeLockOrder), + TimeRelockOrder(time_lock_order::TimeRelockOrder), + TimeUnlockOrder(time_lock_order::TimeUnlockOrder), + TokenFreezeOrder(token_order::TokenFreezeOrder), + TokenUnfreezeOrder(token_order::TokenUnfreezeOrder), + TokenIssueOrder(token_order::TokenIssueOrder), + TokenMintOrder(token_order::TokenMintOrder), + TokenBurnOrder(token_order::TokenBurnOrder), + NewTradeOrder(trade_order::NewTradeOrder), + CancelTradeOrder(trade_order::CancelTradeOrder), + TransferOutOrder(tranfer_out_order::TransferOutOrder), +} + +impl TWBinanceProto for BinanceMessageEnum { + type Proto<'a> = BinanceMessageProto<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + match msg { + BinanceMessageProto::trade_order(ref order) => { + trade_order::NewTradeOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::NewTradeOrder) + }, + BinanceMessageProto::cancel_trade_order(ref order) => { + trade_order::CancelTradeOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::CancelTradeOrder) + }, + BinanceMessageProto::send_order(ref order) => { + send_order::SendOrder::from_tw_proto(coin, order).map(BinanceMessageEnum::SendOrder) + }, + BinanceMessageProto::freeze_order(ref order) => { + token_order::TokenFreezeOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::TokenFreezeOrder) + }, + BinanceMessageProto::unfreeze_order(ref order) => { + token_order::TokenUnfreezeOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::TokenUnfreezeOrder) + }, + BinanceMessageProto::htlt_order(ref order) => { + htlt_order::HTLTOrder::from_tw_proto(coin, order).map(BinanceMessageEnum::HTLTOrder) + }, + BinanceMessageProto::depositHTLT_order(ref order) => { + htlt_order::DepositHTLTOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::DepositHTLTOrder) + }, + BinanceMessageProto::claimHTLT_order(ref order) => { + htlt_order::ClaimHTLTOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::ClaimHTLTOrder) + }, + BinanceMessageProto::refundHTLT_order(ref order) => { + htlt_order::RefundHTLTOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::RefundHTLTOrder) + }, + BinanceMessageProto::issue_order(ref order) => { + token_order::TokenIssueOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::TokenIssueOrder) + }, + BinanceMessageProto::mint_order(ref order) => { + token_order::TokenMintOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::TokenMintOrder) + }, + BinanceMessageProto::burn_order(ref order) => { + token_order::TokenBurnOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::TokenBurnOrder) + }, + BinanceMessageProto::transfer_out_order(ref order) => { + tranfer_out_order::TransferOutOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::TransferOutOrder) + }, + BinanceMessageProto::side_delegate_order(ref order) => { + side_chain_delegate::SideDelegateOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::SideDelegateOrder) + }, + BinanceMessageProto::side_redelegate_order(ref order) => { + side_chain_delegate::SideRedelegateOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::SideRedelegateOrder) + }, + BinanceMessageProto::side_undelegate_order(ref order) => { + side_chain_delegate::SideUndelegateOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::SideUndelegateOrder) + }, + BinanceMessageProto::time_lock_order(ref order) => { + time_lock_order::TimeLockOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::TimeLockOrder) + }, + BinanceMessageProto::time_relock_order(ref order) => { + time_lock_order::TimeRelockOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::TimeRelockOrder) + }, + BinanceMessageProto::time_unlock_order(ref order) => { + time_lock_order::TimeUnlockOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::TimeUnlockOrder) + }, + BinanceMessageProto::None => Err(SigningError(SigningErrorType::Error_invalid_params)), + } + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + match self { + BinanceMessageEnum::HTLTOrder(m) => BinanceMessageProto::htlt_order(m.to_tw_proto()), + BinanceMessageEnum::DepositHTLTOrder(m) => { + BinanceMessageProto::depositHTLT_order(m.to_tw_proto()) + }, + BinanceMessageEnum::ClaimHTLTOrder(m) => { + BinanceMessageProto::claimHTLT_order(m.to_tw_proto()) + }, + BinanceMessageEnum::RefundHTLTOrder(m) => { + BinanceMessageProto::refundHTLT_order(m.to_tw_proto()) + }, + BinanceMessageEnum::SendOrder(m) => BinanceMessageProto::send_order(m.to_tw_proto()), + BinanceMessageEnum::SideDelegateOrder(m) => { + BinanceMessageProto::side_delegate_order(m.to_tw_proto()) + }, + BinanceMessageEnum::SideRedelegateOrder(m) => { + BinanceMessageProto::side_redelegate_order(m.to_tw_proto()) + }, + BinanceMessageEnum::SideUndelegateOrder(m) => { + BinanceMessageProto::side_undelegate_order(m.to_tw_proto()) + }, + BinanceMessageEnum::TimeLockOrder(m) => { + BinanceMessageProto::time_lock_order(m.to_tw_proto()) + }, + BinanceMessageEnum::TimeRelockOrder(m) => { + BinanceMessageProto::time_relock_order(m.to_tw_proto()) + }, + BinanceMessageEnum::TimeUnlockOrder(m) => { + BinanceMessageProto::time_unlock_order(m.to_tw_proto()) + }, + BinanceMessageEnum::TokenFreezeOrder(m) => { + BinanceMessageProto::freeze_order(m.to_tw_proto()) + }, + BinanceMessageEnum::TokenUnfreezeOrder(m) => { + BinanceMessageProto::unfreeze_order(m.to_tw_proto()) + }, + BinanceMessageEnum::TokenIssueOrder(m) => { + BinanceMessageProto::issue_order(m.to_tw_proto()) + }, + BinanceMessageEnum::TokenMintOrder(m) => { + BinanceMessageProto::mint_order(m.to_tw_proto()) + }, + BinanceMessageEnum::TokenBurnOrder(m) => { + BinanceMessageProto::burn_order(m.to_tw_proto()) + }, + BinanceMessageEnum::NewTradeOrder(m) => { + BinanceMessageProto::trade_order(m.to_tw_proto()) + }, + BinanceMessageEnum::CancelTradeOrder(m) => { + BinanceMessageProto::cancel_trade_order(m.to_tw_proto()) + }, + BinanceMessageEnum::TransferOutOrder(m) => { + BinanceMessageProto::transfer_out_order(m.to_tw_proto()) + }, + } } } -pub fn message_to_json(msg: T) -> SigningResult { - serde_json::to_value(msg).map_err(|_| SigningError(SigningErrorType::Error_internal)) +impl<'a> AsRef for BinanceMessageEnum { + fn as_ref(&self) -> &(dyn BinanceMessage + 'a) { + match self { + BinanceMessageEnum::HTLTOrder(m) => m, + BinanceMessageEnum::DepositHTLTOrder(m) => m, + BinanceMessageEnum::ClaimHTLTOrder(m) => m, + BinanceMessageEnum::RefundHTLTOrder(m) => m, + BinanceMessageEnum::SendOrder(m) => m, + BinanceMessageEnum::SideDelegateOrder(m) => m, + BinanceMessageEnum::SideRedelegateOrder(m) => m, + BinanceMessageEnum::SideUndelegateOrder(m) => m, + BinanceMessageEnum::TimeLockOrder(m) => m, + BinanceMessageEnum::TimeRelockOrder(m) => m, + BinanceMessageEnum::TimeUnlockOrder(m) => m, + BinanceMessageEnum::TokenFreezeOrder(m) => m, + BinanceMessageEnum::TokenUnfreezeOrder(m) => m, + BinanceMessageEnum::TokenIssueOrder(m) => m, + BinanceMessageEnum::TokenMintOrder(m) => m, + BinanceMessageEnum::TokenBurnOrder(m) => m, + BinanceMessageEnum::NewTradeOrder(m) => m, + BinanceMessageEnum::CancelTradeOrder(m) => m, + BinanceMessageEnum::TransferOutOrder(m) => m, + } + } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct Token { - /// Token ID. - pub denom: String, /// Amount. pub amount: i64, + /// Token ID. + pub denom: String, } impl Token { - pub fn to_proto(&self) -> Proto::mod_SendOrder::Token { - Proto::mod_SendOrder::Token { - denom: self.denom.clone().into(), - amount: self.amount, - } - } - pub fn serialize_with_string_amount(&self, serializer: S) -> Result where S: Serializer, { #[derive(Serialize)] struct TokenWithStringAmount<'a> { - denom: &'a str, amount: String, + denom: &'a str, } TokenWithStringAmount { - denom: &self.denom, amount: self.amount.to_string(), + denom: &self.denom, } .serialize(serializer) } + + fn from_tw_proto(msg: &Proto::mod_SendOrder::Token<'_>) -> Self { + Token { + denom: msg.denom.to_string(), + amount: msg.amount, + } + } + + fn to_tw_proto(&self) -> Proto::mod_SendOrder::Token<'static> { + Proto::mod_SendOrder::Token { + denom: self.denom.clone().into(), + amount: self.amount, + } + } } diff --git a/rust/chains/tw_binance/src/transaction/message/send_order.rs b/rust/chains/tw_binance/src/transaction/message/send_order.rs index f8e5632a85d..23f9460c160 100644 --- a/rust/chains/tw_binance/src/transaction/message/send_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/send_order.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 @@ -6,16 +6,16 @@ use crate::address::BinanceAddress; use crate::amino::AminoEncoder; -use crate::transaction::message::{message_to_json, BinanceMessage, Token}; -use serde::Serialize; -use serde_json::Value as Json; +use crate::transaction::message::{BinanceMessage, TWBinanceProto, Token}; +use serde::{Deserialize, Serialize}; +use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; use tw_coin_entry::error::SigningResult; use tw_memory::Data; use tw_proto::Binance::Proto; /// Either an input or output of a `SendOrder`. -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct InOut { /// Source address. pub address: BinanceAddress, @@ -25,22 +25,22 @@ pub struct InOut { } impl InOut { - pub fn to_input_proto(&self) -> Proto::mod_SendOrder::Input { + pub fn to_input_proto(&self) -> Proto::mod_SendOrder::Input<'static> { Proto::mod_SendOrder::Input { address: self.address.data().into(), - coins: self.coins.iter().map(Token::to_proto).collect(), + coins: self.coins.iter().map(Token::to_tw_proto).collect(), } } - pub fn to_output_proto(&self) -> Proto::mod_SendOrder::Output { + pub fn to_output_proto(&self) -> Proto::mod_SendOrder::Output<'static> { Proto::mod_SendOrder::Output { address: self.address.data().into(), - coins: self.coins.iter().map(Token::to_proto).collect(), + coins: self.coins.iter().map(Token::to_tw_proto).collect(), } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct SendOrder { pub inputs: Vec, pub outputs: Vec, @@ -52,18 +52,47 @@ impl SendOrder { } impl BinanceMessage for SendOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::SendOrder { +impl TWBinanceProto for SendOrder { + type Proto<'a> = Proto::SendOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + fn in_out_from_proto( + coin: &dyn CoinContext, + address_key_hash: &[u8], + coins: &[Proto::mod_SendOrder::Token], + ) -> SigningResult { + let address = BinanceAddress::from_key_hash_with_coin(coin, address_key_hash.to_vec())?; + let coins = coins.iter().map(Token::from_tw_proto).collect(); + + Ok(InOut { address, coins }) + } + + let inputs = msg + .inputs + .iter() + .map(|input| in_out_from_proto(coin, &input.address, &input.coins)) + .collect::>>()?; + + let outputs = msg + .outputs + .iter() + .map(|output| in_out_from_proto(coin, &output.address, &output.coins)) + .collect::>>()?; + + Ok(SendOrder { inputs, outputs }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::SendOrder { inputs: self.inputs.iter().map(InOut::to_input_proto).collect(), outputs: self.outputs.iter().map(InOut::to_output_proto).collect(), - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } diff --git a/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs b/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs index d376e530eee..39008818681 100644 --- a/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs +++ b/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 @@ -6,26 +6,28 @@ use crate::address::BinanceAddress; use crate::amino::AminoEncoder; -use crate::transaction::message::{message_to_json, BinanceMessage, Token}; -use serde::Serialize; -use serde_json::Value as Json; +use crate::transaction::message::{BinanceMessage, TWBinanceProto, Token}; +use serde::{Deserialize, Serialize}; +use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::SigningResult; -use tw_cosmos_sdk::modules::serializer::json_serializer::AnyMsg; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_memory::Data; +use tw_misc::serde::Typed; use tw_proto::Binance::Proto; +pub type SideDelegateOrder = Typed; + /// cosmos-sdk/MsgSideChainDelegate -#[derive(Serialize)] -pub struct SideDelegateOrder { - pub delegator_addr: BinanceAddress, - pub validator_addr: BinanceAddress, +#[derive(Deserialize, Serialize)] +pub struct SideDelegateOrderValue { #[serde(serialize_with = "Token::serialize_with_string_amount")] pub delegation: Token, + pub delegator_addr: BinanceAddress, pub side_chain_id: String, + pub validator_addr: BinanceAddress, } -impl SideDelegateOrder { +impl SideDelegateOrderValue { /// cbindgen:ignore pub const PREFIX: [u8; 4] = [0xE3, 0xA0, 0x7F, 0xD2]; /// cbindgen:ignore @@ -33,40 +35,62 @@ impl SideDelegateOrder { } impl BinanceMessage for SideDelegateOrder { - fn to_json(&self) -> SigningResult { - let any_msg = AnyMsg { - msg_type: Self::MESSAGE_TYPE.to_string(), - value: message_to_json(self)?, - }; - message_to_json(any_msg) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&SideDelegateOrderValue::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::SideChainDelegate { - delegator_addr: self.delegator_addr.data().into(), - validator_addr: self.validator_addr.data().into(), - delegation: Some(self.delegation.to_proto()), - chain_id: self.side_chain_id.clone().into(), +impl TWBinanceProto for SideDelegateOrder { + type Proto<'a> = Proto::SideChainDelegate<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let delegator_addr = + BinanceAddress::from_key_hash_with_coin(coin, msg.delegator_addr.to_vec())?; + let validator_addr = BinanceAddress::new_validator_addr(msg.validator_addr.to_vec())?; + + let delegation = msg + .delegation + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + + let value = SideDelegateOrderValue { + delegator_addr, + validator_addr, + delegation: Token::from_tw_proto(delegation), + side_chain_id: msg.chain_id.to_string(), }; + Ok(Typed { + ty: SideDelegateOrderValue::MESSAGE_TYPE.to_string(), + value, + }) + } - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::SideChainDelegate { + delegator_addr: self.value.delegator_addr.data().into(), + validator_addr: self.value.validator_addr.data().into(), + delegation: Some(self.value.delegation.to_tw_proto()), + chain_id: self.value.side_chain_id.clone().into(), + } } } +pub type SideRedelegateOrder = Typed; + /// cosmos-sdk/MsgSideChainRedelegate -#[derive(Serialize)] -pub struct SideRedelegateOrder { - pub delegator_addr: BinanceAddress, - pub validator_src_addr: BinanceAddress, - pub validator_dst_addr: BinanceAddress, +#[derive(Deserialize, Serialize)] +pub struct SideRedelegateOrderValue { #[serde(serialize_with = "Token::serialize_with_string_amount")] pub amount: Token, + pub delegator_addr: BinanceAddress, pub side_chain_id: String, + pub validator_dst_addr: BinanceAddress, + pub validator_src_addr: BinanceAddress, } -impl SideRedelegateOrder { +impl SideRedelegateOrderValue { /// cbindgen:ignore pub const PREFIX: [u8; 4] = [0xE3, 0xCE, 0xD3, 0x64]; /// cbindgen:ignore @@ -74,40 +98,66 @@ impl SideRedelegateOrder { } impl BinanceMessage for SideRedelegateOrder { - fn to_json(&self) -> SigningResult { - let any_msg = AnyMsg { - msg_type: Self::MESSAGE_TYPE.to_string(), - value: message_to_json(self)?, - }; - message_to_json(any_msg) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&SideRedelegateOrderValue::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::SideChainRedelegate { - delegator_addr: self.delegator_addr.data().into(), - validator_src_addr: self.validator_src_addr.data().into(), - validator_dst_addr: self.validator_dst_addr.data().into(), - amount: Some(self.amount.to_proto()), - chain_id: self.side_chain_id.clone().into(), +impl TWBinanceProto for SideRedelegateOrder { + type Proto<'a> = Proto::SideChainRedelegate<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let delegator_addr = + BinanceAddress::from_key_hash_with_coin(coin, msg.delegator_addr.to_vec())?; + let validator_src_addr = + BinanceAddress::new_validator_addr(msg.validator_src_addr.to_vec())?; + let validator_dst_addr = + BinanceAddress::new_validator_addr(msg.validator_dst_addr.to_vec())?; + + let amount = msg + .amount + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + + let value = SideRedelegateOrderValue { + delegator_addr, + validator_src_addr, + validator_dst_addr, + amount: Token::from_tw_proto(amount), + side_chain_id: msg.chain_id.to_string(), }; + Ok(Typed { + ty: SideRedelegateOrderValue::MESSAGE_TYPE.to_string(), + value, + }) + } - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::SideChainRedelegate { + delegator_addr: self.value.delegator_addr.data().into(), + validator_src_addr: self.value.validator_src_addr.data().into(), + validator_dst_addr: self.value.validator_dst_addr.data().into(), + amount: Some(self.value.amount.to_tw_proto()), + chain_id: self.value.side_chain_id.clone().into(), + } } } +pub type SideUndelegateOrder = Typed; + /// cosmos-sdk/MsgSideChainUndelegate -#[derive(Serialize)] -pub struct SideUndelegateOrder { - pub delegator_addr: BinanceAddress, - pub validator_addr: BinanceAddress, +#[derive(Deserialize, Serialize)] +pub struct SideUndelegateOrderValue { #[serde(serialize_with = "Token::serialize_with_string_amount")] pub amount: Token, + pub delegator_addr: BinanceAddress, pub side_chain_id: String, + pub validator_addr: BinanceAddress, } -impl SideUndelegateOrder { +impl SideUndelegateOrderValue { /// cbindgen:ignore pub const PREFIX: [u8; 4] = [0x51, 0x4F, 0x7E, 0x0E]; /// cbindgen:ignore @@ -115,24 +165,44 @@ impl SideUndelegateOrder { } impl BinanceMessage for SideUndelegateOrder { - fn to_json(&self) -> SigningResult { - let any_msg = AnyMsg { - msg_type: Self::MESSAGE_TYPE.to_string(), - value: message_to_json(self)?, - }; - message_to_json(any_msg) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&SideUndelegateOrderValue::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::SideChainUndelegate { - delegator_addr: self.delegator_addr.data().into(), - validator_addr: self.validator_addr.data().into(), - amount: Some(self.amount.to_proto()), - chain_id: self.side_chain_id.clone().into(), +impl TWBinanceProto for SideUndelegateOrder { + type Proto<'a> = Proto::SideChainUndelegate<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let delegator_addr = + BinanceAddress::from_key_hash_with_coin(coin, msg.delegator_addr.to_vec())?; + let validator_addr = BinanceAddress::new_validator_addr(msg.validator_addr.to_vec())?; + + let amount = msg + .amount + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + + let value = SideUndelegateOrderValue { + delegator_addr, + validator_addr, + amount: Token::from_tw_proto(amount), + side_chain_id: msg.chain_id.to_string(), }; + Ok(Typed { + ty: SideUndelegateOrderValue::MESSAGE_TYPE.to_string(), + value, + }) + } - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::SideChainUndelegate { + delegator_addr: self.value.delegator_addr.data().into(), + validator_addr: self.value.validator_addr.data().into(), + amount: Some(self.value.amount.to_tw_proto()), + chain_id: self.value.side_chain_id.clone().into(), + } } } diff --git a/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs b/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs index 83dbdd6cf24..0581dba133c 100644 --- a/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 @@ -6,19 +6,19 @@ use crate::address::BinanceAddress; use crate::amino::AminoEncoder; -use crate::transaction::message::{message_to_json, BinanceMessage, Token}; -use serde::Serialize; -use serde_json::Value as Json; +use crate::transaction::message::{BinanceMessage, TWBinanceProto, Token}; +use serde::{Deserialize, Serialize}; +use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; use tw_coin_entry::error::SigningResult; use tw_memory::Data; use tw_proto::Binance::Proto; -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct TimeLockOrder { - pub from: BinanceAddress, - pub description: String, pub amount: Vec, + pub description: String, + pub from: BinanceAddress, pub lock_time: i64, } @@ -28,32 +28,46 @@ impl TimeLockOrder { } impl BinanceMessage for TimeLockOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::TimeLockOrder { +impl TWBinanceProto for TimeLockOrder { + type Proto<'a> = Proto::TimeLockOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from_address.to_vec())?; + let amount = msg.amount.iter().map(Token::from_tw_proto).collect(); + + Ok(TimeLockOrder { + from, + description: msg.description.to_string(), + amount, + lock_time: msg.lock_time, + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::TimeLockOrder { from_address: self.from.data().into(), description: self.description.clone().into(), - amount: self.amount.iter().map(Token::to_proto).collect(), + amount: self.amount.iter().map(Token::to_tw_proto).collect(), lock_time: self.lock_time, - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct TimeRelockOrder { - pub from: BinanceAddress, - pub time_lock_id: i64, - pub description: String, /// If the amount is empty or omitted, set null to avoid signature verification error. pub amount: Option>, + pub description: String, + pub from: BinanceAddress, pub lock_time: i64, + pub time_lock_id: i64, } impl TimeRelockOrder { @@ -62,31 +76,51 @@ impl TimeRelockOrder { } impl BinanceMessage for TimeRelockOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { +impl TWBinanceProto for TimeRelockOrder { + type Proto<'a> = Proto::TimeRelockOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from_address.to_vec())?; + + let amount = if msg.amount.is_empty() { + None + } else { + Some(msg.amount.iter().map(Token::from_tw_proto).collect()) + }; + + Ok(TimeRelockOrder { + from, + time_lock_id: msg.id, + description: msg.description.to_string(), + amount, + lock_time: msg.lock_time, + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { let amount = match self.amount { - Some(ref tokens) => tokens.iter().map(Token::to_proto).collect(), + Some(ref tokens) => tokens.iter().map(Token::to_tw_proto).collect(), None => Vec::default(), }; - let msg = Proto::TimeRelockOrder { + Proto::TimeRelockOrder { from_address: self.from.data().into(), id: self.time_lock_id, description: self.description.clone().into(), amount, lock_time: self.lock_time, - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct TimeUnlockOrder { pub from: BinanceAddress, pub time_lock_id: i64, @@ -98,18 +132,28 @@ impl TimeUnlockOrder { } impl BinanceMessage for TimeUnlockOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::TimeUnlockOrder { +impl TWBinanceProto for TimeUnlockOrder { + type Proto<'a> = Proto::TimeUnlockOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from_address.to_vec())?; + Ok(TimeUnlockOrder { + from, + time_lock_id: msg.id, + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::TimeUnlockOrder { from_address: self.from.data().into(), id: self.time_lock_id, - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } diff --git a/rust/chains/tw_binance/src/transaction/message/token_order.rs b/rust/chains/tw_binance/src/transaction/message/token_order.rs index 16f2594fbcb..1d2c442cd9a 100644 --- a/rust/chains/tw_binance/src/transaction/message/token_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/token_order.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 @@ -6,19 +6,19 @@ use crate::address::BinanceAddress; use crate::amino::AminoEncoder; -use crate::transaction::message::{message_to_json, BinanceMessage}; -use serde::Serialize; -use serde_json::Value as Json; +use crate::transaction::message::{BinanceMessage, TWBinanceProto}; +use serde::{Deserialize, Serialize}; +use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; use tw_coin_entry::error::SigningResult; use tw_memory::Data; use tw_proto::Binance::Proto; -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct TokenFreezeOrder { + pub amount: i64, pub from: BinanceAddress, pub symbol: String, - pub amount: i64, } impl TokenFreezeOrder { @@ -27,28 +27,39 @@ impl TokenFreezeOrder { } impl BinanceMessage for TokenFreezeOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::TokenFreezeOrder { +impl TWBinanceProto for TokenFreezeOrder { + type Proto<'a> = Proto::TokenFreezeOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; + Ok(TokenFreezeOrder { + from, + symbol: msg.symbol.to_string(), + amount: msg.amount, + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::TokenFreezeOrder { from: self.from.data().into(), symbol: self.symbol.clone().into(), amount: self.amount, - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct TokenUnfreezeOrder { + pub amount: i64, pub from: BinanceAddress, pub symbol: String, - pub amount: i64, } impl TokenUnfreezeOrder { @@ -57,30 +68,41 @@ impl TokenUnfreezeOrder { } impl BinanceMessage for TokenUnfreezeOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::TokenUnfreezeOrder { +impl TWBinanceProto for TokenUnfreezeOrder { + type Proto<'a> = Proto::TokenUnfreezeOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; + Ok(TokenUnfreezeOrder { + from, + symbol: msg.symbol.to_string(), + amount: msg.amount, + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::TokenUnfreezeOrder { from: self.from.data().into(), symbol: self.symbol.clone().into(), amount: self.amount, - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct TokenIssueOrder { pub from: BinanceAddress, + pub mintable: bool, pub name: String, pub symbol: String, pub total_supply: i64, - pub mintable: bool, } impl TokenIssueOrder { @@ -89,30 +111,43 @@ impl TokenIssueOrder { } impl BinanceMessage for TokenIssueOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) + } +} + +impl TWBinanceProto for TokenIssueOrder { + type Proto<'a> = Proto::TokenIssueOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; + Ok(TokenIssueOrder { + from, + name: msg.name.to_string(), + symbol: msg.symbol.to_string(), + total_supply: msg.total_supply, + mintable: msg.mintable, + }) } - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::TokenIssueOrder { + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::TokenIssueOrder { from: self.from.data().into(), name: self.name.clone().into(), symbol: self.symbol.clone().into(), total_supply: self.total_supply, mintable: self.mintable, - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct TokenMintOrder { + pub amount: i64, pub from: BinanceAddress, pub symbol: String, - pub amount: i64, } impl TokenMintOrder { @@ -121,28 +156,39 @@ impl TokenMintOrder { } impl BinanceMessage for TokenMintOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) + } +} + +impl TWBinanceProto for TokenMintOrder { + type Proto<'a> = Proto::TokenMintOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; + Ok(TokenMintOrder { + from, + symbol: msg.symbol.to_string(), + amount: msg.amount, + }) } - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::TokenMintOrder { + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::TokenMintOrder { from: self.from.data().into(), symbol: self.symbol.clone().into(), amount: self.amount, - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct TokenBurnOrder { + pub amount: i64, pub from: BinanceAddress, pub symbol: String, - pub amount: i64, } impl TokenBurnOrder { @@ -151,19 +197,30 @@ impl TokenBurnOrder { } impl BinanceMessage for TokenBurnOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::TokenBurnOrder { +impl TWBinanceProto for TokenBurnOrder { + type Proto<'a> = Proto::TokenBurnOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; + Ok(TokenBurnOrder { + from, + symbol: msg.symbol.to_string(), + amount: msg.amount, + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::TokenBurnOrder { from: self.from.data().into(), symbol: self.symbol.clone().into(), amount: self.amount, - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } diff --git a/rust/chains/tw_binance/src/transaction/message/trade_order.rs b/rust/chains/tw_binance/src/transaction/message/trade_order.rs index 8e02780e214..9020bb00f9d 100644 --- a/rust/chains/tw_binance/src/transaction/message/trade_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/trade_order.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 @@ -6,22 +6,24 @@ use crate::address::BinanceAddress; use crate::amino::AminoEncoder; -use crate::transaction::message::{message_to_json, BinanceMessage}; -use serde::Serialize; -use serde_json::Value as Json; +use crate::transaction::message::{BinanceMessage, TWBinanceProto}; +use serde::{Deserialize, Serialize}; +use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_memory::Data; use tw_proto::Binance::Proto; #[repr(i64)] -#[derive(Clone, Copy, serde_repr::Serialize_repr, strum_macros::FromRepr)] +#[derive( + Clone, Copy, serde_repr::Deserialize_repr, serde_repr::Serialize_repr, strum_macros::FromRepr, +)] pub enum OrderType { /// https://github.com/bnb-chain/python-sdk/blob/0f6b8a6077f486b26eda3e448f3e84ef35bfff75/binance_chain/constants.py#L62 Limit = 2, } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct NewTradeOrder { /// Order id, optional. pub id: String, @@ -49,12 +51,35 @@ impl NewTradeOrder { } impl BinanceMessage for NewTradeOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::TradeOrder { +impl TWBinanceProto for NewTradeOrder { + type Proto<'a> = Proto::TradeOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let order_type = OrderType::from_repr(msg.ordertype) + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + let sender = BinanceAddress::from_key_hash_with_coin(coin, msg.sender.to_vec())?; + + Ok(NewTradeOrder { + id: msg.id.to_string(), + order_type, + price: msg.price, + quantity: msg.quantity, + sender, + side: msg.side, + symbol: msg.symbol.to_string(), + time_in_force: msg.timeinforce, + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::TradeOrder { id: self.id.clone().into(), ordertype: self.order_type as i64, price: self.price, @@ -63,21 +88,18 @@ impl BinanceMessage for NewTradeOrder { side: self.side, symbol: self.symbol.clone().into(), timeinforce: self.time_in_force, - }; - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct CancelTradeOrder { + /// Order id to cancel. + pub refid: String, /// Originating address. pub sender: BinanceAddress, /// Symbol for trading pair in full name of the tokens. pub symbol: String, - /// Order id to cancel. - pub refid: String, } impl CancelTradeOrder { @@ -86,18 +108,30 @@ impl CancelTradeOrder { } impl BinanceMessage for CancelTradeOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) + } +} + +impl TWBinanceProto for CancelTradeOrder { + type Proto<'a> = Proto::CancelTradeOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let sender = BinanceAddress::from_key_hash_with_coin(coin, msg.sender.to_vec())?; + Ok(CancelTradeOrder { + sender, + symbol: msg.symbol.to_string(), + refid: msg.refid.to_string(), + }) } - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::CancelTradeOrder { + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::CancelTradeOrder { sender: self.sender.data().into(), symbol: self.symbol.clone().into(), refid: self.refid.clone().into(), - }; - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } diff --git a/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs b/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs index 3f838793e11..68030deb104 100644 --- a/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 @@ -6,21 +6,22 @@ use crate::address::BinanceAddress; use crate::amino::AminoEncoder; -use crate::transaction::message::{message_to_json, BinanceMessage, Token}; -use serde::Serialize; -use serde_json::Value as Json; +use crate::transaction::message::{BinanceMessage, TWBinanceProto, Token}; +use serde::{Deserialize, Serialize}; +use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_evm::address::Address as EthereumAddress; +use tw_hash::H160; use tw_memory::Data; use tw_proto::Binance::Proto; -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct TransferOutOrder { - pub from: BinanceAddress, - pub to: EthereumAddress, pub amount: Token, pub expire_time: i64, + pub from: BinanceAddress, + pub to: EthereumAddress, } impl TransferOutOrder { @@ -29,20 +30,42 @@ impl TransferOutOrder { } impl BinanceMessage for TransferOutOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::TransferOut { +impl TWBinanceProto for TransferOutOrder { + type Proto<'a> = Proto::TransferOut<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; + + let to_bytes = H160::try_from(msg.to.as_ref()) + .map_err(|_| SigningError(SigningErrorType::Error_invalid_address))?; + let to = EthereumAddress::from_bytes(to_bytes); + + let amount_proto = msg + .amount + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + + Ok(TransferOutOrder { + from, + to, + amount: Token::from_tw_proto(amount_proto), + expire_time: msg.expire_time, + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::TransferOut { from: self.from.data().into(), to: self.to.data().into(), - amount: Some(self.amount.to_proto()), + amount: Some(self.amount.to_tw_proto()), expire_time: self.expire_time, - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } diff --git a/rust/chains/tw_binance/src/transaction/mod.rs b/rust/chains/tw_binance/src/transaction/mod.rs index 74e96f2595b..021f7180203 100644 --- a/rust/chains/tw_binance/src/transaction/mod.rs +++ b/rust/chains/tw_binance/src/transaction/mod.rs @@ -1,29 +1,29 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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. use crate::signature::BinanceSignature; -use crate::transaction::message::BinanceMessageBox; -use serde::Serialize; -use tw_keypair::ecdsa::secp256k1; +use crate::transaction::message::BinanceMessageEnum; +use serde::{Deserialize, Serialize}; +use tw_cosmos_sdk::public_key::secp256k1::Secp256PublicKey; use tw_memory::Data; use tw_misc::serde::as_string; pub mod message; -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct UnsignedTransaction { - #[serde(serialize_with = "as_string")] + #[serde(with = "as_string")] pub account_number: i64, pub chain_id: String, pub data: Option, pub memo: String, - pub msgs: Vec, - #[serde(serialize_with = "as_string")] + pub msgs: Vec, + #[serde(with = "as_string")] pub sequence: i64, - #[serde(serialize_with = "as_string")] + #[serde(with = "as_string")] pub source: i64, } @@ -37,7 +37,7 @@ impl UnsignedTransaction { } pub struct SignerInfo { - pub public_key: secp256k1::PublicKey, + pub public_key: Secp256PublicKey, pub signature: BinanceSignature, } diff --git a/rust/chains/tw_cosmos/src/entry.rs b/rust/chains/tw_cosmos/src/entry.rs index ca2d84efcf7..fad4cfb1735 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_connector::NoWalletConnector; 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 WalletConnector = NoWalletConnector; #[inline] fn parse_address( diff --git a/rust/chains/tw_greenfield/src/entry.rs b/rust/chains/tw_greenfield/src/entry.rs index c78823b9b27..c3aa6fd68d0 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_connector::NoWalletConnector; 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 WalletConnector = NoWalletConnector; #[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..9f1d0c9820a 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_connector::NoWalletConnector; 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 WalletConnector = NoWalletConnector; #[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..ed1759554ec 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_connector::NoWalletConnector; 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 WalletConnector = NoWalletConnector; #[inline] fn parse_address( diff --git a/rust/chains/tw_thorchain/src/entry.rs b/rust/chains/tw_thorchain/src/entry.rs index 81f8bf562f1..4cd52da3965 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_connector::NoWalletConnector; 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 WalletConnector = NoWalletConnector; #[inline] fn parse_address( diff --git a/rust/tw_any_coin/src/ffi/mod.rs b/rust/tw_any_coin/src/ffi/mod.rs index 62c144645d8..854c6cd5e98 100644 --- a/rust/tw_any_coin/src/ffi/mod.rs +++ b/rust/tw_any_coin/src/ffi/mod.rs @@ -8,3 +8,4 @@ pub mod tw_any_address; pub mod tw_any_signer; pub mod tw_message_signer; pub mod tw_transaction_compiler; +pub mod tw_wallet_connect_request; 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 new file mode 100644 index 00000000000..fbbb5da4247 --- /dev/null +++ b/rust/tw_any_coin/src/ffi/tw_wallet_connect_request.rs @@ -0,0 +1,31 @@ +// 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. + +#![allow(clippy::missing_safety_doc)] + +use crate::wallet_connect_request::WalletConnectRequest; +use tw_coin_registry::coin_type::CoinType; +use tw_memory::ffi::tw_data::TWData; +use tw_memory::ffi::RawPtrTrait; +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 `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, + input: *const TWData, +) -> *mut TWData { + let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut); + let input = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); + + WalletConnectRequest::parse(coin, input.as_slice()) + .map(|output| TWData::from(output).into_ptr()) + .unwrap_or_else(|_| std::ptr::null_mut()) +} diff --git a/rust/tw_any_coin/src/lib.rs b/rust/tw_any_coin/src/lib.rs index ebe47631a8e..5788831c45a 100644 --- a/rust/tw_any_coin/src/lib.rs +++ b/rust/tw_any_coin/src/lib.rs @@ -9,6 +9,7 @@ pub mod any_signer; pub mod ffi; pub mod message_signer; pub mod transaction_compiler; +pub mod wallet_connect_request; #[cfg(feature = "test-utils")] pub mod test_utils; diff --git a/rust/tw_any_coin/src/test_utils/mod.rs b/rust/tw_any_coin/src/test_utils/mod.rs index 098e098ea64..2c5b412f804 100644 --- a/rust/tw_any_coin/src/test_utils/mod.rs +++ b/rust/tw_any_coin/src/test_utils/mod.rs @@ -6,3 +6,4 @@ pub mod address_utils; pub mod sign_utils; +pub mod wallet_connect_utils; 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 new file mode 100644 index 00000000000..d270a3422f2 --- /dev/null +++ b/rust/tw_any_coin/src/test_utils/wallet_connect_utils.rs @@ -0,0 +1,34 @@ +// 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. + +use crate::ffi::tw_wallet_connect_request::tw_wallet_connect_request_parse; +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, WalletConnect::Proto as WCProto}; + +#[derive(Default)] +pub struct WalletConnectRequestHelper { + output_data: Data, +} + +impl WalletConnectRequestHelper { + pub fn parse<'a>( + &'a mut self, + coin_type: CoinType, + input: &WCProto::ParseRequestInput, + ) -> WCProto::ParseRequestOutput<'a> { + let input_data = TWDataHelper::create(serialize(input).unwrap()); + + 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"); + + deserialize(&self.output_data).unwrap() + } +} diff --git a/rust/tw_any_coin/src/wallet_connect_request.rs b/rust/tw_any_coin/src/wallet_connect_request.rs new file mode 100644 index 00000000000..97a5f672ea5 --- /dev/null +++ b/rust/tw_any_coin/src/wallet_connect_request.rs @@ -0,0 +1,25 @@ +// 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. + +use tw_coin_entry::error::{SigningError, SigningResult}; +use tw_coin_registry::coin_type::CoinType; +use tw_coin_registry::dispatcher::coin_dispatcher; +use tw_memory::Data; + +/// Represents a signer to sign transactions for any blockchain. +pub struct WalletConnectRequest; + +impl WalletConnectRequest { + /// Parses the WalletConnect signing request as a `SigningInput`. + /// It is optional. Returns an error if the chain does not support WalletConnect signing. + #[inline] + pub fn parse(coin: CoinType, input: &[u8]) -> SigningResult { + let (ctx, entry) = coin_dispatcher(coin)?; + entry + .wallet_connect_parse_request(&ctx, input) + .map_err(SigningError::from) + } +} diff --git a/rust/tw_any_coin/tests/chains/binance/binance_address.rs b/rust/tw_any_coin/tests/chains/binance/binance_address.rs index d915ef496a8..076bcf9f61f 100644 --- a/rust/tw_any_coin/tests/chains/binance/binance_address.rs +++ b/rust/tw_any_coin/tests/chains/binance/binance_address.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 diff --git a/rust/tw_any_coin/tests/chains/binance/binance_compile.rs b/rust/tw_any_coin/tests/chains/binance/binance_compile.rs index f1104b9657e..6f3b0766e02 100644 --- a/rust/tw_any_coin/tests/chains/binance/binance_compile.rs +++ b/rust/tw_any_coin/tests/chains/binance/binance_compile.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 @@ -55,15 +55,19 @@ fn test_binance_compile() { // Step 3: Compile transaction info // Simulate signature, normally obtained from signature server. - let signature = "1b1181faec30b60a2ddaa2804c253cf264c69180ec31814929b5de62088c0c5a45e8a816d1208fc5366bb8b041781a6771248550d04094c3d7a504f9e8310679" - .decode_hex() - .unwrap(); + let signature = "1b1181faec30b60a2ddaa2804c253cf264c69180ec31814929b5de62088c0c5a45e8a816d1208fc5366bb8b041781a6771248550d04094c3d7a504f9e8310679"; + let signature_bytes = signature.decode_hex().unwrap(); let public_key = "026a35920088d98c3888ca68c53dfc93f4564602606cbb87f0fe5ee533db38e502" .decode_hex() .unwrap(); let mut compiler = CompilerHelper::::default(); - let output = compiler.compile(CoinType::Binance, &input, vec![signature], vec![public_key]); + let output = compiler.compile( + CoinType::Binance, + &input, + vec![signature_bytes], + vec![public_key], + ); assert_eq!(output.error, SigningError::OK); let expected_tx = concat!( @@ -74,4 +78,7 @@ fn test_binance_compile() { "04f9e8310679", ); assert_eq!(output.encoded.to_hex(), expected_tx); + assert_eq!(output.signature.to_hex(), signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"Amo1kgCI2Yw4iMpoxT38k/RWRgJgbLuH8P5e5TPbOOUC"},"signature":"GxGB+uwwtgot2qKATCU88mTGkYDsMYFJKbXeYgiMDFpF6KgW0SCPxTZruLBBeBpncSSFUNBAlMPXpQT56DEGeQ=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } diff --git a/rust/tw_any_coin/tests/chains/binance/binance_sign.rs b/rust/tw_any_coin/tests/chains/binance/binance_sign.rs index de227f4efc5..ecddd3e6f92 100644 --- a/rust/tw_any_coin/tests/chains/binance/binance_sign.rs +++ b/rust/tw_any_coin/tests/chains/binance/binance_sign.rs @@ -1,10 +1,13 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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. -use crate::chains::binance::make_token; +use crate::chains::binance::{ + make_token, ACCOUNT_12_PRIVATE_KEY, ACCOUNT_15_PRIVATE_KEY, ACCOUNT_16_PRIVATE_KEY, + ACCOUNT_19_PRIVATE_KEY, +}; use tw_any_coin::test_utils::sign_utils::AnySignerHelper; use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::{DecodeHex, ToHex}; @@ -12,15 +15,6 @@ use tw_proto::Binance::Proto; use tw_proto::Binance::Proto::mod_SigningInput::OneOforder_oneof as OrderEnum; use tw_proto::Common::Proto::SigningError; -const ACCOUNT_12_PRIVATE_KEY: &str = - "90335b9d2153ad1a9799a3ccc070bd64b4164e9642ee1dd48053c33f9a3a05e9"; -const ACCOUNT_19_PRIVATE_KEY: &str = - "95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832"; -const ACCOUNT_15_PRIVATE_KEY: &str = - "eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d"; -const ACCOUNT_16_PRIVATE_KEY: &str = - "851fab89c14f4bbec0cc06f5e445ec065efc641068d78b308c67217d9bd5c88a"; - #[test] fn test_binance_sign_trade_order() { // bnb1hgm0p7khfk85zpz5v0j8wnej3a90w709vhkdfu @@ -57,6 +51,11 @@ fn test_binance_sign_trade_order() { output.encoded.to_hex(), "dc01f0625dee0a64ce6dc0430a14ba36f0fad74d8f41045463e4774f328f4af779e5122b424133364630464144373444384634313034353436334534373734463332384634414637373945352d33361a0b4e4e422d3333385f424e422002280130b09282413880c2d72f4001126e0a26eb5ae98721029729a52e4e3c2b4a4e52aa74033eedaf8ba1df5ab6d1f518fd69e67bbd309b0e12409123cb6906bb20aeb753f4a121d4d88ff0e9750ba75b0c4e10d76caee1e7d2481290fa3b9887a6225d6997f5f939ef834ea61d596a314237c48e560da9e17b5a180c20232001" ); + + let expected_signature = "9123cb6906bb20aeb753f4a121d4d88ff0e9750ba75b0c4e10d76caee1e7d2481290fa3b9887a6225d6997f5f939ef834ea61d596a314237c48e560da9e17b5a"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"ApcppS5OPCtKTlKqdAM+7a+Lod9attH1GP1p5nu9MJsO"},"signature":"kSPLaQa7IK63U/ShIdTYj/DpdQunWwxOENdsruHn0kgSkPo7mIemIl1pl/X5Oe+DTqYdWWoxQjfEjlYNqeF7Wg=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -90,6 +89,11 @@ fn test_binance_sign_cancel_trade_order() { output.encoded.to_hex(), "cc01f0625dee0a54166e681b0a14ba36f0fad74d8f41045463e4774f328f4af779e5120b4e4e422d3333385f424e421a2b424133364630464144373444384634313034353436334534373734463332384634414637373945352d3336126e0a26eb5ae98721029729a52e4e3c2b4a4e52aa74033eedaf8ba1df5ab6d1f518fd69e67bbd309b0e12403df6603426b991f7040bce22ce0137c12137df01e1d4d425cf3d9104103aec6335ac05c825e08ba26b9f72aa4cc45aa75cacfb6082df86b00692fef9701eb0f5180c20242001" ); + + let expected_signature = "3df6603426b991f7040bce22ce0137c12137df01e1d4d425cf3d9104103aec6335ac05c825e08ba26b9f72aa4cc45aa75cacfb6082df86b00692fef9701eb0f5"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"ApcppS5OPCtKTlKqdAM+7a+Lod9attH1GP1p5nu9MJsO"},"signature":"PfZgNCa5kfcEC84izgE3wSE33wHh1NQlzz2RBBA67GM1rAXIJeCLomufcqpMxFqnXKz7YILfhrAGkv75cB6w9Q=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -97,6 +101,7 @@ fn test_binance_sign_send_order() { let amount = 1_001_000_000; // bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2 let from_address_key_hash = "40c2979694bbc961023d1d27be6fc4d21a9febe6"; + // bnb13zeh6hs97d5eu2s5qerguhv8ewwue6u4ywa6yf let to_address_key_hash = "88b37d5e05f3699e2a1406468e5d87cb9dcceb95"; let send_order = Proto::SendOrder { @@ -143,6 +148,11 @@ fn test_binance_sign_send_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "c65a13440f18a155bd971ee40b9e0dd58586f5bf344e12ec4c76c439aebca8c7789bab7bfbfb4ce89aadc4a02df225b6b6efc861c13bbeb5f7a3eea2d7ffc80f"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"Amo1kgCI2Yw4iMpoxT38k/RWRgJgbLuH8P5e5TPbOOUC"},"signature":"xloTRA8YoVW9lx7kC54N1YWG9b80ThLsTHbEOa68qMd4m6t7+/tM6JqtxKAt8iW2tu/IYcE7vrX3o+6i1//IDw=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -178,6 +188,11 @@ fn test_binance_sign_token_freeze_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "e3022069d897bf5bf4846d354fcd2c0e85807053be643c8b8c8596306003f7340d43a162722673eb848258b0435b1f49993d0e75d4ae43d03453a3ae57fe6991"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"4wIgadiXv1v0hG01T80sDoWAcFO+ZDyLjIWWMGAD9zQNQ6FiciZz64SCWLBDWx9JmT0OddSuQ9A0U6OuV/5pkQ=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -213,6 +228,11 @@ fn test_binance_sign_token_unfreeze_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "e3022069d897bf5bf4846d354fcd2c0e85807053be643c8b8c8596306003f7340d43a162722673eb848258b0435b1f49993d0e75d4ae43d03453a3ae57fe6991"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"4wIgadiXv1v0hG01T80sDoWAcFO+ZDyLjIWWMGAD9zQNQ6FiciZz64SCWLBDWx9JmT0OddSuQ9A0U6OuV/5pkQ=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -250,6 +270,11 @@ fn test_binance_sign_token_issue_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "1fbb993d643f03b3e8e757a502035f58c4c45aaaa6e107a3059ab7c6164283c10f1254e87feee21477c64c87b1a27d8481048533ae2f685b3ac0dc66e4edbc0b"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"H7uZPWQ/A7Po51elAgNfWMTEWqqm4QejBZq3xhZCg8EPElTof+7iFHfGTIexon2EgQSFM64vaFs6wNxm5O28Cw=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -283,6 +308,11 @@ fn test_binance_sign_token_mint_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "e3022069d897bf5bf4846d354fcd2c0e85807053be643c8b8c8596306003f7340d43a162722673eb848258b0435b1f49993d0e75d4ae43d03453a3ae57fe6991"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"4wIgadiXv1v0hG01T80sDoWAcFO+ZDyLjIWWMGAD9zQNQ6FiciZz64SCWLBDWx9JmT0OddSuQ9A0U6OuV/5pkQ=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -316,6 +346,11 @@ fn test_binance_sign_token_burn_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "e3022069d897bf5bf4846d354fcd2c0e85807053be643c8b8c8596306003f7340d43a162722673eb848258b0435b1f49993d0e75d4ae43d03453a3ae57fe6991"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"4wIgadiXv1v0hG01T80sDoWAcFO+ZDyLjIWWMGAD9zQNQ6FiciZz64SCWLBDWx9JmT0OddSuQ9A0U6OuV/5pkQ=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -358,6 +393,11 @@ fn test_binance_sign_htlt_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "51439de2da19fe9fd22137c903cfc5dc87553bf05dca0bb202c0e07c47f9b51269efa27243eb7b55888f5384a84ac1eac6d325c830d1be0ed042838e2dc0f6a9"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"UUOd4toZ/p/SITfJA8/F3IdVO/BdyguyAsDgfEf5tRJp76JyQ+t7VYiPU4SoSsHqxtMlyDDRvg7QQoOOLcD2qQ=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -392,6 +432,11 @@ fn test_binance_sign_deposit_htlt_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "0ca4144c6818e2836d09b4faf3161781d85f9adfc00badb2eaa0953174610a233b81413dafcf84716abad48a4ed3aeb9884d90eb8416eec5d5c0c6930ab60bd0"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A432lgCE4gstB9UOFCL5QQXGJB2fFIKk63nOi/1GDxnk"},"signature":"DKQUTGgY4oNtCbT68xYXgdhfmt/AC62y6qCVMXRhCiM7gUE9r8+EcWq61IpO0665iE2Q64QW7sXVwMaTCrYL0A=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -427,6 +472,11 @@ fn test_binance_sign_claim_htlt_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "fa30ba50111aa31d8329dacb6d044c1c7d54f1cb782bc9aa2a50c3fabce02a4579d75b76ca69a9fab11b676d9da66b5af7aa4c9ad3d18e24fffeb16433be39fb"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"+jC6UBEaox2DKdrLbQRMHH1U8ct4K8mqKlDD+rzgKkV511t2ymmp+rEbZ22dpmta96pMmtPRjiT//rFkM745+w=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -460,6 +510,11 @@ fn test_binance_sign_refund_htlt_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "c9f36142534d16ec8ce656f8eb7370b32206a2d15198b7165acf1e2a18952c9e4570b0f862e1ab7bb868c30781a42c9e3ec0ae08982e8d6c91c55b83c71b7b1e"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"yfNhQlNNFuyM5lb463NwsyIGotFRmLcWWs8eKhiVLJ5FcLD4YuGre7howweBpCyePsCuCJgujWyRxVuDxxt7Hg=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -495,6 +550,11 @@ fn test_binance_sign_transfer_out_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "7eda148e1167b1be1271a788ccf4e3eade1c7e1773e9d2093982d7f802f8f85f35ef550049011728206e4eda1a272f9e96fd95ef3983cad85a29cd14262c22e0"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"ftoUjhFnsb4ScaeIzPTj6t4cfhdz6dIJOYLX+AL4+F8171UASQEXKCBuTtoaJy+elv2V7zmDythaKc0UJiwi4A=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -531,6 +591,11 @@ fn test_binance_sign_side_chain_delegate_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "39302c9975fb2a09ac2b6b6fb1d3b9fb5b4c03630d3d7a7da42b1c6736d6127142a3fcdca0b70a3d065da8d4f4df8b5d9d8f46aeb3627a7d7aa901fe186af34c"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"OTAsmXX7KgmsK2tvsdO5+1tMA2MNPXp9pCscZzbWEnFCo/zcoLcKPQZdqNT034tdnY9GrrNien16qQH+GGrzTA=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -570,6 +635,11 @@ fn test_binance_sign_side_chain_redelegate_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "114c6927423e95ecc831ec763b629b3a40db8feeb330528a941fd74843c0d63b4271b23916770d4901988c1f56b20086e5768a12290ebec265e30a80f8f3d88e"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"EUxpJ0I+lezIMex2O2KbOkDbj+6zMFKKlB/XSEPA1jtCcbI5FncNSQGYjB9WsgCG5XaKEikOvsJl4wqA+PPYjg=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -606,6 +676,11 @@ fn test_binance_sign_side_chain_undelegate_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "a622b7ca7a2875e5eaa675a5ed976b2ec3b8ca055a2b05e7fb471d328bd04df854789437dd06407e41ebb1e5a345604c93663dfb660e223800636c0b94c2e798"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"piK3ynoodeXqpnWl7ZdrLsO4ygVaKwXn+0cdMovQTfhUeJQ33QZAfkHrseWjRWBMk2Y9+2YOIjgAY2wLlMLnmA=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -638,6 +713,11 @@ fn test_binance_sign_time_lock_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "c270822b9515ba486c6a6b3472d388a5aea872ed960c0b53de0fafdc8682ef473a126f01e7dd2c00f04a0138a601b9540f54b14026846de362f7ab7f9fed948b"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"wnCCK5UVukhsams0ctOIpa6ocu2WDAtT3g+v3IaC70c6Em8B590sAPBKATimAblUD1SxQCaEbeNi96t/n+2Uiw=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -673,6 +753,11 @@ fn test_binance_sign_time_relock_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "86ddaa077c8ae551d402fa409cf7e91663982b0542200967c03c0b5876b181353250f689d342f2217624a077b671ce7d09649187e29879f40abbbee9de7ab27c"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"ht2qB3yK5VHUAvpAnPfpFmOYKwVCIAlnwDwLWHaxgTUyUPaJ00LyIXYkoHe2cc59CWSRh+KYefQKu77p3nqyfA=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -704,4 +789,9 @@ fn test_binance_sign_time_unlock_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "da777bfd2032834f59ec9fe69fd6eaa4aca24242dfbc5ec4ef8c435cb9da7eb05ab78e1b8ca9f109657cb77996898f1b59137b3d8f1e00f842e409e18033b347"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"2nd7/SAyg09Z7J/mn9bqpKyiQkLfvF7E74xDXLnafrBat44bjKnxCWV8t3mWiY8bWRN7PY8eAPhC5AnhgDOzRw=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } 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 new file mode 100644 index 00000000000..60430cd5a99 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/binance/binance_wallet_connect.rs @@ -0,0 +1,71 @@ +// 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. + +use crate::chains::binance::{make_token, ACCOUNT_19_PRIVATE_KEY}; +use std::borrow::Cow; +use tw_any_coin::test_utils::sign_utils::AnySignerHelper; +use tw_any_coin::test_utils::wallet_connect_utils::WalletConnectRequestHelper; +use tw_coin_registry::coin_type::CoinType; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_proto::Binance::Proto::{self, mod_SigningInput::OneOforder_oneof as MessageEnum}; +use tw_proto::Common::Proto::SigningError; +use tw_proto::WalletConnect::Proto as WCProto; + +const WC_SIGN_REQUEST_CASE_1: &str = include_str!("data/wc_sign_request_case_1.json"); + +#[test] +fn test_binance_sign_wallet_connect_case_1() { + let input = WCProto::ParseRequestInput { + protocol: WCProto::Protocol::V2, + method: WCProto::Method::CosmosSignAmino, + payload: WC_SIGN_REQUEST_CASE_1.to_string().into(), + }; + + let mut parser = WalletConnectRequestHelper::default(); + let parsing_output = parser.parse(CoinType::Binance, &input); + + 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"; + // bnb13zeh6hs97d5eu2s5qerguhv8ewwue6u4ywa6yf + let expected_to_addr_key_hash = "88b37d5e05f3699e2a1406468e5d87cb9dcceb95"; + let expected_send_order = Proto::SendOrder { + inputs: vec![Proto::mod_SendOrder::Input { + address: expected_from_addr_key_hash.decode_hex().unwrap().into(), + coins: vec![make_token("BNB", 1_001_000_000)], + }], + outputs: vec![Proto::mod_SendOrder::Output { + address: expected_to_addr_key_hash.decode_hex().unwrap().into(), + coins: vec![make_token("BNB", 1_001_000_000)], + }], + }; + let expected_signing_input = Proto::SigningInput { + chain_id: "chain-bnb".into(), + account_number: 19, + sequence: 23, + source: 1, + memo: "".into(), + private_key: Cow::default(), + order_oneof: MessageEnum::send_order(expected_send_order), + }; + assert_eq!(signing_input, expected_signing_input); + + // Set missing private key. + signing_input.private_key = ACCOUNT_19_PRIVATE_KEY.decode_hex().unwrap().into(); + + let mut signer = AnySignerHelper::::default(); + let signing_output = signer.sign(CoinType::Binance, signing_input); + + assert_eq!(signing_output.error, SigningError::OK); + let expected_signature = "3c24c784c6bbf99d54ffabb153edcb6d3c4a774936df5c72a5d32897256f8e062f320fb4753302fb0a96f08c475974d20edfd1a27bbeeda73587f58ddc958975"; + assert_eq!(signing_output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"Amo1kgCI2Yw4iMpoxT38k/RWRgJgbLuH8P5e5TPbOOUC"},"signature":"PCTHhMa7+Z1U/6uxU+3LbTxKd0k231xypdMolyVvjgYvMg+0dTMC+wqW8IxHWXTSDt/Ronu+7ac1h/WN3JWJdQ=="}"#; + assert_eq!(signing_output.signature_json, expected_signature_json); +} diff --git a/rust/tw_any_coin/tests/chains/binance/data/wc_sign_request_case_1.json b/rust/tw_any_coin/tests/chains/binance/data/wc_sign_request_case_1.json new file mode 100644 index 00000000000..3e070de9aef --- /dev/null +++ b/rust/tw_any_coin/tests/chains/binance/data/wc_sign_request_case_1.json @@ -0,0 +1,37 @@ +{ + "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" + } +} \ No newline at end of file diff --git a/rust/tw_any_coin/tests/chains/binance/mod.rs b/rust/tw_any_coin/tests/chains/binance/mod.rs index 5dd8c0fa9fb..a38fee8ce8a 100644 --- a/rust/tw_any_coin/tests/chains/binance/mod.rs +++ b/rust/tw_any_coin/tests/chains/binance/mod.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// 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 @@ -9,6 +9,16 @@ use tw_proto::Binance::Proto; mod binance_address; mod binance_compile; mod binance_sign; +mod binance_wallet_connect; + +const ACCOUNT_12_PRIVATE_KEY: &str = + "90335b9d2153ad1a9799a3ccc070bd64b4164e9642ee1dd48053c33f9a3a05e9"; +const ACCOUNT_19_PRIVATE_KEY: &str = + "95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832"; +const ACCOUNT_15_PRIVATE_KEY: &str = + "eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d"; +const ACCOUNT_16_PRIVATE_KEY: &str = + "851fab89c14f4bbec0cc06f5e445ec065efc641068d78b308c67217d9bd5c88a"; fn make_token(denom: &str, amount: i64) -> Proto::mod_SendOrder::Token { Proto::mod_SendOrder::Token { diff --git a/rust/tw_aptos/src/entry.rs b/rust/tw_aptos/src/entry.rs index e2a16cd0ac8..bed1d3c8d47 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_connector::NoWalletConnector; 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 WalletConnector = NoWalletConnector; #[inline] fn parse_address( diff --git a/rust/tw_bech32_address/src/lib.rs b/rust/tw_bech32_address/src/lib.rs index d8493362219..338e26f6b16 100644 --- a/rust/tw_bech32_address/src/lib.rs +++ b/rust/tw_bech32_address/src/lib.rs @@ -5,7 +5,7 @@ // file LICENSE at the root of the source code distribution tree. use crate::bech32_prefix::Bech32Prefix; -use serde::{Serialize, Serializer}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; @@ -19,6 +19,7 @@ use tw_memory::Data; pub mod bech32_prefix; +#[derive(PartialEq)] pub struct Bech32Address { hrp: String, key_hash: Data, @@ -178,6 +179,18 @@ impl fmt::Debug for Bech32Address { } } +impl<'de> Deserialize<'de> for Bech32Address { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de::Error as DeError; + + let address_str = String::deserialize(deserializer)?; + Bech32Address::from_str(&address_str).map_err(|e| DeError::custom(format!("{e:?}"))) + } +} + impl Serialize for Bech32Address { fn serialize(&self, serializer: S) -> Result where diff --git a/rust/tw_bitcoin/src/entry.rs b/rust/tw_bitcoin/src/entry.rs index a847c4a0690..0ba49752e50 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_connector::NoWalletConnector; 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 WalletConnector = NoWalletConnector; #[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..83ff059e43d 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_connector::WalletConnector; 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 Connector - the module allows to parse transactions received in WalletConnect format. + /// + /// **Optional**. Use `NoWalletConnector` if the blockchain does not support WalletConnect transactions. + type WalletConnector: WalletConnector; /// 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, Parsing signing requests received through WalletConnect. + /// Returns `Ok(None)` if the blockchain does not support WalletConnect transactions. + #[inline] + fn wallet_connector(&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..cc0b39549d3 100644 --- a/rust/tw_coin_entry/src/coin_entry_ext.rs +++ b/rust/tw_coin_entry/src/coin_entry_ext.rs @@ -12,9 +12,11 @@ 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_connector::WalletConnector; use crate::prefix::AddressPrefix; use tw_keypair::tw::{PrivateKey, PublicKey}; use tw_memory::Data; +use tw_proto::WalletConnect::Proto as WCProto; use tw_proto::{deserialize, serialize, ProtoResult}; pub type PrivateKeyBytes = Data; @@ -84,6 +86,13 @@ 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 wallet_connect_parse_request( + &self, + coin: &dyn CoinContext, + input: &[u8], + ) -> SigningResult; } impl CoinEntryExt for T @@ -209,4 +218,18 @@ where deserialize(input)?; Ok(message_signer.verify_message(coin, input)) } + + fn wallet_connect_parse_request( + &self, + coin: &dyn CoinContext, + input: &[u8], + ) -> SigningResult { + let Some(wc_connector) = self.wallet_connector() else { + return Err(SigningError(SigningErrorType::Error_not_supported)); + }; + + let input: WCProto::ParseRequestInput = deserialize(input)?; + let output = wc_connector.parse_request(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..f49968d2816 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_connector; diff --git a/rust/tw_coin_entry/src/modules/wallet_connector.rs b/rust/tw_coin_entry/src/modules/wallet_connector.rs new file mode 100644 index 00000000000..25db0008587 --- /dev/null +++ b/rust/tw_coin_entry/src/modules/wallet_connector.rs @@ -0,0 +1,30 @@ +// 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::WalletConnect::Proto as WCProto; + +pub trait WalletConnector { + /// Parses a signing request received through Wallet Connect. + fn parse_request( + &self, + coin: &dyn CoinContext, + request: WCProto::ParseRequestInput<'_>, + ) -> WCProto::ParseRequestOutput<'static>; +} + +/// `NoWalletConnector` can't be created since there are no enum variants. +pub enum NoWalletConnector {} + +impl WalletConnector for NoWalletConnector { + fn parse_request( + &self, + _coin: &dyn CoinContext, + _request: WCProto::ParseRequestInput<'_>, + ) -> WCProto::ParseRequestOutput<'static> { + panic!("`NoWalletConnector` should never be constructed and used") + } +} 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/rust/tw_cosmos_sdk/src/public_key/secp256k1.rs b/rust/tw_cosmos_sdk/src/public_key/secp256k1.rs index 3558aba8626..230582a8472 100644 --- a/rust/tw_cosmos_sdk/src/public_key/secp256k1.rs +++ b/rust/tw_cosmos_sdk/src/public_key/secp256k1.rs @@ -14,10 +14,21 @@ use tw_memory::Data; use tw_misc::traits::ToBytesVec; use tw_proto::{google, to_any}; +#[derive(Clone)] pub struct Secp256PublicKey { public_key: Data, } +impl Secp256PublicKey { + pub fn from_secp256k1_public_key( + coin: &dyn CoinContext, + public_key: &secp256k1::PublicKey, + ) -> KeyPairResult { + let public_key = prepare_secp256k1_public_key(coin, public_key.compressed().as_slice())?; + Ok(Secp256PublicKey { public_key }) + } +} + impl CosmosPublicKey for Secp256PublicKey { fn from_private_key(coin: &dyn CoinContext, private_key: &tw::PrivateKey) -> KeyPairResult where diff --git a/rust/tw_ethereum/src/entry.rs b/rust/tw_ethereum/src/entry.rs index f69893fd63f..807e6ba3d58 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_connector::NoWalletConnector; 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 WalletConnector = NoWalletConnector; #[inline] fn parse_address( diff --git a/rust/tw_internet_computer/src/entry.rs b/rust/tw_internet_computer/src/entry.rs index 6fb47ba161c..99a76913646 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_connector::NoWalletConnector, }, prefix::NoPrefix, signing_output_error, @@ -43,6 +44,8 @@ impl CoinEntry for InternetComputerEntry { type MessageSigner = NoMessageSigner; + type WalletConnector = NoWalletConnector; + #[inline] fn parse_address( &self, diff --git a/rust/tw_misc/src/serde.rs b/rust/tw_misc/src/serde.rs index 0e1e85d5256..869da0e1dc1 100644 --- a/rust/tw_misc/src/serde.rs +++ b/rust/tw_misc/src/serde.rs @@ -4,13 +4,38 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use serde::{Serialize, Serializer}; +use serde::{Deserialize, Serialize}; -/// Serializes the `value` as a string. -pub fn as_string(value: &T, serializer: S) -> Result -where - T: ToString, - S: Serializer, -{ - value.to_string().serialize(serializer) +pub mod as_string { + use serde::de::Error as DeError; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use std::fmt; + use std::str::FromStr; + + /// Serializes the `value` as a string. + pub fn serialize(value: &T, serializer: S) -> Result + where + T: ToString, + S: Serializer, + { + value.to_string().serialize(serializer) + } + + /// Deserialize a value from a string. + pub fn deserialize<'de, T, D>(deserializer: D) -> Result + where + T: FromStr, + T::Err: fmt::Display, + D: Deserializer<'de>, + { + let str = String::deserialize(deserializer)?; + T::from_str(&str).map_err(DeError::custom) + } +} + +#[derive(Deserialize, Serialize)] +pub struct Typed { + #[serde(rename = "type")] + pub ty: String, + pub value: Value, } diff --git a/rust/tw_ronin/src/entry.rs b/rust/tw_ronin/src/entry.rs index 36bb8fb96b2..79020884ff9 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_connector::NoWalletConnector; 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 WalletConnector = NoWalletConnector; #[inline] fn parse_address( diff --git a/src/interface/TWWalletConnectRequest.cpp b/src/interface/TWWalletConnectRequest.cpp new file mode 100644 index 00000000000..621f3c9cbcd --- /dev/null +++ b/src/interface/TWWalletConnectRequest.cpp @@ -0,0 +1,20 @@ +// 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 + +#include "rust/Wrapper.h" + +using namespace TW; + +TWData* _Nonnull TWWalletConnectRequestParse(enum TWCoinType coin, TWData* _Nonnull input) { + const Data& inputData = *reinterpret_cast(input); + Rust::TWDataWrapper twInputData = inputData; + + Rust::TWDataWrapper twOutputData = Rust::tw_wallet_connect_request_parse(static_cast(coin), twInputData.get()); + auto outputData = twOutputData.toDataOrDefault(); + return TWDataCreateWithBytes(outputData.data(), outputData.size()); +} diff --git a/src/proto/Binance.proto b/src/proto/Binance.proto index 00df9f21fd2..01aa30a3698 100644 --- a/src/proto/Binance.proto +++ b/src/proto/Binance.proto @@ -382,4 +382,10 @@ message SigningOutput { // error description in case of error string error_message = 3; + + // Signature bytes. + bytes signature = 4; + + // Signature JSON string. + string signature_json = 5; } diff --git a/src/proto/WalletConnect.proto b/src/proto/WalletConnect.proto new file mode 100644 index 00000000000..37b8a9796c0 --- /dev/null +++ b/src/proto/WalletConnect.proto @@ -0,0 +1,44 @@ +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. +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". + Method method = 2; + + // Transaction payload to sign. + // Basically, a JSON object. + string payload = 3; +} + +message ParseRequestOutput { + // OK (=0) or other codes in case of error + Common.Proto.SigningError error = 1; + + // error description in case of error + 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/swift/Tests/Blockchains/BinanceChainTests.swift b/swift/Tests/Blockchains/BinanceChainTests.swift index 90df7dd3573..78ad5dd1bc6 100644 --- a/swift/Tests/Blockchains/BinanceChainTests.swift +++ b/swift/Tests/Blockchains/BinanceChainTests.swift @@ -238,4 +238,35 @@ class BinanceChainTests: XCTestCase { } queue.waitUntilAllOperationsAreFinished() } + + func testSignFromWalletConnectRequest() throws { + // Step 1: Parse a signing request received through WalletConnect. + + let requestPayload = """ + {"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"}} + """ + let parsingInput = WalletConnectParseRequestInput.with { + $0.method = .cosmosSignAmino + $0.payload = requestPayload + } + let parsingInputBytes = try parsingInput.serializedData() + + let parsingOutputBytes = WalletConnectRequest.parse(coin: .binance, input: parsingInputBytes) + let parsingOutput = try WalletConnectParseRequestOutput(serializedData: parsingOutputBytes) + + var signingInput = parsingOutput.binance + + // Step 2: Set missing fields. + + signingInput.privateKey = Data(hexString: "95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832")! + + // Step 3: Sign the transaction. + + let output: BinanceSigningOutput = AnySigner.sign(input: signingInput, coin: .binance) + + let expected = """ + {"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"Amo1kgCI2Yw4iMpoxT38k/RWRgJgbLuH8P5e5TPbOOUC"},"signature":"PCTHhMa7+Z1U/6uxU+3LbTxKd0k231xypdMolyVvjgYvMg+0dTMC+wqW8IxHWXTSDt/Ronu+7ac1h/WN3JWJdQ=="} + """ + XCTAssertEqual(output.signatureJson, expected) + } } diff --git a/tests/chains/Binance/TWWalletConnectSigning.cpp b/tests/chains/Binance/TWWalletConnectSigning.cpp new file mode 100644 index 00000000000..8a463f99470 --- /dev/null +++ b/tests/chains/Binance/TWWalletConnectSigning.cpp @@ -0,0 +1,54 @@ +// 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 "HexCoding.h" +#include "proto/Binance.pb.h" +#include "proto/WalletConnect.pb.h" +#include "Coin.h" +#include +#include + +#include "TestUtilities.h" +#include + +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(parsinginputData.c_str()), parsinginputData.size())); + + const auto outputDataPtr = WRAPD(TWWalletConnectRequestParse(TWCoinTypeBinance, parsingInputDataPtr.get())); + + WalletConnect::Proto::ParseRequestOutput parsingOutput; + parsingOutput.ParseFromArray( + TWDataBytes(outputDataPtr.get()), + static_cast(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())); diff --git a/tests/interface/TWTransactionCompilerTests.cpp b/tests/interface/TWTransactionCompilerTests.cpp index f2be40920e9..9746a4e9b8f 100644 --- a/tests/interface/TWTransactionCompilerTests.cpp +++ b/tests/interface/TWTransactionCompilerTests.cpp @@ -111,7 +111,6 @@ TEST(TWTransactionCompiler, ExternalSignatureSignBinance) { "253cf264c69180ec31814929b5de62088c0c5a45e8a816d1208fc5366bb8b041781a6771248550d04094c3d7a5" "04f9e8310679"; { - EXPECT_EQ(TWDataSize(outputData.get()), 189ul); Binance::Proto::SigningOutput output; ASSERT_TRUE(output.ParseFromArray(TWDataBytes(outputData.get()), (int)TWDataSize(outputData.get())));