diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkSigner.kt
index 6fb0edc455c..59b3fcdd465 100644
--- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkSigner.kt
+++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkSigner.kt
@@ -25,7 +25,6 @@ class TestTheOpenNetworkSigner {
val privateKey = PrivateKey("c38f49de2fb13223a9e7d37d5d0ffbdd89a5eb7c8b0ee4d1c299f2cefe7dc4a0".toHexByteArray())
val transfer = TheOpenNetwork.Transfer.newBuilder()
- .setWalletVersion(TheOpenNetwork.WalletVersion.WALLET_V4_R2)
.setDest("EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q")
.setAmount(10)
.setMode(TheOpenNetwork.SendMode.PAY_FEES_SEPARATELY_VALUE or TheOpenNetwork.SendMode.IGNORE_ACTION_PHASE_ERRORS_VALUE)
@@ -37,6 +36,7 @@ class TestTheOpenNetworkSigner {
.addMessages(transfer)
.setSequenceNumber(6)
.setExpireAt(1671132440)
+ .setWalletVersion(TheOpenNetwork.WalletVersion.WALLET_V4_R2)
.build()
val output = AnySigner.sign(input, CoinType.TON, SigningOutput.parser())
@@ -59,7 +59,6 @@ class TestTheOpenNetworkSigner {
.build()
val transfer = TheOpenNetwork.Transfer.newBuilder()
- .setWalletVersion(TheOpenNetwork.WalletVersion.WALLET_V4_R2)
.setDest("EQBiaD8PO1NwfbxSkwbcNT9rXDjqhiIvXWymNO-edV0H5lja")
.setAmount(100 * 1000 * 1000)
.setMode(TheOpenNetwork.SendMode.PAY_FEES_SEPARATELY_VALUE or TheOpenNetwork.SendMode.IGNORE_ACTION_PHASE_ERRORS_VALUE)
@@ -72,6 +71,7 @@ class TestTheOpenNetworkSigner {
.addMessages(transfer)
.setSequenceNumber(1)
.setExpireAt(1787693046)
+ .setWalletVersion(TheOpenNetwork.WalletVersion.WALLET_V4_R2)
.build()
val output = AnySigner.sign(input, CoinType.TON, SigningOutput.parser())
@@ -103,7 +103,6 @@ class TestTheOpenNetworkSigner {
.build()
val transfer = TheOpenNetwork.Transfer.newBuilder()
- .setWalletVersion(TheOpenNetwork.WalletVersion.WALLET_V4_R2)
.setDest(dogeChatbotDeployingAddress)
// 0.069 TON
.setAmount(69_000_000)
@@ -116,6 +115,7 @@ class TestTheOpenNetworkSigner {
.addMessages(transfer)
.setSequenceNumber(4)
.setExpireAt(1721939714)
+ .setWalletVersion(TheOpenNetwork.WalletVersion.WALLET_V4_R2)
.build()
val output = AnySigner.sign(input, CoinType.TON, SigningOutput.parser())
diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestLiquidStaking.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestLiquidStaking.kt
index 41adb7f1d4f..44701d5f2b1 100644
--- a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestLiquidStaking.kt
+++ b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestLiquidStaking.kt
@@ -29,7 +29,7 @@ class TestLiquidStaking {
stake = LiquidStaking.Stake.newBuilder().apply {
amount = "1000000000000000000"
asset = LiquidStaking.Asset.newBuilder().apply {
- stakingToken = LiquidStaking.Coin.MATIC
+ stakingToken = LiquidStaking.Coin.POL
}.build()
}.build()
}
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 c76b1b25d69..9b1194bbb68 100644
--- a/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs
+++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs
@@ -14,6 +14,7 @@ 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::transaction_decoder::NoTransactionDecoder;
+use tw_coin_entry::modules::transaction_util::NoTransactionUtil;
use tw_coin_entry::modules::wallet_connector::NoWalletConnector;
use tw_coin_entry::prefix::NoPrefix;
use tw_keypair::tw::PublicKey;
@@ -35,6 +36,7 @@ impl CoinEntry for {BLOCKCHAIN}Entry {
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
+ type TransactionUtil = NoTransactionUtil;
#[inline]
fn parse_address(
diff --git a/docs/registry.md b/docs/registry.md
index b278eaba2a0..bfbc3b27235 100644
--- a/docs/registry.md
+++ b/docs/registry.md
@@ -75,7 +75,7 @@ This list is generated from [./registry.json](../registry.json)
| 889 | Viction | VIC | | |
| 899 | eCash | XEC | | |
| 931 | THORChain | RUNE | | |
-| 966 | Polygon | MATIC | | |
+| 966 | Polygon | POL | | |
| 996 | OKX Chain | OKT | | |
| 999 | Bitcoin Diamond | BCD | | |
| 1001 | ThunderCore | TT | | |
diff --git a/include/TrustWalletCore/TWTransactionUtil.h b/include/TrustWalletCore/TWTransactionUtil.h
new file mode 100644
index 00000000000..55b2a811428
--- /dev/null
+++ b/include/TrustWalletCore/TWTransactionUtil.h
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+#pragma once
+
+#include "TWBase.h"
+#include "TWCoinType.h"
+#include "TWData.h"
+
+TW_EXTERN_C_BEGIN
+
+TW_EXPORT_STRUCT
+struct TWTransactionUtil;
+
+/// Calculate the TX hash of a transaction.
+///
+/// \param coin coin type.
+/// \param encodedTx encoded transaction data.
+/// \return The TX hash of a transaction, If the input is invalid or the chain is unsupported, null is returned.
+TW_EXPORT_STATIC_METHOD
+TWString* _Nullable TWTransactionUtilCalcTxHash(enum TWCoinType coinType, TWString* _Nonnull encodedTx);
+
+TW_EXTERN_C_END
diff --git a/protobuf-plugin/CMakeLists.txt b/protobuf-plugin/CMakeLists.txt
index 52694a9c14e..237789be7be 100644
--- a/protobuf-plugin/CMakeLists.txt
+++ b/protobuf-plugin/CMakeLists.txt
@@ -2,30 +2,25 @@
#
# Copyright © 2017 Trust Wallet.
-cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(TrustWalletCoreProtobufPlugin)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14" CACHE STRING "Minimum OS X deployment version" FORCE)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
-if ("$ENV{PREFIX}" STREQUAL "")
+if("$ENV{PREFIX}" STREQUAL "")
set(PREFIX "${CMAKE_SOURCE_DIR}/../build/local")
else()
set(PREFIX "$ENV{PREFIX}")
endif()
-include_directories(${PREFIX}/include)
-link_directories(${PREFIX}/lib)
+find_package(Protobuf CONFIG REQUIRED PATH ${PREFIX}/lib/pkgconfig)
-find_package(Protobuf REQUIRED PATH ${PREFIX}/lib/pkgconfig)
-include_directories(${Protobuf_INCLUDE_DIRS})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
+add_executable(protoc-gen-c-typedef c_typedef.cc)
+target_link_libraries(protoc-gen-c-typedef protobuf::libprotobuf protobuf::libprotoc)
-add_executable(protoc-gen-c-typedef c_typedef.cc ${PROTO_SRCS} ${PROTO_HDRS})
-target_link_libraries(protoc-gen-c-typedef protobuf -lprotoc -pthread)
-
-add_executable(protoc-gen-swift-typealias swift_typealias.cc ${PROTO_SRCS} ${PROTO_HDRS})
-target_link_libraries(protoc-gen-swift-typealias protobuf -lprotoc -pthread)
+add_executable(protoc-gen-swift-typealias swift_typealias.cc)
+target_link_libraries(protoc-gen-swift-typealias protobuf::libprotobuf protobuf::libprotoc)
install(TARGETS protoc-gen-c-typedef protoc-gen-swift-typealias DESTINATION bin)
diff --git a/protobuf-plugin/c_typedef.cc b/protobuf-plugin/c_typedef.cc
index 5197185f9ac..1352898cb73 100644
--- a/protobuf-plugin/c_typedef.cc
+++ b/protobuf-plugin/c_typedef.cc
@@ -14,7 +14,7 @@ class Generator : public compiler::CodeGenerator {
return "TW" + proto_file.substr(0, index) + "Proto.h";
}
- bool Generate(const FileDescriptor* file, const std::string& parameter, compiler::GeneratorContext* generator_context, string* error) const {
+ bool Generate(const FileDescriptor* file, const std::string& parameter, compiler::GeneratorContext* generator_context, std::string* error) const {
std::unique_ptr output(generator_context->Open(GetOutputFilename(file->name())));
io::Printer printer(output.get(), '$');
diff --git a/protobuf-plugin/swift_typealias.cc b/protobuf-plugin/swift_typealias.cc
index 40e84df0ee9..3d0983c2ca3 100644
--- a/protobuf-plugin/swift_typealias.cc
+++ b/protobuf-plugin/swift_typealias.cc
@@ -16,7 +16,7 @@ class Generator : public compiler::CodeGenerator {
return proto_file.substr(0, index) + "+Proto.swift";
}
- bool Generate(const FileDescriptor* file, const std::string& parameter, compiler::GeneratorContext* generator_context, string* error) const {
+ bool Generate(const FileDescriptor* file, const std::string& parameter, compiler::GeneratorContext* generator_context, std::string* error) const {
std::unique_ptr output(generator_context->Open(GetOutputFilename(file->name())));
io::Printer printer(output.get(), '$');
diff --git a/registry.json b/registry.json
index ab83bc849f1..f04b7224460 100644
--- a/registry.json
+++ b/registry.json
@@ -3108,7 +3108,7 @@
"id": "polygon",
"name": "Polygon",
"coinId": 966,
- "symbol": "MATIC",
+ "symbol": "POL",
"decimals": 18,
"blockchain": "Ethereum",
"derivation": [
@@ -3129,9 +3129,9 @@
},
"info": {
"url": "https://polygon.technology",
- "source": "https://github.com/maticnetwork/contracts",
+ "source": "https://github.com/maticnetwork",
"rpc": "https://polygon-rpc.com",
- "documentation": "https://eth.wiki/json-rpc/API"
+ "documentation": "https://docs.polygon.technology"
}
},
{
diff --git a/rust/chains/tw_aptos/src/entry.rs b/rust/chains/tw_aptos/src/entry.rs
index bee4fb47ddc..f9a46c55049 100644
--- a/rust/chains/tw_aptos/src/entry.rs
+++ b/rust/chains/tw_aptos/src/entry.rs
@@ -4,6 +4,7 @@
use crate::address::Address;
use crate::compiler::Compiler;
+use crate::modules::transaction_util::AptosTransactionUtil;
use crate::signer::Signer;
use std::str::FromStr;
use tw_coin_entry::coin_context::CoinContext;
@@ -35,6 +36,7 @@ impl CoinEntry for AptosEntry {
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
+ type TransactionUtil = AptosTransactionUtil;
#[inline]
fn parse_address(
@@ -102,4 +104,9 @@ impl CoinEntry for AptosEntry {
fn message_signer(&self) -> Option {
None
}
+
+ #[inline]
+ fn transaction_util(&self) -> Option {
+ Some(AptosTransactionUtil)
+ }
}
diff --git a/rust/chains/tw_aptos/src/lib.rs b/rust/chains/tw_aptos/src/lib.rs
index 99f90ae428d..5388e03f4e7 100644
--- a/rust/chains/tw_aptos/src/lib.rs
+++ b/rust/chains/tw_aptos/src/lib.rs
@@ -12,6 +12,7 @@ pub mod nft;
pub mod compiler;
pub mod liquid_staking;
+pub mod modules;
pub mod signer;
pub mod transaction;
pub mod transaction_builder;
diff --git a/rust/chains/tw_aptos/src/modules/mod.rs b/rust/chains/tw_aptos/src/modules/mod.rs
new file mode 100644
index 00000000000..c083bb0102e
--- /dev/null
+++ b/rust/chains/tw_aptos/src/modules/mod.rs
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+pub mod transaction_util;
diff --git a/rust/chains/tw_aptos/src/modules/transaction_util.rs b/rust/chains/tw_aptos/src/modules/transaction_util.rs
new file mode 100644
index 00000000000..6b7034477f8
--- /dev/null
+++ b/rust/chains/tw_aptos/src/modules/transaction_util.rs
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+use tw_coin_entry::coin_context::CoinContext;
+use tw_coin_entry::error::prelude::*;
+use tw_coin_entry::modules::transaction_util::TransactionUtil;
+use tw_encoding::hex;
+use tw_hash::sha3::sha3_256;
+
+pub struct AptosTransactionUtil;
+
+impl TransactionUtil for AptosTransactionUtil {
+ fn calc_tx_hash(&self, coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult {
+ Self::calc_tx_hash_impl(coin, encoded_tx)
+ }
+}
+
+impl AptosTransactionUtil {
+ fn calc_tx_hash_impl(_coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult {
+ let txn_bytes = hex::decode(encoded_tx).map_err(|_| SigningErrorType::Error_input_parse)?;
+
+ // See: https://github.com/aptos-labs/aptos-ts-sdk/blob/f54cac824a41e41dea09c7a6916858a8604dc901/src/api/transaction.ts#L118
+ let prefix = sha3_256("APTOS::Transaction".as_bytes());
+
+ let mut hash_message = Vec::new();
+ hash_message.extend_from_slice(&prefix);
+ // 0 is the index of the enum `Transaction`, see: https://github.com/aptos-labs/aptos-core/blob/6a130c1cca274a5cfdb4a65b441cd5fe61b6c15b/types/src/transaction/mod.rs#L1939
+ hash_message.push(0);
+ hash_message.extend_from_slice(&txn_bytes);
+
+ let tx_hash = sha3_256(&hash_message);
+ Ok(hex::encode(tx_hash, true))
+ }
+}
diff --git a/rust/chains/tw_binance/src/entry.rs b/rust/chains/tw_binance/src/entry.rs
index cd552881c9d..55930473d53 100644
--- a/rust/chains/tw_binance/src/entry.rs
+++ b/rust/chains/tw_binance/src/entry.rs
@@ -16,6 +16,7 @@ use tw_coin_entry::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::transaction_decoder::NoTransactionDecoder;
+use tw_coin_entry::modules::transaction_util::NoTransactionUtil;
use tw_keypair::tw::PublicKey;
use tw_proto::Binance::Proto;
use tw_proto::TxCompiler::Proto as CompilerProto;
@@ -35,6 +36,7 @@ impl CoinEntry for BinanceEntry {
type MessageSigner = NoMessageSigner;
type WalletConnector = BinanceWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
+ type TransactionUtil = NoTransactionUtil;
#[inline]
fn parse_address(
diff --git a/rust/chains/tw_cosmos/src/entry.rs b/rust/chains/tw_cosmos/src/entry.rs
index 67a15bc2d1e..dc2e9de681b 100644
--- a/rust/chains/tw_cosmos/src/entry.rs
+++ b/rust/chains/tw_cosmos/src/entry.rs
@@ -16,6 +16,7 @@ use tw_cosmos_sdk::address::{Address, Bech32Prefix};
use tw_cosmos_sdk::context::StandardCosmosContext;
use tw_cosmos_sdk::modules::compiler::tw_compiler::TWTransactionCompiler;
use tw_cosmos_sdk::modules::signer::tw_signer::TWSigner;
+use tw_cosmos_sdk::modules::transaction_util::CosmosTransactionUtil;
use tw_keypair::tw;
use tw_proto::Cosmos::Proto;
use tw_proto::TxCompiler::Proto as CompilerProto;
@@ -35,6 +36,7 @@ impl CoinEntry for CosmosEntry {
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
+ type TransactionUtil = CosmosTransactionUtil;
#[inline]
fn parse_address(
@@ -95,4 +97,9 @@ impl CoinEntry for CosmosEntry {
public_keys,
)
}
+
+ #[inline]
+ fn transaction_util(&self) -> Option {
+ Some(CosmosTransactionUtil::::default())
+ }
}
diff --git a/rust/chains/tw_ethereum/src/entry.rs b/rust/chains/tw_ethereum/src/entry.rs
index 13f48c92701..aad3d6f90b5 100644
--- a/rust/chains/tw_ethereum/src/entry.rs
+++ b/rust/chains/tw_ethereum/src/entry.rs
@@ -18,6 +18,7 @@ use tw_evm::evm_entry::EvmEntry;
use tw_evm::modules::compiler::Compiler;
use tw_evm::modules::message_signer::EthMessageSigner;
use tw_evm::modules::signer::Signer;
+use tw_evm::modules::transaction_util::EvmTransactionUtil;
use tw_keypair::tw::PublicKey;
use tw_proto::Ethereum::Proto;
use tw_proto::TxCompiler::Proto as CompilerProto;
@@ -37,6 +38,7 @@ impl CoinEntry for EthereumEntry {
type MessageSigner = EthMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
+ type TransactionUtil = EvmTransactionUtil;
#[inline]
fn parse_address(
@@ -100,6 +102,11 @@ impl CoinEntry for EthereumEntry {
fn message_signer(&self) -> Option {
Some(EthMessageSigner)
}
+
+ #[inline]
+ fn transaction_util(&self) -> Option {
+ Some(EvmTransactionUtil)
+ }
}
impl EvmEntry for EthereumEntry {
diff --git a/rust/chains/tw_greenfield/src/entry.rs b/rust/chains/tw_greenfield/src/entry.rs
index 01ee90e50ae..448cbe54f15 100644
--- a/rust/chains/tw_greenfield/src/entry.rs
+++ b/rust/chains/tw_greenfield/src/entry.rs
@@ -14,6 +14,7 @@ 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::transaction_decoder::NoTransactionDecoder;
+use tw_coin_entry::modules::transaction_util::NoTransactionUtil;
use tw_coin_entry::modules::wallet_connector::NoWalletConnector;
use tw_coin_entry::prefix::NoPrefix;
use tw_keypair::tw::PublicKey;
@@ -35,6 +36,7 @@ impl CoinEntry for GreenfieldEntry {
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
+ type TransactionUtil = NoTransactionUtil;
#[inline]
fn parse_address(
diff --git a/rust/chains/tw_internet_computer/src/entry.rs b/rust/chains/tw_internet_computer/src/entry.rs
index a11d07ad9cc..3e9be8f75cb 100644
--- a/rust/chains/tw_internet_computer/src/entry.rs
+++ b/rust/chains/tw_internet_computer/src/entry.rs
@@ -5,6 +5,7 @@
use std::str::FromStr;
use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder;
+use tw_coin_entry::modules::transaction_util::NoTransactionUtil;
use tw_coin_entry::{
coin_context::CoinContext,
coin_entry::CoinEntry,
@@ -16,7 +17,6 @@ use tw_coin_entry::{
prefix::NoPrefix,
signing_output_error,
};
-
use tw_proto::{
Common::Proto::SigningError as CommonError, InternetComputer::Proto,
TxCompiler::Proto as CompilerProto,
@@ -39,6 +39,7 @@ impl CoinEntry for InternetComputerEntry {
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
+ type TransactionUtil = NoTransactionUtil;
#[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 86a9e715b73..e3964e6454c 100644
--- a/rust/chains/tw_native_evmos/src/entry.rs
+++ b/rust/chains/tw_native_evmos/src/entry.rs
@@ -12,6 +12,7 @@ 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::transaction_decoder::NoTransactionDecoder;
+use tw_coin_entry::modules::transaction_util::NoTransactionUtil;
use tw_coin_entry::modules::wallet_connector::NoWalletConnector;
use tw_cosmos_sdk::address::{Address, Bech32Prefix};
use tw_cosmos_sdk::modules::compiler::tw_compiler::TWTransactionCompiler;
@@ -35,6 +36,7 @@ impl CoinEntry for NativeEvmosEntry {
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
+ type TransactionUtil = NoTransactionUtil;
#[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 3a16d84836c..bca5b9759dd 100644
--- a/rust/chains/tw_native_injective/src/entry.rs
+++ b/rust/chains/tw_native_injective/src/entry.rs
@@ -12,6 +12,7 @@ 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::transaction_decoder::NoTransactionDecoder;
+use tw_coin_entry::modules::transaction_util::NoTransactionUtil;
use tw_coin_entry::modules::wallet_connector::NoWalletConnector;
use tw_cosmos_sdk::address::{Address, Bech32Prefix};
use tw_cosmos_sdk::modules::compiler::tw_compiler::TWTransactionCompiler;
@@ -35,6 +36,7 @@ impl CoinEntry for NativeInjectiveEntry {
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
+ type TransactionUtil = NoTransactionUtil;
#[inline]
fn parse_address(
diff --git a/rust/chains/tw_ronin/src/entry.rs b/rust/chains/tw_ronin/src/entry.rs
index 1c3a9ab674d..49a21c71c0c 100644
--- a/rust/chains/tw_ronin/src/entry.rs
+++ b/rust/chains/tw_ronin/src/entry.rs
@@ -12,6 +12,7 @@ use tw_coin_entry::error::prelude::*;
use tw_coin_entry::modules::json_signer::NoJsonSigner;
use tw_coin_entry::modules::plan_builder::NoPlanBuilder;
use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder;
+use tw_coin_entry::modules::transaction_util::NoTransactionUtil;
use tw_coin_entry::modules::wallet_connector::NoWalletConnector;
use tw_coin_entry::prefix::NoPrefix;
use tw_evm::evm_entry::EvmEntry;
@@ -37,6 +38,7 @@ impl CoinEntry for RoninEntry {
type MessageSigner = EthMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
+ type TransactionUtil = NoTransactionUtil;
#[inline]
fn parse_address(
diff --git a/rust/chains/tw_solana/src/entry.rs b/rust/chains/tw_solana/src/entry.rs
index cc30d20c6fa..5fd330739fe 100644
--- a/rust/chains/tw_solana/src/entry.rs
+++ b/rust/chains/tw_solana/src/entry.rs
@@ -5,6 +5,7 @@
use crate::address::SolanaAddress;
use crate::compiler::SolanaCompiler;
use crate::modules::transaction_decoder::SolanaTransactionDecoder;
+use crate::modules::transaction_util::SolanaTransactionUtil;
use crate::modules::wallet_connect::connector::SolanaWalletConnector;
use crate::signer::SolanaSigner;
use std::str::FromStr;
@@ -34,6 +35,7 @@ impl CoinEntry for SolanaEntry {
type MessageSigner = NoMessageSigner;
type WalletConnector = SolanaWalletConnector;
type TransactionDecoder = SolanaTransactionDecoder;
+ type TransactionUtil = SolanaTransactionUtil;
#[inline]
fn parse_address(
@@ -99,4 +101,9 @@ impl CoinEntry for SolanaEntry {
fn transaction_decoder(&self) -> Option {
Some(SolanaTransactionDecoder)
}
+
+ #[inline]
+ fn transaction_util(&self) -> Option {
+ Some(SolanaTransactionUtil)
+ }
}
diff --git a/rust/chains/tw_solana/src/modules/mod.rs b/rust/chains/tw_solana/src/modules/mod.rs
index 253aec84a3d..074e5cbc8a7 100644
--- a/rust/chains/tw_solana/src/modules/mod.rs
+++ b/rust/chains/tw_solana/src/modules/mod.rs
@@ -12,6 +12,7 @@ pub mod instruction_builder;
pub mod message_builder;
pub mod proto_builder;
pub mod transaction_decoder;
+pub mod transaction_util;
pub mod tx_signer;
pub mod utils;
pub mod wallet_connect;
diff --git a/rust/chains/tw_solana/src/modules/transaction_decoder.rs b/rust/chains/tw_solana/src/modules/transaction_decoder.rs
index 3a98fb58203..02a441a464a 100644
--- a/rust/chains/tw_solana/src/modules/transaction_decoder.rs
+++ b/rust/chains/tw_solana/src/modules/transaction_decoder.rs
@@ -22,7 +22,7 @@ impl TransactionDecoder for SolanaTransactionDecoder {
}
impl SolanaTransactionDecoder {
- fn decode_transaction_impl(
+ pub(crate) fn decode_transaction_impl(
_coin: &dyn CoinContext,
tx: &[u8],
) -> SigningResult> {
diff --git a/rust/chains/tw_solana/src/modules/transaction_util.rs b/rust/chains/tw_solana/src/modules/transaction_util.rs
new file mode 100644
index 00000000000..fdde5d6952c
--- /dev/null
+++ b/rust/chains/tw_solana/src/modules/transaction_util.rs
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+use crate::modules::transaction_decoder::SolanaTransactionDecoder;
+use tw_coin_entry::coin_context::CoinContext;
+use tw_coin_entry::error::prelude::*;
+use tw_coin_entry::modules::transaction_util::TransactionUtil;
+use tw_encoding::base64;
+use tw_encoding::base64::STANDARD;
+
+pub struct SolanaTransactionUtil;
+
+impl TransactionUtil for SolanaTransactionUtil {
+ fn calc_tx_hash(&self, coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult {
+ Self::calc_tx_hash_impl(coin, encoded_tx)
+ }
+}
+
+impl SolanaTransactionUtil {
+ fn calc_tx_hash_impl(coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult {
+ // Solana signed transactions can be encoded in either base64 or base58. For more information, see: https://solana.com/docs/rpc/http/sendtransaction
+ // Currently, this function only accepts base64 encoding.
+ let tx_bytes = base64::decode(encoded_tx, STANDARD)?;
+ let decoded_tx_output = SolanaTransactionDecoder::decode_transaction_impl(coin, &tx_bytes)?;
+
+ let first_sig = decoded_tx_output
+ .transaction
+ .as_ref()
+ .and_then(|tx| tx.signatures.first())
+ .or_tw_err(SigningErrorType::Error_input_parse)
+ .context("There is no transaction signatures. Looks like it hasn't been signed yet")?;
+
+ // Tx hash is the first signature
+ Ok(first_sig.signature.to_string())
+ }
+}
diff --git a/rust/chains/tw_sui/src/entry.rs b/rust/chains/tw_sui/src/entry.rs
index 72ec4866299..63bd4e92df7 100644
--- a/rust/chains/tw_sui/src/entry.rs
+++ b/rust/chains/tw_sui/src/entry.rs
@@ -4,6 +4,7 @@
use crate::address::SuiAddress;
use crate::compiler::SuiCompiler;
+use crate::modules::transaction_util::SuiTransactionUtil;
use crate::signer::SuiSigner;
use std::str::FromStr;
use tw_coin_entry::coin_context::CoinContext;
@@ -35,6 +36,7 @@ impl CoinEntry for SuiEntry {
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
+ type TransactionUtil = SuiTransactionUtil;
#[inline]
fn parse_address(
@@ -93,4 +95,9 @@ impl CoinEntry for SuiEntry {
) -> Self::SigningOutput {
SuiCompiler::compile(coin, input, signatures, public_keys)
}
+
+ #[inline]
+ fn transaction_util(&self) -> Option {
+ Some(SuiTransactionUtil)
+ }
}
diff --git a/rust/chains/tw_sui/src/modules/mod.rs b/rust/chains/tw_sui/src/modules/mod.rs
index 7398efd0e9b..32f928c8ec3 100644
--- a/rust/chains/tw_sui/src/modules/mod.rs
+++ b/rust/chains/tw_sui/src/modules/mod.rs
@@ -2,5 +2,6 @@
//
// Copyright © 2017 Trust Wallet.
+pub mod transaction_util;
pub mod tx_builder;
pub mod tx_signer;
diff --git a/rust/chains/tw_sui/src/modules/transaction_util.rs b/rust/chains/tw_sui/src/modules/transaction_util.rs
new file mode 100644
index 00000000000..f2ad4ff585f
--- /dev/null
+++ b/rust/chains/tw_sui/src/modules/transaction_util.rs
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+use tw_coin_entry::coin_context::CoinContext;
+use tw_coin_entry::error::prelude::*;
+use tw_coin_entry::modules::transaction_util::TransactionUtil;
+use tw_encoding::base58::{self, Alphabet};
+use tw_encoding::base64::{self, STANDARD};
+use tw_hash::blake2::blake2_b;
+use tw_hash::H256;
+
+pub struct SuiTransactionUtil;
+
+impl TransactionUtil for SuiTransactionUtil {
+ fn calc_tx_hash(&self, coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult {
+ Self::calc_tx_hash_impl(coin, encoded_tx)
+ }
+}
+
+// See: https://github.com/mofalabs/sui/blob/74908b3ad8b82e5e401d5017fed4fa7dc2361569/lib/builder/hash.dart#L7
+fn hash_typed_data(type_tag: &str, data: &[u8]) -> Result, tw_hash::Error> {
+ let type_tag_bytes: Vec = format!("{}::", type_tag).into_bytes();
+
+ let mut data_with_tag = Vec::with_capacity(type_tag_bytes.len() + data.len());
+ data_with_tag.extend_from_slice(&type_tag_bytes);
+ data_with_tag.extend_from_slice(data);
+
+ blake2_b(&data_with_tag, H256::LEN)
+}
+
+impl SuiTransactionUtil {
+ fn calc_tx_hash_impl(_coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult {
+ let tx = base64::decode(encoded_tx, STANDARD)?;
+ let tx_hash = hash_typed_data("TransactionData", &tx)
+ .map_err(|_| SigningErrorType::Error_input_parse)?;
+
+ Ok(base58::encode(&tx_hash, Alphabet::Bitcoin))
+ }
+}
diff --git a/rust/chains/tw_thorchain/src/entry.rs b/rust/chains/tw_thorchain/src/entry.rs
index 970701b0d14..9f93134601f 100644
--- a/rust/chains/tw_thorchain/src/entry.rs
+++ b/rust/chains/tw_thorchain/src/entry.rs
@@ -13,6 +13,7 @@ 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::transaction_decoder::NoTransactionDecoder;
+use tw_coin_entry::modules::transaction_util::NoTransactionUtil;
use tw_coin_entry::modules::wallet_connector::NoWalletConnector;
use tw_cosmos_sdk::address::{Address, Bech32Prefix};
use tw_keypair::tw;
@@ -34,6 +35,7 @@ impl CoinEntry for ThorchainEntry {
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
+ type TransactionUtil = NoTransactionUtil;
#[inline]
fn parse_address(
diff --git a/rust/chains/tw_ton/Cargo.toml b/rust/chains/tw_ton/Cargo.toml
index 61485b8f034..145508522a6 100644
--- a/rust/chains/tw_ton/Cargo.toml
+++ b/rust/chains/tw_ton/Cargo.toml
@@ -11,6 +11,7 @@ tw_hash = { path = "../../tw_hash" }
tw_keypair = { path = "../../tw_keypair" }
tw_memory = { path = "../../tw_memory" }
tw_number = { path = "../../tw_number" }
+tw_misc = { path = "../../tw_misc" }
tw_proto = { path = "../../tw_proto" }
tw_ton_sdk = { path = "../../frameworks/tw_ton_sdk" }
zeroize = "1.8.1"
diff --git a/rust/chains/tw_ton/resources/wallet/wallet_v5r1.code b/rust/chains/tw_ton/resources/wallet/wallet_v5r1.code
new file mode 100644
index 00000000000..586cfc39e88
--- /dev/null
+++ b/rust/chains/tw_ton/resources/wallet/wallet_v5r1.code
@@ -0,0 +1 @@
+te6cckECFAEAAoEAART/APSkE/S88sgLAQIBIAINAgFIAwQC3NAg10nBIJFbj2Mg1wsfIIIQZXh0br0hghBzaW50vbCSXwPgghBleHRuuo60gCDXIQHQdNch+kAw+kT4KPpEMFi9kVvg7UTQgQFB1yH0BYMH9A5voTGRMOGAQNchcH/bPOAxINdJgQKAuZEw4HDiEA8CASAFDAIBIAYJAgFuBwgAGa3OdqJoQCDrkOuF/8AAGa8d9qJoQBDrkOuFj8ACAUgKCwAXsyX7UTQcdch1wsfgABGyYvtRNDXCgCAAGb5fD2omhAgKDrkPoCwBAvIOAR4g1wsfghBzaWduuvLgin8PAeaO8O2i7fshgwjXIgKDCNcjIIAg1yHTH9Mf0x/tRNDSANMfINMf0//XCgAK+QFAzPkQmiiUXwrbMeHywIffArNQB7Dy0IRRJbry4IVQNrry4Ib4I7vy0IgikvgA3gGkf8jKAMsfAc8Wye1UIJL4D95w2zzYEAP27aLt+wL0BCFukmwhjkwCIdc5MHCUIccAs44tAdcoIHYeQ2wg10nACPLgkyDXSsAC8uCTINcdBscSwgBSMLDy0InXTNc5MAGk6GwShAe78uCT10rAAPLgk+1V4tIAAcAAkVvg69csCBQgkXCWAdcsCBwS4lIQseMPINdKERITAJYB+kAB+kT4KPpEMFi68uCR7UTQgQFB1xj0BQSdf8jKAEAEgwf0U/Lgi44UA4MH9Fvy4Iwi1woAIW4Bs7Dy0JDiyFADzxYS9ADJ7VQAcjDXLAgkji0h8uCS0gDtRNDSAFETuvLQj1RQMJExnAGBAUDXIdcKAPLgjuLIygBYzxbJ7VST8sCN4gAQk1vbMeHXTNC01sNe
\ No newline at end of file
diff --git a/rust/chains/tw_ton/src/compiler.rs b/rust/chains/tw_ton/src/compiler.rs
index 060e6879acd..2bcea271351 100644
--- a/rust/chains/tw_ton/src/compiler.rs
+++ b/rust/chains/tw_ton/src/compiler.rs
@@ -2,12 +2,20 @@
//
// Copyright © 2017 Trust Wallet.
+use crate::signing_request::builder::SigningRequestBuilder;
+use crate::signing_request::cell_creator::ExternalMessageCreator;
+use std::borrow::Cow;
use tw_coin_entry::coin_context::CoinContext;
use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes};
use tw_coin_entry::error::prelude::*;
use tw_coin_entry::signing_output_error;
+use tw_keypair::ed25519::Signature;
use tw_proto::TheOpenNetwork::Proto;
use tw_proto::TxCompiler::Proto as CompilerProto;
+use tw_ton_sdk::boc::BagOfCells;
+use tw_ton_sdk::error::cell_to_signing_error;
+
+pub(crate) const HAS_CRC32: bool = true;
pub struct TheOpenNetworkCompiler;
@@ -23,10 +31,18 @@ impl TheOpenNetworkCompiler {
fn preimage_hashes_impl(
_coin: &dyn CoinContext,
- _input: Proto::SigningInput<'_>,
+ input: Proto::SigningInput<'_>,
) -> SigningResult> {
- SigningError::err(SigningErrorType::Error_not_supported)
- .context("Transaction pre-image hashing is not supported for TON blockchain yet")
+ let signing_request = SigningRequestBuilder::build(&input)?;
+
+ let external_message =
+ ExternalMessageCreator::create_external_message_to_sign(&signing_request)
+ .map_err(cell_to_signing_error)?;
+
+ Ok(CompilerProto::PreSigningOutput {
+ data: Cow::from(external_message.cell_hash().to_vec()),
+ ..CompilerProto::PreSigningOutput::default()
+ })
}
#[inline]
@@ -42,11 +58,46 @@ impl TheOpenNetworkCompiler {
fn compile_impl(
_coin: &dyn CoinContext,
- _input: Proto::SigningInput<'_>,
- _signatures: Vec,
+ input: Proto::SigningInput<'_>,
+ signatures: Vec,
_public_keys: Vec,
) -> SigningResult> {
- SigningError::err(SigningErrorType::Error_not_supported)
- .context("Transaction compiling is not supported for TON blockchain yet")
+ if signatures.len() != 1 {
+ return TWError::err(SigningErrorType::Error_signatures_count)
+ .context("Expected exactly one signature");
+ }
+ let signature = Signature::try_from(signatures[0].as_slice())?;
+
+ let signing_request = SigningRequestBuilder::build(&input)?;
+
+ let external_message =
+ ExternalMessageCreator::create_external_message_to_sign(&signing_request)
+ .map_err(cell_to_signing_error)?;
+
+ let signed_external_message = signing_request
+ .wallet
+ .compile_signed_external_message(external_message, signature)?;
+
+ // Whether to add 'StateInit' reference.
+ let state_init = signing_request.seqno == 0;
+ let signed_tx = signing_request
+ .wallet
+ .compile_transaction(signed_external_message, state_init)
+ .context("Error compiling an external message")?
+ .build()
+ .context("Error generating signed message cell")
+ .map_err(cell_to_signing_error)?;
+
+ let signed_tx_hash = signed_tx.cell_hash();
+ let signed_tx_encoded = BagOfCells::from_root(signed_tx)
+ .to_base64(HAS_CRC32)
+ .context("Error serializing signed transaction as BoC")
+ .map_err(cell_to_signing_error)?;
+
+ Ok(Proto::SigningOutput {
+ encoded: signed_tx_encoded.into(),
+ hash: signed_tx_hash.to_vec().into(),
+ ..Proto::SigningOutput::default()
+ })
}
}
diff --git a/rust/chains/tw_ton/src/entry.rs b/rust/chains/tw_ton/src/entry.rs
index a0bbf9cd165..a2bd5aadc2d 100644
--- a/rust/chains/tw_ton/src/entry.rs
+++ b/rust/chains/tw_ton/src/entry.rs
@@ -4,8 +4,9 @@
use crate::address::TonAddress;
use crate::compiler::TheOpenNetworkCompiler;
+use crate::modules::transaction_util::TonTransactionUtil;
use crate::signer::TheOpenNetworkSigner;
-use crate::wallet::TonWallet;
+use crate::wallet::{wallet_v4, VersionedTonWallet};
use std::str::FromStr;
use tw_coin_entry::coin_context::CoinContext;
use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes};
@@ -36,6 +37,7 @@ impl CoinEntry for TheOpenNetworkEntry {
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
+ type TransactionUtil = TonTransactionUtil;
#[inline]
fn parse_address(
@@ -68,9 +70,12 @@ impl CoinEntry for TheOpenNetworkEntry {
let ed25519_pubkey = public_key
.to_ed25519()
.ok_or(AddressError::PublicKeyTypeMismatch)?;
- TonWallet::std_with_public_key(ed25519_pubkey.clone())
- .map(|wallet| wallet.address().clone())
- .map_err(|_| AddressError::Internal)
+ // Currently, we use the V4R2 wallet
+ let wallet = VersionedTonWallet::V4R2(
+ wallet_v4::WalletV4R2::std_with_public_key(ed25519_pubkey.clone())
+ .map_err(|_| AddressError::Internal)?,
+ );
+ Ok(wallet.address().clone())
}
#[inline]
@@ -97,4 +102,9 @@ impl CoinEntry for TheOpenNetworkEntry {
) -> Self::SigningOutput {
TheOpenNetworkCompiler::compile(coin, input, signatures, public_keys)
}
+
+ #[inline]
+ fn transaction_util(&self) -> Option {
+ Some(TonTransactionUtil)
+ }
}
diff --git a/rust/chains/tw_ton/src/message/external_message/mod.rs b/rust/chains/tw_ton/src/message/external_message/mod.rs
new file mode 100644
index 00000000000..88c077ae30c
--- /dev/null
+++ b/rust/chains/tw_ton/src/message/external_message/mod.rs
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+pub mod wallet_v4;
+pub mod wallet_v5;
diff --git a/rust/chains/tw_ton/src/message/external_message.rs b/rust/chains/tw_ton/src/message/external_message/wallet_v4.rs
similarity index 71%
rename from rust/chains/tw_ton/src/message/external_message.rs
rename to rust/chains/tw_ton/src/message/external_message/wallet_v4.rs
index 874c45b8469..d23d0090de9 100644
--- a/rust/chains/tw_ton/src/message/external_message.rs
+++ b/rust/chains/tw_ton/src/message/external_message/wallet_v4.rs
@@ -7,26 +7,21 @@ use tw_ton_sdk::cell::cell_builder::CellBuilder;
use tw_ton_sdk::cell::Cell;
use tw_ton_sdk::error::CellResult;
-pub struct ExternalMessage {
+pub struct ExternalMessageWalletV4 {
pub wallet_id: i32,
pub expire_at: u32,
pub seqno: u32,
- /// Whether the wallet version supports OP codes,
- /// eg https://github.com/ton-blockchain/wallet-contract/blob/4111fd9e3313ec17d99ca9b5b1656445b5b49d8f/func/wallet-v4-code.fc#L94
- pub has_op: bool,
pub internal_messages: Vec,
}
-impl ExternalMessage {
+impl ExternalMessageWalletV4 {
pub fn build(&self) -> CellResult {
let mut builder = CellBuilder::new();
builder
.store_i32(32, self.wallet_id)?
.store_u32(32, self.expire_at)?
.store_u32(32, self.seqno)?;
- if self.has_op {
- builder.store_u8(8, 0)?;
- }
+ builder.store_u8(8, 0)?; // has op
for internal_message in self.internal_messages.iter() {
builder.store_u8(8, internal_message.mode)?;
builder.store_reference(&internal_message.message)?;
diff --git a/rust/chains/tw_ton/src/message/external_message/wallet_v5.rs b/rust/chains/tw_ton/src/message/external_message/wallet_v5.rs
new file mode 100644
index 00000000000..af4a2863323
--- /dev/null
+++ b/rust/chains/tw_ton/src/message/external_message/wallet_v5.rs
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+use crate::message::out_list::build_out_list;
+use crate::message::out_list::out_action::OutAction;
+use tw_coin_entry::error::prelude::ResultContext;
+use tw_ton_sdk::cell::cell_builder::CellBuilder;
+use tw_ton_sdk::cell::Cell;
+use tw_ton_sdk::error::{CellError, CellErrorType, CellResult};
+
+const SEND_MODE_IGNORE_ACTION_PHASE_ERRORS: u8 = 0x02;
+
+pub enum V5R1OpCode {
+ // Currently, only AuthSignedExternal is supported. AuthSignedInternal/AuthExtension are not supported.
+ AuthSignedExternal,
+}
+
+impl V5R1OpCode {
+ // See https://github.com/ton-blockchain/wallet-contract-v5/blob/88557ebc33047a95207f6e47ac8aadb102dff744/contracts/wallet_v5.fc#L33
+ pub fn to_ser_tag(&self) -> u32 {
+ match self {
+ V5R1OpCode::AuthSignedExternal => 0x7369676e,
+ }
+ }
+}
+
+pub struct ExternalMessageWalletV5 {
+ pub opcode: V5R1OpCode,
+ pub wallet_id: i32,
+ pub expire_at: u32,
+ pub seqno: u32,
+ /// Currently, only basic actions are supported. Extended actions are not supported.
+ pub basic_actions: Vec,
+}
+
+impl ExternalMessageWalletV5 {
+ /// Build the external message for wallet v5.
+ pub fn build(&self) -> CellResult {
+ // Check the number of basic actions
+ if self.basic_actions.len() > 255 {
+ return CellError::err(CellErrorType::InternalError)
+ .context("Maximum number of actions in a single request is 255");
+ }
+
+ let mut builder = CellBuilder::new();
+ match self.opcode {
+ V5R1OpCode::AuthSignedExternal => {
+ builder.store_u32(32, self.opcode.to_ser_tag())?;
+
+ // Make sure +2 flag (ignore errors send mode) is set for all external send messages
+ // See https://github.com/ton-blockchain/wallet-contract-v5/blob/88557ebc33047a95207f6e47ac8aadb102dff744/contracts/wallet_v5.fc#L82
+ for action in &self.basic_actions {
+ if (action.mode & SEND_MODE_IGNORE_ACTION_PHASE_ERRORS) == 0 {
+ return CellError::err(CellErrorType::InternalError)
+ .context("External send message must have ignore errors send mode");
+ }
+ }
+ },
+ }
+ builder
+ .store_i32(32, self.wallet_id)?
+ .store_u32(32, self.expire_at)?
+ .store_u32(32, self.seqno)?;
+ builder.store_bit(true)?; // true means basic actions is stored in reference
+
+ let mut basic_actions = self.basic_actions.clone();
+ basic_actions.reverse();
+
+ builder.store_child(build_out_list(&basic_actions)?)?;
+ builder.store_bit(false)?; // false means no extended actions
+ builder.build()
+ }
+}
diff --git a/rust/chains/tw_ton/src/message/mod.rs b/rust/chains/tw_ton/src/message/mod.rs
index 1f8c298d45c..0996f7b8276 100644
--- a/rust/chains/tw_ton/src/message/mod.rs
+++ b/rust/chains/tw_ton/src/message/mod.rs
@@ -1,4 +1,5 @@
pub mod external_message;
pub mod internal_message;
+pub mod out_list;
pub mod payload;
pub mod signed_message;
diff --git a/rust/chains/tw_ton/src/message/out_list/mod.rs b/rust/chains/tw_ton/src/message/out_list/mod.rs
new file mode 100644
index 00000000000..85f6df623d8
--- /dev/null
+++ b/rust/chains/tw_ton/src/message/out_list/mod.rs
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+use crate::message::out_list::out_action::OutAction;
+use tw_ton_sdk::cell::cell_builder::CellBuilder;
+use tw_ton_sdk::cell::Cell;
+use tw_ton_sdk::error::CellResult;
+
+pub mod out_action;
+
+pub fn build_out_list(actions: &[OutAction]) -> CellResult {
+ let cell = actions
+ .iter()
+ .fold(CellBuilder::new().build(), |acc, action| {
+ let mut builder = CellBuilder::new();
+ builder.store_child(acc?)?;
+ builder.store_cell(&action.build()?)?;
+ builder.build()
+ });
+
+ let mut builder = CellBuilder::new();
+ builder.store_cell(&cell?)?;
+ builder.build()
+}
diff --git a/rust/chains/tw_ton/src/message/out_list/out_action.rs b/rust/chains/tw_ton/src/message/out_list/out_action.rs
new file mode 100644
index 00000000000..bf270c307b2
--- /dev/null
+++ b/rust/chains/tw_ton/src/message/out_list/out_action.rs
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+use tw_ton_sdk::cell::cell_builder::CellBuilder;
+use tw_ton_sdk::cell::{Cell, CellArc};
+use tw_ton_sdk::error::CellResult;
+
+#[derive(Clone)]
+pub enum OutActionType {
+ // Currently, only SendMsg is supported. SetCode is not supported.
+ SendMsg,
+}
+
+impl OutActionType {
+ // See https://ton.org/tblkch.pdf 4.4.11. Serialization of output actions
+ pub fn to_ser_tag(&self) -> u32 {
+ match self {
+ OutActionType::SendMsg => 0x0ec3c86d,
+ }
+ }
+}
+
+#[derive(Clone)]
+pub struct OutAction {
+ pub typ: OutActionType,
+ pub mode: u8,
+ pub data: CellArc, // out msg (SendMsg) or new code (SetCode)
+}
+
+impl OutAction {
+ pub fn new(typ: OutActionType, mode: u8, out_msg: CellArc) -> Self {
+ OutAction {
+ typ,
+ mode,
+ data: out_msg,
+ }
+ }
+
+ pub fn build(&self) -> CellResult {
+ match self.typ {
+ OutActionType::SendMsg => self.build_out_action_send_msg(),
+ }
+ }
+
+ fn build_out_action_send_msg(&self) -> CellResult {
+ let mut builder = CellBuilder::new();
+ builder
+ .store_u32(32, self.typ.to_ser_tag())?
+ .store_u8(8, self.mode)?
+ .store_reference(&self.data)?;
+ builder.build()
+ }
+}
diff --git a/rust/chains/tw_ton/src/message/signed_message/mod.rs b/rust/chains/tw_ton/src/message/signed_message/mod.rs
new file mode 100644
index 00000000000..cfe3788fe74
--- /dev/null
+++ b/rust/chains/tw_ton/src/message/signed_message/mod.rs
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+pub mod signed_message_v4;
+pub mod signed_message_v5;
diff --git a/rust/chains/tw_ton/src/message/signed_message/signed_message_v4.rs b/rust/chains/tw_ton/src/message/signed_message/signed_message_v4.rs
new file mode 100644
index 00000000000..4b624b247f0
--- /dev/null
+++ b/rust/chains/tw_ton/src/message/signed_message/signed_message_v4.rs
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+use tw_hash::H512;
+use tw_ton_sdk::cell::cell_builder::CellBuilder;
+use tw_ton_sdk::cell::Cell;
+use tw_ton_sdk::error::*;
+
+pub struct SignedMessageV4 {
+ pub signature: H512,
+ pub external_message: Cell,
+}
+
+impl SignedMessageV4 {
+ pub fn build(&self) -> CellResult {
+ let mut body_builder = CellBuilder::new();
+
+ // In the case of WALLET_V4_R2, the signature is stored before the external message.
+ body_builder.store_slice(self.signature.as_slice())?;
+ body_builder.store_cell(&self.external_message)?;
+
+ body_builder.build()
+ }
+}
diff --git a/rust/chains/tw_ton/src/message/signed_message.rs b/rust/chains/tw_ton/src/message/signed_message/signed_message_v5.rs
similarity index 77%
rename from rust/chains/tw_ton/src/message/signed_message.rs
rename to rust/chains/tw_ton/src/message/signed_message/signed_message_v5.rs
index 3791d8d20f6..932fafa9894 100644
--- a/rust/chains/tw_ton/src/message/signed_message.rs
+++ b/rust/chains/tw_ton/src/message/signed_message/signed_message_v5.rs
@@ -7,16 +7,19 @@ use tw_ton_sdk::cell::cell_builder::CellBuilder;
use tw_ton_sdk::cell::Cell;
use tw_ton_sdk::error::*;
-pub struct SignedMessage {
+pub struct SignedMessageV5 {
pub signature: H512,
pub external_message: Cell,
}
-impl SignedMessage {
+impl SignedMessageV5 {
pub fn build(&self) -> CellResult {
let mut body_builder = CellBuilder::new();
- body_builder.store_slice(self.signature.as_slice())?;
+
+ // In the case of WALLET_V5_R1, the signature is stored after the external message.
body_builder.store_cell(&self.external_message)?;
+ body_builder.store_slice(self.signature.as_slice())?;
+
body_builder.build()
}
}
diff --git a/rust/chains/tw_ton/src/modules/mod.rs b/rust/chains/tw_ton/src/modules/mod.rs
index d27e79c19d1..95d32dc4847 100644
--- a/rust/chains/tw_ton/src/modules/mod.rs
+++ b/rust/chains/tw_ton/src/modules/mod.rs
@@ -4,4 +4,5 @@
pub mod address_converter;
pub mod personal_message_signer;
+pub mod transaction_util;
pub mod wallet_provider;
diff --git a/rust/chains/tw_ton/src/modules/transaction_util.rs b/rust/chains/tw_ton/src/modules/transaction_util.rs
new file mode 100644
index 00000000000..47200e82187
--- /dev/null
+++ b/rust/chains/tw_ton/src/modules/transaction_util.rs
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+use tw_coin_entry::coin_context::CoinContext;
+use tw_coin_entry::error::prelude::*;
+use tw_coin_entry::modules::transaction_util::TransactionUtil;
+use tw_ton_sdk::boc::BagOfCells;
+
+pub struct TonTransactionUtil;
+
+impl TransactionUtil for TonTransactionUtil {
+ fn calc_tx_hash(&self, coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult {
+ Self::calc_tx_hash_impl(coin, encoded_tx)
+ }
+}
+
+impl TonTransactionUtil {
+ // In the TON blockchain, there are both message hashes and transaction hashes.
+ // Strictly speaking, this function returns the message hash, not the transaction hash,
+ // because we often use the TON message hash to track transaction status.
+ // The transaction hash is unknown until the transaction is included in a block.
+ fn calc_tx_hash_impl(_coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult {
+ let boc = BagOfCells::parse_base64(encoded_tx)
+ .map_err(|_| SigningErrorType::Error_input_parse)?;
+ let root_cell_hash = boc
+ .roots
+ .first()
+ .ok_or(SigningErrorType::Error_input_parse)?
+ .cell_hash();
+
+ // The message hash in TON can be encoded in base64, base64url, or hex.
+ // Here, we return the message hash in hex encoding.
+ Ok(root_cell_hash.to_string())
+ }
+}
diff --git a/rust/chains/tw_ton/src/modules/wallet_provider.rs b/rust/chains/tw_ton/src/modules/wallet_provider.rs
index 212e0617126..ed09e7f93df 100644
--- a/rust/chains/tw_ton/src/modules/wallet_provider.rs
+++ b/rust/chains/tw_ton/src/modules/wallet_provider.rs
@@ -2,8 +2,7 @@
//
// Copyright © 2017 Trust Wallet.
-use crate::wallet::wallet_v4::WalletV4;
-use crate::wallet::TonWallet;
+use crate::wallet::{wallet_v4, wallet_v5};
use tw_keypair::ed25519::sha512::PublicKey;
use tw_ton_sdk::boc::BagOfCells;
use tw_ton_sdk::error::CellResult;
@@ -20,10 +19,21 @@ impl WalletProvider {
workchain: i32,
wallet_id: i32,
) -> CellResult {
- let state_init =
- TonWallet::with_public_key(workchain, WalletV4::r2()?, public_key, wallet_id)?
- .state_init()?
- .to_cell()?;
+ let state_init = wallet_v4::WalletV4R2::with_public_key(workchain, public_key, wallet_id)?
+ .state_init()?
+ .to_cell()?;
+ BagOfCells::from_root(state_init).to_base64(HAS_CRC32)
+ }
+
+ /// Constructs a TON Wallet V5R1 stateInit encoded as BoC (BagOfCells) for the given `public_key`.
+ pub fn v5r1_state_init(
+ public_key: PublicKey,
+ workchain: i32,
+ wallet_id: i32,
+ ) -> CellResult {
+ let state_init = wallet_v5::WalletV5R1::with_public_key(workchain, public_key, wallet_id)?
+ .state_init()?
+ .to_cell()?;
BagOfCells::from_root(state_init).to_base64(HAS_CRC32)
}
}
diff --git a/rust/chains/tw_ton/src/resources.rs b/rust/chains/tw_ton/src/resources.rs
index 01f4e26a646..04a1ad37c81 100644
--- a/rust/chains/tw_ton/src/resources.rs
+++ b/rust/chains/tw_ton/src/resources.rs
@@ -6,6 +6,9 @@ use lazy_static::lazy_static;
use tw_ton_sdk::boc::BagOfCells;
pub const DEFAULT_WALLET_ID: i32 = 0x29a9a317;
+/// The wallet id 2147483409 comes from: https://github.com/ton-org/ton/blob/f9842909ac0e7d6f66d055dd18a4c41ec3416c02/src/wallets/v5r1/WalletV5R1WalletId.ts#L21C22-L21C32
+/// The V5R1 wallet id differs between the mainnet and testnet. We support V5R1 only on the mainnet.
+pub const WALLET_ID_V5R1_TON_MAINNET: i32 = 2147483409;
/// https://docs.ton.org/develop/howto/step-by-step#1-smart-contract-addresses
pub const BASE_WORKCHAIN: i32 = 0;
pub const MASTER_WORKCHAIN: i32 = -1;
@@ -15,4 +18,8 @@ lazy_static! {
let code = include_str!("../resources/wallet/wallet_v4r2.code");
BagOfCells::parse_base64(code).expect("Cannot decode wallet_v4r2.code")
};
+ pub static ref WALLET_V5R1_CODE: BagOfCells = {
+ let code = include_str!("../resources/wallet/wallet_v5r1.code");
+ BagOfCells::parse_base64(code).expect("Cannot decode wallet_v5r1.code")
+ };
}
diff --git a/rust/chains/tw_ton/src/signer.rs b/rust/chains/tw_ton/src/signer.rs
index 3a1417b7088..44306434b73 100644
--- a/rust/chains/tw_ton/src/signer.rs
+++ b/rust/chains/tw_ton/src/signer.rs
@@ -2,17 +2,17 @@
//
// Copyright © 2017 Trust Wallet.
+use crate::compiler::HAS_CRC32;
use crate::signing_request::builder::SigningRequestBuilder;
use crate::signing_request::cell_creator::ExternalMessageCreator;
use tw_coin_entry::coin_context::CoinContext;
use tw_coin_entry::error::prelude::*;
use tw_coin_entry::signing_output_error;
+use tw_misc::traits::ToBytesVec;
use tw_proto::TheOpenNetwork::Proto;
use tw_ton_sdk::boc::BagOfCells;
use tw_ton_sdk::error::cell_to_signing_error;
-const HAS_CRC32: bool = true;
-
pub struct TheOpenNetworkSigner;
impl TheOpenNetworkSigner {
diff --git a/rust/chains/tw_ton/src/signing_request/builder.rs b/rust/chains/tw_ton/src/signing_request/builder.rs
index 0b180435bb6..5324546deb4 100644
--- a/rust/chains/tw_ton/src/signing_request/builder.rs
+++ b/rust/chains/tw_ton/src/signing_request/builder.rs
@@ -6,8 +6,7 @@ use crate::address::TonAddress;
use crate::signing_request::{
JettonTransferRequest, SigningRequest, TransferCustomRequest, TransferPayload, TransferRequest,
};
-use crate::wallet::wallet_v4::WalletV4;
-use crate::wallet::TonWallet;
+use crate::wallet::{wallet_v4, wallet_v5, VersionedTonWallet};
use std::str::FromStr;
use tw_coin_entry::error::prelude::*;
use tw_keypair::ed25519::sha512::{KeyPair, PublicKey};
@@ -47,27 +46,46 @@ impl SigningRequestBuilder {
})
}
- /// Currently, V4R2 wallet supported only.
- fn wallet(input: &Proto::SigningInput) -> SigningResult> {
+ /// Currently, V4R2 and V5R1 wallets supported.
+ fn wallet(input: &Proto::SigningInput) -> SigningResult {
if !input.private_key.is_empty() {
let key_pair = KeyPair::try_from(input.private_key.as_ref())
.into_tw()
.context("Invalid private key")?;
- return TonWallet::std_with_key_pair(&key_pair).map_err(cell_to_signing_error);
+
+ return match input.wallet_version {
+ Proto::WalletVersion::WALLET_V4_R2 => Ok(VersionedTonWallet::V4R2(
+ wallet_v4::WalletV4R2::std_with_key_pair(&key_pair)
+ .map_err(cell_to_signing_error)?,
+ )),
+ Proto::WalletVersion::WALLET_V5_R1 => Ok(VersionedTonWallet::V5R1(
+ wallet_v5::WalletV5R1::std_with_key_pair(&key_pair)
+ .map_err(cell_to_signing_error)?,
+ )),
+ _ => SigningError::err(SigningErrorType::Error_not_supported)
+ .context("Wallet version not supported"),
+ };
}
let public_key = PublicKey::try_from(input.public_key.as_ref())
.into_tw()
.context("Expected either 'private_key' or 'public_key' to be set")?;
- TonWallet::std_with_public_key(public_key).map_err(cell_to_signing_error)
- }
- fn transfer_request(input: &Proto::Transfer) -> SigningResult {
- if input.wallet_version != Proto::WalletVersion::WALLET_V4_R2 {
- return SigningError::err(SigningErrorType::Error_not_supported)
- .context("'WALLET_V4_R2' version is supported only");
+ match input.wallet_version {
+ Proto::WalletVersion::WALLET_V4_R2 => Ok(VersionedTonWallet::V4R2(
+ wallet_v4::WalletV4R2::std_with_public_key(public_key)
+ .map_err(cell_to_signing_error)?,
+ )),
+ Proto::WalletVersion::WALLET_V5_R1 => Ok(VersionedTonWallet::V5R1(
+ wallet_v5::WalletV5R1::std_with_public_key(public_key)
+ .map_err(cell_to_signing_error)?,
+ )),
+ _ => SigningError::err(SigningErrorType::Error_not_supported)
+ .context("Wallet version not supported"),
}
+ }
+ fn transfer_request(input: &Proto::Transfer) -> SigningResult {
let dest = TonAddress::from_str(input.dest.as_ref())
.into_tw()
.context("Invalid 'dest' address")?
diff --git a/rust/chains/tw_ton/src/signing_request/mod.rs b/rust/chains/tw_ton/src/signing_request/mod.rs
index ec3b3109cfb..d0d0e3b05bf 100644
--- a/rust/chains/tw_ton/src/signing_request/mod.rs
+++ b/rust/chains/tw_ton/src/signing_request/mod.rs
@@ -3,8 +3,7 @@
// Copyright © 2017 Trust Wallet.
use crate::address::TonAddress;
-use crate::wallet::wallet_v4::WalletV4;
-use crate::wallet::TonWallet;
+use crate::wallet::VersionedTonWallet;
use tw_number::U256;
pub mod builder;
@@ -57,7 +56,7 @@ pub struct TransferCustomRequest {
pub struct SigningRequest {
/// Wallet initialized with the user's key-pair or public key.
- pub wallet: TonWallet,
+ pub wallet: VersionedTonWallet,
pub messages: Vec,
/// External message counter.
/// https://ton.org/docs/develop/smart-contracts/guidelines/external-messages
diff --git a/rust/chains/tw_ton/src/transaction.rs b/rust/chains/tw_ton/src/transaction.rs
index 37dd1f6fdce..1fdf8e8d925 100644
--- a/rust/chains/tw_ton/src/transaction.rs
+++ b/rust/chains/tw_ton/src/transaction.rs
@@ -3,7 +3,6 @@
// Copyright © 2017 Trust Wallet.
use crate::address::TonAddress;
-use crate::message::signed_message::SignedMessage;
use tw_number::U256;
use tw_ton_sdk::cell::cell_builder::CellBuilder;
use tw_ton_sdk::cell::Cell;
@@ -19,7 +18,7 @@ pub struct SignedTransaction {
pub import_fee: U256,
/// Created via `StateInit`.
pub state_init: Option,
- pub signed_body: SignedMessage,
+ pub signed_body: Cell,
}
impl SignedTransaction {
@@ -40,7 +39,7 @@ impl SignedTransaction {
}
wrap_builder.store_bit(true)?; // signed_body is always defined
- wrap_builder.store_child(self.signed_body.build()?)?; // Signed body
+ wrap_builder.store_child(self.signed_body.clone())?; // Signed body
wrap_builder.build()
}
diff --git a/rust/chains/tw_ton/src/wallet/mod.rs b/rust/chains/tw_ton/src/wallet/mod.rs
index e6257528e76..8b4a01311b7 100644
--- a/rust/chains/tw_ton/src/wallet/mod.rs
+++ b/rust/chains/tw_ton/src/wallet/mod.rs
@@ -3,97 +3,42 @@
// Copyright © 2017 Trust Wallet.
use crate::address::TonAddress;
-use crate::message::external_message::ExternalMessage;
use crate::message::internal_message::InternalMessage;
-use crate::message::signed_message::SignedMessage;
-use crate::resources::{BASE_WORKCHAIN, DEFAULT_WALLET_ID};
+use crate::message::signed_message::signed_message_v4::SignedMessageV4;
+use crate::message::signed_message::signed_message_v5::SignedMessageV5;
use crate::transaction::SignedTransaction;
use tw_coin_entry::error::prelude::*;
-use tw_hash::H256;
-use tw_keypair::ed25519::sha512::{KeyPair, PrivateKey, PublicKey};
-use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait};
+use tw_keypair::ed25519::Signature;
+use tw_keypair::traits::SigningKeyTrait;
use tw_number::U256;
-use tw_ton_sdk::cell::{Cell, CellArc};
+use tw_ton_sdk::cell::Cell;
use tw_ton_sdk::error::{cell_to_signing_error, CellResult};
use tw_ton_sdk::message::state_init::StateInit;
pub mod wallet_v4;
-
-pub trait WalletVersion {
- /// Returns data that is (will be) stored in the wallet smart contract
- /// when it is first deployed to the blockchain.
- fn initial_data(&self, wallet_id: i32, public_key: H256) -> CellResult;
-
- /// Returns this wallet specific version contract code as a Cell.
- fn code(&self) -> CellResult;
-
- /// Whether the wallet version supports OP codes.
- /// For example, plugin OP codes: https://github.com/ton-blockchain/wallet-contract/blob/main/func/wallet-v4-code.fc#L102
- fn has_op(&self) -> bool;
-}
-
-pub struct TonWallet {
- public_key: PublicKey,
- private_key: Option,
- version: Version,
- /// TON address derived from the [`TonWallet::public_key`].
- address: TonAddress,
- wallet_id: i32,
-}
-
-impl TonWallet {
- /// Creates a standard TON wallet from the given public key.
- /// Please note when created with public key only, wallet cannot be used to sign messages.
- pub fn std_with_public_key(public_key: PublicKey) -> CellResult {
- Self::with_public_key(
- BASE_WORKCHAIN,
- wallet_v4::WalletV4::r2()?,
- public_key,
- DEFAULT_WALLET_ID,
- )
- }
-
- /// Creates a standard TON wallet from the given key-pair.
- pub fn std_with_key_pair(key_pair: &KeyPair) -> CellResult {
- Self::with_key_pair(
- BASE_WORKCHAIN,
- wallet_v4::WalletV4::r2()?,
- key_pair,
- DEFAULT_WALLET_ID,
- )
- }
+pub mod wallet_v5;
+
+/// Notes: Rust specialization for generic code is not completed. See https://github.com/rust-lang/rust/issues/31844
+/// Currently, we use a workaround to implement the versioned TonWallet struct.
+/// After the Rust specialization feature is finished, maybe we can remove this workaround.
+pub enum VersionedTonWallet {
+ V4R2(wallet_v4::WalletV4R2),
+ V5R1(wallet_v5::WalletV5R1),
}
-impl TonWallet {
- /// Creates a TON wallet from the given public key.
- /// Please note when created with public key only, wallet cannot be used to sign messages.
- pub fn with_public_key(
- workchain: i32,
- version: Version,
- public_key: PublicKey,
- wallet_id: i32,
- ) -> CellResult {
- Self::new(workchain, version, public_key, None, wallet_id)
- }
-
- /// Creates a TON wallet from the given key-pair.
- pub fn with_key_pair(
- workchain: i32,
- version: Version,
- key_pair: &KeyPair,
- wallet_id: i32,
- ) -> CellResult {
- let public = key_pair.public().clone();
- let private = key_pair.private().clone();
- Self::new(workchain, version, public, Some(private), wallet_id)
- }
-
+impl VersionedTonWallet {
pub fn address(&self) -> &TonAddress {
- &self.address
+ match self {
+ Self::V4R2(wallet_v4r2) => &wallet_v4r2.address,
+ Self::V5R1(wallet_v5r1) => &wallet_v5r1.address,
+ }
}
pub fn state_init(&self) -> CellResult {
- Self::state_init_impl(&self.version, &self.public_key, self.wallet_id)
+ match self {
+ Self::V4R2(wallet_v4r2) => wallet_v4r2.state_init(),
+ Self::V5R1(wallet_v5r1) => wallet_v5r1.state_init(),
+ }
}
pub fn create_external_body(
@@ -102,36 +47,64 @@ impl TonWallet {
seqno: u32,
internal_messages: Vec,
) -> CellResult {
- ExternalMessage {
- wallet_id: self.wallet_id,
- expire_at,
- seqno,
- has_op: self.version.has_op(),
- internal_messages,
+ match self {
+ Self::V4R2(wallet_v4r2) => {
+ wallet_v4r2.create_external_body(expire_at, seqno, internal_messages)
+ },
+ Self::V5R1(wallet_v5r1) => {
+ wallet_v5r1.create_external_body(expire_at, seqno, internal_messages)
+ },
}
- .build()
}
- pub fn sign_external_message(&self, external_message: Cell) -> SigningResult {
+ pub fn sign_external_message(&self, external_message: Cell) -> SigningResult {
let message_hash = external_message.cell_hash();
- let sig = self
- .private_key
- .as_ref()
- .or_tw_err(SigningErrorType::Error_internal)
- .context(
- "'TonWallet' should be initialized with a key-pair to be able to sign a message",
- )?
- .sign(message_hash.to_vec())?;
- Ok(SignedMessage {
- signature: sig.to_bytes(),
- external_message,
- })
+ let sig = match self {
+ Self::V4R2(wallet_v4r2) => wallet_v4r2.private_key.as_ref(),
+ Self::V5R1(wallet_v5r1) => wallet_v5r1.private_key.as_ref(),
+ }
+ .or_tw_err(SigningErrorType::Error_internal)
+ .context("'TonWallet' should be initialized with a key-pair to be able to sign a message")?
+ .sign(message_hash.to_vec())?;
+
+ self.compile_signed_external_message(external_message, sig)
+ }
+
+ pub fn compile_signed_external_message(
+ &self,
+ external_message: Cell,
+ sig: Signature,
+ ) -> SigningResult {
+ match self {
+ Self::V4R2(_) => Ok(SignedMessageV4 {
+ signature: sig.to_bytes(),
+ external_message,
+ }
+ .build()
+ .map_err(cell_to_signing_error)?),
+
+ Self::V5R1(_) => Ok(SignedMessageV5 {
+ signature: sig.to_bytes(),
+ external_message,
+ }
+ .build()
+ .map_err(cell_to_signing_error)?),
+ }
}
pub fn sign_transaction(
&self,
external_message: Cell,
state_init: bool,
+ ) -> SigningResult {
+ let signed_external_message = self.sign_external_message(external_message.clone())?;
+ self.compile_transaction(signed_external_message, state_init)
+ }
+
+ pub fn compile_transaction(
+ &self,
+ signed_external_message: Cell,
+ state_init: bool,
) -> SigningResult {
let state_init = if state_init {
let state_init = self.state_init().map_err(cell_to_signing_error)?;
@@ -140,49 +113,13 @@ impl TonWallet {
None
};
- let signed_body = self.sign_external_message(external_message)?;
Ok(SignedTransaction {
src_address: TonAddress::null(),
// The wallet contract address.
- dest_address: self.address.clone(),
+ dest_address: self.address().clone(),
import_fee: U256::zero(),
state_init,
- signed_body,
- })
- }
-
- /// Private function to create the TonWallet with the given public and optional private keys.
- /// Do not make it public as the function caller can provide unrelated keys.
- fn new(
- workchain: i32,
- version: Version,
- public_key: PublicKey,
- private_key: Option,
- wallet_id: i32,
- ) -> CellResult {
- let state_init_hash =
- Self::state_init_impl(&version, &public_key, wallet_id)?.create_account_id()?;
- let address = TonAddress::new(workchain, state_init_hash);
- Ok(TonWallet {
- public_key,
- private_key,
- version,
- address,
- wallet_id,
+ signed_body: signed_external_message,
})
}
-
- fn state_init_impl(
- version: &Version,
- public_key: &PublicKey,
- wallet_id: i32,
- ) -> CellResult {
- let public_key_bytes = public_key.to_bytes();
-
- let initial_data = version
- .initial_data(wallet_id, public_key_bytes)?
- .into_arc();
- let code = version.code()?;
- Ok(StateInit::default().set_code(code).set_data(initial_data))
- }
}
diff --git a/rust/chains/tw_ton/src/wallet/wallet_v4.rs b/rust/chains/tw_ton/src/wallet/wallet_v4.rs
index 9beae8846ec..2ecb279a1e1 100644
--- a/rust/chains/tw_ton/src/wallet/wallet_v4.rs
+++ b/rust/chains/tw_ton/src/wallet/wallet_v4.rs
@@ -2,34 +2,82 @@
//
// Copyright © 2017 Trust Wallet.
-use crate::resources::WALLET_V4R2_CODE;
-use crate::wallet::WalletVersion;
+use crate::address::TonAddress;
+use crate::message::external_message::wallet_v4::ExternalMessageWalletV4;
+use crate::message::internal_message::InternalMessage;
+use crate::resources::{BASE_WORKCHAIN, DEFAULT_WALLET_ID, WALLET_V4R2_CODE};
use std::sync::Arc;
-use tw_hash::H256;
+use tw_keypair::ed25519::sha512::{KeyPair, PrivateKey, PublicKey};
+use tw_keypair::traits::KeyPairTrait;
use tw_ton_sdk::cell::cell_builder::CellBuilder;
-use tw_ton_sdk::cell::{Cell, CellArc};
+use tw_ton_sdk::cell::Cell;
use tw_ton_sdk::error::CellResult;
+use tw_ton_sdk::message::state_init::StateInit;
-/// We support WalletV4R2 version only.
-/// Consider adding a new `WalletVR` if needed.
-enum Revision {
- R2,
+pub struct WalletV4R2 {
+ pub public_key: PublicKey,
+ pub(crate) private_key: Option,
+ /// TON address derived from the [`TonWallet::public_key`].
+ pub address: TonAddress,
+ pub wallet_id: i32,
}
-pub struct WalletV4 {
- revision: Revision,
-}
+impl WalletV4R2 {
+ /// Creates a standard TON wallet from the given public key.
+ /// Please note when created with public key only, wallet cannot be used to sign messages.
+ pub fn std_with_public_key(public_key: PublicKey) -> CellResult {
+ let wallet_id = DEFAULT_WALLET_ID;
+ Self::with_public_key(BASE_WORKCHAIN, public_key, wallet_id)
+ }
+
+ /// Creates a standard TON wallet from the given key-pair.
+ pub fn std_with_key_pair(key_pair: &KeyPair) -> CellResult {
+ let wallet_id = DEFAULT_WALLET_ID;
+ Self::with_key_pair(BASE_WORKCHAIN, key_pair, wallet_id)
+ }
+
+ /// Creates a TON wallet from the given public key.
+ /// Please note when created with public key only, wallet cannot be used to sign messages.
+ pub(crate) fn with_public_key(
+ workchain: i32,
+ public_key: PublicKey,
+ wallet_id: i32,
+ ) -> CellResult {
+ Self::new(workchain, public_key, None, wallet_id)
+ }
+
+ /// Creates a TON wallet from the given key-pair.
+ fn with_key_pair(workchain: i32, key_pair: &KeyPair, wallet_id: i32) -> CellResult {
+ let public = key_pair.public().clone();
+ let private = key_pair.private().clone();
+ Self::new(workchain, public, Some(private), wallet_id)
+ }
-impl WalletV4 {
- pub fn r2() -> CellResult {
- Ok(WalletV4 {
- revision: Revision::R2,
+ /// Private function to create the VersionedTonWallet with the given public and optional private keys.
+ /// Do not make it public as the function caller can provide unrelated keys.
+ fn new(
+ workchain: i32,
+ public_key: PublicKey,
+ private_key: Option,
+ wallet_id: i32,
+ ) -> CellResult {
+ let state_init_hash = Self::state_init_impl(&public_key, wallet_id)?.create_account_id()?;
+ let address = TonAddress::new(workchain, state_init_hash);
+
+ Ok(Self {
+ public_key,
+ private_key,
+ address,
+ wallet_id,
})
}
-}
-impl WalletVersion for WalletV4 {
- fn initial_data(&self, wallet_id: i32, public_key: H256) -> CellResult {
+ /// Return the stateInit for the wallet.
+ pub fn state_init(&self) -> CellResult {
+ Self::state_init_impl(&self.public_key, self.wallet_id)
+ }
+
+ fn state_init_impl(public_key: &PublicKey, wallet_id: i32) -> CellResult {
let seqno = 0;
let mut builder = CellBuilder::new();
@@ -39,16 +87,25 @@ impl WalletVersion for WalletV4 {
.store_slice(public_key.as_slice())?
// empty plugin dict
.store_bit(false)?;
- builder.build()
- }
- fn code(&self) -> CellResult {
- match self.revision {
- Revision::R2 => WALLET_V4R2_CODE.single_root().map(Arc::clone),
- }
+ let initial_data = builder.build()?.into_arc();
+ let code = WALLET_V4R2_CODE.single_root().map(Arc::clone)?;
+
+ Ok(StateInit::default().set_code(code).set_data(initial_data))
}
- fn has_op(&self) -> bool {
- matches!(self.revision, Revision::R2)
+ pub(crate) fn create_external_body(
+ &self,
+ expire_at: u32,
+ seqno: u32,
+ internal_messages: Vec,
+ ) -> CellResult {
+ ExternalMessageWalletV4 {
+ wallet_id: self.wallet_id,
+ expire_at,
+ seqno,
+ internal_messages,
+ }
+ .build()
}
}
diff --git a/rust/chains/tw_ton/src/wallet/wallet_v5.rs b/rust/chains/tw_ton/src/wallet/wallet_v5.rs
new file mode 100644
index 00000000000..98a45b6a94e
--- /dev/null
+++ b/rust/chains/tw_ton/src/wallet/wallet_v5.rs
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+use crate::address::TonAddress;
+use crate::message::external_message::wallet_v5::{ExternalMessageWalletV5, V5R1OpCode};
+use crate::message::internal_message::InternalMessage;
+use crate::message::out_list::out_action::{OutAction, OutActionType};
+use crate::resources::{BASE_WORKCHAIN, WALLET_ID_V5R1_TON_MAINNET, WALLET_V5R1_CODE};
+use std::sync::Arc;
+use tw_keypair::ed25519::sha512::{KeyPair, PrivateKey, PublicKey};
+use tw_keypair::traits::KeyPairTrait;
+use tw_ton_sdk::cell::cell_builder::CellBuilder;
+use tw_ton_sdk::cell::Cell;
+use tw_ton_sdk::error::CellResult;
+use tw_ton_sdk::message::state_init::StateInit;
+
+pub struct WalletV5R1 {
+ pub public_key: PublicKey,
+ pub(crate) private_key: Option,
+ /// TON address derived from the [`TonWallet::public_key`].
+ pub address: TonAddress,
+ pub wallet_id: i32,
+}
+
+impl WalletV5R1 {
+ /// Creates a standard TON wallet from the given public key.
+ /// Please note when created with public key only, wallet cannot be used to sign messages.
+ pub fn std_with_public_key(public_key: PublicKey) -> CellResult {
+ let wallet_id = WALLET_ID_V5R1_TON_MAINNET;
+ Self::with_public_key(BASE_WORKCHAIN, public_key, wallet_id)
+ }
+
+ /// Creates a standard TON wallet from the given key-pair.
+ pub fn std_with_key_pair(key_pair: &KeyPair) -> CellResult {
+ let wallet_id = WALLET_ID_V5R1_TON_MAINNET;
+ Self::with_key_pair(BASE_WORKCHAIN, key_pair, wallet_id)
+ }
+
+ /// Creates a TON wallet from the given public key.
+ /// Please note when created with public key only, wallet cannot be used to sign messages.
+ pub(crate) fn with_public_key(
+ workchain: i32,
+ public_key: PublicKey,
+ wallet_id: i32,
+ ) -> CellResult {
+ Self::new(workchain, public_key, None, wallet_id)
+ }
+
+ /// Creates a TON wallet from the given key-pair.
+ fn with_key_pair(workchain: i32, key_pair: &KeyPair, wallet_id: i32) -> CellResult {
+ let public = key_pair.public().clone();
+ let private = key_pair.private().clone();
+ Self::new(workchain, public, Some(private), wallet_id)
+ }
+
+ /// Private function to create the VersionedTonWallet with the given public and optional private keys.
+ /// Do not make it public as the function caller can provide unrelated keys.
+ fn new(
+ workchain: i32,
+ public_key: PublicKey,
+ private_key: Option,
+ wallet_id: i32,
+ ) -> CellResult {
+ let state_init_hash = Self::state_init_impl(&public_key, wallet_id)?.create_account_id()?;
+ let address = TonAddress::new(workchain, state_init_hash);
+
+ Ok(Self {
+ public_key,
+ private_key,
+ address,
+ wallet_id,
+ })
+ }
+
+ /// Return the stateInit for the wallet.
+ pub(crate) fn state_init(&self) -> CellResult {
+ Self::state_init_impl(&self.public_key, self.wallet_id)
+ }
+
+ fn state_init_impl(public_key: &PublicKey, wallet_id: i32) -> CellResult {
+ let seqno = 0;
+
+ let mut builder = CellBuilder::new();
+ builder
+ .store_bit(true)? // signature auth allowed
+ .store_u32(32, seqno)?
+ .store_i32(32, wallet_id)?
+ .store_slice(public_key.as_slice())?
+ // empty plugin dict
+ .store_bit(false)?;
+
+ let initial_data = builder.build()?.into_arc();
+ let code = WALLET_V5R1_CODE.single_root().map(Arc::clone)?;
+
+ Ok(StateInit::default().set_code(code).set_data(initial_data))
+ }
+
+ pub(crate) fn create_external_body(
+ &self,
+ expire_at: u32,
+ seqno: u32,
+ internal_messages: Vec,
+ ) -> CellResult {
+ // Convert internal_messages to basic_actions
+ let basic_actions: Vec = internal_messages
+ .into_iter()
+ .map(|msg| OutAction {
+ typ: OutActionType::SendMsg,
+ mode: msg.mode,
+ data: msg.message,
+ })
+ .collect();
+
+ ExternalMessageWalletV5 {
+ opcode: V5R1OpCode::AuthSignedExternal,
+ wallet_id: self.wallet_id,
+ expire_at,
+ seqno,
+ basic_actions,
+ }
+ .build()
+ }
+}
diff --git a/rust/chains/tw_ton/tests/address.rs b/rust/chains/tw_ton/tests/address.rs
new file mode 100644
index 00000000000..fb99cd0c008
--- /dev/null
+++ b/rust/chains/tw_ton/tests/address.rs
@@ -0,0 +1,39 @@
+use tw_keypair::ed25519::sha512::KeyPair;
+use tw_keypair::traits::KeyPairTrait;
+use tw_ton::wallet::wallet_v5;
+
+/// Tests for TON V5R1 address.
+///
+/// Note: These tests should move to rust/tw_any_coin/tests/chains/ton/ton_address.rs after we define
+/// a new Derivation for the TON V5R1 address.
+#[test]
+fn test_ton_v5r1_address_derive() {
+ let test_cases = [(
+ "3570e35f54cfb843f2cfaf2b8cae7ceeb7b32225d7dbbd86f611056d74d9073e", // private key
+ "UQAU3o5-Sp1MYRpw3U7b_wmARxqI49LxiFhEoVCxpUKjTYXk", // V5R1 address on the mainnet
+ )];
+
+ for test_case in test_cases.iter() {
+ let private_key_hex = test_case.0;
+ let expected_address = test_case.1;
+
+ // Decode the private key from hex
+ let private_key_bytes =
+ tw_encoding::hex::decode(private_key_hex).expect("Invalid hex string");
+
+ // Create a KeyPair from the private key bytes
+ let key_pair =
+ KeyPair::try_from(private_key_bytes.as_slice()).expect("Failed to create key pair");
+
+ // Extract the public key from the KeyPair
+ let public_key = key_pair.public().clone();
+
+ // Create the VersionedTonWallet using the public key
+ let wallet = wallet_v5::WalletV5R1::std_with_public_key(public_key)
+ .expect("Failed to create wallet");
+
+ let actual_address = wallet.address.to_string();
+
+ assert_eq!(actual_address, expected_address);
+ }
+}
diff --git a/rust/frameworks/tw_ton_sdk/src/boc/boc_to_raw_boc.rs b/rust/frameworks/tw_ton_sdk/src/boc/boc_to_raw_boc.rs
index efcbddd4001..c3f4e3d60e0 100644
--- a/rust/frameworks/tw_ton_sdk/src/boc/boc_to_raw_boc.rs
+++ b/rust/frameworks/tw_ton_sdk/src/boc/boc_to_raw_boc.rs
@@ -9,12 +9,13 @@ use crate::boc::BagOfCells;
use crate::cell::{Cell, CellArc};
use crate::error::{CellErrorType, CellResult};
use std::cell::RefCell;
-use std::collections::HashMap;
+use std::collections::BTreeMap;
use std::sync::Arc;
use tw_coin_entry::error::prelude::{OrTWError, ResultContext};
use tw_hash::H256;
type IndexedCellRef = RefCell;
+type CellsByHash = BTreeMap;
#[derive(Debug, Clone)]
struct IndexedCell {
@@ -47,10 +48,14 @@ pub(crate) fn convert_to_raw_boc(boc: &BagOfCells) -> CellResult
})
}
-fn build_and_verify_index(roots: &[CellArc]) -> HashMap {
+fn build_and_verify_index(roots: &[CellArc]) -> CellsByHash {
let mut current_cells: Vec<_> = roots.iter().map(Arc::clone).collect();
let mut new_hash_index = 0;
- let mut cells_by_hash = HashMap::new();
+
+ // The Bag of Cells serialization process is not deterministic,
+ // and these uncertainties make it difficult to write test cases.
+ // Therefore, we use a BTreeMap instead of a HashMap to remove the uncertainty.
+ let mut cells_by_hash = BTreeMap::new();
// Process cells to build the initial index.
while !current_cells.is_empty() {
@@ -99,10 +104,7 @@ fn build_and_verify_index(roots: &[CellArc]) -> HashMap {
cells_by_hash
}
-fn root_indices(
- roots: &[CellArc],
- cells_dict: &HashMap,
-) -> CellResult> {
+fn root_indices(roots: &[CellArc], cells_dict: &CellsByHash) -> CellResult> {
roots
.iter()
.map(|root_cell| root_cell.cell_hash())
@@ -122,17 +124,14 @@ fn root_indices(
fn raw_cells_from_cells(
cells: impl Iterator- ,
- cells_by_hash: &HashMap,
+ cells_by_hash: &CellsByHash,
) -> CellResult> {
cells
.map(|cell| raw_cell_from_cell(&cell, cells_by_hash))
.collect()
}
-fn raw_cell_from_cell(
- cell: &Cell,
- cells_by_hash: &HashMap,
-) -> CellResult {
+fn raw_cell_from_cell(cell: &Cell, cells_by_hash: &CellsByHash) -> CellResult {
raw_cell_reference_indices(cell, cells_by_hash).map(|reference_indices| {
RawCell::new(
cell.data().to_vec(),
@@ -144,10 +143,7 @@ fn raw_cell_from_cell(
})
}
-fn raw_cell_reference_indices(
- cell: &Cell,
- cells_by_hash: &HashMap,
-) -> CellResult> {
+fn raw_cell_reference_indices(cell: &Cell, cells_by_hash: &CellsByHash) -> CellResult> {
cell.references()
.iter()
.map(|cell| {
diff --git a/rust/tw_any_coin/src/ffi/mod.rs b/rust/tw_any_coin/src/ffi/mod.rs
index 8d422f37de4..698f616468f 100644
--- a/rust/tw_any_coin/src/ffi/mod.rs
+++ b/rust/tw_any_coin/src/ffi/mod.rs
@@ -7,4 +7,5 @@ pub mod tw_any_signer;
pub mod tw_message_signer;
pub mod tw_transaction_compiler;
pub mod tw_transaction_decoder;
+pub mod tw_transaction_util;
pub mod tw_wallet_connect_request;
diff --git a/rust/tw_any_coin/src/ffi/tw_transaction_util.rs b/rust/tw_any_coin/src/ffi/tw_transaction_util.rs
new file mode 100644
index 00000000000..7dd7acbf382
--- /dev/null
+++ b/rust/tw_any_coin/src/ffi/tw_transaction_util.rs
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+#![allow(clippy::missing_safety_doc)]
+
+use crate::transaction_util::TransactionUtil;
+use tw_coin_registry::coin_type::CoinType;
+use tw_memory::ffi::tw_string::TWString;
+use tw_memory::ffi::RawPtrTrait;
+use tw_misc::try_or_else;
+
+/// Calculate the TX hash of a transaction.
+///
+/// \param coin coin type.
+/// \param encoded_tx encoded transaction data.
+/// \return The TX hash of a transaction, If the input is invalid or the chain is unsupported, null is returned.
+#[no_mangle]
+pub unsafe extern "C" fn tw_transaction_util_calc_tx_hash(
+ coin: u32,
+ encoded_tx: *const TWString,
+) -> *mut TWString {
+ let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut);
+
+ let encoded_tx = try_or_else!(TWString::from_ptr_as_ref(encoded_tx), std::ptr::null_mut);
+ let encoded_tx = try_or_else!(encoded_tx.as_str(), std::ptr::null_mut);
+
+ TransactionUtil::calc_tx_hash(coin, encoded_tx)
+ .map(|output| TWString::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 412873c0095..be2b4c4909e 100644
--- a/rust/tw_any_coin/src/lib.rs
+++ b/rust/tw_any_coin/src/lib.rs
@@ -12,3 +12,4 @@ pub mod wallet_connect_request;
#[cfg(feature = "test-utils")]
pub mod test_utils;
+pub mod transaction_util;
diff --git a/rust/tw_any_coin/src/test_utils/mod.rs b/rust/tw_any_coin/src/test_utils/mod.rs
index fa4b9e5d3b2..19ddedaf441 100644
--- a/rust/tw_any_coin/src/test_utils/mod.rs
+++ b/rust/tw_any_coin/src/test_utils/mod.rs
@@ -5,5 +5,6 @@
pub mod address_utils;
pub mod plan_utils;
pub mod sign_utils;
+pub mod transaction_calc_tx_hash_utils;
pub mod transaction_decode_utils;
pub mod wallet_connect_utils;
diff --git a/rust/tw_any_coin/src/test_utils/transaction_calc_tx_hash_utils.rs b/rust/tw_any_coin/src/test_utils/transaction_calc_tx_hash_utils.rs
new file mode 100644
index 00000000000..269104a37e1
--- /dev/null
+++ b/rust/tw_any_coin/src/test_utils/transaction_calc_tx_hash_utils.rs
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+use crate::ffi::tw_transaction_util::tw_transaction_util_calc_tx_hash;
+use tw_coin_registry::coin_type::CoinType;
+use tw_memory::test_utils::tw_string_helper::TWStringHelper;
+
+pub struct TransactionUtilHelper;
+
+impl TransactionUtilHelper {
+ pub fn calc_tx_hash(coin_type: CoinType, tx: &str) -> String {
+ let tx_data = TWStringHelper::create(tx);
+
+ TWStringHelper::wrap(unsafe {
+ tw_transaction_util_calc_tx_hash(coin_type as u32, tx_data.ptr())
+ })
+ .to_string()
+ .expect("!tw_transaction_util_calc_tx_hash returned nullptr")
+ }
+}
diff --git a/rust/tw_any_coin/src/transaction_util.rs b/rust/tw_any_coin/src/transaction_util.rs
new file mode 100644
index 00000000000..09032126f70
--- /dev/null
+++ b/rust/tw_any_coin/src/transaction_util.rs
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+use tw_coin_entry::error::prelude::*;
+use tw_coin_registry::coin_type::CoinType;
+use tw_coin_registry::dispatcher::coin_dispatcher;
+
+pub struct TransactionUtil;
+
+impl TransactionUtil {
+ /// Calculate the TX hash of a transaction.
+ #[inline]
+ pub fn calc_tx_hash(coin: CoinType, encoded_tx: &str) -> SigningResult {
+ let (ctx, entry) = coin_dispatcher(coin)?;
+ entry.calc_tx_hash(&ctx, encoded_tx)
+ }
+}
diff --git a/rust/tw_any_coin/tests/chains/aptos/aptos_transaction_util.rs b/rust/tw_any_coin/tests/chains/aptos/aptos_transaction_util.rs
new file mode 100644
index 00000000000..8145d24935d
--- /dev/null
+++ b/rust/tw_any_coin/tests/chains/aptos/aptos_transaction_util.rs
@@ -0,0 +1,13 @@
+use tw_any_coin::test_utils::transaction_calc_tx_hash_utils::TransactionUtilHelper;
+use tw_coin_registry::coin_type::CoinType;
+
+#[test]
+fn test_aptos_transaction_util_calc_tx_hash() {
+ let encoded_tx = "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01";
+ let tx_hash = TransactionUtilHelper::calc_tx_hash(CoinType::Aptos, encoded_tx);
+
+ assert_eq!(
+ tx_hash,
+ "0xb4d62afd3862116e060dd6ad9848ccb50c2bc177799819f1d29c059ae2042467"
+ );
+}
diff --git a/rust/tw_any_coin/tests/chains/aptos/mod.rs b/rust/tw_any_coin/tests/chains/aptos/mod.rs
index 4aee9d56923..3b7b55c4d36 100644
--- a/rust/tw_any_coin/tests/chains/aptos/mod.rs
+++ b/rust/tw_any_coin/tests/chains/aptos/mod.rs
@@ -5,6 +5,7 @@
mod aptos_address;
mod aptos_compile;
mod aptos_sign;
+mod aptos_transaction_util;
mod test_cases;
const APTOS_COIN_TYPE: u32 = 637;
diff --git a/rust/tw_any_coin/tests/chains/bitcoin/bitcoin_transaction_util.rs b/rust/tw_any_coin/tests/chains/bitcoin/bitcoin_transaction_util.rs
new file mode 100644
index 00000000000..a42d55cf322
--- /dev/null
+++ b/rust/tw_any_coin/tests/chains/bitcoin/bitcoin_transaction_util.rs
@@ -0,0 +1,13 @@
+use tw_any_coin::test_utils::transaction_calc_tx_hash_utils::TransactionUtilHelper;
+use tw_coin_registry::coin_type::CoinType;
+
+#[test]
+fn test_bitcoin_transaction_util_calc_tx_hash() {
+ let encoded_tx = "02000000000101089098890d2653567b9e8df2d1fbe5c3c8bf1910ca7184e301db0ad3b495c88e0100000000ffffffff02581b000000000000225120e8b706a97732e705e22ae7710703e7f589ed13c636324461afa443016134cc051040000000000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d02483045022100a44aa28446a9a886b378a4a65e32ad9a3108870bd725dc6105160bed4f317097022069e9de36422e4ce2e42b39884aa5f626f8f94194d1013007d5a1ea9220a06dce0121030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000";
+ let tx_hash = TransactionUtilHelper::calc_tx_hash(CoinType::Bitcoin, encoded_tx);
+
+ assert_eq!(
+ tx_hash,
+ "797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1"
+ );
+}
diff --git a/rust/tw_any_coin/tests/chains/bitcoin/mod.rs b/rust/tw_any_coin/tests/chains/bitcoin/mod.rs
index 7ccbcb26932..25954022bf4 100644
--- a/rust/tw_any_coin/tests/chains/bitcoin/mod.rs
+++ b/rust/tw_any_coin/tests/chains/bitcoin/mod.rs
@@ -6,3 +6,4 @@ mod bitcoin_address;
mod bitcoin_compile;
mod bitcoin_plan;
mod bitcoin_sign;
+mod bitcoin_transaction_util;
diff --git a/rust/tw_any_coin/tests/chains/cosmos/cosmos_transaction_util.rs b/rust/tw_any_coin/tests/chains/cosmos/cosmos_transaction_util.rs
new file mode 100644
index 00000000000..d986376243e
--- /dev/null
+++ b/rust/tw_any_coin/tests/chains/cosmos/cosmos_transaction_util.rs
@@ -0,0 +1,13 @@
+use tw_any_coin::test_utils::transaction_calc_tx_hash_utils::TransactionUtilHelper;
+use tw_coin_registry::coin_type::CoinType;
+
+#[test]
+fn test_cosmos_transaction_util_calc_tx_hash() {
+ let encoded_tx = "CpIBCo8BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm8KLWNvc21vczFta3k2OWNuOGVrdHd5MDg0NXZlYzl1cHNkcGhrdHh0MDNna3dseBItY29zbW9zMThzMGhkbnNsbGdjY2x3ZXU5YXltdzRuZ2t0cjJrMHJreWdkemRwGg8KBXVhdG9tEgY0MDAwMDASZQpOCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAuzvXOQ3owLGf5VGjeSzHzbpEfRn1+alK0HB4T4dVjZJEgQKAggBEhMKDQoFdWF0b20SBDEwMDAQwJoMGkCvvVE6d29P30cO9/lnXyGunWMPxNY12NuqDcCnFkNM0H4CUQdl1Gc9+ogIJbro5nyzZzlv9rl2/GsZox/JXoCX";
+ let tx_hash = TransactionUtilHelper::calc_tx_hash(CoinType::Cosmos, encoded_tx);
+
+ assert_eq!(
+ tx_hash,
+ "85392373F54577562067030BF0D61596C91188AA5E6CA8FFE731BD0349296411"
+ );
+}
diff --git a/rust/tw_any_coin/tests/chains/cosmos/mod.rs b/rust/tw_any_coin/tests/chains/cosmos/mod.rs
index c81241c5434..fbf72ad83a8 100644
--- a/rust/tw_any_coin/tests/chains/cosmos/mod.rs
+++ b/rust/tw_any_coin/tests/chains/cosmos/mod.rs
@@ -4,3 +4,4 @@
mod cosmos_address;
mod cosmos_sign;
+mod cosmos_transaction_util;
diff --git a/rust/tw_any_coin/tests/chains/ethereum/ethereum_transaction_util.rs b/rust/tw_any_coin/tests/chains/ethereum/ethereum_transaction_util.rs
new file mode 100644
index 00000000000..4b38c8b9ff6
--- /dev/null
+++ b/rust/tw_any_coin/tests/chains/ethereum/ethereum_transaction_util.rs
@@ -0,0 +1,13 @@
+use tw_any_coin::test_utils::transaction_calc_tx_hash_utils::TransactionUtilHelper;
+use tw_coin_registry::coin_type::CoinType;
+
+#[test]
+fn test_ethereum_transaction_util_calc_tx_hash() {
+ let encoded_tx = "f8aa808509c7652400830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec8000025a0724c62ad4fbf47346b02de06e603e013f26f26b56fdc0be7ba3d6273401d98cea0032131cae15da7ddcda66963e8bef51ca0d9962bfef0547d3f02597a4a58c931";
+ let tx_hash = TransactionUtilHelper::calc_tx_hash(CoinType::Ethereum, encoded_tx);
+
+ assert_eq!(
+ tx_hash,
+ "0x199a7829fc5149e49b452c2cab76d8fa5a9682fee6e4891b8acb697ac142513e"
+ );
+}
diff --git a/rust/tw_any_coin/tests/chains/ethereum/mod.rs b/rust/tw_any_coin/tests/chains/ethereum/mod.rs
index d6e5a36b07b..2587748ad06 100644
--- a/rust/tw_any_coin/tests/chains/ethereum/mod.rs
+++ b/rust/tw_any_coin/tests/chains/ethereum/mod.rs
@@ -6,3 +6,4 @@ mod ethereum_address;
mod ethereum_compile;
mod ethereum_message_sign;
mod ethereum_sign;
+mod ethereum_transaction_util;
diff --git a/rust/tw_any_coin/tests/chains/solana/mod.rs b/rust/tw_any_coin/tests/chains/solana/mod.rs
index 069e9709509..a680f8aba73 100644
--- a/rust/tw_any_coin/tests/chains/solana/mod.rs
+++ b/rust/tw_any_coin/tests/chains/solana/mod.rs
@@ -6,4 +6,5 @@ mod solana_address;
mod solana_compile;
mod solana_sign;
mod solana_transaction;
+mod solana_transaction_util;
mod solana_wallet_connect;
diff --git a/rust/tw_any_coin/tests/chains/solana/solana_transaction_util.rs b/rust/tw_any_coin/tests/chains/solana/solana_transaction_util.rs
new file mode 100644
index 00000000000..72d19c6a864
--- /dev/null
+++ b/rust/tw_any_coin/tests/chains/solana/solana_transaction_util.rs
@@ -0,0 +1,13 @@
+use tw_any_coin::test_utils::transaction_calc_tx_hash_utils::TransactionUtilHelper;
+use tw_coin_registry::coin_type::CoinType;
+
+#[test]
+fn test_solana_transaction_util_calc_tx_hash() {
+ let encoded_tx = "AnQTYwZpkm3fs4SdLxnV6gQj3hSLsyacpxDdLMALYWObm722f79IfYFTbZeFK9xHtMumiDOWAM2hHQP4r/GtbARpncaXgOVFv7OgbRLMbuCEJHO1qwcdCbtH72VzyzU8yw9sqqHIAaCUE8xaQTgT6Z5IyZfeyMe2QGJIfOjz65UPAgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8Aqe6sdLXiXSDILEtzckCjkjchiSf6zVGpMYiAE5BE2IqHAQUEAgQDAQoMoA8AAAAAAAAG";
+ let tx_hash = TransactionUtilHelper::calc_tx_hash(CoinType::Solana, encoded_tx);
+
+ assert_eq!(
+ tx_hash,
+ "3KbvREZUat76wgWMtnJfWbJL74Vzh4U2eabVJa3Z3bb2fPtW8AREP5pbmRwUrxZCESbTomWpL41PeKDcPGbojsej"
+ );
+}
diff --git a/rust/tw_any_coin/tests/chains/sui/mod.rs b/rust/tw_any_coin/tests/chains/sui/mod.rs
index 53876af861f..c19d11c0bc7 100644
--- a/rust/tw_any_coin/tests/chains/sui/mod.rs
+++ b/rust/tw_any_coin/tests/chains/sui/mod.rs
@@ -7,6 +7,7 @@ use tw_proto::Sui::Proto;
mod sui_address;
mod sui_compile;
mod sui_sign;
+mod sui_transaction_util;
mod test_cases;
fn object_ref(id: &'static str, version: u64, digest: &'static str) -> Proto::ObjectRef<'static> {
diff --git a/rust/tw_any_coin/tests/chains/sui/sui_transaction_util.rs b/rust/tw_any_coin/tests/chains/sui/sui_transaction_util.rs
new file mode 100644
index 00000000000..42119d0b4e6
--- /dev/null
+++ b/rust/tw_any_coin/tests/chains/sui/sui_transaction_util.rs
@@ -0,0 +1,10 @@
+use tw_any_coin::test_utils::transaction_calc_tx_hash_utils::TransactionUtilHelper;
+use tw_coin_registry::coin_type::CoinType;
+
+#[test]
+fn test_sui_transaction_util_calc_tx_hash() {
+ let encoded_tx = "AAACAAgQJwAAAAAAAAAgJZ/4B0q0Jcu0ifI24Y4I8D8aeFa998eih3vWT3OLUBUCAgABAQAAAQEDAAAAAAEBANV1rX8Y6UhGKlz2mPVk7zlKdSpx/sYkk6+KBVwBLA1QAQbywsjB2JZN8QGdZhbpcFcZvrq9kx2idVy5SM635olk7AIAAAAAAAAgYEVuxmf1zRBGdoDr+VDtMpIFF12s2Ua7I2ru1XyGF8/Vda1/GOlIRipc9pj1ZO85SnUqcf7GJJOvigVcASwNUAEAAAAAAAAA0AcAAAAAAAAA";
+ let tx_hash = TransactionUtilHelper::calc_tx_hash(CoinType::Sui, encoded_tx);
+
+ assert_eq!(tx_hash, "HkPo6rYPyDY53x1MBszvSZVZyixVN7CHvCJGX381czAh");
+}
diff --git a/rust/tw_any_coin/tests/chains/ton/mod.rs b/rust/tw_any_coin/tests/chains/ton/mod.rs
index 1a5d122cbdf..ede77ad1aef 100644
--- a/rust/tw_any_coin/tests/chains/ton/mod.rs
+++ b/rust/tw_any_coin/tests/chains/ton/mod.rs
@@ -6,3 +6,5 @@ mod cell_example;
mod ton_address;
mod ton_compile;
mod ton_sign;
+mod ton_sign_wallet_v5r1;
+mod ton_transaction_util;
diff --git a/rust/tw_any_coin/tests/chains/ton/ton_compile.rs b/rust/tw_any_coin/tests/chains/ton/ton_compile.rs
index 43fa1e9daf6..b13d6fc472e 100644
--- a/rust/tw_any_coin/tests/chains/ton/ton_compile.rs
+++ b/rust/tw_any_coin/tests/chains/ton/ton_compile.rs
@@ -2,35 +2,104 @@
//
// Copyright © 2017 Trust Wallet.
+use crate::chains::ton::ton_sign::assert_eq_boc;
use tw_any_coin::test_utils::sign_utils::{CompilerHelper, PreImageHelper};
use tw_coin_registry::coin_type::CoinType;
-use tw_encoding::hex::DecodeHex;
+use tw_encoding::hex::{DecodeHex, ToHex};
use tw_proto::Common::Proto::SigningError;
use tw_proto::TheOpenNetwork::Proto;
use tw_proto::TxCompiler::Proto as CompilerProto;
#[test]
-fn test_ton_compile_not_supported() {
- let input = Proto::SigningInput::default();
+fn test_ton_compile_wallet_v4r2_transfer_and_deploy() {
+ let public_key = "f42c77f931bea20ec5d0150731276bbb2e2860947661245b2319ef8133ee8d41";
+
+ let transfer = Proto::Transfer {
+ dest: "EQDYW_1eScJVxtitoBRksvoV9cCYo4uKGWLVNIHB1JqRR3n0".into(),
+ amount: 10,
+ mode: Proto::SendMode::PAY_FEES_SEPARATELY as u32
+ | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS as u32,
+ bounceable: true,
+ ..Proto::Transfer::default()
+ };
+
+ let input = Proto::SigningInput {
+ public_key: public_key.decode_hex().unwrap().into(),
+ messages: vec![transfer],
+ expire_at: 1671135440,
+ wallet_version: Proto::WalletVersion::WALLET_V4_R2,
+ ..Proto::SigningInput::default()
+ };
// Step 2: Obtain preimage hash
let mut pre_imager = PreImageHelper::::default();
let preimage_output = pre_imager.pre_image_hashes(CoinType::TON, &input);
- assert_eq!(preimage_output.error, SigningError::Error_not_supported);
+ assert_eq!(preimage_output.error, SigningError::OK);
+ assert_eq!(
+ preimage_output.data.to_hex(),
+ "9da0ab29120fd96b48b2bb76843e77c0a1770b47c2f78da23299e2c32f50192e"
+ );
// Step 3: Compile transaction info
- let signature_bytes = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".decode_hex().unwrap();
- let public_key = "0000000000000000000000000000000000000000000000000000000000000000"
- .decode_hex()
- .unwrap();
+
+ // Simulate signature, normally obtained from signature server.
+ let signature_bytes = "e7c6f18186845784fa20c4d63c4261e58a1256c60e6de8aa6da9c008665564b35afe67eaf56af0963427aa78b0bfa1a895378983c7f135f99862df71b6e6b708".decode_hex().unwrap();
let mut compiler = CompilerHelper::::default();
- let output = compiler.compile(
- CoinType::TON,
- &input,
- vec![signature_bytes],
- vec![public_key],
+ let output = compiler.compile(CoinType::TON, &input, vec![signature_bytes], vec![]);
+
+ assert_eq!(output.error, SigningError::OK, "{}", output.error_message);
+ // Successfully broadcasted: https://tonviewer.com/transaction/6ZzWOFKZt_m3kZjbwfbATwLaVwmUOdDp0xjhuY7PO3k=
+ assert_eq_boc(&output.encoded, "te6ccgICABoAAQAAA8sAAAJFiADN98eLgHfrkE8l8gmT8X5REpTVR6QnqDhArTbKlVvbZh4ABAABAZznxvGBhoRXhPogxNY8QmHlihJWxg5t6KptqcAIZlVks1r+Z+r1avCWNCeqeLC/oaiVN4mDx/E1+Zhi33G25rcIKamjF/////8AAAAAAAMAAgFiYgBsLf6vJOEq42xW0AoyWX0K+uBMUcXFDLFqmkDg6k1Io4hQAAAAAAAAAAAAAAAAAQADAAACATQABgAFAFEAAAAAKamjF/Qsd/kxvqIOxdAVBzEna7suKGCUdmEkWyMZ74Ez7o1BQAEU/wD0pBP0vPLICwAHAgEgAA0ACAT48oMI1xgg0x/TH9MfAvgju/Jk7UTQ0x/TH9P/9ATRUUO68qFRUbryogX5AVQQZPkQ8qP4ACSkyMsfUkDLH1Iwy/9SEPQAye1U+A8B0wchwACfbFGTINdKltMH1AL7AOgw4CHAAeMAIcAC4wABwAORMOMNA6TIyx8Syx/L/wAMAAsACgAJAAr0AMntVABsgQEI1xj6ANM/MFIkgQEI9Fnyp4IQZHN0cnB0gBjIywXLAlAFzxZQA/oCE8tqyx8Syz/Jc/sAAHCBAQjXGPoA0z/IVCBHgQEI9FHyp4IQbm90ZXB0gBjIywXLAlAGzxZQBPoCFMtqEssfyz/Jc/sAAgBu0gf6ANTUIvkABcjKBxXL/8nQd3SAGMjLBcsCIs8WUAX6AhTLaxLMzMlz+wDIQBSBAQj0UfKnAgIBSAAXAA4CASAAEAAPAFm9JCtvaiaECAoGuQ+gIYRw1AgIR6STfSmRDOaQPp/5g3gSgBt4EBSJhxWfMYQCASAAEgARABG4yX7UTQ1wsfgCAVgAFgATAgEgABUAFAAZrx32omhAEGuQ64WPwAAZrc52omhAIGuQ64X/wAA9sp37UTQgQFA1yH0BDACyMoHy//J0AGBAQj0Cm+hMYALm0AHQ0wMhcbCSXwTgItdJwSCSXwTgAtMfIYIQcGx1Z70ighBkc3RyvbCSXwXgA/pAMCD6RAHIygfL/8nQ7UTQgQFA1yH0BDBcgQEI9ApvoTGzkl8H4AXTP8glghBwbHVnupI4MOMNA4IQZHN0crqSXwbjDQAZABgAilAEgQEI9Fkw7UTQgQFA1yDIAc8W9ADJ7VQBcrCOI4IQZHN0coMesXCAGFAFywVQA88WI/oCE8tqyx/LP8mAQPsAkl8D4gB4AfoA9AQw+CdvIjBQCqEhvvLgUIIQcGx1Z4MesXCAGFAEywUmzxZY+gIZ9ADLaRfLH1Jgyz8gyYBA+wAG");
+ assert_eq!(
+ output.hash.to_hex(),
+ "b3d9462c13a8c67e19b62002447839c386de51415ace3ff6473b1e6294299819"
);
+}
- assert_eq!(output.error, SigningError::Error_not_supported);
+#[test]
+fn test_ton_compile_wallet_v5r1_transfer_and_deploy() {
+ let public_key = "55b837ceefc94d1865b763149c4751b43e297cea65dabfe20febed13d56072e4";
+
+ let transfer = Proto::Transfer {
+ dest: "EQBe6DtCpJZe8M4t-crMXe93JlEYgSl30S5OUuMSLOfeQfBu".into(),
+ amount: 10,
+ mode: Proto::SendMode::PAY_FEES_SEPARATELY as u32
+ | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS as u32,
+ bounceable: true,
+ ..Proto::Transfer::default()
+ };
+
+ let input = Proto::SigningInput {
+ public_key: public_key.decode_hex().unwrap().into(),
+ messages: vec![transfer],
+ expire_at: 0xffffffff,
+ wallet_version: Proto::WalletVersion::WALLET_V5_R1,
+ ..Proto::SigningInput::default()
+ };
+
+ // Step 2: Obtain preimage hash
+ let mut pre_imager = PreImageHelper::::default();
+ let preimage_output = pre_imager.pre_image_hashes(CoinType::TON, &input);
+
+ assert_eq!(preimage_output.error, SigningError::OK);
+ assert_eq!(
+ preimage_output.data.to_hex(),
+ "d3b8dbedb049074d90bcae341484e0385789e19f69cd86d570939b94043739a7"
+ );
+
+ // Step 3: Compile transaction info
+
+ // Simulate signature, normally obtained from signature server.
+ let signature_bytes = "00239fa98ecbde5dda29ce7c78dc6e25369e1ff7e0070d9a1b472833e8630e98d684929b60a9ac003f2fd39e07612b8eeaf3776d09d5e54d7d061b067ded4c0e".decode_hex().unwrap();
+ let mut compiler = CompilerHelper::::default();
+ let output = compiler.compile(CoinType::TON, &input, vec![signature_bytes], vec![]);
+
+ assert_eq!(output.error, SigningError::OK, "{}", output.error_message);
+ // Successfully broadcasted: https://tonviewer.com/transaction/6ZzWOFKZt_m3kZjbwfbATwLaVwmUOdDp0xjhuY7PO3k=
+ assert_eq_boc(&output.encoded, "te6cckECGwEAA2sAAkWIACm9HPyVOpjCNOG6nbf+EwCONRHHpeMQsIlCoWNKhUaaHgECAgE0AwQBoXNpZ25///8R/////wAAAACACOfqY7L3l3aKc58eNxuJTaeH/fgBw2aG0coM+hjDpjWhJKbYKmsAD8v054HYSuO6vN3bQnV5U19BhsGfe1MDoAUBFP8A9KQT9LzyyAsGAFGAAAAAP///iKrcG+d35KaMMtuxik4jqNofFL51Mu1f8Qf19onqsDlyIAIKDsPIbQMWBwIBIAgJAWJiAC90HaFSSy94Zxb85WYu97uTKIxAlLvolycpcYkWc+8giFAAAAAAAAAAAAAAAAABFgIBSAoLAQLyDALc0CDXScEgkVuPYyDXCx8gghBleHRuvSGCEHNpbnS9sJJfA+CCEGV4dG66jrSAINchAdB01yH6QDD6RPgo+kQwWL2RW+DtRNCBAUHXIfQFgwf0Dm+hMZEw4YBA1yFwf9s84DEg10mBAoC5kTDgcOIXDQIBIA4PAR4g1wsfghBzaWduuvLgin8NAeaO8O2i7fshgwjXIgKDCNcjIIAg1yHTH9Mf0x/tRNDSANMfINMf0//XCgAK+QFAzPkQmiiUXwrbMeHywIffArNQB7Dy0IRRJbry4IVQNrry4Ib4I7vy0IgikvgA3gGkf8jKAMsfAc8Wye1UIJL4D95w2zzYFwIBIBARABm+Xw9qJoQICg65D6AsAgFuEhMCAUgUFQAZrc52omhAIOuQ64X/wAAZrx32omhAEOuQ64WPwAAXsyX7UTQcdch1wsfgABGyYvtRNDXCgCAAAAP27aLt+wL0BCFukmwhjkwCIdc5MHCUIccAs44tAdcoIHYeQ2wg10nACPLgkyDXSsAC8uCTINcdBscSwgBSMLDy0InXTNc5MAGk6GwShAe78uCT10rAAPLgk+1V4tIAAcAAkVvg69csCBQgkXCWAdcsCBwS4lIQseMPINdKGBkaAJYB+kAB+kT4KPpEMFi68uCR7UTQgQFB1xj0BQSdf8jKAEAEgwf0U/Lgi44UA4MH9Fvy4Iwi1woAIW4Bs7Dy0JDiyFADzxYS9ADJ7VQAcjDXLAgkji0h8uCS0gDtRNDSAFETuvLQj1RQMJExnAGBAUDXIdcKAPLgjuLIygBYzxbJ7VST8sCN4gAQk1vbMeHXTNB8Ui06");
+ assert_eq!(
+ output.hash.to_hex(),
+ "437dae441a95a6bccdcdcea2560c313de24f13dd85c76d5d7ecaab1e70a1e52b"
+ );
}
diff --git a/rust/tw_any_coin/tests/chains/ton/ton_sign.rs b/rust/tw_any_coin/tests/chains/ton/ton_sign.rs
index a34b9b2cdbc..5437b95bb20 100644
--- a/rust/tw_any_coin/tests/chains/ton/ton_sign.rs
+++ b/rust/tw_any_coin/tests/chains/ton/ton_sign.rs
@@ -15,7 +15,7 @@ use tw_proto::TheOpenNetwork::Proto::mod_Transfer::OneOfpayload as PayloadType;
/// The same Cell can be BoC encoded differently.
/// Use this function to compare inner Cells closing eyes on the encoding.
#[track_caller]
-fn assert_eq_boc(left: &str, right: &str) {
+pub(crate) fn assert_eq_boc(left: &str, right: &str) {
use tw_ton_sdk::boc::BagOfCells;
let left_boc = BagOfCells::parse_base64(left).unwrap();
@@ -29,7 +29,6 @@ fn test_ton_sign_transfer_and_deploy() {
let private_key = "63474e5fe9511f1526a50567ce142befc343e71a49b865ac3908f58667319cb8";
let transfer = Proto::Transfer {
- wallet_version: Proto::WalletVersion::WALLET_V4_R2,
dest: "EQDYW_1eScJVxtitoBRksvoV9cCYo4uKGWLVNIHB1JqRR3n0".into(),
amount: 10,
mode: Proto::SendMode::PAY_FEES_SEPARATELY as u32
@@ -42,6 +41,7 @@ fn test_ton_sign_transfer_and_deploy() {
private_key: private_key.decode_hex().unwrap().into(),
messages: vec![transfer],
expire_at: 1671135440,
+ wallet_version: Proto::WalletVersion::WALLET_V4_R2,
..Proto::SigningInput::default()
};
@@ -63,7 +63,6 @@ fn test_ton_sign_transfer_and_deploy_4b1d9f() {
let private_key = "e97620499dfee0107c0cd7f0ecb2afb3323d385b3a82320a5e3fa1fbdca6e722";
let transfer = Proto::Transfer {
- wallet_version: Proto::WalletVersion::WALLET_V4_R2,
dest: "UQA6whN_oU5h9jPljnlDSWRYQNDPkLaUqqaEWULNB_Zoykuu".into(),
// 0.0001 TON
amount: 100_000,
@@ -77,6 +76,7 @@ fn test_ton_sign_transfer_and_deploy_4b1d9f() {
private_key: private_key.decode_hex().unwrap().into(),
messages: vec![transfer],
expire_at: 1721892371,
+ wallet_version: Proto::WalletVersion::WALLET_V4_R2,
..Proto::SigningInput::default()
};
@@ -97,7 +97,6 @@ fn test_ton_sign_transfer_ordinary() {
let private_key = "c38f49de2fb13223a9e7d37d5d0ffbdd89a5eb7c8b0ee4d1c299f2cefe7dc4a0";
let transfer = Proto::Transfer {
- wallet_version: Proto::WalletVersion::WALLET_V4_R2,
dest: "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q".into(),
amount: 10,
mode: Proto::SendMode::PAY_FEES_SEPARATELY as u32
@@ -111,6 +110,7 @@ fn test_ton_sign_transfer_ordinary() {
messages: vec![transfer],
sequence_number: 6,
expire_at: 1671132440,
+ wallet_version: Proto::WalletVersion::WALLET_V4_R2,
..Proto::SigningInput::default()
};
@@ -131,7 +131,6 @@ fn test_ton_sign_transfer_all_balance() {
let private_key = "c38f49de2fb13223a9e7d37d5d0ffbdd89a5eb7c8b0ee4d1c299f2cefe7dc4a0";
let transfer = Proto::Transfer {
- wallet_version: Proto::WalletVersion::WALLET_V4_R2,
dest: "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q".into(),
amount: 0,
mode: Proto::SendMode::ATTACH_ALL_CONTRACT_BALANCE as u32
@@ -145,6 +144,7 @@ fn test_ton_sign_transfer_all_balance() {
messages: vec![transfer],
sequence_number: 7,
expire_at: 1681102222,
+ wallet_version: Proto::WalletVersion::WALLET_V4_R2,
..Proto::SigningInput::default()
};
@@ -165,7 +165,6 @@ fn test_ton_sign_transfer_all_balance_non_bounceable() {
let private_key = "c38f49de2fb13223a9e7d37d5d0ffbdd89a5eb7c8b0ee4d1c299f2cefe7dc4a0";
let transfer = Proto::Transfer {
- wallet_version: Proto::WalletVersion::WALLET_V4_R2,
dest: "UQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts4DV".into(),
amount: 0,
mode: Proto::SendMode::ATTACH_ALL_CONTRACT_BALANCE as u32
@@ -179,6 +178,7 @@ fn test_ton_sign_transfer_all_balance_non_bounceable() {
messages: vec![transfer],
sequence_number: 8,
expire_at: 1681102222,
+ wallet_version: Proto::WalletVersion::WALLET_V4_R2,
..Proto::SigningInput::default()
};
@@ -199,7 +199,6 @@ fn test_ton_sign_transfer_with_ascii_comment() {
let private_key = "c38f49de2fb13223a9e7d37d5d0ffbdd89a5eb7c8b0ee4d1c299f2cefe7dc4a0";
let transfer = Proto::Transfer {
- wallet_version: Proto::WalletVersion::WALLET_V4_R2,
dest: "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q".into(),
amount: 10,
mode: Proto::SendMode::PAY_FEES_SEPARATELY as u32
@@ -214,6 +213,7 @@ fn test_ton_sign_transfer_with_ascii_comment() {
messages: vec![transfer],
sequence_number: 10,
expire_at: 1681102222,
+ wallet_version: Proto::WalletVersion::WALLET_V4_R2,
..Proto::SigningInput::default()
};
@@ -234,7 +234,6 @@ fn test_ton_sign_transfer_with_utf8_comment() {
let private_key = "c38f49de2fb13223a9e7d37d5d0ffbdd89a5eb7c8b0ee4d1c299f2cefe7dc4a0";
let transfer = Proto::Transfer {
- wallet_version: Proto::WalletVersion::WALLET_V4_R2,
dest: "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q".into(),
amount: 10,
mode: Proto::SendMode::PAY_FEES_SEPARATELY as u32
@@ -249,6 +248,7 @@ fn test_ton_sign_transfer_with_utf8_comment() {
messages: vec![transfer],
sequence_number: 11,
expire_at: 1681102222,
+ wallet_version: Proto::WalletVersion::WALLET_V4_R2,
..Proto::SigningInput::default()
};
@@ -269,7 +269,6 @@ fn test_ton_sign_transfer_invalid_wallet_version() {
let private_key = "63474e5fe9511f1526a50567ce142befc343e71a49b865ac3908f58667319cb8";
let transfer = Proto::Transfer {
- wallet_version: Proto::WalletVersion::WALLET_V3_R2,
dest: "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q".into(),
amount: 10,
mode: Proto::SendMode::PAY_FEES_SEPARATELY as u32
@@ -282,6 +281,7 @@ fn test_ton_sign_transfer_invalid_wallet_version() {
private_key: private_key.decode_hex().unwrap().into(),
messages: vec![transfer],
expire_at: 1671135440,
+ wallet_version: Proto::WalletVersion::WALLET_V3_R2,
..Proto::SigningInput::default()
};
@@ -306,7 +306,6 @@ fn test_ton_sign_transfer_jettons() {
};
let transfer = Proto::Transfer {
- wallet_version: Proto::WalletVersion::WALLET_V4_R2,
dest: "EQBiaD8PO1NwfbxSkwbcNT9rXDjqhiIvXWymNO-edV0H5lja".into(),
amount: 100 * 1000 * 1000,
mode: Proto::SendMode::PAY_FEES_SEPARATELY as u32
@@ -321,6 +320,7 @@ fn test_ton_sign_transfer_jettons() {
messages: vec![transfer],
sequence_number: 0,
expire_at: 1787693046,
+ wallet_version: Proto::WalletVersion::WALLET_V4_R2,
..Proto::SigningInput::default()
};
@@ -351,7 +351,6 @@ fn test_ton_sign_transfer_jettons_with_comment() {
};
let transfer = Proto::Transfer {
- wallet_version: Proto::WalletVersion::WALLET_V4_R2,
dest: "EQBiaD8PO1NwfbxSkwbcNT9rXDjqhiIvXWymNO-edV0H5lja".into(),
amount: 100 * 1000 * 1000,
mode: Proto::SendMode::PAY_FEES_SEPARATELY as u32
@@ -367,6 +366,7 @@ fn test_ton_sign_transfer_jettons_with_comment() {
messages: vec![transfer],
sequence_number: 1,
expire_at: 1787693046,
+ wallet_version: Proto::WalletVersion::WALLET_V4_R2,
..Proto::SigningInput::default()
};
@@ -388,7 +388,6 @@ fn test_ton_sign_transfer_custom_payload() {
let private_key = "e97620499dfee0107c0cd7f0ecb2afb3323d385b3a82320a5e3fa1fbdca6e722";
let transfer = Proto::Transfer {
- wallet_version: Proto::WalletVersion::WALLET_V4_R2,
dest: "UQA6whN_oU5h9jPljnlDSWRYQNDPkLaUqqaEWULNB_Zoykuu".into(),
// 0.00025 TON
amount: 250_000,
@@ -407,6 +406,7 @@ fn test_ton_sign_transfer_custom_payload() {
expire_at: 1721906219,
messages: vec![transfer],
sequence_number: 2,
+ wallet_version: Proto::WalletVersion::WALLET_V4_R2,
..Proto::SigningInput::default()
};
@@ -414,7 +414,7 @@ fn test_ton_sign_transfer_custom_payload() {
let output = signer.sign(CoinType::TON, input);
assert_eq!(output.error, SigningError::OK, "{}", output.error_message);
- // Successfully broadcasted: https://tonviewer.com/transaction/4b1d9f09856af70ea1058b557a87c9ba2abb0bca2029e0cbbe8c659d5dae4ce1
+ // Successfully broadcasted: https://tonviewer.com/transaction/4ca4442921d0737d518b4863f8eafc2eb26824e9362ebaa9bc59373950b7fa86
assert_eq_boc(&output.encoded, "te6cckEBBAEAvwABRYgAUunJPoppOUA+tmGjPUbtz/BjUoB+QZYAbdqNMq3gIWQMAQGc981GQ9a8Yr4m2YeIeuuNIWlzdHliyW6MRq3RDs5kgvXJP+iNhdZU7o79DJnm/OKuzWI5FbiNy3SF0fGGBObDDCmpoxdmojQrAAAAAgADAgFmYgAdYQm/0Kcw+xnyxzyhpLIsIGhnyFtKVVNCLKFmg/s0ZRgehIAAAAAAAAAAAAAAAAABAwAgAAAAAEhpIHRoZXJlIHNpcoAlI8E=");
assert_eq!(
output.hash.to_hex(),
@@ -442,7 +442,6 @@ fn test_ton_sign_transfer_custom_payload_with_state_init() {
);
let transfer = Proto::Transfer {
- wallet_version: Proto::WalletVersion::WALLET_V4_R2,
dest: doge_contract_address.into(),
// 0.069 TON
amount: 69_000_000,
@@ -461,6 +460,7 @@ fn test_ton_sign_transfer_custom_payload_with_state_init() {
expire_at,
messages: vec![transfer],
sequence_number: 4,
+ wallet_version: Proto::WalletVersion::WALLET_V4_R2,
..Proto::SigningInput::default()
};
diff --git a/rust/tw_any_coin/tests/chains/ton/ton_sign_wallet_v5r1.rs b/rust/tw_any_coin/tests/chains/ton/ton_sign_wallet_v5r1.rs
new file mode 100644
index 00000000000..9e5c0f5308c
--- /dev/null
+++ b/rust/tw_any_coin/tests/chains/ton/ton_sign_wallet_v5r1.rs
@@ -0,0 +1,434 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+use crate::chains::ton::cell_example::{
+ comment_cell, contract_address_from_state_init, doge_chatbot_state_init,
+};
+use crate::chains::ton::ton_sign::assert_eq_boc;
+use tw_any_coin::test_utils::sign_utils::AnySignerHelper;
+use tw_coin_registry::coin_type::CoinType;
+use tw_encoding::hex::{DecodeHex, ToHex};
+use tw_proto::Common::Proto::SigningError;
+use tw_proto::TheOpenNetwork::Proto;
+use tw_proto::TheOpenNetwork::Proto::mod_Transfer::OneOfpayload as PayloadType;
+
+#[test]
+fn test_ton_sign_wallet_v5r1_transfer_and_deploy() {
+ let private_key = "3570e35f54cfb843f2cfaf2b8cae7ceeb7b32225d7dbbd86f611056d74d9073e";
+
+ let transfer = Proto::Transfer {
+ dest: "EQBe6DtCpJZe8M4t-crMXe93JlEYgSl30S5OUuMSLOfeQfBu".into(),
+ amount: 10,
+ mode: Proto::SendMode::PAY_FEES_SEPARATELY as u32
+ | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS as u32,
+ bounceable: true,
+ ..Proto::Transfer::default()
+ };
+
+ let input = Proto::SigningInput {
+ private_key: private_key.decode_hex().unwrap().into(),
+ messages: vec![transfer],
+ expire_at: 0xffffffff,
+ wallet_version: Proto::WalletVersion::WALLET_V5_R1,
+ ..Proto::SigningInput::default()
+ };
+
+ let mut signer = AnySignerHelper::::default();
+ let output = signer.sign(CoinType::TON, input);
+
+ assert_eq!(output.error, SigningError::OK, "{}", output.error_message);
+ // Successfully broadcasted: https://tonviewer.com/transaction/Q32uRBqVprzNzc6iVgwxPeJPE92Fx21dfsqrHnCh5Ss=
+ assert_eq_boc(&output.encoded, "te6cckECGwEAA2sAAkWIACm9HPyVOpjCNOG6nbf+EwCONRHHpeMQsIlCoWNKhUaaHgECAgE0AwQBoXNpZ25///8R/////wAAAACACOfqY7L3l3aKc58eNxuJTaeH/fgBw2aG0coM+hjDpjWhJKbYKmsAD8v054HYSuO6vN3bQnV5U19BhsGfe1MDoAUBFP8A9KQT9LzyyAsGAFGAAAAAP///iKrcG+d35KaMMtuxik4jqNofFL51Mu1f8Qf19onqsDlyIAIKDsPIbQMWBwIBIAgJAWJiAC90HaFSSy94Zxb85WYu97uTKIxAlLvolycpcYkWc+8giFAAAAAAAAAAAAAAAAABFgIBSAoLAQLyDALc0CDXScEgkVuPYyDXCx8gghBleHRuvSGCEHNpbnS9sJJfA+CCEGV4dG66jrSAINchAdB01yH6QDD6RPgo+kQwWL2RW+DtRNCBAUHXIfQFgwf0Dm+hMZEw4YBA1yFwf9s84DEg10mBAoC5kTDgcOIXDQIBIA4PAR4g1wsfghBzaWduuvLgin8NAeaO8O2i7fshgwjXIgKDCNcjIIAg1yHTH9Mf0x/tRNDSANMfINMf0//XCgAK+QFAzPkQmiiUXwrbMeHywIffArNQB7Dy0IRRJbry4IVQNrry4Ib4I7vy0IgikvgA3gGkf8jKAMsfAc8Wye1UIJL4D95w2zzYFwIBIBARABm+Xw9qJoQICg65D6AsAgFuEhMCAUgUFQAZrc52omhAIOuQ64X/wAAZrx32omhAEOuQ64WPwAAXsyX7UTQcdch1wsfgABGyYvtRNDXCgCAAAAP27aLt+wL0BCFukmwhjkwCIdc5MHCUIccAs44tAdcoIHYeQ2wg10nACPLgkyDXSsAC8uCTINcdBscSwgBSMLDy0InXTNc5MAGk6GwShAe78uCT10rAAPLgk+1V4tIAAcAAkVvg69csCBQgkXCWAdcsCBwS4lIQseMPINdKGBkaAJYB+kAB+kT4KPpEMFi68uCR7UTQgQFB1xj0BQSdf8jKAEAEgwf0U/Lgi44UA4MH9Fvy4Iwi1woAIW4Bs7Dy0JDiyFADzxYS9ADJ7VQAcjDXLAgkji0h8uCS0gDtRNDSAFETuvLQj1RQMJExnAGBAUDXIdcKAPLgjuLIygBYzxbJ7VST8sCN4gAQk1vbMeHXTNB8Ui06");
+ assert_eq!(
+ output.hash.to_hex(),
+ "437dae441a95a6bccdcdcea2560c313de24f13dd85c76d5d7ecaab1e70a1e52b"
+ );
+}
+
+#[test]
+fn test_ton_sign_wallet_v5r1_transfer_ordinary() {
+ let private_key = "3570e35f54cfb843f2cfaf2b8cae7ceeb7b32225d7dbbd86f611056d74d9073e";
+
+ let transfer = Proto::Transfer {
+ dest: "EQBe6DtCpJZe8M4t-crMXe93JlEYgSl30S5OUuMSLOfeQfBu".into(),
+ amount: 10,
+ mode: Proto::SendMode::PAY_FEES_SEPARATELY as u32
+ | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS as u32,
+ bounceable: true,
+ ..Proto::Transfer::default()
+ };
+
+ let input = Proto::SigningInput {
+ private_key: private_key.decode_hex().unwrap().into(),
+ messages: vec![transfer],
+ sequence_number: 1,
+ expire_at: 1723469663,
+ wallet_version: Proto::WalletVersion::WALLET_V5_R1,
+ ..Proto::SigningInput::default()
+ };
+
+ let mut signer = AnySignerHelper::::default();
+ let output = signer.sign(CoinType::TON, input);
+
+ assert_eq!(output.error, SigningError::OK, "{}", output.error_message);
+ // Successfully broadcasted: https://tonviewer.com/transaction/UhZFShj8QG6M4nUPELVROdY_IAxSSkeZojh-t2XC2IU=
+ assert_eq_boc(&output.encoded, "te6cckEBBQEAuQABRYgAKb0c/JU6mMI04bqdt/4TAI41Ecel4xCwiUKhY0qFRpoMAQGhc2lnbn///xFmug9fAAAAAbsTmIIbTn/5n8piniYykIpNGhw0mstvB4PfGuTjdh7eIY3HAokbPMSdaAoOXq8mTWzT2p3v6XSoqTh7XKacOIDgAgIKDsPIbQMEAwFiYgAvdB2hUksveGcW/OVmLve7kyiMQJS76JcnKXGJFnPvIIhQAAAAAAAAAAAAAAAAAQQAAN6nHJg=");
+
+ assert_eq!(
+ output.hash.to_hex(),
+ "5216454a18fc406e8ce2750f10b55139d63f200c524a4799a2387eb765c2d885"
+ );
+}
+
+#[test]
+fn test_ton_sign_wallet_v5r1_transfer_all_balance() {
+ let private_key = "502d60b0f3327382e7d0585b789f1db9aa04907fe5cddc5c28818ec163ebf4ba";
+
+ let transfer = Proto::Transfer {
+ dest: "UQAU3o5-Sp1MYRpw3U7b_wmARxqI49LxiFhEoVCxpUKjTYXk".into(),
+ amount: 0,
+ mode: Proto::SendMode::ATTACH_ALL_CONTRACT_BALANCE as u32
+ | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS as u32,
+ bounceable: true,
+ ..Proto::Transfer::default()
+ };
+
+ let input = Proto::SigningInput {
+ private_key: private_key.decode_hex().unwrap().into(),
+ messages: vec![transfer],
+ sequence_number: 4,
+ expire_at: 1723482783,
+ wallet_version: Proto::WalletVersion::WALLET_V5_R1,
+ ..Proto::SigningInput::default()
+ };
+
+ let mut signer = AnySignerHelper::::default();
+ let output = signer.sign(CoinType::TON, input);
+
+ assert_eq!(output.error, SigningError::OK, "{}", output.error_message);
+ // Successfully broadcasted: https://tonviewer.com/transaction/q7MJULnzKZ1DY4itqKfHg_cj48ueFRIlT6I2O8rSLt0=
+ println!("{}", output.encoded);
+ assert_eq_boc(&output.encoded, "te6cckEBBQEAuAABRYgBQ8awIH6gcEaSzgT9ZAPxWhgEMUrjdKCkL1PJZ6pq4mgMAQGhc2lnbn///xFmukKfAAAABKg9bRJRcUN/+LR7Bny8XjKzf3+GzopWEf+n7ibzPpr9ZOZs5CRon4i0XFBiFMVVwfcnDI4RVWht6Jf5eSzOjwEgAgIKDsPIbYIEAwFgYgAKb0c/JU6mMI04bqdt/4TAI41Ecel4xCwiUKhY0qFRpoAAAAAAAAAAAAAAAAABBAAANXkwpA==");
+ assert_eq!(
+ output.hash.to_hex(),
+ "abb30950b9f3299d436388ada8a7c783f723e3cb9e1512254fa2363bcad22edd"
+ );
+}
+
+#[test]
+fn test_ton_sign_wallet_v5r1_transfer_all_balance_non_bounceable() {
+ let private_key = "3570e35f54cfb843f2cfaf2b8cae7ceeb7b32225d7dbbd86f611056d74d9073e";
+
+ let transfer = Proto::Transfer {
+ dest: "UQBe6DtCpJZe8M4t-crMXe93JlEYgSl30S5OUuMSLOfeQa2r".into(),
+ amount: 0,
+ mode: Proto::SendMode::ATTACH_ALL_CONTRACT_BALANCE as u32
+ | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS as u32,
+ bounceable: false,
+ ..Proto::Transfer::default()
+ };
+
+ let input = Proto::SigningInput {
+ private_key: private_key.decode_hex().unwrap().into(),
+ messages: vec![transfer],
+ sequence_number: 2,
+ expire_at: 1723470825,
+ wallet_version: Proto::WalletVersion::WALLET_V5_R1,
+ ..Proto::SigningInput::default()
+ };
+
+ let mut signer = AnySignerHelper::::default();
+ let output = signer.sign(CoinType::TON, input);
+
+ assert_eq!(output.error, SigningError::OK, "{}", output.error_message);
+ // Successfully broadcasted: https://tonviewer.com/transaction/f8meQabVMmfvUOJ0D_NTyji1qr4LJ58tTkPI6dlYSPE=
+ assert_eq_boc(&output.encoded, "te6cckEBBQEAuAABRYgAKb0c/JU6mMI04bqdt/4TAI41Ecel4xCwiUKhY0qFRpoMAQGhc2lnbn///xFmuhPpAAAAAq9jgguGOlFmpN566XBRCth1GfF8kVEc0DJkvLPKOa7MQMGzr0L1xpLt5OLovDpy4KwPqNxALo/b9NluZDLcukPgAgIKDsPIbYIEAwFgQgAvdB2hUksveGcW/OVmLve7kyiMQJS76JcnKXGJFnPvIIAAAAAAAAAAAAAAAAABBAAAI3vzjA==");
+ assert_eq!(
+ output.hash.to_hex(),
+ "7fc99e41a6d53267ef50e2740ff353ca38b5aabe0b279f2d4e43c8e9d95848f1"
+ );
+}
+
+#[test]
+fn test_ton_sign_wallet_v5r1_transfer_with_ascii_comment() {
+ let private_key = "3570e35f54cfb843f2cfaf2b8cae7ceeb7b32225d7dbbd86f611056d74d9073e";
+
+ let transfer = Proto::Transfer {
+ dest: "EQBe6DtCpJZe8M4t-crMXe93JlEYgSl30S5OUuMSLOfeQfBu".into(),
+ amount: 10,
+ mode: Proto::SendMode::PAY_FEES_SEPARATELY as u32
+ | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS as u32,
+ bounceable: true,
+ comment: "test comment".into(),
+ ..Proto::Transfer::default()
+ };
+
+ let input = Proto::SigningInput {
+ private_key: private_key.decode_hex().unwrap().into(),
+ messages: vec![transfer],
+ sequence_number: 3,
+ expire_at: 1723472681,
+ wallet_version: Proto::WalletVersion::WALLET_V5_R1,
+ ..Proto::SigningInput::default()
+ };
+
+ let mut signer = AnySignerHelper::::default();
+ let output = signer.sign(CoinType::TON, input);
+
+ assert_eq!(output.error, SigningError::OK, "{}", output.error_message);
+ // Successfully broadcasted: https://tonviewer.com/transaction/uc9sMl9Ey3yQ6sX48EKM0nFSeJoq2JcEi0xMzTLYV6M=
+ assert_eq_boc(&output.encoded, "te6cckEBBgEAywABRYgAKb0c/JU6mMI04bqdt/4TAI41Ecel4xCwiUKhY0qFRpoMAQGhc2lnbn///xFmuhspAAAAA6oLkiOh1mWEhej/y/dCZAZZT1hFo6aX9ROjIOX5HvwBz99SLLlCmTJd4QV1Vp9D9eaTXL7aT28rswEjyBSYpwPgAgIKDsPIbQMDBAAAAWJiAC90HaFSSy94Zxb85WYu97uTKIxAlLvolycpcYkWc+8giFAAAAAAAAAAAAAAAAABBQAgAAAAAHRlc3QgY29tbWVudDHkkjo=");
+ assert_eq!(
+ output.hash.to_hex(),
+ "b9cf6c325f44cb7c90eac5f8f0428cd27152789a2ad897048b4c4ccd32d857a3"
+ );
+}
+
+#[test]
+fn test_ton_sign_wallet_v5r1_transfer_with_utf8_comment() {
+ let private_key = "3570e35f54cfb843f2cfaf2b8cae7ceeb7b32225d7dbbd86f611056d74d9073e";
+
+ let transfer = Proto::Transfer {
+ dest: "EQBe6DtCpJZe8M4t-crMXe93JlEYgSl30S5OUuMSLOfeQfBu".into(),
+ amount: 10,
+ mode: Proto::SendMode::PAY_FEES_SEPARATELY as u32
+ | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS as u32,
+ bounceable: true,
+ comment: "тестовый комментарий".into(),
+ ..Proto::Transfer::default()
+ };
+
+ let input = Proto::SigningInput {
+ private_key: private_key.decode_hex().unwrap().into(),
+ messages: vec![transfer],
+ sequence_number: 4,
+ expire_at: 1723472948,
+ wallet_version: Proto::WalletVersion::WALLET_V5_R1,
+ ..Proto::SigningInput::default()
+ };
+
+ let mut signer = AnySignerHelper::::default();
+ let output = signer.sign(CoinType::TON, input);
+
+ assert_eq!(output.error, SigningError::OK, "{}", output.error_message);
+ // Successfully broadcasted: https://tonviewer.com/transaction/tijrXAMehLvfESLWWgGf1R5jhW4CMoio11ZFD_musFg=
+ assert_eq_boc(&output.encoded, "te6cckEBBgEA5gABRYgAKb0c/JU6mMI04bqdt/4TAI41Ecel4xCwiUKhY0qFRpoMAQGhc2lnbn///xFmuhw0AAAABIDSMWXCM7WX+aQyurxO4nsYaqKhHm4ndzkjAS00AhtXe5fpKHUvVAFDvyqCi5T/PI/fwyZdralzzpXTrQaRP0CgAgIKDsPIbQMDBAAAAWJiAC90HaFSSy94Zxb85WYu97uTKIxAlLvolycpcYkWc+8giFAAAAAAAAAAAAAAAAABBQBWAAAAANGC0LXRgdGC0L7QstGL0Lkg0LrQvtC80LzQtdC90YLQsNGA0LjQuV6Inwc=");
+ assert_eq!(
+ output.hash.to_hex(),
+ "b628eb5c031e84bbdf1122d65a019fd51e63856e023288a8d756450ff9aeb058"
+ );
+}
+
+#[test]
+fn test_ton_sign_wallet_v5r1_transfer_jettons() {
+ let private_key = "502d60b0f3327382e7d0585b789f1db9aa04907fe5cddc5c28818ec163ebf4ba";
+
+ let jetton_transfer = Proto::JettonTransfer {
+ query_id: 69,
+ // Transfer 0.12 USDT (decimal precision is 6).
+ jetton_amount: 120000,
+ to_owner: "UQAU3o5-Sp1MYRpw3U7b_wmARxqI49LxiFhEoVCxpUKjTYXk".into(),
+ // Send unused toncoins back to sender.
+ response_address: "UQCh41gQP1A4I0lnAn6yAfitDAIYpXG6UFIXqeSz1TVxNOJ_".into(),
+ forward_amount: 1,
+ };
+
+ let transfer = Proto::Transfer {
+ dest: "EQDg4AjfaxQBVsUFueenkKlHLhhYWrcBvCEzbEgfrT0nxuGC".into(),
+ amount: 100 * 1000 * 1000,
+ mode: Proto::SendMode::PAY_FEES_SEPARATELY as u32
+ | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS as u32,
+ bounceable: true,
+ payload: PayloadType::jetton_transfer(jetton_transfer),
+ ..Proto::Transfer::default()
+ };
+
+ let input = Proto::SigningInput {
+ private_key: private_key.decode_hex().unwrap().into(),
+ messages: vec![transfer],
+ sequence_number: 0,
+ expire_at: 1723479990,
+ wallet_version: Proto::WalletVersion::WALLET_V5_R1,
+ ..Proto::SigningInput::default()
+ };
+
+ let mut signer = AnySignerHelper::::default();
+ let output = signer.sign(CoinType::TON, input);
+
+ assert_eq!(output.error, SigningError::OK, "{}", output.error_message);
+ // Successfully broadcasted: https://tonviewer.com/transaction/CapFFJlwYNa_p8-fImgUPyKLxNuq4mR5K7U-G3zbFNg=
+ assert_eq_boc(&output.encoded, "te6cckECHAEAA8QAAkWIAUPGsCB+oHBGks4E/WQD8VoYBDFK43SgpC9TyWeqauJoHgECAgE0AwQBoXNpZ25///8R/////wAAAACK9TWZkcw/aJwl28F2cStjO8o6owv7/VKsx3RW0XtPaiwwFgi+CkmtzzJLGGp24JEwbwslQ1jNHyRqqUEM41AD4AUBFP8A9KQT9LzyyAsGAFGAAAAAP///iOEJZED3G4tDJvijkvHmVvq03ZLJ8Db8Bmagv84HH0SSIAIKDsPIbQMHCAIBIAkKAAABaGIAcHAEb7WKAKtigtzz08hUo5cMLC1bgN4QmbYkD9aek+MgL68IAAAAAAAAAAAAAAAAAAELAgFIDA0BAvIOAKgPin6lAAAAAAAAAEUwHUwIACm9HPyVOpjCNOG6nbf+EwCONRHHpeMQsIlCoWNKhUabACh41gQP1A4I0lnAn6yAfitDAIYpXG6UFIXqeSz1TVxNAgIC3NAg10nBIJFbj2Mg1wsfIIIQZXh0br0hghBzaW50vbCSXwPgghBleHRuuo60gCDXIQHQdNch+kAw+kT4KPpEMFi9kVvg7UTQgQFB1yH0BYMH9A5voTGRMOGAQNchcH/bPOAxINdJgQKAuZEw4HDiGA8CASAQEQEeINcLH4IQc2lnbrry4Ip/DwHmjvDtou37IYMI1yICgwjXIyCAINch0x/TH9Mf7UTQ0gDTHyDTH9P/1woACvkBQMz5EJoolF8K2zHh8sCH3wKzUAew8tCEUSW68uCFUDa68uCG+CO78tCIIpL4AN4BpH/IygDLHwHPFsntVCCS+A/ecNs82BgCASASEwAZvl8PaiaECAoOuQ+gLAIBbhQVAgFIFhcAGa3OdqJoQCDrkOuF/8AAGa8d9qJoQBDrkOuFj8AAF7Ml+1E0HHXIdcLH4AARsmL7UTQ1woAgA/btou37AvQEIW6SbCGOTAIh1zkwcJQhxwCzji0B1yggdh5DbCDXScAI8uCTINdKwALy4JMg1x0GxxLCAFIwsPLQiddM1zkwAaTobBKEB7vy4JPXSsAA8uCT7VXi0gABwACRW+Dr1ywIFCCRcJYB1ywIHBLiUhCx4w8g10oZGhsAlgH6QAH6RPgo+kQwWLry4JHtRNCBAUHXGPQFBJ1/yMoAQASDB/RT8uCLjhQDgwf0W/LgjCLXCgAhbgGzsPLQkOLIUAPPFhL0AMntVAByMNcsCCSOLSHy4JLSAO1E0NIAURO68tCPVFAwkTGcAYEBQNch1woA8uCO4sjKAFjPFsntVJPywI3iABCTW9sx4ddM0JlgUSM=");
+ assert_eq!(
+ output.hash.to_hex(),
+ "09aa4514997060d6bfa7cf9f2268143f228bc4dbaae264792bb53e1b7cdb14d8"
+ );
+}
+
+#[test]
+fn test_ton_sign_wallet_v5r1_transfer_jettons_with_comment() {
+ let private_key = "502d60b0f3327382e7d0585b789f1db9aa04907fe5cddc5c28818ec163ebf4ba";
+
+ let jetton_transfer = Proto::JettonTransfer {
+ query_id: 0,
+ // Transfer 0.11 USDT (decimal precision is 6).
+ jetton_amount: 110000,
+ to_owner: "UQAU3o5-Sp1MYRpw3U7b_wmARxqI49LxiFhEoVCxpUKjTYXk".into(),
+ // Send unused toncoins back to sender.
+ response_address: "UQCh41gQP1A4I0lnAn6yAfitDAIYpXG6UFIXqeSz1TVxNOJ_".into(),
+ forward_amount: 1,
+ };
+
+ let transfer = Proto::Transfer {
+ dest: "EQDg4AjfaxQBVsUFueenkKlHLhhYWrcBvCEzbEgfrT0nxuGC".into(),
+ amount: 100 * 1000 * 1000,
+ mode: Proto::SendMode::PAY_FEES_SEPARATELY as u32
+ | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS as u32,
+ bounceable: true,
+ comment: "test comment".into(),
+ payload: PayloadType::jetton_transfer(jetton_transfer),
+ ..Proto::Transfer::default()
+ };
+
+ let input = Proto::SigningInput {
+ private_key: private_key.decode_hex().unwrap().into(),
+ messages: vec![transfer],
+ sequence_number: 1,
+ expire_at: 1723480954,
+ wallet_version: Proto::WalletVersion::WALLET_V5_R1,
+ ..Proto::SigningInput::default()
+ };
+
+ let mut signer = AnySignerHelper::::default();
+ let output = signer.sign(CoinType::TON, input);
+
+ assert_eq!(output.error, SigningError::OK, "{}", output.error_message);
+ // Successfully broadcasted: https://tonviewer.com/transaction/gkyDtRqJa_5-GGut6BZcVS5JmRLbObGiKrpmaIBPoxc=
+ assert_eq_boc(&output.encoded, "te6cckECBgEAASIAAUWIAUPGsCB+oHBGks4E/WQD8VoYBDFK43SgpC9TyWeqauJoDAEBoXNpZ25///8RZro7egAAAAGYUu8+ZHZK/wWb9ojB++h5tz2ie7e4GktWZfr475zJcnpRLZADd9wbyuuV7GXr8QzKeqvQbSDVJlgv4bcdnjwDYAICCg7DyG0DAwQAAAFoYgBwcARvtYoAq2KC3PPTyFSjlwwsLVuA3hCZtiQP1p6T4yAvrwgAAAAAAAAAAAAAAAAAAQUAyA+KfqUAAAAAAAAAADAa2wgAKb0c/JU6mMI04bqdt/4TAI41Ecel4xCwiUKhY0qFRpsAKHjWBA/UDgjSWcCfrIB+K0MAhilcbpQUhep5LPVNXE0CAgAAAAB0ZXN0IGNvbW1lbnQIl4du");
+ assert_eq!(
+ output.hash.to_hex(),
+ "824c83b51a896bfe7e186bade8165c552e499912db39b1a22aba6668804fa317"
+ );
+}
+
+#[test]
+fn test_ton_sign_wallet_v5r1_transfer_custom_payload() {
+ // UQCh41gQP1A4I0lnAn6yAfitDAIYpXG6UFIXqeSz1TVxNOJ_
+ let private_key = "502d60b0f3327382e7d0585b789f1db9aa04907fe5cddc5c28818ec163ebf4ba";
+
+ let transfer = Proto::Transfer {
+ dest: "UQAU3o5-Sp1MYRpw3U7b_wmARxqI49LxiFhEoVCxpUKjTYXk".into(),
+ // 0.00025 TON
+ amount: 250_000,
+ mode: Proto::SendMode::PAY_FEES_SEPARATELY as u32
+ | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS as u32,
+ bounceable: true,
+ payload: PayloadType::custom_payload(Proto::CustomPayload {
+ state_init: "".into(),
+ payload: comment_cell("Hi there sir").into(),
+ }),
+ ..Proto::Transfer::default()
+ };
+
+ let input = Proto::SigningInput {
+ private_key: private_key.decode_hex().unwrap().into(),
+ expire_at: 1723481664,
+ messages: vec![transfer],
+ sequence_number: 2,
+ wallet_version: Proto::WalletVersion::WALLET_V5_R1,
+ ..Proto::SigningInput::default()
+ };
+
+ let mut signer = AnySignerHelper::::default();
+ let output = signer.sign(CoinType::TON, input);
+
+ assert_eq!(output.error, SigningError::OK, "{}", output.error_message);
+ // Successfully broadcasted: https://tonviewer.com/transaction/_Pr9kkthRh5qQtALT4HPpWJKRF3qK7L7DUa_hmu3VAI=
+ assert_eq_boc(&output.encoded, "te6cckEBBgEAzQABRYgBQ8awIH6gcEaSzgT9ZAPxWhgEMUrjdKCkL1PJZ6pq4mgMAQGhc2lnbn///xFmuj5AAAAAAqWnjYMI5ukKZ/38yUBJq3dj7fHGvxzmY4WgFTqo6utiQ1rAW1gZTm8tErHXXXEZykBfG9qDSKaTB/XEvbvTK4CgAgIKDsPIbQMDBAAAAWZiAApvRz8lTqYwjThup23/hMAjjURx6XjELCJQqFjSoVGmmB6EgAAAAAAAAAAAAAAAAAEFACAAAAAASGkgdGhlcmUgc2lyZT4q+g==");
+ assert_eq!(
+ output.hash.to_hex(),
+ "fcfafd924b61461e6a42d00b4f81cfa5624a445dea2bb2fb0d46bf866bb75402"
+ );
+}
+
+#[test]
+fn test_ton_sign_wallet_v5r1_transfer_custom_payload_with_state_init() {
+ // UQCh41gQP1A4I0lnAn6yAfitDAIYpXG6UFIXqeSz1TVxNOJ_
+ let private_key = "502d60b0f3327382e7d0585b789f1db9aa04907fe5cddc5c28818ec163ebf4ba";
+
+ let current_timestamp = 1723481400;
+ let expire_at = current_timestamp + 60 * 10;
+
+ let doge_state_init = doge_chatbot_state_init(current_timestamp);
+ let doge_contract_address = contract_address_from_state_init(&doge_state_init);
+ assert_eq!(
+ doge_state_init,
+ "te6cckEBBAEAUwACATQBAgEU/wD0pBP0vPLICwMAEAAAAZFHfyLAAGrTMAGCCGlJILmRMODQ0wMx+kAwi0ZG9nZYcCCAGMjLBVAEzxaARfoCE8tqEssfAc8WyXP7AOj1qKs="
+ );
+ assert_eq!(
+ doge_contract_address,
+ "0:6af6c37f5e1704fc46174f0cbf5b558cbbf1dbc750c59c3c851bb98c7a94a145",
+ );
+
+ let transfer = Proto::Transfer {
+ dest: doge_contract_address.into(),
+ // 0.0069 TON
+ amount: 6_900_000,
+ mode: Proto::SendMode::PAY_FEES_SEPARATELY as u32
+ | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS as u32,
+ bounceable: false,
+ payload: PayloadType::custom_payload(Proto::CustomPayload {
+ state_init: doge_state_init.into(),
+ payload: comment_cell("This transaction deploys Doge Chatbot contract").into(),
+ }),
+ ..Proto::Transfer::default()
+ };
+
+ let input = Proto::SigningInput {
+ private_key: private_key.decode_hex().unwrap().into(),
+ expire_at,
+ messages: vec![transfer],
+ sequence_number: 3,
+ wallet_version: Proto::WalletVersion::WALLET_V5_R1,
+ ..Proto::SigningInput::default()
+ };
+
+ let mut signer = AnySignerHelper::::default();
+ let output = signer.sign(CoinType::TON, input);
+
+ assert_eq!(output.error, SigningError::OK, "{}", output.error_message);
+ // Successfully broadcasted: https://tonviewer.com/transaction/Y35nc-J86gbrMC5CljEMqzCUG5vFLilLjNdHgb193eA=
+ assert_eq_boc(&output.encoded, "te6cckECCgEAAUQAAUWIAUPGsCB+oHBGks4E/WQD8VoYBDFK43SgpC9TyWeqauJoDAEBoXNpZ25///8RZro/kAAAAAOAA5mkphyRfc8/OtuqrYWeR9u1Eib135FXAntzMzjdjj+1xH96YruRHinS9gB3bfHxtK3QUvy+zrcqKliVvbtA4AICCg7DyG0DAwQAAAJnQgA1e2G/rwuCfiMLp4ZfrarGXfjt46hizh5CjdzGPUpQoptKSQAAAAAAAAAAAAAAAAADwAUGAgE0BwgAZAAAAABUaGlzIHRyYW5zYWN0aW9uIGRlcGxveXMgRG9nZSBDaGF0Ym90IGNvbnRyYWN0ART/APSkE/S88sgLCQAQAAABkUd/IsAAatMwAYIIaUkguZEw4NDTAzH6QDCLRkb2dlhwIIAYyMsFUATPFoBF+gITy2oSyx8BzxbJc/sArbH+dw==");
+ assert_eq!(
+ output.hash.to_hex(),
+ "637e6773e27cea06eb302e4296310cab30941b9bc52e294b8cd74781bd7ddde0"
+ );
+}
+
+#[test]
+fn test_ton_sign_wallet_v5r1_missing_required_send_mode() {
+ let private_key = "3570e35f54cfb843f2cfaf2b8cae7ceeb7b32225d7dbbd86f611056d74d9073e";
+
+ let transfer = Proto::Transfer {
+ dest: "EQBe6DtCpJZe8M4t-crMXe93JlEYgSl30S5OUuMSLOfeQfBu".into(),
+ amount: 10,
+ // Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS is required for wallet v5r1 external messages.
+ mode: Proto::SendMode::PAY_FEES_SEPARATELY as u32,
+ bounceable: true,
+ ..Proto::Transfer::default()
+ };
+
+ let input = Proto::SigningInput {
+ private_key: private_key.decode_hex().unwrap().into(),
+ messages: vec![transfer],
+ sequence_number: 1,
+ expire_at: 1723469663,
+ wallet_version: Proto::WalletVersion::WALLET_V5_R1,
+ ..Proto::SigningInput::default()
+ };
+
+ let mut signer = AnySignerHelper::::default();
+ let output = signer.sign(CoinType::TON, input);
+
+ assert_eq!(output.error, SigningError::Error_internal);
+}
diff --git a/rust/tw_any_coin/tests/chains/ton/ton_transaction_util.rs b/rust/tw_any_coin/tests/chains/ton/ton_transaction_util.rs
new file mode 100644
index 00000000000..78a6d727f60
--- /dev/null
+++ b/rust/tw_any_coin/tests/chains/ton/ton_transaction_util.rs
@@ -0,0 +1,13 @@
+use tw_any_coin::test_utils::transaction_calc_tx_hash_utils::TransactionUtilHelper;
+use tw_coin_registry::coin_type::CoinType;
+
+#[test]
+fn test_ton_transaction_util_calc_tx_hash() {
+ let encoded_tx = "te6cckECGwEAA2sAAkWIACm9HPyVOpjCNOG6nbf+EwCONRHHpeMQsIlCoWNKhUaaHgECAgE0AwQBoXNpZ25///8R/////wAAAACACOfqY7L3l3aKc58eNxuJTaeH/fgBw2aG0coM+hjDpjWhJKbYKmsAD8v054HYSuO6vN3bQnV5U19BhsGfe1MDoAUBFP8A9KQT9LzyyAsGAFGAAAAAP///iKrcG+d35KaMMtuxik4jqNofFL51Mu1f8Qf19onqsDlyIAIKDsPIbQMWBwIBIAgJAWJiAC90HaFSSy94Zxb85WYu97uTKIxAlLvolycpcYkWc+8giFAAAAAAAAAAAAAAAAABFgIBSAoLAQLyDALc0CDXScEgkVuPYyDXCx8gghBleHRuvSGCEHNpbnS9sJJfA+CCEGV4dG66jrSAINchAdB01yH6QDD6RPgo+kQwWL2RW+DtRNCBAUHXIfQFgwf0Dm+hMZEw4YBA1yFwf9s84DEg10mBAoC5kTDgcOIXDQIBIA4PAR4g1wsfghBzaWduuvLgin8NAeaO8O2i7fshgwjXIgKDCNcjIIAg1yHTH9Mf0x/tRNDSANMfINMf0//XCgAK+QFAzPkQmiiUXwrbMeHywIffArNQB7Dy0IRRJbry4IVQNrry4Ib4I7vy0IgikvgA3gGkf8jKAMsfAc8Wye1UIJL4D95w2zzYFwIBIBARABm+Xw9qJoQICg65D6AsAgFuEhMCAUgUFQAZrc52omhAIOuQ64X/wAAZrx32omhAEOuQ64WPwAAXsyX7UTQcdch1wsfgABGyYvtRNDXCgCAAAAP27aLt+wL0BCFukmwhjkwCIdc5MHCUIccAs44tAdcoIHYeQ2wg10nACPLgkyDXSsAC8uCTINcdBscSwgBSMLDy0InXTNc5MAGk6GwShAe78uCT10rAAPLgk+1V4tIAAcAAkVvg69csCBQgkXCWAdcsCBwS4lIQseMPINdKGBkaAJYB+kAB+kT4KPpEMFi68uCR7UTQgQFB1xj0BQSdf8jKAEAEgwf0U/Lgi44UA4MH9Fvy4Iwi1woAIW4Bs7Dy0JDiyFADzxYS9ADJ7VQAcjDXLAgkji0h8uCS0gDtRNDSAFETuvLQj1RQMJExnAGBAUDXIdcKAPLgjuLIygBYzxbJ7VST8sCN4gAQk1vbMeHXTNB8Ui06";
+ let tx_hash = TransactionUtilHelper::calc_tx_hash(CoinType::TON, encoded_tx);
+
+ assert_eq!(
+ tx_hash,
+ "437dae441a95a6bccdcdcea2560c313de24f13dd85c76d5d7ecaab1e70a1e52b"
+ );
+}
diff --git a/rust/tw_bitcoin/src/entry.rs b/rust/tw_bitcoin/src/entry.rs
index 850bf638e58..5d6b81508ec 100644
--- a/rust/tw_bitcoin/src/entry.rs
+++ b/rust/tw_bitcoin/src/entry.rs
@@ -1,6 +1,7 @@
use crate::modules::compiler::BitcoinCompiler;
use crate::modules::planner::BitcoinPlanner;
use crate::modules::signer::BitcoinSigner;
+use crate::modules::transaction_util::BitcoinTransactionUtil;
use std::str::FromStr;
use tw_coin_entry::coin_context::CoinContext;
use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes};
@@ -29,6 +30,7 @@ impl CoinEntry for BitcoinEntry {
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
+ type TransactionUtil = BitcoinTransactionUtil;
#[inline]
fn parse_address(
@@ -89,4 +91,9 @@ impl CoinEntry for BitcoinEntry {
fn plan_builder(&self) -> Option {
Some(BitcoinPlanner)
}
+
+ #[inline]
+ fn transaction_util(&self) -> Option {
+ Some(BitcoinTransactionUtil)
+ }
}
diff --git a/rust/tw_bitcoin/src/modules/mod.rs b/rust/tw_bitcoin/src/modules/mod.rs
index 29d57ad224e..d7ac86f787f 100644
--- a/rust/tw_bitcoin/src/modules/mod.rs
+++ b/rust/tw_bitcoin/src/modules/mod.rs
@@ -7,4 +7,5 @@ pub mod planner;
pub mod protobuf_builder;
pub mod signer;
pub mod signing_request;
+pub mod transaction_util;
pub mod tx_builder;
diff --git a/rust/tw_bitcoin/src/modules/transaction_util.rs b/rust/tw_bitcoin/src/modules/transaction_util.rs
new file mode 100644
index 00000000000..19862095334
--- /dev/null
+++ b/rust/tw_bitcoin/src/modules/transaction_util.rs
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+use bitcoin::consensus::deserialize;
+use bitcoin::Transaction;
+use tw_coin_entry::coin_context::CoinContext;
+use tw_coin_entry::error::prelude::*;
+use tw_coin_entry::modules::transaction_util::TransactionUtil;
+use tw_encoding::hex::decode;
+
+pub struct BitcoinTransactionUtil;
+
+impl TransactionUtil for BitcoinTransactionUtil {
+ fn calc_tx_hash(&self, coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult {
+ Self::calc_tx_hash_impl(coin, encoded_tx)
+ }
+}
+
+impl BitcoinTransactionUtil {
+ fn calc_tx_hash_impl(_coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult {
+ let tx = decode(encoded_tx).map_err(|_| SigningErrorType::Error_input_parse)?;
+
+ // Deserialize the transaction
+ let tx: Transaction = deserialize(&tx).map_err(|_| SigningErrorType::Error_input_parse)?;
+
+ // Calculate the transaction ID
+ let txid = tx.txid();
+
+ // Note: to_string() returns the reversed byte order, which is the RPC format
+ Ok(txid.to_string())
+ }
+}
diff --git a/rust/tw_coin_entry/src/coin_entry.rs b/rust/tw_coin_entry/src/coin_entry.rs
index e9609515d68..85d9cd89f05 100644
--- a/rust/tw_coin_entry/src/coin_entry.rs
+++ b/rust/tw_coin_entry/src/coin_entry.rs
@@ -15,6 +15,7 @@ use tw_proto::{MessageRead, MessageWrite};
use crate::modules::message_signer::MessageSigner;
use crate::modules::transaction_decoder::TransactionDecoder;
+use crate::modules::transaction_util::TransactionUtil;
use crate::modules::wallet_connector::WalletConnector;
pub use tw_proto::{ProtoError, ProtoResult};
@@ -69,6 +70,8 @@ pub trait CoinEntry {
/// **Optional**. Use `NoTransactionDecoder` if the blockchain does not support transaction decoding yet.
type TransactionDecoder: TransactionDecoder;
+ type TransactionUtil: TransactionUtil;
+
/// Tries to parse `Self::Address` from the given `address` string by `coin` type and address `prefix`.
fn parse_address(
&self,
@@ -147,4 +150,11 @@ pub trait CoinEntry {
fn transaction_decoder(&self) -> Option {
None
}
+
+ /// It is optional, Transaction util, for example, calculating the TX hash of a transaction.
+ /// Returns `Ok(None)` if the blockchain does not support transaction util yet.
+ #[inline]
+ fn transaction_util(&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 a07546ae190..6983bb99848 100644
--- a/rust/tw_coin_entry/src/coin_entry_ext.rs
+++ b/rust/tw_coin_entry/src/coin_entry_ext.rs
@@ -10,6 +10,7 @@ use crate::modules::json_signer::JsonSigner;
use crate::modules::message_signer::MessageSigner;
use crate::modules::plan_builder::PlanBuilder;
use crate::modules::transaction_decoder::TransactionDecoder;
+use crate::modules::transaction_util::TransactionUtil;
use crate::modules::wallet_connector::WalletConnector;
use crate::prefix::AddressPrefix;
use tw_keypair::tw::{PrivateKey, PublicKey};
@@ -94,6 +95,9 @@ pub trait CoinEntryExt {
/// Decodes a transaction from binary representation.
fn decode_transaction(&self, coin: &dyn CoinContext, tx: &[u8]) -> SigningResult;
+
+ /// Calculate the TX hash of a transaction.
+ fn calc_tx_hash(&self, coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult;
}
impl CoinEntryExt for T
@@ -242,4 +246,12 @@ where
let output = tx_decoder.decode_transaction(coin, tx);
serialize(&output).map_err(SigningError::from)
}
+
+ fn calc_tx_hash(&self, coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult {
+ let Some(tx_util) = self.transaction_util() else {
+ return TWError::err(SigningErrorType::Error_not_supported);
+ };
+
+ tx_util.calc_tx_hash(coin, encoded_tx)
+ }
}
diff --git a/rust/tw_coin_entry/src/modules/mod.rs b/rust/tw_coin_entry/src/modules/mod.rs
index bf9d37ccd6e..89a93030d4b 100644
--- a/rust/tw_coin_entry/src/modules/mod.rs
+++ b/rust/tw_coin_entry/src/modules/mod.rs
@@ -8,4 +8,5 @@ pub mod json_signer;
pub mod message_signer;
pub mod plan_builder;
pub mod transaction_decoder;
+pub mod transaction_util;
pub mod wallet_connector;
diff --git a/rust/tw_coin_entry/src/modules/transaction_util.rs b/rust/tw_coin_entry/src/modules/transaction_util.rs
new file mode 100644
index 00000000000..122216f5137
--- /dev/null
+++ b/rust/tw_coin_entry/src/modules/transaction_util.rs
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+use crate::coin_context::CoinContext;
+use crate::error::prelude::*;
+
+pub trait TransactionUtil {
+ /// Calculate the TX hash of a transaction.
+ fn calc_tx_hash(&self, coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult;
+}
+
+/// `NoTransactionUtil` can't be created since there are no enum variants.
+pub enum NoTransactionUtil {}
+
+impl TransactionUtil for NoTransactionUtil {
+ fn calc_tx_hash(&self, _coin: &dyn CoinContext, _encoded_tx: &str) -> SigningResult {
+ panic!("`NoTransactionUtil` should never be constructed and used")
+ }
+}
diff --git a/rust/tw_cosmos_sdk/src/modules/mod.rs b/rust/tw_cosmos_sdk/src/modules/mod.rs
index e49153b3c92..78d55f5dedd 100644
--- a/rust/tw_cosmos_sdk/src/modules/mod.rs
+++ b/rust/tw_cosmos_sdk/src/modules/mod.rs
@@ -6,4 +6,5 @@ pub mod broadcast_msg;
pub mod compiler;
pub mod serializer;
pub mod signer;
+pub mod transaction_util;
pub mod tx_builder;
diff --git a/rust/tw_cosmos_sdk/src/modules/transaction_util.rs b/rust/tw_cosmos_sdk/src/modules/transaction_util.rs
new file mode 100644
index 00000000000..44ed31dcf34
--- /dev/null
+++ b/rust/tw_cosmos_sdk/src/modules/transaction_util.rs
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+use crate::context::CosmosContext;
+use std::marker::PhantomData;
+use tw_coin_entry::coin_context::CoinContext;
+use tw_coin_entry::error::prelude::*;
+use tw_coin_entry::modules::transaction_util::TransactionUtil;
+use tw_encoding::base64;
+use tw_encoding::base64::STANDARD;
+use tw_encoding::hex::encode;
+
+pub struct CosmosTransactionUtil {
+ _phantom: PhantomData,
+}
+
+impl Default for CosmosTransactionUtil {
+ fn default() -> Self {
+ Self {
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl TransactionUtil for CosmosTransactionUtil {
+ fn calc_tx_hash(&self, coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult {
+ Self::calc_tx_hash_impl(coin, encoded_tx)
+ }
+}
+
+impl CosmosTransactionUtil {
+ fn calc_tx_hash_impl(_coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult {
+ let tx = base64::decode(encoded_tx, STANDARD)?;
+
+ let tx_hash = Context::default_tx_hasher().hash(&tx);
+
+ Ok(encode(&tx_hash, false).to_uppercase())
+ }
+}
diff --git a/rust/tw_evm/src/modules/mod.rs b/rust/tw_evm/src/modules/mod.rs
index 526e4bf78a1..fb246766ac1 100644
--- a/rust/tw_evm/src/modules/mod.rs
+++ b/rust/tw_evm/src/modules/mod.rs
@@ -7,4 +7,5 @@ pub mod compiler;
pub mod message_signer;
pub mod rlp_encoder;
pub mod signer;
+pub mod transaction_util;
pub mod tx_builder;
diff --git a/rust/tw_evm/src/modules/transaction_util.rs b/rust/tw_evm/src/modules/transaction_util.rs
new file mode 100644
index 00000000000..632634653d8
--- /dev/null
+++ b/rust/tw_evm/src/modules/transaction_util.rs
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+use tw_coin_entry::coin_context::CoinContext;
+use tw_coin_entry::error::prelude::*;
+use tw_coin_entry::modules::transaction_util::TransactionUtil;
+use tw_encoding::hex::{decode, encode};
+use tw_hash::sha3::keccak256;
+
+pub struct EvmTransactionUtil;
+
+impl TransactionUtil for EvmTransactionUtil {
+ fn calc_tx_hash(&self, coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult {
+ Self::calc_tx_hash_impl(coin, encoded_tx)
+ }
+}
+
+impl EvmTransactionUtil {
+ fn calc_tx_hash_impl(_coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult {
+ let tx = decode(encoded_tx).map_err(|_| SigningErrorType::Error_input_parse)?;
+
+ Ok(encode(keccak256(&tx), true))
+ }
+}
diff --git a/rust/wallet_core_rs/src/ffi/wallet/ton_wallet.rs b/rust/wallet_core_rs/src/ffi/wallet/ton_wallet.rs
index 7e785dca41d..5b467a15dbc 100644
--- a/rust/wallet_core_rs/src/ffi/wallet/ton_wallet.rs
+++ b/rust/wallet_core_rs/src/ffi/wallet/ton_wallet.rs
@@ -107,3 +107,25 @@ pub unsafe extern "C" fn tw_ton_wallet_build_v4_r2_state_init(
);
TWString::from(state_init).into_ptr()
}
+
+// Constructs a TON Wallet V5R1 stateInit encoded as BoC (BagOfCells) for the given `public_key`.
+///
+/// \param public_key wallet's public key.
+/// \param workchain TON workchain to which the wallet belongs. Usually, base chain is used (0).
+/// \param wallet_id wallet's ID allows to create multiple wallets for the same private key.
+/// \return Pointer to a base64 encoded Bag Of Cells (BoC) StateInit. Null if invalid public key provided.
+#[no_mangle]
+pub unsafe extern "C" fn tw_ton_wallet_build_v5_r1_state_init(
+ public_key: *const TWPublicKey,
+ workchain: i32,
+ wallet_id: i32,
+) -> *mut TWString {
+ let public_key = try_or_else!(TWPublicKey::from_ptr_as_ref(public_key), std::ptr::null_mut);
+ let ed_pubkey = try_or_else!(public_key.as_ref().to_ed25519(), std::ptr::null_mut).clone();
+
+ let state_init = try_or_else!(
+ WalletProvider::v5r1_state_init(ed_pubkey, workchain, wallet_id),
+ std::ptr::null_mut
+ );
+ TWString::from(state_init).into_ptr()
+}
diff --git a/rust/wallet_core_rs/tests/wallet/ton_wallet.rs b/rust/wallet_core_rs/tests/wallet/ton_wallet.rs
index d2f583c02bb..a2187e038ed 100644
--- a/rust/wallet_core_rs/tests/wallet/ton_wallet.rs
+++ b/rust/wallet_core_rs/tests/wallet/ton_wallet.rs
@@ -8,9 +8,11 @@ use tw_keypair::test_utils::tw_public_key_helper::TWPublicKeyHelper;
use tw_keypair::tw::PublicKeyType;
use tw_memory::test_utils::tw_string_helper::TWStringHelper;
use tw_memory::test_utils::tw_wrapper::TWWrapper;
+use tw_ton::resources::WALLET_ID_V5R1_TON_MAINNET;
use wallet_core_rs::ffi::wallet::ton_wallet::{
- tw_ton_wallet_build_v4_r2_state_init, tw_ton_wallet_create_with_mnemonic, tw_ton_wallet_delete,
- tw_ton_wallet_get_key, tw_ton_wallet_is_valid_mnemonic,
+ tw_ton_wallet_build_v4_r2_state_init, tw_ton_wallet_build_v5_r1_state_init,
+ tw_ton_wallet_create_with_mnemonic, tw_ton_wallet_delete, tw_ton_wallet_get_key,
+ tw_ton_wallet_is_valid_mnemonic,
};
#[test]
@@ -39,7 +41,7 @@ fn test_ton_wallet_get_key() {
}
#[test]
-fn test_ton_wallet_create_state_init() {
+fn test_ton_wallet_v4_r2_create_state_init() {
let public_key = TWPublicKeyHelper::with_hex(
"f229a9371fa7c2108b3d90ea22c9be705ff5d0cfeaee9cbb9366ff0171579357",
PublicKeyType::Ed25519,
@@ -50,3 +52,16 @@ fn test_ton_wallet_create_state_init() {
});
assert_eq!(state_init_boc.to_string().unwrap(), "te6cckECFgEAAwQAAgE0AQIBFP8A9KQT9LzyyAsDAFEAAAAAKamjF/IpqTcfp8IQiz2Q6iLJvnBf9dDP6u6cu5Nm/wFxV5NXQAIBIAQFAgFIBgcE+PKDCNcYINMf0x/THwL4I7vyZO1E0NMf0x/T//QE0VFDuvKhUVG68qIF+QFUEGT5EPKj+AAkpMjLH1JAyx9SMMv/UhD0AMntVPgPAdMHIcAAn2xRkyDXSpbTB9QC+wDoMOAhwAHjACHAAuMAAcADkTDjDQOkyMsfEssfy/8ICQoLAubQAdDTAyFxsJJfBOAi10nBIJJfBOAC0x8hghBwbHVnvSKCEGRzdHK9sJJfBeAD+kAwIPpEAcjKB8v/ydDtRNCBAUDXIfQEMFyBAQj0Cm+hMbOSXwfgBdM/yCWCEHBsdWe6kjgw4w0DghBkc3RyupJfBuMNDA0CASAODwBu0gf6ANTUIvkABcjKBxXL/8nQd3SAGMjLBcsCIs8WUAX6AhTLaxLMzMlz+wDIQBSBAQj0UfKnAgBwgQEI1xj6ANM/yFQgR4EBCPRR8qeCEG5vdGVwdIAYyMsFywJQBs8WUAT6AhTLahLLH8s/yXP7AAIAbIEBCNcY+gDTPzBSJIEBCPRZ8qeCEGRzdHJwdIAYyMsFywJQBc8WUAP6AhPLassfEss/yXP7AAAK9ADJ7VQAeAH6APQEMPgnbyIwUAqhIb7y4FCCEHBsdWeDHrFwgBhQBMsFJs8WWPoCGfQAy2kXyx9SYMs/IMmAQPsABgCKUASBAQj0WTDtRNCBAUDXIMgBzxb0AMntVAFysI4jghBkc3Rygx6xcIAYUAXLBVADzxYj+gITy2rLH8s/yYBA+wCSXwPiAgEgEBEAWb0kK29qJoQICga5D6AhhHDUCAhHpJN9KZEM5pA+n/mDeBKAG3gQFImHFZ8xhAIBWBITABG4yX7UTQ1wsfgAPbKd+1E0IEBQNch9AQwAsjKB8v/ydABgQEI9ApvoTGACASAUFQAZrc52omhAIGuQ64X/wAAZrx32omhAEGuQ64WPwEXtMkg=");
}
+
+#[test]
+fn test_ton_wallet_v5_r1_create_state_init() {
+ let public_key = TWPublicKeyHelper::with_hex(
+ "f229a9371fa7c2108b3d90ea22c9be705ff5d0cfeaee9cbb9366ff0171579357",
+ PublicKeyType::Ed25519,
+ );
+ assert!(!public_key.is_null());
+ let state_init_boc = TWStringHelper::wrap(unsafe {
+ tw_ton_wallet_build_v5_r1_state_init(public_key.ptr(), 0, WALLET_ID_V5R1_TON_MAINNET)
+ });
+ assert_eq!(state_init_boc.to_string().unwrap(), "te6cckECFgEAArEAAgE0AQIBFP8A9KQT9LzyyAsDAFGAAAAAP///iPkU1JuP0+EIRZ7IdRFk3zgv+uhn9XdOXcmzf4C4q8mroAIBIAQFAgFIBgcBAvIIAtzQINdJwSCRW49jINcLHyCCEGV4dG69IYIQc2ludL2wkl8D4IIQZXh0brqOtIAg1yEB0HTXIfpAMPpE+Cj6RDBYvZFb4O1E0IEBQdch9AWDB/QOb6ExkTDhgEDXIXB/2zzgMSDXSYECgLmRMOBw4hIJAgEgCgsBHiDXCx+CEHNpZ2668uCKfwkB5o7w7aLt+yGDCNciAoMI1yMggCDXIdMf0x/TH+1E0NIA0x8g0x/T/9cKAAr5AUDM+RCaKJRfCtsx4fLAh98Cs1AHsPLQhFEluvLghVA2uvLghvgju/LQiCKS+ADeAaR/yMoAyx8BzxbJ7VQgkvgP3nDbPNgSAgEgDA0AGb5fD2omhAgKDrkPoCwCAW4ODwIBSBARABmtznaiaEAg65Drhf/AABmvHfaiaEAQ65DrhY/AABezJftRNBx1yHXCx+AAEbJi+1E0NcKAIAP27aLt+wL0BCFukmwhjkwCIdc5MHCUIccAs44tAdcoIHYeQ2wg10nACPLgkyDXSsAC8uCTINcdBscSwgBSMLDy0InXTNc5MAGk6GwShAe78uCT10rAAPLgk+1V4tIAAcAAkVvg69csCBQgkXCWAdcsCBwS4lIQseMPINdKExQVAJYB+kAB+kT4KPpEMFi68uCR7UTQgQFB1xj0BQSdf8jKAEAEgwf0U/Lgi44UA4MH9Fvy4Iwi1woAIW4Bs7Dy0JDiyFADzxYS9ADJ7VQAcjDXLAgkji0h8uCS0gDtRNDSAFETuvLQj1RQMJExnAGBAUDXIdcKAPLgjuLIygBYzxbJ7VST8sCN4gAQk1vbMeHXTNBAlw92");
+}
diff --git a/src/interface/TWTranscationUtil.cpp b/src/interface/TWTranscationUtil.cpp
new file mode 100644
index 00000000000..3f99dd81a83
--- /dev/null
+++ b/src/interface/TWTranscationUtil.cpp
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+#include "TrustWalletCore/TWTransactionUtil.h"
+#include "rust/Wrapper.h"
+
+using namespace TW;
+
+TWString* _Nullable TWTransactionUtilCalcTxHash(enum TWCoinType coinType, TWString* _Nonnull encodedTx) {
+ try {
+ if (encodedTx == nullptr) {
+ return nullptr;
+ }
+ const Rust::TWStringWrapper encodedTxWrapper = TWStringUTF8Bytes(encodedTx);
+
+ const Rust::TWStringWrapper outputDataPtr = Rust::tw_transaction_util_calc_tx_hash(static_cast(coinType), encodedTxWrapper.get());
+ if (!outputDataPtr) {
+ return nullptr;
+ }
+
+ return TWStringCreateWithUTF8Bytes(outputDataPtr.c_str());
+ } catch (...) {
+ return nullptr;
+ }
+}
diff --git a/src/proto/LiquidStaking.proto b/src/proto/LiquidStaking.proto
index 2193c5d283e..b91c1bdf3b4 100644
--- a/src/proto/LiquidStaking.proto
+++ b/src/proto/LiquidStaking.proto
@@ -13,7 +13,8 @@ import "Aptos.proto";
// Enum for supported coins for liquid staking
enum Coin {
- MATIC = 0;
+ // Previously, MATIC.
+ POL = 0;
ATOM = 1;
BNB = 2;
APT = 3;
diff --git a/src/proto/TheOpenNetwork.proto b/src/proto/TheOpenNetwork.proto
index cc2424b9a75..092ec3bcd9b 100644
--- a/src/proto/TheOpenNetwork.proto
+++ b/src/proto/TheOpenNetwork.proto
@@ -13,6 +13,7 @@ enum WalletVersion {
WALLET_V3_R1 = 0;
WALLET_V3_R2 = 1;
WALLET_V4_R2 = 2;
+ WALLET_V5_R1 = 3;
};
enum SendMode {
@@ -25,32 +26,29 @@ enum SendMode {
};
message Transfer {
- // Wallet version
- WalletVersion wallet_version = 1;
-
// Recipient address
- string dest = 2;
+ string dest = 1;
// Amount to send in nanotons
- uint64 amount = 3;
+ uint64 amount = 2;
// Send mode (optional, 0 by default)
// Learn more: https://ton.org/docs/develop/func/stdlib#send_raw_message
- uint32 mode = 4;
+ uint32 mode = 3;
// Transfer comment message (optional, empty by default)
// Ignored if `custom_payload` is specified
- string comment = 5;
+ string comment = 4;
// If the address is bounceable
- bool bounceable = 6;
+ bool bounceable = 5;
// One of the Transfer message payloads (optional).
oneof payload {
// Jetton transfer payload.
- JettonTransfer jetton_transfer = 7;
+ JettonTransfer jetton_transfer = 6;
// TON transfer with custom stateInit and payload (contract call).
- CustomPayload custom_payload = 8;
+ CustomPayload custom_payload = 7;
}
}
@@ -97,6 +95,9 @@ message SigningInput {
// Expiration UNIX timestamp (optional, now() + 60 by default)
uint32 expire_at = 5;
+
+ // Wallet version
+ WalletVersion wallet_version = 6;
}
// Transaction signing output.
diff --git a/swift/Tests/Blockchains/TheOpenNetworkTests.swift b/swift/Tests/Blockchains/TheOpenNetworkTests.swift
index 716d5fd8b9c..f27e3a2fbd8 100644
--- a/swift/Tests/Blockchains/TheOpenNetworkTests.swift
+++ b/swift/Tests/Blockchains/TheOpenNetworkTests.swift
@@ -82,7 +82,6 @@ class TheOpenNetworkTests: XCTestCase {
let privateKeyData = Data(hexString: "c38f49de2fb13223a9e7d37d5d0ffbdd89a5eb7c8b0ee4d1c299f2cefe7dc4a0")!
let transfer = TheOpenNetworkTransfer.with {
- $0.walletVersion = TheOpenNetworkWalletVersion.walletV4R2
$0.dest = "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q"
$0.amount = 10
$0.mode = UInt32(TheOpenNetworkSendMode.payFeesSeparately.rawValue | TheOpenNetworkSendMode.ignoreActionPhaseErrors.rawValue)
@@ -94,6 +93,7 @@ class TheOpenNetworkTests: XCTestCase {
$0.privateKey = privateKeyData
$0.sequenceNumber = 6
$0.expireAt = 1671132440
+ $0.walletVersion = TheOpenNetworkWalletVersion.walletV4R2
}
let output: TheOpenNetworkSigningOutput = AnySigner.sign(input: input, coin: .ton)
@@ -115,7 +115,6 @@ class TheOpenNetworkTests: XCTestCase {
}
let transfer = TheOpenNetworkTransfer.with {
- $0.walletVersion = TheOpenNetworkWalletVersion.walletV4R2
$0.dest = "EQBiaD8PO1NwfbxSkwbcNT9rXDjqhiIvXWymNO-edV0H5lja"
$0.amount = 100 * 1000 * 1000
$0.mode = UInt32(TheOpenNetworkSendMode.payFeesSeparately.rawValue | TheOpenNetworkSendMode.ignoreActionPhaseErrors.rawValue)
@@ -129,6 +128,7 @@ class TheOpenNetworkTests: XCTestCase {
$0.privateKey = privateKeyData
$0.sequenceNumber = 1
$0.expireAt = 1787693046
+ $0.walletVersion = TheOpenNetworkWalletVersion.walletV4R2
}
let output: TheOpenNetworkSigningOutput = AnySigner.sign(input: input, coin: .ton)
@@ -159,7 +159,6 @@ class TheOpenNetworkTests: XCTestCase {
}
let transfer = TheOpenNetworkTransfer.with {
- $0.walletVersion = TheOpenNetworkWalletVersion.walletV4R2
$0.dest = dogeChatbotDeployingAddress
// 0.069 TON
$0.amount = 69_000_000
@@ -173,6 +172,7 @@ class TheOpenNetworkTests: XCTestCase {
$0.privateKey = privateKeyData
$0.sequenceNumber = 4
$0.expireAt = 1721939714
+ $0.walletVersion = TheOpenNetworkWalletVersion.walletV4R2
}
let output: TheOpenNetworkSigningOutput = AnySigner.sign(input: input, coin: .ton)
diff --git a/swift/Tests/LiquidStakingTests.swift b/swift/Tests/LiquidStakingTests.swift
index b5d721c40d6..009ad2759d1 100644
--- a/swift/Tests/LiquidStakingTests.swift
+++ b/swift/Tests/LiquidStakingTests.swift
@@ -15,7 +15,7 @@ class LiquidStakingTests: XCTestCase {
$0.stake = LiquidStakingStake.with {
$0.amount = "1000000000000000000"
$0.asset = LiquidStakingAsset.with {
- $0.stakingToken = .matic
+ $0.stakingToken = .pol
}
}
}
diff --git a/tests/chains/Polygon/TWCoinTypeTests.cpp b/tests/chains/Polygon/TWCoinTypeTests.cpp
index bf535061355..e9360551de0 100644
--- a/tests/chains/Polygon/TWCoinTypeTests.cpp
+++ b/tests/chains/Polygon/TWCoinTypeTests.cpp
@@ -24,7 +24,7 @@ TEST(TWPolygonCoinType, TWCoinType) {
ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypePolygon));
ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypePolygon));
ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypePolygon));
- assertStringsEqual(symbol, "MATIC");
+ assertStringsEqual(symbol, "POL");
assertStringsEqual(txUrl, "https://polygonscan.com/tx/0xe26ed1470d5bf99a53d687843e7acdf7e4ba6620af93b4d672e714de90476e8e");
assertStringsEqual(accUrl, "https://polygonscan.com/address/0x720E1fa107A1Df39Db4E78A3633121ac36Bec132");
assertStringsEqual(id, "polygon");
diff --git a/tests/chains/TheOpenNetwork/TWAnySignerTests.cpp b/tests/chains/TheOpenNetwork/TWAnySignerTests.cpp
index 467461bf1b8..ffd5d276a78 100644
--- a/tests/chains/TheOpenNetwork/TWAnySignerTests.cpp
+++ b/tests/chains/TheOpenNetwork/TWAnySignerTests.cpp
@@ -12,11 +12,10 @@
namespace TW::TheOpenNetwork::tests {
-TEST(TWAnySignerTheOpenNetwork, SingMessageToTransferAndDeployWallet) {
+TEST(TWAnySignerTheOpenNetwork, SignMessageToTransferAndDeployWalletV4R2) {
Proto::SigningInput input;
auto& transfer = *input.add_messages();
- transfer.set_wallet_version(Proto::WALLET_V4_R2);
transfer.set_dest("EQDYW_1eScJVxtitoBRksvoV9cCYo4uKGWLVNIHB1JqRR3n0");
transfer.set_amount(10);
transfer.set_mode(Proto::SendMode::PAY_FEES_SEPARATELY | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS);
@@ -27,6 +26,8 @@ TEST(TWAnySignerTheOpenNetwork, SingMessageToTransferAndDeployWallet) {
input.set_expire_at(1671135440);
+ input.set_wallet_version(Proto::WALLET_V4_R2);
+
Proto::SigningOutput output;
ANY_SIGN(input, TWCoinTypeTON);
@@ -36,5 +37,29 @@ TEST(TWAnySignerTheOpenNetwork, SingMessageToTransferAndDeployWallet) {
ASSERT_EQ(output.encoded(), "te6cckECGgEAA7IAAkWIAM33x4uAd+uQTyXyCZPxflESlNVHpCeoOECtNsqVW9tmHgECAgE0AwQBnOfG8YGGhFeE+iDE1jxCYeWKElbGDm3oqm2pwAhmVWSzWv5n6vVq8JY0J6p4sL+hqJU3iYPH8TX5mGLfcbbmtwgpqaMX/////wAAAAAAAwUBFP8A9KQT9LzyyAsGAFEAAAAAKamjF/Qsd/kxvqIOxdAVBzEna7suKGCUdmEkWyMZ74Ez7o1BQAFiYgBsLf6vJOEq42xW0AoyWX0K+uBMUcXFDLFqmkDg6k1Io4hQAAAAAAAAAAAAAAAAAQcCASAICQAAAgFICgsE+PKDCNcYINMf0x/THwL4I7vyZO1E0NMf0x/T//QE0VFDuvKhUVG68qIF+QFUEGT5EPKj+AAkpMjLH1JAyx9SMMv/UhD0AMntVPgPAdMHIcAAn2xRkyDXSpbTB9QC+wDoMOAhwAHjACHAAuMAAcADkTDjDQOkyMsfEssfy/8MDQ4PAubQAdDTAyFxsJJfBOAi10nBIJJfBOAC0x8hghBwbHVnvSKCEGRzdHK9sJJfBeAD+kAwIPpEAcjKB8v/ydDtRNCBAUDXIfQEMFyBAQj0Cm+hMbOSXwfgBdM/yCWCEHBsdWe6kjgw4w0DghBkc3RyupJfBuMNEBECASASEwBu0gf6ANTUIvkABcjKBxXL/8nQd3SAGMjLBcsCIs8WUAX6AhTLaxLMzMlz+wDIQBSBAQj0UfKnAgBwgQEI1xj6ANM/yFQgR4EBCPRR8qeCEG5vdGVwdIAYyMsFywJQBs8WUAT6AhTLahLLH8s/yXP7AAIAbIEBCNcY+gDTPzBSJIEBCPRZ8qeCEGRzdHJwdIAYyMsFywJQBc8WUAP6AhPLassfEss/yXP7AAAK9ADJ7VQAeAH6APQEMPgnbyIwUAqhIb7y4FCCEHBsdWeDHrFwgBhQBMsFJs8WWPoCGfQAy2kXyx9SYMs/IMmAQPsABgCKUASBAQj0WTDtRNCBAUDXIMgBzxb0AMntVAFysI4jghBkc3Rygx6xcIAYUAXLBVADzxYj+gITy2rLH8s/yYBA+wCSXwPiAgEgFBUAWb0kK29qJoQICga5D6AhhHDUCAhHpJN9KZEM5pA+n/mDeBKAG3gQFImHFZ8xhAIBWBYXABG4yX7UTQ1wsfgAPbKd+1E0IEBQNch9AQwAsjKB8v/ydABgQEI9ApvoTGACASAYGQAZrc52omhAIGuQ64X/wAAZrx32omhAEGuQ64WPwJiaP4Q=");
}
+TEST(TWAnySignerTheOpenNetwork, SignMessageToTransferAndDeployWalletV5R1) {
+ Proto::SigningInput input;
+
+ auto& transfer = *input.add_messages();
+ transfer.set_dest("EQBe6DtCpJZe8M4t-crMXe93JlEYgSl30S5OUuMSLOfeQfBu");
+ transfer.set_amount(10);
+ transfer.set_mode(Proto::SendMode::PAY_FEES_SEPARATELY | Proto::SendMode::IGNORE_ACTION_PHASE_ERRORS);
+ transfer.set_bounceable(true);
+
+ const auto privateKey = parse_hex("3570e35f54cfb843f2cfaf2b8cae7ceeb7b32225d7dbbd86f611056d74d9073e");
+ input.set_private_key(privateKey.data(), privateKey.size());
+
+ input.set_expire_at(0xffffffff);
+
+ input.set_wallet_version(Proto::WALLET_V5_R1);
+
+ Proto::SigningOutput output;
+ ANY_SIGN(input, TWCoinTypeTON);
+
+ // Successfully broadcasted: https://tonviewer.com/transaction/Q32uRBqVprzNzc6iVgwxPeJPE92Fx21dfsqrHnCh5Ss=
+ ASSERT_EQ(hex(output.hash()), "437dae441a95a6bccdcdcea2560c313de24f13dd85c76d5d7ecaab1e70a1e52b");
+ ASSERT_EQ(output.encoded(), "te6cckECGwEAA2sAAkWIACm9HPyVOpjCNOG6nbf+EwCONRHHpeMQsIlCoWNKhUaaHgECAgE0AwQBoXNpZ25///8R/////wAAAACACOfqY7L3l3aKc58eNxuJTaeH/fgBw2aG0coM+hjDpjWhJKbYKmsAD8v054HYSuO6vN3bQnV5U19BhsGfe1MDoAUBFP8A9KQT9LzyyAsGAFGAAAAAP///iKrcG+d35KaMMtuxik4jqNofFL51Mu1f8Qf19onqsDlyIAIKDsPIbQMWBwIBIAgJAWJiAC90HaFSSy94Zxb85WYu97uTKIxAlLvolycpcYkWc+8giFAAAAAAAAAAAAAAAAABFgIBSAoLAQLyDALc0CDXScEgkVuPYyDXCx8gghBleHRuvSGCEHNpbnS9sJJfA+CCEGV4dG66jrSAINchAdB01yH6QDD6RPgo+kQwWL2RW+DtRNCBAUHXIfQFgwf0Dm+hMZEw4YBA1yFwf9s84DEg10mBAoC5kTDgcOIXDQIBIA4PAR4g1wsfghBzaWduuvLgin8NAeaO8O2i7fshgwjXIgKDCNcjIIAg1yHTH9Mf0x/tRNDSANMfINMf0//XCgAK+QFAzPkQmiiUXwrbMeHywIffArNQB7Dy0IRRJbry4IVQNrry4Ib4I7vy0IgikvgA3gGkf8jKAMsfAc8Wye1UIJL4D95w2zzYFwIBIBARABm+Xw9qJoQICg65D6AsAgFuEhMCAUgUFQAZrc52omhAIOuQ64X/wAAZrx32omhAEOuQ64WPwAAXsyX7UTQcdch1wsfgABGyYvtRNDXCgCAAAAP27aLt+wL0BCFukmwhjkwCIdc5MHCUIccAs44tAdcoIHYeQ2wg10nACPLgkyDXSsAC8uCTINcdBscSwgBSMLDy0InXTNc5MAGk6GwShAe78uCT10rAAPLgk+1V4tIAAcAAkVvg69csCBQgkXCWAdcsCBwS4lIQseMPINdKGBkaAJYB+kAB+kT4KPpEMFi68uCR7UTQgQFB1xj0BQSdf8jKAEAEgwf0U/Lgi44UA4MH9Fvy4Iwi1woAIW4Bs7Dy0JDiyFADzxYS9ADJ7VQAcjDXLAgkji0h8uCS0gDtRNDSAFETuvLQj1RQMJExnAGBAUDXIdcKAPLgjuLIygBYzxbJ7VST8sCN4gAQk1vbMeHXTNB8Ui06");
+}
+
} // namespace TW::TheOpenNetwork::tests
diff --git a/tests/common/LiquidStaking/LiquidStakingTests.cpp b/tests/common/LiquidStaking/LiquidStakingTests.cpp
index 46f00178f57..ac5bad4c7b2 100644
--- a/tests/common/LiquidStaking/LiquidStakingTests.cpp
+++ b/tests/common/LiquidStaking/LiquidStakingTests.cpp
@@ -56,7 +56,7 @@ namespace TW::LiquidStaking::tests {
input.set_protocol(Proto::Strader);
Proto::Stake stake;
Proto::Asset asset;
- asset.set_staking_token(Proto::MATIC);
+ asset.set_staking_token(Proto::POL);
*stake.mutable_asset() = asset;
stake.set_amount("1000000000000000000");
*input.mutable_stake() = stake;
@@ -80,14 +80,14 @@ namespace TW::LiquidStaking::tests {
ASSERT_EQ(ls_output.status().code(), Proto::ERROR_TARGETED_BLOCKCHAIN_NOT_SUPPORTED_BY_PROTOCOL);
}
- TEST(LiquidStaking, PolygonStraderStakeMatic) {
+ TEST(LiquidStaking, PolygonStraderStakePol) {
Proto::Input input;
input.set_blockchain(Proto::POLYGON);
input.set_protocol(Proto::Strader);
input.set_smart_contract_address("0xfd225c9e6601c9d38d8f98d8731bf59efcf8c0e3");
Proto::Stake stake;
Proto::Asset asset;
- asset.set_staking_token(Proto::MATIC);
+ asset.set_staking_token(Proto::POL);
*stake.mutable_asset() = asset;
stake.set_amount("1000000000000000000");
*input.mutable_stake() = stake;
@@ -146,7 +146,7 @@ namespace TW::LiquidStaking::tests {
}
}
- TEST(LiquidStaking, PolygonStraderUnStakeMatic) {
+ TEST(LiquidStaking, PolygonStraderUnStakePol) {
Proto::Input input;
input.set_blockchain(Proto::POLYGON);
input.set_protocol(Proto::Strader);
@@ -184,7 +184,7 @@ namespace TW::LiquidStaking::tests {
// Successfully broadcasted https://polygonscan.com/tx/0xa66855e4af8e654e458915f59acd77e88706c01b59a3e4aed1363a665458368a
}
- TEST(LiquidStaking, PolygonStraderWithdrawMatic) {
+ TEST(LiquidStaking, PolygonStraderWithdrawPol) {
Proto::Input input;
input.set_blockchain(Proto::POLYGON);
input.set_protocol(Proto::Strader);
diff --git a/tests/interface/TWTransactionUtilTests.cpp b/tests/interface/TWTransactionUtilTests.cpp
new file mode 100644
index 00000000000..c6b59420e6b
--- /dev/null
+++ b/tests/interface/TWTransactionUtilTests.cpp
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Copyright © 2017 Trust Wallet.
+
+#include
+#include
+#include
+
+#include "Bitcoin/Script.h"
+#include "uint256.h"
+
+#include "TestUtilities.h"
+#include
+
+#include
+
+using namespace TW;
+
+TEST(TWTransactionUtil, CalcTxHashBitcoin) {
+ constexpr auto coin = TWCoinTypeBitcoin;
+ const auto encodedTxPtr = WRAPS(TWStringCreateWithUTF8Bytes("02000000000101089098890d2653567b9e8df2d1fbe5c3c8bf1910ca7184e301db0ad3b495c88e0100000000ffffffff02581b000000000000225120e8b706a97732e705e22ae7710703e7f589ed13c636324461afa443016134cc051040000000000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d02483045022100a44aa28446a9a886b378a4a65e32ad9a3108870bd725dc6105160bed4f317097022069e9de36422e4ce2e42b39884aa5f626f8f94194d1013007d5a1ea9220a06dce0121030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000"));
+ const auto txHashResult = WRAPS(TWTransactionUtilCalcTxHash(coin, encodedTxPtr.get()));
+
+ assertStringsEqual(txHashResult, "797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1");
+}
+
+TEST(TWTransactionUtil, CalcTxHashEthereum) {
+ constexpr auto coin = TWCoinTypeEthereum;
+ const auto encodedTxPtr = WRAPS(TWStringCreateWithUTF8Bytes("f8aa808509c7652400830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec8000025a0724c62ad4fbf47346b02de06e603e013f26f26b56fdc0be7ba3d6273401d98cea0032131cae15da7ddcda66963e8bef51ca0d9962bfef0547d3f02597a4a58c931"));
+ const auto txHashResult = WRAPS(TWTransactionUtilCalcTxHash(coin, encodedTxPtr.get()));
+
+ assertStringsEqual(txHashResult, "0x199a7829fc5149e49b452c2cab76d8fa5a9682fee6e4891b8acb697ac142513e");
+}
+
+TEST(TWTransactionUtil, CalcTxHashSolana) {
+ constexpr auto coin = TWCoinTypeSolana;
+ const auto encodedTxPtr = WRAPS(TWStringCreateWithUTF8Bytes("AnQTYwZpkm3fs4SdLxnV6gQj3hSLsyacpxDdLMALYWObm722f79IfYFTbZeFK9xHtMumiDOWAM2hHQP4r/GtbARpncaXgOVFv7OgbRLMbuCEJHO1qwcdCbtH72VzyzU8yw9sqqHIAaCUE8xaQTgT6Z5IyZfeyMe2QGJIfOjz65UPAgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8Aqe6sdLXiXSDILEtzckCjkjchiSf6zVGpMYiAE5BE2IqHAQUEAgQDAQoMoA8AAAAAAAAG"));
+ const auto txHashResult = WRAPS(TWTransactionUtilCalcTxHash(coin, encodedTxPtr.get()));
+
+ assertStringsEqual(txHashResult, "3KbvREZUat76wgWMtnJfWbJL74Vzh4U2eabVJa3Z3bb2fPtW8AREP5pbmRwUrxZCESbTomWpL41PeKDcPGbojsej");
+}
+
+TEST(TWTransactionUtil, CalcTxHashCosmos) {
+ constexpr auto coin = TWCoinTypeCosmos;
+ const auto encodedTxPtr = WRAPS(TWStringCreateWithUTF8Bytes("CpIBCo8BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm8KLWNvc21vczFta3k2OWNuOGVrdHd5MDg0NXZlYzl1cHNkcGhrdHh0MDNna3dseBItY29zbW9zMThzMGhkbnNsbGdjY2x3ZXU5YXltdzRuZ2t0cjJrMHJreWdkemRwGg8KBXVhdG9tEgY0MDAwMDASZQpOCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAuzvXOQ3owLGf5VGjeSzHzbpEfRn1+alK0HB4T4dVjZJEgQKAggBEhMKDQoFdWF0b20SBDEwMDAQwJoMGkCvvVE6d29P30cO9/lnXyGunWMPxNY12NuqDcCnFkNM0H4CUQdl1Gc9+ogIJbro5nyzZzlv9rl2/GsZox/JXoCX"));
+ const auto txHashResult = WRAPS(TWTransactionUtilCalcTxHash(coin, encodedTxPtr.get()));
+
+ assertStringsEqual(txHashResult, "85392373F54577562067030BF0D61596C91188AA5E6CA8FFE731BD0349296411");
+}
+
+TEST(TWTransactionUtil, CalcTxHashTon) {
+ constexpr auto coin = TWCoinTypeTON;
+ const auto encodedTxPtr = WRAPS(TWStringCreateWithUTF8Bytes("te6cckECGwEAA2sAAkWIACm9HPyVOpjCNOG6nbf+EwCONRHHpeMQsIlCoWNKhUaaHgECAgE0AwQBoXNpZ25///8R/////wAAAACACOfqY7L3l3aKc58eNxuJTaeH/fgBw2aG0coM+hjDpjWhJKbYKmsAD8v054HYSuO6vN3bQnV5U19BhsGfe1MDoAUBFP8A9KQT9LzyyAsGAFGAAAAAP///iKrcG+d35KaMMtuxik4jqNofFL51Mu1f8Qf19onqsDlyIAIKDsPIbQMWBwIBIAgJAWJiAC90HaFSSy94Zxb85WYu97uTKIxAlLvolycpcYkWc+8giFAAAAAAAAAAAAAAAAABFgIBSAoLAQLyDALc0CDXScEgkVuPYyDXCx8gghBleHRuvSGCEHNpbnS9sJJfA+CCEGV4dG66jrSAINchAdB01yH6QDD6RPgo+kQwWL2RW+DtRNCBAUHXIfQFgwf0Dm+hMZEw4YBA1yFwf9s84DEg10mBAoC5kTDgcOIXDQIBIA4PAR4g1wsfghBzaWduuvLgin8NAeaO8O2i7fshgwjXIgKDCNcjIIAg1yHTH9Mf0x/tRNDSANMfINMf0//XCgAK+QFAzPkQmiiUXwrbMeHywIffArNQB7Dy0IRRJbry4IVQNrry4Ib4I7vy0IgikvgA3gGkf8jKAMsfAc8Wye1UIJL4D95w2zzYFwIBIBARABm+Xw9qJoQICg65D6AsAgFuEhMCAUgUFQAZrc52omhAIOuQ64X/wAAZrx32omhAEOuQ64WPwAAXsyX7UTQcdch1wsfgABGyYvtRNDXCgCAAAAP27aLt+wL0BCFukmwhjkwCIdc5MHCUIccAs44tAdcoIHYeQ2wg10nACPLgkyDXSsAC8uCTINcdBscSwgBSMLDy0InXTNc5MAGk6GwShAe78uCT10rAAPLgk+1V4tIAAcAAkVvg69csCBQgkXCWAdcsCBwS4lIQseMPINdKGBkaAJYB+kAB+kT4KPpEMFi68uCR7UTQgQFB1xj0BQSdf8jKAEAEgwf0U/Lgi44UA4MH9Fvy4Iwi1woAIW4Bs7Dy0JDiyFADzxYS9ADJ7VQAcjDXLAgkji0h8uCS0gDtRNDSAFETuvLQj1RQMJExnAGBAUDXIdcKAPLgjuLIygBYzxbJ7VST8sCN4gAQk1vbMeHXTNB8Ui06"));
+ const auto txHashResult = WRAPS(TWTransactionUtilCalcTxHash(coin, encodedTxPtr.get()));
+
+ assertStringsEqual(txHashResult, "437dae441a95a6bccdcdcea2560c313de24f13dd85c76d5d7ecaab1e70a1e52b");
+}
+
+TEST(TWTransactionUtil, CalcTxHashAptos) {
+ constexpr auto coin = TWCoinTypeAptos;
+ const auto encodedTxPtr = WRAPS(TWStringCreateWithUTF8Bytes("07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"));
+ const auto txHashResult = WRAPS(TWTransactionUtilCalcTxHash(coin, encodedTxPtr.get()));
+
+ assertStringsEqual(txHashResult, "0xb4d62afd3862116e060dd6ad9848ccb50c2bc177799819f1d29c059ae2042467");
+}
+
+TEST(TWTransactionUtil, CalcTxHashSui) {
+ constexpr auto coin = TWCoinTypeSui;
+ const auto encodedTxPtr = WRAPS(TWStringCreateWithUTF8Bytes("AAACAAgQJwAAAAAAAAAgJZ/4B0q0Jcu0ifI24Y4I8D8aeFa998eih3vWT3OLUBUCAgABAQAAAQEDAAAAAAEBANV1rX8Y6UhGKlz2mPVk7zlKdSpx/sYkk6+KBVwBLA1QAQbywsjB2JZN8QGdZhbpcFcZvrq9kx2idVy5SM635olk7AIAAAAAAAAgYEVuxmf1zRBGdoDr+VDtMpIFF12s2Ua7I2ru1XyGF8/Vda1/GOlIRipc9pj1ZO85SnUqcf7GJJOvigVcASwNUAEAAAAAAAAA0AcAAAAAAAAA"));
+ const auto txHashResult = WRAPS(TWTransactionUtilCalcTxHash(coin, encodedTxPtr.get()));
+
+ assertStringsEqual(txHashResult, "HkPo6rYPyDY53x1MBszvSZVZyixVN7CHvCJGX381czAh");
+}
diff --git a/tools/install-dependencies b/tools/install-dependencies
index 3a04f8434bd..3ad3d77a54d 100755
--- a/tools/install-dependencies
+++ b/tools/install-dependencies
@@ -10,7 +10,10 @@ CMAKE=cmake
MAKE=make
# Load dependencies version
-BASE_DIR=$(cd `dirname $0`; pwd)
+BASE_DIR=$(
+ cd $(dirname $0)
+ pwd
+)
source ${BASE_DIR}/dependencies-version
# Setup up folders
@@ -50,28 +53,28 @@ function build_protobuf() {
PROTOBUF_DIR="$ROOT/build/local/src/protobuf"
cd ${PROTOBUF_DIR}/protobuf-$PROTOBUF_VERSION
- ./configure --prefix="$PREFIX"
- $MAKE -j4
- $MAKE install
+ $CMAKE -Scmake -B . -Dprotobuf_BUILD_TESTS=OFF -Dprotobuf_MODULE_COMPATIBLE=ON
+ $CMAKE --build . -j
+ $CMAKE --install . --prefix $PREFIX
# after install, cleanup to save space (docker)
- make clean
+ $CMAKE --build . --target clean
"$PREFIX/bin/protoc" --version
# Protobuf plugins
cd "$ROOT/protobuf-plugin"
- $CMAKE -H. -Bbuild -DCMAKE_INSTALL_PREFIX=$PREFIX
- $MAKE -Cbuild -j12
- $MAKE -Cbuild install
- rm -rf build
+ $CMAKE . -Bbuild -DProtobuf_DIR=$PREFIX/lib/cmake/protobuf
+ $CMAKE --build build -j
+ $CMAKE --install build --prefix $PREFIX
+ $CMAKE --build build --target clean
- if [[ -x "$(command -v swift)" && `uname` == "Darwin" ]]; then
+ if [[ -x "$(command -v swift)" && $(uname) == "Darwin" ]]; then
build_swift_plugin
fi
}
function build_swift_plugin() {
- # Download Swift Protobuf sources
+ # Download Swift Protobuf sources
SWIFT_PROTOBUF_DIR="$ROOT/build/local/src/swift-protobuf"
mkdir -p "$SWIFT_PROTOBUF_DIR"
cd "$SWIFT_PROTOBUF_DIR"
diff --git a/wasm/tests/Blockchain/TheOpenNetwork.test.ts b/wasm/tests/Blockchain/TheOpenNetwork.test.ts
index 6da95ff578c..b96cb551675 100644
--- a/wasm/tests/Blockchain/TheOpenNetwork.test.ts
+++ b/wasm/tests/Blockchain/TheOpenNetwork.test.ts
@@ -71,7 +71,6 @@ describe("TheOpenNetwork", () => {
let privateKeyData = HexCoding.decode("c38f49de2fb13223a9e7d37d5d0ffbdd89a5eb7c8b0ee4d1c299f2cefe7dc4a0");
let transfer = TW.TheOpenNetwork.Proto.Transfer.create({
- walletVersion: TW.TheOpenNetwork.Proto.WalletVersion.WALLET_V4_R2,
dest: "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q",
amount: new Long(10),
mode: (TW.TheOpenNetwork.Proto.SendMode.PAY_FEES_SEPARATELY | TW.TheOpenNetwork.Proto.SendMode.IGNORE_ACTION_PHASE_ERRORS),
@@ -83,6 +82,7 @@ describe("TheOpenNetwork", () => {
privateKey: PrivateKey.createWithData(privateKeyData).data(),
sequenceNumber: 6,
expireAt: 1671132440,
+ walletVersion: TW.TheOpenNetwork.Proto.WalletVersion.WALLET_V4_R2,
});
const encoded = TW.TheOpenNetwork.Proto.SigningInput.encode(input).finish();
@@ -108,7 +108,6 @@ describe("TheOpenNetwork", () => {
});
let transfer = TW.TheOpenNetwork.Proto.Transfer.create({
- walletVersion: TW.TheOpenNetwork.Proto.WalletVersion.WALLET_V4_R2,
dest: "EQBiaD8PO1NwfbxSkwbcNT9rXDjqhiIvXWymNO-edV0H5lja",
amount: new Long(100 * 1000 * 1000),
mode: (TW.TheOpenNetwork.Proto.SendMode.PAY_FEES_SEPARATELY | TW.TheOpenNetwork.Proto.SendMode.IGNORE_ACTION_PHASE_ERRORS),
@@ -122,6 +121,7 @@ describe("TheOpenNetwork", () => {
privateKey: PrivateKey.createWithData(privateKeyData).data(),
sequenceNumber: 1,
expireAt: 1787693046,
+ walletVersion: TW.TheOpenNetwork.Proto.WalletVersion.WALLET_V4_R2,
});
const encoded = TW.TheOpenNetwork.Proto.SigningInput.encode(input).finish();
| | | | | | | | | | | | | |