From 158c896682e9a3330c7b3ecbc8f8a026597dac3b Mon Sep 17 00:00:00 2001 From: Milerius Date: Mon, 23 Oct 2023 08:44:07 -0500 Subject: [PATCH 01/60] feat(aptos_rust): place holder tw_aptos --- rust/Cargo.lock | 14 +++++ rust/Cargo.toml | 1 + rust/tw_aptos/Cargo.toml | 17 ++++++ rust/tw_aptos/src/entry.rs | 106 +++++++++++++++++++++++++++++++++ rust/tw_aptos/src/lib.rs | 7 +++ rust/wallet_core_rs/Cargo.toml | 1 + 6 files changed, 146 insertions(+) create mode 100644 rust/tw_aptos/Cargo.toml create mode 100644 rust/tw_aptos/src/entry.rs create mode 100644 rust/tw_aptos/src/lib.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 035cd54ddad..9d3c757b120 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1457,6 +1457,19 @@ dependencies = [ "tw_proto", ] +[[package]] +name = "tw_aptos" +version = "0.1.0" +dependencies = [ + "tw_coin_entry", + "tw_encoding", + "tw_hash", + "tw_keypair", + "tw_memory", + "tw_number", + "tw_proto", +] + [[package]] name = "tw_bitcoin" version = "0.1.0" @@ -1746,6 +1759,7 @@ version = "0.1.0" dependencies = [ "serde_json", "tw_any_coin", + "tw_aptos", "tw_bitcoin", "tw_coin_entry", "tw_coin_registry", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index e5c065055f1..b1546b6055a 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "tw_any_coin", + "tw_aptos", "tw_bitcoin", "tw_coin_entry", "tw_coin_registry", diff --git a/rust/tw_aptos/Cargo.toml b/rust/tw_aptos/Cargo.toml new file mode 100644 index 00000000000..c26d2a03724 --- /dev/null +++ b/rust/tw_aptos/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "tw_aptos" +version = "0.1.0" +edition = "2021" + +[dependencies] +tw_coin_entry = { path = "../tw_coin_entry" } +tw_keypair = { path = "../tw_keypair" } +tw_proto = { path = "../tw_proto" } +tw_number = { path = "../tw_number" } +tw_hash = { path = "../tw_hash" } +tw_memory = { path = "../tw_memory" } + +[dev-dependencies] +tw_coin_entry = { path = "../tw_coin_entry", features = ["test-utils"] } +tw_encoding = { path = "../tw_encoding" } +tw_number = { path = "../tw_number", features = ["helpers"] } diff --git a/rust/tw_aptos/src/entry.rs b/rust/tw_aptos/src/entry.rs new file mode 100644 index 00000000000..4a82a5cea17 --- /dev/null +++ b/rust/tw_aptos/src/entry.rs @@ -0,0 +1,106 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::fmt::{Display, Formatter}; +use std::ops::Add; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{CoinAddress, CoinEntry, PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::derivation::Derivation; +use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::modules::json_signer::NoJsonSigner; +use tw_coin_entry::modules::message_signer::NoMessageSigner; +use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::prefix::NoPrefix; +use tw_keypair::tw::PublicKey; +use tw_hash::H160; +use tw_proto::Aptos::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; +use tw_memory::Data; + +pub struct AptosEntry; + +pub struct Address { + bytes: H160, +} + +impl Display for Address { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + todo!() + } +} + +impl CoinAddress for Address { + #[inline] + fn data(&self) -> Data { + todo!() + } +} + +impl CoinEntry for AptosEntry { + type AddressPrefix = NoPrefix; + type Address = Address; + type SigningInput<'a> = Proto::SigningInput<'a>; + type SigningOutput = Proto::SigningOutput<'static>; + type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + + // Optional modules: + type JsonSigner = NoJsonSigner; + type PlanBuilder = NoPlanBuilder; + type MessageSigner = NoMessageSigner; + + #[inline] + fn parse_address( + &self, + _coin: &dyn CoinContext, + address: &str, + _prefix: Option, + ) -> AddressResult { + todo!() + } + + fn derive_address( + &self, + _coin: &dyn CoinContext, + public_key: PublicKey, + _derivation: Derivation, + _prefix: Option, + ) -> AddressResult { + todo!() + } + + #[inline] + fn sign(&self, _coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput { + todo!() + } + + #[inline] + fn preimage_hashes( + &self, + _coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + ) -> Self::PreSigningOutput { + todo!() + } + + #[inline] + fn compile( + &self, + _coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Self::SigningOutput { + todo!() + } + + #[inline] + fn json_signer(&self) -> Option { + None + } + + #[inline] + fn message_signer(&self) -> Option { None } +} \ No newline at end of file diff --git a/rust/tw_aptos/src/lib.rs b/rust/tw_aptos/src/lib.rs new file mode 100644 index 00000000000..1afc00798db --- /dev/null +++ b/rust/tw_aptos/src/lib.rs @@ -0,0 +1,7 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod entry; diff --git a/rust/wallet_core_rs/Cargo.toml b/rust/wallet_core_rs/Cargo.toml index 90586221f9a..10de66fc423 100644 --- a/rust/wallet_core_rs/Cargo.toml +++ b/rust/wallet_core_rs/Cargo.toml @@ -15,6 +15,7 @@ ethereum-rlp = [] [dependencies] tw_any_coin = { path = "../tw_any_coin" } +tw_aptos = { path = "../tw_aptos" } tw_bitcoin = { path = "../tw_bitcoin" } tw_coin_entry = { path = "../tw_coin_entry", features = ["test-utils"] } tw_coin_registry = { path = "../tw_coin_registry" } From 73cbc9968743d489070fcd79cb0040455aaaf5af Mon Sep 17 00:00:00 2001 From: Milerius Date: Mon, 23 Oct 2023 13:33:50 -0500 Subject: [PATCH 02/60] feat(aptos_rust): Continue the place holder --- rust/Cargo.lock | 191 +++++++++++++++++- .../tests/tw_any_address_ffi_tests.rs | 8 + rust/tw_aptos/Cargo.toml | 1 + rust/tw_aptos/src/entry.rs | 28 +-- rust/tw_aptos/src/lib.rs | 1 + rust/tw_coin_registry/Cargo.toml | 1 + rust/tw_coin_registry/src/blockchain_type.rs | 2 + rust/tw_coin_registry/src/dispatcher.rs | 4 + 8 files changed, 203 insertions(+), 33 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 9d3c757b120..2b7f0768c12 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -196,16 +196,28 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitvec" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" +dependencies = [ + "funty 1.1.0", + "radium 0.6.2", + "tap", + "wyz 0.2.0", +] + [[package]] name = "bitvec" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ - "funty", - "radium", + "funty 2.0.0", + "radium 0.7.0", "tap", - "wyz", + "wyz 0.5.1", ] [[package]] @@ -521,6 +533,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" +[[package]] +name = "ethnum" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8ff382b2fa527fb7fb06eeebfc5bbb3f17e3cc6b9d70b006c41daa8824adac" + [[package]] name = "ff" version = "0.13.0" @@ -531,6 +549,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + [[package]] name = "fixed-hash" version = "0.8.0" @@ -544,6 +574,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + [[package]] name = "funty" version = "2.0.0" @@ -665,13 +701,31 @@ dependencies = [ "quick-error", ] +[[package]] +name = "impl-codec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443" +dependencies = [ + "parity-scale-codec 2.3.1", +] + [[package]] name = "impl-codec" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 3.5.0", +] + +[[package]] +name = "impl-serde" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" +dependencies = [ + "serde", ] [[package]] @@ -775,6 +829,25 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "move-core-types" +version = "0.0.4" +source = "git+https://github.com/move-language/move?rev=ea70797099baea64f05194a918cebd69ed02b285#ea70797099baea64f05194a918cebd69ed02b285" +dependencies = [ + "anyhow", + "bcs", + "ethnum", + "hex", + "num", + "once_cell", + "primitive-types 0.10.1", + "rand", + "ref-cast", + "serde", + "serde_bytes", + "uint", +] + [[package]] name = "move-core-types" version = "0.0.4" @@ -800,6 +873,20 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.3" @@ -811,6 +898,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-complex" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +dependencies = [ + "num-traits", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -821,6 +917,29 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -854,6 +973,20 @@ dependencies = [ "sha2 0.10.6", ] +[[package]] +name = "parity-scale-codec" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" +dependencies = [ + "arrayvec", + "bitvec 0.20.4", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive 2.3.1", + "serde", +] + [[package]] name = "parity-scale-codec" version = "3.5.0" @@ -861,13 +994,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ddb756ca205bd108aee3c62c6d3c994e1df84a59b9d6d4a5ea42ee1fd5a9a28" dependencies = [ "arrayvec", - "bitvec", + "bitvec 1.0.1", "byte-slice-cast", "impl-trait-for-tuples", - "parity-scale-codec-derive", + "parity-scale-codec-derive 3.1.4", "serde", ] +[[package]] +name = "parity-scale-codec-derive" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.107", +] + [[package]] name = "parity-scale-codec-derive" version = "3.1.4" @@ -923,14 +1068,26 @@ dependencies = [ "elliptic-curve", ] +[[package]] +name = "primitive-types" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" +dependencies = [ + "fixed-hash 0.7.0", + "impl-codec 0.5.1", + "impl-serde", + "uint", +] + [[package]] name = "primitive-types" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" dependencies = [ - "fixed-hash", - "impl-codec", + "fixed-hash 0.8.0", + "impl-codec 0.6.0", "uint", ] @@ -977,6 +1134,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" + [[package]] name = "radium" version = "0.7.0" @@ -1461,6 +1624,7 @@ dependencies = [ name = "tw_aptos" version = "0.1.0" dependencies = [ + "move-core-types 0.0.4 (git+https://github.com/move-language/move?rev=ea70797099baea64f05194a918cebd69ed02b285)", "tw_coin_entry", "tw_encoding", "tw_hash", @@ -1507,6 +1671,7 @@ dependencies = [ "lazy_static", "serde", "serde_json", + "tw_aptos", "tw_bitcoin", "tw_coin_entry", "tw_ethereum", @@ -1644,7 +1809,7 @@ version = "0.1.0" dependencies = [ "bcs", "hex", - "move-core-types", + "move-core-types 0.0.4 (git+https://github.com/move-language/move?rev=f7137eabc2046f76fdad3ded2c51e03a3b1fbd01)", "tw_memory", ] @@ -1654,7 +1819,7 @@ version = "0.1.0" dependencies = [ "arbitrary", "lazy_static", - "primitive-types", + "primitive-types 0.12.1", "serde", "tw_encoding", "tw_hash", @@ -1890,6 +2055,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + [[package]] name = "wyz" version = "0.5.1" diff --git a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs index 0449c69ca59..57284c7bd32 100644 --- a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs +++ b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs @@ -34,6 +34,7 @@ fn test_any_address_derive() { // TODO match `CoinType` when it's generated. let expected_address = match coin.blockchain { + BlockchainType::Aptos => "", // By default, Bitcoin will return a P2PKH address. BlockchainType::Bitcoin => "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", BlockchainType::Ethereum => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309", @@ -62,6 +63,10 @@ fn test_any_address_derive() { fn test_any_address_normalize_eth() { for coin in supported_coin_items() { let (denormalized, expected_normalized) = match coin.blockchain { + BlockchainType::Aptos => ( + "", + "", + ), BlockchainType::Bitcoin => ( "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", @@ -136,6 +141,9 @@ fn test_any_address_is_valid_coin() { fn test_any_address_is_valid_coin_invalid() { for coin in supported_coin_items() { let invalid = match coin.blockchain { + BlockchainType::Aptos => { + vec![""] + } BlockchainType::Bitcoin => { vec!["0xb16db98b365b1f89191996942612b14f1da4bd5f"] }, diff --git a/rust/tw_aptos/Cargo.toml b/rust/tw_aptos/Cargo.toml index c26d2a03724..ecbb3ade29d 100644 --- a/rust/tw_aptos/Cargo.toml +++ b/rust/tw_aptos/Cargo.toml @@ -10,6 +10,7 @@ tw_proto = { path = "../tw_proto" } tw_number = { path = "../tw_number" } tw_hash = { path = "../tw_hash" } tw_memory = { path = "../tw_memory" } +move-core-types = { git = "https://github.com/move-language/move", rev = "ea70797099baea64f05194a918cebd69ed02b285", features = ["address32"] } [dev-dependencies] tw_coin_entry = { path = "../tw_coin_entry", features = ["test-utils"] } diff --git a/rust/tw_aptos/src/entry.rs b/rust/tw_aptos/src/entry.rs index 4a82a5cea17..7ebc31a7aa3 100644 --- a/rust/tw_aptos/src/entry.rs +++ b/rust/tw_aptos/src/entry.rs @@ -4,40 +4,22 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use std::fmt::{Display, Formatter}; -use std::ops::Add; +use std::fmt::{Display}; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::coin_entry::{CoinAddress, CoinEntry, PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::{AddressResult}; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; use tw_coin_entry::prefix::NoPrefix; use tw_keypair::tw::PublicKey; -use tw_hash::H160; use tw_proto::Aptos::Proto; use tw_proto::TxCompiler::Proto as CompilerProto; -use tw_memory::Data; +use crate::address::Address; -pub struct AptosEntry; - -pub struct Address { - bytes: H160, -} - -impl Display for Address { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - todo!() - } -} -impl CoinAddress for Address { - #[inline] - fn data(&self) -> Data { - todo!() - } -} +pub struct AptosEntry; impl CoinEntry for AptosEntry { type AddressPrefix = NoPrefix; diff --git a/rust/tw_aptos/src/lib.rs b/rust/tw_aptos/src/lib.rs index 1afc00798db..94622b77f1a 100644 --- a/rust/tw_aptos/src/lib.rs +++ b/rust/tw_aptos/src/lib.rs @@ -5,3 +5,4 @@ // file LICENSE at the root of the source code distribution tree. pub mod entry; +pub mod address; diff --git a/rust/tw_coin_registry/Cargo.toml b/rust/tw_coin_registry/Cargo.toml index 4ad4e6d87de..b45a573c5a1 100644 --- a/rust/tw_coin_registry/Cargo.toml +++ b/rust/tw_coin_registry/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" lazy_static = "1.4.0" serde = { version = "1.0.163", features = ["derive"] } serde_json = "1.0.96" +tw_aptos = { path = "../tw_aptos" } tw_bitcoin = { path = "../tw_bitcoin" } tw_coin_entry = { path = "../tw_coin_entry" } tw_ethereum = { path = "../tw_ethereum" } diff --git a/rust/tw_coin_registry/src/blockchain_type.rs b/rust/tw_coin_registry/src/blockchain_type.rs index 72a4141d1ea..2139a373eb3 100644 --- a/rust/tw_coin_registry/src/blockchain_type.rs +++ b/rust/tw_coin_registry/src/blockchain_type.rs @@ -13,6 +13,7 @@ use std::str::FromStr; /// Extend this enum when adding new blockchains. #[derive(Copy, Clone, Debug)] pub enum BlockchainType { + Aptos, Bitcoin, Ethereum, InternetComputer, @@ -35,6 +36,7 @@ impl FromStr for BlockchainType { fn from_str(s: &str) -> Result { match s { + "Aptos" => Ok(BlockchainType::Aptos), "Bitcoin" => Ok(BlockchainType::Bitcoin), "Ethereum" => Ok(BlockchainType::Ethereum), "InternetComputer" => Ok(BlockchainType::InternetComputer), diff --git a/rust/tw_coin_registry/src/dispatcher.rs b/rust/tw_coin_registry/src/dispatcher.rs index e027054e443..68970653371 100644 --- a/rust/tw_coin_registry/src/dispatcher.rs +++ b/rust/tw_coin_registry/src/dispatcher.rs @@ -9,6 +9,7 @@ use crate::coin_context::CoinRegistryContext; use crate::coin_type::CoinType; use crate::error::{RegistryError, RegistryResult}; use crate::registry::get_coin_item; +use tw_aptos::entry::AptosEntry; use tw_bitcoin::entry::BitcoinEntry; use tw_coin_entry::coin_entry_ext::CoinEntryExt; use tw_ethereum::entry::EthereumEntry; @@ -19,6 +20,7 @@ use tw_ronin::entry::RoninEntry; pub type CoinEntryExtStaticRef = &'static dyn CoinEntryExt; pub type EvmEntryExtStaticRef = &'static dyn EvmEntryExt; +const APTOS: AptosEntry = AptosEntry; const BITCOIN: BitcoinEntry = BitcoinEntry; const ETHEREUM: EthereumEntry = EthereumEntry; const INTERNET_COMPUTER: InternetComputerEntry = InternetComputerEntry; @@ -26,6 +28,7 @@ const RONIN: RoninEntry = RoninEntry; pub fn blockchain_dispatcher(blockchain: BlockchainType) -> RegistryResult { match blockchain { + BlockchainType::Aptos => Ok(&APTOS), BlockchainType::Bitcoin => Ok(&BITCOIN), BlockchainType::Ethereum => Ok(ÐEREUM), BlockchainType::InternetComputer => Ok(&INTERNET_COMPUTER), @@ -46,6 +49,7 @@ pub fn coin_dispatcher( pub fn evm_dispatcher(coin: CoinType) -> RegistryResult { let item = get_coin_item(coin)?; match item.blockchain { + BlockchainType::Aptos => Err(RegistryError::Unsupported), BlockchainType::Bitcoin => Err(RegistryError::Unsupported), BlockchainType::Ethereum => Ok(ÐEREUM), BlockchainType::InternetComputer => Err(RegistryError::Unsupported), From 15e233a880bc126722053ce342de9a66e358cc22 Mon Sep 17 00:00:00 2001 From: Milerius Date: Mon, 23 Oct 2023 13:34:45 -0500 Subject: [PATCH 03/60] feat(aptos_rust): Start address implementation --- rust/tw_aptos/src/address.rs | 44 ++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 rust/tw_aptos/src/address.rs diff --git a/rust/tw_aptos/src/address.rs b/rust/tw_aptos/src/address.rs new file mode 100644 index 00000000000..2f52ad69bd7 --- /dev/null +++ b/rust/tw_aptos/src/address.rs @@ -0,0 +1,44 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::fmt::{Display, Formatter}; +use std::str::FromStr; +use move_core_types::account_address::{AccountAddress, AccountAddressParseError}; +use tw_coin_entry::coin_entry::CoinAddress; +use tw_coin_entry::error::AddressError; +use tw_memory::Data; + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Address { + addr: AccountAddress, +} + +impl Display for Address { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.addr) + } +} + +impl CoinAddress for Address { + #[inline] + fn data(&self) -> Data { + self.addr.to_vec() + } +} + +#[inline] +fn from_account_error(_err: AccountAddressParseError) -> AddressError { + AddressError::InvalidInput +} + +impl FromStr for Address { + type Err = AddressError; + + fn from_str(s: &str) -> Result { + let addr = AccountAddress::from_str(s).map_err(from_account_error)?; + Ok(Address{addr}) + } +} \ No newline at end of file From e4f2c99c0fbef61eaeed22c2ed62f2c3d3dfdc62 Mon Sep 17 00:00:00 2001 From: Milerius Date: Mon, 23 Oct 2023 15:14:52 -0500 Subject: [PATCH 04/60] feat(aptos_rust): Continue address implementation --- .../tests/tw_any_address_ffi_tests.rs | 17 ++++++++++++++--- rust/tw_aptos/src/address.rs | 2 +- rust/tw_aptos/src/entry.rs | 3 ++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs index 57284c7bd32..e45e4d2b10d 100644 --- a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs +++ b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs @@ -64,8 +64,8 @@ fn test_any_address_normalize_eth() { for coin in supported_coin_items() { let (denormalized, expected_normalized) = match coin.blockchain { BlockchainType::Aptos => ( - "", - "", + "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", + "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", ), BlockchainType::Bitcoin => ( "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", @@ -106,6 +106,15 @@ fn test_any_address_normalize_eth() { fn test_any_address_is_valid_coin() { for coin in supported_coin_items() { let valid = match coin.blockchain { + BlockchainType::Aptos => vec![ + "0x1", + "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", + "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", + "19aadeca9388e009d136245b9a67423f3eee242b03142849eb4f81a4a409e59c", + "777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb", + "0x777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb", + "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175" // Too short automatically padded + ], BlockchainType::Bitcoin => vec![ "1MrZNGN7mfWZiZNQttrzHjfw72jnJC2JNx", "bc1qunq74p3h8425hr6wllevlvqqr6sezfxj262rff", @@ -142,7 +151,9 @@ fn test_any_address_is_valid_coin_invalid() { for coin in supported_coin_items() { let invalid = match coin.blockchain { BlockchainType::Aptos => { - vec![""] + vec!["", // Empty + "Seff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", // Invalid Hex + "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175bb" ] // Too long } BlockchainType::Bitcoin => { vec!["0xb16db98b365b1f89191996942612b14f1da4bd5f"] diff --git a/rust/tw_aptos/src/address.rs b/rust/tw_aptos/src/address.rs index 2f52ad69bd7..080c28a9d97 100644 --- a/rust/tw_aptos/src/address.rs +++ b/rust/tw_aptos/src/address.rs @@ -18,7 +18,7 @@ pub struct Address { impl Display for Address { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.addr) + write!(f, "{}", self.addr.to_hex_literal()) } } diff --git a/rust/tw_aptos/src/entry.rs b/rust/tw_aptos/src/entry.rs index 7ebc31a7aa3..d8a348f4571 100644 --- a/rust/tw_aptos/src/entry.rs +++ b/rust/tw_aptos/src/entry.rs @@ -5,6 +5,7 @@ // file LICENSE at the root of the source code distribution tree. use std::fmt::{Display}; +use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; @@ -40,7 +41,7 @@ impl CoinEntry for AptosEntry { address: &str, _prefix: Option, ) -> AddressResult { - todo!() + Address::from_str(address) } fn derive_address( From f5783068229f7112abed8b521757d4fb78c4e0e9 Mon Sep 17 00:00:00 2001 From: Milerius Date: Mon, 23 Oct 2023 16:15:36 -0500 Subject: [PATCH 05/60] feat(aptos_rust): Continue address implementation --- .../tests/tw_any_address_ffi_tests.rs | 5 +- rust/tw_aptos/src/address.rs | 67 ++++++++++++++++++- rust/tw_aptos/src/entry.rs | 7 +- rust/tw_keypair/src/tw/public.rs | 9 +++ 4 files changed, 82 insertions(+), 6 deletions(-) diff --git a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs index e45e4d2b10d..856c54172a1 100644 --- a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs +++ b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs @@ -34,7 +34,7 @@ fn test_any_address_derive() { // TODO match `CoinType` when it's generated. let expected_address = match coin.blockchain { - BlockchainType::Aptos => "", + BlockchainType::Aptos => "0x9006fa46f038224e8004bdda97f2e7a60c2c3d135bce7cb15541e5c0aae907a4", // By default, Bitcoin will return a P2PKH address. BlockchainType::Bitcoin => "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", BlockchainType::Ethereum => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309", @@ -111,9 +111,8 @@ fn test_any_address_is_valid_coin() { "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", "19aadeca9388e009d136245b9a67423f3eee242b03142849eb4f81a4a409e59c", - "777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb", "0x777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb", - "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175" // Too short automatically padded + "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175" ], BlockchainType::Bitcoin => vec![ "1MrZNGN7mfWZiZNQttrzHjfw72jnJC2JNx", diff --git a/rust/tw_aptos/src/address.rs b/rust/tw_aptos/src/address.rs index 080c28a9d97..17e699a923b 100644 --- a/rust/tw_aptos/src/address.rs +++ b/rust/tw_aptos/src/address.rs @@ -9,13 +9,33 @@ use std::str::FromStr; use move_core_types::account_address::{AccountAddress, AccountAddressParseError}; use tw_coin_entry::coin_entry::CoinAddress; use tw_coin_entry::error::AddressError; +use tw_keypair::ed25519; use tw_memory::Data; +use tw_hash::sha3::sha3_256; + + +#[derive(Debug)] +#[repr(u8)] +pub enum Scheme { + Ed25519 = 0, +} #[derive(Clone, Copy, Debug, PartialEq)] pub struct Address { addr: AccountAddress, } +impl Address { + /// Initializes an address with a `ed25519` public key. + pub fn with_ed25519_pubkey(pubkey: &ed25519::sha512::PublicKey) -> Result { + let mut to_hash = pubkey.as_slice().to_vec(); + to_hash.push(Scheme::Ed25519 as u8); + let hashed = sha3_256(to_hash.as_slice()); + let addr = AccountAddress::from_bytes(hashed).map_err(from_account_error)?; + Ok(Address{addr}) + } +} + impl Display for Address { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.addr.to_hex_literal()) @@ -38,7 +58,52 @@ impl FromStr for Address { type Err = AddressError; fn from_str(s: &str) -> Result { - let addr = AccountAddress::from_str(s).map_err(from_account_error)?; + const NUM_CHARS: usize = AccountAddress::LENGTH * 2; + let mut has_0x = false; + let mut working = s.trim(); + + // Checks if it has a 0x at the beginning, which is okay + if working.starts_with("0x") { + has_0x = true; + working = &working[2..]; + } + + if working.len() > NUM_CHARS { + return Err(AddressError::InvalidInput) + } else if !has_0x && working.len() < NUM_CHARS { + return Err(AddressError::InvalidInput) + } + + if !working.chars().all(|c| char::is_ascii_hexdigit(&c)) { + return Err(AddressError::InvalidInput) + } + + let addr = if has_0x { + AccountAddress::from_hex_literal(s.trim()) + } else { + AccountAddress::from_str(s.trim()) + }.map_err(from_account_error)?; + Ok(Address{addr}) } +} + +#[cfg(test)] +mod tests { + use super::*; + use tw_keypair::ed25519::sha512::PrivateKey; + + #[test] + fn test_from_public_key() { + let private = PrivateKey::try_from( + "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5", + ) + .unwrap(); + let public = private.public(); + let addr = Address::with_ed25519_pubkey(&public); + assert_eq!( + addr.unwrap().to_string(), + "0x9006fa46f038224e8004bdda97f2e7a60c2c3d135bce7cb15541e5c0aae907a4" + ); + } } \ No newline at end of file diff --git a/rust/tw_aptos/src/entry.rs b/rust/tw_aptos/src/entry.rs index d8a348f4571..a01e5417662 100644 --- a/rust/tw_aptos/src/entry.rs +++ b/rust/tw_aptos/src/entry.rs @@ -9,7 +9,7 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::{AddressResult}; +use tw_coin_entry::error::{AddressError, AddressResult}; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; @@ -51,7 +51,10 @@ impl CoinEntry for AptosEntry { _derivation: Derivation, _prefix: Option, ) -> AddressResult { - todo!() + let public_key = public_key + .to_ed25519() + .ok_or(AddressError::PublicKeyTypeMismatch)?; + Address::with_ed25519_pubkey(public_key) } #[inline] diff --git a/rust/tw_keypair/src/tw/public.rs b/rust/tw_keypair/src/tw/public.rs index e8a31f0fc20..46cb0e4f7e9 100644 --- a/rust/tw_keypair/src/tw/public.rs +++ b/rust/tw_keypair/src/tw/public.rs @@ -135,4 +135,13 @@ impl PublicKey { _ => None, } } + + pub fn to_ed25519(&self) -> Option<&ed25519::sha512::PublicKey> { + match self { + PublicKey::Ed25519(ed25519) => { + Some(ed25519) + }, + _ => None, + } + } } From a0ffda757bee639569728429eb85a5a58622631b Mon Sep 17 00:00:00 2001 From: Milerius Date: Mon, 23 Oct 2023 16:16:39 -0500 Subject: [PATCH 06/60] feat(aptos_rust): Continue address implementation --- rust/tw_aptos/src/address.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/tw_aptos/src/address.rs b/rust/tw_aptos/src/address.rs index 17e699a923b..e6651b7239f 100644 --- a/rust/tw_aptos/src/address.rs +++ b/rust/tw_aptos/src/address.rs @@ -57,6 +57,7 @@ fn from_account_error(_err: AccountAddressParseError) -> AddressError { impl FromStr for Address { type Err = AddressError; + // https://github.com/aptos-labs/aptos-core/blob/261019cbdafe1c514c60c2b74357ea2c77d25e67/types/src/account_address.rs#L44 fn from_str(s: &str) -> Result { const NUM_CHARS: usize = AccountAddress::LENGTH * 2; let mut has_0x = false; From b66e002a35819b8d2f2a6516172e1f6d713d2ec4 Mon Sep 17 00:00:00 2001 From: Milerius Date: Mon, 23 Oct 2023 16:24:14 -0500 Subject: [PATCH 07/60] feat(aptos_rust): Increase address coverage --- rust/tw_aptos/src/address.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/rust/tw_aptos/src/address.rs b/rust/tw_aptos/src/address.rs index e6651b7239f..6b8c33f9eb2 100644 --- a/rust/tw_aptos/src/address.rs +++ b/rust/tw_aptos/src/address.rs @@ -13,14 +13,10 @@ use tw_keypair::ed25519; use tw_memory::Data; use tw_hash::sha3::sha3_256; - -#[derive(Debug)] #[repr(u8)] pub enum Scheme { Ed25519 = 0, } - -#[derive(Clone, Copy, Debug, PartialEq)] pub struct Address { addr: AccountAddress, } From f7d77c33773d99506960c9df436141f767a78e75 Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 24 Oct 2023 05:46:03 -0500 Subject: [PATCH 08/60] feat(aptos_rust): Increase address coverage --- rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs | 3 ++- rust/tw_aptos/src/address.rs | 10 +++++++++- rust/tw_coin_entry/src/error.rs | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs index 856c54172a1..7fb1f6e80fa 100644 --- a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs +++ b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs @@ -152,7 +152,8 @@ fn test_any_address_is_valid_coin_invalid() { BlockchainType::Aptos => { vec!["", // Empty "Seff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", // Invalid Hex - "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175bb" ] // Too long + "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175bb", // Too long + "0xSeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b"] } BlockchainType::Bitcoin => { vec!["0xb16db98b365b1f89191996942612b14f1da4bd5f"] diff --git a/rust/tw_aptos/src/address.rs b/rust/tw_aptos/src/address.rs index 6b8c33f9eb2..750e3207278 100644 --- a/rust/tw_aptos/src/address.rs +++ b/rust/tw_aptos/src/address.rs @@ -17,11 +17,13 @@ use tw_hash::sha3::sha3_256; pub enum Scheme { Ed25519 = 0, } +#[derive(Clone)] pub struct Address { addr: AccountAddress, } impl Address { + pub const LENGTH: usize = AccountAddress::LENGTH; /// Initializes an address with a `ed25519` public key. pub fn with_ed25519_pubkey(pubkey: &ed25519::sha512::PublicKey) -> Result { let mut to_hash = pubkey.as_slice().to_vec(); @@ -99,8 +101,14 @@ mod tests { let public = private.public(); let addr = Address::with_ed25519_pubkey(&public); assert_eq!( - addr.unwrap().to_string(), + addr.as_ref().unwrap().to_string(), "0x9006fa46f038224e8004bdda97f2e7a60c2c3d135bce7cb15541e5c0aae907a4" ); + assert_eq!(addr.unwrap().data().len(), Address::LENGTH); + } + + #[test] + fn test_from_account_error() { + assert_eq!(from_account_error(AccountAddressParseError{}), AddressError::InvalidInput); } } \ No newline at end of file diff --git a/rust/tw_coin_entry/src/error.rs b/rust/tw_coin_entry/src/error.rs index c31b495576b..bfbecf2ad4d 100644 --- a/rust/tw_coin_entry/src/error.rs +++ b/rust/tw_coin_entry/src/error.rs @@ -26,7 +26,7 @@ macro_rules! signing_output_error { pub type AddressResult = Result; -#[derive(Debug)] +#[derive(Debug, Eq, PartialEq)] pub enum AddressError { UnknownCoinType, MissingPrefix, From da596f853eddeffcba9062097cf2e1c89b477378 Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 24 Oct 2023 06:07:49 -0500 Subject: [PATCH 09/60] feat(aptos): use rust implementation --- src/Aptos/Entry.cpp | 8 ++++---- tests/chains/Aptos/TWAptosAddressTests.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Aptos/Entry.cpp b/src/Aptos/Entry.cpp index 8c7fafced8e..0a6cfef6834 100644 --- a/src/Aptos/Entry.cpp +++ b/src/Aptos/Entry.cpp @@ -11,12 +11,12 @@ namespace TW::Aptos { -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { - return Address::isValid(address); +bool Entry::validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const { + return validateAddressRust(coin, address, addressPrefix); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { - return Address(publicKey).string(); +std::string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const { + return deriveAddressRust(coin, publicKey, derivation, addressPrefix); } void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { diff --git a/tests/chains/Aptos/TWAptosAddressTests.cpp b/tests/chains/Aptos/TWAptosAddressTests.cpp index 2b9e3fd8047..ec6612d0960 100644 --- a/tests/chains/Aptos/TWAptosAddressTests.cpp +++ b/tests/chains/Aptos/TWAptosAddressTests.cpp @@ -28,7 +28,7 @@ TEST(TWAptosAddress, HDWallet) { auto address = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(publicKey.get(), TWCoinTypeAptos)); auto addressStr = WRAPS(TWAnyAddressDescription(address.get())); - assertStringsEqual(addressStr, "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + assertStringsEqual(addressStr, "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); } } // namespace TW::Aptos::tests From b9b6ed94b0317f3c70a1349b5eb1fbb4d00efa2c Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 24 Oct 2023 09:24:24 -0500 Subject: [PATCH 10/60] feat(aptos): start implementation of transaction_payload --- rust/Cargo.lock | 15 ++-- rust/tw_aptos/Cargo.toml | 3 + rust/tw_aptos/src/address.rs | 5 ++ rust/tw_aptos/src/lib.rs | 4 + rust/tw_aptos/src/serde_helper/mod.rs | 5 ++ rust/tw_aptos/src/serde_helper/vec_bytes.rs | 30 +++++++ rust/tw_aptos/src/transaction.rs | 88 +++++++++++++++++++++ rust/tw_aptos/src/transaction_payload.rs | 68 ++++++++++++++++ 8 files changed, 212 insertions(+), 6 deletions(-) create mode 100644 rust/tw_aptos/src/serde_helper/mod.rs create mode 100644 rust/tw_aptos/src/serde_helper/vec_bytes.rs create mode 100644 rust/tw_aptos/src/transaction.rs create mode 100644 rust/tw_aptos/src/transaction_payload.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 2b7f0768c12..fbc013b3f4a 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -136,9 +136,9 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bcs" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b06b4c1f053002b70e7084ac944c77d58d5d92b2110dbc5e852735e00ad3ccc" +checksum = "85b6598a2f5d564fb7855dc6b06fd1c38cff5a72bd8b863a4d021938497b440a" dependencies = [ "serde", "thiserror", @@ -1329,9 +1329,9 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ "serde_derive", ] @@ -1347,9 +1347,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", @@ -1624,7 +1624,10 @@ dependencies = [ name = "tw_aptos" version = "0.1.0" dependencies = [ + "bcs", "move-core-types 0.0.4 (git+https://github.com/move-language/move?rev=ea70797099baea64f05194a918cebd69ed02b285)", + "serde", + "serde_bytes", "tw_coin_entry", "tw_encoding", "tw_hash", diff --git a/rust/tw_aptos/Cargo.toml b/rust/tw_aptos/Cargo.toml index ecbb3ade29d..6753e185550 100644 --- a/rust/tw_aptos/Cargo.toml +++ b/rust/tw_aptos/Cargo.toml @@ -10,7 +10,10 @@ tw_proto = { path = "../tw_proto" } tw_number = { path = "../tw_number" } tw_hash = { path = "../tw_hash" } tw_memory = { path = "../tw_memory" } +bcs = "0.1.6" move-core-types = { git = "https://github.com/move-language/move", rev = "ea70797099baea64f05194a918cebd69ed02b285", features = ["address32"] } +serde = { version = "1.0.189", features = ["derive"] } +serde_bytes = "0.11.12" [dev-dependencies] tw_coin_entry = { path = "../tw_coin_entry", features = ["test-utils"] } diff --git a/rust/tw_aptos/src/address.rs b/rust/tw_aptos/src/address.rs index 750e3207278..2de65e751ea 100644 --- a/rust/tw_aptos/src/address.rs +++ b/rust/tw_aptos/src/address.rs @@ -25,6 +25,7 @@ pub struct Address { impl Address { pub const LENGTH: usize = AccountAddress::LENGTH; /// Initializes an address with a `ed25519` public key. + pub fn with_ed25519_pubkey(pubkey: &ed25519::sha512::PublicKey) -> Result { let mut to_hash = pubkey.as_slice().to_vec(); to_hash.push(Scheme::Ed25519 as u8); @@ -32,6 +33,10 @@ impl Address { let addr = AccountAddress::from_bytes(hashed).map_err(from_account_error)?; Ok(Address{addr}) } + + pub fn inner(&self) -> AccountAddress { + self.addr + } } impl Display for Address { diff --git a/rust/tw_aptos/src/lib.rs b/rust/tw_aptos/src/lib.rs index 94622b77f1a..d39611279bb 100644 --- a/rust/tw_aptos/src/lib.rs +++ b/rust/tw_aptos/src/lib.rs @@ -6,3 +6,7 @@ pub mod entry; pub mod address; +mod serde_helper; + +pub mod transaction; +pub mod transaction_payload; diff --git a/rust/tw_aptos/src/serde_helper/mod.rs b/rust/tw_aptos/src/serde_helper/mod.rs new file mode 100644 index 00000000000..a35216bebee --- /dev/null +++ b/rust/tw_aptos/src/serde_helper/mod.rs @@ -0,0 +1,5 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +pub mod vec_bytes; \ No newline at end of file diff --git a/rust/tw_aptos/src/serde_helper/vec_bytes.rs b/rust/tw_aptos/src/serde_helper/vec_bytes.rs new file mode 100644 index 00000000000..aba6da2feaa --- /dev/null +++ b/rust/tw_aptos/src/serde_helper/vec_bytes.rs @@ -0,0 +1,30 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use serde::{ + de::Deserializer, + ser::{SerializeSeq, Serializer}, + Deserialize, +}; + +pub fn serialize(data: &[Vec], serializer: S) -> Result + where + S: Serializer, +{ + let mut seq = serializer.serialize_seq(Some(data.len()))?; + for e in data { + seq.serialize_element(serde_bytes::Bytes::new(e.as_slice()))?; + } + seq.end() +} + +pub fn deserialize<'de, D>(deserializer: D) -> Result>, D::Error> + where + D: Deserializer<'de>, +{ + Ok(>::deserialize(deserializer)? + .into_iter() + .map(serde_bytes::ByteBuf::into_vec) + .collect()) +} \ No newline at end of file diff --git a/rust/tw_aptos/src/transaction.rs b/rust/tw_aptos/src/transaction.rs new file mode 100644 index 00000000000..60a1f998d58 --- /dev/null +++ b/rust/tw_aptos/src/transaction.rs @@ -0,0 +1,88 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use move_core_types::account_address::AccountAddress; +use tw_keypair::ed25519::sha512::PublicKey; +use crate::transaction_payload::{EntryFunction, TransactionPayload}; + +#[derive(Clone)] +pub enum TransactionAuthenticator { + /// Single Ed25519 signature + Ed25519 { + public_key: PublicKey, + signature: Vec, + } +} + +/// RawTransaction is the portion of a transaction that a client signs. +#[derive(Clone)] +pub struct RawTransaction { + /// Sender's address. + sender: AccountAddress, + + /// Sequence number of this transaction. This must match the sequence number + /// stored in the sender's account at the time the transaction executes. + sequence_number: u64, + + /// The transaction payload, e.g., a script to execute. + payload: TransactionPayload, + + /// Maximal total gas to spend for this transaction. + max_gas_amount: u64, + + /// Price to be paid per gas unit. + gas_unit_price: u64, + + /// Expiration timestamp for this transaction, represented + /// as seconds from the Unix Epoch. If the current blockchain timestamp + /// is greater than or equal to this time, then the transaction has + /// expired and will be discarded. This can be set to a large value far + /// in the future to indicate that a transaction does not expire. + expiration_timestamp_secs: u64, + + /// Chain ID of the Aptos network this transaction is intended for. + chain_id: u8, +} + +impl RawTransaction { + /// Create a new `RawTransaction` with an entry function. + pub fn new_entry_function( + sender: AccountAddress, + sequence_number: u64, + entry_function: EntryFunction, + max_gas_amount: u64, + gas_unit_price: u64, + expiration_timestamp_secs: u64, + chain_id: u8, + ) -> Self { + RawTransaction { + sender, + sequence_number, + payload: TransactionPayload::EntryFunction(entry_function), + max_gas_amount, + gas_unit_price, + expiration_timestamp_secs, + chain_id, + } + } +} + +/// A transaction that has been signed. +/// +/// A `SignedTransaction` is a single transaction that can be atomically executed. Clients submit +/// these to validator nodes, and the validator and executor submits these to the VM. +/// +/// **IMPORTANT:** The signature of a `SignedTransaction` is not guaranteed to be verified. For a +/// transaction whose signature is statically guaranteed to be verified, see +/// [`SignatureCheckedTransaction`]. +#[derive(Clone)] +pub struct SignedTransaction { + /// The raw transaction + raw_txn: RawTransaction, + + /// Public key and signature to authenticate + authenticator: TransactionAuthenticator, +} \ No newline at end of file diff --git a/rust/tw_aptos/src/transaction_payload.rs b/rust/tw_aptos/src/transaction_payload.rs new file mode 100644 index 00000000000..7160ed306eb --- /dev/null +++ b/rust/tw_aptos/src/transaction_payload.rs @@ -0,0 +1,68 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use move_core_types::identifier::Identifier; +use move_core_types::language_storage::{ModuleId, TypeTag}; +use serde::{Deserialize, Serialize}; +use crate::{serde_helper::vec_bytes}; + +#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] +pub struct EntryFunction { + module: ModuleId, + function: Identifier, + ty_args: Vec, + #[serde(with = "vec_bytes")] + args: Vec>, +} + +#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] +pub enum TransactionPayload { + Script, + ModuleBundle, + /// A transaction that executes an existing entry function published on-chain. + EntryFunction(EntryFunction), +} + +impl EntryFunction { + pub fn new( + module: ModuleId, + function: Identifier, + ty_args: Vec, + args: Vec>, + ) -> Self { + EntryFunction { + module, + function, + ty_args, + args, + } + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + use crate::address::Address; + use move_core_types::account_address::AccountAddress; + use move_core_types::identifier::Identifier; + use move_core_types::language_storage::{ModuleId, TypeTag}; + use tw_encoding::hex; + use crate::transaction_payload::{EntryFunction, TransactionPayload}; + + #[test] + fn test_basic_payload() { + let addr = Address::from_str("0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b").unwrap().inner(); + let amount: i64 = 1000; + let args = vec![bcs::to_bytes(&addr).unwrap(), bcs::to_bytes(&amount).unwrap()]; + let module = ModuleId::new(AccountAddress::ONE, Identifier::from_str("coin").unwrap()); + let function = Identifier::from_str("transfer").unwrap(); + let type_tag = vec![TypeTag::from_str("0x1::aptos_coin::AptosCoin").unwrap()]; + let entry = EntryFunction::new(module, function, type_tag, args); + let tp = TransactionPayload::EntryFunction(entry); + let serialized = bcs::to_bytes(&tp).unwrap(); + assert_eq!(hex::encode(serialized, false), "02000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b08e803000000000000"); + } +} \ No newline at end of file From 606f07b65d842435302b2f8067f83e7d837eb828 Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 24 Oct 2023 09:46:21 -0500 Subject: [PATCH 11/60] feat(aptos): continue implementation of transaction --- rust/tw_aptos/src/constants.rs | 8 + rust/tw_aptos/src/lib.rs | 7 +- rust/tw_aptos/src/transaction.rs | 24 +++ rust/tw_aptos/src/transaction_builder.rs | 180 +++++++++++++++++++++++ 4 files changed, 216 insertions(+), 3 deletions(-) create mode 100644 rust/tw_aptos/src/constants.rs create mode 100644 rust/tw_aptos/src/transaction_builder.rs diff --git a/rust/tw_aptos/src/constants.rs b/rust/tw_aptos/src/constants.rs new file mode 100644 index 00000000000..63eceb2063c --- /dev/null +++ b/rust/tw_aptos/src/constants.rs @@ -0,0 +1,8 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub const GAS_UNIT_PRICE: u64 = 100; +pub const MAX_GAS_AMOUNT: u64 = 100_000_000; \ No newline at end of file diff --git a/rust/tw_aptos/src/lib.rs b/rust/tw_aptos/src/lib.rs index d39611279bb..9c7cb6a2915 100644 --- a/rust/tw_aptos/src/lib.rs +++ b/rust/tw_aptos/src/lib.rs @@ -4,9 +4,10 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -pub mod entry; -pub mod address; mod serde_helper; - +pub mod address; +pub mod constants; +pub mod entry; pub mod transaction; +pub mod transaction_builder; pub mod transaction_payload; diff --git a/rust/tw_aptos/src/transaction.rs b/rust/tw_aptos/src/transaction.rs index 60a1f998d58..8e4ba90d86f 100644 --- a/rust/tw_aptos/src/transaction.rs +++ b/rust/tw_aptos/src/transaction.rs @@ -48,6 +48,30 @@ pub struct RawTransaction { } impl RawTransaction { + /// Create a new `RawTransaction` with a payload. + /// + /// It can be either to publish a module, to execute a script, or to issue a writeset + /// transaction. + pub fn new( + sender: AccountAddress, + sequence_number: u64, + payload: TransactionPayload, + max_gas_amount: u64, + gas_unit_price: u64, + expiration_timestamp_secs: u64, + chain_id: u8, + ) -> Self { + RawTransaction { + sender, + sequence_number, + payload, + max_gas_amount, + gas_unit_price, + expiration_timestamp_secs, + chain_id, + } + } + /// Create a new `RawTransaction` with an entry function. pub fn new_entry_function( sender: AccountAddress, diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs new file mode 100644 index 00000000000..85a69588f7c --- /dev/null +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -0,0 +1,180 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use move_core_types::account_address::AccountAddress; +use crate::constants::{GAS_UNIT_PRICE, MAX_GAS_AMOUNT}; +use crate::transaction::RawTransaction; +use crate::transaction_payload::{EntryFunction, TransactionPayload}; + +pub struct TransactionBuilder { + sender: Option, + sequence_number: Option, + payload: TransactionPayload, + max_gas_amount: u64, + gas_unit_price: u64, + expiration_timestamp_secs: u64, + chain_id: u8, +} + +impl TransactionBuilder { + pub fn new( + payload: TransactionPayload, + expiration_timestamp_secs: u64, + chain_id: u8, + ) -> Self { + Self { + payload, + chain_id, + expiration_timestamp_secs, + max_gas_amount: MAX_GAS_AMOUNT, + gas_unit_price: std::cmp::max(GAS_UNIT_PRICE, 1), + sender: None, + sequence_number: None, + } + } + + pub fn sender(mut self, sender: AccountAddress) -> Self { + self.sender = Some(sender); + self + } + + pub fn sequence_number(mut self, sequence_number: u64) -> Self { + self.sequence_number = Some(sequence_number); + self + } + + pub fn max_gas_amount(mut self, max_gas_amount: u64) -> Self { + self.max_gas_amount = max_gas_amount; + self + } + + pub fn gas_unit_price(mut self, gas_unit_price: u64) -> Self { + self.gas_unit_price = gas_unit_price; + self + } + + pub fn chain_id(mut self, chain_id: u8) -> Self { + self.chain_id = chain_id; + self + } + + pub fn expiration_timestamp_secs(mut self, expiration_timestamp_secs: u64) -> Self { + self.expiration_timestamp_secs = expiration_timestamp_secs; + self + } + + pub fn build(self) -> RawTransaction { + RawTransaction::new( + self.sender.expect("sender must have been set"), + self.sequence_number + .expect("sequence number must have been set"), + self.payload, + self.max_gas_amount, + self.gas_unit_price, + self.expiration_timestamp_secs, + self.chain_id, + ) + } +} + +#[derive(Clone, Debug)] +pub struct TransactionFactory { + max_gas_amount: u64, + gas_unit_price: u64, + transaction_expiration_time: u64, + chain_id: u8, +} + +impl TransactionFactory { + pub fn new(chain_id: u8) -> Self { + Self { + max_gas_amount: MAX_GAS_AMOUNT, + gas_unit_price: GAS_UNIT_PRICE, + transaction_expiration_time: 30, + chain_id, + } + } + + pub fn with_max_gas_amount(mut self, max_gas_amount: u64) -> Self { + self.max_gas_amount = max_gas_amount; + self + } + + pub fn with_gas_unit_price(mut self, gas_unit_price: u64) -> Self { + self.gas_unit_price = gas_unit_price; + self + } + + pub fn with_transaction_expiration_time(mut self, transaction_expiration_time: u64) -> Self { + self.transaction_expiration_time = transaction_expiration_time; + self + } + + pub fn with_chain_id(mut self, chain_id: u8) -> Self { + self.chain_id = chain_id; + self + } + + pub fn get_max_gas_amount(&self) -> u64 { + self.max_gas_amount + } + + pub fn get_gas_unit_price(&self) -> u64 { + self.gas_unit_price + } + + pub fn get_transaction_expiration_time(&self) -> u64 { + self.transaction_expiration_time + } + + pub fn payload(&self, payload: TransactionPayload) -> TransactionBuilder { + self.transaction_builder(payload) + } + + pub fn entry_function(&self, func: EntryFunction) -> TransactionBuilder { + self.payload(TransactionPayload::EntryFunction(func)) + } + + pub fn create_user_account(&self, public_key: &tw_keypair::ed25519::sha512::PublicKey) -> TransactionBuilder { + todo!() + } + + pub fn implicitly_create_user_account_and_transfer( + &self, + public_key: &tw_keypair::ed25519::sha512::PublicKey, + amount: u64, + ) -> TransactionBuilder { + todo!() + } + + pub fn transfer(&self, to: AccountAddress, amount: u64) -> TransactionBuilder { + todo!() + } + + pub fn account_transfer(&self, to: AccountAddress, amount: u64) -> TransactionBuilder { + todo!() + } + + fn transaction_builder(&self, payload: TransactionPayload) -> TransactionBuilder { + TransactionBuilder { + sender: None, + sequence_number: None, + payload, + max_gas_amount: self.max_gas_amount, + gas_unit_price: self.gas_unit_price, + expiration_timestamp_secs: self.expiration_timestamp(), + chain_id: self.chain_id, + } + } + + fn expiration_timestamp(&self) -> u64 { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() + + self.transaction_expiration_time + } +} From e25dc6491ee4ed529a8f0d9a019775ea5a6be6f0 Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 24 Oct 2023 14:10:12 -0500 Subject: [PATCH 12/60] feat(aptos): first attempt of a signing tx --- rust/tw_aptos/src/aptos_move_packages.rs | 25 ++++++++++++++++++++ rust/tw_aptos/src/entry.rs | 4 ++++ rust/tw_aptos/src/lib.rs | 1 + rust/tw_aptos/src/transaction.rs | 21 +++++++++++++++-- rust/tw_aptos/src/transaction_builder.rs | 29 +++++++++++++++++++++++- 5 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 rust/tw_aptos/src/aptos_move_packages.rs diff --git a/rust/tw_aptos/src/aptos_move_packages.rs b/rust/tw_aptos/src/aptos_move_packages.rs new file mode 100644 index 00000000000..c9f82e91bb4 --- /dev/null +++ b/rust/tw_aptos/src/aptos_move_packages.rs @@ -0,0 +1,25 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use move_core_types::account_address::AccountAddress; +use move_core_types::ident_str; +use move_core_types::language_storage::ModuleId; +use crate::transaction_payload::{EntryFunction, TransactionPayload}; + +pub fn aptos_account_transfer(to: AccountAddress, amount: u64) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ]), + ident_str!("aptos_account").to_owned(), + ), + ident_str!("transfer").to_owned(), + vec![], + vec![bcs::to_bytes(&to).unwrap(), bcs::to_bytes(&amount).unwrap()], + )) +} \ No newline at end of file diff --git a/rust/tw_aptos/src/entry.rs b/rust/tw_aptos/src/entry.rs index a01e5417662..65674de3c8e 100644 --- a/rust/tw_aptos/src/entry.rs +++ b/rust/tw_aptos/src/entry.rs @@ -18,6 +18,7 @@ use tw_keypair::tw::PublicKey; use tw_proto::Aptos::Proto; use tw_proto::TxCompiler::Proto as CompilerProto; use crate::address::Address; +use crate::transaction_builder; pub struct AptosEntry; @@ -59,6 +60,9 @@ impl CoinEntry for AptosEntry { #[inline] fn sign(&self, _coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput { + let mut builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone()); + builder = builder.sender(Address::from_str(&input.sender).unwrap().inner()).sequence_number(input.sequence_number as u64); + let rax_tx = builder.build(); todo!() } diff --git a/rust/tw_aptos/src/lib.rs b/rust/tw_aptos/src/lib.rs index 9c7cb6a2915..960516c0b16 100644 --- a/rust/tw_aptos/src/lib.rs +++ b/rust/tw_aptos/src/lib.rs @@ -6,6 +6,7 @@ mod serde_helper; pub mod address; +pub mod aptos_move_packages; pub mod constants; pub mod entry; pub mod transaction; diff --git a/rust/tw_aptos/src/transaction.rs b/rust/tw_aptos/src/transaction.rs index 8e4ba90d86f..4b03c8615d9 100644 --- a/rust/tw_aptos/src/transaction.rs +++ b/rust/tw_aptos/src/transaction.rs @@ -5,7 +5,10 @@ // file LICENSE at the root of the source code distribution tree. use move_core_types::account_address::AccountAddress; -use tw_keypair::ed25519::sha512::PublicKey; +use serde::Serialize; +use tw_keypair::ed25519::sha512::{KeyPair, PublicKey}; +use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; +use tw_number::Sign; use crate::transaction_payload::{EntryFunction, TransactionPayload}; #[derive(Clone)] @@ -18,7 +21,7 @@ pub enum TransactionAuthenticator { } /// RawTransaction is the portion of a transaction that a client signs. -#[derive(Clone)] +#[derive(Clone, Serialize)] pub struct RawTransaction { /// Sender's address. sender: AccountAddress, @@ -92,6 +95,20 @@ impl RawTransaction { chain_id, } } + + pub fn sign( + self, + key_pair: KeyPair + ) -> Result { + let mut serialized = bcs::to_bytes(&self).unwrap(); + let mut to_sign = tw_hash::sha3::sha3_256("APTOS::RawTransaction".as_bytes()); + to_sign.extend_from_slice(serialized.as_slice()); + let signed = key_pair.private().sign(to_sign).unwrap(); + Ok(SignedTransaction{ raw_txn: self.clone(), authenticator: TransactionAuthenticator::Ed25519 { + public_key: key_pair.public().clone(), + signature: signed.to_bytes().into_vec(), + } }) + } } /// A transaction that has been signed. diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index 85a69588f7c..23b320bc023 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -5,9 +5,15 @@ // file LICENSE at the root of the source code distribution tree. use move_core_types::account_address::AccountAddress; +use tw_keypair::traits::KeyPairTrait; use crate::constants::{GAS_UNIT_PRICE, MAX_GAS_AMOUNT}; use crate::transaction::RawTransaction; use crate::transaction_payload::{EntryFunction, TransactionPayload}; +use tw_proto::Aptos::Proto; +use tw_proto::Aptos::Proto::mod_SigningInput::OneOftransaction_payload; +use tw_proto::Aptos::Proto::SigningInput; +use crate::address::Address; +use crate::aptos_move_packages::aptos_account_transfer; pub struct TransactionBuilder { sender: Option, @@ -98,6 +104,27 @@ impl TransactionFactory { } } + pub fn new_from_protobuf(input: SigningInput) -> TransactionBuilder { + let factory = TransactionFactory::new(input.chain_id as u8) + .with_gas_unit_price(input.gas_unit_price) + .with_max_gas_amount(input.max_gas_amount) + .with_transaction_expiration_time(input.expiration_timestamp_secs); + let keypair = tw_keypair::ed25519::sha512::KeyPair::try_from(input.private_key.to_vec().as_slice()).unwrap(); + match input.transaction_payload { + OneOftransaction_payload::transfer(transfer) => { + return factory.implicitly_create_user_account_and_transfer(keypair.public(), transfer.amount) + } + OneOftransaction_payload::token_transfer(_) => {} + OneOftransaction_payload::create_account(_) => {} + OneOftransaction_payload::nft_message(_) => {} + OneOftransaction_payload::register_token(_) => {} + OneOftransaction_payload::liquid_staking_message(_) => {} + OneOftransaction_payload::token_transfer_coins(_) => {} + OneOftransaction_payload::None => {} + } + todo!() + } + pub fn with_max_gas_amount(mut self, max_gas_amount: u64) -> Self { self.max_gas_amount = max_gas_amount; self @@ -147,7 +174,7 @@ impl TransactionFactory { public_key: &tw_keypair::ed25519::sha512::PublicKey, amount: u64, ) -> TransactionBuilder { - todo!() + self.payload(aptos_account_transfer(Address::with_ed25519_pubkey(public_key).unwrap().inner(), amount)) } pub fn transfer(&self, to: AccountAddress, amount: u64) -> TransactionBuilder { From df873dbb555deb7fbc67a1726274f53c8110c2b5 Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 24 Oct 2023 14:18:51 -0500 Subject: [PATCH 13/60] feat(aptos): continue implementation --- rust/tw_aptos/src/entry.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rust/tw_aptos/src/entry.rs b/rust/tw_aptos/src/entry.rs index 65674de3c8e..bec0919da03 100644 --- a/rust/tw_aptos/src/entry.rs +++ b/rust/tw_aptos/src/entry.rs @@ -15,6 +15,7 @@ use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; use tw_coin_entry::prefix::NoPrefix; use tw_keypair::tw::PublicKey; +use tw_keypair::ed25519::sha512::KeyPair; use tw_proto::Aptos::Proto; use tw_proto::TxCompiler::Proto as CompilerProto; use crate::address::Address; @@ -61,8 +62,9 @@ impl CoinEntry for AptosEntry { #[inline] fn sign(&self, _coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput { let mut builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone()); - builder = builder.sender(Address::from_str(&input.sender).unwrap().inner()).sequence_number(input.sequence_number as u64); - let rax_tx = builder.build(); + let sender = Address::from_str(&input.sender).unwrap().inner(); + let key_pair = KeyPair::try_from(input.private_key.to_vec().as_slice()).unwrap(); + let raw_tx = builder.sender(sender).sequence_number(input.sequence_number as u64).build().sign(key_pair).unwrap(); todo!() } From 441083dc4d903f44dca0fb6f49306100791b932b Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 24 Oct 2023 17:13:13 -0500 Subject: [PATCH 14/60] feat(aptos): transaction replication work --- rust/tw_aptos/src/transaction.rs | 56 +++++++++++++++++++----- rust/tw_aptos/src/transaction_builder.rs | 37 ++++++++++++---- 2 files changed, 74 insertions(+), 19 deletions(-) diff --git a/rust/tw_aptos/src/transaction.rs b/rust/tw_aptos/src/transaction.rs index 4b03c8615d9..c3b200781e7 100644 --- a/rust/tw_aptos/src/transaction.rs +++ b/rust/tw_aptos/src/transaction.rs @@ -11,15 +11,21 @@ use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; use tw_number::Sign; use crate::transaction_payload::{EntryFunction, TransactionPayload}; -#[derive(Clone)] +#[derive(Clone, Serialize)] pub enum TransactionAuthenticator { /// Single Ed25519 signature Ed25519 { - public_key: PublicKey, + public_key: Vec, signature: Vec, } } +impl TransactionAuthenticator { + pub fn get_signature(&self) -> Vec { + match self { TransactionAuthenticator::Ed25519 { public_key: _public_key, signature } => { signature.clone() } } + } +} + /// RawTransaction is the portion of a transaction that a client signs. #[derive(Clone, Serialize)] pub struct RawTransaction { @@ -98,16 +104,24 @@ impl RawTransaction { pub fn sign( self, - key_pair: KeyPair + key_pair: KeyPair, ) -> Result { let mut serialized = bcs::to_bytes(&self).unwrap(); let mut to_sign = tw_hash::sha3::sha3_256("APTOS::RawTransaction".as_bytes()); to_sign.extend_from_slice(serialized.as_slice()); - let signed = key_pair.private().sign(to_sign).unwrap(); - Ok(SignedTransaction{ raw_txn: self.clone(), authenticator: TransactionAuthenticator::Ed25519 { - public_key: key_pair.public().clone(), + let signed = key_pair.private().sign(to_sign.clone()).unwrap(); + let auth = TransactionAuthenticator::Ed25519 { + public_key: key_pair.public().as_slice().to_vec(), signature: signed.to_bytes().into_vec(), - } }) + }; + let mut encoded = serialized.clone(); + encoded.extend_from_slice(bcs::to_bytes(&auth).unwrap().as_slice()); + Ok(SignedTransaction { + raw_txn: self.clone(), + authenticator: auth.clone(), + raw_txn_bytes: serialized.to_vec(), + encoded, + }) } } @@ -116,14 +130,34 @@ impl RawTransaction { /// A `SignedTransaction` is a single transaction that can be atomically executed. Clients submit /// these to validator nodes, and the validator and executor submits these to the VM. /// -/// **IMPORTANT:** The signature of a `SignedTransaction` is not guaranteed to be verified. For a -/// transaction whose signature is statically guaranteed to be verified, see -/// [`SignatureCheckedTransaction`]. -#[derive(Clone)] +#[derive(Clone, Serialize)] pub struct SignedTransaction { /// The raw transaction raw_txn: RawTransaction, /// Public key and signature to authenticate authenticator: TransactionAuthenticator, + + #[serde(skip_serializing)] + /// Raw txs bytes + raw_txn_bytes: Vec, + + #[serde(skip_serializing)] + /// Encoded bytes to be broadcast + encoded: Vec, +} + +impl SignedTransaction { + pub fn raw_txn(&self) -> &RawTransaction { + &self.raw_txn + } + pub fn authenticator(&self) -> &TransactionAuthenticator { + &self.authenticator + } + pub fn raw_txn_bytes(&self) -> &Vec { + &self.raw_txn_bytes + } + pub fn encoded(&self) -> &Vec { + &self.encoded + } } \ No newline at end of file diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index 23b320bc023..03cba9a7e3c 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -4,6 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use std::str::FromStr; use move_core_types::account_address::AccountAddress; use tw_keypair::traits::KeyPairTrait; use crate::constants::{GAS_UNIT_PRICE, MAX_GAS_AMOUNT}; @@ -112,7 +113,7 @@ impl TransactionFactory { let keypair = tw_keypair::ed25519::sha512::KeyPair::try_from(input.private_key.to_vec().as_slice()).unwrap(); match input.transaction_payload { OneOftransaction_payload::transfer(transfer) => { - return factory.implicitly_create_user_account_and_transfer(keypair.public(), transfer.amount) + return factory.implicitly_create_user_account_and_transfer(AccountAddress::from_str(&transfer.to).unwrap(), transfer.amount); } OneOftransaction_payload::token_transfer(_) => {} OneOftransaction_payload::create_account(_) => {} @@ -171,10 +172,10 @@ impl TransactionFactory { pub fn implicitly_create_user_account_and_transfer( &self, - public_key: &tw_keypair::ed25519::sha512::PublicKey, + to: AccountAddress, amount: u64, ) -> TransactionBuilder { - self.payload(aptos_account_transfer(Address::with_ed25519_pubkey(public_key).unwrap().inner(), amount)) + self.payload(aptos_account_transfer(to, amount)) } pub fn transfer(&self, to: AccountAddress, amount: u64) -> TransactionBuilder { @@ -198,10 +199,30 @@ impl TransactionFactory { } fn expiration_timestamp(&self) -> u64 { - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_secs() - + self.transaction_expiration_time + self.transaction_expiration_time + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + use move_core_types::account_address::AccountAddress; + use tw_encoding::hex; + use crate::transaction_builder::TransactionFactory; + + #[test] + fn test_aptos_account_transfer() { + let factory = TransactionFactory::new(33u8) + .with_max_gas_amount(3296766) + .with_gas_unit_price(100) + .with_transaction_expiration_time(3664390082); + let to = AccountAddress::from_str("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30").unwrap(); + let keypair = tw_keypair::ed25519::sha512::KeyPair::try_from("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec").unwrap(); + let mut builder = factory.implicitly_create_user_account_and_transfer(to.clone(), 1000); + builder = builder.sender(to.clone()).sequence_number(99); + let res = builder.build().sign(keypair).unwrap(); + assert_eq!(hex::encode(res.raw_txn_bytes(), false), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021"); + assert_eq!(hex::encode(res.authenticator().get_signature(), false), "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"); + assert_eq!(hex::encode(res.encoded(), false), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"); } } From 007f74d5d9ffbf61d74922be39b437f50bd07545 Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 24 Oct 2023 17:14:01 -0500 Subject: [PATCH 15/60] feat(aptos): remove useless code --- rust/tw_aptos/src/transaction_builder.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index 03cba9a7e3c..16e47c78644 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -110,7 +110,6 @@ impl TransactionFactory { .with_gas_unit_price(input.gas_unit_price) .with_max_gas_amount(input.max_gas_amount) .with_transaction_expiration_time(input.expiration_timestamp_secs); - let keypair = tw_keypair::ed25519::sha512::KeyPair::try_from(input.private_key.to_vec().as_slice()).unwrap(); match input.transaction_payload { OneOftransaction_payload::transfer(transfer) => { return factory.implicitly_create_user_account_and_transfer(AccountAddress::from_str(&transfer.to).unwrap(), transfer.amount); From 1a34b2fa41160cbf288cd5e619aae5701063ede4 Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 24 Oct 2023 17:23:54 -0500 Subject: [PATCH 16/60] feat(aptos): remove useless code --- rust/tw_aptos/src/transaction_builder.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index 16e47c78644..11f2bc39134 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -7,13 +7,11 @@ use std::str::FromStr; use move_core_types::account_address::AccountAddress; use tw_keypair::traits::KeyPairTrait; +use tw_proto::Aptos::Proto::mod_SigningInput::OneOftransaction_payload; use crate::constants::{GAS_UNIT_PRICE, MAX_GAS_AMOUNT}; use crate::transaction::RawTransaction; use crate::transaction_payload::{EntryFunction, TransactionPayload}; -use tw_proto::Aptos::Proto; -use tw_proto::Aptos::Proto::mod_SigningInput::OneOftransaction_payload; use tw_proto::Aptos::Proto::SigningInput; -use crate::address::Address; use crate::aptos_move_packages::aptos_account_transfer; pub struct TransactionBuilder { @@ -211,12 +209,12 @@ mod tests { #[test] fn test_aptos_account_transfer() { + let to = AccountAddress::from_str("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30").unwrap(); + let keypair = tw_keypair::ed25519::sha512::KeyPair::try_from("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec").unwrap(); let factory = TransactionFactory::new(33u8) .with_max_gas_amount(3296766) .with_gas_unit_price(100) .with_transaction_expiration_time(3664390082); - let to = AccountAddress::from_str("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30").unwrap(); - let keypair = tw_keypair::ed25519::sha512::KeyPair::try_from("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec").unwrap(); let mut builder = factory.implicitly_create_user_account_and_transfer(to.clone(), 1000); builder = builder.sender(to.clone()).sequence_number(99); let res = builder.build().sign(keypair).unwrap(); From 1abb8aa7da1f1d159619633040d65e90bac52399 Mon Sep 17 00:00:00 2001 From: Milerius Date: Wed, 25 Oct 2023 16:51:59 -0500 Subject: [PATCH 17/60] feat(aptos): add aptos signer.rs --- rust/tw_aptos/src/address.rs | 18 +++++++++++- rust/tw_aptos/src/entry.rs | 7 ++--- rust/tw_aptos/src/lib.rs | 2 ++ rust/tw_aptos/src/signer.rs | 48 ++++++++++++++++++++++++++++++++ rust/tw_aptos/src/transaction.rs | 18 +++++++++++- 5 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 rust/tw_aptos/src/signer.rs diff --git a/rust/tw_aptos/src/address.rs b/rust/tw_aptos/src/address.rs index 2de65e751ea..a1c20b50171 100644 --- a/rust/tw_aptos/src/address.rs +++ b/rust/tw_aptos/src/address.rs @@ -8,11 +8,27 @@ use std::fmt::{Display, Formatter}; use std::str::FromStr; use move_core_types::account_address::{AccountAddress, AccountAddressParseError}; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::AddressError; +use tw_coin_entry::error::{AddressError, AddressResult}; use tw_keypair::ed25519; use tw_memory::Data; use tw_hash::sha3::sha3_256; + +pub trait AptosAddress: FromStr + Into
{ + /// Tries to parse an address from the string representation. + /// Returns `Ok(None)` if the given `s` string is empty. + #[inline] + fn from_str_optional(s: &str) -> AddressResult> { + if s.is_empty() { + return Ok(None); + } + + Self::from_str(s).map(Some) + } +} + +impl AptosAddress for Address {} + #[repr(u8)] pub enum Scheme { Ed25519 = 0, diff --git a/rust/tw_aptos/src/entry.rs b/rust/tw_aptos/src/entry.rs index bec0919da03..6665d526772 100644 --- a/rust/tw_aptos/src/entry.rs +++ b/rust/tw_aptos/src/entry.rs @@ -19,6 +19,7 @@ use tw_keypair::ed25519::sha512::KeyPair; use tw_proto::Aptos::Proto; use tw_proto::TxCompiler::Proto as CompilerProto; use crate::address::Address; +use crate::signer::{AptosContext, Signer, StandardAptosContext}; use crate::transaction_builder; @@ -61,11 +62,7 @@ impl CoinEntry for AptosEntry { #[inline] fn sign(&self, _coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput { - let mut builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone()); - let sender = Address::from_str(&input.sender).unwrap().inner(); - let key_pair = KeyPair::try_from(input.private_key.to_vec().as_slice()).unwrap(); - let raw_tx = builder.sender(sender).sequence_number(input.sequence_number as u64).build().sign(key_pair).unwrap(); - todo!() + Signer::::sign_proto(input) } #[inline] diff --git a/rust/tw_aptos/src/lib.rs b/rust/tw_aptos/src/lib.rs index 960516c0b16..b3235f83aac 100644 --- a/rust/tw_aptos/src/lib.rs +++ b/rust/tw_aptos/src/lib.rs @@ -9,6 +9,8 @@ pub mod address; pub mod aptos_move_packages; pub mod constants; pub mod entry; + +pub mod signer; pub mod transaction; pub mod transaction_builder; pub mod transaction_payload; diff --git a/rust/tw_aptos/src/signer.rs b/rust/tw_aptos/src/signer.rs new file mode 100644 index 00000000000..a9d017ad720 --- /dev/null +++ b/rust/tw_aptos/src/signer.rs @@ -0,0 +1,48 @@ +use std::borrow::Cow; +use std::marker::PhantomData; +use std::str::FromStr; +use tw_coin_entry::error::SigningResult; +use tw_coin_entry::signing_output_error; +use tw_keypair::ed25519; +use tw_proto::Aptos::Proto; +use tw_proto::Aptos::Proto::TransactionAuthenticator; +use crate::address::{Address, AptosAddress}; +use crate::transaction_builder; + +pub trait AptosContext { + type Address: AptosAddress; +} + +#[derive(Default)] +pub struct StandardAptosContext; + +impl AptosContext for StandardAptosContext { + type Address = Address; +} + +pub struct Signer { + _phantom: PhantomData, +} + +impl Signer { + #[inline] + pub fn sign_proto(input: Proto::SigningInput<'_>) -> Proto::SigningOutput<'static> { + Self::sign_proto_impl(input) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + fn sign_proto_impl( + input: Proto::SigningInput<'_>, + ) -> SigningResult> { + let key_pair = ed25519::sha512::KeyPair::try_from(input.private_key.as_ref())?; + let mut builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone()); + let sender = Address::from_str(&input.sender)?; + let raw_tx = builder.sender(sender.inner()).sequence_number(input.sequence_number as u64).build().sign(key_pair)?; + Ok(Proto::SigningOutput { + raw_txn: raw_tx.raw_txn_bytes().clone().into(), + encoded: raw_tx.encoded().clone().into(), + authenticator: Some((*raw_tx.authenticator()).clone().into()), + ..Proto::SigningOutput::default() + }) + } +} \ No newline at end of file diff --git a/rust/tw_aptos/src/transaction.rs b/rust/tw_aptos/src/transaction.rs index c3b200781e7..5058a35254b 100644 --- a/rust/tw_aptos/src/transaction.rs +++ b/rust/tw_aptos/src/transaction.rs @@ -4,11 +4,14 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use std::borrow::Cow; use move_core_types::account_address::AccountAddress; use serde::Serialize; +use tw_coin_entry::error::SigningResult; use tw_keypair::ed25519::sha512::{KeyPair, PublicKey}; use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; use tw_number::Sign; +use tw_proto::Aptos::Proto; use crate::transaction_payload::{EntryFunction, TransactionPayload}; #[derive(Clone, Serialize)] @@ -20,10 +23,23 @@ pub enum TransactionAuthenticator { } } +impl From for Proto::TransactionAuthenticator<'_> { + fn from(from: TransactionAuthenticator) -> Self { + Proto::TransactionAuthenticator { + signature: Cow::from(from.get_signature()), + public_key: Cow::from(from.get_public_key()), + } + } +} + impl TransactionAuthenticator { pub fn get_signature(&self) -> Vec { match self { TransactionAuthenticator::Ed25519 { public_key: _public_key, signature } => { signature.clone() } } } + + pub fn get_public_key(&self) -> Vec { + match self { TransactionAuthenticator::Ed25519 { public_key, signature: _signature } => { public_key.clone() } } + } } /// RawTransaction is the portion of a transaction that a client signs. @@ -105,7 +121,7 @@ impl RawTransaction { pub fn sign( self, key_pair: KeyPair, - ) -> Result { + ) -> SigningResult { let mut serialized = bcs::to_bytes(&self).unwrap(); let mut to_sign = tw_hash::sha3::sha3_256("APTOS::RawTransaction".as_bytes()); to_sign.extend_from_slice(serialized.as_slice()); From 3fba52cab999def0d2d6b67074a206408da3981b Mon Sep 17 00:00:00 2001 From: Milerius Date: Mon, 30 Oct 2023 05:01:55 -0500 Subject: [PATCH 18/60] feat(aptos): remove code --- rust/tw_aptos/src/signer.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/rust/tw_aptos/src/signer.rs b/rust/tw_aptos/src/signer.rs index a9d017ad720..82e388c83b2 100644 --- a/rust/tw_aptos/src/signer.rs +++ b/rust/tw_aptos/src/signer.rs @@ -1,11 +1,9 @@ -use std::borrow::Cow; use std::marker::PhantomData; use std::str::FromStr; use tw_coin_entry::error::SigningResult; use tw_coin_entry::signing_output_error; use tw_keypair::ed25519; use tw_proto::Aptos::Proto; -use tw_proto::Aptos::Proto::TransactionAuthenticator; use crate::address::{Address, AptosAddress}; use crate::transaction_builder; From 7deb6b923a619e6bca40f8888d6b1627de2f9ba9 Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 7 Nov 2023 07:34:40 -0500 Subject: [PATCH 19/60] feat(aptos): add to_json for payload --- rust/Cargo.lock | 5 ++- rust/tw_aptos/Cargo.toml | 1 + rust/tw_aptos/src/aptos_move_packages.rs | 5 ++- rust/tw_aptos/src/transaction_payload.rs | 55 ++++++++++++++++++++++-- 4 files changed, 59 insertions(+), 7 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index fbc013b3f4a..5f825710ed2 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1358,9 +1358,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -1628,6 +1628,7 @@ dependencies = [ "move-core-types 0.0.4 (git+https://github.com/move-language/move?rev=ea70797099baea64f05194a918cebd69ed02b285)", "serde", "serde_bytes", + "serde_json", "tw_coin_entry", "tw_encoding", "tw_hash", diff --git a/rust/tw_aptos/Cargo.toml b/rust/tw_aptos/Cargo.toml index 6753e185550..ae42e57286d 100644 --- a/rust/tw_aptos/Cargo.toml +++ b/rust/tw_aptos/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] +serde_json = "1.0.108" tw_coin_entry = { path = "../tw_coin_entry" } tw_keypair = { path = "../tw_keypair" } tw_proto = { path = "../tw_proto" } diff --git a/rust/tw_aptos/src/aptos_move_packages.rs b/rust/tw_aptos/src/aptos_move_packages.rs index c9f82e91bb4..d9b2a6d4270 100644 --- a/rust/tw_aptos/src/aptos_move_packages.rs +++ b/rust/tw_aptos/src/aptos_move_packages.rs @@ -7,6 +7,7 @@ use move_core_types::account_address::AccountAddress; use move_core_types::ident_str; use move_core_types::language_storage::ModuleId; +use serde_json::json; use crate::transaction_payload::{EntryFunction, TransactionPayload}; pub fn aptos_account_transfer(to: AccountAddress, amount: u64) -> TransactionPayload { @@ -14,12 +15,12 @@ pub fn aptos_account_transfer(to: AccountAddress, amount: u64) -> TransactionPay ModuleId::new( AccountAddress::new([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, - ]), + 0, 0, 0, 1, ]), ident_str!("aptos_account").to_owned(), ), ident_str!("transfer").to_owned(), vec![], vec![bcs::to_bytes(&to).unwrap(), bcs::to_bytes(&amount).unwrap()], + json!([to.to_hex_literal(), amount.to_string()]), )) } \ No newline at end of file diff --git a/rust/tw_aptos/src/transaction_payload.rs b/rust/tw_aptos/src/transaction_payload.rs index 7160ed306eb..4c957b68499 100644 --- a/rust/tw_aptos/src/transaction_payload.rs +++ b/rust/tw_aptos/src/transaction_payload.rs @@ -7,18 +7,45 @@ use move_core_types::identifier::Identifier; use move_core_types::language_storage::{ModuleId, TypeTag}; use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; use crate::{serde_helper::vec_bytes}; -#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct EntryFunction { module: ModuleId, function: Identifier, ty_args: Vec, #[serde(with = "vec_bytes")] args: Vec>, + #[serde(skip_serializing)] + json_args: Value } -#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] +impl EntryFunction { + fn to_json(&self) -> Value { + // Create a JSON array from the `ty_args` field by filtering and mapping + // the items that match `TypeTag::Struct` to their string representation. + let type_arguments: Value = self.ty_args.iter() + .filter_map(|item| { + if let TypeTag::Struct(value) = item { + Some(json!(value.to_string())) + } else { + None + } + }) + .collect(); + + // Construct the final JSON value + json!({ + "type": "entry_function_payload", + "function": format!("{}::{}", self.module.short_str_lossless(), self.function.clone().into_string()), + "arguments": self.json_args, + "type_arguments": type_arguments + }) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub enum TransactionPayload { Script, ModuleBundle, @@ -26,18 +53,30 @@ pub enum TransactionPayload { EntryFunction(EntryFunction), } +impl TransactionPayload { + fn to_json(&self) -> Value { + match self { + TransactionPayload::Script => { Value::default() } + TransactionPayload::ModuleBundle => { Value::default() } + TransactionPayload::EntryFunction(entry) => { json!({"payload": entry.to_json()}) } + } + } +} + impl EntryFunction { pub fn new( module: ModuleId, function: Identifier, ty_args: Vec, args: Vec>, + json_args: Value, ) -> Self { EntryFunction { module, function, ty_args, args, + json_args } } } @@ -49,6 +88,7 @@ mod tests { use move_core_types::account_address::AccountAddress; use move_core_types::identifier::Identifier; use move_core_types::language_storage::{ModuleId, TypeTag}; + use serde_json::{json, Value}; use tw_encoding::hex; use crate::transaction_payload::{EntryFunction, TransactionPayload}; @@ -60,9 +100,18 @@ mod tests { let module = ModuleId::new(AccountAddress::ONE, Identifier::from_str("coin").unwrap()); let function = Identifier::from_str("transfer").unwrap(); let type_tag = vec![TypeTag::from_str("0x1::aptos_coin::AptosCoin").unwrap()]; - let entry = EntryFunction::new(module, function, type_tag, args); + let entry = EntryFunction::new(module, function, type_tag, args, json!([addr.to_hex_literal(), amount.to_string()])); let tp = TransactionPayload::EntryFunction(entry); let serialized = bcs::to_bytes(&tp).unwrap(); assert_eq!(hex::encode(serialized, false), "02000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b08e803000000000000"); + let payload_value: Value = json!({ + "payload": { + "function": "0x1::coin::transfer", + "type": "entry_function_payload", + "arguments": ["0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", "1000"], + "type_arguments": ["0x1::aptos_coin::AptosCoin"] + } + }); + assert_eq!(tp.to_json(), payload_value); } } \ No newline at end of file From bcdb69f678634a903adae6d20bacc12126e7d24b Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 7 Nov 2023 07:36:32 -0500 Subject: [PATCH 20/60] feat(aptos): reduce warnings --- rust/tw_aptos/src/entry.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/rust/tw_aptos/src/entry.rs b/rust/tw_aptos/src/entry.rs index 6665d526772..b0739d5d695 100644 --- a/rust/tw_aptos/src/entry.rs +++ b/rust/tw_aptos/src/entry.rs @@ -4,7 +4,6 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use std::fmt::{Display}; use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; @@ -15,12 +14,10 @@ use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; use tw_coin_entry::prefix::NoPrefix; use tw_keypair::tw::PublicKey; -use tw_keypair::ed25519::sha512::KeyPair; use tw_proto::Aptos::Proto; use tw_proto::TxCompiler::Proto as CompilerProto; use crate::address::Address; use crate::signer::{AptosContext, Signer, StandardAptosContext}; -use crate::transaction_builder; pub struct AptosEntry; From 30b57d1cf8d4cc9a100dcf31005882e4f2aa61c5 Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 7 Nov 2023 08:39:10 -0500 Subject: [PATCH 21/60] feat(aptos): continue with to_json --- rust/tw_aptos/Cargo.toml | 1 + rust/tw_aptos/src/entry.rs | 2 +- rust/tw_aptos/src/transaction.rs | 33 +++++++++++++++++++++--- rust/tw_aptos/src/transaction_builder.rs | 21 +++++++++++++++ rust/tw_aptos/src/transaction_payload.rs | 12 +++------ 5 files changed, 56 insertions(+), 13 deletions(-) diff --git a/rust/tw_aptos/Cargo.toml b/rust/tw_aptos/Cargo.toml index ae42e57286d..f0b683cdace 100644 --- a/rust/tw_aptos/Cargo.toml +++ b/rust/tw_aptos/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] serde_json = "1.0.108" tw_coin_entry = { path = "../tw_coin_entry" } +tw_encoding = { path = "../tw_encoding"} tw_keypair = { path = "../tw_keypair" } tw_proto = { path = "../tw_proto" } tw_number = { path = "../tw_number" } diff --git a/rust/tw_aptos/src/entry.rs b/rust/tw_aptos/src/entry.rs index b0739d5d695..b5d5d89fe8e 100644 --- a/rust/tw_aptos/src/entry.rs +++ b/rust/tw_aptos/src/entry.rs @@ -17,7 +17,7 @@ use tw_keypair::tw::PublicKey; use tw_proto::Aptos::Proto; use tw_proto::TxCompiler::Proto as CompilerProto; use crate::address::Address; -use crate::signer::{AptosContext, Signer, StandardAptosContext}; +use crate::signer::{Signer, StandardAptosContext}; pub struct AptosEntry; diff --git a/rust/tw_aptos/src/transaction.rs b/rust/tw_aptos/src/transaction.rs index 5058a35254b..24fb3853664 100644 --- a/rust/tw_aptos/src/transaction.rs +++ b/rust/tw_aptos/src/transaction.rs @@ -7,10 +7,11 @@ use std::borrow::Cow; use move_core_types::account_address::AccountAddress; use serde::Serialize; +use serde_json::{json, Value}; use tw_coin_entry::error::SigningResult; use tw_keypair::ed25519::sha512::{KeyPair, PublicKey}; use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; -use tw_number::Sign; +use tw_encoding::hex::encode; use tw_proto::Aptos::Proto; use crate::transaction_payload::{EntryFunction, TransactionPayload}; @@ -40,6 +41,17 @@ impl TransactionAuthenticator { pub fn get_public_key(&self) -> Vec { match self { TransactionAuthenticator::Ed25519 { public_key, signature: _signature } => { public_key.clone() } } } + + pub fn to_json(&self) -> Value { + match self { + TransactionAuthenticator::Ed25519 { public_key, signature } => { + + json!({"public_key": encode(&public_key, true), + "signature": encode(&signature, true), + "type": "ed25519_signature"}) + } + } + } } /// RawTransaction is the portion of a transaction that a client signs. @@ -71,7 +83,6 @@ pub struct RawTransaction { /// Chain ID of the Aptos network this transaction is intended for. chain_id: u8, } - impl RawTransaction { /// Create a new `RawTransaction` with a payload. /// @@ -139,6 +150,17 @@ impl RawTransaction { encoded, }) } + + pub fn to_json(&self) -> Value { + json!({ + "expiration_timestamp_secs": self.expiration_timestamp_secs.to_string(), + "gas_unit_price": self.gas_unit_price.to_string(), + "max_gas_amount": self.max_gas_amount.to_string(), + "payload": self.payload.to_json(), + "sender": self.sender.to_hex_literal(), + "sequence_number": self.sequence_number.to_string() + }) + } } /// A transaction that has been signed. @@ -162,7 +184,6 @@ pub struct SignedTransaction { /// Encoded bytes to be broadcast encoded: Vec, } - impl SignedTransaction { pub fn raw_txn(&self) -> &RawTransaction { &self.raw_txn @@ -176,4 +197,10 @@ impl SignedTransaction { pub fn encoded(&self) -> &Vec { &self.encoded } + + pub fn to_json(&self) -> Value { + let mut json_value = self.raw_txn.to_json(); + json_value.as_object_mut().unwrap().insert("signature".to_string(), self.authenticator.to_json()); + json_value + } } \ No newline at end of file diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index 11f2bc39134..394e09a5bbd 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -204,6 +204,7 @@ impl TransactionFactory { mod tests { use std::str::FromStr; use move_core_types::account_address::AccountAddress; + use serde_json::{Value}; use tw_encoding::hex; use crate::transaction_builder::TransactionFactory; @@ -221,5 +222,25 @@ mod tests { assert_eq!(hex::encode(res.raw_txn_bytes(), false), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021"); assert_eq!(hex::encode(res.authenticator().get_signature(), false), "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"); assert_eq!(hex::encode(res.encoded(), false), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"); + let json_literal = r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": ["0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], + "function": "0x1::aptos_account::transfer", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "99", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", + "type": "ed25519_signature" + } + }"#; + assert_eq!(res.to_json(), Value::from_str(json_literal).unwrap()); + } } diff --git a/rust/tw_aptos/src/transaction_payload.rs b/rust/tw_aptos/src/transaction_payload.rs index 4c957b68499..85c9b7bffc6 100644 --- a/rust/tw_aptos/src/transaction_payload.rs +++ b/rust/tw_aptos/src/transaction_payload.rs @@ -27,11 +27,7 @@ impl EntryFunction { // the items that match `TypeTag::Struct` to their string representation. let type_arguments: Value = self.ty_args.iter() .filter_map(|item| { - if let TypeTag::Struct(value) = item { - Some(json!(value.to_string())) - } else { - None - } + Some(json!(item.to_string())) }) .collect(); @@ -54,11 +50,11 @@ pub enum TransactionPayload { } impl TransactionPayload { - fn to_json(&self) -> Value { + pub fn to_json(&self) -> Value { match self { TransactionPayload::Script => { Value::default() } TransactionPayload::ModuleBundle => { Value::default() } - TransactionPayload::EntryFunction(entry) => { json!({"payload": entry.to_json()}) } + TransactionPayload::EntryFunction(entry) => { entry.to_json() } } } } @@ -105,12 +101,10 @@ mod tests { let serialized = bcs::to_bytes(&tp).unwrap(); assert_eq!(hex::encode(serialized, false), "02000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b08e803000000000000"); let payload_value: Value = json!({ - "payload": { "function": "0x1::coin::transfer", "type": "entry_function_payload", "arguments": ["0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", "1000"], "type_arguments": ["0x1::aptos_coin::AptosCoin"] - } }); assert_eq!(tp.to_json(), payload_value); } From d780899dff20a778efc66fae1c07741d8698efd5 Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 7 Nov 2023 08:42:24 -0500 Subject: [PATCH 22/60] feat(aptos): reduce warnings --- rust/tw_aptos/src/entry.rs | 8 ++++---- rust/tw_aptos/src/signer.rs | 2 +- rust/tw_aptos/src/transaction.rs | 4 ++-- rust/tw_aptos/src/transaction_builder.rs | 7 +++---- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/rust/tw_aptos/src/entry.rs b/rust/tw_aptos/src/entry.rs index b5d5d89fe8e..00a69082d8d 100644 --- a/rust/tw_aptos/src/entry.rs +++ b/rust/tw_aptos/src/entry.rs @@ -66,7 +66,7 @@ impl CoinEntry for AptosEntry { fn preimage_hashes( &self, _coin: &dyn CoinContext, - input: Self::SigningInput<'_>, + _input: Self::SigningInput<'_>, ) -> Self::PreSigningOutput { todo!() } @@ -75,9 +75,9 @@ impl CoinEntry for AptosEntry { fn compile( &self, _coin: &dyn CoinContext, - input: Self::SigningInput<'_>, - signatures: Vec, - public_keys: Vec, + _input: Self::SigningInput<'_>, + _signatures: Vec, + _public_keys: Vec, ) -> Self::SigningOutput { todo!() } diff --git a/rust/tw_aptos/src/signer.rs b/rust/tw_aptos/src/signer.rs index 82e388c83b2..f21d3c22203 100644 --- a/rust/tw_aptos/src/signer.rs +++ b/rust/tw_aptos/src/signer.rs @@ -33,7 +33,7 @@ impl Signer { input: Proto::SigningInput<'_>, ) -> SigningResult> { let key_pair = ed25519::sha512::KeyPair::try_from(input.private_key.as_ref())?; - let mut builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone()); + let builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone()); let sender = Address::from_str(&input.sender)?; let raw_tx = builder.sender(sender.inner()).sequence_number(input.sequence_number as u64).build().sign(key_pair)?; Ok(Proto::SigningOutput { diff --git a/rust/tw_aptos/src/transaction.rs b/rust/tw_aptos/src/transaction.rs index 24fb3853664..bb748a38839 100644 --- a/rust/tw_aptos/src/transaction.rs +++ b/rust/tw_aptos/src/transaction.rs @@ -9,7 +9,7 @@ use move_core_types::account_address::AccountAddress; use serde::Serialize; use serde_json::{json, Value}; use tw_coin_entry::error::SigningResult; -use tw_keypair::ed25519::sha512::{KeyPair, PublicKey}; +use tw_keypair::ed25519::sha512::KeyPair; use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; use tw_encoding::hex::encode; use tw_proto::Aptos::Proto; @@ -133,7 +133,7 @@ impl RawTransaction { self, key_pair: KeyPair, ) -> SigningResult { - let mut serialized = bcs::to_bytes(&self).unwrap(); + let serialized = bcs::to_bytes(&self).unwrap(); let mut to_sign = tw_hash::sha3::sha3_256("APTOS::RawTransaction".as_bytes()); to_sign.extend_from_slice(serialized.as_slice()); let signed = key_pair.private().sign(to_sign.clone()).unwrap(); diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index 394e09a5bbd..b275f8e11c1 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -6,7 +6,6 @@ use std::str::FromStr; use move_core_types::account_address::AccountAddress; -use tw_keypair::traits::KeyPairTrait; use tw_proto::Aptos::Proto::mod_SigningInput::OneOftransaction_payload; use crate::constants::{GAS_UNIT_PRICE, MAX_GAS_AMOUNT}; use crate::transaction::RawTransaction; @@ -163,7 +162,7 @@ impl TransactionFactory { self.payload(TransactionPayload::EntryFunction(func)) } - pub fn create_user_account(&self, public_key: &tw_keypair::ed25519::sha512::PublicKey) -> TransactionBuilder { + pub fn create_user_account(&self, _public_key: &tw_keypair::ed25519::sha512::PublicKey) -> TransactionBuilder { todo!() } @@ -175,11 +174,11 @@ impl TransactionFactory { self.payload(aptos_account_transfer(to, amount)) } - pub fn transfer(&self, to: AccountAddress, amount: u64) -> TransactionBuilder { + pub fn transfer(&self, _to: AccountAddress, _amount: u64) -> TransactionBuilder { todo!() } - pub fn account_transfer(&self, to: AccountAddress, amount: u64) -> TransactionBuilder { + pub fn account_transfer(&self, _to: AccountAddress, _amount: u64) -> TransactionBuilder { todo!() } From deab38b670aaef0fde5c887625ae5bd950d10ace Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 7 Nov 2023 10:16:02 -0500 Subject: [PATCH 23/60] feat(aptos): simplify tx unit test --- rust/tw_aptos/src/aptos_move_packages.rs | 16 +++ rust/tw_aptos/src/transaction_builder.rs | 127 +++++++++++++++++------ 2 files changed, 111 insertions(+), 32 deletions(-) diff --git a/rust/tw_aptos/src/aptos_move_packages.rs b/rust/tw_aptos/src/aptos_move_packages.rs index d9b2a6d4270..49c7974eb41 100644 --- a/rust/tw_aptos/src/aptos_move_packages.rs +++ b/rust/tw_aptos/src/aptos_move_packages.rs @@ -23,4 +23,20 @@ pub fn aptos_account_transfer(to: AccountAddress, amount: u64) -> TransactionPay vec![bcs::to_bytes(&to).unwrap(), bcs::to_bytes(&amount).unwrap()], json!([to.to_hex_literal(), amount.to_string()]), )) +} + +pub fn aptos_account_create_account(auth_key: AccountAddress) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ]), + ident_str!("aptos_account").to_owned(), + ), + ident_str!("create_account").to_owned(), + vec![], + vec![bcs::to_bytes(&auth_key).unwrap()], + json!([auth_key.to_hex_literal()]), + )) } \ No newline at end of file diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index b275f8e11c1..653afb138de 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -11,7 +11,7 @@ use crate::constants::{GAS_UNIT_PRICE, MAX_GAS_AMOUNT}; use crate::transaction::RawTransaction; use crate::transaction_payload::{EntryFunction, TransactionPayload}; use tw_proto::Aptos::Proto::SigningInput; -use crate::aptos_move_packages::aptos_account_transfer; +use crate::aptos_move_packages::{aptos_account_create_account, aptos_account_transfer}; pub struct TransactionBuilder { sender: Option, @@ -162,8 +162,8 @@ impl TransactionFactory { self.payload(TransactionPayload::EntryFunction(func)) } - pub fn create_user_account(&self, _public_key: &tw_keypair::ed25519::sha512::PublicKey) -> TransactionBuilder { - todo!() + pub fn create_user_account(&self, to: AccountAddress) -> TransactionBuilder { + self.payload(aptos_account_create_account(to)) } pub fn implicitly_create_user_account_and_transfer( @@ -203,43 +203,106 @@ impl TransactionFactory { mod tests { use std::str::FromStr; use move_core_types::account_address::AccountAddress; - use serde_json::{Value}; use tw_encoding::hex; use crate::transaction_builder::TransactionFactory; - #[test] - fn test_aptos_account_transfer() { - let to = AccountAddress::from_str("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30").unwrap(); - let keypair = tw_keypair::ed25519::sha512::KeyPair::try_from("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec").unwrap(); + fn setup_transaction( + sender: &str, + keypair_str: &str, + transaction_type: &str, + sequence_number: u64, + to: &str, + expected_raw_txn_bytes_str: &str, + expected_signature_str: &str, + expected_encoded_txn_str: &str, + json_literal: &str, + ) { + let account_address = AccountAddress::from_str(sender).unwrap(); + let to = AccountAddress::from_str(to).unwrap(); + let keypair = tw_keypair::ed25519::sha512::KeyPair::try_from(keypair_str).unwrap(); let factory = TransactionFactory::new(33u8) .with_max_gas_amount(3296766) .with_gas_unit_price(100) .with_transaction_expiration_time(3664390082); - let mut builder = factory.implicitly_create_user_account_and_transfer(to.clone(), 1000); - builder = builder.sender(to.clone()).sequence_number(99); + + let mut builder = match transaction_type { + "transfer" => factory.implicitly_create_user_account_and_transfer(to.clone(), 1000), + "create_account" => { factory.create_user_account(to.clone()) }, + _ => panic!("Unsupported transaction type"), + }; + + builder = builder.sender(account_address).sequence_number(sequence_number); let res = builder.build().sign(keypair).unwrap(); - assert_eq!(hex::encode(res.raw_txn_bytes(), false), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021"); - assert_eq!(hex::encode(res.authenticator().get_signature(), false), "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"); - assert_eq!(hex::encode(res.encoded(), false), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"); - let json_literal = r#"{ - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "3296766", - "payload": { - "arguments": ["0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], - "function": "0x1::aptos_account::transfer", - "type": "entry_function_payload", - "type_arguments": [] - }, - "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "99", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0x5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", - "type": "ed25519_signature" - } - }"#; - assert_eq!(res.to_json(), Value::from_str(json_literal).unwrap()); + assert_eq!(hex::encode(res.raw_txn_bytes(), false), expected_raw_txn_bytes_str); + assert_eq!(hex::encode(res.authenticator().get_signature(), false), expected_signature_str); + assert_eq!(hex::encode(res.encoded(), false), expected_encoded_txn_str); + + let json_value: serde_json::Value = serde_json::from_str(json_literal).unwrap(); + assert_eq!(res.to_json(), json_value); + } + + #[test] + fn test_aptos_account_transfer() { + setup_transaction( + "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", + "transfer", + 99, + "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021", + "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": ["0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], + "function": "0x1::aptos_account::transfer", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "99", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", + "type": "ed25519_signature" + } + }"# + ); + } + + #[test] + fn test_aptos_create_account() { + setup_transaction( + "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "create_account", + 0, // Sequence number + "0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e", + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada0000000021", // Expected raw transaction bytes + "fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", // Expected signature + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": ["0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e"], + "function": "0x1::aptos_account::create_account", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "0", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0xfcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", + "type": "ed25519_signature" + } + }"# // Expected JSON literal + ); } } From 827e8997c3304ad0db7fc9d30f0b6817bd6e6ee7 Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 7 Nov 2023 10:20:17 -0500 Subject: [PATCH 24/60] feat(aptos): add create_account to new_from_protobuf --- rust/tw_aptos/src/transaction_builder.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index 653afb138de..a641d92a141 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -112,7 +112,9 @@ impl TransactionFactory { return factory.implicitly_create_user_account_and_transfer(AccountAddress::from_str(&transfer.to).unwrap(), transfer.amount); } OneOftransaction_payload::token_transfer(_) => {} - OneOftransaction_payload::create_account(_) => {} + OneOftransaction_payload::create_account(create_account) => { + return factory.create_user_account(AccountAddress::from_str(&create_account.auth_key).unwrap()); + } OneOftransaction_payload::nft_message(_) => {} OneOftransaction_payload::register_token(_) => {} OneOftransaction_payload::liquid_staking_message(_) => {} From 9e8368d621b542169692235136807769a2291bdd Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 7 Nov 2023 10:56:51 -0500 Subject: [PATCH 25/60] feat(aptos): add aptos_coin_transfer --- rust/tw_aptos/src/aptos_move_packages.rs | 18 ++++++- rust/tw_aptos/src/transaction_builder.rs | 63 +++++++++++++++++++++--- 2 files changed, 74 insertions(+), 7 deletions(-) diff --git a/rust/tw_aptos/src/aptos_move_packages.rs b/rust/tw_aptos/src/aptos_move_packages.rs index 49c7974eb41..c59bc073b69 100644 --- a/rust/tw_aptos/src/aptos_move_packages.rs +++ b/rust/tw_aptos/src/aptos_move_packages.rs @@ -6,7 +6,7 @@ use move_core_types::account_address::AccountAddress; use move_core_types::ident_str; -use move_core_types::language_storage::ModuleId; +use move_core_types::language_storage::{ModuleId, TypeTag}; use serde_json::json; use crate::transaction_payload::{EntryFunction, TransactionPayload}; @@ -39,4 +39,20 @@ pub fn aptos_account_create_account(auth_key: AccountAddress) -> TransactionPayl vec![bcs::to_bytes(&auth_key).unwrap()], json!([auth_key.to_hex_literal()]), )) +} + +pub fn coin_transfer(coin_type: TypeTag, to: AccountAddress, amount: u64) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ]), + ident_str!("coin").to_owned(), + ), + ident_str!("transfer").to_owned(), + vec![coin_type], + vec![bcs::to_bytes(&to).unwrap(), bcs::to_bytes(&amount).unwrap()], + json!([to.to_hex_literal(), amount.to_string()]), + )) } \ No newline at end of file diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index a641d92a141..4005a198808 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -6,12 +6,13 @@ use std::str::FromStr; use move_core_types::account_address::AccountAddress; +use move_core_types::language_storage::TypeTag; use tw_proto::Aptos::Proto::mod_SigningInput::OneOftransaction_payload; use crate::constants::{GAS_UNIT_PRICE, MAX_GAS_AMOUNT}; use crate::transaction::RawTransaction; use crate::transaction_payload::{EntryFunction, TransactionPayload}; use tw_proto::Aptos::Proto::SigningInput; -use crate::aptos_move_packages::{aptos_account_create_account, aptos_account_transfer}; +use crate::aptos_move_packages::{aptos_account_create_account, aptos_account_transfer, coin_transfer}; pub struct TransactionBuilder { sender: Option, @@ -176,6 +177,10 @@ impl TransactionFactory { self.payload(aptos_account_transfer(to, amount)) } + pub fn coins_transfer(&self, to: AccountAddress, + amount: u64, coin_type: TypeTag) -> TransactionBuilder { + self.payload(coin_transfer(coin_type, to, amount)) + } pub fn transfer(&self, _to: AccountAddress, _amount: u64) -> TransactionBuilder { todo!() } @@ -205,6 +210,7 @@ impl TransactionFactory { mod tests { use std::str::FromStr; use move_core_types::account_address::AccountAddress; + use move_core_types::language_storage::TypeTag; use tw_encoding::hex; use crate::transaction_builder::TransactionFactory; @@ -214,22 +220,26 @@ mod tests { transaction_type: &str, sequence_number: u64, to: &str, + chain_id: u8, + amount: u64, expected_raw_txn_bytes_str: &str, expected_signature_str: &str, expected_encoded_txn_str: &str, json_literal: &str, + coin_type: Option, ) { let account_address = AccountAddress::from_str(sender).unwrap(); let to = AccountAddress::from_str(to).unwrap(); let keypair = tw_keypair::ed25519::sha512::KeyPair::try_from(keypair_str).unwrap(); - let factory = TransactionFactory::new(33u8) + let factory = TransactionFactory::new(chain_id) .with_max_gas_amount(3296766) .with_gas_unit_price(100) .with_transaction_expiration_time(3664390082); let mut builder = match transaction_type { - "transfer" => factory.implicitly_create_user_account_and_transfer(to.clone(), 1000), - "create_account" => { factory.create_user_account(to.clone()) }, + "transfer" => factory.implicitly_create_user_account_and_transfer(to.clone(), amount), + "create_account" => { factory.create_user_account(to.clone()) } + "coin_transfer" => { factory.coins_transfer(to.clone(), amount, coin_type.unwrap()) } _ => panic!("Unsupported transaction type"), }; @@ -252,6 +262,8 @@ mod tests { "transfer", 99, "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + 33, + 1000, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021", "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", @@ -272,7 +284,8 @@ mod tests { "signature": "0x5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", "type": "ed25519_signature" } - }"# + }"#, + None, ); } @@ -284,6 +297,8 @@ mod tests { "create_account", 0, // Sequence number "0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e", + 33, + 0, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada0000000021", // Expected raw transaction bytes "fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", // Expected signature "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", // Expected encoded transaction @@ -304,7 +319,43 @@ mod tests { "signature": "0xfcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", "type": "ed25519_signature" } - }"# // Expected JSON literal + }"#, // Expected JSON literal + None, + ); + } + + #[test] + fn test_aptos_coin_transfer() { + setup_transaction( + "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "coin_transfer", + 24, // Sequence number + "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + 32, + 100000, + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada0000000020", // Expected raw transaction bytes + "7643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", // Expected signature + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada00000000200020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c407643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": ["0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","100000"], + "function": "0x1::coin::transfer", + "type": "entry_function_payload", + "type_arguments": ["0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC"] + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "24", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x7643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", + "type": "ed25519_signature" + } + }"#, // Expected JSON literal + Some(TypeTag::from_str("0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC").unwrap()), ); } } From 15bedf0d04d8f641b37100b0a75b85e63fbed688 Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 7 Nov 2023 11:05:40 -0500 Subject: [PATCH 26/60] feat(aptos): add aptos_coin_transfer to new_from_protobuf --- rust/tw_aptos/src/transaction_builder.rs | 9 +++++++-- rust/tw_aptos/src/transaction_payload.rs | 5 +++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index 4005a198808..d20447d411b 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -10,7 +10,7 @@ use move_core_types::language_storage::TypeTag; use tw_proto::Aptos::Proto::mod_SigningInput::OneOftransaction_payload; use crate::constants::{GAS_UNIT_PRICE, MAX_GAS_AMOUNT}; use crate::transaction::RawTransaction; -use crate::transaction_payload::{EntryFunction, TransactionPayload}; +use crate::transaction_payload::{convert_proto_struct_tag_to_type_tag, EntryFunction, TransactionPayload}; use tw_proto::Aptos::Proto::SigningInput; use crate::aptos_move_packages::{aptos_account_create_account, aptos_account_transfer, coin_transfer}; @@ -112,7 +112,12 @@ impl TransactionFactory { OneOftransaction_payload::transfer(transfer) => { return factory.implicitly_create_user_account_and_transfer(AccountAddress::from_str(&transfer.to).unwrap(), transfer.amount); } - OneOftransaction_payload::token_transfer(_) => {} + OneOftransaction_payload::token_transfer(token_transfer) => { + let func = token_transfer.function.unwrap(); + return factory.coins_transfer(AccountAddress::from_str(&token_transfer.to).unwrap(), token_transfer.amount, + convert_proto_struct_tag_to_type_tag(func) + ) + } OneOftransaction_payload::create_account(create_account) => { return factory.create_user_account(AccountAddress::from_str(&create_account.auth_key).unwrap()); } diff --git a/rust/tw_aptos/src/transaction_payload.rs b/rust/tw_aptos/src/transaction_payload.rs index 85c9b7bffc6..2a8ec48031c 100644 --- a/rust/tw_aptos/src/transaction_payload.rs +++ b/rust/tw_aptos/src/transaction_payload.rs @@ -4,6 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use std::str::FromStr; use move_core_types::identifier::Identifier; use move_core_types::language_storage::{ModuleId, TypeTag}; use serde::{Deserialize, Serialize}; @@ -21,6 +22,10 @@ pub struct EntryFunction { json_args: Value } +pub fn convert_proto_struct_tag_to_type_tag(struct_tag: tw_proto::Aptos::Proto::StructTag) -> TypeTag { + TypeTag::from_str(&format!("{}::{}::{}", struct_tag.account_address, struct_tag.module, struct_tag.name)).unwrap() +} + impl EntryFunction { fn to_json(&self) -> Value { // Create a JSON array from the `ty_args` field by filtering and mapping From 778aa5e5ff80d0a12dd4b0d8e92e14f033884165 Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 7 Nov 2023 12:06:49 -0500 Subject: [PATCH 27/60] feat(aptos): add nft offer --- rust/tw_aptos/src/aptos_move_packages.rs | 30 +++++ rust/tw_aptos/src/lib.rs | 1 + rust/tw_aptos/src/nft.rs | 48 ++++++++ rust/tw_aptos/src/signer.rs | 6 + rust/tw_aptos/src/transaction_builder.rs | 141 +++++++++++++++++++---- 5 files changed, 206 insertions(+), 20 deletions(-) create mode 100644 rust/tw_aptos/src/nft.rs diff --git a/rust/tw_aptos/src/aptos_move_packages.rs b/rust/tw_aptos/src/aptos_move_packages.rs index c59bc073b69..99a0efbe4b9 100644 --- a/rust/tw_aptos/src/aptos_move_packages.rs +++ b/rust/tw_aptos/src/aptos_move_packages.rs @@ -55,4 +55,34 @@ pub fn coin_transfer(coin_type: TypeTag, to: AccountAddress, amount: u64) -> Tra vec![bcs::to_bytes(&to).unwrap(), bcs::to_bytes(&amount).unwrap()], json!([to.to_hex_literal(), amount.to_string()]), )) +} + +pub fn token_transfers_offer_script( + receiver: AccountAddress, + creator: AccountAddress, + collection: Vec, + name: Vec, + property_version: u64, + amount: u64, +) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, + ]), + ident_str!("token_transfers").to_owned(), + ), + ident_str!("offer_script").to_owned(), + vec![], + vec![ + bcs::to_bytes(&receiver).unwrap(), + bcs::to_bytes(&creator).unwrap(), + bcs::to_bytes(&collection).unwrap(), + bcs::to_bytes(&name).unwrap(), + bcs::to_bytes(&property_version).unwrap(), + bcs::to_bytes(&amount).unwrap(), + ], + json!([receiver.to_hex_literal(), creator.to_hex_literal(), String::from_utf8_lossy(&collection), String::from_utf8_lossy(&name), property_version.to_string(), amount.to_string()]) + )) } \ No newline at end of file diff --git a/rust/tw_aptos/src/lib.rs b/rust/tw_aptos/src/lib.rs index b3235f83aac..383ba8275c3 100644 --- a/rust/tw_aptos/src/lib.rs +++ b/rust/tw_aptos/src/lib.rs @@ -10,6 +10,7 @@ pub mod aptos_move_packages; pub mod constants; pub mod entry; +pub mod nft; pub mod signer; pub mod transaction; pub mod transaction_builder; diff --git a/rust/tw_aptos/src/nft.rs b/rust/tw_aptos/src/nft.rs new file mode 100644 index 00000000000..5b806b7d2a7 --- /dev/null +++ b/rust/tw_aptos/src/nft.rs @@ -0,0 +1,48 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::str::FromStr; +use move_core_types::account_address::AccountAddress; +use tw_proto::Aptos::Proto::mod_NftMessage::OneOfnft_transaction_payload; +use tw_proto::Aptos::Proto::{NftMessage, OfferNftMessage}; + +pub struct NftOffer { + pub receiver: AccountAddress, + pub creator: AccountAddress, + pub collection: Vec, + pub name: Vec, + pub property_version: u64, + pub amount: u64 +} + +pub enum NftOperation { + Claim, + Offer(NftOffer) +} + +impl From> for NftOperation { + fn from(value: NftMessage) -> Self { + match value.nft_transaction_payload { + OneOfnft_transaction_payload::offer_nft(msg) => { NftOperation::Offer(msg.into()) } + OneOfnft_transaction_payload::cancel_offer_nft(_) => { todo!() } + OneOfnft_transaction_payload::claim_nft(_) => { todo!() } + OneOfnft_transaction_payload::None => { todo!() } + } + } +} + +impl From> for NftOffer { + fn from(value: OfferNftMessage) -> Self { + NftOffer { + receiver: AccountAddress::from_str(&value.receiver).unwrap(), + creator: AccountAddress::from_str(&value.creator).unwrap(), + collection: value.collectionName.as_bytes().to_vec(), + name: value.name.as_bytes().to_vec(), + property_version: value.property_version, + amount: value.amount, + } + } +} \ No newline at end of file diff --git a/rust/tw_aptos/src/signer.rs b/rust/tw_aptos/src/signer.rs index f21d3c22203..2f674b465b4 100644 --- a/rust/tw_aptos/src/signer.rs +++ b/rust/tw_aptos/src/signer.rs @@ -1,3 +1,9 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + use std::marker::PhantomData; use std::str::FromStr; use tw_coin_entry::error::SigningResult; diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index d20447d411b..176f72941f7 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -7,12 +7,15 @@ use std::str::FromStr; use move_core_types::account_address::AccountAddress; use move_core_types::language_storage::TypeTag; +use tw_coin_entry::derivation::Derivation::Default; +use tw_proto::Aptos::Proto::mod_NftMessage::OneOfnft_transaction_payload; use tw_proto::Aptos::Proto::mod_SigningInput::OneOftransaction_payload; use crate::constants::{GAS_UNIT_PRICE, MAX_GAS_AMOUNT}; use crate::transaction::RawTransaction; use crate::transaction_payload::{convert_proto_struct_tag_to_type_tag, EntryFunction, TransactionPayload}; use tw_proto::Aptos::Proto::SigningInput; -use crate::aptos_move_packages::{aptos_account_create_account, aptos_account_transfer, coin_transfer}; +use crate::aptos_move_packages::{aptos_account_create_account, aptos_account_transfer, coin_transfer, token_transfers_offer_script}; +use crate::nft::NftOperation; pub struct TransactionBuilder { sender: Option, @@ -115,13 +118,15 @@ impl TransactionFactory { OneOftransaction_payload::token_transfer(token_transfer) => { let func = token_transfer.function.unwrap(); return factory.coins_transfer(AccountAddress::from_str(&token_transfer.to).unwrap(), token_transfer.amount, - convert_proto_struct_tag_to_type_tag(func) - ) + convert_proto_struct_tag_to_type_tag(func), + ); } OneOftransaction_payload::create_account(create_account) => { return factory.create_user_account(AccountAddress::from_str(&create_account.auth_key).unwrap()); } - OneOftransaction_payload::nft_message(_) => {} + OneOftransaction_payload::nft_message(nft_message) => { + return factory.nft_ops(nft_message.into()); + } OneOftransaction_payload::register_token(_) => {} OneOftransaction_payload::liquid_staking_message(_) => {} OneOftransaction_payload::token_transfer_coins(_) => {} @@ -174,6 +179,15 @@ impl TransactionFactory { self.payload(aptos_account_create_account(to)) } + pub fn nft_ops(&self, operation: NftOperation) -> TransactionBuilder { + match operation { + NftOperation::Claim => { todo!() } + NftOperation::Offer(offer) => { + self.payload(token_transfers_offer_script(offer.receiver, offer.creator, offer.collection, offer.name, offer.property_version, offer.amount)) + } + } + } + pub fn implicitly_create_user_account_and_transfer( &self, to: AccountAddress, @@ -217,24 +231,43 @@ mod tests { use move_core_types::account_address::AccountAddress; use move_core_types::language_storage::TypeTag; use tw_encoding::hex; + use crate::nft::{NftOffer, NftOperation}; use crate::transaction_builder::TransactionFactory; + + pub struct AccountCreation { + to: String, + } + pub struct Transfer { + to: String, + amount: u64, + } + + pub struct TokenTransfer { + transfer: Transfer, + tag: TypeTag, + } + + pub enum OpsDetails { + AccountCreation(AccountCreation), + Transfer(Transfer), + TokenTransfer(TokenTransfer), + NftOps(NftOperation), + } + fn setup_transaction( sender: &str, keypair_str: &str, transaction_type: &str, sequence_number: u64, - to: &str, chain_id: u8, - amount: u64, expected_raw_txn_bytes_str: &str, expected_signature_str: &str, expected_encoded_txn_str: &str, json_literal: &str, - coin_type: Option, + ops_details: Option, ) { let account_address = AccountAddress::from_str(sender).unwrap(); - let to = AccountAddress::from_str(to).unwrap(); let keypair = tw_keypair::ed25519::sha512::KeyPair::try_from(keypair_str).unwrap(); let factory = TransactionFactory::new(chain_id) .with_max_gas_amount(3296766) @@ -242,9 +275,35 @@ mod tests { .with_transaction_expiration_time(3664390082); let mut builder = match transaction_type { - "transfer" => factory.implicitly_create_user_account_and_transfer(to.clone(), amount), - "create_account" => { factory.create_user_account(to.clone()) } - "coin_transfer" => { factory.coins_transfer(to.clone(), amount, coin_type.unwrap()) } + "transfer" => { + if let OpsDetails::Transfer(transfer) = ops_details.unwrap() { + factory.implicitly_create_user_account_and_transfer(AccountAddress::from_str(&transfer.to).unwrap(), transfer.amount) + } else { + panic!("Unsupported arguments") + } + }, + "create_account" => { + if let OpsDetails::AccountCreation(account) = ops_details.unwrap() { + factory.create_user_account(AccountAddress::from_str(&account.to).unwrap()) + } else { + panic!("Unsupported arguments") + } + + } + "coin_transfer" => { + if let OpsDetails::TokenTransfer(token_transfer) = ops_details.unwrap() { + factory.coins_transfer(AccountAddress::from_str(&token_transfer.transfer.to).unwrap(), token_transfer.transfer.amount, token_transfer.tag) + } else { + panic!("Unsupported arguments") + } + } + "nft_ops" => { + if let OpsDetails::NftOps(nft) = ops_details.unwrap() { + factory.nft_ops(nft) + } else { + panic!("Unsupported arguments") + } + } _ => panic!("Unsupported transaction type"), }; @@ -266,9 +325,7 @@ mod tests { "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", "transfer", 99, - "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", 33, - 1000, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021", "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", @@ -290,7 +347,10 @@ mod tests { "type": "ed25519_signature" } }"#, - None, + Some(OpsDetails::Transfer(Transfer { + to: "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30".to_string(), + amount: 1000, + })), ); } @@ -301,9 +361,7 @@ mod tests { "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair "create_account", 0, // Sequence number - "0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e", 33, - 0, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada0000000021", // Expected raw transaction bytes "fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", // Expected signature "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", // Expected encoded transaction @@ -325,7 +383,9 @@ mod tests { "type": "ed25519_signature" } }"#, // Expected JSON literal - None, + Some(OpsDetails::AccountCreation(AccountCreation { + to: "0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e".to_string() + })), ); } @@ -336,9 +396,7 @@ mod tests { "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair "coin_transfer", 24, // Sequence number - "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", 32, - 100000, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada0000000020", // Expected raw transaction bytes "7643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", // Expected signature "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada00000000200020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c407643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", // Expected encoded transaction @@ -360,7 +418,50 @@ mod tests { "type": "ed25519_signature" } }"#, // Expected JSON literal - Some(TypeTag::from_str("0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC").unwrap()), + Some(OpsDetails::TokenTransfer(TokenTransfer { transfer: Transfer { to: "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30".to_string(), amount: 100000 }, tag: TypeTag::from_str("0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC").unwrap() })), ); } + + #[test] + fn test_nft_offer() { + setup_transaction( + "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", // Sender's address + "7bebb6d543d17f6fe4e685cfab239fa37896edd594ff859f1df32f244fb707e2", // Keypair + "nft_ops", + 1, // Sequence number + 2, + "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes + "af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", // Expected signature + "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada00000000020020d1d99b67e37b483161a0fa369c46f34a3be4863c20e20fc7cdc669c0826a411340af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": [ + "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", + "Topaz Troopers", "Topaz Trooper #20068", "0", "1"], + "function": "0x3::token_transfers::offer_script", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", + "sequence_number": "1", + "signature": { + "public_key": "0xd1d99b67e37b483161a0fa369c46f34a3be4863c20e20fc7cdc669c0826a4113", + "signature": "0xaf5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", + "type": "ed25519_signature" + } + }"#, // Expected JSON literal + Some(OpsDetails::NftOps(NftOperation::Offer(NftOffer { + receiver: AccountAddress::from_str("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30").unwrap(), + creator: AccountAddress::from_str("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac").unwrap(), + collection: "Topaz Troopers".as_bytes().to_vec(), + name: "Topaz Trooper #20068".as_bytes().to_vec(), + property_version: 0, + amount: 1, + }))), + ) + } } From a6530946ffcdeffb930ddbe18ae4cab9f1051e84 Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 7 Nov 2023 13:36:28 -0500 Subject: [PATCH 28/60] feat(aptos): add cancel nft offer --- rust/tw_aptos/src/aptos_move_packages.rs | 28 +++++++++++++ rust/tw_aptos/src/nft.rs | 27 +++++++++--- rust/tw_aptos/src/transaction_builder.rs | 52 ++++++++++++++++++++++-- 3 files changed, 98 insertions(+), 9 deletions(-) diff --git a/rust/tw_aptos/src/aptos_move_packages.rs b/rust/tw_aptos/src/aptos_move_packages.rs index 99a0efbe4b9..7c8942f1c15 100644 --- a/rust/tw_aptos/src/aptos_move_packages.rs +++ b/rust/tw_aptos/src/aptos_move_packages.rs @@ -85,4 +85,32 @@ pub fn token_transfers_offer_script( ], json!([receiver.to_hex_literal(), creator.to_hex_literal(), String::from_utf8_lossy(&collection), String::from_utf8_lossy(&name), property_version.to_string(), amount.to_string()]) )) +} + +pub fn token_transfers_cancel_offer_script( + receiver: AccountAddress, + creator: AccountAddress, + collection: Vec, + name: Vec, + property_version: u64, +) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, + ]), + ident_str!("token_transfers").to_owned(), + ), + ident_str!("cancel_offer_script").to_owned(), + vec![], + vec![ + bcs::to_bytes(&receiver).unwrap(), + bcs::to_bytes(&creator).unwrap(), + bcs::to_bytes(&collection).unwrap(), + bcs::to_bytes(&name).unwrap(), + bcs::to_bytes(&property_version).unwrap(), + ], + json!([receiver.to_hex_literal(), creator.to_hex_literal(), String::from_utf8_lossy(&collection), String::from_utf8_lossy(&name), property_version.to_string()]) + )) } \ No newline at end of file diff --git a/rust/tw_aptos/src/nft.rs b/rust/tw_aptos/src/nft.rs index 5b806b7d2a7..32b7a815f4b 100644 --- a/rust/tw_aptos/src/nft.rs +++ b/rust/tw_aptos/src/nft.rs @@ -6,10 +6,11 @@ use std::str::FromStr; use move_core_types::account_address::AccountAddress; +use tw_coin_entry::derivation::Derivation::Default; use tw_proto::Aptos::Proto::mod_NftMessage::OneOfnft_transaction_payload; -use tw_proto::Aptos::Proto::{NftMessage, OfferNftMessage}; +use tw_proto::Aptos::Proto::{CancelOfferNftMessage, NftMessage, OfferNftMessage}; -pub struct NftOffer { +pub struct Offer { pub receiver: AccountAddress, pub creator: AccountAddress, pub collection: Vec, @@ -20,23 +21,24 @@ pub struct NftOffer { pub enum NftOperation { Claim, - Offer(NftOffer) + Offer(Offer), + Cancel(Offer) } impl From> for NftOperation { fn from(value: NftMessage) -> Self { match value.nft_transaction_payload { OneOfnft_transaction_payload::offer_nft(msg) => { NftOperation::Offer(msg.into()) } - OneOfnft_transaction_payload::cancel_offer_nft(_) => { todo!() } + OneOfnft_transaction_payload::cancel_offer_nft(msg) => { NftOperation::Cancel(msg.into()) } OneOfnft_transaction_payload::claim_nft(_) => { todo!() } OneOfnft_transaction_payload::None => { todo!() } } } } -impl From> for NftOffer { +impl From> for Offer { fn from(value: OfferNftMessage) -> Self { - NftOffer { + Offer { receiver: AccountAddress::from_str(&value.receiver).unwrap(), creator: AccountAddress::from_str(&value.creator).unwrap(), collection: value.collectionName.as_bytes().to_vec(), @@ -45,4 +47,17 @@ impl From> for NftOffer { amount: value.amount, } } +} + +impl From> for Offer { + fn from(value: CancelOfferNftMessage) -> Self { + Offer { + receiver: AccountAddress::from_str(&value.receiver).unwrap(), + creator: AccountAddress::from_str(&value.creator).unwrap(), + collection: value.collectionName.as_bytes().to_vec(), + name: value.name.as_bytes().to_vec(), + property_version: value.property_version, + amount: 0, + } + } } \ No newline at end of file diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index 176f72941f7..d3d72df9044 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -14,7 +14,7 @@ use crate::constants::{GAS_UNIT_PRICE, MAX_GAS_AMOUNT}; use crate::transaction::RawTransaction; use crate::transaction_payload::{convert_proto_struct_tag_to_type_tag, EntryFunction, TransactionPayload}; use tw_proto::Aptos::Proto::SigningInput; -use crate::aptos_move_packages::{aptos_account_create_account, aptos_account_transfer, coin_transfer, token_transfers_offer_script}; +use crate::aptos_move_packages::{aptos_account_create_account, aptos_account_transfer, coin_transfer, token_transfers_cancel_offer_script, token_transfers_offer_script}; use crate::nft::NftOperation; pub struct TransactionBuilder { @@ -182,6 +182,9 @@ impl TransactionFactory { pub fn nft_ops(&self, operation: NftOperation) -> TransactionBuilder { match operation { NftOperation::Claim => { todo!() } + NftOperation::Cancel(offer) => { + self.payload(token_transfers_cancel_offer_script(offer.receiver, offer.creator, offer.collection, offer.name, offer.property_version)) + } NftOperation::Offer(offer) => { self.payload(token_transfers_offer_script(offer.receiver, offer.creator, offer.collection, offer.name, offer.property_version, offer.amount)) } @@ -231,7 +234,7 @@ mod tests { use move_core_types::account_address::AccountAddress; use move_core_types::language_storage::TypeTag; use tw_encoding::hex; - use crate::nft::{NftOffer, NftOperation}; + use crate::nft::{Offer, NftOperation}; use crate::transaction_builder::TransactionFactory; @@ -454,7 +457,7 @@ mod tests { "type": "ed25519_signature" } }"#, // Expected JSON literal - Some(OpsDetails::NftOps(NftOperation::Offer(NftOffer { + Some(OpsDetails::NftOps(NftOperation::Offer(Offer { receiver: AccountAddress::from_str("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30").unwrap(), creator: AccountAddress::from_str("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac").unwrap(), collection: "Topaz Troopers".as_bytes().to_vec(), @@ -464,4 +467,47 @@ mod tests { }))), ) } + + #[test] + fn test_cancel_nft_offer() { + setup_transaction( + "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "nft_ops", + 21, // Sequence number + 2, + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes + "826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", // Expected signature + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": [ + "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", + "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", + "Topaz Troopers", "Topaz Trooper #20068", "0"], + "function": "0x3::token_transfers::cancel_offer_script", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "21", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", + "type": "ed25519_signature" + } + }"#, // Expected JSON literal + Some(OpsDetails::NftOps(NftOperation::Cancel(Offer { + receiver: AccountAddress::from_str("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee").unwrap(), + creator: AccountAddress::from_str("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac").unwrap(), + collection: "Topaz Troopers".as_bytes().to_vec(), + name: "Topaz Trooper #20068".as_bytes().to_vec(), + property_version: 0, + amount: 0, + }))), + ) + } } From 81638cdb8582835251bb83a209b6cc18dc81da63 Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 7 Nov 2023 13:47:19 -0500 Subject: [PATCH 29/60] feat(aptos): finish nft --- rust/tw_aptos/src/aptos_move_packages.rs | 28 +++++++++++++ rust/tw_aptos/src/nft.rs | 27 ++++++++++-- rust/tw_aptos/src/transaction_builder.rs | 52 +++++++++++++++++++++--- 3 files changed, 98 insertions(+), 9 deletions(-) diff --git a/rust/tw_aptos/src/aptos_move_packages.rs b/rust/tw_aptos/src/aptos_move_packages.rs index 7c8942f1c15..89fb0d45f0f 100644 --- a/rust/tw_aptos/src/aptos_move_packages.rs +++ b/rust/tw_aptos/src/aptos_move_packages.rs @@ -113,4 +113,32 @@ pub fn token_transfers_cancel_offer_script( ], json!([receiver.to_hex_literal(), creator.to_hex_literal(), String::from_utf8_lossy(&collection), String::from_utf8_lossy(&name), property_version.to_string()]) )) +} + +pub fn token_transfers_claim_script( + sender: AccountAddress, + creator: AccountAddress, + collection: Vec, + name: Vec, + property_version: u64, +) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, + ]), + ident_str!("token_transfers").to_owned(), + ), + ident_str!("claim_script").to_owned(), + vec![], + vec![ + bcs::to_bytes(&sender).unwrap(), + bcs::to_bytes(&creator).unwrap(), + bcs::to_bytes(&collection).unwrap(), + bcs::to_bytes(&name).unwrap(), + bcs::to_bytes(&property_version).unwrap(), + ], + json!([sender.to_hex_literal(), creator.to_hex_literal(), String::from_utf8_lossy(&collection), String::from_utf8_lossy(&name), property_version.to_string()]) + )) } \ No newline at end of file diff --git a/rust/tw_aptos/src/nft.rs b/rust/tw_aptos/src/nft.rs index 32b7a815f4b..7e1aa5c3e6c 100644 --- a/rust/tw_aptos/src/nft.rs +++ b/rust/tw_aptos/src/nft.rs @@ -6,9 +6,8 @@ use std::str::FromStr; use move_core_types::account_address::AccountAddress; -use tw_coin_entry::derivation::Derivation::Default; use tw_proto::Aptos::Proto::mod_NftMessage::OneOfnft_transaction_payload; -use tw_proto::Aptos::Proto::{CancelOfferNftMessage, NftMessage, OfferNftMessage}; +use tw_proto::Aptos::Proto::{CancelOfferNftMessage, ClaimNftMessage, NftMessage, OfferNftMessage}; pub struct Offer { pub receiver: AccountAddress, @@ -19,8 +18,16 @@ pub struct Offer { pub amount: u64 } +pub struct Claim { + pub sender: AccountAddress, + pub creator: AccountAddress, + pub collection: Vec, + pub name: Vec, + pub property_version: u64, +} + pub enum NftOperation { - Claim, + Claim(Claim), Offer(Offer), Cancel(Offer) } @@ -30,7 +37,7 @@ impl From> for NftOperation { match value.nft_transaction_payload { OneOfnft_transaction_payload::offer_nft(msg) => { NftOperation::Offer(msg.into()) } OneOfnft_transaction_payload::cancel_offer_nft(msg) => { NftOperation::Cancel(msg.into()) } - OneOfnft_transaction_payload::claim_nft(_) => { todo!() } + OneOfnft_transaction_payload::claim_nft(msg) => { NftOperation::Claim(msg.into()) } OneOfnft_transaction_payload::None => { todo!() } } } @@ -60,4 +67,16 @@ impl From> for Offer { amount: 0, } } +} + +impl From> for Claim { + fn from(value: ClaimNftMessage) -> Self { + Claim { + sender: AccountAddress::from_str(&value.sender).unwrap(), + creator: AccountAddress::from_str(&value.creator).unwrap(), + collection: value.collectionName.as_bytes().to_vec(), + name: value.name.as_bytes().to_vec(), + property_version: value.property_version, + } + } } \ No newline at end of file diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index d3d72df9044..ff0a111562a 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -7,14 +7,12 @@ use std::str::FromStr; use move_core_types::account_address::AccountAddress; use move_core_types::language_storage::TypeTag; -use tw_coin_entry::derivation::Derivation::Default; -use tw_proto::Aptos::Proto::mod_NftMessage::OneOfnft_transaction_payload; use tw_proto::Aptos::Proto::mod_SigningInput::OneOftransaction_payload; use crate::constants::{GAS_UNIT_PRICE, MAX_GAS_AMOUNT}; use crate::transaction::RawTransaction; use crate::transaction_payload::{convert_proto_struct_tag_to_type_tag, EntryFunction, TransactionPayload}; use tw_proto::Aptos::Proto::SigningInput; -use crate::aptos_move_packages::{aptos_account_create_account, aptos_account_transfer, coin_transfer, token_transfers_cancel_offer_script, token_transfers_offer_script}; +use crate::aptos_move_packages::{aptos_account_create_account, aptos_account_transfer, coin_transfer, token_transfers_cancel_offer_script, token_transfers_claim_script, token_transfers_offer_script}; use crate::nft::NftOperation; pub struct TransactionBuilder { @@ -181,7 +179,9 @@ impl TransactionFactory { pub fn nft_ops(&self, operation: NftOperation) -> TransactionBuilder { match operation { - NftOperation::Claim => { todo!() } + NftOperation::Claim(claim) => { + self.payload(token_transfers_claim_script(claim.sender, claim.creator, claim.collection, claim.name, claim.property_version)) + } NftOperation::Cancel(offer) => { self.payload(token_transfers_cancel_offer_script(offer.receiver, offer.creator, offer.collection, offer.name, offer.property_version)) } @@ -234,7 +234,7 @@ mod tests { use move_core_types::account_address::AccountAddress; use move_core_types::language_storage::TypeTag; use tw_encoding::hex; - use crate::nft::{Offer, NftOperation}; + use crate::nft::{Offer, NftOperation, Claim}; use crate::transaction_builder::TransactionFactory; @@ -510,4 +510,46 @@ mod tests { }))), ) } + + #[test] + fn test_claim_nft_offer() { + setup_transaction( + "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "nft_ops", + 19, // Sequence number + 2, + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes + "ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", // Expected signature + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": [ + "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", + "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", + "Topaz Troopers", "Topaz Trooper #20068", "0"], + "function": "0x3::token_transfers::claim_script", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "19", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0xede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", + "type": "ed25519_signature" + } + }"#, // Expected JSON literal + Some(OpsDetails::NftOps(NftOperation::Claim(Claim { + sender: AccountAddress::from_str("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee").unwrap(), + creator: AccountAddress::from_str("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac").unwrap(), + collection: "Topaz Troopers".as_bytes().to_vec(), + name: "Topaz Trooper #20068".as_bytes().to_vec(), + property_version: 0, + }))), + ) + } } From 625defca2b0179f14924462470ba4021934c07c3 Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 7 Nov 2023 18:14:33 -0500 Subject: [PATCH 30/60] feat(aptos): add register_token --- rust/tw_aptos/src/aptos_move_packages.rs | 16 ++++++ rust/tw_aptos/src/transaction_builder.rs | 65 ++++++++++++++++++++++-- 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/rust/tw_aptos/src/aptos_move_packages.rs b/rust/tw_aptos/src/aptos_move_packages.rs index 89fb0d45f0f..92a7ff871ae 100644 --- a/rust/tw_aptos/src/aptos_move_packages.rs +++ b/rust/tw_aptos/src/aptos_move_packages.rs @@ -141,4 +141,20 @@ pub fn token_transfers_claim_script( ], json!([sender.to_hex_literal(), creator.to_hex_literal(), String::from_utf8_lossy(&collection), String::from_utf8_lossy(&name), property_version.to_string()]) )) +} + +pub fn managed_coin_register(coin_type: TypeTag) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ]), + ident_str!("managed_coin").to_owned(), + ), + ident_str!("register").to_owned(), + vec![coin_type], + vec![], + json!([]) + )) } \ No newline at end of file diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index ff0a111562a..00ab376afa0 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -12,7 +12,7 @@ use crate::constants::{GAS_UNIT_PRICE, MAX_GAS_AMOUNT}; use crate::transaction::RawTransaction; use crate::transaction_payload::{convert_proto_struct_tag_to_type_tag, EntryFunction, TransactionPayload}; use tw_proto::Aptos::Proto::SigningInput; -use crate::aptos_move_packages::{aptos_account_create_account, aptos_account_transfer, coin_transfer, token_transfers_cancel_offer_script, token_transfers_claim_script, token_transfers_offer_script}; +use crate::aptos_move_packages::{aptos_account_create_account, aptos_account_transfer, coin_transfer, managed_coin_register, token_transfers_cancel_offer_script, token_transfers_claim_script, token_transfers_offer_script}; use crate::nft::NftOperation; pub struct TransactionBuilder { @@ -125,7 +125,9 @@ impl TransactionFactory { OneOftransaction_payload::nft_message(nft_message) => { return factory.nft_ops(nft_message.into()); } - OneOftransaction_payload::register_token(_) => {} + OneOftransaction_payload::register_token(register_token) => { + return factory.register_token(convert_proto_struct_tag_to_type_tag(register_token.function.unwrap())) + } OneOftransaction_payload::liquid_staking_message(_) => {} OneOftransaction_payload::token_transfer_coins(_) => {} OneOftransaction_payload::None => {} @@ -177,6 +179,10 @@ impl TransactionFactory { self.payload(aptos_account_create_account(to)) } + pub fn register_token(&self, coin_type: TypeTag) -> TransactionBuilder { + self.payload(managed_coin_register(coin_type)) + } + pub fn nft_ops(&self, operation: NftOperation) -> TransactionBuilder { match operation { NftOperation::Claim(claim) => { @@ -251,7 +257,12 @@ mod tests { tag: TypeTag, } + pub struct RegisterToken { + coin_type: TypeTag, + } + pub enum OpsDetails { + RegisterToken(RegisterToken), AccountCreation(AccountCreation), Transfer(Transfer), TokenTransfer(TokenTransfer), @@ -264,6 +275,7 @@ mod tests { transaction_type: &str, sequence_number: u64, chain_id: u8, + max_gas_amount: u64, expected_raw_txn_bytes_str: &str, expected_signature_str: &str, expected_encoded_txn_str: &str, @@ -273,7 +285,7 @@ mod tests { let account_address = AccountAddress::from_str(sender).unwrap(); let keypair = tw_keypair::ed25519::sha512::KeyPair::try_from(keypair_str).unwrap(); let factory = TransactionFactory::new(chain_id) - .with_max_gas_amount(3296766) + .with_max_gas_amount(max_gas_amount) .with_gas_unit_price(100) .with_transaction_expiration_time(3664390082); @@ -307,6 +319,13 @@ mod tests { panic!("Unsupported arguments") } } + "register_token" => { + if let OpsDetails::RegisterToken(register_token) = ops_details.unwrap() { + factory.register_token(register_token.coin_type) + } else { + panic!("Unsupported arguments") + } + } _ => panic!("Unsupported transaction type"), }; @@ -329,6 +348,7 @@ mod tests { "transfer", 99, 33, + 3296766, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021", "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", @@ -365,6 +385,7 @@ mod tests { "create_account", 0, // Sequence number 33, + 3296766, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada0000000021", // Expected raw transaction bytes "fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", // Expected signature "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", // Expected encoded transaction @@ -400,6 +421,7 @@ mod tests { "coin_transfer", 24, // Sequence number 32, + 3296766, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada0000000020", // Expected raw transaction bytes "7643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", // Expected signature "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada00000000200020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c407643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", // Expected encoded transaction @@ -433,6 +455,7 @@ mod tests { "nft_ops", 1, // Sequence number 2, + 3296766, "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes "af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", // Expected signature "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada00000000020020d1d99b67e37b483161a0fa369c46f34a3be4863c20e20fc7cdc669c0826a411340af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", // Expected encoded transaction @@ -476,6 +499,7 @@ mod tests { "nft_ops", 21, // Sequence number 2, + 3296766, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes "826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", // Expected signature "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", // Expected encoded transaction @@ -519,6 +543,7 @@ mod tests { "nft_ops", 19, // Sequence number 2, + 3296766, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes "ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", // Expected signature "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", // Expected encoded transaction @@ -552,4 +577,38 @@ mod tests { }))), ) } + + #[test] + fn test_register_token() { + setup_transaction( + "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "register_token", + 23, // Sequence number + 2, + 2000000, + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3017000000000000000200000000000000000000000000000000000000000000000000000000000000010c6d616e616765645f636f696e0872656769737465720107e4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379096d6f76655f636f696e084d6f7665436f696e000080841e00000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes + "e230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100", // Expected signature + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3017000000000000000200000000000000000000000000000000000000000000000000000000000000010c6d616e616765645f636f696e0872656769737465720107e4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379096d6f76655f636f696e084d6f7665436f696e000080841e00000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40e230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "2000000", + "payload": { + "arguments": [], + "function": "0x1::managed_coin::register", + "type": "entry_function_payload", + "type_arguments": ["0xe4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379::move_coin::MoveCoin"] + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "23", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0xe230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100", + "type": "ed25519_signature" + } + }"#, // Expected JSON literal + Some(OpsDetails::RegisterToken(RegisterToken { coin_type: TypeTag::from_str("0xe4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379::move_coin::MoveCoin").unwrap() })), + ) + } } From c0fa0ad777abe5164f172c50e3dda3f4383c18d8 Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 7 Nov 2023 18:26:00 -0500 Subject: [PATCH 31/60] feat(aptos): implicit aptos coin transfer --- rust/tw_aptos/src/aptos_move_packages.rs | 20 +++++++++ rust/tw_aptos/src/transaction_builder.rs | 53 ++++++++++++++++++++++-- 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/rust/tw_aptos/src/aptos_move_packages.rs b/rust/tw_aptos/src/aptos_move_packages.rs index 92a7ff871ae..5709782933b 100644 --- a/rust/tw_aptos/src/aptos_move_packages.rs +++ b/rust/tw_aptos/src/aptos_move_packages.rs @@ -57,6 +57,26 @@ pub fn coin_transfer(coin_type: TypeTag, to: AccountAddress, amount: u64) -> Tra )) } +pub fn aptos_account_transfer_coins( + coin_type: TypeTag, + to: AccountAddress, + amount: u64, +) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ]), + ident_str!("aptos_account").to_owned(), + ), + ident_str!("transfer_coins").to_owned(), + vec![coin_type], + vec![bcs::to_bytes(&to).unwrap(), bcs::to_bytes(&amount).unwrap()], + json!([to.to_hex_literal(), amount.to_string()]), + )) +} + pub fn token_transfers_offer_script( receiver: AccountAddress, creator: AccountAddress, diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index 00ab376afa0..b5d443de46d 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -12,7 +12,7 @@ use crate::constants::{GAS_UNIT_PRICE, MAX_GAS_AMOUNT}; use crate::transaction::RawTransaction; use crate::transaction_payload::{convert_proto_struct_tag_to_type_tag, EntryFunction, TransactionPayload}; use tw_proto::Aptos::Proto::SigningInput; -use crate::aptos_move_packages::{aptos_account_create_account, aptos_account_transfer, coin_transfer, managed_coin_register, token_transfers_cancel_offer_script, token_transfers_claim_script, token_transfers_offer_script}; +use crate::aptos_move_packages::{aptos_account_create_account, aptos_account_transfer, aptos_account_transfer_coins, coin_transfer, managed_coin_register, token_transfers_cancel_offer_script, token_transfers_claim_script, token_transfers_offer_script}; use crate::nft::NftOperation; pub struct TransactionBuilder { @@ -209,6 +209,11 @@ impl TransactionFactory { amount: u64, coin_type: TypeTag) -> TransactionBuilder { self.payload(coin_transfer(coin_type, to, amount)) } + + pub fn implicitly_create_user_and_coins_transfer(&self, to: AccountAddress, + amount: u64, coin_type: TypeTag) -> TransactionBuilder { + self.payload(aptos_account_transfer_coins(coin_type, to, amount)) + } pub fn transfer(&self, _to: AccountAddress, _amount: u64) -> TransactionBuilder { todo!() } @@ -266,6 +271,7 @@ mod tests { AccountCreation(AccountCreation), Transfer(Transfer), TokenTransfer(TokenTransfer), + ImplicitTokenTransfer(TokenTransfer), NftOps(NftOperation), } @@ -311,14 +317,21 @@ mod tests { } else { panic!("Unsupported arguments") } - } + }, + "implicit_coin_transfer" => { + if let OpsDetails::ImplicitTokenTransfer(token_transfer) = ops_details.unwrap() { + factory.implicitly_create_user_and_coins_transfer(AccountAddress::from_str(&token_transfer.transfer.to).unwrap(), token_transfer.transfer.amount, token_transfer.tag) + } else { + panic!("Unsupported arguments") + } + }, "nft_ops" => { if let OpsDetails::NftOps(nft) = ops_details.unwrap() { factory.nft_ops(nft) } else { panic!("Unsupported arguments") } - } + }, "register_token" => { if let OpsDetails::RegisterToken(register_token) = ops_details.unwrap() { factory.register_token(register_token.coin_type) @@ -611,4 +624,38 @@ mod tests { Some(OpsDetails::RegisterToken(RegisterToken { coin_type: TypeTag::from_str("0xe4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379::move_coin::MoveCoin").unwrap() })), ) } + + #[test] + fn test_implicit_aptos_coin_transfer() { + setup_transaction( + "0x1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf25", // Sender's address + "e7f56c77189e03699a75d8ec5c090e41f3d9d4783bc49c33df8a93d915e10de8", // Keypair + "implicit_coin_transfer", + 2, // Sequence number + 1, + 2000, + "1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf2502000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e730107e9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9086d65655f636f696e074d6565436f696e000220b7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c081027000000000000d0070000000000006400000000000000c2276ada0000000001", // Expected raw transaction bytes + "30ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d", // Expected signature + "1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf2502000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e730107e9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9086d65655f636f696e074d6565436f696e000220b7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c081027000000000000d0070000000000006400000000000000c2276ada0000000001002062e7a6a486553b56a53e89dfae3f780693e537e5b0a7ed33290780e581ca83694030ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "2000", + "payload": { + "arguments": ["0xb7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c","10000"], + "function": "0x1::aptos_account::transfer_coins", + "type": "entry_function_payload", + "type_arguments": ["0xe9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9::mee_coin::MeeCoin"] + }, + "sender": "0x1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf25", + "sequence_number": "2", + "signature": { + "public_key": "0x62e7a6a486553b56a53e89dfae3f780693e537e5b0a7ed33290780e581ca8369", + "signature": "0x30ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d", + "type": "ed25519_signature" + } + }"#, // Expected JSON literal + Some(OpsDetails::ImplicitTokenTransfer(TokenTransfer { transfer: Transfer { to: "0xb7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c".to_string(), amount: 10000 }, tag: TypeTag::from_str("0xe9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9::mee_coin::MeeCoin").unwrap() })), + ); + } } From 979afbf768988cf025a8d87a7833fe967461bf3e Mon Sep 17 00:00:00 2001 From: Milerius Date: Thu, 9 Nov 2023 14:13:27 -0500 Subject: [PATCH 32/60] feat(aptos): start liquid staking --- rust/tw_aptos/src/lib.rs | 2 + rust/tw_aptos/src/liquid_staking.rs | 54 ++++++++++++++++++++++++ rust/tw_aptos/src/transaction_builder.rs | 21 ++++++++- 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 rust/tw_aptos/src/liquid_staking.rs diff --git a/rust/tw_aptos/src/lib.rs b/rust/tw_aptos/src/lib.rs index 383ba8275c3..a59709a6c95 100644 --- a/rust/tw_aptos/src/lib.rs +++ b/rust/tw_aptos/src/lib.rs @@ -11,6 +11,8 @@ pub mod constants; pub mod entry; pub mod nft; + +pub mod liquid_staking; pub mod signer; pub mod transaction; pub mod transaction_builder; diff --git a/rust/tw_aptos/src/liquid_staking.rs b/rust/tw_aptos/src/liquid_staking.rs new file mode 100644 index 00000000000..148473ef089 --- /dev/null +++ b/rust/tw_aptos/src/liquid_staking.rs @@ -0,0 +1,54 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::str::FromStr; +use move_core_types::account_address::AccountAddress; +use move_core_types::ident_str; +use move_core_types::language_storage::ModuleId; +use serde_json::json; +use tw_proto::Aptos::Proto::LiquidStaking; +use tw_proto::Aptos::Proto::mod_LiquidStaking::OneOfliquid_stake_transaction_payload; +use crate::transaction_payload::{EntryFunction, TransactionPayload}; + +pub fn tortuga_stake( + smart_contract_address: AccountAddress, + amount: u64, +) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + smart_contract_address, + ident_str!("stake_router").to_owned(), + ), + ident_str!("stake").to_owned(), + vec![], + vec![bcs::to_bytes(&amount).unwrap()], + json!([amount.to_string()]), + )) +} + +pub struct Stake { + pub amount: u64, + pub smart_contract_address: AccountAddress, +} + +pub enum LiquidStakingOperation { + Stake(Stake), + Unstake, + Claim, +} + +impl From> for LiquidStakingOperation { + fn from(value: LiquidStaking) -> Self { + match value.liquid_stake_transaction_payload { + OneOfliquid_stake_transaction_payload::stake(stake_msg) => { + LiquidStakingOperation::Stake(Stake { amount: stake_msg.amount, smart_contract_address: AccountAddress::from_str(&value.smart_contract_address).unwrap() }) + } + OneOfliquid_stake_transaction_payload::unstake(_) => {todo!()} + OneOfliquid_stake_transaction_payload::claim(_) => {todo!()} + OneOfliquid_stake_transaction_payload::None => {todo!()} + } + } +} \ No newline at end of file diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index b5d443de46d..ad9cada9482 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -13,6 +13,7 @@ use crate::transaction::RawTransaction; use crate::transaction_payload::{convert_proto_struct_tag_to_type_tag, EntryFunction, TransactionPayload}; use tw_proto::Aptos::Proto::SigningInput; use crate::aptos_move_packages::{aptos_account_create_account, aptos_account_transfer, aptos_account_transfer_coins, coin_transfer, managed_coin_register, token_transfers_cancel_offer_script, token_transfers_claim_script, token_transfers_offer_script}; +use crate::liquid_staking::{LiquidStakingOperation, tortuga_stake}; use crate::nft::NftOperation; pub struct TransactionBuilder { @@ -128,8 +129,14 @@ impl TransactionFactory { OneOftransaction_payload::register_token(register_token) => { return factory.register_token(convert_proto_struct_tag_to_type_tag(register_token.function.unwrap())) } - OneOftransaction_payload::liquid_staking_message(_) => {} - OneOftransaction_payload::token_transfer_coins(_) => {} + OneOftransaction_payload::liquid_staking_message(msg) => { + return factory.liquid_staking_ops(msg.into()) + } + OneOftransaction_payload::token_transfer_coins(token_transfer_coins) => { + let func = token_transfer_coins.function.unwrap(); + return factory.implicitly_create_user_and_coins_transfer(AccountAddress::from_str(&token_transfer_coins.to).unwrap(), token_transfer_coins.amount, + convert_proto_struct_tag_to_type_tag(func)) + } OneOftransaction_payload::None => {} } todo!() @@ -197,6 +204,16 @@ impl TransactionFactory { } } + pub fn liquid_staking_ops(&self, operation: LiquidStakingOperation) -> TransactionBuilder { + match operation { + LiquidStakingOperation::Stake(stake) => { + self.payload(tortuga_stake(stake.smart_contract_address, stake.amount)) + } + LiquidStakingOperation::Unstake => {todo!()} + LiquidStakingOperation::Claim => {todo!()} + } + } + pub fn implicitly_create_user_account_and_transfer( &self, to: AccountAddress, From 04ee7c12e7c95ef88439fa35cc6a9805f0fa74d5 Mon Sep 17 00:00:00 2001 From: Milerius Date: Thu, 9 Nov 2023 14:24:57 -0500 Subject: [PATCH 33/60] feat(aptos): add liquid staking unit test --- rust/tw_aptos/src/transaction_builder.rs | 65 +++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index ad9cada9482..3a23ca98c4d 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -262,6 +262,7 @@ mod tests { use move_core_types::account_address::AccountAddress; use move_core_types::language_storage::TypeTag; use tw_encoding::hex; + use crate::liquid_staking::{LiquidStakingOperation, Stake}; use crate::nft::{Offer, NftOperation, Claim}; use crate::transaction_builder::TransactionFactory; @@ -283,8 +284,14 @@ mod tests { coin_type: TypeTag, } + pub struct LiquidStakingStake { + smart_contract_address: AccountAddress, + amount: u64, + } + pub enum OpsDetails { RegisterToken(RegisterToken), + LiquidStakingOps(LiquidStakingOperation), AccountCreation(AccountCreation), Transfer(Transfer), TokenTransfer(TokenTransfer), @@ -299,6 +306,7 @@ mod tests { sequence_number: u64, chain_id: u8, max_gas_amount: u64, + timestamp: u64, expected_raw_txn_bytes_str: &str, expected_signature_str: &str, expected_encoded_txn_str: &str, @@ -310,7 +318,7 @@ mod tests { let factory = TransactionFactory::new(chain_id) .with_max_gas_amount(max_gas_amount) .with_gas_unit_price(100) - .with_transaction_expiration_time(3664390082); + .with_transaction_expiration_time(timestamp); let mut builder = match transaction_type { "transfer" => { @@ -355,6 +363,13 @@ mod tests { } else { panic!("Unsupported arguments") } + }, + "liquid_staking_ops" => { + if let OpsDetails::LiquidStakingOps(liquid_staking_ops) = ops_details.unwrap() { + factory.liquid_staking_ops(liquid_staking_ops) + } else { + panic!("Unsupported arguments") + } } _ => panic!("Unsupported transaction type"), }; @@ -379,6 +394,7 @@ mod tests { 99, 33, 3296766, + 3664390082, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021", "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", @@ -416,6 +432,7 @@ mod tests { 0, // Sequence number 33, 3296766, + 3664390082, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada0000000021", // Expected raw transaction bytes "fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", // Expected signature "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", // Expected encoded transaction @@ -452,6 +469,7 @@ mod tests { 24, // Sequence number 32, 3296766, + 3664390082, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada0000000020", // Expected raw transaction bytes "7643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", // Expected signature "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada00000000200020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c407643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", // Expected encoded transaction @@ -486,6 +504,7 @@ mod tests { 1, // Sequence number 2, 3296766, + 3664390082, "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes "af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", // Expected signature "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada00000000020020d1d99b67e37b483161a0fa369c46f34a3be4863c20e20fc7cdc669c0826a411340af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", // Expected encoded transaction @@ -530,6 +549,7 @@ mod tests { 21, // Sequence number 2, 3296766, + 3664390082, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes "826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", // Expected signature "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", // Expected encoded transaction @@ -574,6 +594,7 @@ mod tests { 19, // Sequence number 2, 3296766, + 3664390082, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes "ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", // Expected signature "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", // Expected encoded transaction @@ -617,6 +638,7 @@ mod tests { 23, // Sequence number 2, 2000000, + 3664390082, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3017000000000000000200000000000000000000000000000000000000000000000000000000000000010c6d616e616765645f636f696e0872656769737465720107e4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379096d6f76655f636f696e084d6f7665436f696e000080841e00000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes "e230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100", // Expected signature "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3017000000000000000200000000000000000000000000000000000000000000000000000000000000010c6d616e616765645f636f696e0872656769737465720107e4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379096d6f76655f636f696e084d6f7665436f696e000080841e00000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40e230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100", // Expected encoded transaction @@ -651,6 +673,7 @@ mod tests { 2, // Sequence number 1, 2000, + 3664390082, "1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf2502000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e730107e9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9086d65655f636f696e074d6565436f696e000220b7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c081027000000000000d0070000000000006400000000000000c2276ada0000000001", // Expected raw transaction bytes "30ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d", // Expected signature "1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf2502000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e730107e9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9086d65655f636f696e074d6565436f696e000220b7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c081027000000000000d0070000000000006400000000000000c2276ada0000000001002062e7a6a486553b56a53e89dfae3f780693e537e5b0a7ed33290780e581ca83694030ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d", // Expected encoded transaction @@ -675,4 +698,44 @@ mod tests { Some(OpsDetails::ImplicitTokenTransfer(TokenTransfer { transfer: Transfer { to: "0xb7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c".to_string(), amount: 10000 }, tag: TypeTag::from_str("0xe9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9::mee_coin::MeeCoin").unwrap() })), ); } + + #[test] + fn test_implicit_aptos_tortuga_stake() { + setup_transaction( + "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address + "786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05", // Keypair + "liquid_staking_ops", + 19, // Sequence number + 1, + 5554, + 1670240203, + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1300000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000b2150000000000006400000000000000cbd78d630000000001", // Expected raw transaction bytes + "22d3166c3003f9c24a35fd39c71eb27e0d2bb82541be610822165c9283f56fefe5a9d46421b9caf174995bd8f83141e60ea8cff521ecf4741fe19e6ae9a5680d", // Expected signature + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1300000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000b2150000000000006400000000000000cbd78d630000000001002089e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc44022d3166c3003f9c24a35fd39c71eb27e0d2bb82541be610822165c9283f56fefe5a9d46421b9caf174995bd8f83141e60ea8cff521ecf4741fe19e6ae9a5680d", // Expected encoded transaction + r#"{ + "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", + "sequence_number": "19", + "max_gas_amount": "5554", + "gas_unit_price": "100", + "expiration_timestamp_secs": "1670240203", + "payload": { + "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::stake", + "type_arguments": [], + "arguments": [ + "100000000" + ], + "type": "entry_function_payload" + }, + "signature": { + "public_key": "0x89e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4", + "signature": "0x22d3166c3003f9c24a35fd39c71eb27e0d2bb82541be610822165c9283f56fefe5a9d46421b9caf174995bd8f83141e60ea8cff521ecf4741fe19e6ae9a5680d", + "type": "ed25519_signature" + } + }"#, // Expected JSON literal + Some(OpsDetails::LiquidStakingOps(LiquidStakingOperation::Stake(Stake { + amount: 100000000, + smart_contract_address: AccountAddress::from_str("0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f").unwrap(), + }))), + ); + } } From 4ffe4bb0abcb2e2d7cd55b6e7d74a4111c436c62 Mon Sep 17 00:00:00 2001 From: Milerius Date: Thu, 9 Nov 2023 14:35:52 -0500 Subject: [PATCH 34/60] feat(aptos): simplify code --- rust/tw_aptos/src/transaction_builder.rs | 48 +++++------------------- 1 file changed, 9 insertions(+), 39 deletions(-) diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index 3a23ca98c4d..d035d76eb6b 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -112,34 +112,33 @@ impl TransactionFactory { .with_transaction_expiration_time(input.expiration_timestamp_secs); match input.transaction_payload { OneOftransaction_payload::transfer(transfer) => { - return factory.implicitly_create_user_account_and_transfer(AccountAddress::from_str(&transfer.to).unwrap(), transfer.amount); + factory.implicitly_create_user_account_and_transfer(AccountAddress::from_str(&transfer.to).unwrap(), transfer.amount) } OneOftransaction_payload::token_transfer(token_transfer) => { let func = token_transfer.function.unwrap(); - return factory.coins_transfer(AccountAddress::from_str(&token_transfer.to).unwrap(), token_transfer.amount, + factory.coins_transfer(AccountAddress::from_str(&token_transfer.to).unwrap(), token_transfer.amount, convert_proto_struct_tag_to_type_tag(func), - ); + ) } OneOftransaction_payload::create_account(create_account) => { - return factory.create_user_account(AccountAddress::from_str(&create_account.auth_key).unwrap()); + factory.create_user_account(AccountAddress::from_str(&create_account.auth_key).unwrap()) } OneOftransaction_payload::nft_message(nft_message) => { - return factory.nft_ops(nft_message.into()); + factory.nft_ops(nft_message.into()) } OneOftransaction_payload::register_token(register_token) => { - return factory.register_token(convert_proto_struct_tag_to_type_tag(register_token.function.unwrap())) + factory.register_token(convert_proto_struct_tag_to_type_tag(register_token.function.unwrap())) } OneOftransaction_payload::liquid_staking_message(msg) => { - return factory.liquid_staking_ops(msg.into()) + factory.liquid_staking_ops(msg.into()) } OneOftransaction_payload::token_transfer_coins(token_transfer_coins) => { let func = token_transfer_coins.function.unwrap(); - return factory.implicitly_create_user_and_coins_transfer(AccountAddress::from_str(&token_transfer_coins.to).unwrap(), token_transfer_coins.amount, + factory.implicitly_create_user_and_coins_transfer(AccountAddress::from_str(&token_transfer_coins.to).unwrap(), token_transfer_coins.amount, convert_proto_struct_tag_to_type_tag(func)) } - OneOftransaction_payload::None => {} + OneOftransaction_payload::None => {todo!()} } - todo!() } pub fn with_max_gas_amount(mut self, max_gas_amount: u64) -> Self { @@ -157,23 +156,6 @@ impl TransactionFactory { self } - pub fn with_chain_id(mut self, chain_id: u8) -> Self { - self.chain_id = chain_id; - self - } - - pub fn get_max_gas_amount(&self) -> u64 { - self.max_gas_amount - } - - pub fn get_gas_unit_price(&self) -> u64 { - self.gas_unit_price - } - - pub fn get_transaction_expiration_time(&self) -> u64 { - self.transaction_expiration_time - } - pub fn payload(&self, payload: TransactionPayload) -> TransactionBuilder { self.transaction_builder(payload) } @@ -231,13 +213,6 @@ impl TransactionFactory { amount: u64, coin_type: TypeTag) -> TransactionBuilder { self.payload(aptos_account_transfer_coins(coin_type, to, amount)) } - pub fn transfer(&self, _to: AccountAddress, _amount: u64) -> TransactionBuilder { - todo!() - } - - pub fn account_transfer(&self, _to: AccountAddress, _amount: u64) -> TransactionBuilder { - todo!() - } fn transaction_builder(&self, payload: TransactionPayload) -> TransactionBuilder { TransactionBuilder { @@ -284,11 +259,6 @@ mod tests { coin_type: TypeTag, } - pub struct LiquidStakingStake { - smart_contract_address: AccountAddress, - amount: u64, - } - pub enum OpsDetails { RegisterToken(RegisterToken), LiquidStakingOps(LiquidStakingOperation), From be40eec37c90c0e194197dc877c5ae5acb127924 Mon Sep 17 00:00:00 2001 From: Milerius Date: Thu, 9 Nov 2023 17:06:12 -0500 Subject: [PATCH 35/60] feat(aptos): add unstake ops --- rust/tw_aptos/src/liquid_staking.rs | 31 ++++++++++-- rust/tw_aptos/src/transaction_builder.rs | 61 ++++++++++++++++++++++-- 2 files changed, 84 insertions(+), 8 deletions(-) diff --git a/rust/tw_aptos/src/liquid_staking.rs b/rust/tw_aptos/src/liquid_staking.rs index 148473ef089..8bea7c84444 100644 --- a/rust/tw_aptos/src/liquid_staking.rs +++ b/rust/tw_aptos/src/liquid_staking.rs @@ -29,14 +29,35 @@ pub fn tortuga_stake( )) } +pub fn tortuga_unstake( + smart_contract_address: AccountAddress, + amount: u64, +) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + smart_contract_address, + ident_str!("stake_router").to_owned(), + ), + ident_str!("unstake").to_owned(), + vec![], + vec![bcs::to_bytes(&amount).unwrap()], + json!([amount.to_string()]), + )) +} + pub struct Stake { pub amount: u64, pub smart_contract_address: AccountAddress, } +pub struct Unstake { + pub amount: u64, + pub smart_contract_address: AccountAddress, +} + pub enum LiquidStakingOperation { Stake(Stake), - Unstake, + Unstake(Unstake), Claim, } @@ -46,9 +67,11 @@ impl From> for LiquidStakingOperation { OneOfliquid_stake_transaction_payload::stake(stake_msg) => { LiquidStakingOperation::Stake(Stake { amount: stake_msg.amount, smart_contract_address: AccountAddress::from_str(&value.smart_contract_address).unwrap() }) } - OneOfliquid_stake_transaction_payload::unstake(_) => {todo!()} - OneOfliquid_stake_transaction_payload::claim(_) => {todo!()} - OneOfliquid_stake_transaction_payload::None => {todo!()} + OneOfliquid_stake_transaction_payload::unstake(unstake_msg) => { + LiquidStakingOperation::Unstake(Unstake { amount: unstake_msg.amount, smart_contract_address: AccountAddress::from_str(&value.smart_contract_address).unwrap() }) + } + OneOfliquid_stake_transaction_payload::claim(_) => { todo!() } + OneOfliquid_stake_transaction_payload::None => { todo!() } } } } \ No newline at end of file diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index d035d76eb6b..4bf7691e638 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -13,7 +13,7 @@ use crate::transaction::RawTransaction; use crate::transaction_payload::{convert_proto_struct_tag_to_type_tag, EntryFunction, TransactionPayload}; use tw_proto::Aptos::Proto::SigningInput; use crate::aptos_move_packages::{aptos_account_create_account, aptos_account_transfer, aptos_account_transfer_coins, coin_transfer, managed_coin_register, token_transfers_cancel_offer_script, token_transfers_claim_script, token_transfers_offer_script}; -use crate::liquid_staking::{LiquidStakingOperation, tortuga_stake}; +use crate::liquid_staking::{LiquidStakingOperation, tortuga_stake, tortuga_unstake}; use crate::nft::NftOperation; pub struct TransactionBuilder { @@ -191,7 +191,9 @@ impl TransactionFactory { LiquidStakingOperation::Stake(stake) => { self.payload(tortuga_stake(stake.smart_contract_address, stake.amount)) } - LiquidStakingOperation::Unstake => {todo!()} + LiquidStakingOperation::Unstake(unstake) => { + self.payload(tortuga_unstake(unstake.smart_contract_address, unstake.amount)) + } LiquidStakingOperation::Claim => {todo!()} } } @@ -237,7 +239,7 @@ mod tests { use move_core_types::account_address::AccountAddress; use move_core_types::language_storage::TypeTag; use tw_encoding::hex; - use crate::liquid_staking::{LiquidStakingOperation, Stake}; + use crate::liquid_staking::{LiquidStakingOperation, Stake, Unstake}; use crate::nft::{Offer, NftOperation, Claim}; use crate::transaction_builder::TransactionFactory; @@ -277,6 +279,7 @@ mod tests { chain_id: u8, max_gas_amount: u64, timestamp: u64, + gas_unit_price: u64, expected_raw_txn_bytes_str: &str, expected_signature_str: &str, expected_encoded_txn_str: &str, @@ -287,7 +290,7 @@ mod tests { let keypair = tw_keypair::ed25519::sha512::KeyPair::try_from(keypair_str).unwrap(); let factory = TransactionFactory::new(chain_id) .with_max_gas_amount(max_gas_amount) - .with_gas_unit_price(100) + .with_gas_unit_price(gas_unit_price) .with_transaction_expiration_time(timestamp); let mut builder = match transaction_type { @@ -365,6 +368,7 @@ mod tests { 33, 3296766, 3664390082, + 100, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021", "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", @@ -403,6 +407,7 @@ mod tests { 33, 3296766, 3664390082, + 100, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada0000000021", // Expected raw transaction bytes "fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", // Expected signature "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", // Expected encoded transaction @@ -440,6 +445,7 @@ mod tests { 32, 3296766, 3664390082, + 100, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada0000000020", // Expected raw transaction bytes "7643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", // Expected signature "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada00000000200020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c407643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", // Expected encoded transaction @@ -475,6 +481,7 @@ mod tests { 2, 3296766, 3664390082, + 100, "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes "af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", // Expected signature "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada00000000020020d1d99b67e37b483161a0fa369c46f34a3be4863c20e20fc7cdc669c0826a411340af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", // Expected encoded transaction @@ -520,6 +527,7 @@ mod tests { 2, 3296766, 3664390082, + 100, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes "826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", // Expected signature "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", // Expected encoded transaction @@ -565,6 +573,7 @@ mod tests { 2, 3296766, 3664390082, + 100, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes "ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", // Expected signature "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", // Expected encoded transaction @@ -609,6 +618,7 @@ mod tests { 2, 2000000, 3664390082, + 100, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3017000000000000000200000000000000000000000000000000000000000000000000000000000000010c6d616e616765645f636f696e0872656769737465720107e4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379096d6f76655f636f696e084d6f7665436f696e000080841e00000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes "e230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100", // Expected signature "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3017000000000000000200000000000000000000000000000000000000000000000000000000000000010c6d616e616765645f636f696e0872656769737465720107e4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379096d6f76655f636f696e084d6f7665436f696e000080841e00000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40e230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100", // Expected encoded transaction @@ -644,6 +654,7 @@ mod tests { 1, 2000, 3664390082, + 100, "1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf2502000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e730107e9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9086d65655f636f696e074d6565436f696e000220b7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c081027000000000000d0070000000000006400000000000000c2276ada0000000001", // Expected raw transaction bytes "30ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d", // Expected signature "1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf2502000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e730107e9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9086d65655f636f696e074d6565436f696e000220b7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c081027000000000000d0070000000000006400000000000000c2276ada0000000001002062e7a6a486553b56a53e89dfae3f780693e537e5b0a7ed33290780e581ca83694030ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d", // Expected encoded transaction @@ -679,6 +690,7 @@ mod tests { 1, 5554, 1670240203, + 100, "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1300000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000b2150000000000006400000000000000cbd78d630000000001", // Expected raw transaction bytes "22d3166c3003f9c24a35fd39c71eb27e0d2bb82541be610822165c9283f56fefe5a9d46421b9caf174995bd8f83141e60ea8cff521ecf4741fe19e6ae9a5680d", // Expected signature "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1300000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000b2150000000000006400000000000000cbd78d630000000001002089e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc44022d3166c3003f9c24a35fd39c71eb27e0d2bb82541be610822165c9283f56fefe5a9d46421b9caf174995bd8f83141e60ea8cff521ecf4741fe19e6ae9a5680d", // Expected encoded transaction @@ -708,4 +720,45 @@ mod tests { }))), ); } + + #[test] + fn test_implicit_aptos_tortuga_unstake() { + setup_transaction( + "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address + "786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05", // Keypair + "liquid_staking_ops", + 20, // Sequence number + 1, + 2371, + 1670304949, + 120, + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1400000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e9050000000043090000000000007800000000000000b5d48e630000000001", // Expected raw transaction bytes + "6994b917432ad70ae84d2ce1484e6aece589a68aad1b7c6e38c9697f2a012a083a3a755c5e010fd3d0f149a75dd8d257acbd09f10800e890074e5ad384314d0c", // Expected signature + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1400000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e9050000000043090000000000007800000000000000b5d48e630000000001002089e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4406994b917432ad70ae84d2ce1484e6aece589a68aad1b7c6e38c9697f2a012a083a3a755c5e010fd3d0f149a75dd8d257acbd09f10800e890074e5ad384314d0c", // Expected encoded transaction + r#"{ + "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", + "sequence_number": "20", + "max_gas_amount": "2371", + "gas_unit_price": "120", + "expiration_timestamp_secs": "1670304949", + "payload": { + "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::unstake", + "type_arguments": [], + "arguments": [ + "99178100" + ], + "type": "entry_function_payload" + }, + "signature": { + "public_key": "0x89e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4", + "signature": "0x6994b917432ad70ae84d2ce1484e6aece589a68aad1b7c6e38c9697f2a012a083a3a755c5e010fd3d0f149a75dd8d257acbd09f10800e890074e5ad384314d0c", + "type": "ed25519_signature" + } + }"#, // Expected JSON literal + Some(OpsDetails::LiquidStakingOps(LiquidStakingOperation::Unstake(Unstake { + amount: 99178100, + smart_contract_address: AccountAddress::from_str("0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f").unwrap(), + }))), + ); + } } From b057c02df1995d6f1a28d062e1f377af9fc77198 Mon Sep 17 00:00:00 2001 From: Milerius Date: Thu, 9 Nov 2023 17:12:30 -0500 Subject: [PATCH 36/60] feat(aptos): add claim liquid staking ops --- rust/tw_aptos/src/liquid_staking.rs | 27 +++++++++- rust/tw_aptos/src/transaction_builder.rs | 68 +++++++++++++++++++----- 2 files changed, 81 insertions(+), 14 deletions(-) diff --git a/rust/tw_aptos/src/liquid_staking.rs b/rust/tw_aptos/src/liquid_staking.rs index 8bea7c84444..35b40c007b0 100644 --- a/rust/tw_aptos/src/liquid_staking.rs +++ b/rust/tw_aptos/src/liquid_staking.rs @@ -45,6 +45,22 @@ pub fn tortuga_unstake( )) } +pub fn tortuga_claim( + smart_contract_address: AccountAddress, + idx: u64, +) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + smart_contract_address, + ident_str!("stake_router").to_owned(), + ), + ident_str!("claim").to_owned(), + vec![], + vec![bcs::to_bytes(&idx).unwrap()], + json!([idx.to_string()]), + )) +} + pub struct Stake { pub amount: u64, pub smart_contract_address: AccountAddress, @@ -55,10 +71,15 @@ pub struct Unstake { pub smart_contract_address: AccountAddress, } +pub struct Claim { + pub idx: u64, + pub smart_contract_address: AccountAddress, +} + pub enum LiquidStakingOperation { Stake(Stake), Unstake(Unstake), - Claim, + Claim(Claim), } impl From> for LiquidStakingOperation { @@ -70,7 +91,9 @@ impl From> for LiquidStakingOperation { OneOfliquid_stake_transaction_payload::unstake(unstake_msg) => { LiquidStakingOperation::Unstake(Unstake { amount: unstake_msg.amount, smart_contract_address: AccountAddress::from_str(&value.smart_contract_address).unwrap() }) } - OneOfliquid_stake_transaction_payload::claim(_) => { todo!() } + OneOfliquid_stake_transaction_payload::claim(claim) => { + LiquidStakingOperation::Claim(Claim { idx: claim.idx, smart_contract_address: AccountAddress::from_str(&value.smart_contract_address).unwrap() }) + } OneOfliquid_stake_transaction_payload::None => { todo!() } } } diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index 4bf7691e638..f27a370bb2c 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -13,7 +13,7 @@ use crate::transaction::RawTransaction; use crate::transaction_payload::{convert_proto_struct_tag_to_type_tag, EntryFunction, TransactionPayload}; use tw_proto::Aptos::Proto::SigningInput; use crate::aptos_move_packages::{aptos_account_create_account, aptos_account_transfer, aptos_account_transfer_coins, coin_transfer, managed_coin_register, token_transfers_cancel_offer_script, token_transfers_claim_script, token_transfers_offer_script}; -use crate::liquid_staking::{LiquidStakingOperation, tortuga_stake, tortuga_unstake}; +use crate::liquid_staking::{LiquidStakingOperation, tortuga_claim, tortuga_stake, tortuga_unstake}; use crate::nft::NftOperation; pub struct TransactionBuilder { @@ -117,7 +117,7 @@ impl TransactionFactory { OneOftransaction_payload::token_transfer(token_transfer) => { let func = token_transfer.function.unwrap(); factory.coins_transfer(AccountAddress::from_str(&token_transfer.to).unwrap(), token_transfer.amount, - convert_proto_struct_tag_to_type_tag(func), + convert_proto_struct_tag_to_type_tag(func), ) } OneOftransaction_payload::create_account(create_account) => { @@ -135,9 +135,9 @@ impl TransactionFactory { OneOftransaction_payload::token_transfer_coins(token_transfer_coins) => { let func = token_transfer_coins.function.unwrap(); factory.implicitly_create_user_and_coins_transfer(AccountAddress::from_str(&token_transfer_coins.to).unwrap(), token_transfer_coins.amount, - convert_proto_struct_tag_to_type_tag(func)) + convert_proto_struct_tag_to_type_tag(func)) } - OneOftransaction_payload::None => {todo!()} + OneOftransaction_payload::None => { todo!() } } } @@ -194,7 +194,9 @@ impl TransactionFactory { LiquidStakingOperation::Unstake(unstake) => { self.payload(tortuga_unstake(unstake.smart_contract_address, unstake.amount)) } - LiquidStakingOperation::Claim => {todo!()} + LiquidStakingOperation::Claim(claim) => { + self.payload(tortuga_claim(claim.smart_contract_address, claim.idx)) + } } } @@ -212,7 +214,7 @@ impl TransactionFactory { } pub fn implicitly_create_user_and_coins_transfer(&self, to: AccountAddress, - amount: u64, coin_type: TypeTag) -> TransactionBuilder { + amount: u64, coin_type: TypeTag) -> TransactionBuilder { self.payload(aptos_account_transfer_coins(coin_type, to, amount)) } @@ -239,6 +241,7 @@ mod tests { use move_core_types::account_address::AccountAddress; use move_core_types::language_storage::TypeTag; use tw_encoding::hex; + use crate::liquid_staking; use crate::liquid_staking::{LiquidStakingOperation, Stake, Unstake}; use crate::nft::{Offer, NftOperation, Claim}; use crate::transaction_builder::TransactionFactory; @@ -247,6 +250,7 @@ mod tests { pub struct AccountCreation { to: String, } + pub struct Transfer { to: String, amount: u64, @@ -300,14 +304,13 @@ mod tests { } else { panic!("Unsupported arguments") } - }, + } "create_account" => { if let OpsDetails::AccountCreation(account) = ops_details.unwrap() { factory.create_user_account(AccountAddress::from_str(&account.to).unwrap()) } else { panic!("Unsupported arguments") } - } "coin_transfer" => { if let OpsDetails::TokenTransfer(token_transfer) = ops_details.unwrap() { @@ -315,28 +318,28 @@ mod tests { } else { panic!("Unsupported arguments") } - }, + } "implicit_coin_transfer" => { if let OpsDetails::ImplicitTokenTransfer(token_transfer) = ops_details.unwrap() { factory.implicitly_create_user_and_coins_transfer(AccountAddress::from_str(&token_transfer.transfer.to).unwrap(), token_transfer.transfer.amount, token_transfer.tag) } else { panic!("Unsupported arguments") } - }, + } "nft_ops" => { if let OpsDetails::NftOps(nft) = ops_details.unwrap() { factory.nft_ops(nft) } else { panic!("Unsupported arguments") } - }, + } "register_token" => { if let OpsDetails::RegisterToken(register_token) = ops_details.unwrap() { factory.register_token(register_token.coin_type) } else { panic!("Unsupported arguments") } - }, + } "liquid_staking_ops" => { if let OpsDetails::LiquidStakingOps(liquid_staking_ops) = ops_details.unwrap() { factory.liquid_staking_ops(liquid_staking_ops) @@ -761,4 +764,45 @@ mod tests { }))), ); } + + #[test] + fn test_implicit_aptos_tortuga_claim() { + setup_transaction( + "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address + "786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05", // Keypair + "liquid_staking_ops", + 28, // Sequence number + 1, + 10, + 1682066783, + 148, + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657205636c61696d00010800000000000000000a0000000000000094000000000000005f4d42640000000001", // Expected raw transaction bytes + "c936584f89777e1fe2d5dd75cd8d9c514efc445810ba22f462b6fe7229c6ec7fc1c8b25d3e233eafaa8306433b3220235e563498ba647be38cac87ff618e3d03", // Expected signature + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657205636c61696d00010800000000000000000a0000000000000094000000000000005f4d42640000000001002089e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc440c936584f89777e1fe2d5dd75cd8d9c514efc445810ba22f462b6fe7229c6ec7fc1c8b25d3e233eafaa8306433b3220235e563498ba647be38cac87ff618e3d03", // Expected encoded transaction + r#"{ + "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", + "sequence_number": "28", + "max_gas_amount": "10", + "gas_unit_price": "148", + "expiration_timestamp_secs": "1682066783", + "payload": { + "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::claim", + "type_arguments": [], + "arguments": [ + "0" + ], + "type": "entry_function_payload" + }, + "signature": { + "public_key": "0x89e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4", + "signature": "0xc936584f89777e1fe2d5dd75cd8d9c514efc445810ba22f462b6fe7229c6ec7fc1c8b25d3e233eafaa8306433b3220235e563498ba647be38cac87ff618e3d03", + "type": "ed25519_signature" + } + }"#, // Expected JSON literal + Some(OpsDetails::LiquidStakingOps(LiquidStakingOperation::Claim(liquid_staking::Claim { + idx: 0, + smart_contract_address: AccountAddress::from_str("0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f").unwrap(), + }))), + ); + } } From 9bb0f4f12bc5cd0e9d5ab84e310cc4f7cdf639c6 Mon Sep 17 00:00:00 2001 From: Milerius Date: Fri, 10 Nov 2023 07:52:32 -0500 Subject: [PATCH 37/60] feat(signer): start moving signer tests to unit tests folder --- rust/tw_aptos/src/signer.rs | 9 ++- rust/tw_aptos/tests/signer.rs | 142 ++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 rust/tw_aptos/tests/signer.rs diff --git a/rust/tw_aptos/src/signer.rs b/rust/tw_aptos/src/signer.rs index 2f674b465b4..26094a53d95 100644 --- a/rust/tw_aptos/src/signer.rs +++ b/rust/tw_aptos/src/signer.rs @@ -41,11 +41,12 @@ impl Signer { let key_pair = ed25519::sha512::KeyPair::try_from(input.private_key.as_ref())?; let builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone()); let sender = Address::from_str(&input.sender)?; - let raw_tx = builder.sender(sender.inner()).sequence_number(input.sequence_number as u64).build().sign(key_pair)?; + let signed_tx = builder.sender(sender.inner()).sequence_number(input.sequence_number as u64).build().sign(key_pair)?; Ok(Proto::SigningOutput { - raw_txn: raw_tx.raw_txn_bytes().clone().into(), - encoded: raw_tx.encoded().clone().into(), - authenticator: Some((*raw_tx.authenticator()).clone().into()), + raw_txn: signed_tx.raw_txn_bytes().clone().into(), + encoded: signed_tx.encoded().clone().into(), + authenticator: Some((*signed_tx.authenticator()).clone().into()), + json: signed_tx.to_json().to_string().into(), ..Proto::SigningOutput::default() }) } diff --git a/rust/tw_aptos/tests/signer.rs b/rust/tw_aptos/tests/signer.rs new file mode 100644 index 00000000000..874eabb758b --- /dev/null +++ b/rust/tw_aptos/tests/signer.rs @@ -0,0 +1,142 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use move_core_types::language_storage::TypeTag; +use tw_aptos::liquid_staking::LiquidStakingOperation; +use tw_aptos::nft::NftOperation; +use tw_aptos::signer::{Signer, StandardAptosContext}; +use tw_coin_entry::error::SigningErrorType; +use tw_encoding::hex; +use tw_proto::Aptos::Proto; +use tw_proto::Aptos::Proto::{SigningInput, SigningOutput}; + + +pub struct AccountCreation { + to: String, +} + +pub struct Transfer { + to: String, + amount: u64, +} + +pub struct TokenTransfer { + transfer: Transfer, + tag: TypeTag, +} + +pub struct RegisterToken { + coin_type: TypeTag, +} + +pub enum OpsDetails { + RegisterToken(RegisterToken), + LiquidStakingOps(LiquidStakingOperation), + AccountCreation(AccountCreation), + Transfer(Transfer), + TokenTransfer(TokenTransfer), + ImplicitTokenTransfer(TokenTransfer), + NftOps(NftOperation), +} + +fn setup_proto_transaction<'a>(sender: &'a str, + keypair_str: &'a str, + transaction_type: &'a str, + sequence_number: i64, + chain_id: u32, + max_gas_amount: u64, + timestamp: u64, + gas_unit_price: u64, + ops_details: Option) -> SigningInput<'a> { + let private = hex::decode(keypair_str).unwrap(); + + let payload: Proto::mod_SigningInput::OneOftransaction_payload = match transaction_type { + "transfer" => { + if let OpsDetails::Transfer(transfer) = ops_details.unwrap() { + Proto::mod_SigningInput::OneOftransaction_payload::transfer(Proto::TransferMessage { + to: transfer.to.into(), + amount: transfer.amount, + }) + } else { + panic!("Unsupported arguments") + } + } + _ => { todo!() } + }; + + + let input = Proto::SigningInput { + chain_id, + sender: sender.into(), + sequence_number, + max_gas_amount, + gas_unit_price, + expiration_timestamp_secs: timestamp, + private_key: private.into(), + any_encoded: Default::default(), + transaction_payload: payload + }; + + input +} + +fn test_tx_result( + output: SigningOutput, + expected_raw_txn_bytes_str: &str, + expected_signature_str: &str, + expected_encoded_txn_str: &str, + json_literal: &str) { + + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + + assert_eq!(hex::encode(output.raw_txn.to_vec(), false), expected_raw_txn_bytes_str); + assert_eq!(hex::encode(output.authenticator.unwrap().signature.to_vec(), false), expected_signature_str); + assert_eq!(hex::encode(output.encoded.to_vec(), false), expected_encoded_txn_str); + + let json_value_expected: serde_json::Value = serde_json::from_str(json_literal).unwrap(); + let json_value: serde_json::Value = serde_json::from_str(output.json.as_ref()).unwrap(); + assert_eq!(json_value, json_value_expected); +} + +#[test] +fn test_aptos_sign_transaction_transfer() { + let input = setup_proto_transaction("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", + "transfer", + 99, + 33, + 3296766, + 3664390082, + 100, Some(OpsDetails::Transfer(Transfer { + to: "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30".to_string(), + amount: 1000, + }))); + let output = Signer::::sign_proto(input); + test_tx_result(output, + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021", + "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": ["0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], + "function": "0x1::aptos_account::transfer", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "99", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", + "type": "ed25519_signature" + } + }"#); +} \ No newline at end of file From 2a70b27ae01f1d0949db952fac3a40af7aa7cfd6 Mon Sep 17 00:00:00 2001 From: Milerius Date: Fri, 10 Nov 2023 07:59:05 -0500 Subject: [PATCH 38/60] feat(signer): continue moving signer tests to unit tests folder --- rust/tw_aptos/tests/signer.rs | 94 +++++++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 22 deletions(-) diff --git a/rust/tw_aptos/tests/signer.rs b/rust/tw_aptos/tests/signer.rs index 874eabb758b..ac35c0f5601 100644 --- a/rust/tw_aptos/tests/signer.rs +++ b/rust/tw_aptos/tests/signer.rs @@ -43,14 +43,15 @@ pub enum OpsDetails { } fn setup_proto_transaction<'a>(sender: &'a str, - keypair_str: &'a str, - transaction_type: &'a str, - sequence_number: i64, - chain_id: u32, - max_gas_amount: u64, - timestamp: u64, - gas_unit_price: u64, - ops_details: Option) -> SigningInput<'a> { + keypair_str: &'a str, + transaction_type: &'a str, + sequence_number: i64, + chain_id: u32, + max_gas_amount: u64, + timestamp: u64, + gas_unit_price: u64, + any_encoded: &'a str, + ops_details: Option) -> SigningInput<'a> { let private = hex::decode(keypair_str).unwrap(); let payload: Proto::mod_SigningInput::OneOftransaction_payload = match transaction_type { @@ -64,11 +65,20 @@ fn setup_proto_transaction<'a>(sender: &'a str, panic!("Unsupported arguments") } } + "create_account" => { + if let OpsDetails::AccountCreation(account) = ops_details.unwrap() { + Proto::mod_SigningInput::OneOftransaction_payload::create_account(Proto::CreateAccountMessage { + auth_key: account.to.into(), + }) + } else { + panic!("Unsupported arguments") + } + } _ => { todo!() } }; - let input = Proto::SigningInput { + let input = SigningInput { chain_id, sender: sender.into(), sequence_number, @@ -76,8 +86,8 @@ fn setup_proto_transaction<'a>(sender: &'a str, gas_unit_price, expiration_timestamp_secs: timestamp, private_key: private.into(), - any_encoded: Default::default(), - transaction_payload: payload + any_encoded: any_encoded.into(), + transaction_payload: payload, }; input @@ -89,7 +99,6 @@ fn test_tx_result( expected_signature_str: &str, expected_encoded_txn_str: &str, json_literal: &str) { - assert_eq!(output.error, SigningErrorType::OK); assert!(output.error_message.is_empty()); @@ -106,16 +115,18 @@ fn test_tx_result( #[test] fn test_aptos_sign_transaction_transfer() { let input = setup_proto_transaction("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", - "transfer", - 99, - 33, - 3296766, - 3664390082, - 100, Some(OpsDetails::Transfer(Transfer { - to: "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30".to_string(), - amount: 1000, - }))); + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", + "transfer", + 99, + 33, + 3296766, + 3664390082, + 100, + "", + Some(OpsDetails::Transfer(Transfer { + to: "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30".to_string(), + amount: 1000, + }))); let output = Signer::::sign_proto(input); test_tx_result(output, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021", @@ -139,4 +150,43 @@ fn test_aptos_sign_transaction_transfer() { "type": "ed25519_signature" } }"#); +} + +#[test] +fn test_aptos_sign_create_account() { + let input = setup_proto_transaction("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "create_account", + 0, // Sequence number + 33, + 3296766, + 3664390082, + 100, + "", + Some(OpsDetails::AccountCreation(AccountCreation { + to: "0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e".to_string(), + }))); + let output = Signer::::sign_proto(input); + test_tx_result(output, + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada0000000021", // Expected raw transaction bytes + "fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", // Expected signature + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": ["0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e"], + "function": "0x1::aptos_account::create_account", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "0", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0xfcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", + "type": "ed25519_signature" + } + }"#); } \ No newline at end of file From 233d14303d0e98247c9cf237f8011ad143afa232 Mon Sep 17 00:00:00 2001 From: Milerius Date: Fri, 10 Nov 2023 09:00:36 -0500 Subject: [PATCH 39/60] feat(signer): continue moving signer tests to unit tests folder --- rust/tw_aptos/src/transaction_payload.rs | 20 ++++++++-- rust/tw_aptos/tests/signer.rs | 51 ++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/rust/tw_aptos/src/transaction_payload.rs b/rust/tw_aptos/src/transaction_payload.rs index 2a8ec48031c..4537b87b8a6 100644 --- a/rust/tw_aptos/src/transaction_payload.rs +++ b/rust/tw_aptos/src/transaction_payload.rs @@ -4,11 +4,13 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use std::default::Default; use std::str::FromStr; use move_core_types::identifier::Identifier; use move_core_types::language_storage::{ModuleId, TypeTag}; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; +use tw_proto::Aptos; use crate::{serde_helper::vec_bytes}; #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] @@ -19,13 +21,25 @@ pub struct EntryFunction { #[serde(with = "vec_bytes")] args: Vec>, #[serde(skip_serializing)] - json_args: Value + json_args: Value, } -pub fn convert_proto_struct_tag_to_type_tag(struct_tag: tw_proto::Aptos::Proto::StructTag) -> TypeTag { +pub fn convert_proto_struct_tag_to_type_tag(struct_tag: Aptos::Proto::StructTag) -> TypeTag { TypeTag::from_str(&format!("{}::{}::{}", struct_tag.account_address, struct_tag.module, struct_tag.name)).unwrap() } +pub fn convert_type_tag_to_struct_tag(type_tag: TypeTag) -> Aptos::Proto::StructTag<'static> { + if let TypeTag::Struct(st) = type_tag { + Aptos::Proto::StructTag { + account_address: st.address.to_hex_literal().into(), + module: st.module.to_string().into(), + name: st.name.to_string().into(), + } + } else { + Aptos::Proto::StructTag::default() + } +} + impl EntryFunction { fn to_json(&self) -> Value { // Create a JSON array from the `ty_args` field by filtering and mapping @@ -77,7 +91,7 @@ impl EntryFunction { function, ty_args, args, - json_args + json_args, } } } diff --git a/rust/tw_aptos/tests/signer.rs b/rust/tw_aptos/tests/signer.rs index ac35c0f5601..adad171d046 100644 --- a/rust/tw_aptos/tests/signer.rs +++ b/rust/tw_aptos/tests/signer.rs @@ -4,10 +4,12 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use std::str::FromStr; use move_core_types::language_storage::TypeTag; use tw_aptos::liquid_staking::LiquidStakingOperation; use tw_aptos::nft::NftOperation; use tw_aptos::signer::{Signer, StandardAptosContext}; +use tw_aptos::transaction_payload::convert_type_tag_to_struct_tag; use tw_coin_entry::error::SigningErrorType; use tw_encoding::hex; use tw_proto::Aptos::Proto; @@ -74,6 +76,17 @@ fn setup_proto_transaction<'a>(sender: &'a str, panic!("Unsupported arguments") } } + "coin_transfer" => { + if let OpsDetails::TokenTransfer(token_transfer) = ops_details.unwrap() { + Proto::mod_SigningInput::OneOftransaction_payload::token_transfer(Proto::TokenTransferMessage { + to: token_transfer.transfer.to.into(), + amount: token_transfer.transfer.amount, + function: Some(convert_type_tag_to_struct_tag(token_transfer.tag)), + }) + } else { + panic!("Unsupported arguments") + } + } _ => { todo!() } }; @@ -189,4 +202,42 @@ fn test_aptos_sign_create_account() { "type": "ed25519_signature" } }"#); +} + +#[test] +fn test_aptos_sign_coin_transfer() { + let input = setup_proto_transaction("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "coin_transfer", + 24, // Sequence number + 32, + 3296766, + 3664390082, + 100, + "", + Some(OpsDetails::TokenTransfer(TokenTransfer { transfer: Transfer { to: "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30".to_string(), amount: 100000 }, tag: TypeTag::from_str("0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC").unwrap() })), + ); + let output = Signer::::sign_proto(input); + test_tx_result(output, + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada0000000020", // Expected raw transaction bytes + "7643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", // Expected signature + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada00000000200020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c407643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": ["0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","100000"], + "function": "0x1::coin::transfer", + "type": "entry_function_payload", + "type_arguments": ["0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC"] + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "24", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x7643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", + "type": "ed25519_signature" + } + }"#); } \ No newline at end of file From 9c8d10eda5543ce540ed33db7371463a07c3a32e Mon Sep 17 00:00:00 2001 From: Milerius Date: Fri, 10 Nov 2023 09:38:46 -0500 Subject: [PATCH 40/60] feat(signer): continue moving signer tests to unit tests folder --- rust/tw_aptos/src/nft.rs | 46 ++++++++ rust/tw_aptos/tests/signer.rs | 204 +++++++++++++++++++++++++++++++++- 2 files changed, 248 insertions(+), 2 deletions(-) diff --git a/rust/tw_aptos/src/nft.rs b/rust/tw_aptos/src/nft.rs index 7e1aa5c3e6c..ee323a5521e 100644 --- a/rust/tw_aptos/src/nft.rs +++ b/rust/tw_aptos/src/nft.rs @@ -43,6 +43,15 @@ impl From> for NftOperation { } } +impl From for NftMessage<'_> { + fn from(value: NftOperation) -> Self { + match value { + NftOperation::Claim(claim) => { NftMessage { nft_transaction_payload: OneOfnft_transaction_payload::claim_nft(claim.into()) } } + NftOperation::Offer(offer) => { NftMessage { nft_transaction_payload: OneOfnft_transaction_payload::offer_nft(offer.into()) } } + NftOperation::Cancel(cancel) => { NftMessage { nft_transaction_payload: OneOfnft_transaction_payload::cancel_offer_nft(cancel.into()) } } + } + } +} impl From> for Offer { fn from(value: OfferNftMessage) -> Self { Offer { @@ -56,6 +65,19 @@ impl From> for Offer { } } +impl From for OfferNftMessage<'_> { + fn from(value: Offer) -> Self { + OfferNftMessage { + receiver: value.receiver.to_hex_literal().into(), + creator: value.creator.to_hex_literal().into(), + collectionName: String::from_utf8_lossy(value.collection.as_slice()).to_string().into(), + name: String::from_utf8_lossy(&value.name).to_string().into(), + property_version: value.property_version, + amount: value.amount, + } + } +} + impl From> for Offer { fn from(value: CancelOfferNftMessage) -> Self { Offer { @@ -69,6 +91,18 @@ impl From> for Offer { } } +impl From for CancelOfferNftMessage<'_> { + fn from(value: Offer) -> Self { + CancelOfferNftMessage { + receiver: value.receiver.to_hex_literal().into(), + creator: value.creator.to_hex_literal().into(), + collectionName: String::from_utf8_lossy(value.collection.as_slice()).to_string().into(), + name: String::from_utf8_lossy(value.name.as_slice()).to_string().into(), + property_version: value.property_version, + } + } +} + impl From> for Claim { fn from(value: ClaimNftMessage) -> Self { Claim { @@ -79,4 +113,16 @@ impl From> for Claim { property_version: value.property_version, } } +} + +impl From for ClaimNftMessage<'_> { + fn from(value: Claim) -> Self { + ClaimNftMessage { + sender: value.sender.to_hex_literal().into(), + creator: value.creator.to_hex_literal().into(), + collectionName: String::from_utf8_lossy(value.collection.as_slice()).to_string().into(), + name: String::from_utf8_lossy(value.name.as_slice()).to_string().into(), + property_version: value.property_version, + } + } } \ No newline at end of file diff --git a/rust/tw_aptos/tests/signer.rs b/rust/tw_aptos/tests/signer.rs index adad171d046..be7c4fca2aa 100644 --- a/rust/tw_aptos/tests/signer.rs +++ b/rust/tw_aptos/tests/signer.rs @@ -5,9 +5,10 @@ // file LICENSE at the root of the source code distribution tree. use std::str::FromStr; +use move_core_types::account_address::AccountAddress; use move_core_types::language_storage::TypeTag; use tw_aptos::liquid_staking::LiquidStakingOperation; -use tw_aptos::nft::NftOperation; +use tw_aptos::nft::{Claim, NftOperation, Offer}; use tw_aptos::signer::{Signer, StandardAptosContext}; use tw_aptos::transaction_payload::convert_type_tag_to_struct_tag; use tw_coin_entry::error::SigningErrorType; @@ -87,6 +88,24 @@ fn setup_proto_transaction<'a>(sender: &'a str, panic!("Unsupported arguments") } } + "implicit_coin_transfer" => { + if let OpsDetails::ImplicitTokenTransfer(token_transfer) = ops_details.unwrap() { + Proto::mod_SigningInput::OneOftransaction_payload::token_transfer_coins(Proto::TokenTransferCoinsMessage { + to: token_transfer.transfer.to.into(), + amount: token_transfer.transfer.amount, + function: Some(convert_type_tag_to_struct_tag(token_transfer.tag)), + }) + } else { + panic!("Unsupported arguments") + } + } + "nft_ops" => { + if let OpsDetails::NftOps(nft) = ops_details.unwrap() { + Proto::mod_SigningInput::OneOftransaction_payload::nft_message(nft.into()) + } else { + panic!("Unsupported arguments") + } + } _ => { todo!() } }; @@ -216,7 +235,7 @@ fn test_aptos_sign_coin_transfer() { 100, "", Some(OpsDetails::TokenTransfer(TokenTransfer { transfer: Transfer { to: "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30".to_string(), amount: 100000 }, tag: TypeTag::from_str("0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC").unwrap() })), - ); + ); let output = Signer::::sign_proto(input); test_tx_result(output, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada0000000020", // Expected raw transaction bytes @@ -240,4 +259,185 @@ fn test_aptos_sign_coin_transfer() { "type": "ed25519_signature" } }"#); +} + +#[test] +fn test_implicit_aptos_sign_coin_transfer() { + let input = setup_proto_transaction("0x1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf25", // Sender's address + "e7f56c77189e03699a75d8ec5c090e41f3d9d4783bc49c33df8a93d915e10de8", // Keypair + "implicit_coin_transfer", + 2, // Sequence number + 1, + 2000, + 3664390082, + 100, + "", + Some(OpsDetails::ImplicitTokenTransfer(TokenTransfer { transfer: Transfer { to: "0xb7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c".to_string(), amount: 10000 }, tag: TypeTag::from_str("0xe9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9::mee_coin::MeeCoin").unwrap() })), + ); + let output = Signer::::sign_proto(input); + test_tx_result(output, + "1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf2502000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e730107e9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9086d65655f636f696e074d6565436f696e000220b7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c081027000000000000d0070000000000006400000000000000c2276ada0000000001", // Expected raw transaction bytes + "30ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d", // Expected signature + "1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf2502000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e730107e9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9086d65655f636f696e074d6565436f696e000220b7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c081027000000000000d0070000000000006400000000000000c2276ada0000000001002062e7a6a486553b56a53e89dfae3f780693e537e5b0a7ed33290780e581ca83694030ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "2000", + "payload": { + "arguments": ["0xb7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c","10000"], + "function": "0x1::aptos_account::transfer_coins", + "type": "entry_function_payload", + "type_arguments": ["0xe9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9::mee_coin::MeeCoin"] + }, + "sender": "0x1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf25", + "sequence_number": "2", + "signature": { + "public_key": "0x62e7a6a486553b56a53e89dfae3f780693e537e5b0a7ed33290780e581ca8369", + "signature": "0x30ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d", + "type": "ed25519_signature" + } + }"#); +} + +#[test] +fn test_aptos_nft_offer() { + let input = setup_proto_transaction("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", // Sender's address + "7bebb6d543d17f6fe4e685cfab239fa37896edd594ff859f1df32f244fb707e2", // Keypair + "nft_ops", + 1, // Sequence number + 2, + 3296766, + 3664390082, + 100, + "", + Some(OpsDetails::NftOps(NftOperation::Offer(Offer { + receiver: AccountAddress::from_str("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30").unwrap(), + creator: AccountAddress::from_str("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac").unwrap(), + collection: "Topaz Troopers".as_bytes().to_vec(), + name: "Topaz Trooper #20068".as_bytes().to_vec(), + property_version: 0, + amount: 1, + }))), + ); + let output = Signer::::sign_proto(input); + test_tx_result(output, + "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes + "af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", // Expected signature + "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada00000000020020d1d99b67e37b483161a0fa369c46f34a3be4863c20e20fc7cdc669c0826a411340af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": [ + "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", + "Topaz Troopers", "Topaz Trooper #20068", "0", "1"], + "function": "0x3::token_transfers::offer_script", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", + "sequence_number": "1", + "signature": { + "public_key": "0xd1d99b67e37b483161a0fa369c46f34a3be4863c20e20fc7cdc669c0826a4113", + "signature": "0xaf5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", + "type": "ed25519_signature" + } + }"#); +} + +#[test] +fn test_aptos_cancel_nft_offer() { + let input = setup_proto_transaction("0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "nft_ops", + 21, // Sequence number + 2, + 3296766, + 3664390082, + 100, + "", + Some(OpsDetails::NftOps(NftOperation::Cancel(Offer { + receiver: AccountAddress::from_str("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee").unwrap(), + creator: AccountAddress::from_str("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac").unwrap(), + collection: "Topaz Troopers".as_bytes().to_vec(), + name: "Topaz Trooper #20068".as_bytes().to_vec(), + property_version: 0, + amount: 0, + }))), + ); + let output = Signer::::sign_proto(input); + test_tx_result(output, + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes + "826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", // Expected signature + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": [ + "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", + "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", + "Topaz Troopers", "Topaz Trooper #20068", "0"], + "function": "0x3::token_transfers::cancel_offer_script", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "21", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", + "type": "ed25519_signature" + } + }"#); +} + +#[test] +fn test_aptos_nft_claim() { + let input = setup_proto_transaction("0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "nft_ops", + 19, // Sequence number + 2, + 3296766, + 3664390082, + 100, + "", + Some(OpsDetails::NftOps(NftOperation::Claim(Claim { + sender: AccountAddress::from_str("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee").unwrap(), + creator: AccountAddress::from_str("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac").unwrap(), + collection: "Topaz Troopers".as_bytes().to_vec(), + name: "Topaz Trooper #20068".as_bytes().to_vec(), + property_version: 0, + }))), + ); + let output = Signer::::sign_proto(input); + test_tx_result(output, + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes + "ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", // Expected signature + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": [ + "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", + "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", + "Topaz Troopers", "Topaz Trooper #20068", "0"], + "function": "0x3::token_transfers::claim_script", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "19", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0xede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", + "type": "ed25519_signature" + } + }"#); } \ No newline at end of file From cd383708e99100ae1361bf08bec7d79be8d6b084 Mon Sep 17 00:00:00 2001 From: Milerius Date: Fri, 10 Nov 2023 09:46:21 -0500 Subject: [PATCH 41/60] feat(signer): continue moving signer tests to unit tests folder (register_token) --- rust/tw_aptos/tests/signer.rs | 47 +++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/rust/tw_aptos/tests/signer.rs b/rust/tw_aptos/tests/signer.rs index be7c4fca2aa..12556850602 100644 --- a/rust/tw_aptos/tests/signer.rs +++ b/rust/tw_aptos/tests/signer.rs @@ -106,6 +106,15 @@ fn setup_proto_transaction<'a>(sender: &'a str, panic!("Unsupported arguments") } } + "register_token" => { + if let OpsDetails::RegisterToken(register_token) = ops_details.unwrap() { + Proto::mod_SigningInput::OneOftransaction_payload::register_token(Proto::ManagedTokensRegisterMessage { + function: Some(convert_type_tag_to_struct_tag(register_token.coin_type)), + }) + } else { + panic!("Unsupported arguments") + } + } _ => { todo!() } }; @@ -440,4 +449,42 @@ fn test_aptos_nft_claim() { "type": "ed25519_signature" } }"#); +} + +#[test] +fn test_aptos_register_token() { + let input = setup_proto_transaction("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "register_token", + 23, // Sequence number + 2, + 2000000, + 3664390082, + 100, + "", + Some(OpsDetails::RegisterToken(RegisterToken { coin_type: TypeTag::from_str("0xe4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379::move_coin::MoveCoin").unwrap() })), + ); + let output = Signer::::sign_proto(input); + test_tx_result(output, + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3017000000000000000200000000000000000000000000000000000000000000000000000000000000010c6d616e616765645f636f696e0872656769737465720107e4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379096d6f76655f636f696e084d6f7665436f696e000080841e00000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes + "e230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100", // Expected signature + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3017000000000000000200000000000000000000000000000000000000000000000000000000000000010c6d616e616765645f636f696e0872656769737465720107e4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379096d6f76655f636f696e084d6f7665436f696e000080841e00000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40e230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "2000000", + "payload": { + "arguments": [], + "function": "0x1::managed_coin::register", + "type": "entry_function_payload", + "type_arguments": ["0xe4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379::move_coin::MoveCoin"] + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "23", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0xe230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100", + "type": "ed25519_signature" + } + }"#); } \ No newline at end of file From e9c5bcfb62bf6aff5c25628dd995513597f213b1 Mon Sep 17 00:00:00 2001 From: Milerius Date: Fri, 10 Nov 2023 11:00:57 -0500 Subject: [PATCH 42/60] feat(signer): improve coverage aptos --- rust/tw_aptos/src/liquid_staking.rs | 48 +- rust/tw_aptos/src/transaction_builder.rs | 614 +---------------------- rust/tw_aptos/tests/signer.rs | 141 +++++- 3 files changed, 183 insertions(+), 620 deletions(-) diff --git a/rust/tw_aptos/src/liquid_staking.rs b/rust/tw_aptos/src/liquid_staking.rs index 35b40c007b0..621e25c5090 100644 --- a/rust/tw_aptos/src/liquid_staking.rs +++ b/rust/tw_aptos/src/liquid_staking.rs @@ -5,12 +5,16 @@ // file LICENSE at the root of the source code distribution tree. use std::str::FromStr; -use move_core_types::account_address::AccountAddress; -use move_core_types::ident_str; -use move_core_types::language_storage::ModuleId; +use move_core_types::{ + account_address::AccountAddress, + ident_str, + language_storage::ModuleId +}; use serde_json::json; -use tw_proto::Aptos::Proto::LiquidStaking; -use tw_proto::Aptos::Proto::mod_LiquidStaking::OneOfliquid_stake_transaction_payload; +use tw_proto::{ + Aptos::Proto::{LiquidStaking, TortugaClaim, TortugaStake, TortugaUnstake}, + Aptos::Proto::mod_LiquidStaking::OneOfliquid_stake_transaction_payload +}; use crate::transaction_payload::{EntryFunction, TransactionPayload}; pub fn tortuga_stake( @@ -97,4 +101,38 @@ impl From> for LiquidStakingOperation { OneOfliquid_stake_transaction_payload::None => { todo!() } } } +} + +impl From for LiquidStaking<'_> { + fn from(value: LiquidStakingOperation) -> Self { + match value { + LiquidStakingOperation::Stake(stake) => { + LiquidStaking { + smart_contract_address: stake.smart_contract_address.to_hex_literal().into(), + liquid_stake_transaction_payload: OneOfliquid_stake_transaction_payload::stake( + TortugaStake { + amount: stake.amount + }), + } + } + LiquidStakingOperation::Unstake(unstake) => { + LiquidStaking { + smart_contract_address: unstake.smart_contract_address.to_hex_literal().into(), + liquid_stake_transaction_payload: OneOfliquid_stake_transaction_payload::unstake( + TortugaUnstake { + amount: unstake.amount + }), + } + } + LiquidStakingOperation::Claim(claim) => { + LiquidStaking { + smart_contract_address: claim.smart_contract_address.to_hex_literal().into(), + liquid_stake_transaction_payload: OneOfliquid_stake_transaction_payload::claim( + TortugaClaim { + idx: claim.idx + }), + } + } + } + } } \ No newline at end of file diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index f27a370bb2c..9e2ab2a5aa0 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -10,7 +10,7 @@ use move_core_types::language_storage::TypeTag; use tw_proto::Aptos::Proto::mod_SigningInput::OneOftransaction_payload; use crate::constants::{GAS_UNIT_PRICE, MAX_GAS_AMOUNT}; use crate::transaction::RawTransaction; -use crate::transaction_payload::{convert_proto_struct_tag_to_type_tag, EntryFunction, TransactionPayload}; +use crate::transaction_payload::{convert_proto_struct_tag_to_type_tag, TransactionPayload}; use tw_proto::Aptos::Proto::SigningInput; use crate::aptos_move_packages::{aptos_account_create_account, aptos_account_transfer, aptos_account_transfer_coins, coin_transfer, managed_coin_register, token_transfers_cancel_offer_script, token_transfers_claim_script, token_transfers_offer_script}; use crate::liquid_staking::{LiquidStakingOperation, tortuga_claim, tortuga_stake, tortuga_unstake}; @@ -27,22 +27,6 @@ pub struct TransactionBuilder { } impl TransactionBuilder { - pub fn new( - payload: TransactionPayload, - expiration_timestamp_secs: u64, - chain_id: u8, - ) -> Self { - Self { - payload, - chain_id, - expiration_timestamp_secs, - max_gas_amount: MAX_GAS_AMOUNT, - gas_unit_price: std::cmp::max(GAS_UNIT_PRICE, 1), - sender: None, - sequence_number: None, - } - } - pub fn sender(mut self, sender: AccountAddress) -> Self { self.sender = Some(sender); self @@ -53,26 +37,6 @@ impl TransactionBuilder { self } - pub fn max_gas_amount(mut self, max_gas_amount: u64) -> Self { - self.max_gas_amount = max_gas_amount; - self - } - - pub fn gas_unit_price(mut self, gas_unit_price: u64) -> Self { - self.gas_unit_price = gas_unit_price; - self - } - - pub fn chain_id(mut self, chain_id: u8) -> Self { - self.chain_id = chain_id; - self - } - - pub fn expiration_timestamp_secs(mut self, expiration_timestamp_secs: u64) -> Self { - self.expiration_timestamp_secs = expiration_timestamp_secs; - self - } - pub fn build(self) -> RawTransaction { RawTransaction::new( self.sender.expect("sender must have been set"), @@ -160,10 +124,6 @@ impl TransactionFactory { self.transaction_builder(payload) } - pub fn entry_function(&self, func: EntryFunction) -> TransactionBuilder { - self.payload(TransactionPayload::EntryFunction(func)) - } - pub fn create_user_account(&self, to: AccountAddress) -> TransactionBuilder { self.payload(aptos_account_create_account(to)) } @@ -234,575 +194,3 @@ impl TransactionFactory { self.transaction_expiration_time } } - -#[cfg(test)] -mod tests { - use std::str::FromStr; - use move_core_types::account_address::AccountAddress; - use move_core_types::language_storage::TypeTag; - use tw_encoding::hex; - use crate::liquid_staking; - use crate::liquid_staking::{LiquidStakingOperation, Stake, Unstake}; - use crate::nft::{Offer, NftOperation, Claim}; - use crate::transaction_builder::TransactionFactory; - - - pub struct AccountCreation { - to: String, - } - - pub struct Transfer { - to: String, - amount: u64, - } - - pub struct TokenTransfer { - transfer: Transfer, - tag: TypeTag, - } - - pub struct RegisterToken { - coin_type: TypeTag, - } - - pub enum OpsDetails { - RegisterToken(RegisterToken), - LiquidStakingOps(LiquidStakingOperation), - AccountCreation(AccountCreation), - Transfer(Transfer), - TokenTransfer(TokenTransfer), - ImplicitTokenTransfer(TokenTransfer), - NftOps(NftOperation), - } - - fn setup_transaction( - sender: &str, - keypair_str: &str, - transaction_type: &str, - sequence_number: u64, - chain_id: u8, - max_gas_amount: u64, - timestamp: u64, - gas_unit_price: u64, - expected_raw_txn_bytes_str: &str, - expected_signature_str: &str, - expected_encoded_txn_str: &str, - json_literal: &str, - ops_details: Option, - ) { - let account_address = AccountAddress::from_str(sender).unwrap(); - let keypair = tw_keypair::ed25519::sha512::KeyPair::try_from(keypair_str).unwrap(); - let factory = TransactionFactory::new(chain_id) - .with_max_gas_amount(max_gas_amount) - .with_gas_unit_price(gas_unit_price) - .with_transaction_expiration_time(timestamp); - - let mut builder = match transaction_type { - "transfer" => { - if let OpsDetails::Transfer(transfer) = ops_details.unwrap() { - factory.implicitly_create_user_account_and_transfer(AccountAddress::from_str(&transfer.to).unwrap(), transfer.amount) - } else { - panic!("Unsupported arguments") - } - } - "create_account" => { - if let OpsDetails::AccountCreation(account) = ops_details.unwrap() { - factory.create_user_account(AccountAddress::from_str(&account.to).unwrap()) - } else { - panic!("Unsupported arguments") - } - } - "coin_transfer" => { - if let OpsDetails::TokenTransfer(token_transfer) = ops_details.unwrap() { - factory.coins_transfer(AccountAddress::from_str(&token_transfer.transfer.to).unwrap(), token_transfer.transfer.amount, token_transfer.tag) - } else { - panic!("Unsupported arguments") - } - } - "implicit_coin_transfer" => { - if let OpsDetails::ImplicitTokenTransfer(token_transfer) = ops_details.unwrap() { - factory.implicitly_create_user_and_coins_transfer(AccountAddress::from_str(&token_transfer.transfer.to).unwrap(), token_transfer.transfer.amount, token_transfer.tag) - } else { - panic!("Unsupported arguments") - } - } - "nft_ops" => { - if let OpsDetails::NftOps(nft) = ops_details.unwrap() { - factory.nft_ops(nft) - } else { - panic!("Unsupported arguments") - } - } - "register_token" => { - if let OpsDetails::RegisterToken(register_token) = ops_details.unwrap() { - factory.register_token(register_token.coin_type) - } else { - panic!("Unsupported arguments") - } - } - "liquid_staking_ops" => { - if let OpsDetails::LiquidStakingOps(liquid_staking_ops) = ops_details.unwrap() { - factory.liquid_staking_ops(liquid_staking_ops) - } else { - panic!("Unsupported arguments") - } - } - _ => panic!("Unsupported transaction type"), - }; - - builder = builder.sender(account_address).sequence_number(sequence_number); - let res = builder.build().sign(keypair).unwrap(); - - assert_eq!(hex::encode(res.raw_txn_bytes(), false), expected_raw_txn_bytes_str); - assert_eq!(hex::encode(res.authenticator().get_signature(), false), expected_signature_str); - assert_eq!(hex::encode(res.encoded(), false), expected_encoded_txn_str); - - let json_value: serde_json::Value = serde_json::from_str(json_literal).unwrap(); - assert_eq!(res.to_json(), json_value); - } - - #[test] - fn test_aptos_account_transfer() { - setup_transaction( - "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", - "transfer", - 99, - 33, - 3296766, - 3664390082, - 100, - "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021", - "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", - "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", - r#"{ - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "3296766", - "payload": { - "arguments": ["0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], - "function": "0x1::aptos_account::transfer", - "type": "entry_function_payload", - "type_arguments": [] - }, - "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "99", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0x5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", - "type": "ed25519_signature" - } - }"#, - Some(OpsDetails::Transfer(Transfer { - to: "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30".to_string(), - amount: 1000, - })), - ); - } - - #[test] - fn test_aptos_create_account() { - setup_transaction( - "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address - "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair - "create_account", - 0, // Sequence number - 33, - 3296766, - 3664390082, - 100, - "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada0000000021", // Expected raw transaction bytes - "fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", // Expected signature - "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", // Expected encoded transaction - r#"{ - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "3296766", - "payload": { - "arguments": ["0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e"], - "function": "0x1::aptos_account::create_account", - "type": "entry_function_payload", - "type_arguments": [] - }, - "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "0", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0xfcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", - "type": "ed25519_signature" - } - }"#, // Expected JSON literal - Some(OpsDetails::AccountCreation(AccountCreation { - to: "0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e".to_string() - })), - ); - } - - #[test] - fn test_aptos_coin_transfer() { - setup_transaction( - "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address - "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair - "coin_transfer", - 24, // Sequence number - 32, - 3296766, - 3664390082, - 100, - "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada0000000020", // Expected raw transaction bytes - "7643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", // Expected signature - "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada00000000200020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c407643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", // Expected encoded transaction - r#"{ - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "3296766", - "payload": { - "arguments": ["0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","100000"], - "function": "0x1::coin::transfer", - "type": "entry_function_payload", - "type_arguments": ["0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC"] - }, - "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "24", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0x7643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", - "type": "ed25519_signature" - } - }"#, // Expected JSON literal - Some(OpsDetails::TokenTransfer(TokenTransfer { transfer: Transfer { to: "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30".to_string(), amount: 100000 }, tag: TypeTag::from_str("0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC").unwrap() })), - ); - } - - #[test] - fn test_nft_offer() { - setup_transaction( - "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", // Sender's address - "7bebb6d543d17f6fe4e685cfab239fa37896edd594ff859f1df32f244fb707e2", // Keypair - "nft_ops", - 1, // Sequence number - 2, - 3296766, - 3664390082, - 100, - "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes - "af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", // Expected signature - "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada00000000020020d1d99b67e37b483161a0fa369c46f34a3be4863c20e20fc7cdc669c0826a411340af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", // Expected encoded transaction - r#"{ - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "3296766", - "payload": { - "arguments": [ - "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", - "Topaz Troopers", "Topaz Trooper #20068", "0", "1"], - "function": "0x3::token_transfers::offer_script", - "type": "entry_function_payload", - "type_arguments": [] - }, - "sender": "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", - "sequence_number": "1", - "signature": { - "public_key": "0xd1d99b67e37b483161a0fa369c46f34a3be4863c20e20fc7cdc669c0826a4113", - "signature": "0xaf5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", - "type": "ed25519_signature" - } - }"#, // Expected JSON literal - Some(OpsDetails::NftOps(NftOperation::Offer(Offer { - receiver: AccountAddress::from_str("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30").unwrap(), - creator: AccountAddress::from_str("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac").unwrap(), - collection: "Topaz Troopers".as_bytes().to_vec(), - name: "Topaz Trooper #20068".as_bytes().to_vec(), - property_version: 0, - amount: 1, - }))), - ) - } - - #[test] - fn test_cancel_nft_offer() { - setup_transaction( - "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address - "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair - "nft_ops", - 21, // Sequence number - 2, - 3296766, - 3664390082, - 100, - "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes - "826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", // Expected signature - "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", // Expected encoded transaction - r#"{ - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "3296766", - "payload": { - "arguments": [ - "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", - "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", - "Topaz Troopers", "Topaz Trooper #20068", "0"], - "function": "0x3::token_transfers::cancel_offer_script", - "type": "entry_function_payload", - "type_arguments": [] - }, - "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "21", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0x826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", - "type": "ed25519_signature" - } - }"#, // Expected JSON literal - Some(OpsDetails::NftOps(NftOperation::Cancel(Offer { - receiver: AccountAddress::from_str("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee").unwrap(), - creator: AccountAddress::from_str("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac").unwrap(), - collection: "Topaz Troopers".as_bytes().to_vec(), - name: "Topaz Trooper #20068".as_bytes().to_vec(), - property_version: 0, - amount: 0, - }))), - ) - } - - #[test] - fn test_claim_nft_offer() { - setup_transaction( - "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address - "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair - "nft_ops", - 19, // Sequence number - 2, - 3296766, - 3664390082, - 100, - "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes - "ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", // Expected signature - "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", // Expected encoded transaction - r#"{ - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "3296766", - "payload": { - "arguments": [ - "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", - "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", - "Topaz Troopers", "Topaz Trooper #20068", "0"], - "function": "0x3::token_transfers::claim_script", - "type": "entry_function_payload", - "type_arguments": [] - }, - "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "19", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0xede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", - "type": "ed25519_signature" - } - }"#, // Expected JSON literal - Some(OpsDetails::NftOps(NftOperation::Claim(Claim { - sender: AccountAddress::from_str("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee").unwrap(), - creator: AccountAddress::from_str("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac").unwrap(), - collection: "Topaz Troopers".as_bytes().to_vec(), - name: "Topaz Trooper #20068".as_bytes().to_vec(), - property_version: 0, - }))), - ) - } - - #[test] - fn test_register_token() { - setup_transaction( - "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address - "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair - "register_token", - 23, // Sequence number - 2, - 2000000, - 3664390082, - 100, - "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3017000000000000000200000000000000000000000000000000000000000000000000000000000000010c6d616e616765645f636f696e0872656769737465720107e4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379096d6f76655f636f696e084d6f7665436f696e000080841e00000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes - "e230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100", // Expected signature - "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3017000000000000000200000000000000000000000000000000000000000000000000000000000000010c6d616e616765645f636f696e0872656769737465720107e4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379096d6f76655f636f696e084d6f7665436f696e000080841e00000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40e230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100", // Expected encoded transaction - r#"{ - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "2000000", - "payload": { - "arguments": [], - "function": "0x1::managed_coin::register", - "type": "entry_function_payload", - "type_arguments": ["0xe4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379::move_coin::MoveCoin"] - }, - "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "23", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0xe230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100", - "type": "ed25519_signature" - } - }"#, // Expected JSON literal - Some(OpsDetails::RegisterToken(RegisterToken { coin_type: TypeTag::from_str("0xe4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379::move_coin::MoveCoin").unwrap() })), - ) - } - - #[test] - fn test_implicit_aptos_coin_transfer() { - setup_transaction( - "0x1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf25", // Sender's address - "e7f56c77189e03699a75d8ec5c090e41f3d9d4783bc49c33df8a93d915e10de8", // Keypair - "implicit_coin_transfer", - 2, // Sequence number - 1, - 2000, - 3664390082, - 100, - "1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf2502000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e730107e9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9086d65655f636f696e074d6565436f696e000220b7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c081027000000000000d0070000000000006400000000000000c2276ada0000000001", // Expected raw transaction bytes - "30ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d", // Expected signature - "1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf2502000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e730107e9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9086d65655f636f696e074d6565436f696e000220b7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c081027000000000000d0070000000000006400000000000000c2276ada0000000001002062e7a6a486553b56a53e89dfae3f780693e537e5b0a7ed33290780e581ca83694030ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d", // Expected encoded transaction - r#"{ - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "2000", - "payload": { - "arguments": ["0xb7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c","10000"], - "function": "0x1::aptos_account::transfer_coins", - "type": "entry_function_payload", - "type_arguments": ["0xe9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9::mee_coin::MeeCoin"] - }, - "sender": "0x1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf25", - "sequence_number": "2", - "signature": { - "public_key": "0x62e7a6a486553b56a53e89dfae3f780693e537e5b0a7ed33290780e581ca8369", - "signature": "0x30ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d", - "type": "ed25519_signature" - } - }"#, // Expected JSON literal - Some(OpsDetails::ImplicitTokenTransfer(TokenTransfer { transfer: Transfer { to: "0xb7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c".to_string(), amount: 10000 }, tag: TypeTag::from_str("0xe9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9::mee_coin::MeeCoin").unwrap() })), - ); - } - - #[test] - fn test_implicit_aptos_tortuga_stake() { - setup_transaction( - "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address - "786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05", // Keypair - "liquid_staking_ops", - 19, // Sequence number - 1, - 5554, - 1670240203, - 100, - "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1300000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000b2150000000000006400000000000000cbd78d630000000001", // Expected raw transaction bytes - "22d3166c3003f9c24a35fd39c71eb27e0d2bb82541be610822165c9283f56fefe5a9d46421b9caf174995bd8f83141e60ea8cff521ecf4741fe19e6ae9a5680d", // Expected signature - "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1300000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000b2150000000000006400000000000000cbd78d630000000001002089e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc44022d3166c3003f9c24a35fd39c71eb27e0d2bb82541be610822165c9283f56fefe5a9d46421b9caf174995bd8f83141e60ea8cff521ecf4741fe19e6ae9a5680d", // Expected encoded transaction - r#"{ - "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", - "sequence_number": "19", - "max_gas_amount": "5554", - "gas_unit_price": "100", - "expiration_timestamp_secs": "1670240203", - "payload": { - "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::stake", - "type_arguments": [], - "arguments": [ - "100000000" - ], - "type": "entry_function_payload" - }, - "signature": { - "public_key": "0x89e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4", - "signature": "0x22d3166c3003f9c24a35fd39c71eb27e0d2bb82541be610822165c9283f56fefe5a9d46421b9caf174995bd8f83141e60ea8cff521ecf4741fe19e6ae9a5680d", - "type": "ed25519_signature" - } - }"#, // Expected JSON literal - Some(OpsDetails::LiquidStakingOps(LiquidStakingOperation::Stake(Stake { - amount: 100000000, - smart_contract_address: AccountAddress::from_str("0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f").unwrap(), - }))), - ); - } - - #[test] - fn test_implicit_aptos_tortuga_unstake() { - setup_transaction( - "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address - "786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05", // Keypair - "liquid_staking_ops", - 20, // Sequence number - 1, - 2371, - 1670304949, - 120, - "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1400000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e9050000000043090000000000007800000000000000b5d48e630000000001", // Expected raw transaction bytes - "6994b917432ad70ae84d2ce1484e6aece589a68aad1b7c6e38c9697f2a012a083a3a755c5e010fd3d0f149a75dd8d257acbd09f10800e890074e5ad384314d0c", // Expected signature - "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1400000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e9050000000043090000000000007800000000000000b5d48e630000000001002089e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4406994b917432ad70ae84d2ce1484e6aece589a68aad1b7c6e38c9697f2a012a083a3a755c5e010fd3d0f149a75dd8d257acbd09f10800e890074e5ad384314d0c", // Expected encoded transaction - r#"{ - "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", - "sequence_number": "20", - "max_gas_amount": "2371", - "gas_unit_price": "120", - "expiration_timestamp_secs": "1670304949", - "payload": { - "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::unstake", - "type_arguments": [], - "arguments": [ - "99178100" - ], - "type": "entry_function_payload" - }, - "signature": { - "public_key": "0x89e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4", - "signature": "0x6994b917432ad70ae84d2ce1484e6aece589a68aad1b7c6e38c9697f2a012a083a3a755c5e010fd3d0f149a75dd8d257acbd09f10800e890074e5ad384314d0c", - "type": "ed25519_signature" - } - }"#, // Expected JSON literal - Some(OpsDetails::LiquidStakingOps(LiquidStakingOperation::Unstake(Unstake { - amount: 99178100, - smart_contract_address: AccountAddress::from_str("0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f").unwrap(), - }))), - ); - } - - #[test] - fn test_implicit_aptos_tortuga_claim() { - setup_transaction( - "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address - "786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05", // Keypair - "liquid_staking_ops", - 28, // Sequence number - 1, - 10, - 1682066783, - 148, - "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657205636c61696d00010800000000000000000a0000000000000094000000000000005f4d42640000000001", // Expected raw transaction bytes - "c936584f89777e1fe2d5dd75cd8d9c514efc445810ba22f462b6fe7229c6ec7fc1c8b25d3e233eafaa8306433b3220235e563498ba647be38cac87ff618e3d03", // Expected signature - "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657205636c61696d00010800000000000000000a0000000000000094000000000000005f4d42640000000001002089e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc440c936584f89777e1fe2d5dd75cd8d9c514efc445810ba22f462b6fe7229c6ec7fc1c8b25d3e233eafaa8306433b3220235e563498ba647be38cac87ff618e3d03", // Expected encoded transaction - r#"{ - "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", - "sequence_number": "28", - "max_gas_amount": "10", - "gas_unit_price": "148", - "expiration_timestamp_secs": "1682066783", - "payload": { - "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::claim", - "type_arguments": [], - "arguments": [ - "0" - ], - "type": "entry_function_payload" - }, - "signature": { - "public_key": "0x89e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4", - "signature": "0xc936584f89777e1fe2d5dd75cd8d9c514efc445810ba22f462b6fe7229c6ec7fc1c8b25d3e233eafaa8306433b3220235e563498ba647be38cac87ff618e3d03", - "type": "ed25519_signature" - } - }"#, // Expected JSON literal - Some(OpsDetails::LiquidStakingOps(LiquidStakingOperation::Claim(liquid_staking::Claim { - idx: 0, - smart_contract_address: AccountAddress::from_str("0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f").unwrap(), - }))), - ); - } -} diff --git a/rust/tw_aptos/tests/signer.rs b/rust/tw_aptos/tests/signer.rs index 12556850602..aeaddded53b 100644 --- a/rust/tw_aptos/tests/signer.rs +++ b/rust/tw_aptos/tests/signer.rs @@ -7,7 +7,8 @@ use std::str::FromStr; use move_core_types::account_address::AccountAddress; use move_core_types::language_storage::TypeTag; -use tw_aptos::liquid_staking::LiquidStakingOperation; +use tw_aptos::liquid_staking; +use tw_aptos::liquid_staking::{LiquidStakingOperation, Stake, Unstake}; use tw_aptos::nft::{Claim, NftOperation, Offer}; use tw_aptos::signer::{Signer, StandardAptosContext}; use tw_aptos::transaction_payload::convert_type_tag_to_struct_tag; @@ -115,7 +116,14 @@ fn setup_proto_transaction<'a>(sender: &'a str, panic!("Unsupported arguments") } } - _ => { todo!() } + "liquid_staking_ops" => { + if let OpsDetails::LiquidStakingOps(liquid_staking_ops) = ops_details.unwrap() { + Proto::mod_SigningInput::OneOftransaction_payload::liquid_staking_message(liquid_staking_ops.into()) + } else { + panic!("Unsupported arguments") + } + } + _ => { Proto::mod_SigningInput::OneOftransaction_payload::None } }; @@ -487,4 +495,133 @@ fn test_aptos_register_token() { "type": "ed25519_signature" } }"#); +} + +#[test] +fn test_aptos_tortuga_stake() { + let input = setup_proto_transaction("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address + "786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05", // Keypair + "liquid_staking_ops", + 19, // Sequence number + 1, + 5554, + 1670240203, + 100, + "", + Some(OpsDetails::LiquidStakingOps(LiquidStakingOperation::Stake(Stake { + amount: 100000000, + smart_contract_address: AccountAddress::from_str("0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f").unwrap(), + }))), + ); + let output = Signer::::sign_proto(input); + test_tx_result(output, + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1300000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000b2150000000000006400000000000000cbd78d630000000001", // Expected raw transaction bytes + "22d3166c3003f9c24a35fd39c71eb27e0d2bb82541be610822165c9283f56fefe5a9d46421b9caf174995bd8f83141e60ea8cff521ecf4741fe19e6ae9a5680d", // Expected signature + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1300000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000b2150000000000006400000000000000cbd78d630000000001002089e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc44022d3166c3003f9c24a35fd39c71eb27e0d2bb82541be610822165c9283f56fefe5a9d46421b9caf174995bd8f83141e60ea8cff521ecf4741fe19e6ae9a5680d", // Expected encoded transaction + r#"{ + "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", + "sequence_number": "19", + "max_gas_amount": "5554", + "gas_unit_price": "100", + "expiration_timestamp_secs": "1670240203", + "payload": { + "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::stake", + "type_arguments": [], + "arguments": [ + "100000000" + ], + "type": "entry_function_payload" + }, + "signature": { + "public_key": "0x89e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4", + "signature": "0x22d3166c3003f9c24a35fd39c71eb27e0d2bb82541be610822165c9283f56fefe5a9d46421b9caf174995bd8f83141e60ea8cff521ecf4741fe19e6ae9a5680d", + "type": "ed25519_signature" + } + }"#); +} + +#[test] +fn test_aptos_tortuga_unstake() { + let input = setup_proto_transaction("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address + "786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05", // Keypair + "liquid_staking_ops", + 20, // Sequence number + 1, + 2371, + 1670304949, + 120, + "", + Some(OpsDetails::LiquidStakingOps(LiquidStakingOperation::Unstake(Unstake { + amount: 99178100, + smart_contract_address: AccountAddress::from_str("0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f").unwrap(), + }))), + ); + let output = Signer::::sign_proto(input); + test_tx_result(output, + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1400000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e9050000000043090000000000007800000000000000b5d48e630000000001", // Expected raw transaction bytes + "6994b917432ad70ae84d2ce1484e6aece589a68aad1b7c6e38c9697f2a012a083a3a755c5e010fd3d0f149a75dd8d257acbd09f10800e890074e5ad384314d0c", // Expected signature + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1400000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e9050000000043090000000000007800000000000000b5d48e630000000001002089e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4406994b917432ad70ae84d2ce1484e6aece589a68aad1b7c6e38c9697f2a012a083a3a755c5e010fd3d0f149a75dd8d257acbd09f10800e890074e5ad384314d0c", // Expected encoded transaction + r#"{ + "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", + "sequence_number": "20", + "max_gas_amount": "2371", + "gas_unit_price": "120", + "expiration_timestamp_secs": "1670304949", + "payload": { + "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::unstake", + "type_arguments": [], + "arguments": [ + "99178100" + ], + "type": "entry_function_payload" + }, + "signature": { + "public_key": "0x89e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4", + "signature": "0x6994b917432ad70ae84d2ce1484e6aece589a68aad1b7c6e38c9697f2a012a083a3a755c5e010fd3d0f149a75dd8d257acbd09f10800e890074e5ad384314d0c", + "type": "ed25519_signature" + } + }"#); +} + +#[test] +fn test_aptos_tortuga_claim() { + let input = setup_proto_transaction("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address + "786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05", // Keypair + "liquid_staking_ops", + 28, // Sequence number + 1, + 10, + 1682066783, + 148, + "", + Some(OpsDetails::LiquidStakingOps(LiquidStakingOperation::Claim(liquid_staking::Claim { + idx: 0, + smart_contract_address: AccountAddress::from_str("0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f").unwrap(), + }))), + ); + let output = Signer::::sign_proto(input); + test_tx_result(output, + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657205636c61696d00010800000000000000000a0000000000000094000000000000005f4d42640000000001", // Expected raw transaction bytes + "c936584f89777e1fe2d5dd75cd8d9c514efc445810ba22f462b6fe7229c6ec7fc1c8b25d3e233eafaa8306433b3220235e563498ba647be38cac87ff618e3d03", // Expected signature + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657205636c61696d00010800000000000000000a0000000000000094000000000000005f4d42640000000001002089e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc440c936584f89777e1fe2d5dd75cd8d9c514efc445810ba22f462b6fe7229c6ec7fc1c8b25d3e233eafaa8306433b3220235e563498ba647be38cac87ff618e3d03", // Expected encoded transaction + r#"{ + "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", + "sequence_number": "28", + "max_gas_amount": "10", + "gas_unit_price": "148", + "expiration_timestamp_secs": "1682066783", + "payload": { + "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::claim", + "type_arguments": [], + "arguments": [ + "0" + ], + "type": "entry_function_payload" + }, + "signature": { + "public_key": "0x89e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4", + "signature": "0xc936584f89777e1fe2d5dd75cd8d9c514efc445810ba22f462b6fe7229c6ec7fc1c8b25d3e233eafaa8306433b3220235e563498ba647be38cac87ff618e3d03", + "type": "ed25519_signature" + } + }"#); } \ No newline at end of file From 8065171d1de9b8f1531492671999b1edb746c3b1 Mon Sep 17 00:00:00 2001 From: Milerius Date: Mon, 13 Nov 2023 11:22:45 -0500 Subject: [PATCH 43/60] feat(signer): add preHash/Compile support aptos rust --- rust/Cargo.lock | 5 +- rust/tw_aptos/Cargo.toml | 1 + rust/tw_aptos/src/compiler.rs | 65 +++++++++++++++++++++ rust/tw_aptos/src/entry.rs | 16 ++--- rust/tw_aptos/src/lib.rs | 1 + rust/tw_aptos/src/signer.rs | 2 +- rust/tw_aptos/src/transaction.rs | 39 +++++++++---- rust/tw_aptos/src/transaction_builder.rs | 34 +++++++---- rust/tw_aptos/src/transaction_payload.rs | 74 +++++++++++++++++++++++- rust/tw_aptos/tests/signer.rs | 62 ++++++++++++++++++++ rust/tw_coin_entry/src/error.rs | 7 +++ src/Aptos/Entry.cpp | 2 +- tests/chains/Aptos/TWAnySignerTests.cpp | 4 +- 13 files changed, 276 insertions(+), 36 deletions(-) create mode 100644 rust/tw_aptos/src/compiler.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 5f825710ed2..3025d4b61bb 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -22,9 +22,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.69" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arbitrary" @@ -1624,6 +1624,7 @@ dependencies = [ name = "tw_aptos" version = "0.1.0" dependencies = [ + "anyhow", "bcs", "move-core-types 0.0.4 (git+https://github.com/move-language/move?rev=ea70797099baea64f05194a918cebd69ed02b285)", "serde", diff --git a/rust/tw_aptos/Cargo.toml b/rust/tw_aptos/Cargo.toml index f0b683cdace..2983f82b8fd 100644 --- a/rust/tw_aptos/Cargo.toml +++ b/rust/tw_aptos/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] +anyhow = "1.0.75" serde_json = "1.0.108" tw_coin_entry = { path = "../tw_coin_entry" } tw_encoding = { path = "../tw_encoding"} diff --git a/rust/tw_aptos/src/compiler.rs b/rust/tw_aptos/src/compiler.rs new file mode 100644 index 00000000000..ce263de6ffb --- /dev/null +++ b/rust/tw_aptos/src/compiler.rs @@ -0,0 +1,65 @@ +use std::marker::PhantomData; +use std::str::FromStr; +use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::error::SigningResult; +use tw_coin_entry::signing_output_error; +use tw_proto::Aptos::Proto; +use crate::signer::AptosContext; +use tw_proto::TxCompiler::Proto as CompilerProto; +use crate::address::Address; +use crate::transaction_builder; + +pub struct Compiler { + _phantom: PhantomData, +} + +impl Compiler { + #[inline] + pub fn preimage_hashes( + input: Proto::SigningInput<'_>, + ) -> CompilerProto::PreSigningOutput<'static> { + Self::preimage_hashes_impl(input) + .unwrap_or_else(|e| signing_output_error!(CompilerProto::PreSigningOutput, e)) + } + + fn preimage_hashes_impl( + input: Proto::SigningInput<'_>, + ) -> SigningResult> { + let builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone())?; + let sender = Address::from_str(&input.sender)?; + let signed_tx = builder.sender(sender.inner()).sequence_number(input.sequence_number as u64).build().pre_image(); + Ok(CompilerProto::PreSigningOutput { + data: signed_tx.into(), + ..CompilerProto::PreSigningOutput::default() + }) + } + + #[inline] + pub fn compile( + input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Proto::SigningOutput<'static> { + Self::compile_impl(input, signatures, public_keys) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + fn compile_impl( + input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> SigningResult> { + let builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone())?; + let sender = Address::from_str(&input.sender)?; + let signed_tx = builder.sender(sender.inner()).sequence_number(input.sequence_number as u64).build().compile( + signatures.first().unwrap().to_vec(), public_keys.first().unwrap().to_vec() + )?; + Ok(Proto::SigningOutput { + raw_txn: signed_tx.raw_txn_bytes().clone().into(), + encoded: signed_tx.encoded().clone().into(), + authenticator: Some((*signed_tx.authenticator()).clone().into()), + json: signed_tx.to_json().to_string().into(), + ..Proto::SigningOutput::default() + }) + } +} \ No newline at end of file diff --git a/rust/tw_aptos/src/entry.rs b/rust/tw_aptos/src/entry.rs index 00a69082d8d..f93a7363b4d 100644 --- a/rust/tw_aptos/src/entry.rs +++ b/rust/tw_aptos/src/entry.rs @@ -17,6 +17,7 @@ use tw_keypair::tw::PublicKey; use tw_proto::Aptos::Proto; use tw_proto::TxCompiler::Proto as CompilerProto; use crate::address::Address; +use crate::compiler::Compiler; use crate::signer::{Signer, StandardAptosContext}; @@ -66,20 +67,21 @@ impl CoinEntry for AptosEntry { fn preimage_hashes( &self, _coin: &dyn CoinContext, - _input: Self::SigningInput<'_>, - ) -> Self::PreSigningOutput { - todo!() + input: Self::SigningInput<'_>, + ) -> Self::PreSigningOutput + { + Compiler::::preimage_hashes(input) } #[inline] fn compile( &self, _coin: &dyn CoinContext, - _input: Self::SigningInput<'_>, - _signatures: Vec, - _public_keys: Vec, + input: Self::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, ) -> Self::SigningOutput { - todo!() + Compiler::::compile(input, signatures, public_keys) } #[inline] diff --git a/rust/tw_aptos/src/lib.rs b/rust/tw_aptos/src/lib.rs index a59709a6c95..2a085865782 100644 --- a/rust/tw_aptos/src/lib.rs +++ b/rust/tw_aptos/src/lib.rs @@ -17,3 +17,4 @@ pub mod signer; pub mod transaction; pub mod transaction_builder; pub mod transaction_payload; +pub mod compiler; diff --git a/rust/tw_aptos/src/signer.rs b/rust/tw_aptos/src/signer.rs index 26094a53d95..ede021e0d50 100644 --- a/rust/tw_aptos/src/signer.rs +++ b/rust/tw_aptos/src/signer.rs @@ -39,7 +39,7 @@ impl Signer { input: Proto::SigningInput<'_>, ) -> SigningResult> { let key_pair = ed25519::sha512::KeyPair::try_from(input.private_key.as_ref())?; - let builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone()); + let builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone())?; let sender = Address::from_str(&input.sender)?; let signed_tx = builder.sender(sender.inner()).sequence_number(input.sequence_number as u64).build().sign(key_pair)?; Ok(Proto::SigningOutput { diff --git a/rust/tw_aptos/src/transaction.rs b/rust/tw_aptos/src/transaction.rs index bb748a38839..49279027434 100644 --- a/rust/tw_aptos/src/transaction.rs +++ b/rust/tw_aptos/src/transaction.rs @@ -129,17 +129,26 @@ impl RawTransaction { } } - pub fn sign( - self, - key_pair: KeyPair, - ) -> SigningResult { - let serialized = bcs::to_bytes(&self).unwrap(); - let mut to_sign = tw_hash::sha3::sha3_256("APTOS::RawTransaction".as_bytes()); - to_sign.extend_from_slice(serialized.as_slice()); - let signed = key_pair.private().sign(to_sign.clone()).unwrap(); + fn serialize(&self) -> Vec { + bcs::to_bytes(&self).unwrap() + } + + fn msg_to_sign(&self) -> Vec { + let serialized = self.serialize(); + let mut preimage = tw_hash::sha3::sha3_256("APTOS::RawTransaction".as_bytes()); + preimage.extend_from_slice(serialized.as_slice()); + preimage + } + + pub fn pre_image(&self) -> Vec { + self.msg_to_sign() + } + + pub fn compile(&self, signature: Vec, public_key: Vec) -> SigningResult { + let serialized = self.serialize(); let auth = TransactionAuthenticator::Ed25519 { - public_key: key_pair.public().as_slice().to_vec(), - signature: signed.to_bytes().into_vec(), + public_key, + signature, }; let mut encoded = serialized.clone(); encoded.extend_from_slice(bcs::to_bytes(&auth).unwrap().as_slice()); @@ -151,6 +160,16 @@ impl RawTransaction { }) } + pub fn sign( + self, + key_pair: KeyPair, + ) -> SigningResult { + let to_sign = self.pre_image(); + let signature = key_pair.private().sign(to_sign)?.to_bytes().into_vec(); + let pubkey = key_pair.public().as_slice().to_vec(); + self.compile(signature, pubkey) + } + pub fn to_json(&self) -> Value { json!({ "expiration_timestamp_secs": self.expiration_timestamp_secs.to_string(), diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index 9e2ab2a5aa0..b080adc9155 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -7,10 +7,12 @@ use std::str::FromStr; use move_core_types::account_address::AccountAddress; use move_core_types::language_storage::TypeTag; +use serde_json::Value; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_proto::Aptos::Proto::mod_SigningInput::OneOftransaction_payload; use crate::constants::{GAS_UNIT_PRICE, MAX_GAS_AMOUNT}; use crate::transaction::RawTransaction; -use crate::transaction_payload::{convert_proto_struct_tag_to_type_tag, TransactionPayload}; +use crate::transaction_payload::{convert_proto_struct_tag_to_type_tag, EntryFunction, TransactionPayload}; use tw_proto::Aptos::Proto::SigningInput; use crate::aptos_move_packages::{aptos_account_create_account, aptos_account_transfer, aptos_account_transfer_coins, coin_transfer, managed_coin_register, token_transfers_cancel_offer_script, token_transfers_claim_script, token_transfers_offer_script}; use crate::liquid_staking::{LiquidStakingOperation, tortuga_claim, tortuga_stake, tortuga_unstake}; @@ -69,39 +71,47 @@ impl TransactionFactory { } } - pub fn new_from_protobuf(input: SigningInput) -> TransactionBuilder { + pub fn new_from_protobuf(input: SigningInput) -> SigningResult { let factory = TransactionFactory::new(input.chain_id as u8) .with_gas_unit_price(input.gas_unit_price) .with_max_gas_amount(input.max_gas_amount) .with_transaction_expiration_time(input.expiration_timestamp_secs); match input.transaction_payload { OneOftransaction_payload::transfer(transfer) => { - factory.implicitly_create_user_account_and_transfer(AccountAddress::from_str(&transfer.to).unwrap(), transfer.amount) + Ok(factory.implicitly_create_user_account_and_transfer(AccountAddress::from_str(&transfer.to).unwrap(), transfer.amount)) } OneOftransaction_payload::token_transfer(token_transfer) => { let func = token_transfer.function.unwrap(); - factory.coins_transfer(AccountAddress::from_str(&token_transfer.to).unwrap(), token_transfer.amount, + Ok(factory.coins_transfer(AccountAddress::from_str(&token_transfer.to).unwrap(), token_transfer.amount, convert_proto_struct_tag_to_type_tag(func), - ) + )) } OneOftransaction_payload::create_account(create_account) => { - factory.create_user_account(AccountAddress::from_str(&create_account.auth_key).unwrap()) + Ok(factory.create_user_account(AccountAddress::from_str(&create_account.auth_key).unwrap())) } OneOftransaction_payload::nft_message(nft_message) => { - factory.nft_ops(nft_message.into()) + Ok(factory.nft_ops(nft_message.into())) } OneOftransaction_payload::register_token(register_token) => { - factory.register_token(convert_proto_struct_tag_to_type_tag(register_token.function.unwrap())) + Ok(factory.register_token(convert_proto_struct_tag_to_type_tag(register_token.function.unwrap()))) } OneOftransaction_payload::liquid_staking_message(msg) => { - factory.liquid_staking_ops(msg.into()) + Ok(factory.liquid_staking_ops(msg.into())) } OneOftransaction_payload::token_transfer_coins(token_transfer_coins) => { let func = token_transfer_coins.function.unwrap(); - factory.implicitly_create_user_and_coins_transfer(AccountAddress::from_str(&token_transfer_coins.to).unwrap(), token_transfer_coins.amount, - convert_proto_struct_tag_to_type_tag(func)) + Ok(factory.implicitly_create_user_and_coins_transfer(AccountAddress::from_str(&token_transfer_coins.to).unwrap(), token_transfer_coins.amount, + convert_proto_struct_tag_to_type_tag(func))) + } + OneOftransaction_payload::None => { + let is_blind_sign = !input.any_encoded.is_empty(); + let v = serde_json::from_str::(&input.any_encoded)?; + if is_blind_sign { + Ok(factory.payload(TransactionPayload::EntryFunction(EntryFunction::try_from(v).unwrap()))) + } else { + Err(SigningError(SigningErrorType::Error_input_parse)) + } } - OneOftransaction_payload::None => { todo!() } } } diff --git a/rust/tw_aptos/src/transaction_payload.rs b/rust/tw_aptos/src/transaction_payload.rs index 4537b87b8a6..a414e1cc2f5 100644 --- a/rust/tw_aptos/src/transaction_payload.rs +++ b/rust/tw_aptos/src/transaction_payload.rs @@ -7,9 +7,13 @@ use std::default::Default; use std::str::FromStr; use move_core_types::identifier::Identifier; -use move_core_types::language_storage::{ModuleId, TypeTag}; +use move_core_types::language_storage::{ModuleId, StructTag, TypeTag}; +use move_core_types::parser::parse_transaction_argument; +use move_core_types::transaction_argument::TransactionArgument; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; +use anyhow::{Result, anyhow}; + use tw_proto::Aptos; use crate::{serde_helper::vec_bytes}; @@ -24,6 +28,57 @@ pub struct EntryFunction { json_args: Value, } +impl TryFrom for EntryFunction { + type Error = anyhow::Error; + + fn try_from(value: Value) -> Result { + let function_str = value["function"].as_str().ok_or_else(|| anyhow!("Missing function string"))?; + let tag = StructTag::from_str(function_str)?; + + let args = value["arguments"].as_array().ok_or_else(|| anyhow!("Arguments field is not an array or is missing"))? + .iter() + .map(|element| { + let arg_str = element.as_str().ok_or_else(|| anyhow!("Invalid argument string"))?; + let arg = parse_transaction_argument(arg_str)?; + serialize_argument(&arg) + }) + .collect::>>>()?; + + let ty_args = value["type_arguments"].as_array().ok_or_else(|| anyhow!("Type arguments field is not an array or is missing"))? + .iter() + .map(|element| { + let ty_arg_str = element.as_str().ok_or_else(|| anyhow!("Invalid type argument string"))?; + TypeTag::from_str(ty_arg_str) + }) + .collect::>>()?; + + Ok(EntryFunction { + module: tag.module_id(), + function: tag.name, + ty_args, + args, + json_args: value["arguments"].clone(), + }) + } +} + +fn serialize_argument(arg: &TransactionArgument) -> Result> { + match arg { + TransactionArgument::U8(v) => bcs::to_bytes(v), + TransactionArgument::U16(v) => bcs::to_bytes(v), + TransactionArgument::U32(v) => bcs::to_bytes(v), + TransactionArgument::U64(v) => bcs::to_bytes(v), + TransactionArgument::U128(v) => bcs::to_bytes(v), + TransactionArgument::U256(v) => bcs::to_bytes(v), + TransactionArgument::U8Vector(v) => bcs::to_bytes(v), + TransactionArgument::Bool(v) => bcs::to_bytes(v), + TransactionArgument::Address(v) => { + let serialized_v = bcs::to_bytes(v)?; + bcs::to_bytes(&serialized_v) + } + }.map_err(|e| anyhow!(e)) +} + pub fn convert_proto_struct_tag_to_type_tag(struct_tag: Aptos::Proto::StructTag) -> TypeTag { TypeTag::from_str(&format!("{}::{}::{}", struct_tag.account_address, struct_tag.module, struct_tag.name)).unwrap() } @@ -107,6 +162,23 @@ mod tests { use tw_encoding::hex; use crate::transaction_payload::{EntryFunction, TransactionPayload}; + #[test] + fn test_payload_from_json() { + let payload_value: Value = json!({ + "arguments": ["0xc95db29a67a848940829b3df6119b5e67b788ff0248676e4484c7c6f29c0f5e6"], + "function": "0xc23c3b70956ce8d88fb18ad9ed3b463fe873cb045db3f6d2e2fb15b9aab71d50::IFO::release", + "type": "entry_function_payload", + "type_arguments": [ + "0x48e0e3958d42b8d452c9199d4a221d0d1b15d14655787453dbe77208ced90517::coins::BUSD", + "0x48e0e3958d42b8d452c9199d4a221d0d1b15d14655787453dbe77208ced90517::coins::DAI", + "0x9936836587ca33240d3d3f91844651b16cb07802faf5e34514ed6f78580deb0a::uints::U1" + ] + }); + + let v = EntryFunction::try_from(payload_value.clone()).unwrap(); + assert_eq!(payload_value, v.to_json()); + } + #[test] fn test_basic_payload() { let addr = Address::from_str("0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b").unwrap().inner(); diff --git a/rust/tw_aptos/tests/signer.rs b/rust/tw_aptos/tests/signer.rs index aeaddded53b..a6e01a06e1e 100644 --- a/rust/tw_aptos/tests/signer.rs +++ b/rust/tw_aptos/tests/signer.rs @@ -123,6 +123,9 @@ fn setup_proto_transaction<'a>(sender: &'a str, panic!("Unsupported arguments") } } + "blind_sign_json" => { + Proto::mod_SigningInput::OneOftransaction_payload::None + } _ => { Proto::mod_SigningInput::OneOftransaction_payload::None } }; @@ -624,4 +627,63 @@ fn test_aptos_tortuga_claim() { "type": "ed25519_signature" } }"#); +} + +#[test] +fn test_aptos_blind_sign() { + let input = setup_proto_transaction("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "blind_sign_json", + 42, // Sequence number + 1, + 100011, + 3664390082, + 100, + r#"{ + "function": "0x16fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c::AnimeSwapPoolV1::swap_exact_coins_for_coins_3_pair_entry", + "type_arguments": [ + "0x1::aptos_coin::AptosCoin", + "0x881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f4::coin::MOJO", + "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDT", + "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC" + ], + "arguments": [ + "1000000", + "49329" + ], + "type": "entry_function_payload" + }"#, + None, + ); + let output = Signer::::sign_proto(input); + test_tx_result(output, + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f302a000000000000000216fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c0f416e696d6553776170506f6f6c563127737761705f65786163745f636f696e735f666f725f636f696e735f335f706169725f656e747279040700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e0007881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f404636f696e044d4f4a4f0007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa05617373657404555344540007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa056173736574045553444300020840420f000000000008b1c0000000000000ab860100000000006400000000000000c2276ada0000000001", // Expected raw transaction bytes + "42cd67406e85afd1e948e7ad7f5f484fb4c60d82b267c6b6b28a92301e228b983206d2b87cd5487cf9acfb0effbd183ab90123570eb2e047cb152d337152210b", // Expected signature + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f302a000000000000000216fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c0f416e696d6553776170506f6f6c563127737761705f65786163745f636f696e735f666f725f636f696e735f335f706169725f656e747279040700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e0007881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f404636f696e044d4f4a4f0007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa05617373657404555344540007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa056173736574045553444300020840420f000000000008b1c0000000000000ab860100000000006400000000000000c2276ada00000000010020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c4042cd67406e85afd1e948e7ad7f5f484fb4c60d82b267c6b6b28a92301e228b983206d2b87cd5487cf9acfb0effbd183ab90123570eb2e047cb152d337152210b", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "100011", + "payload": { + "function": "0x16fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c::AnimeSwapPoolV1::swap_exact_coins_for_coins_3_pair_entry", + "type_arguments": [ + "0x1::aptos_coin::AptosCoin", + "0x881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f4::coin::MOJO", + "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDT", + "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC" + ], + "arguments": [ + "1000000", + "49329" + ], + "type": "entry_function_payload" + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "42", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x42cd67406e85afd1e948e7ad7f5f484fb4c60d82b267c6b6b28a92301e228b983206d2b87cd5487cf9acfb0effbd183ab90123570eb2e047cb152d337152210b", + "type": "ed25519_signature" + } + }"#); } \ No newline at end of file diff --git a/rust/tw_coin_entry/src/error.rs b/rust/tw_coin_entry/src/error.rs index bfbecf2ad4d..3f700b9708a 100644 --- a/rust/tw_coin_entry/src/error.rs +++ b/rust/tw_coin_entry/src/error.rs @@ -6,6 +6,7 @@ use std::fmt; use std::fmt::Formatter; +use serde_json::Error; use tw_keypair::KeyPairError; use tw_number::NumberError; use tw_proto::Common::Proto; @@ -57,6 +58,12 @@ impl From for SigningError { } } +impl From for SigningError { + fn from(_value: Error) -> Self { + SigningError(SigningErrorType::Error_input_parse) + } +} + impl From for SigningError { fn from(err: KeyPairError) -> Self { match err { diff --git a/src/Aptos/Entry.cpp b/src/Aptos/Entry.cpp index 0a6cfef6834..18cd8ad0baa 100644 --- a/src/Aptos/Entry.cpp +++ b/src/Aptos/Entry.cpp @@ -20,7 +20,7 @@ std::string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW } void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { - signTemplate(dataIn, dataOut); + signRust(dataIn, coin, dataOut); } Data Entry::preImageHashes([[maybe_unused]] TWCoinType coin, const Data& txInputData) const { diff --git a/tests/chains/Aptos/TWAnySignerTests.cpp b/tests/chains/Aptos/TWAnySignerTests.cpp index 28f260970b6..333e917dd0e 100644 --- a/tests/chains/Aptos/TWAnySignerTests.cpp +++ b/tests/chains/Aptos/TWAnySignerTests.cpp @@ -42,12 +42,12 @@ TEST(TWAnySignerAptos, TxSign) { "gas_unit_price": "100", "max_gas_amount": "3296766", "payload": { - "arguments": ["0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], + "arguments": ["0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], "function": "0x1::aptos_account::transfer", "type": "entry_function_payload", "type_arguments": [] }, - "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", "sequence_number": "99", "signature": { "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", From a488ef82a6f0687361ef4ce5f7381870b8d5a632 Mon Sep 17 00:00:00 2001 From: Milerius Date: Mon, 13 Nov 2023 11:43:06 -0500 Subject: [PATCH 44/60] feat(signer): finalize unit test url broadcast --- rust/tw_aptos/tests/signer.rs | 108 ++++++++++++++++++++++++++++++++++ src/Aptos/Entry.cpp | 11 +--- 2 files changed, 110 insertions(+), 9 deletions(-) diff --git a/rust/tw_aptos/tests/signer.rs b/rust/tw_aptos/tests/signer.rs index a6e01a06e1e..89eb67091d8 100644 --- a/rust/tw_aptos/tests/signer.rs +++ b/rust/tw_aptos/tests/signer.rs @@ -164,6 +164,7 @@ fn test_tx_result( assert_eq!(json_value, json_value_expected); } +// Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb4d62afd3862116e060dd6ad9848ccb50c2bc177799819f1d29c059ae2042467?network=devnet #[test] fn test_aptos_sign_transaction_transfer() { let input = setup_proto_transaction("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", @@ -204,6 +205,7 @@ fn test_aptos_sign_transaction_transfer() { }"#); } +// Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x477141736de6b0936a6c3734e4d6fd018c7d21f1f28f99028ef0bc6881168602?network=Devnet #[test] fn test_aptos_sign_create_account() { let input = setup_proto_transaction("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address @@ -243,6 +245,7 @@ fn test_aptos_sign_create_account() { }"#); } +// Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb5b383a5c7f99b2edb3bed9533f8169a89051b149d65876a82f4c0b9bf78a15b?network=Devnet #[test] fn test_aptos_sign_coin_transfer() { let input = setup_proto_transaction("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address @@ -281,6 +284,7 @@ fn test_aptos_sign_coin_transfer() { }"#); } +// Successfully broadcasted https://explorer.aptoslabs.com/txn/0x197d40ea12e2bfc65a0a913b9f4ca3b0b0208fe0c1514d3d55cef3d5bcf25211?network=mainnet #[test] fn test_implicit_aptos_sign_coin_transfer() { let input = setup_proto_transaction("0x1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf25", // Sender's address @@ -319,6 +323,7 @@ fn test_implicit_aptos_sign_coin_transfer() { }"#); } +// Successfully broadcasted https://explorer.aptoslabs.com/txn/0x514e473618bd3cb89a2b110b7c473db9a2e10532f98eb42d02d86fb31c00525d?network=testnet #[test] fn test_aptos_nft_offer() { let input = setup_proto_transaction("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", // Sender's address @@ -367,6 +372,7 @@ fn test_aptos_nft_offer() { }"#); } +// Successfully broadcasted https://explorer.aptoslabs.com/txn/0x0b8c64e6847c368e4c6bd2cce0e9eab378971b0ef2e3bc40cbd292910a80201d?network=testnet #[test] fn test_aptos_cancel_nft_offer() { let input = setup_proto_transaction("0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address @@ -415,6 +421,7 @@ fn test_aptos_cancel_nft_offer() { }"#); } +// Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x60b51e15140ec0b7650334e948fb447ce3cb13ae63492260461ebfa9d02e85c4?network=testnet #[test] fn test_aptos_nft_claim() { let input = setup_proto_transaction("0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address @@ -462,6 +469,7 @@ fn test_aptos_nft_claim() { }"#); } +// Successfully broadcasted https://explorer.aptoslabs.com/txn/0xe591252daed785641bfbbcf72a5d17864568cf32e04c0cc9129f3a13834d0e8e?network=testnet #[test] fn test_aptos_register_token() { let input = setup_proto_transaction("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address @@ -500,6 +508,7 @@ fn test_aptos_register_token() { }"#); } +// Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x25dca849cb4ebacbff223139f7ad5d24c37c225d9506b8b12a925de70429e685/userTxnOverview?network=mainnet #[test] fn test_aptos_tortuga_stake() { let input = setup_proto_transaction("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address @@ -543,6 +552,7 @@ fn test_aptos_tortuga_stake() { }"#); } +// Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x92edb4f756fe86118e34a0e64746c70260ee02c2ae2cf402b3e39f6a282ce968?network=mainnet #[test] fn test_aptos_tortuga_unstake() { let input = setup_proto_transaction("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address @@ -586,6 +596,7 @@ fn test_aptos_tortuga_unstake() { }"#); } +// // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x9fc874de7a7d3e813d9a1658d896023de270a0096a5e258c196005656ace7d54?network=mainnet #[test] fn test_aptos_tortuga_claim() { let input = setup_proto_transaction("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address @@ -629,6 +640,7 @@ fn test_aptos_tortuga_claim() { }"#); } +// Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x7efd69e7f9462774b932ce500ab51c0d0dcc004cf272e09f8ffd5804c2a84e33?network=mainnet #[test] fn test_aptos_blind_sign() { let input = setup_proto_transaction("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address @@ -686,4 +698,100 @@ fn test_aptos_blind_sign() { "type": "ed25519_signature" } }"#); +} + +// Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x25dca849cb4ebacbff223139f7ad5d24c37c225d9506b8b12a925de70429e685/payload +#[test] +fn test_aptos_blind_sign_staking() { + let input = setup_proto_transaction("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "blind_sign_json", + 43, // Sequence number + 1, + 100011, + 3664390082, + 100, + r#"{ + "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::stake", + "type_arguments": [], + "arguments": [ + "100000000" + ], + "type": "entry_function_payload" + }"#, + None, + ); + let output = Signer::::sign_proto(input); + test_tx_result(output, + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2b00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000ab860100000000006400000000000000c2276ada0000000001", // Expected raw transaction bytes + "a41b7440a50f36e8491319508734acb55488abc6d88fbc9cb2b37ba23210f01f5d08c856cb7abf18c414cf9302ee144450bd99495a7e21e61f624764db91eb0b", // Expected signature + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2b00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000ab860100000000006400000000000000c2276ada00000000010020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40a41b7440a50f36e8491319508734acb55488abc6d88fbc9cb2b37ba23210f01f5d08c856cb7abf18c414cf9302ee144450bd99495a7e21e61f624764db91eb0b", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "100011", + "payload": { + "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::stake", + "type_arguments": [], + "arguments": [ + "100000000" + ], + "type": "entry_function_payload" + }, + "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", + "sequence_number": "43", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0xa41b7440a50f36e8491319508734acb55488abc6d88fbc9cb2b37ba23210f01f5d08c856cb7abf18c414cf9302ee144450bd99495a7e21e61f624764db91eb0b", + "type": "ed25519_signature" + } + }"#); +} + +// Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x92edb4f756fe86118e34a0e64746c70260ee02c2ae2cf402b3e39f6a282ce968/payload +#[test] +fn test_aptos_blind_sign_unstaking() { + let input = setup_proto_transaction("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "blind_sign_json", + 44, // Sequence number + 1, + 100011, + 3664390082, + 100, + r#"{ + "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::unstake", + "type_arguments": [], + "arguments": [ + "99178100" + ], + "type": "entry_function_payload" + }"#, + None, + ); + let output = Signer::::sign_proto(input); + test_tx_result(output, + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e90500000000ab860100000000006400000000000000c2276ada0000000001", // Expected raw transaction bytes + "a58ad5e3331beb8c0212a18a1f932207cb664b78f5aad3cb1fe7435e0e0e053247ce49b38fd67b064bed34ed643eb6a03165d77c681d7d73ac3161ab984a960a", // Expected signature + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e90500000000ab860100000000006400000000000000c2276ada00000000010020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40a58ad5e3331beb8c0212a18a1f932207cb664b78f5aad3cb1fe7435e0e0e053247ce49b38fd67b064bed34ed643eb6a03165d77c681d7d73ac3161ab984a960a", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "100011", + "payload": { + "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::unstake", + "type_arguments": [], + "arguments": [ + "99178100" + ], + "type": "entry_function_payload" + }, + "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", + "sequence_number": "44", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0xa58ad5e3331beb8c0212a18a1f932207cb664b78f5aad3cb1fe7435e0e0e053247ce49b38fd67b064bed34ed643eb6a03165d77c681d7d73ac3161ab984a960a", + "type": "ed25519_signature" + } + }"#); } \ No newline at end of file diff --git a/src/Aptos/Entry.cpp b/src/Aptos/Entry.cpp index 18cd8ad0baa..35d8c776f2c 100644 --- a/src/Aptos/Entry.cpp +++ b/src/Aptos/Entry.cpp @@ -24,18 +24,11 @@ void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::D } Data Entry::preImageHashes([[maybe_unused]] TWCoinType coin, const Data& txInputData) const { - return txCompilerTemplate( - txInputData, [](const auto& input, auto& output) { - output = Signer::preImageHashes(input); - }); + return preImageHashesRust(coin, txInputData); } void Entry::compile([[maybe_unused]] TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const { - dataOut = txCompilerSingleTemplate( - txInputData, signatures, publicKeys, - [](const auto& input, auto& output, const auto& signature, const auto& publicKey) { - output = Signer::compile(input, signature, publicKey); - }); + compileRust(coin, txInputData, signatures, publicKeys, dataOut); } } // namespace TW::Aptos From f45ee4d0d495a344f46466b26faf8396fb60be4a Mon Sep 17 00:00:00 2001 From: Milerius Date: Mon, 13 Nov 2023 11:48:07 -0500 Subject: [PATCH 45/60] feat(aptos): finalize pr --- rust/tw_aptos/src/constants.rs | 3 +- rust/tw_aptos/src/transaction.rs | 3 +- tests/chains/Aptos/CompilerTests.cpp | 54 ++-------------------------- 3 files changed, 7 insertions(+), 53 deletions(-) diff --git a/rust/tw_aptos/src/constants.rs b/rust/tw_aptos/src/constants.rs index 63eceb2063c..6af0c572003 100644 --- a/rust/tw_aptos/src/constants.rs +++ b/rust/tw_aptos/src/constants.rs @@ -5,4 +5,5 @@ // file LICENSE at the root of the source code distribution tree. pub const GAS_UNIT_PRICE: u64 = 100; -pub const MAX_GAS_AMOUNT: u64 = 100_000_000; \ No newline at end of file +pub const MAX_GAS_AMOUNT: u64 = 100_000_000; +pub const APTOS_SALT: &[u8] = b"APTOS::RawTransaction"; \ No newline at end of file diff --git a/rust/tw_aptos/src/transaction.rs b/rust/tw_aptos/src/transaction.rs index 49279027434..a6346685e35 100644 --- a/rust/tw_aptos/src/transaction.rs +++ b/rust/tw_aptos/src/transaction.rs @@ -13,6 +13,7 @@ use tw_keypair::ed25519::sha512::KeyPair; use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; use tw_encoding::hex::encode; use tw_proto::Aptos::Proto; +use crate::constants::APTOS_SALT; use crate::transaction_payload::{EntryFunction, TransactionPayload}; #[derive(Clone, Serialize)] @@ -135,7 +136,7 @@ impl RawTransaction { fn msg_to_sign(&self) -> Vec { let serialized = self.serialize(); - let mut preimage = tw_hash::sha3::sha3_256("APTOS::RawTransaction".as_bytes()); + let mut preimage = tw_hash::sha3::sha3_256(APTOS_SALT); preimage.extend_from_slice(serialized.as_slice()); preimage } diff --git a/tests/chains/Aptos/CompilerTests.cpp b/tests/chains/Aptos/CompilerTests.cpp index 6f731cfbfe5..ea5e522d669 100644 --- a/tests/chains/Aptos/CompilerTests.cpp +++ b/tests/chains/Aptos/CompilerTests.cpp @@ -66,12 +66,12 @@ TEST(AptosCompiler, StandardTransaction) { "gas_unit_price": "100", "max_gas_amount": "3296766", "payload": { - "arguments": ["0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], + "arguments": ["0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], "function": "0x1::aptos_account::transfer", "type": "entry_function_payload", "type_arguments": [] }, - "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", "sequence_number": "99", "signature": { "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", @@ -160,7 +160,7 @@ TEST(AptosCompiler, BlindTransactionJson) { ], "type": "entry_function_payload" }, - "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", "sequence_number": "42", "signature": { "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", @@ -173,52 +173,4 @@ TEST(AptosCompiler, BlindTransactionJson) { assertJSONEqual(expectedJson, parsedJson); } -TEST(AptosCompiler, BlindTransactionHex) { - // successfully broadcasted https://explorer.aptoslabs.com/txn/0xd95857a9e644528708778a3a0a6e13986751944fca30eaac98853c1655de0422?network=Devnet - auto anyEncoded = "0xb5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b19307968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300200000000000000024633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b29883704706f6f6c0b737761705f795f746f5f780207deae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e010c6465766e65745f636f696e730a4465766e657455534454000700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00020800e1f50500000000080000000000000000fe4d3200000000006400000000000000c2276ada0000000021"; - - Proto::SigningInput input; - input.set_any_encoded(anyEncoded); - - auto inputString = input.SerializeAsString(); - auto inputStrData = TW::Data(inputString.begin(), inputString.end()); - - // Pre-hash the transaction. - - auto preImageHashesData = TransactionCompiler::preImageHashes(TWCoinTypeAptos, inputStrData); - TxCompiler::Proto::PreSigningOutput preSigningOutput; - preSigningOutput.ParseFromArray(preImageHashesData.data(), static_cast(preImageHashesData.size())); - auto actualDataToSign = data(preSigningOutput.data()); - - EXPECT_EQ(preSigningOutput.error(), Common::Proto::OK); - EXPECT_EQ(actualDataToSign, parse_hex(anyEncoded)); - - // Sign the pre-hash data. - - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519).bytes; - auto signature = privateKey.sign(actualDataToSign, TWCurveED25519); - EXPECT_EQ(hex(signature), "9e81026fdd43986f4d5588afdab875cd18b64dc15b3489fcc00ed46fc361915b27e23e0cefe6d23698ee76a562915fe85e99185dbc1dd29ba720f7fad144af0b"); - - // Compile the transaction. - - auto outputData = TransactionCompiler::compileWithSignatures(TWCoinTypeAptos, inputStrData, {signature}, {publicKey}); - Proto::SigningOutput output; - output.ParseFromArray(outputData.data(), static_cast(outputData.size())); - - EXPECT_EQ(output.error(), Common::Proto::OK); - ASSERT_EQ(hex(output.raw_txn()), "b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b19307968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300200000000000000024633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b29883704706f6f6c0b737761705f795f746f5f780207deae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e010c6465766e65745f636f696e730a4465766e657455534454000700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00020800e1f50500000000080000000000000000fe4d3200000000006400000000000000c2276ada0000000021"); - ASSERT_EQ(hex(output.authenticator().signature()), "9e81026fdd43986f4d5588afdab875cd18b64dc15b3489fcc00ed46fc361915b27e23e0cefe6d23698ee76a562915fe85e99185dbc1dd29ba720f7fad144af0b"); - ASSERT_EQ(hex(output.encoded()), "b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b19307968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300200000000000000024633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b29883704706f6f6c0b737761705f795f746f5f780207deae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e010c6465766e65745f636f696e730a4465766e657455534454000700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00020800e1f50500000000080000000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c409e81026fdd43986f4d5588afdab875cd18b64dc15b3489fcc00ed46fc361915b27e23e0cefe6d23698ee76a562915fe85e99185dbc1dd29ba720f7fad144af0b"); - nlohmann::json expectedJson = R"( - { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0x9e81026fdd43986f4d5588afdab875cd18b64dc15b3489fcc00ed46fc361915b27e23e0cefe6d23698ee76a562915fe85e99185dbc1dd29ba720f7fad144af0b", - "type": "ed25519_signature" - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(output.json()); - assertJSONEqual(expectedJson, parsedJson); -} - } From 48b7d68f73dc6220cbbfd1404cf3ab05a8e6e068 Mon Sep 17 00:00:00 2001 From: Milerius Date: Mon, 13 Nov 2023 11:53:51 -0500 Subject: [PATCH 46/60] feat(aptos): reformat --- rust/tw_aptos/Cargo.toml | 2 +- rust/tw_aptos/src/address.rs | 15 ++++++++------- rust/tw_aptos/src/aptos_move_packages.rs | 8 ++++---- rust/tw_aptos/src/compiler.rs | 2 +- rust/tw_aptos/src/liquid_staking.rs | 4 ++-- rust/tw_aptos/src/nft.rs | 5 +++-- rust/tw_aptos/src/transaction.rs | 3 ++- rust/tw_aptos/src/transaction_builder.rs | 6 +++--- 8 files changed, 24 insertions(+), 21 deletions(-) diff --git a/rust/tw_aptos/Cargo.toml b/rust/tw_aptos/Cargo.toml index 2983f82b8fd..07e737d6390 100644 --- a/rust/tw_aptos/Cargo.toml +++ b/rust/tw_aptos/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" anyhow = "1.0.75" serde_json = "1.0.108" tw_coin_entry = { path = "../tw_coin_entry" } -tw_encoding = { path = "../tw_encoding"} +tw_encoding = { path = "../tw_encoding" } tw_keypair = { path = "../tw_keypair" } tw_proto = { path = "../tw_proto" } tw_number = { path = "../tw_number" } diff --git a/rust/tw_aptos/src/address.rs b/rust/tw_aptos/src/address.rs index a1c20b50171..80f33ab1121 100644 --- a/rust/tw_aptos/src/address.rs +++ b/rust/tw_aptos/src/address.rs @@ -14,7 +14,7 @@ use tw_memory::Data; use tw_hash::sha3::sha3_256; -pub trait AptosAddress: FromStr + Into
{ +pub trait AptosAddress: FromStr + Into
{ /// Tries to parse an address from the string representation. /// Returns `Ok(None)` if the given `s` string is empty. #[inline] @@ -33,6 +33,7 @@ impl AptosAddress for Address {} pub enum Scheme { Ed25519 = 0, } + #[derive(Clone)] pub struct Address { addr: AccountAddress, @@ -47,7 +48,7 @@ impl Address { to_hash.push(Scheme::Ed25519 as u8); let hashed = sha3_256(to_hash.as_slice()); let addr = AccountAddress::from_bytes(hashed).map_err(from_account_error)?; - Ok(Address{addr}) + Ok(Address { addr }) } pub fn inner(&self) -> AccountAddress { @@ -89,13 +90,13 @@ impl FromStr for Address { } if working.len() > NUM_CHARS { - return Err(AddressError::InvalidInput) + return Err(AddressError::InvalidInput); } else if !has_0x && working.len() < NUM_CHARS { - return Err(AddressError::InvalidInput) + return Err(AddressError::InvalidInput); } if !working.chars().all(|c| char::is_ascii_hexdigit(&c)) { - return Err(AddressError::InvalidInput) + return Err(AddressError::InvalidInput); } let addr = if has_0x { @@ -104,7 +105,7 @@ impl FromStr for Address { AccountAddress::from_str(s.trim()) }.map_err(from_account_error)?; - Ok(Address{addr}) + Ok(Address { addr }) } } @@ -130,6 +131,6 @@ mod tests { #[test] fn test_from_account_error() { - assert_eq!(from_account_error(AccountAddressParseError{}), AddressError::InvalidInput); + assert_eq!(from_account_error(AccountAddressParseError {}), AddressError::InvalidInput); } } \ No newline at end of file diff --git a/rust/tw_aptos/src/aptos_move_packages.rs b/rust/tw_aptos/src/aptos_move_packages.rs index 5709782933b..1ca6a53c15c 100644 --- a/rust/tw_aptos/src/aptos_move_packages.rs +++ b/rust/tw_aptos/src/aptos_move_packages.rs @@ -103,7 +103,7 @@ pub fn token_transfers_offer_script( bcs::to_bytes(&property_version).unwrap(), bcs::to_bytes(&amount).unwrap(), ], - json!([receiver.to_hex_literal(), creator.to_hex_literal(), String::from_utf8_lossy(&collection), String::from_utf8_lossy(&name), property_version.to_string(), amount.to_string()]) + json!([receiver.to_hex_literal(), creator.to_hex_literal(), String::from_utf8_lossy(&collection), String::from_utf8_lossy(&name), property_version.to_string(), amount.to_string()]), )) } @@ -131,7 +131,7 @@ pub fn token_transfers_cancel_offer_script( bcs::to_bytes(&name).unwrap(), bcs::to_bytes(&property_version).unwrap(), ], - json!([receiver.to_hex_literal(), creator.to_hex_literal(), String::from_utf8_lossy(&collection), String::from_utf8_lossy(&name), property_version.to_string()]) + json!([receiver.to_hex_literal(), creator.to_hex_literal(), String::from_utf8_lossy(&collection), String::from_utf8_lossy(&name), property_version.to_string()]), )) } @@ -159,7 +159,7 @@ pub fn token_transfers_claim_script( bcs::to_bytes(&name).unwrap(), bcs::to_bytes(&property_version).unwrap(), ], - json!([sender.to_hex_literal(), creator.to_hex_literal(), String::from_utf8_lossy(&collection), String::from_utf8_lossy(&name), property_version.to_string()]) + json!([sender.to_hex_literal(), creator.to_hex_literal(), String::from_utf8_lossy(&collection), String::from_utf8_lossy(&name), property_version.to_string()]), )) } @@ -175,6 +175,6 @@ pub fn managed_coin_register(coin_type: TypeTag) -> TransactionPayload { ident_str!("register").to_owned(), vec![coin_type], vec![], - json!([]) + json!([]), )) } \ No newline at end of file diff --git a/rust/tw_aptos/src/compiler.rs b/rust/tw_aptos/src/compiler.rs index ce263de6ffb..ec985acdab2 100644 --- a/rust/tw_aptos/src/compiler.rs +++ b/rust/tw_aptos/src/compiler.rs @@ -52,7 +52,7 @@ impl Compiler { let builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone())?; let sender = Address::from_str(&input.sender)?; let signed_tx = builder.sender(sender.inner()).sequence_number(input.sequence_number as u64).build().compile( - signatures.first().unwrap().to_vec(), public_keys.first().unwrap().to_vec() + signatures.first().unwrap().to_vec(), public_keys.first().unwrap().to_vec(), )?; Ok(Proto::SigningOutput { raw_txn: signed_tx.raw_txn_bytes().clone().into(), diff --git a/rust/tw_aptos/src/liquid_staking.rs b/rust/tw_aptos/src/liquid_staking.rs index 621e25c5090..a5bf77c8e4a 100644 --- a/rust/tw_aptos/src/liquid_staking.rs +++ b/rust/tw_aptos/src/liquid_staking.rs @@ -8,12 +8,12 @@ use std::str::FromStr; use move_core_types::{ account_address::AccountAddress, ident_str, - language_storage::ModuleId + language_storage::ModuleId, }; use serde_json::json; use tw_proto::{ Aptos::Proto::{LiquidStaking, TortugaClaim, TortugaStake, TortugaUnstake}, - Aptos::Proto::mod_LiquidStaking::OneOfliquid_stake_transaction_payload + Aptos::Proto::mod_LiquidStaking::OneOfliquid_stake_transaction_payload, }; use crate::transaction_payload::{EntryFunction, TransactionPayload}; diff --git a/rust/tw_aptos/src/nft.rs b/rust/tw_aptos/src/nft.rs index ee323a5521e..be36195b37e 100644 --- a/rust/tw_aptos/src/nft.rs +++ b/rust/tw_aptos/src/nft.rs @@ -15,7 +15,7 @@ pub struct Offer { pub collection: Vec, pub name: Vec, pub property_version: u64, - pub amount: u64 + pub amount: u64, } pub struct Claim { @@ -29,7 +29,7 @@ pub struct Claim { pub enum NftOperation { Claim(Claim), Offer(Offer), - Cancel(Offer) + Cancel(Offer), } impl From> for NftOperation { @@ -52,6 +52,7 @@ impl From for NftMessage<'_> { } } } + impl From> for Offer { fn from(value: OfferNftMessage) -> Self { Offer { diff --git a/rust/tw_aptos/src/transaction.rs b/rust/tw_aptos/src/transaction.rs index a6346685e35..f7773e4a37c 100644 --- a/rust/tw_aptos/src/transaction.rs +++ b/rust/tw_aptos/src/transaction.rs @@ -46,7 +46,6 @@ impl TransactionAuthenticator { pub fn to_json(&self) -> Value { match self { TransactionAuthenticator::Ed25519 { public_key, signature } => { - json!({"public_key": encode(&public_key, true), "signature": encode(&signature, true), "type": "ed25519_signature"}) @@ -84,6 +83,7 @@ pub struct RawTransaction { /// Chain ID of the Aptos network this transaction is intended for. chain_id: u8, } + impl RawTransaction { /// Create a new `RawTransaction` with a payload. /// @@ -204,6 +204,7 @@ pub struct SignedTransaction { /// Encoded bytes to be broadcast encoded: Vec, } + impl SignedTransaction { pub fn raw_txn(&self) -> &RawTransaction { &self.raw_txn diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index b080adc9155..206d4f208d7 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -71,7 +71,7 @@ impl TransactionFactory { } } - pub fn new_from_protobuf(input: SigningInput) -> SigningResult { + pub fn new_from_protobuf(input: SigningInput) -> SigningResult { let factory = TransactionFactory::new(input.chain_id as u8) .with_gas_unit_price(input.gas_unit_price) .with_max_gas_amount(input.max_gas_amount) @@ -83,7 +83,7 @@ impl TransactionFactory { OneOftransaction_payload::token_transfer(token_transfer) => { let func = token_transfer.function.unwrap(); Ok(factory.coins_transfer(AccountAddress::from_str(&token_transfer.to).unwrap(), token_transfer.amount, - convert_proto_struct_tag_to_type_tag(func), + convert_proto_struct_tag_to_type_tag(func), )) } OneOftransaction_payload::create_account(create_account) => { @@ -101,7 +101,7 @@ impl TransactionFactory { OneOftransaction_payload::token_transfer_coins(token_transfer_coins) => { let func = token_transfer_coins.function.unwrap(); Ok(factory.implicitly_create_user_and_coins_transfer(AccountAddress::from_str(&token_transfer_coins.to).unwrap(), token_transfer_coins.amount, - convert_proto_struct_tag_to_type_tag(func))) + convert_proto_struct_tag_to_type_tag(func))) } OneOftransaction_payload::None => { let is_blind_sign = !input.any_encoded.is_empty(); From d089f202ed63e6091bd9fb97512b61cb27871dce Mon Sep 17 00:00:00 2001 From: Milerius Date: Mon, 13 Nov 2023 11:55:08 -0500 Subject: [PATCH 47/60] feat(aptos): reformat --- .../tests/tw_any_address_ffi_tests.rs | 18 +- rust/tw_aptos/src/address.rs | 23 +- rust/tw_aptos/src/aptos_move_packages.rs | 32 +- rust/tw_aptos/src/compiler.rs | 25 +- rust/tw_aptos/src/constants.rs | 2 +- rust/tw_aptos/src/entry.rs | 16 +- rust/tw_aptos/src/lib.rs | 4 +- rust/tw_aptos/src/liquid_staking.rs | 106 ++-- rust/tw_aptos/src/nft.rs | 48 +- rust/tw_aptos/src/serde_helper/mod.rs | 2 +- rust/tw_aptos/src/serde_helper/vec_bytes.rs | 10 +- rust/tw_aptos/src/signer.rs | 12 +- rust/tw_aptos/src/transaction.rs | 49 +- rust/tw_aptos/src/transaction_builder.rs | 126 +++-- rust/tw_aptos/src/transaction_payload.rs | 77 ++- rust/tw_aptos/tests/signer.rs | 486 ++++++++++-------- rust/tw_coin_entry/src/error.rs | 2 +- rust/tw_keypair/src/tw/public.rs | 4 +- 18 files changed, 628 insertions(+), 414 deletions(-) diff --git a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs index 7fb1f6e80fa..10013b4e6d3 100644 --- a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs +++ b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs @@ -34,7 +34,9 @@ fn test_any_address_derive() { // TODO match `CoinType` when it's generated. let expected_address = match coin.blockchain { - BlockchainType::Aptos => "0x9006fa46f038224e8004bdda97f2e7a60c2c3d135bce7cb15541e5c0aae907a4", + BlockchainType::Aptos => { + "0x9006fa46f038224e8004bdda97f2e7a60c2c3d135bce7cb15541e5c0aae907a4" + }, // By default, Bitcoin will return a P2PKH address. BlockchainType::Bitcoin => "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", BlockchainType::Ethereum => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309", @@ -112,7 +114,7 @@ fn test_any_address_is_valid_coin() { "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", "19aadeca9388e009d136245b9a67423f3eee242b03142849eb4f81a4a409e59c", "0x777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb", - "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175" + "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175", ], BlockchainType::Bitcoin => vec![ "1MrZNGN7mfWZiZNQttrzHjfw72jnJC2JNx", @@ -150,11 +152,13 @@ fn test_any_address_is_valid_coin_invalid() { for coin in supported_coin_items() { let invalid = match coin.blockchain { BlockchainType::Aptos => { - vec!["", // Empty - "Seff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", // Invalid Hex - "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175bb", // Too long - "0xSeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b"] - } + vec![ + "", // Empty + "Seff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", // Invalid Hex + "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175bb", // Too long + "0xSeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", + ] + }, BlockchainType::Bitcoin => { vec!["0xb16db98b365b1f89191996942612b14f1da4bd5f"] }, diff --git a/rust/tw_aptos/src/address.rs b/rust/tw_aptos/src/address.rs index 80f33ab1121..85a9733c00f 100644 --- a/rust/tw_aptos/src/address.rs +++ b/rust/tw_aptos/src/address.rs @@ -4,17 +4,16 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use move_core_types::account_address::{AccountAddress, AccountAddressParseError}; use std::fmt::{Display, Formatter}; use std::str::FromStr; -use move_core_types::account_address::{AccountAddress, AccountAddressParseError}; use tw_coin_entry::coin_entry::CoinAddress; use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_hash::sha3::sha3_256; use tw_keypair::ed25519; use tw_memory::Data; -use tw_hash::sha3::sha3_256; - -pub trait AptosAddress: FromStr + Into
{ +pub trait AptosAddress: FromStr + Into
{ /// Tries to parse an address from the string representation. /// Returns `Ok(None)` if the given `s` string is empty. #[inline] @@ -43,7 +42,9 @@ impl Address { pub const LENGTH: usize = AccountAddress::LENGTH; /// Initializes an address with a `ed25519` public key. - pub fn with_ed25519_pubkey(pubkey: &ed25519::sha512::PublicKey) -> Result { + pub fn with_ed25519_pubkey( + pubkey: &ed25519::sha512::PublicKey, + ) -> Result { let mut to_hash = pubkey.as_slice().to_vec(); to_hash.push(Scheme::Ed25519 as u8); let hashed = sha3_256(to_hash.as_slice()); @@ -103,7 +104,8 @@ impl FromStr for Address { AccountAddress::from_hex_literal(s.trim()) } else { AccountAddress::from_str(s.trim()) - }.map_err(from_account_error)?; + } + .map_err(from_account_error)?; Ok(Address { addr }) } @@ -119,7 +121,7 @@ mod tests { let private = PrivateKey::try_from( "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5", ) - .unwrap(); + .unwrap(); let public = private.public(); let addr = Address::with_ed25519_pubkey(&public); assert_eq!( @@ -131,6 +133,9 @@ mod tests { #[test] fn test_from_account_error() { - assert_eq!(from_account_error(AccountAddressParseError {}), AddressError::InvalidInput); + assert_eq!( + from_account_error(AccountAddressParseError {}), + AddressError::InvalidInput + ); } -} \ No newline at end of file +} diff --git a/rust/tw_aptos/src/aptos_move_packages.rs b/rust/tw_aptos/src/aptos_move_packages.rs index 1ca6a53c15c..83898b122f2 100644 --- a/rust/tw_aptos/src/aptos_move_packages.rs +++ b/rust/tw_aptos/src/aptos_move_packages.rs @@ -4,18 +4,19 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use crate::transaction_payload::{EntryFunction, TransactionPayload}; use move_core_types::account_address::AccountAddress; use move_core_types::ident_str; use move_core_types::language_storage::{ModuleId, TypeTag}; use serde_json::json; -use crate::transaction_payload::{EntryFunction, TransactionPayload}; pub fn aptos_account_transfer(to: AccountAddress, amount: u64) -> TransactionPayload { TransactionPayload::EntryFunction(EntryFunction::new( ModuleId::new( AccountAddress::new([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, ]), + 0, 0, 0, 1, + ]), ident_str!("aptos_account").to_owned(), ), ident_str!("transfer").to_owned(), @@ -103,7 +104,14 @@ pub fn token_transfers_offer_script( bcs::to_bytes(&property_version).unwrap(), bcs::to_bytes(&amount).unwrap(), ], - json!([receiver.to_hex_literal(), creator.to_hex_literal(), String::from_utf8_lossy(&collection), String::from_utf8_lossy(&name), property_version.to_string(), amount.to_string()]), + json!([ + receiver.to_hex_literal(), + creator.to_hex_literal(), + String::from_utf8_lossy(&collection), + String::from_utf8_lossy(&name), + property_version.to_string(), + amount.to_string() + ]), )) } @@ -131,7 +139,13 @@ pub fn token_transfers_cancel_offer_script( bcs::to_bytes(&name).unwrap(), bcs::to_bytes(&property_version).unwrap(), ], - json!([receiver.to_hex_literal(), creator.to_hex_literal(), String::from_utf8_lossy(&collection), String::from_utf8_lossy(&name), property_version.to_string()]), + json!([ + receiver.to_hex_literal(), + creator.to_hex_literal(), + String::from_utf8_lossy(&collection), + String::from_utf8_lossy(&name), + property_version.to_string() + ]), )) } @@ -159,7 +173,13 @@ pub fn token_transfers_claim_script( bcs::to_bytes(&name).unwrap(), bcs::to_bytes(&property_version).unwrap(), ], - json!([sender.to_hex_literal(), creator.to_hex_literal(), String::from_utf8_lossy(&collection), String::from_utf8_lossy(&name), property_version.to_string()]), + json!([ + sender.to_hex_literal(), + creator.to_hex_literal(), + String::from_utf8_lossy(&collection), + String::from_utf8_lossy(&name), + property_version.to_string() + ]), )) } @@ -177,4 +197,4 @@ pub fn managed_coin_register(coin_type: TypeTag) -> TransactionPayload { vec![], json!([]), )) -} \ No newline at end of file +} diff --git a/rust/tw_aptos/src/compiler.rs b/rust/tw_aptos/src/compiler.rs index ec985acdab2..a63aa8a0464 100644 --- a/rust/tw_aptos/src/compiler.rs +++ b/rust/tw_aptos/src/compiler.rs @@ -1,13 +1,13 @@ +use crate::address::Address; +use crate::signer::AptosContext; +use crate::transaction_builder; use std::marker::PhantomData; use std::str::FromStr; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; use tw_coin_entry::error::SigningResult; use tw_coin_entry::signing_output_error; use tw_proto::Aptos::Proto; -use crate::signer::AptosContext; use tw_proto::TxCompiler::Proto as CompilerProto; -use crate::address::Address; -use crate::transaction_builder; pub struct Compiler { _phantom: PhantomData, @@ -27,7 +27,11 @@ impl Compiler { ) -> SigningResult> { let builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone())?; let sender = Address::from_str(&input.sender)?; - let signed_tx = builder.sender(sender.inner()).sequence_number(input.sequence_number as u64).build().pre_image(); + let signed_tx = builder + .sender(sender.inner()) + .sequence_number(input.sequence_number as u64) + .build() + .pre_image(); Ok(CompilerProto::PreSigningOutput { data: signed_tx.into(), ..CompilerProto::PreSigningOutput::default() @@ -51,9 +55,14 @@ impl Compiler { ) -> SigningResult> { let builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone())?; let sender = Address::from_str(&input.sender)?; - let signed_tx = builder.sender(sender.inner()).sequence_number(input.sequence_number as u64).build().compile( - signatures.first().unwrap().to_vec(), public_keys.first().unwrap().to_vec(), - )?; + let signed_tx = builder + .sender(sender.inner()) + .sequence_number(input.sequence_number as u64) + .build() + .compile( + signatures.first().unwrap().to_vec(), + public_keys.first().unwrap().to_vec(), + )?; Ok(Proto::SigningOutput { raw_txn: signed_tx.raw_txn_bytes().clone().into(), encoded: signed_tx.encoded().clone().into(), @@ -62,4 +71,4 @@ impl Compiler { ..Proto::SigningOutput::default() }) } -} \ No newline at end of file +} diff --git a/rust/tw_aptos/src/constants.rs b/rust/tw_aptos/src/constants.rs index 6af0c572003..6c30d3b68bc 100644 --- a/rust/tw_aptos/src/constants.rs +++ b/rust/tw_aptos/src/constants.rs @@ -6,4 +6,4 @@ pub const GAS_UNIT_PRICE: u64 = 100; pub const MAX_GAS_AMOUNT: u64 = 100_000_000; -pub const APTOS_SALT: &[u8] = b"APTOS::RawTransaction"; \ No newline at end of file +pub const APTOS_SALT: &[u8] = b"APTOS::RawTransaction"; diff --git a/rust/tw_aptos/src/entry.rs b/rust/tw_aptos/src/entry.rs index f93a7363b4d..0babd68600d 100644 --- a/rust/tw_aptos/src/entry.rs +++ b/rust/tw_aptos/src/entry.rs @@ -4,6 +4,9 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use crate::address::Address; +use crate::compiler::Compiler; +use crate::signer::{Signer, StandardAptosContext}; use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; @@ -16,10 +19,6 @@ use tw_coin_entry::prefix::NoPrefix; use tw_keypair::tw::PublicKey; use tw_proto::Aptos::Proto; use tw_proto::TxCompiler::Proto as CompilerProto; -use crate::address::Address; -use crate::compiler::Compiler; -use crate::signer::{Signer, StandardAptosContext}; - pub struct AptosEntry; @@ -68,8 +67,7 @@ impl CoinEntry for AptosEntry { &self, _coin: &dyn CoinContext, input: Self::SigningInput<'_>, - ) -> Self::PreSigningOutput - { + ) -> Self::PreSigningOutput { Compiler::::preimage_hashes(input) } @@ -90,5 +88,7 @@ impl CoinEntry for AptosEntry { } #[inline] - fn message_signer(&self) -> Option { None } -} \ No newline at end of file + fn message_signer(&self) -> Option { + None + } +} diff --git a/rust/tw_aptos/src/lib.rs b/rust/tw_aptos/src/lib.rs index 2a085865782..b85498f42c2 100644 --- a/rust/tw_aptos/src/lib.rs +++ b/rust/tw_aptos/src/lib.rs @@ -4,17 +4,17 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -mod serde_helper; pub mod address; pub mod aptos_move_packages; pub mod constants; pub mod entry; +mod serde_helper; pub mod nft; +pub mod compiler; pub mod liquid_staking; pub mod signer; pub mod transaction; pub mod transaction_builder; pub mod transaction_payload; -pub mod compiler; diff --git a/rust/tw_aptos/src/liquid_staking.rs b/rust/tw_aptos/src/liquid_staking.rs index a5bf77c8e4a..6b6a11197a6 100644 --- a/rust/tw_aptos/src/liquid_staking.rs +++ b/rust/tw_aptos/src/liquid_staking.rs @@ -4,23 +4,16 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use std::str::FromStr; -use move_core_types::{ - account_address::AccountAddress, - ident_str, - language_storage::ModuleId, -}; +use crate::transaction_payload::{EntryFunction, TransactionPayload}; +use move_core_types::{account_address::AccountAddress, ident_str, language_storage::ModuleId}; use serde_json::json; +use std::str::FromStr; use tw_proto::{ - Aptos::Proto::{LiquidStaking, TortugaClaim, TortugaStake, TortugaUnstake}, Aptos::Proto::mod_LiquidStaking::OneOfliquid_stake_transaction_payload, + Aptos::Proto::{LiquidStaking, TortugaClaim, TortugaStake, TortugaUnstake}, }; -use crate::transaction_payload::{EntryFunction, TransactionPayload}; -pub fn tortuga_stake( - smart_contract_address: AccountAddress, - amount: u64, -) -> TransactionPayload { +pub fn tortuga_stake(smart_contract_address: AccountAddress, amount: u64) -> TransactionPayload { TransactionPayload::EntryFunction(EntryFunction::new( ModuleId::new( smart_contract_address, @@ -33,10 +26,7 @@ pub fn tortuga_stake( )) } -pub fn tortuga_unstake( - smart_contract_address: AccountAddress, - amount: u64, -) -> TransactionPayload { +pub fn tortuga_unstake(smart_contract_address: AccountAddress, amount: u64) -> TransactionPayload { TransactionPayload::EntryFunction(EntryFunction::new( ModuleId::new( smart_contract_address, @@ -49,10 +39,7 @@ pub fn tortuga_unstake( )) } -pub fn tortuga_claim( - smart_contract_address: AccountAddress, - idx: u64, -) -> TransactionPayload { +pub fn tortuga_claim(smart_contract_address: AccountAddress, idx: u64) -> TransactionPayload { TransactionPayload::EntryFunction(EntryFunction::new( ModuleId::new( smart_contract_address, @@ -90,15 +77,29 @@ impl From> for LiquidStakingOperation { fn from(value: LiquidStaking) -> Self { match value.liquid_stake_transaction_payload { OneOfliquid_stake_transaction_payload::stake(stake_msg) => { - LiquidStakingOperation::Stake(Stake { amount: stake_msg.amount, smart_contract_address: AccountAddress::from_str(&value.smart_contract_address).unwrap() }) - } + LiquidStakingOperation::Stake(Stake { + amount: stake_msg.amount, + smart_contract_address: AccountAddress::from_str(&value.smart_contract_address) + .unwrap(), + }) + }, OneOfliquid_stake_transaction_payload::unstake(unstake_msg) => { - LiquidStakingOperation::Unstake(Unstake { amount: unstake_msg.amount, smart_contract_address: AccountAddress::from_str(&value.smart_contract_address).unwrap() }) - } + LiquidStakingOperation::Unstake(Unstake { + amount: unstake_msg.amount, + smart_contract_address: AccountAddress::from_str(&value.smart_contract_address) + .unwrap(), + }) + }, OneOfliquid_stake_transaction_payload::claim(claim) => { - LiquidStakingOperation::Claim(Claim { idx: claim.idx, smart_contract_address: AccountAddress::from_str(&value.smart_contract_address).unwrap() }) - } - OneOfliquid_stake_transaction_payload::None => { todo!() } + LiquidStakingOperation::Claim(Claim { + idx: claim.idx, + smart_contract_address: AccountAddress::from_str(&value.smart_contract_address) + .unwrap(), + }) + }, + OneOfliquid_stake_transaction_payload::None => { + todo!() + }, } } } @@ -106,33 +107,28 @@ impl From> for LiquidStakingOperation { impl From for LiquidStaking<'_> { fn from(value: LiquidStakingOperation) -> Self { match value { - LiquidStakingOperation::Stake(stake) => { - LiquidStaking { - smart_contract_address: stake.smart_contract_address.to_hex_literal().into(), - liquid_stake_transaction_payload: OneOfliquid_stake_transaction_payload::stake( - TortugaStake { - amount: stake.amount - }), - } - } - LiquidStakingOperation::Unstake(unstake) => { - LiquidStaking { - smart_contract_address: unstake.smart_contract_address.to_hex_literal().into(), - liquid_stake_transaction_payload: OneOfliquid_stake_transaction_payload::unstake( - TortugaUnstake { - amount: unstake.amount - }), - } - } - LiquidStakingOperation::Claim(claim) => { - LiquidStaking { - smart_contract_address: claim.smart_contract_address.to_hex_literal().into(), - liquid_stake_transaction_payload: OneOfliquid_stake_transaction_payload::claim( - TortugaClaim { - idx: claim.idx - }), - } - } + LiquidStakingOperation::Stake(stake) => LiquidStaking { + smart_contract_address: stake.smart_contract_address.to_hex_literal().into(), + liquid_stake_transaction_payload: OneOfliquid_stake_transaction_payload::stake( + TortugaStake { + amount: stake.amount, + }, + ), + }, + LiquidStakingOperation::Unstake(unstake) => LiquidStaking { + smart_contract_address: unstake.smart_contract_address.to_hex_literal().into(), + liquid_stake_transaction_payload: OneOfliquid_stake_transaction_payload::unstake( + TortugaUnstake { + amount: unstake.amount, + }, + ), + }, + LiquidStakingOperation::Claim(claim) => LiquidStaking { + smart_contract_address: claim.smart_contract_address.to_hex_literal().into(), + liquid_stake_transaction_payload: OneOfliquid_stake_transaction_payload::claim( + TortugaClaim { idx: claim.idx }, + ), + }, } } -} \ No newline at end of file +} diff --git a/rust/tw_aptos/src/nft.rs b/rust/tw_aptos/src/nft.rs index be36195b37e..823eadf8410 100644 --- a/rust/tw_aptos/src/nft.rs +++ b/rust/tw_aptos/src/nft.rs @@ -4,8 +4,8 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use std::str::FromStr; use move_core_types::account_address::AccountAddress; +use std::str::FromStr; use tw_proto::Aptos::Proto::mod_NftMessage::OneOfnft_transaction_payload; use tw_proto::Aptos::Proto::{CancelOfferNftMessage, ClaimNftMessage, NftMessage, OfferNftMessage}; @@ -35,10 +35,12 @@ pub enum NftOperation { impl From> for NftOperation { fn from(value: NftMessage) -> Self { match value.nft_transaction_payload { - OneOfnft_transaction_payload::offer_nft(msg) => { NftOperation::Offer(msg.into()) } - OneOfnft_transaction_payload::cancel_offer_nft(msg) => { NftOperation::Cancel(msg.into()) } - OneOfnft_transaction_payload::claim_nft(msg) => { NftOperation::Claim(msg.into()) } - OneOfnft_transaction_payload::None => { todo!() } + OneOfnft_transaction_payload::offer_nft(msg) => NftOperation::Offer(msg.into()), + OneOfnft_transaction_payload::cancel_offer_nft(msg) => NftOperation::Cancel(msg.into()), + OneOfnft_transaction_payload::claim_nft(msg) => NftOperation::Claim(msg.into()), + OneOfnft_transaction_payload::None => { + todo!() + }, } } } @@ -46,9 +48,17 @@ impl From> for NftOperation { impl From for NftMessage<'_> { fn from(value: NftOperation) -> Self { match value { - NftOperation::Claim(claim) => { NftMessage { nft_transaction_payload: OneOfnft_transaction_payload::claim_nft(claim.into()) } } - NftOperation::Offer(offer) => { NftMessage { nft_transaction_payload: OneOfnft_transaction_payload::offer_nft(offer.into()) } } - NftOperation::Cancel(cancel) => { NftMessage { nft_transaction_payload: OneOfnft_transaction_payload::cancel_offer_nft(cancel.into()) } } + NftOperation::Claim(claim) => NftMessage { + nft_transaction_payload: OneOfnft_transaction_payload::claim_nft(claim.into()), + }, + NftOperation::Offer(offer) => NftMessage { + nft_transaction_payload: OneOfnft_transaction_payload::offer_nft(offer.into()), + }, + NftOperation::Cancel(cancel) => NftMessage { + nft_transaction_payload: OneOfnft_transaction_payload::cancel_offer_nft( + cancel.into(), + ), + }, } } } @@ -71,7 +81,9 @@ impl From for OfferNftMessage<'_> { OfferNftMessage { receiver: value.receiver.to_hex_literal().into(), creator: value.creator.to_hex_literal().into(), - collectionName: String::from_utf8_lossy(value.collection.as_slice()).to_string().into(), + collectionName: String::from_utf8_lossy(value.collection.as_slice()) + .to_string() + .into(), name: String::from_utf8_lossy(&value.name).to_string().into(), property_version: value.property_version, amount: value.amount, @@ -97,8 +109,12 @@ impl From for CancelOfferNftMessage<'_> { CancelOfferNftMessage { receiver: value.receiver.to_hex_literal().into(), creator: value.creator.to_hex_literal().into(), - collectionName: String::from_utf8_lossy(value.collection.as_slice()).to_string().into(), - name: String::from_utf8_lossy(value.name.as_slice()).to_string().into(), + collectionName: String::from_utf8_lossy(value.collection.as_slice()) + .to_string() + .into(), + name: String::from_utf8_lossy(value.name.as_slice()) + .to_string() + .into(), property_version: value.property_version, } } @@ -121,9 +137,13 @@ impl From for ClaimNftMessage<'_> { ClaimNftMessage { sender: value.sender.to_hex_literal().into(), creator: value.creator.to_hex_literal().into(), - collectionName: String::from_utf8_lossy(value.collection.as_slice()).to_string().into(), - name: String::from_utf8_lossy(value.name.as_slice()).to_string().into(), + collectionName: String::from_utf8_lossy(value.collection.as_slice()) + .to_string() + .into(), + name: String::from_utf8_lossy(value.name.as_slice()) + .to_string() + .into(), property_version: value.property_version, } } -} \ No newline at end of file +} diff --git a/rust/tw_aptos/src/serde_helper/mod.rs b/rust/tw_aptos/src/serde_helper/mod.rs index a35216bebee..876f018225e 100644 --- a/rust/tw_aptos/src/serde_helper/mod.rs +++ b/rust/tw_aptos/src/serde_helper/mod.rs @@ -2,4 +2,4 @@ // Parts of the project are originally copyright © Meta Platforms, Inc. // SPDX-License-Identifier: Apache-2.0 -pub mod vec_bytes; \ No newline at end of file +pub mod vec_bytes; diff --git a/rust/tw_aptos/src/serde_helper/vec_bytes.rs b/rust/tw_aptos/src/serde_helper/vec_bytes.rs index aba6da2feaa..f57311d19dd 100644 --- a/rust/tw_aptos/src/serde_helper/vec_bytes.rs +++ b/rust/tw_aptos/src/serde_helper/vec_bytes.rs @@ -9,8 +9,8 @@ use serde::{ }; pub fn serialize(data: &[Vec], serializer: S) -> Result - where - S: Serializer, +where + S: Serializer, { let mut seq = serializer.serialize_seq(Some(data.len()))?; for e in data { @@ -20,11 +20,11 @@ pub fn serialize(data: &[Vec], serializer: S) -> Result } pub fn deserialize<'de, D>(deserializer: D) -> Result>, D::Error> - where - D: Deserializer<'de>, +where + D: Deserializer<'de>, { Ok(>::deserialize(deserializer)? .into_iter() .map(serde_bytes::ByteBuf::into_vec) .collect()) -} \ No newline at end of file +} diff --git a/rust/tw_aptos/src/signer.rs b/rust/tw_aptos/src/signer.rs index ede021e0d50..66ab31621db 100644 --- a/rust/tw_aptos/src/signer.rs +++ b/rust/tw_aptos/src/signer.rs @@ -4,14 +4,14 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use crate::address::{Address, AptosAddress}; +use crate::transaction_builder; use std::marker::PhantomData; use std::str::FromStr; use tw_coin_entry::error::SigningResult; use tw_coin_entry::signing_output_error; use tw_keypair::ed25519; use tw_proto::Aptos::Proto; -use crate::address::{Address, AptosAddress}; -use crate::transaction_builder; pub trait AptosContext { type Address: AptosAddress; @@ -41,7 +41,11 @@ impl Signer { let key_pair = ed25519::sha512::KeyPair::try_from(input.private_key.as_ref())?; let builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone())?; let sender = Address::from_str(&input.sender)?; - let signed_tx = builder.sender(sender.inner()).sequence_number(input.sequence_number as u64).build().sign(key_pair)?; + let signed_tx = builder + .sender(sender.inner()) + .sequence_number(input.sequence_number as u64) + .build() + .sign(key_pair)?; Ok(Proto::SigningOutput { raw_txn: signed_tx.raw_txn_bytes().clone().into(), encoded: signed_tx.encoded().clone().into(), @@ -50,4 +54,4 @@ impl Signer { ..Proto::SigningOutput::default() }) } -} \ No newline at end of file +} diff --git a/rust/tw_aptos/src/transaction.rs b/rust/tw_aptos/src/transaction.rs index f7773e4a37c..c3a266cc92b 100644 --- a/rust/tw_aptos/src/transaction.rs +++ b/rust/tw_aptos/src/transaction.rs @@ -4,17 +4,17 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use std::borrow::Cow; +use crate::constants::APTOS_SALT; +use crate::transaction_payload::{EntryFunction, TransactionPayload}; use move_core_types::account_address::AccountAddress; use serde::Serialize; use serde_json::{json, Value}; +use std::borrow::Cow; use tw_coin_entry::error::SigningResult; +use tw_encoding::hex::encode; use tw_keypair::ed25519::sha512::KeyPair; use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; -use tw_encoding::hex::encode; use tw_proto::Aptos::Proto; -use crate::constants::APTOS_SALT; -use crate::transaction_payload::{EntryFunction, TransactionPayload}; #[derive(Clone, Serialize)] pub enum TransactionAuthenticator { @@ -22,7 +22,7 @@ pub enum TransactionAuthenticator { Ed25519 { public_key: Vec, signature: Vec, - } + }, } impl From for Proto::TransactionAuthenticator<'_> { @@ -36,20 +36,33 @@ impl From for Proto::TransactionAuthenticator<'_> { impl TransactionAuthenticator { pub fn get_signature(&self) -> Vec { - match self { TransactionAuthenticator::Ed25519 { public_key: _public_key, signature } => { signature.clone() } } + match self { + TransactionAuthenticator::Ed25519 { + public_key: _public_key, + signature, + } => signature.clone(), + } } pub fn get_public_key(&self) -> Vec { - match self { TransactionAuthenticator::Ed25519 { public_key, signature: _signature } => { public_key.clone() } } + match self { + TransactionAuthenticator::Ed25519 { + public_key, + signature: _signature, + } => public_key.clone(), + } } pub fn to_json(&self) -> Value { match self { - TransactionAuthenticator::Ed25519 { public_key, signature } => { + TransactionAuthenticator::Ed25519 { + public_key, + signature, + } => { json!({"public_key": encode(&public_key, true), "signature": encode(&signature, true), "type": "ed25519_signature"}) - } + }, } } } @@ -145,7 +158,11 @@ impl RawTransaction { self.msg_to_sign() } - pub fn compile(&self, signature: Vec, public_key: Vec) -> SigningResult { + pub fn compile( + &self, + signature: Vec, + public_key: Vec, + ) -> SigningResult { let serialized = self.serialize(); let auth = TransactionAuthenticator::Ed25519 { public_key, @@ -161,10 +178,7 @@ impl RawTransaction { }) } - pub fn sign( - self, - key_pair: KeyPair, - ) -> SigningResult { + pub fn sign(self, key_pair: KeyPair) -> SigningResult { let to_sign = self.pre_image(); let signature = key_pair.private().sign(to_sign)?.to_bytes().into_vec(); let pubkey = key_pair.public().as_slice().to_vec(); @@ -221,7 +235,10 @@ impl SignedTransaction { pub fn to_json(&self) -> Value { let mut json_value = self.raw_txn.to_json(); - json_value.as_object_mut().unwrap().insert("signature".to_string(), self.authenticator.to_json()); + json_value + .as_object_mut() + .unwrap() + .insert("signature".to_string(), self.authenticator.to_json()); json_value } -} \ No newline at end of file +} diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index 206d4f208d7..f43455953ed 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -4,19 +4,27 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use std::str::FromStr; +use crate::aptos_move_packages::{ + aptos_account_create_account, aptos_account_transfer, aptos_account_transfer_coins, + coin_transfer, managed_coin_register, token_transfers_cancel_offer_script, + token_transfers_claim_script, token_transfers_offer_script, +}; +use crate::constants::{GAS_UNIT_PRICE, MAX_GAS_AMOUNT}; +use crate::liquid_staking::{ + tortuga_claim, tortuga_stake, tortuga_unstake, LiquidStakingOperation, +}; +use crate::nft::NftOperation; +use crate::transaction::RawTransaction; +use crate::transaction_payload::{ + convert_proto_struct_tag_to_type_tag, EntryFunction, TransactionPayload, +}; use move_core_types::account_address::AccountAddress; use move_core_types::language_storage::TypeTag; use serde_json::Value; +use std::str::FromStr; use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_proto::Aptos::Proto::mod_SigningInput::OneOftransaction_payload; -use crate::constants::{GAS_UNIT_PRICE, MAX_GAS_AMOUNT}; -use crate::transaction::RawTransaction; -use crate::transaction_payload::{convert_proto_struct_tag_to_type_tag, EntryFunction, TransactionPayload}; use tw_proto::Aptos::Proto::SigningInput; -use crate::aptos_move_packages::{aptos_account_create_account, aptos_account_transfer, aptos_account_transfer_coins, coin_transfer, managed_coin_register, token_transfers_cancel_offer_script, token_transfers_claim_script, token_transfers_offer_script}; -use crate::liquid_staking::{LiquidStakingOperation, tortuga_claim, tortuga_stake, tortuga_unstake}; -use crate::nft::NftOperation; pub struct TransactionBuilder { sender: Option, @@ -77,41 +85,49 @@ impl TransactionFactory { .with_max_gas_amount(input.max_gas_amount) .with_transaction_expiration_time(input.expiration_timestamp_secs); match input.transaction_payload { - OneOftransaction_payload::transfer(transfer) => { - Ok(factory.implicitly_create_user_account_and_transfer(AccountAddress::from_str(&transfer.to).unwrap(), transfer.amount)) - } + OneOftransaction_payload::transfer(transfer) => Ok(factory + .implicitly_create_user_account_and_transfer( + AccountAddress::from_str(&transfer.to).unwrap(), + transfer.amount, + )), OneOftransaction_payload::token_transfer(token_transfer) => { let func = token_transfer.function.unwrap(); - Ok(factory.coins_transfer(AccountAddress::from_str(&token_transfer.to).unwrap(), token_transfer.amount, - convert_proto_struct_tag_to_type_tag(func), + Ok(factory.coins_transfer( + AccountAddress::from_str(&token_transfer.to).unwrap(), + token_transfer.amount, + convert_proto_struct_tag_to_type_tag(func), )) - } - OneOftransaction_payload::create_account(create_account) => { - Ok(factory.create_user_account(AccountAddress::from_str(&create_account.auth_key).unwrap())) - } + }, + OneOftransaction_payload::create_account(create_account) => Ok(factory + .create_user_account(AccountAddress::from_str(&create_account.auth_key).unwrap())), OneOftransaction_payload::nft_message(nft_message) => { Ok(factory.nft_ops(nft_message.into())) - } - OneOftransaction_payload::register_token(register_token) => { - Ok(factory.register_token(convert_proto_struct_tag_to_type_tag(register_token.function.unwrap()))) - } + }, + OneOftransaction_payload::register_token(register_token) => Ok(factory.register_token( + convert_proto_struct_tag_to_type_tag(register_token.function.unwrap()), + )), OneOftransaction_payload::liquid_staking_message(msg) => { Ok(factory.liquid_staking_ops(msg.into())) - } + }, OneOftransaction_payload::token_transfer_coins(token_transfer_coins) => { let func = token_transfer_coins.function.unwrap(); - Ok(factory.implicitly_create_user_and_coins_transfer(AccountAddress::from_str(&token_transfer_coins.to).unwrap(), token_transfer_coins.amount, - convert_proto_struct_tag_to_type_tag(func))) - } + Ok(factory.implicitly_create_user_and_coins_transfer( + AccountAddress::from_str(&token_transfer_coins.to).unwrap(), + token_transfer_coins.amount, + convert_proto_struct_tag_to_type_tag(func), + )) + }, OneOftransaction_payload::None => { let is_blind_sign = !input.any_encoded.is_empty(); let v = serde_json::from_str::(&input.any_encoded)?; if is_blind_sign { - Ok(factory.payload(TransactionPayload::EntryFunction(EntryFunction::try_from(v).unwrap()))) + Ok(factory.payload(TransactionPayload::EntryFunction( + EntryFunction::try_from(v).unwrap(), + ))) } else { Err(SigningError(SigningErrorType::Error_input_parse)) } - } + }, } } @@ -144,15 +160,28 @@ impl TransactionFactory { pub fn nft_ops(&self, operation: NftOperation) -> TransactionBuilder { match operation { - NftOperation::Claim(claim) => { - self.payload(token_transfers_claim_script(claim.sender, claim.creator, claim.collection, claim.name, claim.property_version)) - } - NftOperation::Cancel(offer) => { - self.payload(token_transfers_cancel_offer_script(offer.receiver, offer.creator, offer.collection, offer.name, offer.property_version)) - } - NftOperation::Offer(offer) => { - self.payload(token_transfers_offer_script(offer.receiver, offer.creator, offer.collection, offer.name, offer.property_version, offer.amount)) - } + NftOperation::Claim(claim) => self.payload(token_transfers_claim_script( + claim.sender, + claim.creator, + claim.collection, + claim.name, + claim.property_version, + )), + NftOperation::Cancel(offer) => self.payload(token_transfers_cancel_offer_script( + offer.receiver, + offer.creator, + offer.collection, + offer.name, + offer.property_version, + )), + NftOperation::Offer(offer) => self.payload(token_transfers_offer_script( + offer.receiver, + offer.creator, + offer.collection, + offer.name, + offer.property_version, + offer.amount, + )), } } @@ -160,13 +189,14 @@ impl TransactionFactory { match operation { LiquidStakingOperation::Stake(stake) => { self.payload(tortuga_stake(stake.smart_contract_address, stake.amount)) - } - LiquidStakingOperation::Unstake(unstake) => { - self.payload(tortuga_unstake(unstake.smart_contract_address, unstake.amount)) - } + }, + LiquidStakingOperation::Unstake(unstake) => self.payload(tortuga_unstake( + unstake.smart_contract_address, + unstake.amount, + )), LiquidStakingOperation::Claim(claim) => { self.payload(tortuga_claim(claim.smart_contract_address, claim.idx)) - } + }, } } @@ -178,13 +208,21 @@ impl TransactionFactory { self.payload(aptos_account_transfer(to, amount)) } - pub fn coins_transfer(&self, to: AccountAddress, - amount: u64, coin_type: TypeTag) -> TransactionBuilder { + pub fn coins_transfer( + &self, + to: AccountAddress, + amount: u64, + coin_type: TypeTag, + ) -> TransactionBuilder { self.payload(coin_transfer(coin_type, to, amount)) } - pub fn implicitly_create_user_and_coins_transfer(&self, to: AccountAddress, - amount: u64, coin_type: TypeTag) -> TransactionBuilder { + pub fn implicitly_create_user_and_coins_transfer( + &self, + to: AccountAddress, + amount: u64, + coin_type: TypeTag, + ) -> TransactionBuilder { self.payload(aptos_account_transfer_coins(coin_type, to, amount)) } diff --git a/rust/tw_aptos/src/transaction_payload.rs b/rust/tw_aptos/src/transaction_payload.rs index a414e1cc2f5..269ce053c12 100644 --- a/rust/tw_aptos/src/transaction_payload.rs +++ b/rust/tw_aptos/src/transaction_payload.rs @@ -4,18 +4,18 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use std::default::Default; -use std::str::FromStr; +use anyhow::{anyhow, Result}; use move_core_types::identifier::Identifier; use move_core_types::language_storage::{ModuleId, StructTag, TypeTag}; use move_core_types::parser::parse_transaction_argument; use move_core_types::transaction_argument::TransactionArgument; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; -use anyhow::{Result, anyhow}; +use std::default::Default; +use std::str::FromStr; +use crate::serde_helper::vec_bytes; use tw_proto::Aptos; -use crate::{serde_helper::vec_bytes}; #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct EntryFunction { @@ -32,22 +32,32 @@ impl TryFrom for EntryFunction { type Error = anyhow::Error; fn try_from(value: Value) -> Result { - let function_str = value["function"].as_str().ok_or_else(|| anyhow!("Missing function string"))?; + let function_str = value["function"] + .as_str() + .ok_or_else(|| anyhow!("Missing function string"))?; let tag = StructTag::from_str(function_str)?; - let args = value["arguments"].as_array().ok_or_else(|| anyhow!("Arguments field is not an array or is missing"))? + let args = value["arguments"] + .as_array() + .ok_or_else(|| anyhow!("Arguments field is not an array or is missing"))? .iter() .map(|element| { - let arg_str = element.as_str().ok_or_else(|| anyhow!("Invalid argument string"))?; + let arg_str = element + .as_str() + .ok_or_else(|| anyhow!("Invalid argument string"))?; let arg = parse_transaction_argument(arg_str)?; serialize_argument(&arg) }) .collect::>>>()?; - let ty_args = value["type_arguments"].as_array().ok_or_else(|| anyhow!("Type arguments field is not an array or is missing"))? + let ty_args = value["type_arguments"] + .as_array() + .ok_or_else(|| anyhow!("Type arguments field is not an array or is missing"))? .iter() .map(|element| { - let ty_arg_str = element.as_str().ok_or_else(|| anyhow!("Invalid type argument string"))?; + let ty_arg_str = element + .as_str() + .ok_or_else(|| anyhow!("Invalid type argument string"))?; TypeTag::from_str(ty_arg_str) }) .collect::>>()?; @@ -75,12 +85,17 @@ fn serialize_argument(arg: &TransactionArgument) -> Result> { TransactionArgument::Address(v) => { let serialized_v = bcs::to_bytes(v)?; bcs::to_bytes(&serialized_v) - } - }.map_err(|e| anyhow!(e)) + }, + } + .map_err(|e| anyhow!(e)) } pub fn convert_proto_struct_tag_to_type_tag(struct_tag: Aptos::Proto::StructTag) -> TypeTag { - TypeTag::from_str(&format!("{}::{}::{}", struct_tag.account_address, struct_tag.module, struct_tag.name)).unwrap() + TypeTag::from_str(&format!( + "{}::{}::{}", + struct_tag.account_address, struct_tag.module, struct_tag.name + )) + .unwrap() } pub fn convert_type_tag_to_struct_tag(type_tag: TypeTag) -> Aptos::Proto::StructTag<'static> { @@ -99,10 +114,10 @@ impl EntryFunction { fn to_json(&self) -> Value { // Create a JSON array from the `ty_args` field by filtering and mapping // the items that match `TypeTag::Struct` to their string representation. - let type_arguments: Value = self.ty_args.iter() - .filter_map(|item| { - Some(json!(item.to_string())) - }) + let type_arguments: Value = self + .ty_args + .iter() + .filter_map(|item| Some(json!(item.to_string()))) .collect(); // Construct the final JSON value @@ -126,9 +141,9 @@ pub enum TransactionPayload { impl TransactionPayload { pub fn to_json(&self) -> Value { match self { - TransactionPayload::Script => { Value::default() } - TransactionPayload::ModuleBundle => { Value::default() } - TransactionPayload::EntryFunction(entry) => { entry.to_json() } + TransactionPayload::Script => Value::default(), + TransactionPayload::ModuleBundle => Value::default(), + TransactionPayload::EntryFunction(entry) => entry.to_json(), } } } @@ -153,14 +168,14 @@ impl EntryFunction { #[cfg(test)] mod tests { - use std::str::FromStr; use crate::address::Address; + use crate::transaction_payload::{EntryFunction, TransactionPayload}; use move_core_types::account_address::AccountAddress; use move_core_types::identifier::Identifier; use move_core_types::language_storage::{ModuleId, TypeTag}; use serde_json::{json, Value}; + use std::str::FromStr; use tw_encoding::hex; - use crate::transaction_payload::{EntryFunction, TransactionPayload}; #[test] fn test_payload_from_json() { @@ -181,13 +196,25 @@ mod tests { #[test] fn test_basic_payload() { - let addr = Address::from_str("0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b").unwrap().inner(); + let addr = + Address::from_str("0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b") + .unwrap() + .inner(); let amount: i64 = 1000; - let args = vec![bcs::to_bytes(&addr).unwrap(), bcs::to_bytes(&amount).unwrap()]; + let args = vec![ + bcs::to_bytes(&addr).unwrap(), + bcs::to_bytes(&amount).unwrap(), + ]; let module = ModuleId::new(AccountAddress::ONE, Identifier::from_str("coin").unwrap()); let function = Identifier::from_str("transfer").unwrap(); let type_tag = vec![TypeTag::from_str("0x1::aptos_coin::AptosCoin").unwrap()]; - let entry = EntryFunction::new(module, function, type_tag, args, json!([addr.to_hex_literal(), amount.to_string()])); + let entry = EntryFunction::new( + module, + function, + type_tag, + args, + json!([addr.to_hex_literal(), amount.to_string()]), + ); let tp = TransactionPayload::EntryFunction(entry); let serialized = bcs::to_bytes(&tp).unwrap(); assert_eq!(hex::encode(serialized, false), "02000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b08e803000000000000"); @@ -199,4 +226,4 @@ mod tests { }); assert_eq!(tp.to_json(), payload_value); } -} \ No newline at end of file +} diff --git a/rust/tw_aptos/tests/signer.rs b/rust/tw_aptos/tests/signer.rs index 89eb67091d8..ca439240cc1 100644 --- a/rust/tw_aptos/tests/signer.rs +++ b/rust/tw_aptos/tests/signer.rs @@ -4,9 +4,9 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use std::str::FromStr; use move_core_types::account_address::AccountAddress; use move_core_types::language_storage::TypeTag; +use std::str::FromStr; use tw_aptos::liquid_staking; use tw_aptos::liquid_staking::{LiquidStakingOperation, Stake, Unstake}; use tw_aptos::nft::{Claim, NftOperation, Offer}; @@ -17,7 +17,6 @@ use tw_encoding::hex; use tw_proto::Aptos::Proto; use tw_proto::Aptos::Proto::{SigningInput, SigningOutput}; - pub struct AccountCreation { to: String, } @@ -46,90 +45,101 @@ pub enum OpsDetails { NftOps(NftOperation), } -fn setup_proto_transaction<'a>(sender: &'a str, - keypair_str: &'a str, - transaction_type: &'a str, - sequence_number: i64, - chain_id: u32, - max_gas_amount: u64, - timestamp: u64, - gas_unit_price: u64, - any_encoded: &'a str, - ops_details: Option) -> SigningInput<'a> { +fn setup_proto_transaction<'a>( + sender: &'a str, + keypair_str: &'a str, + transaction_type: &'a str, + sequence_number: i64, + chain_id: u32, + max_gas_amount: u64, + timestamp: u64, + gas_unit_price: u64, + any_encoded: &'a str, + ops_details: Option, +) -> SigningInput<'a> { let private = hex::decode(keypair_str).unwrap(); let payload: Proto::mod_SigningInput::OneOftransaction_payload = match transaction_type { "transfer" => { if let OpsDetails::Transfer(transfer) = ops_details.unwrap() { - Proto::mod_SigningInput::OneOftransaction_payload::transfer(Proto::TransferMessage { - to: transfer.to.into(), - amount: transfer.amount, - }) + Proto::mod_SigningInput::OneOftransaction_payload::transfer( + Proto::TransferMessage { + to: transfer.to.into(), + amount: transfer.amount, + }, + ) } else { panic!("Unsupported arguments") } - } + }, "create_account" => { if let OpsDetails::AccountCreation(account) = ops_details.unwrap() { - Proto::mod_SigningInput::OneOftransaction_payload::create_account(Proto::CreateAccountMessage { - auth_key: account.to.into(), - }) + Proto::mod_SigningInput::OneOftransaction_payload::create_account( + Proto::CreateAccountMessage { + auth_key: account.to.into(), + }, + ) } else { panic!("Unsupported arguments") } - } + }, "coin_transfer" => { if let OpsDetails::TokenTransfer(token_transfer) = ops_details.unwrap() { - Proto::mod_SigningInput::OneOftransaction_payload::token_transfer(Proto::TokenTransferMessage { - to: token_transfer.transfer.to.into(), - amount: token_transfer.transfer.amount, - function: Some(convert_type_tag_to_struct_tag(token_transfer.tag)), - }) + Proto::mod_SigningInput::OneOftransaction_payload::token_transfer( + Proto::TokenTransferMessage { + to: token_transfer.transfer.to.into(), + amount: token_transfer.transfer.amount, + function: Some(convert_type_tag_to_struct_tag(token_transfer.tag)), + }, + ) } else { panic!("Unsupported arguments") } - } + }, "implicit_coin_transfer" => { if let OpsDetails::ImplicitTokenTransfer(token_transfer) = ops_details.unwrap() { - Proto::mod_SigningInput::OneOftransaction_payload::token_transfer_coins(Proto::TokenTransferCoinsMessage { - to: token_transfer.transfer.to.into(), - amount: token_transfer.transfer.amount, - function: Some(convert_type_tag_to_struct_tag(token_transfer.tag)), - }) + Proto::mod_SigningInput::OneOftransaction_payload::token_transfer_coins( + Proto::TokenTransferCoinsMessage { + to: token_transfer.transfer.to.into(), + amount: token_transfer.transfer.amount, + function: Some(convert_type_tag_to_struct_tag(token_transfer.tag)), + }, + ) } else { panic!("Unsupported arguments") } - } + }, "nft_ops" => { if let OpsDetails::NftOps(nft) = ops_details.unwrap() { Proto::mod_SigningInput::OneOftransaction_payload::nft_message(nft.into()) } else { panic!("Unsupported arguments") } - } + }, "register_token" => { if let OpsDetails::RegisterToken(register_token) = ops_details.unwrap() { - Proto::mod_SigningInput::OneOftransaction_payload::register_token(Proto::ManagedTokensRegisterMessage { - function: Some(convert_type_tag_to_struct_tag(register_token.coin_type)), - }) + Proto::mod_SigningInput::OneOftransaction_payload::register_token( + Proto::ManagedTokensRegisterMessage { + function: Some(convert_type_tag_to_struct_tag(register_token.coin_type)), + }, + ) } else { panic!("Unsupported arguments") } - } + }, "liquid_staking_ops" => { if let OpsDetails::LiquidStakingOps(liquid_staking_ops) = ops_details.unwrap() { - Proto::mod_SigningInput::OneOftransaction_payload::liquid_staking_message(liquid_staking_ops.into()) + Proto::mod_SigningInput::OneOftransaction_payload::liquid_staking_message( + liquid_staking_ops.into(), + ) } else { panic!("Unsupported arguments") } - } - "blind_sign_json" => { - Proto::mod_SigningInput::OneOftransaction_payload::None - } - _ => { Proto::mod_SigningInput::OneOftransaction_payload::None } + }, + "blind_sign_json" => Proto::mod_SigningInput::OneOftransaction_payload::None, + _ => Proto::mod_SigningInput::OneOftransaction_payload::None, }; - let input = SigningInput { chain_id, sender: sender.into(), @@ -150,14 +160,23 @@ fn test_tx_result( expected_raw_txn_bytes_str: &str, expected_signature_str: &str, expected_encoded_txn_str: &str, - json_literal: &str) { + json_literal: &str, +) { assert_eq!(output.error, SigningErrorType::OK); assert!(output.error_message.is_empty()); - - assert_eq!(hex::encode(output.raw_txn.to_vec(), false), expected_raw_txn_bytes_str); - assert_eq!(hex::encode(output.authenticator.unwrap().signature.to_vec(), false), expected_signature_str); - assert_eq!(hex::encode(output.encoded.to_vec(), false), expected_encoded_txn_str); + assert_eq!( + hex::encode(output.raw_txn.to_vec(), false), + expected_raw_txn_bytes_str + ); + assert_eq!( + hex::encode(output.authenticator.unwrap().signature.to_vec(), false), + expected_signature_str + ); + assert_eq!( + hex::encode(output.encoded.to_vec(), false), + expected_encoded_txn_str + ); let json_value_expected: serde_json::Value = serde_json::from_str(json_literal).unwrap(); let json_value: serde_json::Value = serde_json::from_str(output.json.as_ref()).unwrap(); @@ -167,19 +186,21 @@ fn test_tx_result( // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb4d62afd3862116e060dd6ad9848ccb50c2bc177799819f1d29c059ae2042467?network=devnet #[test] fn test_aptos_sign_transaction_transfer() { - let input = setup_proto_transaction("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", - "transfer", - 99, - 33, - 3296766, - 3664390082, - 100, - "", - Some(OpsDetails::Transfer(Transfer { - to: "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30".to_string(), - amount: 1000, - }))); + let input = setup_proto_transaction( + "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", + "transfer", + 99, + 33, + 3296766, + 3664390082, + 100, + "", + Some(OpsDetails::Transfer(Transfer { + to: "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30".to_string(), + amount: 1000, + })), + ); let output = Signer::::sign_proto(input); test_tx_result(output, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021", @@ -208,18 +229,20 @@ fn test_aptos_sign_transaction_transfer() { // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x477141736de6b0936a6c3734e4d6fd018c7d21f1f28f99028ef0bc6881168602?network=Devnet #[test] fn test_aptos_sign_create_account() { - let input = setup_proto_transaction("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address - "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair - "create_account", - 0, // Sequence number - 33, - 3296766, - 3664390082, - 100, - "", - Some(OpsDetails::AccountCreation(AccountCreation { - to: "0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e".to_string(), - }))); + let input = setup_proto_transaction( + "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "create_account", + 0, // Sequence number + 33, + 3296766, + 3664390082, + 100, + "", + Some(OpsDetails::AccountCreation(AccountCreation { + to: "0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e".to_string(), + })), + ); let output = Signer::::sign_proto(input); test_tx_result(output, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada0000000021", // Expected raw transaction bytes @@ -248,16 +271,27 @@ fn test_aptos_sign_create_account() { // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb5b383a5c7f99b2edb3bed9533f8169a89051b149d65876a82f4c0b9bf78a15b?network=Devnet #[test] fn test_aptos_sign_coin_transfer() { - let input = setup_proto_transaction("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address - "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair - "coin_transfer", - 24, // Sequence number - 32, - 3296766, - 3664390082, - 100, - "", - Some(OpsDetails::TokenTransfer(TokenTransfer { transfer: Transfer { to: "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30".to_string(), amount: 100000 }, tag: TypeTag::from_str("0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC").unwrap() })), + let input = setup_proto_transaction( + "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "coin_transfer", + 24, // Sequence number + 32, + 3296766, + 3664390082, + 100, + "", + Some(OpsDetails::TokenTransfer(TokenTransfer { + transfer: Transfer { + to: "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30" + .to_string(), + amount: 100000, + }, + tag: TypeTag::from_str( + "0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC", + ) + .unwrap(), + })), ); let output = Signer::::sign_proto(input); test_tx_result(output, @@ -326,23 +360,30 @@ fn test_implicit_aptos_sign_coin_transfer() { // Successfully broadcasted https://explorer.aptoslabs.com/txn/0x514e473618bd3cb89a2b110b7c473db9a2e10532f98eb42d02d86fb31c00525d?network=testnet #[test] fn test_aptos_nft_offer() { - let input = setup_proto_transaction("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", // Sender's address - "7bebb6d543d17f6fe4e685cfab239fa37896edd594ff859f1df32f244fb707e2", // Keypair - "nft_ops", - 1, // Sequence number - 2, - 3296766, - 3664390082, - 100, - "", - Some(OpsDetails::NftOps(NftOperation::Offer(Offer { - receiver: AccountAddress::from_str("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30").unwrap(), - creator: AccountAddress::from_str("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac").unwrap(), - collection: "Topaz Troopers".as_bytes().to_vec(), - name: "Topaz Trooper #20068".as_bytes().to_vec(), - property_version: 0, - amount: 1, - }))), + let input = setup_proto_transaction( + "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", // Sender's address + "7bebb6d543d17f6fe4e685cfab239fa37896edd594ff859f1df32f244fb707e2", // Keypair + "nft_ops", + 1, // Sequence number + 2, + 3296766, + 3664390082, + 100, + "", + Some(OpsDetails::NftOps(NftOperation::Offer(Offer { + receiver: AccountAddress::from_str( + "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + ) + .unwrap(), + creator: AccountAddress::from_str( + "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", + ) + .unwrap(), + collection: "Topaz Troopers".as_bytes().to_vec(), + name: "Topaz Trooper #20068".as_bytes().to_vec(), + property_version: 0, + amount: 1, + }))), ); let output = Signer::::sign_proto(input); test_tx_result(output, @@ -375,23 +416,30 @@ fn test_aptos_nft_offer() { // Successfully broadcasted https://explorer.aptoslabs.com/txn/0x0b8c64e6847c368e4c6bd2cce0e9eab378971b0ef2e3bc40cbd292910a80201d?network=testnet #[test] fn test_aptos_cancel_nft_offer() { - let input = setup_proto_transaction("0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address - "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair - "nft_ops", - 21, // Sequence number - 2, - 3296766, - 3664390082, - 100, - "", - Some(OpsDetails::NftOps(NftOperation::Cancel(Offer { - receiver: AccountAddress::from_str("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee").unwrap(), - creator: AccountAddress::from_str("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac").unwrap(), - collection: "Topaz Troopers".as_bytes().to_vec(), - name: "Topaz Trooper #20068".as_bytes().to_vec(), - property_version: 0, - amount: 0, - }))), + let input = setup_proto_transaction( + "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "nft_ops", + 21, // Sequence number + 2, + 3296766, + 3664390082, + 100, + "", + Some(OpsDetails::NftOps(NftOperation::Cancel(Offer { + receiver: AccountAddress::from_str( + "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", + ) + .unwrap(), + creator: AccountAddress::from_str( + "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", + ) + .unwrap(), + collection: "Topaz Troopers".as_bytes().to_vec(), + name: "Topaz Trooper #20068".as_bytes().to_vec(), + property_version: 0, + amount: 0, + }))), ); let output = Signer::::sign_proto(input); test_tx_result(output, @@ -424,22 +472,29 @@ fn test_aptos_cancel_nft_offer() { // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x60b51e15140ec0b7650334e948fb447ce3cb13ae63492260461ebfa9d02e85c4?network=testnet #[test] fn test_aptos_nft_claim() { - let input = setup_proto_transaction("0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address - "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair - "nft_ops", - 19, // Sequence number - 2, - 3296766, - 3664390082, - 100, - "", - Some(OpsDetails::NftOps(NftOperation::Claim(Claim { - sender: AccountAddress::from_str("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee").unwrap(), - creator: AccountAddress::from_str("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac").unwrap(), - collection: "Topaz Troopers".as_bytes().to_vec(), - name: "Topaz Trooper #20068".as_bytes().to_vec(), - property_version: 0, - }))), + let input = setup_proto_transaction( + "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "nft_ops", + 19, // Sequence number + 2, + 3296766, + 3664390082, + 100, + "", + Some(OpsDetails::NftOps(NftOperation::Claim(Claim { + sender: AccountAddress::from_str( + "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", + ) + .unwrap(), + creator: AccountAddress::from_str( + "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", + ) + .unwrap(), + collection: "Topaz Troopers".as_bytes().to_vec(), + name: "Topaz Trooper #20068".as_bytes().to_vec(), + property_version: 0, + }))), ); let output = Signer::::sign_proto(input); test_tx_result(output, @@ -511,19 +566,25 @@ fn test_aptos_register_token() { // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x25dca849cb4ebacbff223139f7ad5d24c37c225d9506b8b12a925de70429e685/userTxnOverview?network=mainnet #[test] fn test_aptos_tortuga_stake() { - let input = setup_proto_transaction("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address - "786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05", // Keypair - "liquid_staking_ops", - 19, // Sequence number - 1, - 5554, - 1670240203, - 100, - "", - Some(OpsDetails::LiquidStakingOps(LiquidStakingOperation::Stake(Stake { - amount: 100000000, - smart_contract_address: AccountAddress::from_str("0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f").unwrap(), - }))), + let input = setup_proto_transaction( + "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address + "786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05", // Keypair + "liquid_staking_ops", + 19, // Sequence number + 1, + 5554, + 1670240203, + 100, + "", + Some(OpsDetails::LiquidStakingOps(LiquidStakingOperation::Stake( + Stake { + amount: 100000000, + smart_contract_address: AccountAddress::from_str( + "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f", + ) + .unwrap(), + }, + ))), ); let output = Signer::::sign_proto(input); test_tx_result(output, @@ -555,19 +616,25 @@ fn test_aptos_tortuga_stake() { // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x92edb4f756fe86118e34a0e64746c70260ee02c2ae2cf402b3e39f6a282ce968?network=mainnet #[test] fn test_aptos_tortuga_unstake() { - let input = setup_proto_transaction("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address - "786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05", // Keypair - "liquid_staking_ops", - 20, // Sequence number - 1, - 2371, - 1670304949, - 120, - "", - Some(OpsDetails::LiquidStakingOps(LiquidStakingOperation::Unstake(Unstake { - amount: 99178100, - smart_contract_address: AccountAddress::from_str("0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f").unwrap(), - }))), + let input = setup_proto_transaction( + "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address + "786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05", // Keypair + "liquid_staking_ops", + 20, // Sequence number + 1, + 2371, + 1670304949, + 120, + "", + Some(OpsDetails::LiquidStakingOps( + LiquidStakingOperation::Unstake(Unstake { + amount: 99178100, + smart_contract_address: AccountAddress::from_str( + "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f", + ) + .unwrap(), + }), + )), ); let output = Signer::::sign_proto(input); test_tx_result(output, @@ -599,19 +666,25 @@ fn test_aptos_tortuga_unstake() { // // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x9fc874de7a7d3e813d9a1658d896023de270a0096a5e258c196005656ace7d54?network=mainnet #[test] fn test_aptos_tortuga_claim() { - let input = setup_proto_transaction("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address - "786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05", // Keypair - "liquid_staking_ops", - 28, // Sequence number - 1, - 10, - 1682066783, - 148, - "", - Some(OpsDetails::LiquidStakingOps(LiquidStakingOperation::Claim(liquid_staking::Claim { - idx: 0, - smart_contract_address: AccountAddress::from_str("0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f").unwrap(), - }))), + let input = setup_proto_transaction( + "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address + "786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05", // Keypair + "liquid_staking_ops", + 28, // Sequence number + 1, + 10, + 1682066783, + 148, + "", + Some(OpsDetails::LiquidStakingOps(LiquidStakingOperation::Claim( + liquid_staking::Claim { + idx: 0, + smart_contract_address: AccountAddress::from_str( + "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f", + ) + .unwrap(), + }, + ))), ); let output = Signer::::sign_proto(input); test_tx_result(output, @@ -643,15 +716,16 @@ fn test_aptos_tortuga_claim() { // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x7efd69e7f9462774b932ce500ab51c0d0dcc004cf272e09f8ffd5804c2a84e33?network=mainnet #[test] fn test_aptos_blind_sign() { - let input = setup_proto_transaction("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address - "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair - "blind_sign_json", - 42, // Sequence number - 1, - 100011, - 3664390082, - 100, - r#"{ + let input = setup_proto_transaction( + "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "blind_sign_json", + 42, // Sequence number + 1, + 100011, + 3664390082, + 100, + r#"{ "function": "0x16fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c::AnimeSwapPoolV1::swap_exact_coins_for_coins_3_pair_entry", "type_arguments": [ "0x1::aptos_coin::AptosCoin", @@ -665,7 +739,7 @@ fn test_aptos_blind_sign() { ], "type": "entry_function_payload" }"#, - None, + None, ); let output = Signer::::sign_proto(input); test_tx_result(output, @@ -703,15 +777,16 @@ fn test_aptos_blind_sign() { // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x25dca849cb4ebacbff223139f7ad5d24c37c225d9506b8b12a925de70429e685/payload #[test] fn test_aptos_blind_sign_staking() { - let input = setup_proto_transaction("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address - "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair - "blind_sign_json", - 43, // Sequence number - 1, - 100011, - 3664390082, - 100, - r#"{ + let input = setup_proto_transaction( + "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "blind_sign_json", + 43, // Sequence number + 1, + 100011, + 3664390082, + 100, + r#"{ "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::stake", "type_arguments": [], "arguments": [ @@ -719,7 +794,7 @@ fn test_aptos_blind_sign_staking() { ], "type": "entry_function_payload" }"#, - None, + None, ); let output = Signer::::sign_proto(input); test_tx_result(output, @@ -751,15 +826,16 @@ fn test_aptos_blind_sign_staking() { // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x92edb4f756fe86118e34a0e64746c70260ee02c2ae2cf402b3e39f6a282ce968/payload #[test] fn test_aptos_blind_sign_unstaking() { - let input = setup_proto_transaction("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address - "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair - "blind_sign_json", - 44, // Sequence number - 1, - 100011, - 3664390082, - 100, - r#"{ + let input = setup_proto_transaction( + "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "blind_sign_json", + 44, // Sequence number + 1, + 100011, + 3664390082, + 100, + r#"{ "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::unstake", "type_arguments": [], "arguments": [ @@ -767,7 +843,7 @@ fn test_aptos_blind_sign_unstaking() { ], "type": "entry_function_payload" }"#, - None, + None, ); let output = Signer::::sign_proto(input); test_tx_result(output, @@ -794,4 +870,4 @@ fn test_aptos_blind_sign_unstaking() { "type": "ed25519_signature" } }"#); -} \ No newline at end of file +} diff --git a/rust/tw_coin_entry/src/error.rs b/rust/tw_coin_entry/src/error.rs index 3f700b9708a..0a2d8480676 100644 --- a/rust/tw_coin_entry/src/error.rs +++ b/rust/tw_coin_entry/src/error.rs @@ -4,9 +4,9 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use serde_json::Error; use std::fmt; use std::fmt::Formatter; -use serde_json::Error; use tw_keypair::KeyPairError; use tw_number::NumberError; use tw_proto::Common::Proto; diff --git a/rust/tw_keypair/src/tw/public.rs b/rust/tw_keypair/src/tw/public.rs index 46cb0e4f7e9..8b527050944 100644 --- a/rust/tw_keypair/src/tw/public.rs +++ b/rust/tw_keypair/src/tw/public.rs @@ -138,9 +138,7 @@ impl PublicKey { pub fn to_ed25519(&self) -> Option<&ed25519::sha512::PublicKey> { match self { - PublicKey::Ed25519(ed25519) => { - Some(ed25519) - }, + PublicKey::Ed25519(ed25519) => Some(ed25519), _ => None, } } From 72c3cd2b4e9ca9d802afcc0273c32ff7bd66fe29 Mon Sep 17 00:00:00 2001 From: Milerius Date: Mon, 13 Nov 2023 12:07:48 -0500 Subject: [PATCH 48/60] feat(aptos): fix cargo clippy --- rust/tw_aptos/src/address.rs | 4 +--- rust/tw_aptos/src/transaction.rs | 6 +++--- rust/tw_aptos/src/transaction_payload.rs | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/rust/tw_aptos/src/address.rs b/rust/tw_aptos/src/address.rs index 85a9733c00f..b7a4900bfda 100644 --- a/rust/tw_aptos/src/address.rs +++ b/rust/tw_aptos/src/address.rs @@ -90,9 +90,7 @@ impl FromStr for Address { working = &working[2..]; } - if working.len() > NUM_CHARS { - return Err(AddressError::InvalidInput); - } else if !has_0x && working.len() < NUM_CHARS { + if working.len() > NUM_CHARS || (!has_0x && working.len() < NUM_CHARS) { return Err(AddressError::InvalidInput); } diff --git a/rust/tw_aptos/src/transaction.rs b/rust/tw_aptos/src/transaction.rs index c3a266cc92b..6b1e7422a98 100644 --- a/rust/tw_aptos/src/transaction.rs +++ b/rust/tw_aptos/src/transaction.rs @@ -59,8 +59,8 @@ impl TransactionAuthenticator { public_key, signature, } => { - json!({"public_key": encode(&public_key, true), - "signature": encode(&signature, true), + json!({"public_key": encode(public_key, true), + "signature": encode(signature, true), "type": "ed25519_signature"}) }, } @@ -172,7 +172,7 @@ impl RawTransaction { encoded.extend_from_slice(bcs::to_bytes(&auth).unwrap().as_slice()); Ok(SignedTransaction { raw_txn: self.clone(), - authenticator: auth.clone(), + authenticator: auth, raw_txn_bytes: serialized.to_vec(), encoded, }) diff --git a/rust/tw_aptos/src/transaction_payload.rs b/rust/tw_aptos/src/transaction_payload.rs index 269ce053c12..fdb43314563 100644 --- a/rust/tw_aptos/src/transaction_payload.rs +++ b/rust/tw_aptos/src/transaction_payload.rs @@ -117,7 +117,7 @@ impl EntryFunction { let type_arguments: Value = self .ty_args .iter() - .filter_map(|item| Some(json!(item.to_string()))) + .map(|item| Some(json!(item.to_string()))) .collect(); // Construct the final JSON value From 2b60dda2de2c8fb832906925b9cb40f579e6b806 Mon Sep 17 00:00:00 2001 From: Milerius Date: Mon, 13 Nov 2023 12:11:49 -0500 Subject: [PATCH 49/60] feat(aptos): update unit test end user app --- .../core/app/blockchains/CoinAddressDerivationTests.kt | 2 +- swift/Tests/CoinAddressDerivationTests.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt index 135547a4b09..0b0b62d0834 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt @@ -119,7 +119,7 @@ class CoinAddressDerivationTests { NERVOS -> assertEquals("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdtyq04tvp02wectaumxn0664yw2jd53lqk4mxg3", address) EVERSCALE -> assertEquals("0:0c39661089f86ec5926ea7d4ee4223d634ba4ed6dcc2e80c7b6a8e6d59f79b04", address) TON -> assertEquals("EQDgEMqToTacHic7SnvnPFmvceG5auFkCcAw0mSCvzvKUfk9", address) - APTOS -> assertEquals("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", address) + APTOS -> assertEquals("0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", address) NEBL -> assertEquals("NgDVaXAwNgBwb88xLiFKomfBmPkEh9F2d7", address) SUI -> assertEquals("0xada112cfb90b44ba889cc5d39ac2bf46281e4a91f7919c693bcd9b8323e81ed2", address) HEDERA -> assertEquals("0.0.302a300506032b657003210049eba62f64d0d941045595d9433e65d84ecc46bcdb1421de55e05fcf2d8357d5", address) diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 468aa0c5fbf..df0a6c09048 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -306,7 +306,7 @@ class CoinAddressDerivationTests: XCTestCase { let expectedResult = "EQDgEMqToTacHic7SnvnPFmvceG5auFkCcAw0mSCvzvKUfk9"; assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .aptos: - let expectedResult = "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"; + let expectedResult = "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"; assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .nebl: let expectedResult = "NgDVaXAwNgBwb88xLiFKomfBmPkEh9F2d7"; From c1729df4f13b74c5156baab5802897c6df127ddf Mon Sep 17 00:00:00 2001 From: Milerius Date: Mon, 13 Nov 2023 17:56:11 -0500 Subject: [PATCH 50/60] feat(aptos): update unit test end user app --- .../com/trustwallet/core/test/CoinAddressDerivationTests.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt index 351e7d0aa8e..1cbfd41590e 100644 --- a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt +++ b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt @@ -115,7 +115,7 @@ class CoinAddressDerivationTests { Nervos -> "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdtyq04tvp02wectaumxn0664yw2jd53lqk4mxg3" Everscale -> "0:0c39661089f86ec5926ea7d4ee4223d634ba4ed6dcc2e80c7b6a8e6d59f79b04" TON -> "EQDgEMqToTacHic7SnvnPFmvceG5auFkCcAw0mSCvzvKUfk9" - Aptos -> "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30" + Aptos -> "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30" Nebl -> "NgDVaXAwNgBwb88xLiFKomfBmPkEh9F2d7" Sui -> "0xada112cfb90b44ba889cc5d39ac2bf46281e4a91f7919c693bcd9b8323e81ed2" Hedera -> "0.0.302a300506032b657003210049eba62f64d0d941045595d9433e65d84ecc46bcdb1421de55e05fcf2d8357d5" From abc784f7bd07c1f7974e84203b472d3b490a585b Mon Sep 17 00:00:00 2001 From: Milerius Date: Mon, 13 Nov 2023 18:15:28 -0500 Subject: [PATCH 51/60] feat(aptos): delete c++ aptos implementation --- rust/Cargo.lock | 28 +- rust/Cargo.toml | 1 - rust/tw_move_parser/Cargo.toml | 10 - rust/tw_move_parser/src/ffi.rs | 88 --- rust/tw_move_parser/src/lib.rs | 7 - .../tests/move_parser_ffi_tests.rs | 49 -- rust/wallet_core_rs/Cargo.toml | 1 - rust/wallet_core_rs/src/lib.rs | 2 +- src/Aptos/Address.cpp | 30 - src/Aptos/Address.h | 43 -- src/Aptos/Entry.cpp | 3 - src/Aptos/MoveTypes.cpp | 146 ---- src/Aptos/MoveTypes.h | 107 --- src/Aptos/Signer.cpp | 280 ------- src/Aptos/Signer.h | 32 - src/Aptos/TransactionBuilder.h | 195 ----- src/Aptos/TransactionPayload.cpp | 124 --- src/Aptos/TransactionPayload.h | 47 -- src/BCS.cpp | 42 - src/BCS.h | 222 ------ tests/chains/Aptos/AddressTests.cpp | 53 +- tests/chains/Aptos/CompilerTests.cpp | 2 +- tests/chains/Aptos/MoveTypesTests.cpp | 59 -- tests/chains/Aptos/SignerTests.cpp | 730 ------------------ tests/chains/Aptos/TWAnySignerTests.cpp | 3 +- .../chains/Aptos/TransactionPayloadTests.cpp | 60 -- tests/common/BCSTests.cpp | 165 ---- .../common/rust/bindgen/WalletCoreRsTests.cpp | 9 - 28 files changed, 25 insertions(+), 2513 deletions(-) delete mode 100644 rust/tw_move_parser/Cargo.toml delete mode 100644 rust/tw_move_parser/src/ffi.rs delete mode 100644 rust/tw_move_parser/src/lib.rs delete mode 100644 rust/tw_move_parser/tests/move_parser_ffi_tests.rs delete mode 100644 src/Aptos/Address.cpp delete mode 100644 src/Aptos/Address.h delete mode 100644 src/Aptos/MoveTypes.cpp delete mode 100644 src/Aptos/MoveTypes.h delete mode 100644 src/Aptos/Signer.cpp delete mode 100644 src/Aptos/Signer.h delete mode 100644 src/Aptos/TransactionBuilder.h delete mode 100644 src/Aptos/TransactionPayload.cpp delete mode 100644 src/Aptos/TransactionPayload.h delete mode 100644 src/BCS.cpp delete mode 100644 src/BCS.h delete mode 100644 tests/chains/Aptos/MoveTypesTests.cpp delete mode 100644 tests/chains/Aptos/SignerTests.cpp delete mode 100644 tests/chains/Aptos/TransactionPayloadTests.cpp delete mode 100644 tests/common/BCSTests.cpp diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 3025d4b61bb..6e26a83ba99 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -848,21 +848,6 @@ dependencies = [ "uint", ] -[[package]] -name = "move-core-types" -version = "0.0.4" -source = "git+https://github.com/move-language/move?rev=f7137eabc2046f76fdad3ded2c51e03a3b1fbd01#f7137eabc2046f76fdad3ded2c51e03a3b1fbd01" -dependencies = [ - "anyhow", - "bcs", - "hex", - "once_cell", - "rand", - "ref-cast", - "serde", - "serde_bytes", -] - [[package]] name = "nom" version = "7.1.3" @@ -1626,7 +1611,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bcs", - "move-core-types 0.0.4 (git+https://github.com/move-language/move?rev=ea70797099baea64f05194a918cebd69ed02b285)", + "move-core-types", "serde", "serde_bytes", "serde_json", @@ -1808,16 +1793,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "tw_move_parser" -version = "0.1.0" -dependencies = [ - "bcs", - "hex", - "move-core-types 0.0.4 (git+https://github.com/move-language/move?rev=f7137eabc2046f76fdad3ded2c51e03a3b1fbd01)", - "tw_memory", -] - [[package]] name = "tw_number" version = "0.1.0" @@ -1939,7 +1914,6 @@ dependencies = [ "tw_keypair", "tw_memory", "tw_misc", - "tw_move_parser", "tw_number", "tw_proto", ] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index b1546b6055a..872986a6a6a 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -13,7 +13,6 @@ members = [ "tw_keypair", "tw_memory", "tw_misc", - "tw_move_parser", "tw_number", "tw_proto", "tw_ronin", diff --git a/rust/tw_move_parser/Cargo.toml b/rust/tw_move_parser/Cargo.toml deleted file mode 100644 index b674b9a8908..00000000000 --- a/rust/tw_move_parser/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "tw_move_parser" -version = "0.1.0" -edition = "2021" - -[dependencies] -bcs = "0.1.4" -hex = "0.4.3" -move-core-types = { git = "https://github.com/move-language/move", rev = "f7137eabc2046f76fdad3ded2c51e03a3b1fbd01", features = ["address32"] } -tw_memory = { path = "../tw_memory" } diff --git a/rust/tw_move_parser/src/ffi.rs b/rust/tw_move_parser/src/ffi.rs deleted file mode 100644 index 7552e37f457..00000000000 --- a/rust/tw_move_parser/src/ffi.rs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#![allow(clippy::missing_safety_doc)] - -use move_core_types::language_storage::TypeTag; -use move_core_types::transaction_argument::TransactionArgument; -use move_core_types::*; -use std::{ffi::c_char, ffi::CStr}; -use tw_memory::ffi::c_result::{CStrResult, ErrorCode}; - -#[repr(C)] -pub enum CMoveParserCode { - Ok = 0, - InvalidInput = 1, - ErrorParsing = 2, -} - -impl From for ErrorCode { - fn from(code: CMoveParserCode) -> Self { - code as ErrorCode - } -} - -#[repr(C)] -#[derive(PartialEq, Debug)] -pub enum ETypeTag { - Bool = 1, - U8 = 2, - U64 = 3, - U128 = 4, - Address = 5, - Signer = 6, - Vector = 7, - Struct = 8, - Error = 9, -} - -/// Parses a Move type tag. -/// \param input *non-null* C-compatible, nul-terminated string. -/// \return `ETypeTag` enumeration. -#[no_mangle] -pub unsafe extern "C" fn parse_type_tag(input: *const c_char) -> ETypeTag { - let s = CStr::from_ptr(input).to_str().unwrap(); - let transaction_argument = match parser::parse_type_tag(s) { - Ok(v) => v, - Err(_) => return ETypeTag::Error, - }; - match transaction_argument { - TypeTag::Bool => ETypeTag::Bool, - TypeTag::U8 => ETypeTag::U8, - TypeTag::U64 => ETypeTag::U64, - TypeTag::U128 => ETypeTag::U128, - TypeTag::Address => ETypeTag::Address, - TypeTag::Signer => ETypeTag::Signer, - TypeTag::Vector(_) => ETypeTag::Vector, - TypeTag::Struct(_) => ETypeTag::Struct, - } -} - -/// Parses `input` as a Move function argument. -/// \param input *non-null* C-compatible, nul-terminated string. -/// \return *non-null* C-compatible, nul-terminated string, Binary Canonical Serialization (BCS). -#[no_mangle] -pub unsafe extern "C" fn parse_function_argument_to_bcs(input: *const c_char) -> CStrResult { - let s = match CStr::from_ptr(input).to_str() { - Ok(input) => input, - Err(_) => return CStrResult::error(CMoveParserCode::InvalidInput), - }; - let transaction_argument = match parser::parse_transaction_argument(s) { - Ok(v) => v, - Err(_) => return CStrResult::error(CMoveParserCode::ErrorParsing), - }; - let v = match transaction_argument { - TransactionArgument::U8(v) => hex::encode(bcs::to_bytes(&v).unwrap()), - TransactionArgument::U64(v) => hex::encode(bcs::to_bytes(&v).unwrap()), - TransactionArgument::U128(v) => hex::encode(bcs::to_bytes(&v).unwrap()), - TransactionArgument::Address(v) => { - hex::encode(bcs::to_bytes(&bcs::to_bytes(&v).unwrap()).unwrap()) - }, - TransactionArgument::U8Vector(v) => hex::encode(bcs::to_bytes(&v).unwrap()), - TransactionArgument::Bool(v) => hex::encode(bcs::to_bytes(&v).unwrap()), - }; - CStrResult::ok(tw_memory::c_string_standalone(v)) -} diff --git a/rust/tw_move_parser/src/lib.rs b/rust/tw_move_parser/src/lib.rs deleted file mode 100644 index c9f1c100548..00000000000 --- a/rust/tw_move_parser/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -pub mod ffi; diff --git a/rust/tw_move_parser/tests/move_parser_ffi_tests.rs b/rust/tw_move_parser/tests/move_parser_ffi_tests.rs deleted file mode 100644 index 3cb0d528f5e..00000000000 --- a/rust/tw_move_parser/tests/move_parser_ffi_tests.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -use std::ffi::{c_char, CString}; -use tw_move_parser::ffi::{parse_function_argument_to_bcs, parse_type_tag, ETypeTag}; - -#[test] -fn tests_type_tag() { - let tag = unsafe { parse_type_tag("0x1::aptos_coin::AptosCoin\0".as_ptr() as *const c_char) }; - assert_eq!(tag, ETypeTag::Struct); -} - -#[test] -fn tests_function_argument_to_bcs() { - let str = unsafe { - let input = "10000000\0".as_ptr() as *const c_char; - CString::from_raw(parse_function_argument_to_bcs(input).unwrap() as *mut c_char) - .into_string() - .unwrap() - }; - assert_eq!(str, "8096980000000000"); - - let str = unsafe { - let input = "5047445908\0".as_ptr() as *const c_char; - CString::from_raw(parse_function_argument_to_bcs(input).unwrap() as *mut c_char) - .into_string() - .unwrap() - }; - assert_eq!(str, "94e9d92c01000000"); -} - -#[test] -fn tests_function_argument_to_bcs_another() { - let str = unsafe { - let input = "0xc95db29a67a848940829b3df6119b5e67b788ff0248676e4484c7c6f29c0f5e6\0".as_ptr() - as *const c_char; - CString::from_raw(parse_function_argument_to_bcs(input).unwrap() as *mut c_char) - .into_string() - .unwrap() - }; - let decoded = hex::decode(str).unwrap(); - let v = vec![decoded]; - let actual = hex::encode(bcs::to_bytes(&v).unwrap()); - let expected = "012120c95db29a67a848940829b3df6119b5e67b788ff0248676e4484c7c6f29c0f5e6"; - assert_eq!(actual, expected); -} diff --git a/rust/wallet_core_rs/Cargo.toml b/rust/wallet_core_rs/Cargo.toml index 10de66fc423..37b8c878d1d 100644 --- a/rust/wallet_core_rs/Cargo.toml +++ b/rust/wallet_core_rs/Cargo.toml @@ -24,7 +24,6 @@ tw_ethereum = { path = "../tw_ethereum" } tw_hash = { path = "../tw_hash" } tw_keypair = { path = "../tw_keypair" } tw_memory = { path = "../tw_memory" } -tw_move_parser = { path = "../tw_move_parser" } tw_misc = { path = "../tw_misc" } tw_proto = { path = "../tw_proto" } diff --git a/rust/wallet_core_rs/src/lib.rs b/rust/wallet_core_rs/src/lib.rs index 6335fa4a0f4..eefcb93f8c8 100644 --- a/rust/wallet_core_rs/src/lib.rs +++ b/rust/wallet_core_rs/src/lib.rs @@ -5,6 +5,7 @@ // file LICENSE at the root of the source code distribution tree. pub extern crate tw_any_coin; +pub extern crate tw_aptos; pub extern crate tw_bitcoin; pub extern crate tw_coin_registry; pub extern crate tw_encoding; @@ -12,7 +13,6 @@ pub extern crate tw_ethereum; pub extern crate tw_hash; pub extern crate tw_keypair; pub extern crate tw_memory; -pub extern crate tw_move_parser; pub extern crate tw_proto; pub mod ffi; diff --git a/src/Aptos/Address.cpp b/src/Aptos/Address.cpp deleted file mode 100644 index 18242423a9e..00000000000 --- a/src/Aptos/Address.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// Author: Clement Doumergue -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Address.h" -#include "HexCoding.h" - -namespace TW::Aptos { - -Address::Address(const std::string& string) : Address::AptosAddress(string) { -} - -Address::Address(const PublicKey& publicKey): Address::AptosAddress(publicKey) { -} - -Data Address::getDigest(const PublicKey& publicKey) { - auto key_data = publicKey.bytes; - append(key_data, 0x00); - return key_data; -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, Address addr) noexcept { - stream.add_bytes(addr.bytes.begin(), addr.bytes.end()); - return stream; -} - -} // namespace TW::Aptos diff --git a/src/Aptos/Address.h b/src/Aptos/Address.h deleted file mode 100644 index db735283ddf..00000000000 --- a/src/Aptos/Address.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// Author: Clement Doumergue -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "BCS.h" -#include "Data.h" -#include "Move/Address.h" -#include "PublicKey.h" - -#include - -namespace TW::Aptos { - -class Address : public Move::Address { -public: - using AptosAddress = Move::Address; - using AptosAddress::size; - using AptosAddress::bytes; - - /// Initializes an Aptos address with a string representation. - explicit Address(const std::string& string); - - /// Initializes an Aptos address with a public key. - explicit Address(const PublicKey& publicKey); - - /// Constructor that allow factory programming; - Address() noexcept = default; - - Data getDigest(const PublicKey& publicKey); -}; - -constexpr inline bool operator==(const Address& lhs, const Address& rhs) noexcept { - return lhs.bytes == rhs.bytes; -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, Address) noexcept; - -} // namespace TW::Aptos diff --git a/src/Aptos/Entry.cpp b/src/Aptos/Entry.cpp index 35d8c776f2c..a0d362910ac 100644 --- a/src/Aptos/Entry.cpp +++ b/src/Aptos/Entry.cpp @@ -6,9 +6,6 @@ #include "Entry.h" -#include "Address.h" -#include "Signer.h" - namespace TW::Aptos { bool Entry::validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const { diff --git a/src/Aptos/MoveTypes.cpp b/src/Aptos/MoveTypes.cpp deleted file mode 100644 index ccde47c9439..00000000000 --- a/src/Aptos/MoveTypes.cpp +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include -#include - -namespace TW::Aptos { - -Aptos::ModuleId::ModuleId(Address accountAddress, Identifier name) noexcept - : mAccountAddress(accountAddress), mName(std::move(name)) { -} - -Data ModuleId::accessVector() const noexcept { - BCS::Serializer serializer; - serializer << static_cast(gCodeTag) << mAccountAddress << mName; - return serializer.bytes; -} - -std::string ModuleId::string() const noexcept { - std::stringstream ss; - ss << mAccountAddress.string() << "::" << mName; - return ss.str(); -} - -std::string ModuleId::shortString() const noexcept { - std::stringstream ss; - ss << "0x" << mAccountAddress.shortString() << "::" << mName; - return ss.str(); -} - -Data StructTag::serialize(bool withResourceTag) const noexcept { - BCS::Serializer serializer; - if (withResourceTag) - { - serializer << gResourceTag; - } - serializer << mAccountAddress << mModule << mName << mTypeParams; - return serializer.bytes; -} - -StructTag::StructTag(Address accountAddress, Identifier module, Identifier name, std::vector typeParams) noexcept - : mAccountAddress(accountAddress), mModule(std::move(module)), mName(std::move(name)), mTypeParams(std::move(typeParams)) { -} -std::string StructTag::string() const noexcept { - std::stringstream ss; - ss << "0x" << mAccountAddress.shortString() << "::" << mModule << "::" << mName; - if (!mTypeParams.empty()) { - ss << "<"; - ss << TypeTagToString(*mTypeParams.begin()); - std::for_each(begin(mTypeParams) + 1, end(mTypeParams), [&ss](auto&& cur) { - ss << ", " << TypeTagToString(cur); - }); - ss << ">"; - } - return ss.str(); -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, Bool) noexcept { - stream << Bool::value; - return stream; -} -BCS::Serializer& operator<<(BCS::Serializer& stream, U8) noexcept { - stream << U8::value; - return stream; -} -BCS::Serializer& operator<<(BCS::Serializer& stream, U64) noexcept { - stream << U64::value; - return stream; -} -BCS::Serializer& operator<<(BCS::Serializer& stream, U128) noexcept { - stream << U128::value; - return stream; -} -BCS::Serializer& operator<<(BCS::Serializer& stream, TAddress) noexcept { - stream << TAddress::value; - return stream; -} -BCS::Serializer& operator<<(BCS::Serializer& stream, TSigner) noexcept { - stream << TSigner::value; - return stream; -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, const StructTag& st) noexcept { - auto res = st.serialize(); - stream.add_bytes(begin(res), end(res)); - return stream; -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, const TStructTag& st) noexcept { - stream << TStructTag::value; - auto res = st.st.serialize(false); - stream.add_bytes(begin(res), end(res)); - return stream; -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, const Vector& t) noexcept { - stream << Vector::value; - for (auto&& cur: t.tags) { - stream << cur; - } - return stream; -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, const TypeTag& t) noexcept { - std::visit([&stream](auto&& arg) { stream << arg; }, t.tags); - return stream; -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, const ModuleId& module) noexcept { - stream << module.address() << module.name(); - return stream; -} -std::string TypeTagToString(const TypeTag& typeTag) noexcept { - auto visit_functor = [](const TypeTag::TypeTagVariant& value) -> std::string { - if (std::holds_alternative(value)) { - return "bool"; - } else if (std::holds_alternative(value)) { - return "u8"; - } else if (std::holds_alternative(value)) { - return "u64"; - } else if (std::holds_alternative(value)) { - return "u128"; - } else if (std::holds_alternative(value)) { - return "address"; - } else if (std::holds_alternative(value)) { - return "signer"; - } else if (auto* vectorData = std::get_if(&value); vectorData != nullptr && !vectorData->tags.empty()) { - std::stringstream ss; - ss << "vector<" << TypeTagToString(*vectorData->tags.begin()) << ">"; - return ss.str(); - } else if (auto* structData = std::get_if(&value); structData) { - return structData->string(); - } else if (auto* tStructData = std::get_if(&value); tStructData) { - return tStructData->st.string(); - } else { - return ""; - } - }; - - return std::visit(visit_functor, typeTag.tags); -} - -} // namespace TW::Aptos diff --git a/src/Aptos/MoveTypes.h b/src/Aptos/MoveTypes.h deleted file mode 100644 index 26dfe1b8477..00000000000 --- a/src/Aptos/MoveTypes.h +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "Aptos/Address.h" -#include "BCS.h" -#include - -namespace TW::Aptos { - -constexpr std::uint8_t gCodeTag{0}; -constexpr std::uint8_t gResourceTag{1}; -using Identifier = std::string; - -class ModuleId { -public: - ///< Constructor - ModuleId(Address accountAddress, Identifier name) noexcept; - - ///< Getters - [[nodiscard]] const std::string& name() const noexcept { return mName; } - [[nodiscard]] const Address& address() const noexcept { return mAccountAddress; } - [[nodiscard]] Data accessVector() const noexcept; - [[nodiscard]] std::string string() const noexcept; - [[nodiscard]] std::string shortString() const noexcept; - -private: - Address mAccountAddress; - Identifier mName; -}; - -inline ModuleId gAptosAccountModule{Address::one(), "aptos_account"}; -inline ModuleId gAptosCoinModule{Address::one(), "coin"}; -inline ModuleId gAptosManagedCoinsModule{Address::one(), "managed_coin"}; -inline ModuleId gAptosTokenTransfersModule{Address::three(), "token_transfers"}; - -BCS::Serializer& operator<<(BCS::Serializer& stream, const ModuleId& module) noexcept; - -struct TypeTag; - -struct Bool { - static constexpr std::uint8_t value = 0; -}; -struct U8 { - static constexpr std::uint8_t value = 1; -}; -struct U64 { - static constexpr std::uint8_t value = 2; -}; -struct U128 { - static constexpr std::uint8_t value = 3; -}; -struct TAddress { - static constexpr std::uint8_t value = 4; -}; -struct TSigner { - static constexpr std::uint8_t value = 5; -}; -struct Vector { - static constexpr std::uint8_t value = 6; - std::vector tags; -}; - -class StructTag { -public: - explicit StructTag(Address accountAddress, Identifier module, Identifier name, std::vector typeParams) noexcept; - [[nodiscard]] Data serialize(bool withResourceTag = true) const noexcept; - [[nodiscard]] ModuleId moduleID() const noexcept { return {mAccountAddress, mName}; }; - [[nodiscard]] std::string string() const noexcept; - -private: - Address mAccountAddress; - Identifier mModule; - Identifier mName; - std::vector mTypeParams; -}; - -// C++ limitation, the first StructTag will serialize with ResourceTag, the inner one will use the value 7 instead. Tweaking by wrapping the struct -struct TStructTag { - static constexpr std::uint8_t value = 7; - StructTag st; -}; - -struct TypeTag { - using TypeTagVariant = std::variant; - TypeTagVariant tags; -}; - -std::string TypeTagToString(const TypeTag& typeTag) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, const StructTag& st) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, Bool) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, U8) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, U64) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, U128) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, TAddress) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, TSigner) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, const Vector& t) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, const TStructTag& t) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, const TypeTag& t) noexcept; -static const TypeTag gTransferTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(Address::one(), "aptos_coin", "AptosCoin", {})})}; -static const TypeTag gOfferNftTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(Address::three(), "token_transfers", "offer_script", {})})}; - -} // namespace TW::Aptos diff --git a/src/Aptos/Signer.cpp b/src/Aptos/Signer.cpp deleted file mode 100644 index 992d91dd6f6..00000000000 --- a/src/Aptos/Signer.cpp +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Signer.h" -#include "Address.h" -#include "Hash.h" -#include "MoveTypes.h" -#include "TransactionBuilder.h" -#include "TransactionPayload.h" - -namespace { -template -void serializeToArgs(std::vector& args, T&& toSerialize) { - TW::BCS::Serializer serializer; - serializer << std::forward(toSerialize); - args.emplace_back(serializer.bytes); -} -} // namespace - -namespace TW::Aptos { - -template -std::pair, nlohmann::json> commonTransferPayload(const TPayload& input) { - std::vector args; - serializeToArgs(args, Address(input.to())); - serializeToArgs(args, input.amount()); - nlohmann::json argsJson = nlohmann::json::array({input.to(), std::to_string(input.amount())}); - return std::make_pair(args, argsJson); -} - -TransactionPayload transferPayload(const Proto::SigningInput& input) { - auto&& [args, argsJson] = commonTransferPayload(input.transfer()); - TransactionPayload payload = EntryFunction(gAptosAccountModule, "transfer", {}, args, argsJson); - return payload; -} - -TransactionPayload createAccountPayload(const Proto::SigningInput& input) { - std::vector args; - serializeToArgs(args, Address(input.create_account().auth_key())); - nlohmann::json argsJson = nlohmann::json::array({input.create_account().auth_key()}); - TransactionPayload payload = EntryFunction(gAptosAccountModule, "create_account", {}, args, argsJson); - return payload; -} - -TransactionPayload claimNftPayload(const Proto::ClaimNftMessage& msg) { - std::vector args; - serializeToArgs(args, Address(msg.sender())); - serializeToArgs(args, Address(msg.creator())); - serializeToArgs(args, msg.collectionname()); - serializeToArgs(args, msg.name()); - serializeToArgs(args, msg.property_version()); - // clang-format off - nlohmann::json argsJson = nlohmann::json::array( - { - msg.sender(), - msg.creator(), - msg.collectionname(), - msg.name(), - std::to_string(msg.property_version()), - }); - // clang-format on - TransactionPayload payload = EntryFunction(gAptosTokenTransfersModule, "claim_script", {}, args, argsJson); - return payload; -} - -TransactionPayload nftOfferPayload(const Proto::OfferNftMessage& msg) { - std::vector args; - serializeToArgs(args, Address(msg.receiver())); - serializeToArgs(args, Address(msg.creator())); - serializeToArgs(args, msg.collectionname()); - serializeToArgs(args, msg.name()); - serializeToArgs(args, msg.property_version()); - serializeToArgs(args, msg.amount()); - // clang-format off - nlohmann::json argsJson = nlohmann::json::array( - { - msg.receiver(), - msg.creator(), - msg.collectionname(), - msg.name(), - std::to_string(msg.property_version()), - std::to_string(msg.amount()) - }); - // clang-format on - TransactionPayload payload = EntryFunction(gAptosTokenTransfersModule, "offer_script", {}, args, argsJson); - return payload; -} - -TransactionPayload tortugaClaimPayload(const std::string& smart_contract_address, const Proto::TortugaClaim& msg) { - std::vector args; - serializeToArgs(args, msg.idx()); - // clang-format off - nlohmann::json argsJson = nlohmann::json::array( - { - std::to_string(msg.idx()) - }); - // clang-format on - ModuleId tortugaStakeModule{Address(smart_contract_address), "stake_router"}; - TransactionPayload payload = EntryFunction(tortugaStakeModule, "claim", {}, args, argsJson); - return payload; -} - -TransactionPayload tortugaStakePayload(const std::string& smart_contract_address, const Proto::TortugaStake& msg) { - std::vector args; - serializeToArgs(args, msg.amount()); - // clang-format off - nlohmann::json argsJson = nlohmann::json::array( - { - std::to_string(msg.amount()) - }); - // clang-format on - ModuleId tortugaStakeModule{Address(smart_contract_address), "stake_router"}; - TransactionPayload payload = EntryFunction(tortugaStakeModule, "stake", {}, args, argsJson); - return payload; -} - -TransactionPayload tortugaUnStakePayload(const std::string& smart_contract_address, const Proto::TortugaUnstake& msg) { - std::vector args; - serializeToArgs(args, msg.amount()); - // clang-format off - nlohmann::json argsJson = nlohmann::json::array( - { - std::to_string(msg.amount()) - }); - // clang-format on - ModuleId tortugaStakeModule{Address(smart_contract_address), "stake_router"}; - TransactionPayload payload = EntryFunction(tortugaStakeModule, "unstake", {}, args, argsJson); - return payload; -} - -TransactionPayload cancelNftOfferPayload(const Proto::CancelOfferNftMessage& msg) { - std::vector args; - serializeToArgs(args, Address(msg.receiver())); - serializeToArgs(args, Address(msg.creator())); - serializeToArgs(args, msg.collectionname()); - serializeToArgs(args, msg.name()); - serializeToArgs(args, msg.property_version()); - // clang-format off - nlohmann::json argsJson = nlohmann::json::array( - { - msg.receiver(), - msg.creator(), - msg.collectionname(), - msg.name(), - std::to_string(msg.property_version()), - }); - // clang-format on - TransactionPayload payload = EntryFunction(gAptosTokenTransfersModule, "cancel_offer_script", {}, args, argsJson); - return payload; -} - -TransactionPayload tokenTransferPayload(const Proto::SigningInput& input) { - - auto&& [args, argsJson] = commonTransferPayload(input.token_transfer()); - auto& function = input.token_transfer().function(); - TypeTag tokenTransferTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(Address(function.account_address()), - function.module(), function.name(), {})})}; - TransactionPayload payload = EntryFunction(gAptosCoinModule, "transfer", {tokenTransferTag}, args, argsJson); - return payload; -} - -TransactionPayload tokenTransferCoinsPayload(const Proto::SigningInput& input) { - auto&& [args, argsJson] = commonTransferPayload(input.token_transfer_coins()); - auto& function = input.token_transfer_coins().function(); - TypeTag tokenTransferTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(Address(function.account_address()), - function.module(), function.name(), {})})}; - TransactionPayload payload = EntryFunction(gAptosAccountModule, "transfer_coins", {tokenTransferTag}, args, argsJson); - return payload; -} - -TransactionPayload registerTokenPayload(const Proto::SigningInput& input) { - - auto& function = input.register_token().function(); - TypeTag tokenRegisterTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(Address(function.account_address()), - function.module(), function.name(), {})})}; - TransactionPayload payload = EntryFunction(gAptosManagedCoinsModule, "register", {tokenRegisterTag}, {}); - return payload; -} - -TransactionBasePtr buildBlindTx(const Proto::SigningInput& input) { - if (nlohmann::json j = nlohmann::json::parse(input.any_encoded(), nullptr, false); j.is_discarded()) { - auto blindBuilder = std::make_unique(); - blindBuilder->encodedCallHex(input.any_encoded()); - return blindBuilder; - } else { - auto txBuilder = std::make_unique(); - txBuilder->sender(Address(input.sender())) - .sequenceNumber(input.sequence_number()) - .payload(EntryFunction::from_json(j)) - .maxGasAmount(input.max_gas_amount()) - .gasUnitPrice(input.gas_unit_price()) - .expirationTimestampSecs(input.expiration_timestamp_secs()) - .chainId(static_cast(input.chain_id())); - return txBuilder; - } -} - -TransactionBasePtr buildTx(const Proto::SigningInput& input) { - if (!input.any_encoded().empty()) { - return buildBlindTx(input); - } - - auto nftPayloadFunctor = [](const Proto::NftMessage& nftMessage) { - switch (nftMessage.nft_transaction_payload_case()) { - case Proto::NftMessage::kOfferNft: - return nftOfferPayload(nftMessage.offer_nft()); - case Proto::NftMessage::kCancelOfferNft: - return cancelNftOfferPayload(nftMessage.cancel_offer_nft()); - case Proto::NftMessage::kClaimNft: - return claimNftPayload(nftMessage.claim_nft()); - case Proto::NftMessage::NFT_TRANSACTION_PAYLOAD_NOT_SET: - throw std::runtime_error("Nft message payload not set"); - } - }; - auto liquidStakingFunctor = [](const Proto::LiquidStaking& liquidStakingMessage) { - switch (liquidStakingMessage.liquid_stake_transaction_payload_case()) { - case Proto::LiquidStaking::kStake: - return tortugaStakePayload(liquidStakingMessage.smart_contract_address(), liquidStakingMessage.stake()); - case Proto::LiquidStaking::kUnstake: - return tortugaUnStakePayload(liquidStakingMessage.smart_contract_address(), liquidStakingMessage.unstake()); - case Proto::LiquidStaking::kClaim: - return tortugaClaimPayload(liquidStakingMessage.smart_contract_address(), liquidStakingMessage.claim()); - case Proto::LiquidStaking::LIQUID_STAKE_TRANSACTION_PAYLOAD_NOT_SET: - return TransactionPayload(); - } - }; - auto payloadFunctor = [&input, &nftPayloadFunctor, &liquidStakingFunctor]() { - switch (input.transaction_payload_case()) { - case Proto::SigningInput::kTransfer: { - return transferPayload(input); - } - case Proto::SigningInput::kTokenTransfer: { - return tokenTransferPayload(input); - } - case Proto::SigningInput::kNftMessage: { - return nftPayloadFunctor(input.nft_message()); - } - case Proto::SigningInput::kCreateAccount: { - return createAccountPayload(input); - } - case Proto::SigningInput::kRegisterToken: { - return registerTokenPayload(input); - } - case Proto::SigningInput::kLiquidStakingMessage: { - return liquidStakingFunctor(input.liquid_staking_message()); - } - case Proto::SigningInput::kTokenTransferCoins: { - return tokenTransferCoinsPayload(input); - } - case Proto::SigningInput::TRANSACTION_PAYLOAD_NOT_SET: - throw std::runtime_error("Transaction payload should be set"); - } - }; - auto txBuilder = std::make_unique(); - txBuilder->sender(Address(input.sender())) - .sequenceNumber(input.sequence_number()) - .payload(payloadFunctor()) - .maxGasAmount(input.max_gas_amount()) - .gasUnitPrice(input.gas_unit_price()) - .expirationTimestampSecs(input.expiration_timestamp_secs()) - .chainId(static_cast(input.chain_id())); - return txBuilder; -} - -Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) { - return buildTx(input)->sign(input); -} - -TxCompiler::Proto::PreSigningOutput Signer::preImageHashes(const Proto::SigningInput& input) { - return buildTx(input)->preImage(); -} - -Proto::SigningOutput Signer::compile(const Proto::SigningInput& input, const Data& signature, const PublicKey& publicKey) { - return buildTx(input)->compile(signature, publicKey); -} - -} // namespace TW::Aptos diff --git a/src/Aptos/Signer.h b/src/Aptos/Signer.h deleted file mode 100644 index e0222515ca0..00000000000 --- a/src/Aptos/Signer.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "Data.h" -#include "../PrivateKey.h" -#include "../proto/Aptos.pb.h" -#include "../proto/TransactionCompiler.pb.h" - -namespace TW::Aptos { - -inline const Data gAptosSalt = data("APTOS::RawTransaction"); - -/// Helper class that performs Aptos transaction signing. -class Signer { -public: - /// Hide default constructor - Signer() = delete; - - /// Signs a Proto::SigningInput transaction - static Proto::SigningOutput sign(const Proto::SigningInput& input); - - static TxCompiler::Proto::PreSigningOutput preImageHashes(const Proto::SigningInput& input); - - static Proto::SigningOutput compile(const Proto::SigningInput& input, const Data& signature, const PublicKey& publicKey); -}; - -} // namespace TW::Aptos diff --git a/src/Aptos/TransactionBuilder.h b/src/Aptos/TransactionBuilder.h deleted file mode 100644 index 46bf3fbaa12..00000000000 --- a/src/Aptos/TransactionBuilder.h +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "HexCoding.h" -#include "TransactionPayload.h" - -#include -#include - -namespace TW::Aptos { - -struct TransactionBase; - -using TransactionBasePtr = std::unique_ptr; - -struct TransactionBase { - virtual ~TransactionBase() = default; - - virtual TxCompiler::Proto::PreSigningOutput preImage() noexcept = 0; - - virtual Proto::SigningOutput compile(const Data& signature, const PublicKey& publicKey) = 0; - - virtual Proto::SigningOutput sign(const Proto::SigningInput& input) = 0; -}; - -class BlindBuilder final : public TransactionBase { -public: - BlindBuilder() noexcept = default; - - BlindBuilder& encodedCallHex(const std::string& encodedCallHex) { - mEncodedCall = parse_hex(encodedCallHex); - return *this; - } - - TxCompiler::Proto::PreSigningOutput preImage() noexcept override { - TxCompiler::Proto::PreSigningOutput output; - // Aptos has no preImageHash. - output.set_data(mEncodedCall.data(), mEncodedCall.size()); - return output; - } - - Proto::SigningOutput compile(const Data& signature, const PublicKey& publicKey) override { - Proto::SigningOutput output; - const auto& pubKeyData = publicKey.bytes; - - BCS::Serializer serializer; - serializer.add_bytes(begin(mEncodedCall), end(mEncodedCall)); - - output.set_raw_txn(mEncodedCall.data(), mEncodedCall.size()); - output.mutable_authenticator()->set_public_key(pubKeyData.data(), pubKeyData.size()); - output.mutable_authenticator()->set_signature(signature.data(), signature.size()); - serializer << BCS::uleb128{.value = 0} << pubKeyData << signature; - output.set_encoded(serializer.bytes.data(), serializer.bytes.size()); - - // clang-format off - nlohmann::json json = { - {"type", "ed25519_signature"}, - {"public_key", hexEncoded(pubKeyData)}, - {"signature", hexEncoded(signature)} - }; - // clang-format on - output.set_json(json.dump()); - - return output; - } - - Proto::SigningOutput sign(const Proto::SigningInput& input) override { - auto privateKey = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - auto signature = privateKey.sign(mEncodedCall, TWCurveED25519); - return compile(signature, publicKey); - } - -private: - Data mEncodedCall; -}; - -// Standard transaction builder. -class TransactionBuilder final : public TransactionBase { -public: - TransactionBuilder() noexcept = default; - - TransactionBuilder& sender(Address sender) noexcept { - mSender = sender; - return *this; - } - - TransactionBuilder& sequenceNumber(std::uint64_t sequenceNumber) noexcept { - mSequenceNumber = sequenceNumber; - return *this; - } - - TransactionBuilder& payload(TransactionPayload payload) noexcept { - mPayload = std::move(payload); - return *this; - } - - TransactionBuilder& maxGasAmount(std::uint64_t maxGasAmount) noexcept { - mMaxGasAmount = maxGasAmount; - return *this; - } - - TransactionBuilder& gasUnitPrice(std::uint64_t gasUnitPrice) noexcept { - mGasUnitPrice = gasUnitPrice; - return *this; - } - - TransactionBuilder& expirationTimestampSecs(std::uint64_t expirationTimestampSecs) noexcept { - mExpirationTimestampSecs = expirationTimestampSecs; - return *this; - } - - TransactionBuilder& chainId(std::uint8_t chainId) noexcept { - mChainId = chainId; - return *this; - } - - BCS::Serializer prepareSerializer() noexcept { - BCS::Serializer serializer; - serializer << mSender << mSequenceNumber << mPayload << mMaxGasAmount << mGasUnitPrice << mExpirationTimestampSecs << mChainId; - return serializer; - } - - Data msgToSign() noexcept { - auto serialized = prepareSerializer().bytes; - auto preImageOutput = TW::Hash::sha3_256(gAptosSalt.data(), gAptosSalt.size()); - append(preImageOutput, serialized); - return preImageOutput; - } - - TxCompiler::Proto::PreSigningOutput preImage() noexcept override { - TxCompiler::Proto::PreSigningOutput output; - auto signingMsg = msgToSign(); - // Aptos has no preImageHash. - output.set_data(signingMsg.data(), signingMsg.size()); - return output; - } - - Proto::SigningOutput compile(const Data& signature, const PublicKey& publicKey) noexcept override { - Proto::SigningOutput output; - const auto& pubKeyData = publicKey.bytes; - - auto serializer = prepareSerializer(); - - output.set_raw_txn(serializer.bytes.data(), serializer.bytes.size()); - output.mutable_authenticator()->set_public_key(pubKeyData.data(), pubKeyData.size()); - output.mutable_authenticator()->set_signature(signature.data(), signature.size()); - - serializer << BCS::uleb128{.value = 0} << pubKeyData << signature; - output.set_encoded(serializer.bytes.data(), serializer.bytes.size()); - - // https://fullnode.devnet.aptoslabs.com/v1/spec#/operations/submit_transaction - // clang-format off - nlohmann::json json = { - {"sender", mSender.string()}, - {"sequence_number", std::to_string(mSequenceNumber)}, - {"max_gas_amount", std::to_string(mMaxGasAmount)}, - {"gas_unit_price", std::to_string(mGasUnitPrice)}, - {"expiration_timestamp_secs", std::to_string(mExpirationTimestampSecs)}, - {"payload", payloadToJson(mPayload)}, - {"signature", { - {"type", "ed25519_signature"}, - {"public_key", hexEncoded(pubKeyData)}, - {"signature", hexEncoded(signature)}} - } - }; - // clang-format on - output.set_json(json.dump()); - return output; - } - - Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept override { - auto signingMsg = msgToSign(); - auto privateKey = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); - auto signature = privateKey.sign(signingMsg, TWCurveED25519); - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - return compile(signature, publicKey); - } - -private: - Address mSender{}; - std::uint64_t mSequenceNumber{}; - TransactionPayload mPayload{}; - std::uint64_t mMaxGasAmount{}; - std::uint64_t mGasUnitPrice{}; - std::uint64_t mExpirationTimestampSecs{}; - std::uint8_t mChainId{}; -}; - -} // namespace TW::Aptos diff --git a/src/Aptos/TransactionPayload.cpp b/src/Aptos/TransactionPayload.cpp deleted file mode 100644 index e3bf73bccb7..00000000000 --- a/src/Aptos/TransactionPayload.cpp +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "rust/bindgen/WalletCoreRSBindgen.h" -#include -#include - -namespace TW::Aptos { - -EntryFunction::EntryFunction(ModuleId module, Identifier function, std::vector tyArgs, std::vector args, nlohmann::json jsonArgs) noexcept - : mModule(std::move(module)), mFunction(std::move(function)), mTyArgs(std::move(tyArgs)), mArgs(std::move(args)), mJsonArgs(std::move(jsonArgs)) { -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, const EntryFunction& entryFunction) noexcept { - stream << entryFunction.module() << entryFunction.function() << entryFunction.tyArgs() << entryFunction.args(); - return stream; -} - -nlohmann::json payloadToJson(const TransactionPayload& payload) { - auto visit_functor = [](const TransactionPayload& value) -> nlohmann::json { - if (auto* entryFunction = std::get_if(&value); entryFunction) { - return entryFunction->json(); - } else { - return {}; - } - }; - - return std::visit(visit_functor, payload); -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, [[maybe_unused]] const Script& script) noexcept { - return stream; -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, [[maybe_unused]] const ModuleBundle& moduleBundle) noexcept { - return stream; -} - -nlohmann::json EntryFunction::json() const noexcept { - nlohmann::json tyArgsJson = nlohmann::json::array(); - for (auto&& cur : mTyArgs) { - tyArgsJson.emplace_back(TypeTagToString(cur)); - } - // clang-format off - nlohmann::json out = { - {"type", "entry_function_payload"}, - {"function", mModule.shortString() + "::" + mFunction}, - {"type_arguments", tyArgsJson}, - {"arguments", mJsonArgs.empty() ? nlohmann::json::array() : mJsonArgs} - }; - // clang-format on - return out; -} - -EntryFunction EntryFunction::from_json(const nlohmann::json& payload) noexcept { - auto splitFunctor = [](std::string s, std::string_view delimiter) { - size_t pos_start = 0, pos_end, delim_len = delimiter.size(); - std::string token; - std::vector output; - - while ((pos_end = s.find(delimiter, pos_start)) != std::string::npos) { - token = s.substr(pos_start, pos_end - pos_start); - pos_start = pos_end + delim_len; - output.emplace_back(token); - } - - output.emplace_back(s.substr(pos_start)); - return output; - }; - auto functionSplitted = splitFunctor(payload.at("function").get(), "::"); - auto moduleId = ModuleId(Address(functionSplitted[0]), functionSplitted[1]); - std::vector args; - for (auto&& cur : payload.at("arguments")) { - auto curStr = cur.get(); - auto res = Rust::parse_function_argument_to_bcs(curStr.c_str()); - if (res.code != Rust::OK_CODE) { - // TODO consider exiting this function. - args.emplace_back(); - continue; - } - args.emplace_back(parse_hex(res.result)); - Rust::free_string(res.result); - } - - std::vector tags; - - for (auto&& cur : payload.at("type_arguments")) { - auto curStr = cur.get(); - switch (Rust::parse_type_tag(curStr.c_str())) { - case Rust::ETypeTag::Bool: - break; - case Rust::ETypeTag::U8: - break; - case Rust::ETypeTag::U64: - break; - case Rust::ETypeTag::U128: - break; - case Rust::ETypeTag::Address: - break; - case Rust::ETypeTag::Signer: - break; - case Rust::ETypeTag::Vector: - break; - case Rust::ETypeTag::Struct: { - auto structSplitted = splitFunctor(curStr, "::"); - auto addr = Address(structSplitted[0]); - TypeTag tag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(addr, structSplitted[1], structSplitted[2], {})})}; - tags.emplace_back(tag); - break; - } - case Rust::ETypeTag::Error: - break; - default: - break; - } - } - - return EntryFunction(moduleId, functionSplitted[2], tags, {args}, payload.at("arguments")); -} - -} // namespace TW::Aptos diff --git a/src/Aptos/TransactionPayload.h b/src/Aptos/TransactionPayload.h deleted file mode 100644 index 6041fc0ed8d..00000000000 --- a/src/Aptos/TransactionPayload.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include -#include -#include - -namespace TW::Aptos { - -/// Call a Move entry function. -class EntryFunction { -public: - explicit EntryFunction(ModuleId module, Identifier function, std::vector tyArgs, std::vector args, nlohmann::json jsonArgs = {}) noexcept; - [[nodiscard]] const ModuleId& module() const noexcept { return mModule; } - [[nodiscard]] const Identifier& function() const noexcept { return mFunction; } - [[nodiscard]] const std::vector& tyArgs() const noexcept { return mTyArgs; } - [[nodiscard]] const std::vector& args() const noexcept { return mArgs; } - [[nodiscard]] nlohmann::json json() const noexcept; - static EntryFunction from_json(const nlohmann::json& json) noexcept; - -private: - ModuleId mModule; - Identifier mFunction; - std::vector mTyArgs; - std::vector mArgs; - nlohmann::json mJsonArgs; -}; - - -class Script { -}; - -class ModuleBundle { -}; - -BCS::Serializer& operator<<(BCS::Serializer& stream, const EntryFunction& entryFunction) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, const Script& script) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, const ModuleBundle& moduleBundle) noexcept; -using TransactionPayload = std::variant; -nlohmann::json payloadToJson(const TransactionPayload& payload); - -} // namespace TW::Aptos diff --git a/src/BCS.cpp b/src/BCS.cpp deleted file mode 100644 index fb6015cdf1b..00000000000 --- a/src/BCS.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// Created by Clément Doumergue - -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "BCS.h" - -namespace TW::BCS { - -Serializer& operator<<(Serializer& stream, std::byte b) noexcept { - stream.add_byte(b); - return stream; -} - -Serializer& operator<<(Serializer& stream, uleb128 t) noexcept { - integral auto value = t.value; - - while (value >= 0x80) { - // Add the 7 lowest bits of data and set highest bit to 1 - stream << static_cast((value & 0x7f) | 0x80); - value >>= 7; - } - - // Add the remaining bits of data (highest bit is already 0 at this point) - stream << static_cast(value); - return stream; -} - -Serializer& operator<<(Serializer& stream, std::string_view sv) noexcept { - stream << uleb128{static_cast(sv.size())}; - stream.add_bytes(sv.begin(), sv.end()); - return stream; -} - -Serializer& operator<<(Serializer& stream, std::nullopt_t) noexcept { - stream << false; - return stream; -} - -} // namespace TW::BCS diff --git a/src/BCS.h b/src/BCS.h deleted file mode 100644 index 563c204b8cb..00000000000 --- a/src/BCS.h +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// Created by Clément Doumergue - -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "Data.h" -#include "concepts/tw_concepts.h" - -namespace TW::BCS { - -/// Implementation of BCS encoding (as specified by the Diem project, see github.com/diem/bcs#detailed-specifications) - -struct Serializer { - Data bytes; - - void add_byte(std::byte b) noexcept { - bytes.emplace_back(static_cast(b)); - } - - template - void add_bytes(Iterator first, Iterator last) noexcept { - std::transform(first, last, std::back_inserter(bytes), [](auto&& c) { - return static_cast(c); - }); - } - - void clear() noexcept { - bytes.clear(); - } -}; - -struct uleb128 { - uint32_t value; -}; - -namespace details { - -template -concept aggregate_struct = std::is_class_v> && std::is_aggregate_v>; - -template -concept map_container = requires(T t) { - typename T::key_type; - typename T::mapped_type; - { std::declval().size() } -> std::same_as; - }; - -template - requires integral || - floating_point || - std::same_as || - std::same_as || - std::same_as || - aggregate_struct || - map_container -struct is_serializable { - static constexpr auto value = true; -}; - -template -struct is_serializable> { - static constexpr auto value = is_serializable::value; -}; - -template -struct is_serializable> { - static constexpr auto value = (is_serializable::value && ...); -}; - -template -struct is_serializable> { - static constexpr auto value = is_serializable::value && is_serializable::value; -}; - -template -struct is_serializable> { - static constexpr auto value = is_serializable::value; -}; - -template -struct is_serializable> { - static constexpr auto value = (is_serializable::value && ...); -}; - -template -Serializer& serialize_integral_impl(Serializer& stream, T t, std::index_sequence) noexcept { - const char* bytes = reinterpret_cast(&t); - // Add each byte in little-endian order - return (stream << ... << static_cast(bytes[Is])); -} - -template -Serializer& serialize_tuple_impl(Serializer& stream, const T& t, std::index_sequence) noexcept { - return (stream << ... << std::get(t)); -} - -template -struct dependent_false { - static constexpr auto value = false; -}; - -template -constexpr auto to_tuple(T&& t) { - if constexpr (std::is_empty_v) { - return std::make_tuple(); - } else if constexpr (requires { [&t] { auto&& [a0] = t; }; }) { - auto&& [a0] = std::forward(t); - return std::make_tuple(a0); - } else if constexpr (requires { [&t] { auto&& [a0, a1] = t; }; }) { - auto&& [a0, a1] = std::forward(t); - return std::make_tuple(a0, a1); - } else if constexpr (requires { [&t] { auto&& [a0, a1, a2] = t; }; }) { - auto&& [a0, a1, a2] = std::forward(t); - return std::make_tuple(a0, a1, a2); - } else if constexpr (requires { [&t] { auto&& [a0, a1, a2, a3] = t; }; }) { - auto&& [a0, a1, a2, a3] = std::forward(t); - return std::make_tuple(a0, a1, a2, a3); - } else if constexpr (requires { [&t] { auto&& [a0, a1, a2, a3, a4] = t; }; }) { - auto&& [a0, a1, a2, a3, a4] = std::forward(t); - return std::make_tuple(a0, a1, a2, a3, a4); - } else if constexpr (requires { [&t] { auto&& [a0, a1, a2, a3, a4, a5] = t; }; }) { - auto&& [a0, a1, a2, a3, a4, a5] = std::forward(t); - return std::make_tuple(a0, a1, a2, a3, a4, a5); - } else { - static_assert(dependent_false::value, "the structure has more than 6 members"); - } -} - -template -Serializer& serialize_struct_impl(Serializer& stream, const T& t) noexcept { - return stream << to_tuple(t); -} -} // namespace details - -template -concept PrimitiveSerializable = details::is_serializable::value; - -template -concept CustomSerializable = requires(T t) { - { std::declval() << t } -> std::same_as; - }; - -template -concept Serializable = PrimitiveSerializable || CustomSerializable; - -Serializer& operator<<(Serializer& stream, std::byte b) noexcept; - -template -Serializer& operator<<(Serializer& stream, T t) noexcept { - return details::serialize_integral_impl(stream, t, std::make_index_sequence{}); -} - -Serializer& operator<<(Serializer& stream, uleb128 t) noexcept; - -Serializer& operator<<(Serializer& stream, std::string_view sv) noexcept; - -template -Serializer& operator<<(Serializer& stream, const std::optional o) noexcept { - if (o.has_value()) { - stream << true; - stream << o.value(); - } else { - stream << false; - } - return stream; -} - -Serializer& operator<<(Serializer& stream, std::nullopt_t) noexcept; - -template -Serializer& operator<<(Serializer& stream, const std::tuple& t) noexcept { - return details::serialize_tuple_impl(stream, t, std::make_index_sequence{}); -} - -template -Serializer& operator<<(Serializer& stream, const std::pair& t) noexcept { - return details::serialize_tuple_impl(stream, t, std::make_index_sequence<2>{}); -} - -template -Serializer& operator<<(Serializer& stream, const T& t) noexcept { - return details::serialize_struct_impl(stream, t); -} - -template -Serializer& operator<<(Serializer& stream, const std::vector& t) noexcept { - stream << uleb128{static_cast(t.size())}; - for (auto&& cur: t) { - stream << cur; - } - return stream; -} - -template -Serializer& operator<<(Serializer& stream, const std::variant& t) noexcept { - stream << uleb128{static_cast(t.index())}; - std::visit([&stream](auto&& value) { stream << value; }, t); - return stream; -} - -template -Serializer& operator<<(Serializer& stream, const T& t) noexcept { - stream << uleb128{static_cast(t.size())}; - for (auto&& [k, v] : t) { - stream << std::make_tuple(k, v); - } - return stream; -} - -} // namespace TW::BCS diff --git a/tests/chains/Aptos/AddressTests.cpp b/tests/chains/Aptos/AddressTests.cpp index 8c758428154..fc68f647a88 100644 --- a/tests/chains/Aptos/AddressTests.cpp +++ b/tests/chains/Aptos/AddressTests.cpp @@ -6,7 +6,7 @@ // file LICENSE at the root of the source code distribution tree. #include "HexCoding.h" -#include "Aptos/Address.h" +#include "Aptos/Entry.h" #include "PublicKey.h" #include "PrivateKey.h" #include @@ -15,49 +15,38 @@ namespace TW::Aptos::tests { TEST(AptosAddress, Valid) { - ASSERT_TRUE(Address::isValid("0x1")); - ASSERT_TRUE(Address::isValid(Address::one().string())); - ASSERT_TRUE(Address::isValid(Address::zero().string())); - ASSERT_TRUE(Address::isValid("0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b")); - ASSERT_TRUE(Address::isValid("eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b")); - ASSERT_TRUE(Address::isValid("19aadeca9388e009d136245b9a67423f3eee242b03142849eb4f81a4a409e59c")); - ASSERT_TRUE(Address::isValid("777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb")); - ASSERT_TRUE(Address::isValid("0x777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb")); - ASSERT_TRUE(Address::isValid("eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175")); // too short -> automatically padded + Entry entry; + ASSERT_TRUE(entry.validateAddress(TWCoinTypeAptos, "0x1", std::monostate{})); + ASSERT_TRUE(entry.validateAddress(TWCoinTypeAptos, "0x0", std::monostate{})); + ASSERT_TRUE(entry.validateAddress(TWCoinTypeAptos, "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", std::monostate{})); + ASSERT_TRUE(entry.validateAddress(TWCoinTypeAptos, "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", std::monostate{})); + ASSERT_TRUE(entry.validateAddress(TWCoinTypeAptos, "19aadeca9388e009d136245b9a67423f3eee242b03142849eb4f81a4a409e59c", std::monostate{})); + ASSERT_TRUE(entry.validateAddress(TWCoinTypeAptos, "0x777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb", std::monostate{})); + ASSERT_TRUE(entry.validateAddress(TWCoinTypeAptos, "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175", std::monostate{})); } TEST(AptosAddress, Invalid) { - ASSERT_FALSE(Address::isValid("Seff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b")); // Invalid hex character - ASSERT_FALSE(Address::isValid("eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175bb")); // Invalid length: too long + Entry entry; + ASSERT_FALSE(entry.validateAddress(TWCoinTypeAptos, "", std::monostate{})); + ASSERT_FALSE(entry.validateAddress(TWCoinTypeAptos, "Seff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", std::monostate{})); + ASSERT_FALSE(entry.validateAddress(TWCoinTypeAptos, "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175bb", std::monostate{})); + ASSERT_FALSE(entry.validateAddress(TWCoinTypeAptos, "0xSeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", std::monostate{})); } TEST(AptosAddress, FromPrivateKey) { auto privateKey = PrivateKey(parse_hex("088baa019f081d6eab8dff5c447f9ce2f83c1babf3d03686299eaf6a1e89156e")); - auto address = Address(privateKey.getPublicKey(TWPublicKeyTypeED25519)); - ASSERT_EQ(address.string(), "0xe9c4d0b6fe32a5cc8ebd1e9ad5b54a0276a57f2d081dcb5e30342319963626c3"); + auto pubkey = privateKey.getPublicKey(TWPublicKeyTypeED25519); + Entry entry; + auto address = entry.deriveAddress(TWCoinTypeAptos, pubkey, TWDerivationDefault, std::monostate{}); + ASSERT_EQ(address, "0xe9c4d0b6fe32a5cc8ebd1e9ad5b54a0276a57f2d081dcb5e30342319963626c3"); } TEST(AptosAddress, FromPublicKey) { auto publicKey = PublicKey(parse_hex("ad0e293a56c9fc648d1872a00521d97e6b65724519a2676c2c47cb95d131cf5a"), TWPublicKeyTypeED25519); - auto address = Address(publicKey); - ASSERT_EQ(address.string(), "0xe9c4d0b6fe32a5cc8ebd1e9ad5b54a0276a57f2d081dcb5e30342319963626c3"); -} - -TEST(AptosAddress, FromString) { - auto address = Address("eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b"); - ASSERT_EQ(address.string(), "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b"); - - - address = Address("0x777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb"); - ASSERT_EQ(address.string(), "0x0000777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb"); - address = Address("777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb"); - ASSERT_EQ(address.string(), "0x0000777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb"); -} - -TEST(AptosAddress, ShortString) { - ASSERT_EQ(Address::one().string(), "0x0000000000000000000000000000000000000000000000000000000000000001"); - ASSERT_EQ(Address::one().shortString(), "1"); + Entry entry; + auto address = entry.deriveAddress(TWCoinTypeAptos, publicKey, TWDerivationDefault, std::monostate{}); + ASSERT_EQ(address, "0xe9c4d0b6fe32a5cc8ebd1e9ad5b54a0276a57f2d081dcb5e30342319963626c3"); } } // namespace TW::Aptos::tests diff --git a/tests/chains/Aptos/CompilerTests.cpp b/tests/chains/Aptos/CompilerTests.cpp index ea5e522d669..16d7ae5dc11 100644 --- a/tests/chains/Aptos/CompilerTests.cpp +++ b/tests/chains/Aptos/CompilerTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "Aptos/Signer.h" +#include "proto/Aptos.pb.h" #include "HexCoding.h" #include "PrivateKey.h" #include "PublicKey.h" diff --git a/tests/chains/Aptos/MoveTypesTests.cpp b/tests/chains/Aptos/MoveTypesTests.cpp deleted file mode 100644 index 06ef076991c..00000000000 --- a/tests/chains/Aptos/MoveTypesTests.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include -#include -#include - -namespace TW::Aptos::tests { - -TEST(AptosMoveTypes, ModuleId) { - ModuleId module(Address::one(), "coin"); - ASSERT_EQ(module.address(), Address::one()); - ASSERT_EQ(module.name(), "coin"); - ASSERT_EQ(hex(module.accessVector()), "00000000000000000000000000000000000000000000000000000000000000000104636f696e"); - ASSERT_EQ(module.string(), "0x0000000000000000000000000000000000000000000000000000000000000001::coin"); - ASSERT_EQ(module.shortString(), "0x1::coin"); -} - -TEST(AptosMoveTypes, StructTag) { - auto functorTest = [](T value, const std::string expectedHex) { - TypeTag t{.tags = value}; - StructTag st(Address::one(), "abc", "abc", std::vector{{t}}); - ASSERT_EQ(st.moduleID().name(), "abc"); - ASSERT_EQ(st.moduleID().address(), Address::one()); - ASSERT_EQ(hex(st.serialize()), expectedHex); - }; - functorTest(Bool{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630100"); - functorTest(U8{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630101"); - functorTest(U64{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630102"); - functorTest(U128{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630103"); - functorTest(TAddress{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630104"); - functorTest(TSigner{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630105"); - functorTest(Vector{.tags = std::vector{{TypeTag{.tags = U8{}}}}}, "0100000000000000000000000000000000000000000000000000000000000000010361626303616263010601"); - StructTag stInner(Address::one(), "foo", "bar", std::vector{{U8{}}}); - functorTest(TStructTag{stInner}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630107000000000000000000000000000000000000000000000000000000000000000103666f6f036261720101"); -} - -TEST(AptosMoveTypes, TypeTagDisplay) { - auto functorTest = [](const TypeTag &value, const std::string& expected) { - ASSERT_EQ(TypeTagToString(value), expected); - }; - functorTest(TypeTag{.tags = Bool{}}, "bool"); - functorTest(TypeTag{.tags = U8{}}, "u8"); - functorTest(TypeTag{.tags = U64{}}, "u64"); - functorTest(TypeTag{.tags = U128{}}, "u128"); - functorTest(TypeTag{.tags = TAddress{}}, "address"); - functorTest(TypeTag{.tags = TSigner{}}, "signer"); - TypeTag t{.tags = TypeTag::TypeTagVariant(Vector{.tags = {{U8{}}}})}; - functorTest(t, "vector"); - StructTag st(Address::one(), "foo", "bar", std::vector{{U8{}}}); - TypeTag anotherT{.tags = TypeTag::TypeTagVariant(st)}; - functorTest(anotherT, "0x1::foo::bar"); - functorTest(gTransferTag, "0x1::aptos_coin::AptosCoin"); -} - -} // namespace TW::Aptos::tests diff --git a/tests/chains/Aptos/SignerTests.cpp b/tests/chains/Aptos/SignerTests.cpp deleted file mode 100644 index 65c0bc87089..00000000000 --- a/tests/chains/Aptos/SignerTests.cpp +++ /dev/null @@ -1,730 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Aptos/Address.h" -#include "Aptos/Signer.h" -#include "HexCoding.h" -#include "PrivateKey.h" -#include "PublicKey.h" -#include "TestUtilities.h" -#include - -#include - -namespace TW::Aptos::tests { - -TEST(AptosSigner, ClaimNftTxSign) { - // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x60b51e15140ec0b7650334e948fb447ce3cb13ae63492260461ebfa9d02e85c4?network=testnet - Proto::SigningInput input; - input.set_sender("0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - input.set_sequence_number(19); - auto& tf = *input.mutable_nft_message()->mutable_claim_nft(); - tf.set_sender("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee"); - tf.set_creator("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac"); - tf.set_collectionname("Topaz Troopers"); - tf.set_name("Topaz Trooper #20068"); - tf.set_property_version(0); - input.set_max_gas_amount(3296766); - input.set_gas_unit_price(100); - input.set_expiration_timestamp_secs(3664390082); - input.set_chain_id(2); - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002"); - ASSERT_EQ(hex(result.authenticator().signature()), "ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a"); - ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a"); - nlohmann::json expectedJson = R"( - { - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "3296766", - "payload": { - "arguments": [ - "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", - "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", - "Topaz Troopers", "Topaz Trooper #20068", "0"], - "function": "0x3::token_transfers::claim_script", - "type": "entry_function_payload", - "type_arguments": [] - }, - "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "19", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0xede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, NftOfferTxSign) { - // Successfully broadcasted https://explorer.aptoslabs.com/txn/0x514e473618bd3cb89a2b110b7c473db9a2e10532f98eb42d02d86fb31c00525d?network=testnet - Proto::SigningInput input; - input.set_sender("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee"); - input.set_sequence_number(1); - auto& tf = *input.mutable_nft_message()->mutable_offer_nft(); - tf.set_receiver("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - tf.set_creator("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac"); - tf.set_collectionname("Topaz Troopers"); - tf.set_name("Topaz Trooper #20068"); - tf.set_property_version(0); - tf.set_amount(1); - input.set_max_gas_amount(3296766); - input.set_gas_unit_price(100); - input.set_expiration_timestamp_secs(3664390082); - input.set_chain_id(2); - auto privateKey = PrivateKey(parse_hex("7bebb6d543d17f6fe4e685cfab239fa37896edd594ff859f1df32f244fb707e2")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada0000000002"); - ASSERT_EQ(hex(result.authenticator().signature()), "af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f"); - ASSERT_EQ(hex(result.encoded()), "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada00000000020020d1d99b67e37b483161a0fa369c46f34a3be4863c20e20fc7cdc669c0826a411340af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f"); - nlohmann::json expectedJson = R"( - { - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "3296766", - "payload": { - "arguments": [ - "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", - "Topaz Troopers", "Topaz Trooper #20068", "0", "1"], - "function": "0x3::token_transfers::offer_script", - "type": "entry_function_payload", - "type_arguments": [] - }, - "sender": "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", - "sequence_number": "1", - "signature": { - "public_key": "0xd1d99b67e37b483161a0fa369c46f34a3be4863c20e20fc7cdc669c0826a4113", - "signature": "0xaf5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, CancelNftOfferTxSign) { - // Successfully broadcasted https://explorer.aptoslabs.com/txn/0x0b8c64e6847c368e4c6bd2cce0e9eab378971b0ef2e3bc40cbd292910a80201d?network=testnet - Proto::SigningInput input; - input.set_sender("0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - input.set_sequence_number(21); - auto& tf = *input.mutable_nft_message()->mutable_cancel_offer_nft(); - tf.set_receiver("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee"); - tf.set_creator("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac"); - tf.set_collectionname("Topaz Troopers"); - tf.set_name("Topaz Trooper #20068"); - tf.set_property_version(0); - input.set_max_gas_amount(3296766); - input.set_gas_unit_price(100); - input.set_expiration_timestamp_secs(3664390082); - input.set_chain_id(2); - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002"); - ASSERT_EQ(hex(result.authenticator().signature()), "826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a"); - ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a"); - nlohmann::json expectedJson = R"( - { - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "3296766", - "payload": { - "arguments": [ - "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", - "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", - "Topaz Troopers", "Topaz Trooper #20068", "0"], - "function": "0x3::token_transfers::cancel_offer_script", - "type": "entry_function_payload", - "type_arguments": [] - }, - "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "21", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0x826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, TxSign) { - // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb4d62afd3862116e060dd6ad9848ccb50c2bc177799819f1d29c059ae2042467?network=devnet - Proto::SigningInput input; - input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - input.set_sequence_number(99); - auto& tf = *input.mutable_transfer(); - tf.set_to("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - tf.set_amount(1000); - input.set_max_gas_amount(3296766); - input.set_gas_unit_price(100); - input.set_expiration_timestamp_secs(3664390082); - input.set_chain_id(33); - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021"); - ASSERT_EQ(hex(result.authenticator().signature()), "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"); - ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"); - nlohmann::json expectedJson = R"( - { - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "3296766", - "payload": { - "arguments": ["0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], - "function": "0x1::aptos_account::transfer", - "type": "entry_function_payload", - "type_arguments": [] - }, - "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "99", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0x5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, CreateAccount) { - // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x477141736de6b0936a6c3734e4d6fd018c7d21f1f28f99028ef0bc6881168602?network=Devnet - Proto::SigningInput input; - input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - input.set_sequence_number(0); - auto& tf = *input.mutable_create_account(); - tf.set_auth_key("0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e"); - input.set_max_gas_amount(3296766); - input.set_gas_unit_price(100); - input.set_expiration_timestamp_secs(3664390082); - input.set_chain_id(33); - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada0000000021"); - ASSERT_EQ(hex(result.authenticator().signature()), "fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501"); - ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501"); - nlohmann::json expectedJson = R"( - { - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "3296766", - "payload": { - "arguments": ["0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e"], - "function": "0x1::aptos_account::create_account", - "type": "entry_function_payload", - "type_arguments": [] - }, - "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "0", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0xfcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, BlindSignFromJson) { - // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x7efd69e7f9462774b932ce500ab51c0d0dcc004cf272e09f8ffd5804c2a84e33?network=mainnet - auto payloadJson = R"( - { - "function": "0x16fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c::AnimeSwapPoolV1::swap_exact_coins_for_coins_3_pair_entry", - "type_arguments": [ - "0x1::aptos_coin::AptosCoin", - "0x881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f4::coin::MOJO", - "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDT", - "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC" - ], - "arguments": [ - "1000000", - "49329" - ], - "type": "entry_function_payload" - })"_json; - Proto::SigningInput input; - input.set_sequence_number(42); - input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - input.set_gas_unit_price(100); - input.set_max_gas_amount(100011); - input.set_expiration_timestamp_secs(3664390082); - input.set_any_encoded(payloadJson.dump()); - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_chain_id(1); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f302a000000000000000216fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c0f416e696d6553776170506f6f6c563127737761705f65786163745f636f696e735f666f725f636f696e735f335f706169725f656e747279040700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e0007881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f404636f696e044d4f4a4f0007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa05617373657404555344540007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa056173736574045553444300020840420f000000000008b1c0000000000000ab860100000000006400000000000000c2276ada0000000001"); - ASSERT_EQ(hex(result.authenticator().signature()), "42cd67406e85afd1e948e7ad7f5f484fb4c60d82b267c6b6b28a92301e228b983206d2b87cd5487cf9acfb0effbd183ab90123570eb2e047cb152d337152210b"); - ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f302a000000000000000216fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c0f416e696d6553776170506f6f6c563127737761705f65786163745f636f696e735f666f725f636f696e735f335f706169725f656e747279040700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e0007881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f404636f696e044d4f4a4f0007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa05617373657404555344540007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa056173736574045553444300020840420f000000000008b1c0000000000000ab860100000000006400000000000000c2276ada00000000010020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c4042cd67406e85afd1e948e7ad7f5f484fb4c60d82b267c6b6b28a92301e228b983206d2b87cd5487cf9acfb0effbd183ab90123570eb2e047cb152d337152210b"); - nlohmann::json expectedJson = R"( -{ - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "100011", - "payload": { - "function": "0x16fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c::AnimeSwapPoolV1::swap_exact_coins_for_coins_3_pair_entry", - "type_arguments": [ - "0x1::aptos_coin::AptosCoin", - "0x881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f4::coin::MOJO", - "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDT", - "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC" - ], - "arguments": [ - "1000000", - "49329" - ], - "type": "entry_function_payload" - }, - "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "42", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0x42cd67406e85afd1e948e7ad7f5f484fb4c60d82b267c6b6b28a92301e228b983206d2b87cd5487cf9acfb0effbd183ab90123570eb2e047cb152d337152210b", - "type": "ed25519_signature" - } -} - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, TortugaLiquidStakingStake) { - // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x25dca849cb4ebacbff223139f7ad5d24c37c225d9506b8b12a925de70429e685/userTxnOverview?network=mainnet - Proto::SigningInput input; - input.set_sender("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc"); - input.set_sequence_number(19); - auto& ls = *input.mutable_liquid_staking_message(); - ls.set_smart_contract_address("0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f"); - auto& ls_stake = *ls.mutable_stake(); - ls_stake.set_amount(100000000); - input.set_max_gas_amount(5554); - input.set_gas_unit_price(100); - input.set_expiration_timestamp_secs(1670240203); - input.set_chain_id(1); - auto privateKey = PrivateKey(parse_hex("786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - - EXPECT_EQ(hex(result.raw_txn()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1300000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000b2150000000000006400000000000000cbd78d630000000001"); - EXPECT_EQ(hex(result.authenticator().signature()), "22d3166c3003f9c24a35fd39c71eb27e0d2bb82541be610822165c9283f56fefe5a9d46421b9caf174995bd8f83141e60ea8cff521ecf4741fe19e6ae9a5680d"); - EXPECT_EQ(hex(result.encoded()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1300000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000b2150000000000006400000000000000cbd78d630000000001002089e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc44022d3166c3003f9c24a35fd39c71eb27e0d2bb82541be610822165c9283f56fefe5a9d46421b9caf174995bd8f83141e60ea8cff521ecf4741fe19e6ae9a5680d"); - nlohmann::json expectedJson = R"( - { - "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", - "sequence_number": "19", - "max_gas_amount": "5554", - "gas_unit_price": "100", - "expiration_timestamp_secs": "1670240203", - "payload": { - "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::stake", - "type_arguments": [], - "arguments": [ - "100000000" - ], - "type": "entry_function_payload" - }, - "signature": { - "public_key": "0x89e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4", - "signature": "0x22d3166c3003f9c24a35fd39c71eb27e0d2bb82541be610822165c9283f56fefe5a9d46421b9caf174995bd8f83141e60ea8cff521ecf4741fe19e6ae9a5680d", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, TortugaLiquidStakingUnstake) { - // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x92edb4f756fe86118e34a0e64746c70260ee02c2ae2cf402b3e39f6a282ce968?network=mainnet - Proto::SigningInput input; - input.set_sender("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc"); - input.set_sequence_number(20); - auto& ls = *input.mutable_liquid_staking_message(); - ls.set_smart_contract_address("0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f"); - auto& ls_unstake = *ls.mutable_unstake(); - ls_unstake.set_amount(99178100); - input.set_max_gas_amount(2371); - input.set_gas_unit_price(120); - input.set_expiration_timestamp_secs(1670304949); - input.set_chain_id(1); - auto privateKey = PrivateKey(parse_hex("786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - - EXPECT_EQ(hex(result.raw_txn()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1400000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e9050000000043090000000000007800000000000000b5d48e630000000001"); - EXPECT_EQ(hex(result.authenticator().signature()), "6994b917432ad70ae84d2ce1484e6aece589a68aad1b7c6e38c9697f2a012a083a3a755c5e010fd3d0f149a75dd8d257acbd09f10800e890074e5ad384314d0c"); - EXPECT_EQ(hex(result.encoded()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1400000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e9050000000043090000000000007800000000000000b5d48e630000000001002089e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4406994b917432ad70ae84d2ce1484e6aece589a68aad1b7c6e38c9697f2a012a083a3a755c5e010fd3d0f149a75dd8d257acbd09f10800e890074e5ad384314d0c"); - nlohmann::json expectedJson = R"( - { - "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", - "sequence_number": "20", - "max_gas_amount": "2371", - "gas_unit_price": "120", - "expiration_timestamp_secs": "1670304949", - "payload": { - "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::unstake", - "type_arguments": [], - "arguments": [ - "99178100" - ], - "type": "entry_function_payload" - }, - "signature": { - "public_key": "0x89e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4", - "signature": "0x6994b917432ad70ae84d2ce1484e6aece589a68aad1b7c6e38c9697f2a012a083a3a755c5e010fd3d0f149a75dd8d257acbd09f10800e890074e5ad384314d0c", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, TortugaLiquidStakingClaim) { - // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x9fc874de7a7d3e813d9a1658d896023de270a0096a5e258c196005656ace7d54?network=mainnet - Proto::SigningInput input; - input.set_sender("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc"); - input.set_sequence_number(28); - auto& ls = *input.mutable_liquid_staking_message(); - ls.set_smart_contract_address("0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f"); - auto& ls_claim = *ls.mutable_claim(); - ls_claim.set_idx(0); - input.set_max_gas_amount(10); - input.set_gas_unit_price(148); - input.set_expiration_timestamp_secs(1682066783); - input.set_chain_id(1); - auto privateKey = PrivateKey(parse_hex("786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - - EXPECT_EQ(hex(result.raw_txn()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657205636c61696d00010800000000000000000a0000000000000094000000000000005f4d42640000000001"); - EXPECT_EQ(hex(result.authenticator().signature()), "c936584f89777e1fe2d5dd75cd8d9c514efc445810ba22f462b6fe7229c6ec7fc1c8b25d3e233eafaa8306433b3220235e563498ba647be38cac87ff618e3d03"); - EXPECT_EQ(hex(result.encoded()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657205636c61696d00010800000000000000000a0000000000000094000000000000005f4d42640000000001002089e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc440c936584f89777e1fe2d5dd75cd8d9c514efc445810ba22f462b6fe7229c6ec7fc1c8b25d3e233eafaa8306433b3220235e563498ba647be38cac87ff618e3d03"); - nlohmann::json expectedJson = R"( - { - "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", - "sequence_number": "28", - "max_gas_amount": "10", - "gas_unit_price": "148", - "expiration_timestamp_secs": "1682066783", - "payload": { - "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::claim", - "type_arguments": [], - "arguments": [ - "0" - ], - "type": "entry_function_payload" - }, - "signature": { - "public_key": "0x89e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4", - "signature": "0xc936584f89777e1fe2d5dd75cd8d9c514efc445810ba22f462b6fe7229c6ec7fc1c8b25d3e233eafaa8306433b3220235e563498ba647be38cac87ff618e3d03", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, BlindSignStaking) { - // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x25dca849cb4ebacbff223139f7ad5d24c37c225d9506b8b12a925de70429e685/payload - auto payloadJson = R"( - { - "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::stake", - "type_arguments": [], - "arguments": [ - "100000000" - ], - "type": "entry_function_payload" - })"_json; - - Proto::SigningInput input; - input.set_sequence_number(43); - input.set_sender("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc"); - input.set_gas_unit_price(100); - input.set_max_gas_amount(100011); - input.set_expiration_timestamp_secs(3664390082); - input.set_any_encoded(payloadJson.dump()); - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_chain_id(1); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2b00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000ab860100000000006400000000000000c2276ada0000000001"); - ASSERT_EQ(hex(result.authenticator().signature()), "a41b7440a50f36e8491319508734acb55488abc6d88fbc9cb2b37ba23210f01f5d08c856cb7abf18c414cf9302ee144450bd99495a7e21e61f624764db91eb0b"); - ASSERT_EQ(hex(result.encoded()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2b00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000ab860100000000006400000000000000c2276ada00000000010020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40a41b7440a50f36e8491319508734acb55488abc6d88fbc9cb2b37ba23210f01f5d08c856cb7abf18c414cf9302ee144450bd99495a7e21e61f624764db91eb0b"); - nlohmann::json expectedJson = R"( -{ - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "100011", - "payload": { - "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::stake", - "type_arguments": [], - "arguments": [ - "100000000" - ], - "type": "entry_function_payload" - }, - "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", - "sequence_number": "43", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0xa41b7440a50f36e8491319508734acb55488abc6d88fbc9cb2b37ba23210f01f5d08c856cb7abf18c414cf9302ee144450bd99495a7e21e61f624764db91eb0b", - "type": "ed25519_signature" - } -} - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, BlindSignUnStaking) { - // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x92edb4f756fe86118e34a0e64746c70260ee02c2ae2cf402b3e39f6a282ce968/payload - auto payloadJson = R"( - { - "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::unstake", - "type_arguments": [], - "arguments": [ - "99178100" - ], - "type": "entry_function_payload" - })"_json; - Proto::SigningInput input; - input.set_sequence_number(44); - input.set_sender("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc"); - input.set_gas_unit_price(100); - input.set_max_gas_amount(100011); - input.set_expiration_timestamp_secs(3664390082); - input.set_any_encoded(payloadJson.dump()); - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_chain_id(1); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e90500000000ab860100000000006400000000000000c2276ada0000000001"); - ASSERT_EQ(hex(result.authenticator().signature()), "a58ad5e3331beb8c0212a18a1f932207cb664b78f5aad3cb1fe7435e0e0e053247ce49b38fd67b064bed34ed643eb6a03165d77c681d7d73ac3161ab984a960a"); - ASSERT_EQ(hex(result.encoded()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e90500000000ab860100000000006400000000000000c2276ada00000000010020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40a58ad5e3331beb8c0212a18a1f932207cb664b78f5aad3cb1fe7435e0e0e053247ce49b38fd67b064bed34ed643eb6a03165d77c681d7d73ac3161ab984a960a"); - nlohmann::json expectedJson = R"( -{ - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "100011", - "payload": { - "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::unstake", - "type_arguments": [], - "arguments": [ - "99178100" - ], - "type": "entry_function_payload" - }, - "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", - "sequence_number": "44", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0xa58ad5e3331beb8c0212a18a1f932207cb664b78f5aad3cb1fe7435e0e0e053247ce49b38fd67b064bed34ed643eb6a03165d77c681d7d73ac3161ab984a960a", - "type": "ed25519_signature" - } -} - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - - -TEST(AptosSigner, BlindSign) { - // successfully broadcasted https://explorer.aptoslabs.com/txn/0xd95857a9e644528708778a3a0a6e13986751944fca30eaac98853c1655de0422?network=Devnet - // encoded submission with: - // curl --location --request POST 'https://fullnode.devnet.aptoslabs.com/v1/transactions/encode_submission' \ - //--header 'Content-Type: application/json' \ - //--header 'Cookie: AWSALB=0zI2zWypvEr0I3sGM6vnyHSxYO1D0aaMXfyA/2VwhA291aJJ80Yz67Fur50sXPFBI8dKKID4p8DShj1KkEXPY/NGAylpOj1EG2M2Qjuu1B38Q5C+dZW2CHT+IAZ5; AWSALBCORS=0zI2zWypvEr0I3sGM6vnyHSxYO1D0aaMXfyA/2VwhA291aJJ80Yz67Fur50sXPFBI8dKKID4p8DShj1KkEXPY/NGAylpOj1EG2M2Qjuu1B38Q5C+dZW2CHT+IAZ5' \ - //--data-raw '{ - // "expiration_timestamp_secs": "3664390082", - // "gas_unit_price": "100", - // "max_gas_amount": "3296766", - // "payload": { - // "function": "0x4633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b298837::pool::swap_y_to_x", - // "type_arguments": [ - // "0xdeae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e01::devnet_coins::DevnetUSDT", - // "0x1::aptos_coin::AptosCoin" - // ], - // "arguments": [ - // "100000000", - // "0" - // ], - // "type": "entry_function_payload" - // }, - // "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - // "sequence_number": "2" - //}' - Proto::SigningInput input; - input.set_any_encoded("0xb5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b19307968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300200000000000000024633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b29883704706f6f6c0b737761705f795f746f5f780207deae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e010c6465766e65745f636f696e730a4465766e657455534454000700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00020800e1f50500000000080000000000000000fe4d3200000000006400000000000000c2276ada0000000021"); - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b19307968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300200000000000000024633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b29883704706f6f6c0b737761705f795f746f5f780207deae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e010c6465766e65745f636f696e730a4465766e657455534454000700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00020800e1f50500000000080000000000000000fe4d3200000000006400000000000000c2276ada0000000021"); - ASSERT_EQ(hex(result.authenticator().signature()), "9e81026fdd43986f4d5588afdab875cd18b64dc15b3489fcc00ed46fc361915b27e23e0cefe6d23698ee76a562915fe85e99185dbc1dd29ba720f7fad144af0b"); - ASSERT_EQ(hex(result.encoded()), "b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b19307968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300200000000000000024633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b29883704706f6f6c0b737761705f795f746f5f780207deae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e010c6465766e65745f636f696e730a4465766e657455534454000700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00020800e1f50500000000080000000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c409e81026fdd43986f4d5588afdab875cd18b64dc15b3489fcc00ed46fc361915b27e23e0cefe6d23698ee76a562915fe85e99185dbc1dd29ba720f7fad144af0b"); - nlohmann::json expectedJson = R"( - { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0x9e81026fdd43986f4d5588afdab875cd18b64dc15b3489fcc00ed46fc361915b27e23e0cefe6d23698ee76a562915fe85e99185dbc1dd29ba720f7fad144af0b", - "type": "ed25519_signature" - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, TokenRegisterTxSign) { - // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xe591252daed785641bfbbcf72a5d17864568cf32e04c0cc9129f3a13834d0e8e?network=testnet - Proto::SigningInput input; - input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - input.set_sequence_number(23); - auto& tf = *input.mutable_register_token(); - tf.mutable_function()->set_account_address("0xe4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379"); - tf.mutable_function()->set_module("move_coin"); - tf.mutable_function()->set_name("MoveCoin"); - input.set_max_gas_amount(2000000); - input.set_gas_unit_price(100); - input.set_expiration_timestamp_secs(3664390082); - input.set_chain_id(2); - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3017000000000000000200000000000000000000000000000000000000000000000000000000000000010c6d616e616765645f636f696e0872656769737465720107e4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379096d6f76655f636f696e084d6f7665436f696e000080841e00000000006400000000000000c2276ada0000000002"); - ASSERT_EQ(hex(result.authenticator().signature()), "e230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100"); - ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3017000000000000000200000000000000000000000000000000000000000000000000000000000000010c6d616e616765645f636f696e0872656769737465720107e4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379096d6f76655f636f696e084d6f7665436f696e000080841e00000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40e230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100"); - nlohmann::json expectedJson = R"( - { - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "2000000", - "payload": { - "arguments": [], - "function": "0x1::managed_coin::register", - "type": "entry_function_payload", - "type_arguments": ["0xe4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379::move_coin::MoveCoin"] - }, - "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "23", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0xe230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, TokenTxSign) { - // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb5b383a5c7f99b2edb3bed9533f8169a89051b149d65876a82f4c0b9bf78a15b?network=Devnet - Proto::SigningInput input; - input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - input.set_sequence_number(24); - auto& tf = *input.mutable_token_transfer(); - tf.set_to("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - tf.set_amount(100000); - tf.mutable_function()->set_account_address("0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9"); - tf.mutable_function()->set_module("coins"); - tf.mutable_function()->set_name("BTC"); - input.set_max_gas_amount(3296766); - input.set_gas_unit_price(100); - input.set_expiration_timestamp_secs(3664390082); - input.set_chain_id(32); - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada0000000020"); - ASSERT_EQ(hex(result.authenticator().signature()), "7643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a"); - ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada00000000200020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c407643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a"); - nlohmann::json expectedJson = R"( - { - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "3296766", - "payload": { - "arguments": ["0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","100000"], - "function": "0x1::coin::transfer", - "type": "entry_function_payload", - "type_arguments": ["0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC"] - }, - "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "24", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0x7643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, TokenTransferCoins) { - // Successfully broadcasted https://explorer.aptoslabs.com/txn/0x197d40ea12e2bfc65a0a913b9f4ca3b0b0208fe0c1514d3d55cef3d5bcf25211?network=mainnet - Proto::SigningInput input; - input.set_sender("0x1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf25"); - input.set_sequence_number(2); - auto& tf = *input.mutable_token_transfer_coins(); - tf.set_to("0xb7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c"); - tf.set_amount(10000); - tf.mutable_function()->set_account_address("0xe9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9"); - tf.mutable_function()->set_module("mee_coin"); - tf.mutable_function()->set_name("MeeCoin"); - input.set_max_gas_amount(2000); - input.set_gas_unit_price(100); - input.set_expiration_timestamp_secs(3664390082); - input.set_chain_id(1); - auto privateKey = PrivateKey(parse_hex("e7f56c77189e03699a75d8ec5c090e41f3d9d4783bc49c33df8a93d915e10de8")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - - ASSERT_EQ(hex(result.raw_txn()), "1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf2502000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e730107e9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9086d65655f636f696e074d6565436f696e000220b7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c081027000000000000d0070000000000006400000000000000c2276ada0000000001"); - ASSERT_EQ(hex(result.authenticator().signature()), "30ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d"); - ASSERT_EQ(hex(result.encoded()), "1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf2502000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e730107e9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9086d65655f636f696e074d6565436f696e000220b7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c081027000000000000d0070000000000006400000000000000c2276ada0000000001002062e7a6a486553b56a53e89dfae3f780693e537e5b0a7ed33290780e581ca83694030ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d"); - nlohmann::json expectedJson = R"( - { - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "2000", - "payload": { - "arguments": ["0xb7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c","10000"], - "function": "0x1::aptos_account::transfer_coins", - "type": "entry_function_payload", - "type_arguments": ["0xe9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9::mee_coin::MeeCoin"] - }, - "sender": "0x1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf25", - "sequence_number": "2", - "signature": { - "public_key": "0x62e7a6a486553b56a53e89dfae3f780693e537e5b0a7ed33290780e581ca8369", - "signature": "0x30ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -} // namespace TW::Aptos::tests diff --git a/tests/chains/Aptos/TWAnySignerTests.cpp b/tests/chains/Aptos/TWAnySignerTests.cpp index 333e917dd0e..44885c9f872 100644 --- a/tests/chains/Aptos/TWAnySignerTests.cpp +++ b/tests/chains/Aptos/TWAnySignerTests.cpp @@ -4,11 +4,10 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "Aptos/Address.h" -#include "Aptos/Signer.h" #include "HexCoding.h" #include "PrivateKey.h" #include "PublicKey.h" +#include "proto/Aptos.pb.h" #include #include #include "TestUtilities.h" diff --git a/tests/chains/Aptos/TransactionPayloadTests.cpp b/tests/chains/Aptos/TransactionPayloadTests.cpp deleted file mode 100644 index 45b501230c3..00000000000 --- a/tests/chains/Aptos/TransactionPayloadTests.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include -#include -#include - -namespace TW::Aptos::tests { - -TEST(AptosTransactionPayload, PancakeSwapPayload) { - auto pancakeSwapPayload=R"( - { -"arguments": [ - "0xc95db29a67a848940829b3df6119b5e67b788ff0248676e4484c7c6f29c0f5e6" -], -"function": "0xc23c3b70956ce8d88fb18ad9ed3b463fe873cb045db3f6d2e2fb15b9aab71d50::IFO::release", -"type": "entry_function_payload", -"type_arguments": [ - "0x48e0e3958d42b8d452c9199d4a221d0d1b15d14655787453dbe77208ced90517::coins::BUSD", - "0x48e0e3958d42b8d452c9199d4a221d0d1b15d14655787453dbe77208ced90517::coins::DAI", - "0x9936836587ca33240d3d3f91844651b16cb07802faf5e34514ed6f78580deb0a::uints::U1" -] -} -)"_json; - - TransactionPayload payload = EntryFunction::from_json(pancakeSwapPayload); - BCS::Serializer serializer; - Address sender("0x2ce519d8cd60e0870e874e8000e8cbc87c8172e6acdbec83662b4c8cc3fc3de9"); - std::uint64_t sequenceNumber{75}; - std::uint64_t gasAmount{488130}; - std::uint64_t gasPrice{100}; - std::uint64_t expirationTime{199940521552}; - std::uint8_t chainId{1}; - serializer << sender << sequenceNumber << payload << gasAmount << gasPrice << expirationTime << chainId; - ASSERT_EQ(hex(serializer.bytes), "2ce519d8cd60e0870e874e8000e8cbc87c8172e6acdbec83662b4c8cc3fc3de94b0000000000000002c23c3b70956ce8d88fb18ad9ed3b463fe873cb045db3f6d2e2fb15b9aab71d500349464f0772656c65617365030748e0e3958d42b8d452c9199d4a221d0d1b15d14655787453dbe77208ced9051705636f696e730442555344000748e0e3958d42b8d452c9199d4a221d0d1b15d14655787453dbe77208ced9051705636f696e730344414900079936836587ca33240d3d3f91844651b16cb07802faf5e34514ed6f78580deb0a0575696e747302553100012120c95db29a67a848940829b3df6119b5e67b788ff0248676e4484c7c6f29c0f5e6c2720700000000006400000000000000503e628d2e00000001"); -} - -TEST(AptosTransactionPayload, PayLoadBasis) { - ModuleId module(Address::one(), "coin"); - std::uint64_t amount{1000}; - Address to("0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b"); - BCS::Serializer serializer; - serializer << to; - std::vector args; - args.emplace_back(serializer.bytes); - serializer.clear(); - serializer << amount; - args.emplace_back(serializer.bytes); - TransactionPayload payload = EntryFunction(module, "transfer", {gTransferTag}, args); - ASSERT_EQ(std::get(payload).module().name(), "coin"); - ASSERT_EQ(std::get(payload).module().shortString(), "0x1::coin"); - serializer.clear(); - serializer << payload; - ASSERT_EQ(hex(serializer.bytes), "02000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b08e803000000000000"); -} - -} // namespace TW::Aptos::tests diff --git a/tests/common/BCSTests.cpp b/tests/common/BCSTests.cpp deleted file mode 100644 index f12f8242612..00000000000 --- a/tests/common/BCSTests.cpp +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// Created by Clément Doumergue -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "BCS.h" -#include "HexCoding.h" - -#include - -namespace TW::BCS::tests { - -TEST(BCS, Integral) { - Serializer os; - os << uint32_t(0xAABBCCDD); - ASSERT_EQ(os.bytes, parse_hex("0xDDCCBBAA")); - - os.clear(); - os << int32_t(-305419896); - ASSERT_EQ(os.bytes, parse_hex("0x88A9CBED")); -} - -TEST(BCS, ULEB128) { - Serializer os; - os << uleb128{0x00000001}; - ASSERT_EQ(os.bytes, parse_hex("0x01")); - - os.clear(); - os << uleb128{0x00000080}; - ASSERT_EQ(os.bytes, parse_hex("0x8001")); - - os.clear(); - os << uleb128{0x00004000}; - ASSERT_EQ(os.bytes, parse_hex("0x808001")); - - os.clear(); - os << uleb128{0x00200000}; - ASSERT_EQ(os.bytes, parse_hex("0x80808001")); - - os.clear(); - os << uleb128{0x10000000}; - ASSERT_EQ(os.bytes, parse_hex("0x8080808001")); - - os.clear(); - os << uleb128{0x0000250F}; - ASSERT_EQ(os.bytes, parse_hex("0x8F4A")); -} - -TEST(BCS, String) { - Serializer os; - os << std::string_view("abcd"); - ASSERT_EQ(os.bytes, parse_hex("0x0461626364")); - - os.clear(); - os << std::string_view(""); - ASSERT_EQ(os.bytes, parse_hex("0x00")); -} - -TEST(BCS, Optional) { - Serializer os; - os << std::optional{0xBBCCDD}; - ASSERT_EQ(os.bytes, parse_hex("0x01DDCCBB00")); - - os.clear(); - os << std::optional{}; - ASSERT_EQ(os.bytes, parse_hex("0x00")); - - os.clear(); - os << std::nullopt; - ASSERT_EQ(os.bytes, parse_hex("0x00")); -} - -TEST(BCS, Tuple) { - Serializer os; - os << std::tuple{uint16_t(1), 'a'}; - ASSERT_EQ(os.bytes, parse_hex("0x010061")); - - os.clear(); - os << std::tuple{std::optional{123}, std::string_view("abcd"), uint8_t(0x0E)}; - ASSERT_EQ(os.bytes, parse_hex("0x017b00000004616263640e")); - - os.clear(); - os << std::tuple{}; - ASSERT_EQ(os.bytes, (Data{})); -} - -TEST(BCS, Pair) { - Serializer os; - os << std::pair{uint16_t(1), 'a'}; - ASSERT_EQ(os.bytes, parse_hex("0x010061")); - - os.clear(); - os << std::pair{std::optional{123}, std::string_view("abcd")}; - ASSERT_EQ(os.bytes, parse_hex("0x017b0000000461626364")); -} - -struct my_struct { - std::optional first; - std::string_view second; - uint8_t third; -}; - -TEST(BCS, Struct) { - Serializer os; - os << my_struct{{123}, "abcd", 0x0E}; - ASSERT_EQ(os.bytes, parse_hex("0x017b00000004616263640e")); -} - -TEST(BCS, Variant) { - using V = std::variant; - - Serializer os; - os << V{uint32_t(1)}; - ASSERT_EQ(os.bytes, parse_hex("0x0001000000")); - - os.clear(); - os << V{char('a')}; - ASSERT_EQ(os.bytes, parse_hex("0x0161")); - - os.clear(); - os << V{true}; - ASSERT_EQ(os.bytes, parse_hex("0x0201")); -} - -TEST(BCS, Map) { - Serializer os; - os << std::map{{'a', 0}, {'b', 1}, {'c', 2}}; - ASSERT_EQ(os.bytes, parse_hex("0x03610062016302")); -} - -class my_number { -private: - int value; - -public: - explicit my_number(int value) noexcept - : value(value) { - } - - [[nodiscard]] auto get_value() const { - return value; - } -}; - -Serializer& operator<<(Serializer& stream, my_number n) noexcept { - return stream << n.get_value(); -} - -static_assert(CustomSerializable, "my_number does not model the CustomSerializable concept"); - -TEST(BCS, Custom) { - Serializer os; - os << my_number{0xBBCCDD}; - ASSERT_EQ(os.bytes, parse_hex("0xDDCCBB00")); -} - -TEST(BCS, Vector) { - Serializer os; - os << std::vector{1}; - ASSERT_EQ(os.bytes, parse_hex("0101")); -} - -} diff --git a/tests/common/rust/bindgen/WalletCoreRsTests.cpp b/tests/common/rust/bindgen/WalletCoreRsTests.cpp index 402fbfb6050..bd5cd298a41 100644 --- a/tests/common/rust/bindgen/WalletCoreRsTests.cpp +++ b/tests/common/rust/bindgen/WalletCoreRsTests.cpp @@ -13,15 +13,6 @@ #include "proto/Polkadot.pb.h" #include "uint256.h" -TEST(RustBindgen, MoveParseFunctionArgument) { - using namespace TW; - std::string arg = "10000000"; - auto str_result = Rust::parse_function_argument_to_bcs(arg.c_str()); - ASSERT_EQ(str_result.code, Rust::OK_CODE); - ASSERT_EQ(std::string(str_result.result), "8096980000000000"); - Rust::free_string(str_result.result); -} - TEST(RustBindgen, EthSigningMessageProto) { using namespace TW; From 025061562c0083e4300ccc07d7426c5d2472cbf9 Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 14 Nov 2023 17:58:57 -0500 Subject: [PATCH 52/60] feat(aptos): be able to handle non string arguments --- rust/tw_aptos/src/transaction_payload.rs | 27 ++++++++++++++++++------ 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/rust/tw_aptos/src/transaction_payload.rs b/rust/tw_aptos/src/transaction_payload.rs index fdb43314563..ea44ba50af5 100644 --- a/rust/tw_aptos/src/transaction_payload.rs +++ b/rust/tw_aptos/src/transaction_payload.rs @@ -42,10 +42,10 @@ impl TryFrom for EntryFunction { .ok_or_else(|| anyhow!("Arguments field is not an array or is missing"))? .iter() .map(|element| { - let arg_str = element - .as_str() - .ok_or_else(|| anyhow!("Invalid argument string"))?; - let arg = parse_transaction_argument(arg_str)?; + let arg_str = element.to_string(); + let arg = parse_transaction_argument( + arg_str.trim_start_matches('"').trim_end_matches('"'), + )?; serialize_argument(&arg) }) .collect::>>>()?; @@ -85,9 +85,9 @@ fn serialize_argument(arg: &TransactionArgument) -> Result> { TransactionArgument::Address(v) => { let serialized_v = bcs::to_bytes(v)?; bcs::to_bytes(&serialized_v) - }, + } } - .map_err(|e| anyhow!(e)) + .map_err(|e| anyhow!(e)) } pub fn convert_proto_struct_tag_to_type_tag(struct_tag: Aptos::Proto::StructTag) -> TypeTag { @@ -95,7 +95,7 @@ pub fn convert_proto_struct_tag_to_type_tag(struct_tag: Aptos::Proto::StructTag) "{}::{}::{}", struct_tag.account_address, struct_tag.module, struct_tag.name )) - .unwrap() + .unwrap() } pub fn convert_type_tag_to_struct_tag(type_tag: TypeTag) -> Aptos::Proto::StructTag<'static> { @@ -194,6 +194,19 @@ mod tests { assert_eq!(payload_value, v.to_json()); } + #[test] + fn test_payload_from_json_with_arg_non_str() { + let payload_value: Value = json!({ + "type":"entry_function_payload", + "function":"0xd11107bdf0d6d7040c6c0bfbdecb6545191fdf13e8d8d259952f53e1713f61b5::ditto_staking::stake_aptos", + "type_arguments":[], + "arguments": [1000000] + }); + + let v = EntryFunction::try_from(payload_value.clone()).unwrap(); + assert_eq!(payload_value, v.to_json()); + } + #[test] fn test_basic_payload() { let addr = From b7038e02776b479e17cf2a94784ac434415bcd06 Mon Sep 17 00:00:00 2001 From: Milerius Date: Tue, 14 Nov 2023 18:17:17 -0500 Subject: [PATCH 53/60] feat(aptos): cargo fmt --- rust/tw_aptos/src/transaction_payload.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/tw_aptos/src/transaction_payload.rs b/rust/tw_aptos/src/transaction_payload.rs index ea44ba50af5..a9cd7c48ca7 100644 --- a/rust/tw_aptos/src/transaction_payload.rs +++ b/rust/tw_aptos/src/transaction_payload.rs @@ -85,9 +85,9 @@ fn serialize_argument(arg: &TransactionArgument) -> Result> { TransactionArgument::Address(v) => { let serialized_v = bcs::to_bytes(v)?; bcs::to_bytes(&serialized_v) - } + }, } - .map_err(|e| anyhow!(e)) + .map_err(|e| anyhow!(e)) } pub fn convert_proto_struct_tag_to_type_tag(struct_tag: Aptos::Proto::StructTag) -> TypeTag { @@ -95,7 +95,7 @@ pub fn convert_proto_struct_tag_to_type_tag(struct_tag: Aptos::Proto::StructTag) "{}::{}::{}", struct_tag.account_address, struct_tag.module, struct_tag.name )) - .unwrap() + .unwrap() } pub fn convert_type_tag_to_struct_tag(type_tag: TypeTag) -> Aptos::Proto::StructTag<'static> { From 701de511c98d134348378eabd690b83307fce063 Mon Sep 17 00:00:00 2001 From: Satoshi Otomakan Date: Wed, 15 Nov 2023 14:02:07 +0100 Subject: [PATCH 54/60] [Aptos]: Add `tw_any_signer_sign` and `tw_transaction_compiler_*` tests --- rust/Cargo.lock | 4 + rust/tw_any_coin/Cargo.toml | 3 + rust/tw_any_coin/tests/chain_tests.rs | 7 ++ .../tests/chains/aptos/aptos_compile.rs | 86 +++++++++++++++++++ .../tests/chains/aptos/aptos_sign.rs | 43 ++++++++++ rust/tw_any_coin/tests/chains/aptos/mod.rs | 11 +++ .../tests/chains/aptos/test_cases.rs | 64 ++++++++++++++ rust/tw_any_coin/tests/chains/mod.rs | 7 ++ rust/tw_misc/Cargo.toml | 5 ++ rust/tw_misc/src/lib.rs | 2 + rust/tw_misc/src/test_utils/json.rs | 44 ++++++++++ rust/tw_misc/src/test_utils/mod.rs | 7 ++ 12 files changed, 283 insertions(+) create mode 100644 rust/tw_any_coin/tests/chain_tests.rs create mode 100644 rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs create mode 100644 rust/tw_any_coin/tests/chains/aptos/aptos_sign.rs create mode 100644 rust/tw_any_coin/tests/chains/aptos/mod.rs create mode 100644 rust/tw_any_coin/tests/chains/aptos/test_cases.rs create mode 100644 rust/tw_any_coin/tests/chains/mod.rs create mode 100644 rust/tw_misc/src/test_utils/json.rs create mode 100644 rust/tw_misc/src/test_utils/mod.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 6e26a83ba99..4d77a57536e 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1594,6 +1594,8 @@ dependencies = [ name = "tw_any_coin" version = "0.1.0" dependencies = [ + "serde", + "serde_json", "tw_any_coin", "tw_coin_entry", "tw_coin_registry", @@ -1790,6 +1792,8 @@ version = "0.1.0" name = "tw_misc" version = "0.1.0" dependencies = [ + "serde", + "serde_json", "zeroize", ] diff --git a/rust/tw_any_coin/Cargo.toml b/rust/tw_any_coin/Cargo.toml index 7ea537e73c2..17ba71621f1 100644 --- a/rust/tw_any_coin/Cargo.toml +++ b/rust/tw_any_coin/Cargo.toml @@ -14,9 +14,12 @@ tw_misc = { path = "../tw_misc" } test-utils = [] [dev-dependencies] +serde = { version = "1.0.163", features = ["derive"] } +serde_json = { version = "1.0.96" } tw_any_coin = { path = "./", features = ["test-utils"] } tw_encoding = { path = "../tw_encoding" } tw_keypair = { path = "../tw_keypair", features = ["test-utils"] } tw_memory = { path = "../tw_memory", features = ["test-utils"] } +tw_misc = { path = "../tw_misc", features = ["test-utils"] } tw_number = { path = "../tw_number" } tw_proto = { path = "../tw_proto" } diff --git a/rust/tw_any_coin/tests/chain_tests.rs b/rust/tw_any_coin/tests/chain_tests.rs new file mode 100644 index 00000000000..c1b24a82062 --- /dev/null +++ b/rust/tw_any_coin/tests/chain_tests.rs @@ -0,0 +1,7 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +mod chains; diff --git a/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs b/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs new file mode 100644 index 00000000000..9f21f68d317 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs @@ -0,0 +1,86 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::chains::aptos::test_cases::transfer_b4d62afd::{ + aptos_sign_transfer_input, expected_json, DATA_TO_SIGN, ENCODED, PRIVATE_KEY, RAW_TXN, + SIGNATURE, +}; +use crate::chains::aptos::APTOS_COIN_TYPE; +use tw_any_coin::ffi::tw_transaction_compiler::{ + tw_transaction_compiler_compile, tw_transaction_compiler_pre_image_hashes, +}; +use tw_coin_entry::error::SigningErrorType; +use tw_encoding::hex::ToHex; +use tw_keypair::ed25519; +use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_memory::test_utils::tw_data_vector_helper::TWDataVectorHelper; +use tw_misc::assert_eq_json; +use tw_misc::traits::ToBytesVec; +use tw_proto::Aptos::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; +use tw_proto::{deserialize, serialize}; + +#[test] +fn test_any_signer_sign_aptos() { + let input = aptos_sign_transfer_input(); + + // Step 2: Obtain preimage hash + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + let preimage_data = TWDataHelper::wrap(unsafe { + tw_transaction_compiler_pre_image_hashes(APTOS_COIN_TYPE, input_data.ptr()) + }) + .to_vec() + .expect("!tw_transaction_compiler_pre_image_hashes returned nullptr"); + + let preimage: CompilerProto::PreSigningOutput = + deserialize(&preimage_data).expect("Coin entry returned an invalid output"); + + assert_eq!(preimage.error, SigningErrorType::OK); + assert!(preimage.error_message.is_empty()); + assert_eq!(preimage.data.to_hex(), DATA_TO_SIGN); + + // Step 3: Sign the data "externally" + + let private_key = ed25519::sha512::KeyPair::try_from(PRIVATE_KEY).unwrap(); + let public_key = private_key.public().to_vec(); + + let signature = private_key + .sign(preimage.data.to_vec()) + .expect("Error signing data") + .to_vec(); + assert_eq!(signature.to_hex(), SIGNATURE); + + // Step 4: Compile transaction info + + let signatures = TWDataVectorHelper::create([signature]); + let public_keys = TWDataVectorHelper::create([public_key]); + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + let output_data = TWDataHelper::wrap(unsafe { + tw_transaction_compiler_compile( + APTOS_COIN_TYPE, + input_data.ptr(), + signatures.ptr(), + public_keys.ptr(), + ) + }) + .to_vec() + .expect("!tw_transaction_compiler_compile returned nullptr"); + + let output: Proto::SigningOutput = + deserialize(&output_data).expect("Coin entry returned an invalid output"); + + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let authenticator = output.authenticator.unwrap(); + assert_eq!(authenticator.signature.to_hex(), SIGNATURE); + assert_eq!(output.raw_txn.to_hex(), RAW_TXN); + assert_eq!(output.encoded.to_hex(), ENCODED); + + assert_eq_json!(output.json, expected_json()); +} diff --git a/rust/tw_any_coin/tests/chains/aptos/aptos_sign.rs b/rust/tw_any_coin/tests/chains/aptos/aptos_sign.rs new file mode 100644 index 00000000000..6021ce8bb92 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/aptos/aptos_sign.rs @@ -0,0 +1,43 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::chains::aptos::test_cases::transfer_b4d62afd::{ + aptos_sign_transfer_input, expected_json, ENCODED, PRIVATE_KEY, RAW_TXN, SIGNATURE, +}; +use crate::chains::aptos::APTOS_COIN_TYPE; +use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; +use tw_coin_entry::error::SigningErrorType; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_misc::assert_eq_json; +use tw_proto::Aptos::Proto; +use tw_proto::{deserialize, serialize}; + +#[test] +fn test_any_signer_sign_aptos() { + let input = Proto::SigningInput { + private_key: PRIVATE_KEY.decode_hex().unwrap().into(), + ..aptos_sign_transfer_input() + }; + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + let output = + TWDataHelper::wrap(unsafe { tw_any_signer_sign(input_data.ptr(), APTOS_COIN_TYPE) }) + .to_vec() + .expect("!tw_any_signer_sign returned nullptr"); + + let output: Proto::SigningOutput = deserialize(&output).unwrap(); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let authenticator = output.authenticator.unwrap(); + assert_eq!(authenticator.signature.to_hex(), SIGNATURE); + assert_eq!(output.raw_txn.to_hex(), RAW_TXN); + assert_eq!(output.encoded.to_hex(), ENCODED); + + assert_eq_json!(output.json, expected_json()); +} diff --git a/rust/tw_any_coin/tests/chains/aptos/mod.rs b/rust/tw_any_coin/tests/chains/aptos/mod.rs new file mode 100644 index 00000000000..0e046226232 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/aptos/mod.rs @@ -0,0 +1,11 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +mod aptos_compile; +mod aptos_sign; +mod test_cases; + +const APTOS_COIN_TYPE: u32 = 637; diff --git a/rust/tw_any_coin/tests/chains/aptos/test_cases.rs b/rust/tw_any_coin/tests/chains/aptos/test_cases.rs new file mode 100644 index 00000000000..81b166f7247 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/aptos/test_cases.rs @@ -0,0 +1,64 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde_json::{json, Value as Json}; +use tw_proto::Aptos::Proto; +use tw_proto::Aptos::Proto::mod_SigningInput::OneOftransaction_payload as TransactionPayloadEnum; + +pub(super) mod transfer_b4d62afd { + use super::*; + + /// Expected private key. + pub const PRIVATE_KEY: &str = + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec"; + pub const ENCODED: &str = "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"; + /// Expected `raw_txn`. + pub const RAW_TXN: &str = "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021"; + /// Expected signature. + pub const SIGNATURE: &str = "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"; + /// Expected preimage data to be signed. + pub const DATA_TO_SIGN: &str = "b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b19307968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021"; + + pub fn aptos_sign_transfer_input() -> Proto::SigningInput<'static> { + let transfer = Proto::TransferMessage { + to: "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30".into(), + amount: 1000, + }; + + Proto::SigningInput { + sender: "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30".into(), + sequence_number: 99, + max_gas_amount: 3296766, + gas_unit_price: 100, + expiration_timestamp_secs: 3664390082, + chain_id: 33, + any_encoded: Default::default(), + transaction_payload: TransactionPayloadEnum::transfer(transfer), + ..Proto::SigningInput::default() + } + } + + pub fn expected_json() -> Json { + json!({ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": ["0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], + "function": "0x1::aptos_account::transfer", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "99", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", + "type": "ed25519_signature" + } + }) + } +} diff --git a/rust/tw_any_coin/tests/chains/mod.rs b/rust/tw_any_coin/tests/chains/mod.rs new file mode 100644 index 00000000000..fa5b3a9718c --- /dev/null +++ b/rust/tw_any_coin/tests/chains/mod.rs @@ -0,0 +1,7 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +mod aptos; diff --git a/rust/tw_misc/Cargo.toml b/rust/tw_misc/Cargo.toml index ac5867c20dc..60fe9a1033d 100644 --- a/rust/tw_misc/Cargo.toml +++ b/rust/tw_misc/Cargo.toml @@ -3,5 +3,10 @@ name = "tw_misc" version = "0.1.0" edition = "2021" +[features] +test-utils = ["serde", "serde_json"] + [dependencies] +serde = { version = "1.0.163", features = ["derive"], optional = true } +serde_json = { version = "1.0.96", optional = true } zeroize = "1.6.0" diff --git a/rust/tw_misc/src/lib.rs b/rust/tw_misc/src/lib.rs index 1f73752d7a1..314dedda6dc 100644 --- a/rust/tw_misc/src/lib.rs +++ b/rust/tw_misc/src/lib.rs @@ -1,2 +1,4 @@ pub mod macros; +#[cfg(feature = "test-utils")] +pub mod test_utils; pub mod traits; diff --git a/rust/tw_misc/src/test_utils/json.rs b/rust/tw_misc/src/test_utils/json.rs new file mode 100644 index 00000000000..bcab8c3f6d3 --- /dev/null +++ b/rust/tw_misc/src/test_utils/json.rs @@ -0,0 +1,44 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde_json::Value as Json; +use std::borrow::Cow; + +pub trait ToJson { + fn to_json(&self) -> Json; +} + +impl ToJson for Json { + #[track_caller] + fn to_json(&self) -> Json { + self.clone() + } +} + +impl<'a> ToJson for Cow<'a, str> { + #[track_caller] + fn to_json(&self) -> Json { + self.as_ref().to_json() + } +} + +impl<'a> ToJson for &'a str { + #[track_caller] + fn to_json(&self) -> Json { + serde_json::from_str(self).expect("Error on deserializing JSON from string") + } +} + +#[macro_export] +macro_rules! assert_eq_json { + ($left:expr, $right:expr) => {{ + use $crate::test_utils::json::ToJson; + + let left = $left.to_json(); + let right = $right.to_json(); + assert_eq!(left, right); + }}; +} diff --git a/rust/tw_misc/src/test_utils/mod.rs b/rust/tw_misc/src/test_utils/mod.rs new file mode 100644 index 00000000000..019606a0fca --- /dev/null +++ b/rust/tw_misc/src/test_utils/mod.rs @@ -0,0 +1,7 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod json; From 70b01074767658eda4fba6369e8b93eafd2b1427 Mon Sep 17 00:00:00 2001 From: Satoshi Otomakan Date: Wed, 15 Nov 2023 15:55:49 +0100 Subject: [PATCH 55/60] [Aptos]: Error handling --- rust/tw_aptos/src/aptos_move_packages.rs | 91 +++++++++++++----------- rust/tw_aptos/src/bcs_encoding.rs | 35 +++++++++ rust/tw_aptos/src/lib.rs | 1 + rust/tw_aptos/src/liquid_staking.rs | 73 ++++++++++++------- rust/tw_aptos/src/nft.rs | 35 +++++---- rust/tw_aptos/src/transaction.rs | 26 +------ rust/tw_aptos/src/transaction_builder.rs | 72 ++++++++++--------- rust/tw_aptos/src/transaction_payload.rs | 83 +++++++++++++-------- rust/tw_coin_entry/src/error.rs | 3 +- 9 files changed, 249 insertions(+), 170 deletions(-) create mode 100644 rust/tw_aptos/src/bcs_encoding.rs diff --git a/rust/tw_aptos/src/aptos_move_packages.rs b/rust/tw_aptos/src/aptos_move_packages.rs index 83898b122f2..06674c2755e 100644 --- a/rust/tw_aptos/src/aptos_move_packages.rs +++ b/rust/tw_aptos/src/aptos_move_packages.rs @@ -4,14 +4,19 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use crate::bcs_encoding; use crate::transaction_payload::{EntryFunction, TransactionPayload}; use move_core_types::account_address::AccountAddress; use move_core_types::ident_str; use move_core_types::language_storage::{ModuleId, TypeTag}; use serde_json::json; +use tw_coin_entry::error::SigningResult; -pub fn aptos_account_transfer(to: AccountAddress, amount: u64) -> TransactionPayload { - TransactionPayload::EntryFunction(EntryFunction::new( +pub fn aptos_account_transfer( + to: AccountAddress, + amount: u64, +) -> SigningResult { + Ok(TransactionPayload::EntryFunction(EntryFunction::new( ModuleId::new( AccountAddress::new([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -21,13 +26,13 @@ pub fn aptos_account_transfer(to: AccountAddress, amount: u64) -> TransactionPay ), ident_str!("transfer").to_owned(), vec![], - vec![bcs::to_bytes(&to).unwrap(), bcs::to_bytes(&amount).unwrap()], + vec![bcs_encoding::encode(&to)?, bcs_encoding::encode(&amount)?], json!([to.to_hex_literal(), amount.to_string()]), - )) + ))) } -pub fn aptos_account_create_account(auth_key: AccountAddress) -> TransactionPayload { - TransactionPayload::EntryFunction(EntryFunction::new( +pub fn aptos_account_create_account(auth_key: AccountAddress) -> SigningResult { + Ok(TransactionPayload::EntryFunction(EntryFunction::new( ModuleId::new( AccountAddress::new([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -37,13 +42,17 @@ pub fn aptos_account_create_account(auth_key: AccountAddress) -> TransactionPayl ), ident_str!("create_account").to_owned(), vec![], - vec![bcs::to_bytes(&auth_key).unwrap()], + vec![bcs_encoding::encode(&auth_key)?], json!([auth_key.to_hex_literal()]), - )) + ))) } -pub fn coin_transfer(coin_type: TypeTag, to: AccountAddress, amount: u64) -> TransactionPayload { - TransactionPayload::EntryFunction(EntryFunction::new( +pub fn coin_transfer( + coin_type: TypeTag, + to: AccountAddress, + amount: u64, +) -> SigningResult { + Ok(TransactionPayload::EntryFunction(EntryFunction::new( ModuleId::new( AccountAddress::new([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -53,17 +62,17 @@ pub fn coin_transfer(coin_type: TypeTag, to: AccountAddress, amount: u64) -> Tra ), ident_str!("transfer").to_owned(), vec![coin_type], - vec![bcs::to_bytes(&to).unwrap(), bcs::to_bytes(&amount).unwrap()], + vec![bcs_encoding::encode(&to)?, bcs_encoding::encode(&amount)?], json!([to.to_hex_literal(), amount.to_string()]), - )) + ))) } pub fn aptos_account_transfer_coins( coin_type: TypeTag, to: AccountAddress, amount: u64, -) -> TransactionPayload { - TransactionPayload::EntryFunction(EntryFunction::new( +) -> SigningResult { + Ok(TransactionPayload::EntryFunction(EntryFunction::new( ModuleId::new( AccountAddress::new([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -73,9 +82,9 @@ pub fn aptos_account_transfer_coins( ), ident_str!("transfer_coins").to_owned(), vec![coin_type], - vec![bcs::to_bytes(&to).unwrap(), bcs::to_bytes(&amount).unwrap()], + vec![bcs_encoding::encode(&to)?, bcs_encoding::encode(&amount)?], json!([to.to_hex_literal(), amount.to_string()]), - )) + ))) } pub fn token_transfers_offer_script( @@ -85,8 +94,8 @@ pub fn token_transfers_offer_script( name: Vec, property_version: u64, amount: u64, -) -> TransactionPayload { - TransactionPayload::EntryFunction(EntryFunction::new( +) -> SigningResult { + Ok(TransactionPayload::EntryFunction(EntryFunction::new( ModuleId::new( AccountAddress::new([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -97,12 +106,12 @@ pub fn token_transfers_offer_script( ident_str!("offer_script").to_owned(), vec![], vec![ - bcs::to_bytes(&receiver).unwrap(), - bcs::to_bytes(&creator).unwrap(), - bcs::to_bytes(&collection).unwrap(), - bcs::to_bytes(&name).unwrap(), - bcs::to_bytes(&property_version).unwrap(), - bcs::to_bytes(&amount).unwrap(), + bcs_encoding::encode(&receiver)?, + bcs_encoding::encode(&creator)?, + bcs_encoding::encode(&collection)?, + bcs_encoding::encode(&name)?, + bcs_encoding::encode(&property_version)?, + bcs_encoding::encode(&amount)?, ], json!([ receiver.to_hex_literal(), @@ -112,7 +121,7 @@ pub fn token_transfers_offer_script( property_version.to_string(), amount.to_string() ]), - )) + ))) } pub fn token_transfers_cancel_offer_script( @@ -121,8 +130,8 @@ pub fn token_transfers_cancel_offer_script( collection: Vec, name: Vec, property_version: u64, -) -> TransactionPayload { - TransactionPayload::EntryFunction(EntryFunction::new( +) -> SigningResult { + Ok(TransactionPayload::EntryFunction(EntryFunction::new( ModuleId::new( AccountAddress::new([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -133,11 +142,11 @@ pub fn token_transfers_cancel_offer_script( ident_str!("cancel_offer_script").to_owned(), vec![], vec![ - bcs::to_bytes(&receiver).unwrap(), - bcs::to_bytes(&creator).unwrap(), - bcs::to_bytes(&collection).unwrap(), - bcs::to_bytes(&name).unwrap(), - bcs::to_bytes(&property_version).unwrap(), + bcs_encoding::encode(&receiver)?, + bcs_encoding::encode(&creator)?, + bcs_encoding::encode(&collection)?, + bcs_encoding::encode(&name)?, + bcs_encoding::encode(&property_version)?, ], json!([ receiver.to_hex_literal(), @@ -146,7 +155,7 @@ pub fn token_transfers_cancel_offer_script( String::from_utf8_lossy(&name), property_version.to_string() ]), - )) + ))) } pub fn token_transfers_claim_script( @@ -155,8 +164,8 @@ pub fn token_transfers_claim_script( collection: Vec, name: Vec, property_version: u64, -) -> TransactionPayload { - TransactionPayload::EntryFunction(EntryFunction::new( +) -> SigningResult { + Ok(TransactionPayload::EntryFunction(EntryFunction::new( ModuleId::new( AccountAddress::new([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -167,11 +176,11 @@ pub fn token_transfers_claim_script( ident_str!("claim_script").to_owned(), vec![], vec![ - bcs::to_bytes(&sender).unwrap(), - bcs::to_bytes(&creator).unwrap(), - bcs::to_bytes(&collection).unwrap(), - bcs::to_bytes(&name).unwrap(), - bcs::to_bytes(&property_version).unwrap(), + bcs_encoding::encode(&sender)?, + bcs_encoding::encode(&creator)?, + bcs_encoding::encode(&collection)?, + bcs_encoding::encode(&name)?, + bcs_encoding::encode(&property_version)?, ], json!([ sender.to_hex_literal(), @@ -180,7 +189,7 @@ pub fn token_transfers_claim_script( String::from_utf8_lossy(&name), property_version.to_string() ]), - )) + ))) } pub fn managed_coin_register(coin_type: TypeTag) -> TransactionPayload { diff --git a/rust/tw_aptos/src/bcs_encoding.rs b/rust/tw_aptos/src/bcs_encoding.rs new file mode 100644 index 00000000000..f04cb3d4608 --- /dev/null +++ b/rust/tw_aptos/src/bcs_encoding.rs @@ -0,0 +1,35 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde::Serialize; +use tw_coin_entry::error::{SigningError, SigningErrorType}; +use tw_memory::Data; + +pub type BcsEncodingResult = Result; + +#[derive(Debug)] +pub struct BcsEncodingError(bcs::Error); + +impl From for BcsEncodingError { + fn from(error: bcs::Error) -> BcsEncodingError { + BcsEncodingError(error) + } +} + +impl From for SigningError { + fn from(_: BcsEncodingError) -> Self { + // `bcs` is used to encode internal structures. + // It's an internal error. + SigningError(SigningErrorType::Error_internal) + } +} + +pub fn encode(value: &T) -> BcsEncodingResult +where + T: ?Sized + Serialize, +{ + bcs::to_bytes(value).map_err(BcsEncodingError::from) +} diff --git a/rust/tw_aptos/src/lib.rs b/rust/tw_aptos/src/lib.rs index b85498f42c2..a4f4e9c18f3 100644 --- a/rust/tw_aptos/src/lib.rs +++ b/rust/tw_aptos/src/lib.rs @@ -12,6 +12,7 @@ mod serde_helper; pub mod nft; +pub mod bcs_encoding; pub mod compiler; pub mod liquid_staking; pub mod signer; diff --git a/rust/tw_aptos/src/liquid_staking.rs b/rust/tw_aptos/src/liquid_staking.rs index 6b6a11197a6..99b704a8419 100644 --- a/rust/tw_aptos/src/liquid_staking.rs +++ b/rust/tw_aptos/src/liquid_staking.rs @@ -4,52 +4,63 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use crate::bcs_encoding; use crate::transaction_payload::{EntryFunction, TransactionPayload}; use move_core_types::{account_address::AccountAddress, ident_str, language_storage::ModuleId}; use serde_json::json; use std::str::FromStr; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_proto::{ Aptos::Proto::mod_LiquidStaking::OneOfliquid_stake_transaction_payload, Aptos::Proto::{LiquidStaking, TortugaClaim, TortugaStake, TortugaUnstake}, }; -pub fn tortuga_stake(smart_contract_address: AccountAddress, amount: u64) -> TransactionPayload { - TransactionPayload::EntryFunction(EntryFunction::new( +pub fn tortuga_stake( + smart_contract_address: AccountAddress, + amount: u64, +) -> SigningResult { + Ok(TransactionPayload::EntryFunction(EntryFunction::new( ModuleId::new( smart_contract_address, ident_str!("stake_router").to_owned(), ), ident_str!("stake").to_owned(), vec![], - vec![bcs::to_bytes(&amount).unwrap()], + vec![bcs_encoding::encode(&amount)?], json!([amount.to_string()]), - )) + ))) } -pub fn tortuga_unstake(smart_contract_address: AccountAddress, amount: u64) -> TransactionPayload { - TransactionPayload::EntryFunction(EntryFunction::new( +pub fn tortuga_unstake( + smart_contract_address: AccountAddress, + amount: u64, +) -> SigningResult { + Ok(TransactionPayload::EntryFunction(EntryFunction::new( ModuleId::new( smart_contract_address, ident_str!("stake_router").to_owned(), ), ident_str!("unstake").to_owned(), vec![], - vec![bcs::to_bytes(&amount).unwrap()], + vec![bcs_encoding::encode(&amount)?], json!([amount.to_string()]), - )) + ))) } -pub fn tortuga_claim(smart_contract_address: AccountAddress, idx: u64) -> TransactionPayload { - TransactionPayload::EntryFunction(EntryFunction::new( +pub fn tortuga_claim( + smart_contract_address: AccountAddress, + idx: u64, +) -> SigningResult { + Ok(TransactionPayload::EntryFunction(EntryFunction::new( ModuleId::new( smart_contract_address, ident_str!("stake_router").to_owned(), ), ident_str!("claim").to_owned(), vec![], - vec![bcs::to_bytes(&idx).unwrap()], + vec![bcs_encoding::encode(&idx)?], json!([idx.to_string()]), - )) + ))) } pub struct Stake { @@ -73,32 +84,40 @@ pub enum LiquidStakingOperation { Claim(Claim), } -impl From> for LiquidStakingOperation { - fn from(value: LiquidStaking) -> Self { +impl TryFrom> for LiquidStakingOperation { + type Error = SigningError; + + fn try_from(value: LiquidStaking) -> SigningResult { match value.liquid_stake_transaction_payload { OneOfliquid_stake_transaction_payload::stake(stake_msg) => { - LiquidStakingOperation::Stake(Stake { + let smart_contract_address = + AccountAddress::from_str(&value.smart_contract_address) + .map_err(|_| SigningError(SigningErrorType::Error_invalid_address))?; + Ok(LiquidStakingOperation::Stake(Stake { amount: stake_msg.amount, - smart_contract_address: AccountAddress::from_str(&value.smart_contract_address) - .unwrap(), - }) + smart_contract_address, + })) }, OneOfliquid_stake_transaction_payload::unstake(unstake_msg) => { - LiquidStakingOperation::Unstake(Unstake { + let smart_contract_address = + AccountAddress::from_str(&value.smart_contract_address) + .map_err(|_| SigningError(SigningErrorType::Error_invalid_address))?; + Ok(LiquidStakingOperation::Unstake(Unstake { amount: unstake_msg.amount, - smart_contract_address: AccountAddress::from_str(&value.smart_contract_address) - .unwrap(), - }) + smart_contract_address, + })) }, OneOfliquid_stake_transaction_payload::claim(claim) => { - LiquidStakingOperation::Claim(Claim { + let smart_contract_address = + AccountAddress::from_str(&value.smart_contract_address) + .map_err(|_| SigningError(SigningErrorType::Error_invalid_address))?; + Ok(LiquidStakingOperation::Claim(Claim { idx: claim.idx, - smart_contract_address: AccountAddress::from_str(&value.smart_contract_address) - .unwrap(), - }) + smart_contract_address, + })) }, OneOfliquid_stake_transaction_payload::None => { - todo!() + Err(SigningError(SigningErrorType::Error_invalid_params)) }, } } diff --git a/rust/tw_aptos/src/nft.rs b/rust/tw_aptos/src/nft.rs index 823eadf8410..f1730530885 100644 --- a/rust/tw_aptos/src/nft.rs +++ b/rust/tw_aptos/src/nft.rs @@ -6,6 +6,7 @@ use move_core_types::account_address::AccountAddress; use std::str::FromStr; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_proto::Aptos::Proto::mod_NftMessage::OneOfnft_transaction_payload; use tw_proto::Aptos::Proto::{CancelOfferNftMessage, ClaimNftMessage, NftMessage, OfferNftMessage}; @@ -32,14 +33,20 @@ pub enum NftOperation { Cancel(Offer), } -impl From> for NftOperation { - fn from(value: NftMessage) -> Self { +impl TryFrom> for NftOperation { + type Error = SigningError; + + fn try_from(value: NftMessage) -> SigningResult { match value.nft_transaction_payload { - OneOfnft_transaction_payload::offer_nft(msg) => NftOperation::Offer(msg.into()), - OneOfnft_transaction_payload::cancel_offer_nft(msg) => NftOperation::Cancel(msg.into()), - OneOfnft_transaction_payload::claim_nft(msg) => NftOperation::Claim(msg.into()), + OneOfnft_transaction_payload::offer_nft(msg) => { + Ok(NftOperation::Offer(Offer::try_from(msg)?)) + }, + OneOfnft_transaction_payload::cancel_offer_nft(msg) => { + Ok(NftOperation::Cancel(msg.into())) + }, + OneOfnft_transaction_payload::claim_nft(msg) => Ok(NftOperation::Claim(msg.into())), OneOfnft_transaction_payload::None => { - todo!() + Err(SigningError(SigningErrorType::Error_invalid_params)) }, } } @@ -63,16 +70,20 @@ impl From for NftMessage<'_> { } } -impl From> for Offer { - fn from(value: OfferNftMessage) -> Self { - Offer { - receiver: AccountAddress::from_str(&value.receiver).unwrap(), - creator: AccountAddress::from_str(&value.creator).unwrap(), +impl TryFrom> for Offer { + type Error = SigningError; + + fn try_from(value: OfferNftMessage) -> SigningResult { + Ok(Offer { + receiver: AccountAddress::from_str(&value.receiver) + .map_err(|_| SigningError(SigningErrorType::Error_invalid_address))?, + creator: AccountAddress::from_str(&value.creator) + .map_err(|_| SigningError(SigningErrorType::Error_invalid_address))?, collection: value.collectionName.as_bytes().to_vec(), name: value.name.as_bytes().to_vec(), property_version: value.property_version, amount: value.amount, - } + }) } } diff --git a/rust/tw_aptos/src/transaction.rs b/rust/tw_aptos/src/transaction.rs index 6b1e7422a98..47b5c870835 100644 --- a/rust/tw_aptos/src/transaction.rs +++ b/rust/tw_aptos/src/transaction.rs @@ -5,7 +5,7 @@ // file LICENSE at the root of the source code distribution tree. use crate::constants::APTOS_SALT; -use crate::transaction_payload::{EntryFunction, TransactionPayload}; +use crate::transaction_payload::TransactionPayload; use move_core_types::account_address::AccountAddress; use serde::Serialize; use serde_json::{json, Value}; @@ -122,26 +122,7 @@ impl RawTransaction { } } - /// Create a new `RawTransaction` with an entry function. - pub fn new_entry_function( - sender: AccountAddress, - sequence_number: u64, - entry_function: EntryFunction, - max_gas_amount: u64, - gas_unit_price: u64, - expiration_timestamp_secs: u64, - chain_id: u8, - ) -> Self { - RawTransaction { - sender, - sequence_number, - payload: TransactionPayload::EntryFunction(entry_function), - max_gas_amount, - gas_unit_price, - expiration_timestamp_secs, - chain_id, - } - } + /// Create a new `RawTransaction` with an entry function fn serialize(&self) -> Vec { bcs::to_bytes(&self).unwrap() @@ -220,9 +201,6 @@ pub struct SignedTransaction { } impl SignedTransaction { - pub fn raw_txn(&self) -> &RawTransaction { - &self.raw_txn - } pub fn authenticator(&self) -> &TransactionAuthenticator { &self.authenticator } diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index f43455953ed..7e55bb9d70c 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -85,37 +85,40 @@ impl TransactionFactory { .with_max_gas_amount(input.max_gas_amount) .with_transaction_expiration_time(input.expiration_timestamp_secs); match input.transaction_payload { - OneOftransaction_payload::transfer(transfer) => Ok(factory + OneOftransaction_payload::transfer(transfer) => factory .implicitly_create_user_account_and_transfer( AccountAddress::from_str(&transfer.to).unwrap(), transfer.amount, - )), + ), OneOftransaction_payload::token_transfer(token_transfer) => { let func = token_transfer.function.unwrap(); - Ok(factory.coins_transfer( + factory.coins_transfer( AccountAddress::from_str(&token_transfer.to).unwrap(), token_transfer.amount, convert_proto_struct_tag_to_type_tag(func), - )) + ) }, - OneOftransaction_payload::create_account(create_account) => Ok(factory - .create_user_account(AccountAddress::from_str(&create_account.auth_key).unwrap())), + OneOftransaction_payload::create_account(create_account) => factory + .create_user_account(AccountAddress::from_str(&create_account.auth_key).unwrap()), OneOftransaction_payload::nft_message(nft_message) => { - Ok(factory.nft_ops(nft_message.into())) + factory.nft_ops(NftOperation::try_from(nft_message)?) + }, + OneOftransaction_payload::register_token(register_token) => { + let function = register_token + .function + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + Ok(factory.register_token(convert_proto_struct_tag_to_type_tag(function))) }, - OneOftransaction_payload::register_token(register_token) => Ok(factory.register_token( - convert_proto_struct_tag_to_type_tag(register_token.function.unwrap()), - )), OneOftransaction_payload::liquid_staking_message(msg) => { - Ok(factory.liquid_staking_ops(msg.into())) + factory.liquid_staking_ops(LiquidStakingOperation::try_from(msg)?) }, OneOftransaction_payload::token_transfer_coins(token_transfer_coins) => { let func = token_transfer_coins.function.unwrap(); - Ok(factory.implicitly_create_user_and_coins_transfer( + factory.implicitly_create_user_and_coins_transfer( AccountAddress::from_str(&token_transfer_coins.to).unwrap(), token_transfer_coins.amount, convert_proto_struct_tag_to_type_tag(func), - )) + ) }, OneOftransaction_payload::None => { let is_blind_sign = !input.any_encoded.is_empty(); @@ -150,52 +153,55 @@ impl TransactionFactory { self.transaction_builder(payload) } - pub fn create_user_account(&self, to: AccountAddress) -> TransactionBuilder { - self.payload(aptos_account_create_account(to)) + pub fn create_user_account(&self, to: AccountAddress) -> SigningResult { + Ok(self.payload(aptos_account_create_account(to)?)) } pub fn register_token(&self, coin_type: TypeTag) -> TransactionBuilder { self.payload(managed_coin_register(coin_type)) } - pub fn nft_ops(&self, operation: NftOperation) -> TransactionBuilder { + pub fn nft_ops(&self, operation: NftOperation) -> SigningResult { match operation { - NftOperation::Claim(claim) => self.payload(token_transfers_claim_script( + NftOperation::Claim(claim) => Ok(self.payload(token_transfers_claim_script( claim.sender, claim.creator, claim.collection, claim.name, claim.property_version, - )), - NftOperation::Cancel(offer) => self.payload(token_transfers_cancel_offer_script( + )?)), + NftOperation::Cancel(offer) => Ok(self.payload(token_transfers_cancel_offer_script( offer.receiver, offer.creator, offer.collection, offer.name, offer.property_version, - )), - NftOperation::Offer(offer) => self.payload(token_transfers_offer_script( + )?)), + NftOperation::Offer(offer) => Ok(self.payload(token_transfers_offer_script( offer.receiver, offer.creator, offer.collection, offer.name, offer.property_version, offer.amount, - )), + )?)), } } - pub fn liquid_staking_ops(&self, operation: LiquidStakingOperation) -> TransactionBuilder { + pub fn liquid_staking_ops( + &self, + operation: LiquidStakingOperation, + ) -> SigningResult { match operation { LiquidStakingOperation::Stake(stake) => { - self.payload(tortuga_stake(stake.smart_contract_address, stake.amount)) + Ok(self.payload(tortuga_stake(stake.smart_contract_address, stake.amount)?)) }, - LiquidStakingOperation::Unstake(unstake) => self.payload(tortuga_unstake( + LiquidStakingOperation::Unstake(unstake) => Ok(self.payload(tortuga_unstake( unstake.smart_contract_address, unstake.amount, - )), + )?)), LiquidStakingOperation::Claim(claim) => { - self.payload(tortuga_claim(claim.smart_contract_address, claim.idx)) + Ok(self.payload(tortuga_claim(claim.smart_contract_address, claim.idx)?)) }, } } @@ -204,8 +210,8 @@ impl TransactionFactory { &self, to: AccountAddress, amount: u64, - ) -> TransactionBuilder { - self.payload(aptos_account_transfer(to, amount)) + ) -> SigningResult { + Ok(self.payload(aptos_account_transfer(to, amount)?)) } pub fn coins_transfer( @@ -213,8 +219,8 @@ impl TransactionFactory { to: AccountAddress, amount: u64, coin_type: TypeTag, - ) -> TransactionBuilder { - self.payload(coin_transfer(coin_type, to, amount)) + ) -> SigningResult { + Ok(self.payload(coin_transfer(coin_type, to, amount)?)) } pub fn implicitly_create_user_and_coins_transfer( @@ -222,8 +228,8 @@ impl TransactionFactory { to: AccountAddress, amount: u64, coin_type: TypeTag, - ) -> TransactionBuilder { - self.payload(aptos_account_transfer_coins(coin_type, to, amount)) + ) -> SigningResult { + Ok(self.payload(aptos_account_transfer_coins(coin_type, to, amount)?)) } fn transaction_builder(&self, payload: TransactionPayload) -> TransactionBuilder { diff --git a/rust/tw_aptos/src/transaction_payload.rs b/rust/tw_aptos/src/transaction_payload.rs index a9cd7c48ca7..c0100ef18c9 100644 --- a/rust/tw_aptos/src/transaction_payload.rs +++ b/rust/tw_aptos/src/transaction_payload.rs @@ -4,7 +4,9 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use anyhow::{anyhow, Result}; +use crate::bcs_encoding; +use crate::bcs_encoding::{BcsEncodingError, BcsEncodingResult}; +use crate::serde_helper::vec_bytes; use move_core_types::identifier::Identifier; use move_core_types::language_storage::{ModuleId, StructTag, TypeTag}; use move_core_types::parser::parse_transaction_argument; @@ -13,10 +15,28 @@ use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use std::default::Default; use std::str::FromStr; - -use crate::serde_helper::vec_bytes; +use tw_memory::Data; use tw_proto::Aptos; +pub type EntryFunctionResult = Result; + +#[derive(Debug)] +pub enum EntryFunctionError { + MissingFunctionName, + InvalidFunctionName, + MissingArguments, + InvalidArguments, + BcsSerializingError, + MissingTypeArguments, + InvalidTypeArguments, +} + +impl From for EntryFunctionError { + fn from(_error: BcsEncodingError) -> Self { + EntryFunctionError::BcsSerializingError + } +} + #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct EntryFunction { module: ModuleId, @@ -29,38 +49,40 @@ pub struct EntryFunction { } impl TryFrom for EntryFunction { - type Error = anyhow::Error; + type Error = EntryFunctionError; - fn try_from(value: Value) -> Result { + fn try_from(value: Value) -> EntryFunctionResult { let function_str = value["function"] .as_str() - .ok_or_else(|| anyhow!("Missing function string"))?; - let tag = StructTag::from_str(function_str)?; + .ok_or(EntryFunctionError::MissingFunctionName)?; + let tag = StructTag::from_str(function_str) + .map_err(|_| EntryFunctionError::InvalidFunctionName)?; let args = value["arguments"] .as_array() - .ok_or_else(|| anyhow!("Arguments field is not an array or is missing"))? + .ok_or(EntryFunctionError::MissingArguments)? .iter() .map(|element| { let arg_str = element.to_string(); let arg = parse_transaction_argument( arg_str.trim_start_matches('"').trim_end_matches('"'), - )?; - serialize_argument(&arg) + ) + .map_err(|_| EntryFunctionError::InvalidArguments)?; + serialize_argument(&arg).map_err(EntryFunctionError::from) }) - .collect::>>>()?; + .collect::>>()?; let ty_args = value["type_arguments"] .as_array() - .ok_or_else(|| anyhow!("Type arguments field is not an array or is missing"))? + .ok_or(EntryFunctionError::MissingTypeArguments)? .iter() .map(|element| { let ty_arg_str = element .as_str() - .ok_or_else(|| anyhow!("Invalid type argument string"))?; - TypeTag::from_str(ty_arg_str) + .ok_or(EntryFunctionError::InvalidTypeArguments)?; + TypeTag::from_str(ty_arg_str).map_err(|_| EntryFunctionError::InvalidTypeArguments) }) - .collect::>>()?; + .collect::>>()?; Ok(EntryFunction { module: tag.module_id(), @@ -72,22 +94,21 @@ impl TryFrom for EntryFunction { } } -fn serialize_argument(arg: &TransactionArgument) -> Result> { +fn serialize_argument(arg: &TransactionArgument) -> BcsEncodingResult { match arg { - TransactionArgument::U8(v) => bcs::to_bytes(v), - TransactionArgument::U16(v) => bcs::to_bytes(v), - TransactionArgument::U32(v) => bcs::to_bytes(v), - TransactionArgument::U64(v) => bcs::to_bytes(v), - TransactionArgument::U128(v) => bcs::to_bytes(v), - TransactionArgument::U256(v) => bcs::to_bytes(v), - TransactionArgument::U8Vector(v) => bcs::to_bytes(v), - TransactionArgument::Bool(v) => bcs::to_bytes(v), + TransactionArgument::U8(v) => bcs_encoding::encode(v), + TransactionArgument::U16(v) => bcs_encoding::encode(v), + TransactionArgument::U32(v) => bcs_encoding::encode(v), + TransactionArgument::U64(v) => bcs_encoding::encode(v), + TransactionArgument::U128(v) => bcs_encoding::encode(v), + TransactionArgument::U256(v) => bcs_encoding::encode(v), + TransactionArgument::U8Vector(v) => bcs_encoding::encode(v), + TransactionArgument::Bool(v) => bcs_encoding::encode(v), TransactionArgument::Address(v) => { - let serialized_v = bcs::to_bytes(v)?; - bcs::to_bytes(&serialized_v) + let serialized_v = bcs_encoding::encode(v)?; + bcs_encoding::encode(&serialized_v) }, } - .map_err(|e| anyhow!(e)) } pub fn convert_proto_struct_tag_to_type_tag(struct_tag: Aptos::Proto::StructTag) -> TypeTag { @@ -168,8 +189,8 @@ impl EntryFunction { #[cfg(test)] mod tests { + use super::*; use crate::address::Address; - use crate::transaction_payload::{EntryFunction, TransactionPayload}; use move_core_types::account_address::AccountAddress; use move_core_types::identifier::Identifier; use move_core_types::language_storage::{ModuleId, TypeTag}; @@ -215,8 +236,8 @@ mod tests { .inner(); let amount: i64 = 1000; let args = vec![ - bcs::to_bytes(&addr).unwrap(), - bcs::to_bytes(&amount).unwrap(), + bcs_encoding::encode(&addr).unwrap(), + bcs_encoding::encode(&amount).unwrap(), ]; let module = ModuleId::new(AccountAddress::ONE, Identifier::from_str("coin").unwrap()); let function = Identifier::from_str("transfer").unwrap(); @@ -229,7 +250,7 @@ mod tests { json!([addr.to_hex_literal(), amount.to_string()]), ); let tp = TransactionPayload::EntryFunction(entry); - let serialized = bcs::to_bytes(&tp).unwrap(); + let serialized = bcs_encoding::encode(&tp).unwrap(); assert_eq!(hex::encode(serialized, false), "02000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b08e803000000000000"); let payload_value: Value = json!({ "function": "0x1::coin::transfer", diff --git a/rust/tw_coin_entry/src/error.rs b/rust/tw_coin_entry/src/error.rs index 0a2d8480676..7999b593e6a 100644 --- a/rust/tw_coin_entry/src/error.rs +++ b/rust/tw_coin_entry/src/error.rs @@ -4,7 +4,6 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use serde_json::Error; use std::fmt; use std::fmt::Formatter; use tw_keypair::KeyPairError; @@ -59,7 +58,7 @@ impl From for SigningError { } impl From for SigningError { - fn from(_value: Error) -> Self { + fn from(_value: serde_json::Error) -> Self { SigningError(SigningErrorType::Error_input_parse) } } From 9fc76fec93e447842b806594b6a5224a25e3f144 Mon Sep 17 00:00:00 2001 From: Satoshi Otomakan Date: Wed, 15 Nov 2023 16:26:17 +0100 Subject: [PATCH 56/60] [Aptos]: Error handling #2 --- rust/tw_aptos/src/address.rs | 2 +- rust/tw_aptos/src/compiler.rs | 20 ++++++---- rust/tw_aptos/src/liquid_staking.rs | 7 ++-- rust/tw_aptos/src/nft.rs | 41 ++++++++++--------- rust/tw_aptos/src/signer.rs | 2 +- rust/tw_aptos/src/transaction.rs | 26 ++++++------ rust/tw_aptos/src/transaction_builder.rs | 51 +++++++++++++++--------- rust/tw_aptos/src/transaction_payload.rs | 13 +++++- 8 files changed, 96 insertions(+), 66 deletions(-) diff --git a/rust/tw_aptos/src/address.rs b/rust/tw_aptos/src/address.rs index b7a4900bfda..3f5cf1d3217 100644 --- a/rust/tw_aptos/src/address.rs +++ b/rust/tw_aptos/src/address.rs @@ -71,7 +71,7 @@ impl CoinAddress for Address { } #[inline] -fn from_account_error(_err: AccountAddressParseError) -> AddressError { +pub fn from_account_error(_err: AccountAddressParseError) -> AddressError { AddressError::InvalidInput } diff --git a/rust/tw_aptos/src/compiler.rs b/rust/tw_aptos/src/compiler.rs index a63aa8a0464..36763189cd8 100644 --- a/rust/tw_aptos/src/compiler.rs +++ b/rust/tw_aptos/src/compiler.rs @@ -4,7 +4,7 @@ use crate::transaction_builder; use std::marker::PhantomData; use std::str::FromStr; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_coin_entry::signing_output_error; use tw_proto::Aptos::Proto; use tw_proto::TxCompiler::Proto as CompilerProto; @@ -30,8 +30,8 @@ impl Compiler { let signed_tx = builder .sender(sender.inner()) .sequence_number(input.sequence_number as u64) - .build() - .pre_image(); + .build()? + .pre_image()?; Ok(CompilerProto::PreSigningOutput { data: signed_tx.into(), ..CompilerProto::PreSigningOutput::default() @@ -55,14 +55,18 @@ impl Compiler { ) -> SigningResult> { let builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone())?; let sender = Address::from_str(&input.sender)?; + let signature = signatures + .first() + .ok_or(SigningError(SigningErrorType::Error_signatures_count))?; + let public_key = public_keys + .first() + .ok_or(SigningError(SigningErrorType::Error_signatures_count))?; + let signed_tx = builder .sender(sender.inner()) .sequence_number(input.sequence_number as u64) - .build() - .compile( - signatures.first().unwrap().to_vec(), - public_keys.first().unwrap().to_vec(), - )?; + .build()? + .compile(signature.to_vec(), public_key.to_vec())?; Ok(Proto::SigningOutput { raw_txn: signed_tx.raw_txn_bytes().clone().into(), encoded: signed_tx.encoded().clone().into(), diff --git a/rust/tw_aptos/src/liquid_staking.rs b/rust/tw_aptos/src/liquid_staking.rs index 99b704a8419..a5263a64fab 100644 --- a/rust/tw_aptos/src/liquid_staking.rs +++ b/rust/tw_aptos/src/liquid_staking.rs @@ -4,6 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use crate::address::from_account_error; use crate::bcs_encoding; use crate::transaction_payload::{EntryFunction, TransactionPayload}; use move_core_types::{account_address::AccountAddress, ident_str, language_storage::ModuleId}; @@ -92,7 +93,7 @@ impl TryFrom> for LiquidStakingOperation { OneOfliquid_stake_transaction_payload::stake(stake_msg) => { let smart_contract_address = AccountAddress::from_str(&value.smart_contract_address) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_address))?; + .map_err(from_account_error)?; Ok(LiquidStakingOperation::Stake(Stake { amount: stake_msg.amount, smart_contract_address, @@ -101,7 +102,7 @@ impl TryFrom> for LiquidStakingOperation { OneOfliquid_stake_transaction_payload::unstake(unstake_msg) => { let smart_contract_address = AccountAddress::from_str(&value.smart_contract_address) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_address))?; + .map_err(from_account_error)?; Ok(LiquidStakingOperation::Unstake(Unstake { amount: unstake_msg.amount, smart_contract_address, @@ -110,7 +111,7 @@ impl TryFrom> for LiquidStakingOperation { OneOfliquid_stake_transaction_payload::claim(claim) => { let smart_contract_address = AccountAddress::from_str(&value.smart_contract_address) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_address))?; + .map_err(from_account_error)?; Ok(LiquidStakingOperation::Claim(Claim { idx: claim.idx, smart_contract_address, diff --git a/rust/tw_aptos/src/nft.rs b/rust/tw_aptos/src/nft.rs index f1730530885..fe6f0375146 100644 --- a/rust/tw_aptos/src/nft.rs +++ b/rust/tw_aptos/src/nft.rs @@ -4,6 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use crate::address::from_account_error; use move_core_types::account_address::AccountAddress; use std::str::FromStr; use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; @@ -42,9 +43,11 @@ impl TryFrom> for NftOperation { Ok(NftOperation::Offer(Offer::try_from(msg)?)) }, OneOfnft_transaction_payload::cancel_offer_nft(msg) => { - Ok(NftOperation::Cancel(msg.into())) + Ok(NftOperation::Cancel(Offer::try_from(msg)?)) + }, + OneOfnft_transaction_payload::claim_nft(msg) => { + Ok(NftOperation::Claim(Claim::try_from(msg)?)) }, - OneOfnft_transaction_payload::claim_nft(msg) => Ok(NftOperation::Claim(msg.into())), OneOfnft_transaction_payload::None => { Err(SigningError(SigningErrorType::Error_invalid_params)) }, @@ -75,10 +78,8 @@ impl TryFrom> for Offer { fn try_from(value: OfferNftMessage) -> SigningResult { Ok(Offer { - receiver: AccountAddress::from_str(&value.receiver) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_address))?, - creator: AccountAddress::from_str(&value.creator) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_address))?, + receiver: AccountAddress::from_str(&value.receiver).map_err(from_account_error)?, + creator: AccountAddress::from_str(&value.creator).map_err(from_account_error)?, collection: value.collectionName.as_bytes().to_vec(), name: value.name.as_bytes().to_vec(), property_version: value.property_version, @@ -102,16 +103,18 @@ impl From for OfferNftMessage<'_> { } } -impl From> for Offer { - fn from(value: CancelOfferNftMessage) -> Self { - Offer { - receiver: AccountAddress::from_str(&value.receiver).unwrap(), - creator: AccountAddress::from_str(&value.creator).unwrap(), +impl TryFrom> for Offer { + type Error = SigningError; + + fn try_from(value: CancelOfferNftMessage) -> SigningResult { + Ok(Offer { + receiver: AccountAddress::from_str(&value.receiver).map_err(from_account_error)?, + creator: AccountAddress::from_str(&value.creator).map_err(from_account_error)?, collection: value.collectionName.as_bytes().to_vec(), name: value.name.as_bytes().to_vec(), property_version: value.property_version, amount: 0, - } + }) } } @@ -131,15 +134,17 @@ impl From for CancelOfferNftMessage<'_> { } } -impl From> for Claim { - fn from(value: ClaimNftMessage) -> Self { - Claim { - sender: AccountAddress::from_str(&value.sender).unwrap(), - creator: AccountAddress::from_str(&value.creator).unwrap(), +impl TryFrom> for Claim { + type Error = SigningError; + + fn try_from(value: ClaimNftMessage) -> SigningResult { + Ok(Claim { + sender: AccountAddress::from_str(&value.sender).map_err(from_account_error)?, + creator: AccountAddress::from_str(&value.creator).map_err(from_account_error)?, collection: value.collectionName.as_bytes().to_vec(), name: value.name.as_bytes().to_vec(), property_version: value.property_version, - } + }) } } diff --git a/rust/tw_aptos/src/signer.rs b/rust/tw_aptos/src/signer.rs index 66ab31621db..8be01dbed59 100644 --- a/rust/tw_aptos/src/signer.rs +++ b/rust/tw_aptos/src/signer.rs @@ -44,7 +44,7 @@ impl Signer { let signed_tx = builder .sender(sender.inner()) .sequence_number(input.sequence_number as u64) - .build() + .build()? .sign(key_pair)?; Ok(Proto::SigningOutput { raw_txn: signed_tx.raw_txn_bytes().clone().into(), diff --git a/rust/tw_aptos/src/transaction.rs b/rust/tw_aptos/src/transaction.rs index 47b5c870835..db496d8223b 100644 --- a/rust/tw_aptos/src/transaction.rs +++ b/rust/tw_aptos/src/transaction.rs @@ -4,6 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use crate::bcs_encoding::{self, BcsEncodingResult}; use crate::constants::APTOS_SALT; use crate::transaction_payload::TransactionPayload; use move_core_types::account_address::AccountAddress; @@ -14,6 +15,7 @@ use tw_coin_entry::error::SigningResult; use tw_encoding::hex::encode; use tw_keypair::ed25519::sha512::KeyPair; use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; +use tw_memory::Data; use tw_proto::Aptos::Proto; #[derive(Clone, Serialize)] @@ -123,19 +125,18 @@ impl RawTransaction { } /// Create a new `RawTransaction` with an entry function - - fn serialize(&self) -> Vec { - bcs::to_bytes(&self).unwrap() + fn serialize(&self) -> BcsEncodingResult { + bcs_encoding::encode(&self) } - fn msg_to_sign(&self) -> Vec { - let serialized = self.serialize(); + fn msg_to_sign(&self) -> SigningResult { + let serialized = self.serialize()?; let mut preimage = tw_hash::sha3::sha3_256(APTOS_SALT); preimage.extend_from_slice(serialized.as_slice()); - preimage + Ok(preimage) } - pub fn pre_image(&self) -> Vec { + pub fn pre_image(&self) -> SigningResult> { self.msg_to_sign() } @@ -144,13 +145,13 @@ impl RawTransaction { signature: Vec, public_key: Vec, ) -> SigningResult { - let serialized = self.serialize(); + let serialized = self.serialize()?; let auth = TransactionAuthenticator::Ed25519 { public_key, signature, }; let mut encoded = serialized.clone(); - encoded.extend_from_slice(bcs::to_bytes(&auth).unwrap().as_slice()); + encoded.extend_from_slice(bcs_encoding::encode(&auth)?.as_slice()); Ok(SignedTransaction { raw_txn: self.clone(), authenticator: auth, @@ -160,7 +161,7 @@ impl RawTransaction { } pub fn sign(self, key_pair: KeyPair) -> SigningResult { - let to_sign = self.pre_image(); + let to_sign = self.pre_image()?; let signature = key_pair.private().sign(to_sign)?.to_bytes().into_vec(); let pubkey = key_pair.public().as_slice().to_vec(); self.compile(signature, pubkey) @@ -213,10 +214,7 @@ impl SignedTransaction { pub fn to_json(&self) -> Value { let mut json_value = self.raw_txn.to_json(); - json_value - .as_object_mut() - .unwrap() - .insert("signature".to_string(), self.authenticator.to_json()); + json_value["signature"] = self.authenticator.to_json(); json_value } } diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index 7e55bb9d70c..53f72d32936 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -4,6 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use crate::address::from_account_error; use crate::aptos_move_packages::{ aptos_account_create_account, aptos_account_transfer, aptos_account_transfer_coins, coin_transfer, managed_coin_register, token_transfers_cancel_offer_script, @@ -47,17 +48,22 @@ impl TransactionBuilder { self } - pub fn build(self) -> RawTransaction { - RawTransaction::new( - self.sender.expect("sender must have been set"), - self.sequence_number - .expect("sequence number must have been set"), + pub fn build(self) -> SigningResult { + let sender = self + .sender + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + let sequence_number = self + .sequence_number + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + Ok(RawTransaction::new( + sender, + sequence_number, self.payload, self.max_gas_amount, self.gas_unit_price, self.expiration_timestamp_secs, self.chain_id, - ) + )) } } @@ -87,19 +93,24 @@ impl TransactionFactory { match input.transaction_payload { OneOftransaction_payload::transfer(transfer) => factory .implicitly_create_user_account_and_transfer( - AccountAddress::from_str(&transfer.to).unwrap(), + AccountAddress::from_str(&transfer.to).map_err(from_account_error)?, transfer.amount, ), OneOftransaction_payload::token_transfer(token_transfer) => { - let func = token_transfer.function.unwrap(); + let func = token_transfer + .function + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; factory.coins_transfer( - AccountAddress::from_str(&token_transfer.to).unwrap(), + AccountAddress::from_str(&token_transfer.to).map_err(from_account_error)?, token_transfer.amount, - convert_proto_struct_tag_to_type_tag(func), + convert_proto_struct_tag_to_type_tag(func)?, ) }, - OneOftransaction_payload::create_account(create_account) => factory - .create_user_account(AccountAddress::from_str(&create_account.auth_key).unwrap()), + OneOftransaction_payload::create_account(create_account) => { + let address = AccountAddress::from_str(&create_account.auth_key) + .map_err(from_account_error)?; + factory.create_user_account(address) + }, OneOftransaction_payload::nft_message(nft_message) => { factory.nft_ops(NftOperation::try_from(nft_message)?) }, @@ -107,26 +118,28 @@ impl TransactionFactory { let function = register_token .function .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; - Ok(factory.register_token(convert_proto_struct_tag_to_type_tag(function))) + Ok(factory.register_token(convert_proto_struct_tag_to_type_tag(function)?)) }, OneOftransaction_payload::liquid_staking_message(msg) => { factory.liquid_staking_ops(LiquidStakingOperation::try_from(msg)?) }, OneOftransaction_payload::token_transfer_coins(token_transfer_coins) => { - let func = token_transfer_coins.function.unwrap(); + let func = token_transfer_coins + .function + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; factory.implicitly_create_user_and_coins_transfer( - AccountAddress::from_str(&token_transfer_coins.to).unwrap(), + AccountAddress::from_str(&token_transfer_coins.to) + .map_err(from_account_error)?, token_transfer_coins.amount, - convert_proto_struct_tag_to_type_tag(func), + convert_proto_struct_tag_to_type_tag(func)?, ) }, OneOftransaction_payload::None => { let is_blind_sign = !input.any_encoded.is_empty(); let v = serde_json::from_str::(&input.any_encoded)?; if is_blind_sign { - Ok(factory.payload(TransactionPayload::EntryFunction( - EntryFunction::try_from(v).unwrap(), - ))) + let entry_function = EntryFunction::try_from(v)?; + Ok(factory.payload(TransactionPayload::EntryFunction(entry_function))) } else { Err(SigningError(SigningErrorType::Error_input_parse)) } diff --git a/rust/tw_aptos/src/transaction_payload.rs b/rust/tw_aptos/src/transaction_payload.rs index c0100ef18c9..d7034e5caa3 100644 --- a/rust/tw_aptos/src/transaction_payload.rs +++ b/rust/tw_aptos/src/transaction_payload.rs @@ -15,6 +15,7 @@ use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use std::default::Default; use std::str::FromStr; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_memory::Data; use tw_proto::Aptos; @@ -37,6 +38,12 @@ impl From for EntryFunctionError { } } +impl From for SigningError { + fn from(_: EntryFunctionError) -> Self { + SigningError(SigningErrorType::Error_invalid_params) + } +} + #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct EntryFunction { module: ModuleId, @@ -111,12 +118,14 @@ fn serialize_argument(arg: &TransactionArgument) -> BcsEncodingResult { } } -pub fn convert_proto_struct_tag_to_type_tag(struct_tag: Aptos::Proto::StructTag) -> TypeTag { +pub fn convert_proto_struct_tag_to_type_tag( + struct_tag: Aptos::Proto::StructTag, +) -> SigningResult { TypeTag::from_str(&format!( "{}::{}::{}", struct_tag.account_address, struct_tag.module, struct_tag.name )) - .unwrap() + .map_err(|_| SigningError(SigningErrorType::Error_invalid_params)) } pub fn convert_type_tag_to_struct_tag(type_tag: TypeTag) -> Aptos::Proto::StructTag<'static> { From 7872d415c75c02928772eb0608b78ed1f8410b2f Mon Sep 17 00:00:00 2001 From: Satoshi Otomakan Date: Wed, 15 Nov 2023 16:48:17 +0100 Subject: [PATCH 57/60] [Aptos]: Add fuzz test --- rust/Cargo.lock | 1 - rust/tw_aptos/Cargo.toml | 1 - rust/tw_aptos/fuzz/.gitignore | 4 ++++ rust/tw_aptos/fuzz/Cargo.toml | 29 +++++++++++++++++++++++++ rust/tw_aptos/fuzz/fuzz_targets/sign.rs | 9 ++++++++ rust/tw_evm/fuzz/Cargo.toml | 2 +- 6 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 rust/tw_aptos/fuzz/.gitignore create mode 100644 rust/tw_aptos/fuzz/Cargo.toml create mode 100644 rust/tw_aptos/fuzz/fuzz_targets/sign.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 4d77a57536e..ff49d1ed100 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1611,7 +1611,6 @@ dependencies = [ name = "tw_aptos" version = "0.1.0" dependencies = [ - "anyhow", "bcs", "move-core-types", "serde", diff --git a/rust/tw_aptos/Cargo.toml b/rust/tw_aptos/Cargo.toml index 07e737d6390..6f4f17429ae 100644 --- a/rust/tw_aptos/Cargo.toml +++ b/rust/tw_aptos/Cargo.toml @@ -4,7 +4,6 @@ version = "0.1.0" edition = "2021" [dependencies] -anyhow = "1.0.75" serde_json = "1.0.108" tw_coin_entry = { path = "../tw_coin_entry" } tw_encoding = { path = "../tw_encoding" } diff --git a/rust/tw_aptos/fuzz/.gitignore b/rust/tw_aptos/fuzz/.gitignore new file mode 100644 index 00000000000..1a45eee7760 --- /dev/null +++ b/rust/tw_aptos/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage diff --git a/rust/tw_aptos/fuzz/Cargo.toml b/rust/tw_aptos/fuzz/Cargo.toml new file mode 100644 index 00000000000..2d3506a0ea6 --- /dev/null +++ b/rust/tw_aptos/fuzz/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "tw_aptos-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" +tw_number = { path = "../../tw_number" } +tw_proto = { path = "../../tw_proto", features = ["fuzz"] } + +[dependencies.tw_aptos] +path = ".." + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[profile.release] +debug = 1 + +[[bin]] +name = "sign" +path = "fuzz_targets/sign.rs" +test = false +doc = false diff --git a/rust/tw_aptos/fuzz/fuzz_targets/sign.rs b/rust/tw_aptos/fuzz/fuzz_targets/sign.rs new file mode 100644 index 00000000000..9fcb4c2a87c --- /dev/null +++ b/rust/tw_aptos/fuzz/fuzz_targets/sign.rs @@ -0,0 +1,9 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; +use tw_aptos::signer::{Signer, StandardAptosContext}; +use tw_proto::Aptos::Proto; + +fuzz_target!(|input: Proto::SigningInput<'_>| { + let _ = Signer::::sign_proto(input); +}); diff --git a/rust/tw_evm/fuzz/Cargo.toml b/rust/tw_evm/fuzz/Cargo.toml index 6fad417a7d8..e1ac2e6c65c 100644 --- a/rust/tw_evm/fuzz/Cargo.toml +++ b/rust/tw_evm/fuzz/Cargo.toml @@ -9,7 +9,7 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = { version = "0.4", features = ["arbitrary-derive"] } -serde_json = "1.0.95" +serde_json = "1.0.96" tw_number = { path = "../../tw_number" } tw_proto = { path = "../../tw_proto", features = ["fuzz"] } From f96f8bbfa24d566a7cebaf58a5b97a4353392cde Mon Sep 17 00:00:00 2001 From: Satoshi Otomakan Date: Wed, 15 Nov 2023 17:04:40 +0100 Subject: [PATCH 58/60] [Aptos]: Minor changes --- rust/tw_aptos/fuzz/fuzz_targets/sign.rs | 4 ++-- rust/tw_aptos/src/compiler.rs | 8 ++----- rust/tw_aptos/src/entry.rs | 8 +++---- rust/tw_aptos/src/signer.rs | 20 +++-------------- rust/tw_aptos/tests/signer.rs | 30 ++++++++++++------------- 5 files changed, 26 insertions(+), 44 deletions(-) diff --git a/rust/tw_aptos/fuzz/fuzz_targets/sign.rs b/rust/tw_aptos/fuzz/fuzz_targets/sign.rs index 9fcb4c2a87c..5d55e467d14 100644 --- a/rust/tw_aptos/fuzz/fuzz_targets/sign.rs +++ b/rust/tw_aptos/fuzz/fuzz_targets/sign.rs @@ -1,9 +1,9 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use tw_aptos::signer::{Signer, StandardAptosContext}; +use tw_aptos::signer::Signer; use tw_proto::Aptos::Proto; fuzz_target!(|input: Proto::SigningInput<'_>| { - let _ = Signer::::sign_proto(input); + let _ = Signer::sign_proto(input); }); diff --git a/rust/tw_aptos/src/compiler.rs b/rust/tw_aptos/src/compiler.rs index 36763189cd8..73954261f19 100644 --- a/rust/tw_aptos/src/compiler.rs +++ b/rust/tw_aptos/src/compiler.rs @@ -1,7 +1,5 @@ use crate::address::Address; -use crate::signer::AptosContext; use crate::transaction_builder; -use std::marker::PhantomData; use std::str::FromStr; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; @@ -9,11 +7,9 @@ use tw_coin_entry::signing_output_error; use tw_proto::Aptos::Proto; use tw_proto::TxCompiler::Proto as CompilerProto; -pub struct Compiler { - _phantom: PhantomData, -} +pub struct Compiler; -impl Compiler { +impl Compiler { #[inline] pub fn preimage_hashes( input: Proto::SigningInput<'_>, diff --git a/rust/tw_aptos/src/entry.rs b/rust/tw_aptos/src/entry.rs index 0babd68600d..4438a15ae2b 100644 --- a/rust/tw_aptos/src/entry.rs +++ b/rust/tw_aptos/src/entry.rs @@ -6,7 +6,7 @@ use crate::address::Address; use crate::compiler::Compiler; -use crate::signer::{Signer, StandardAptosContext}; +use crate::signer::Signer; use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; @@ -59,7 +59,7 @@ impl CoinEntry for AptosEntry { #[inline] fn sign(&self, _coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput { - Signer::::sign_proto(input) + Signer::sign_proto(input) } #[inline] @@ -68,7 +68,7 @@ impl CoinEntry for AptosEntry { _coin: &dyn CoinContext, input: Self::SigningInput<'_>, ) -> Self::PreSigningOutput { - Compiler::::preimage_hashes(input) + Compiler::preimage_hashes(input) } #[inline] @@ -79,7 +79,7 @@ impl CoinEntry for AptosEntry { signatures: Vec, public_keys: Vec, ) -> Self::SigningOutput { - Compiler::::compile(input, signatures, public_keys) + Compiler::compile(input, signatures, public_keys) } #[inline] diff --git a/rust/tw_aptos/src/signer.rs b/rust/tw_aptos/src/signer.rs index 8be01dbed59..91431569c73 100644 --- a/rust/tw_aptos/src/signer.rs +++ b/rust/tw_aptos/src/signer.rs @@ -4,31 +4,17 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use crate::address::{Address, AptosAddress}; +use crate::address::Address; use crate::transaction_builder; -use std::marker::PhantomData; use std::str::FromStr; use tw_coin_entry::error::SigningResult; use tw_coin_entry::signing_output_error; use tw_keypair::ed25519; use tw_proto::Aptos::Proto; -pub trait AptosContext { - type Address: AptosAddress; -} - -#[derive(Default)] -pub struct StandardAptosContext; - -impl AptosContext for StandardAptosContext { - type Address = Address; -} - -pub struct Signer { - _phantom: PhantomData, -} +pub struct Signer; -impl Signer { +impl Signer { #[inline] pub fn sign_proto(input: Proto::SigningInput<'_>) -> Proto::SigningOutput<'static> { Self::sign_proto_impl(input) diff --git a/rust/tw_aptos/tests/signer.rs b/rust/tw_aptos/tests/signer.rs index ca439240cc1..09f80e2e26a 100644 --- a/rust/tw_aptos/tests/signer.rs +++ b/rust/tw_aptos/tests/signer.rs @@ -10,7 +10,7 @@ use std::str::FromStr; use tw_aptos::liquid_staking; use tw_aptos::liquid_staking::{LiquidStakingOperation, Stake, Unstake}; use tw_aptos::nft::{Claim, NftOperation, Offer}; -use tw_aptos::signer::{Signer, StandardAptosContext}; +use tw_aptos::signer::Signer; use tw_aptos::transaction_payload::convert_type_tag_to_struct_tag; use tw_coin_entry::error::SigningErrorType; use tw_encoding::hex; @@ -201,7 +201,7 @@ fn test_aptos_sign_transaction_transfer() { amount: 1000, })), ); - let output = Signer::::sign_proto(input); + let output = Signer::sign_proto(input); test_tx_result(output, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021", "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", @@ -243,7 +243,7 @@ fn test_aptos_sign_create_account() { to: "0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e".to_string(), })), ); - let output = Signer::::sign_proto(input); + let output = Signer::sign_proto(input); test_tx_result(output, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada0000000021", // Expected raw transaction bytes "fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", // Expected signature @@ -293,7 +293,7 @@ fn test_aptos_sign_coin_transfer() { .unwrap(), })), ); - let output = Signer::::sign_proto(input); + let output = Signer::sign_proto(input); test_tx_result(output, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada0000000020", // Expected raw transaction bytes "7643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", // Expected signature @@ -332,7 +332,7 @@ fn test_implicit_aptos_sign_coin_transfer() { "", Some(OpsDetails::ImplicitTokenTransfer(TokenTransfer { transfer: Transfer { to: "0xb7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c".to_string(), amount: 10000 }, tag: TypeTag::from_str("0xe9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9::mee_coin::MeeCoin").unwrap() })), ); - let output = Signer::::sign_proto(input); + let output = Signer::sign_proto(input); test_tx_result(output, "1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf2502000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e730107e9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9086d65655f636f696e074d6565436f696e000220b7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c081027000000000000d0070000000000006400000000000000c2276ada0000000001", // Expected raw transaction bytes "30ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d", // Expected signature @@ -385,7 +385,7 @@ fn test_aptos_nft_offer() { amount: 1, }))), ); - let output = Signer::::sign_proto(input); + let output = Signer::sign_proto(input); test_tx_result(output, "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes "af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", // Expected signature @@ -441,7 +441,7 @@ fn test_aptos_cancel_nft_offer() { amount: 0, }))), ); - let output = Signer::::sign_proto(input); + let output = Signer::sign_proto(input); test_tx_result(output, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes "826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", // Expected signature @@ -496,7 +496,7 @@ fn test_aptos_nft_claim() { property_version: 0, }))), ); - let output = Signer::::sign_proto(input); + let output = Signer::sign_proto(input); test_tx_result(output, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes "ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", // Expected signature @@ -538,7 +538,7 @@ fn test_aptos_register_token() { "", Some(OpsDetails::RegisterToken(RegisterToken { coin_type: TypeTag::from_str("0xe4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379::move_coin::MoveCoin").unwrap() })), ); - let output = Signer::::sign_proto(input); + let output = Signer::sign_proto(input); test_tx_result(output, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3017000000000000000200000000000000000000000000000000000000000000000000000000000000010c6d616e616765645f636f696e0872656769737465720107e4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379096d6f76655f636f696e084d6f7665436f696e000080841e00000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes "e230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100", // Expected signature @@ -586,7 +586,7 @@ fn test_aptos_tortuga_stake() { }, ))), ); - let output = Signer::::sign_proto(input); + let output = Signer::sign_proto(input); test_tx_result(output, "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1300000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000b2150000000000006400000000000000cbd78d630000000001", // Expected raw transaction bytes "22d3166c3003f9c24a35fd39c71eb27e0d2bb82541be610822165c9283f56fefe5a9d46421b9caf174995bd8f83141e60ea8cff521ecf4741fe19e6ae9a5680d", // Expected signature @@ -636,7 +636,7 @@ fn test_aptos_tortuga_unstake() { }), )), ); - let output = Signer::::sign_proto(input); + let output = Signer::sign_proto(input); test_tx_result(output, "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1400000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e9050000000043090000000000007800000000000000b5d48e630000000001", // Expected raw transaction bytes "6994b917432ad70ae84d2ce1484e6aece589a68aad1b7c6e38c9697f2a012a083a3a755c5e010fd3d0f149a75dd8d257acbd09f10800e890074e5ad384314d0c", // Expected signature @@ -686,7 +686,7 @@ fn test_aptos_tortuga_claim() { }, ))), ); - let output = Signer::::sign_proto(input); + let output = Signer::sign_proto(input); test_tx_result(output, "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657205636c61696d00010800000000000000000a0000000000000094000000000000005f4d42640000000001", // Expected raw transaction bytes "c936584f89777e1fe2d5dd75cd8d9c514efc445810ba22f462b6fe7229c6ec7fc1c8b25d3e233eafaa8306433b3220235e563498ba647be38cac87ff618e3d03", // Expected signature @@ -741,7 +741,7 @@ fn test_aptos_blind_sign() { }"#, None, ); - let output = Signer::::sign_proto(input); + let output = Signer::sign_proto(input); test_tx_result(output, "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f302a000000000000000216fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c0f416e696d6553776170506f6f6c563127737761705f65786163745f636f696e735f666f725f636f696e735f335f706169725f656e747279040700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e0007881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f404636f696e044d4f4a4f0007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa05617373657404555344540007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa056173736574045553444300020840420f000000000008b1c0000000000000ab860100000000006400000000000000c2276ada0000000001", // Expected raw transaction bytes "42cd67406e85afd1e948e7ad7f5f484fb4c60d82b267c6b6b28a92301e228b983206d2b87cd5487cf9acfb0effbd183ab90123570eb2e047cb152d337152210b", // Expected signature @@ -796,7 +796,7 @@ fn test_aptos_blind_sign_staking() { }"#, None, ); - let output = Signer::::sign_proto(input); + let output = Signer::sign_proto(input); test_tx_result(output, "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2b00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000ab860100000000006400000000000000c2276ada0000000001", // Expected raw transaction bytes "a41b7440a50f36e8491319508734acb55488abc6d88fbc9cb2b37ba23210f01f5d08c856cb7abf18c414cf9302ee144450bd99495a7e21e61f624764db91eb0b", // Expected signature @@ -845,7 +845,7 @@ fn test_aptos_blind_sign_unstaking() { }"#, None, ); - let output = Signer::::sign_proto(input); + let output = Signer::sign_proto(input); test_tx_result(output, "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e90500000000ab860100000000006400000000000000c2276ada0000000001", // Expected raw transaction bytes "a58ad5e3331beb8c0212a18a1f932207cb664b78f5aad3cb1fe7435e0e0e053247ce49b38fd67b064bed34ed643eb6a03165d77c681d7d73ac3161ab984a960a", // Expected signature From 8b1c10a804e8e4e4b97fea99732f806149fd4918 Mon Sep 17 00:00:00 2001 From: Satoshi Otomakan Date: Wed, 15 Nov 2023 17:08:55 +0100 Subject: [PATCH 59/60] [Aptos]: Bump code coverage to 92.0 --- rust/coverage.stats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/coverage.stats b/rust/coverage.stats index 8670b15529f..7d7ab43dc7c 100644 --- a/rust/coverage.stats +++ b/rust/coverage.stats @@ -1 +1 @@ -91.0 \ No newline at end of file +92.0 \ No newline at end of file From 10ae3ff36d49a8911f0407e87479bea3f1062999 Mon Sep 17 00:00:00 2001 From: Satoshi Otomakan Date: Wed, 15 Nov 2023 17:23:35 +0100 Subject: [PATCH 60/60] [Aptos]: Move `bcs` to `tw_encoding` --- rust/Cargo.lock | 3 +- rust/tw_aptos/Cargo.toml | 1 - rust/tw_aptos/src/aptos_move_packages.rs | 42 ++++++++++++------------ rust/tw_aptos/src/bcs_encoding.rs | 35 -------------------- rust/tw_aptos/src/lib.rs | 1 - rust/tw_aptos/src/liquid_staking.rs | 8 ++--- rust/tw_aptos/src/transaction.rs | 8 ++--- rust/tw_aptos/src/transaction_payload.rs | 40 ++++++++++------------ rust/tw_coin_entry/Cargo.toml | 1 + rust/tw_coin_entry/src/error.rs | 7 ++++ rust/tw_encoding/Cargo.toml | 1 + rust/tw_encoding/src/bcs.rs | 16 +++++++++ rust/tw_encoding/src/lib.rs | 1 + 13 files changed, 75 insertions(+), 89 deletions(-) create mode 100644 rust/tw_encoding/src/bcs.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index ff49d1ed100..c6fdc8c55a6 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1611,7 +1611,6 @@ dependencies = [ name = "tw_aptos" version = "0.1.0" dependencies = [ - "bcs", "move-core-types", "serde", "serde_bytes", @@ -1648,6 +1647,7 @@ name = "tw_coin_entry" version = "0.1.0" dependencies = [ "serde_json", + "tw_encoding", "tw_keypair", "tw_memory", "tw_misc", @@ -1679,6 +1679,7 @@ name = "tw_encoding" version = "0.1.0" dependencies = [ "arbitrary", + "bcs", "bs58", "ciborium", "data-encoding", diff --git a/rust/tw_aptos/Cargo.toml b/rust/tw_aptos/Cargo.toml index 6f4f17429ae..49e4d120138 100644 --- a/rust/tw_aptos/Cargo.toml +++ b/rust/tw_aptos/Cargo.toml @@ -12,7 +12,6 @@ tw_proto = { path = "../tw_proto" } tw_number = { path = "../tw_number" } tw_hash = { path = "../tw_hash" } tw_memory = { path = "../tw_memory" } -bcs = "0.1.6" move-core-types = { git = "https://github.com/move-language/move", rev = "ea70797099baea64f05194a918cebd69ed02b285", features = ["address32"] } serde = { version = "1.0.189", features = ["derive"] } serde_bytes = "0.11.12" diff --git a/rust/tw_aptos/src/aptos_move_packages.rs b/rust/tw_aptos/src/aptos_move_packages.rs index 06674c2755e..2e8f7aff378 100644 --- a/rust/tw_aptos/src/aptos_move_packages.rs +++ b/rust/tw_aptos/src/aptos_move_packages.rs @@ -4,13 +4,13 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use crate::bcs_encoding; use crate::transaction_payload::{EntryFunction, TransactionPayload}; use move_core_types::account_address::AccountAddress; use move_core_types::ident_str; use move_core_types::language_storage::{ModuleId, TypeTag}; use serde_json::json; use tw_coin_entry::error::SigningResult; +use tw_encoding::bcs; pub fn aptos_account_transfer( to: AccountAddress, @@ -26,7 +26,7 @@ pub fn aptos_account_transfer( ), ident_str!("transfer").to_owned(), vec![], - vec![bcs_encoding::encode(&to)?, bcs_encoding::encode(&amount)?], + vec![bcs::encode(&to)?, bcs::encode(&amount)?], json!([to.to_hex_literal(), amount.to_string()]), ))) } @@ -42,7 +42,7 @@ pub fn aptos_account_create_account(auth_key: AccountAddress) -> SigningResult = Result; - -#[derive(Debug)] -pub struct BcsEncodingError(bcs::Error); - -impl From for BcsEncodingError { - fn from(error: bcs::Error) -> BcsEncodingError { - BcsEncodingError(error) - } -} - -impl From for SigningError { - fn from(_: BcsEncodingError) -> Self { - // `bcs` is used to encode internal structures. - // It's an internal error. - SigningError(SigningErrorType::Error_internal) - } -} - -pub fn encode(value: &T) -> BcsEncodingResult -where - T: ?Sized + Serialize, -{ - bcs::to_bytes(value).map_err(BcsEncodingError::from) -} diff --git a/rust/tw_aptos/src/lib.rs b/rust/tw_aptos/src/lib.rs index a4f4e9c18f3..b85498f42c2 100644 --- a/rust/tw_aptos/src/lib.rs +++ b/rust/tw_aptos/src/lib.rs @@ -12,7 +12,6 @@ mod serde_helper; pub mod nft; -pub mod bcs_encoding; pub mod compiler; pub mod liquid_staking; pub mod signer; diff --git a/rust/tw_aptos/src/liquid_staking.rs b/rust/tw_aptos/src/liquid_staking.rs index a5263a64fab..2ac80692050 100644 --- a/rust/tw_aptos/src/liquid_staking.rs +++ b/rust/tw_aptos/src/liquid_staking.rs @@ -5,12 +5,12 @@ // file LICENSE at the root of the source code distribution tree. use crate::address::from_account_error; -use crate::bcs_encoding; use crate::transaction_payload::{EntryFunction, TransactionPayload}; use move_core_types::{account_address::AccountAddress, ident_str, language_storage::ModuleId}; use serde_json::json; use std::str::FromStr; use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_encoding::bcs; use tw_proto::{ Aptos::Proto::mod_LiquidStaking::OneOfliquid_stake_transaction_payload, Aptos::Proto::{LiquidStaking, TortugaClaim, TortugaStake, TortugaUnstake}, @@ -27,7 +27,7 @@ pub fn tortuga_stake( ), ident_str!("stake").to_owned(), vec![], - vec![bcs_encoding::encode(&amount)?], + vec![bcs::encode(&amount)?], json!([amount.to_string()]), ))) } @@ -43,7 +43,7 @@ pub fn tortuga_unstake( ), ident_str!("unstake").to_owned(), vec![], - vec![bcs_encoding::encode(&amount)?], + vec![bcs::encode(&amount)?], json!([amount.to_string()]), ))) } @@ -59,7 +59,7 @@ pub fn tortuga_claim( ), ident_str!("claim").to_owned(), vec![], - vec![bcs_encoding::encode(&idx)?], + vec![bcs::encode(&idx)?], json!([idx.to_string()]), ))) } diff --git a/rust/tw_aptos/src/transaction.rs b/rust/tw_aptos/src/transaction.rs index db496d8223b..b48d7950bbc 100644 --- a/rust/tw_aptos/src/transaction.rs +++ b/rust/tw_aptos/src/transaction.rs @@ -4,7 +4,6 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use crate::bcs_encoding::{self, BcsEncodingResult}; use crate::constants::APTOS_SALT; use crate::transaction_payload::TransactionPayload; use move_core_types::account_address::AccountAddress; @@ -13,6 +12,7 @@ use serde_json::{json, Value}; use std::borrow::Cow; use tw_coin_entry::error::SigningResult; use tw_encoding::hex::encode; +use tw_encoding::{bcs, EncodingResult}; use tw_keypair::ed25519::sha512::KeyPair; use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; use tw_memory::Data; @@ -125,8 +125,8 @@ impl RawTransaction { } /// Create a new `RawTransaction` with an entry function - fn serialize(&self) -> BcsEncodingResult { - bcs_encoding::encode(&self) + fn serialize(&self) -> EncodingResult { + bcs::encode(&self) } fn msg_to_sign(&self) -> SigningResult { @@ -151,7 +151,7 @@ impl RawTransaction { signature, }; let mut encoded = serialized.clone(); - encoded.extend_from_slice(bcs_encoding::encode(&auth)?.as_slice()); + encoded.extend_from_slice(bcs::encode(&auth)?.as_slice()); Ok(SignedTransaction { raw_txn: self.clone(), authenticator: auth, diff --git a/rust/tw_aptos/src/transaction_payload.rs b/rust/tw_aptos/src/transaction_payload.rs index d7034e5caa3..5e33e71d465 100644 --- a/rust/tw_aptos/src/transaction_payload.rs +++ b/rust/tw_aptos/src/transaction_payload.rs @@ -4,8 +4,6 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use crate::bcs_encoding; -use crate::bcs_encoding::{BcsEncodingError, BcsEncodingResult}; use crate::serde_helper::vec_bytes; use move_core_types::identifier::Identifier; use move_core_types::language_storage::{ModuleId, StructTag, TypeTag}; @@ -16,6 +14,7 @@ use serde_json::{json, Value}; use std::default::Default; use std::str::FromStr; use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_encoding::{bcs, EncodingError, EncodingResult}; use tw_memory::Data; use tw_proto::Aptos; @@ -27,14 +26,14 @@ pub enum EntryFunctionError { InvalidFunctionName, MissingArguments, InvalidArguments, - BcsSerializingError, + EncodingError, MissingTypeArguments, InvalidTypeArguments, } -impl From for EntryFunctionError { - fn from(_error: BcsEncodingError) -> Self { - EntryFunctionError::BcsSerializingError +impl From for EntryFunctionError { + fn from(_error: EncodingError) -> Self { + EntryFunctionError::EncodingError } } @@ -101,19 +100,19 @@ impl TryFrom for EntryFunction { } } -fn serialize_argument(arg: &TransactionArgument) -> BcsEncodingResult { +fn serialize_argument(arg: &TransactionArgument) -> EncodingResult { match arg { - TransactionArgument::U8(v) => bcs_encoding::encode(v), - TransactionArgument::U16(v) => bcs_encoding::encode(v), - TransactionArgument::U32(v) => bcs_encoding::encode(v), - TransactionArgument::U64(v) => bcs_encoding::encode(v), - TransactionArgument::U128(v) => bcs_encoding::encode(v), - TransactionArgument::U256(v) => bcs_encoding::encode(v), - TransactionArgument::U8Vector(v) => bcs_encoding::encode(v), - TransactionArgument::Bool(v) => bcs_encoding::encode(v), + TransactionArgument::U8(v) => bcs::encode(v), + TransactionArgument::U16(v) => bcs::encode(v), + TransactionArgument::U32(v) => bcs::encode(v), + TransactionArgument::U64(v) => bcs::encode(v), + TransactionArgument::U128(v) => bcs::encode(v), + TransactionArgument::U256(v) => bcs::encode(v), + TransactionArgument::U8Vector(v) => bcs::encode(v), + TransactionArgument::Bool(v) => bcs::encode(v), TransactionArgument::Address(v) => { - let serialized_v = bcs_encoding::encode(v)?; - bcs_encoding::encode(&serialized_v) + let serialized_v = bcs::encode(v)?; + bcs::encode(&serialized_v) }, } } @@ -244,10 +243,7 @@ mod tests { .unwrap() .inner(); let amount: i64 = 1000; - let args = vec![ - bcs_encoding::encode(&addr).unwrap(), - bcs_encoding::encode(&amount).unwrap(), - ]; + let args = vec![bcs::encode(&addr).unwrap(), bcs::encode(&amount).unwrap()]; let module = ModuleId::new(AccountAddress::ONE, Identifier::from_str("coin").unwrap()); let function = Identifier::from_str("transfer").unwrap(); let type_tag = vec![TypeTag::from_str("0x1::aptos_coin::AptosCoin").unwrap()]; @@ -259,7 +255,7 @@ mod tests { json!([addr.to_hex_literal(), amount.to_string()]), ); let tp = TransactionPayload::EntryFunction(entry); - let serialized = bcs_encoding::encode(&tp).unwrap(); + let serialized = bcs::encode(&tp).unwrap(); assert_eq!(hex::encode(serialized, false), "02000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b08e803000000000000"); let payload_value: Value = json!({ "function": "0x1::coin::transfer", diff --git a/rust/tw_coin_entry/Cargo.toml b/rust/tw_coin_entry/Cargo.toml index 8b55efc5c0e..41ab8b48576 100644 --- a/rust/tw_coin_entry/Cargo.toml +++ b/rust/tw_coin_entry/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] serde_json = "1.0.95" +tw_encoding = { path = "../tw_encoding" } tw_keypair = { path = "../tw_keypair" } tw_memory = { path = "../tw_memory" } tw_misc = { path = "../tw_misc" } diff --git a/rust/tw_coin_entry/src/error.rs b/rust/tw_coin_entry/src/error.rs index 7999b593e6a..0e39825ce3b 100644 --- a/rust/tw_coin_entry/src/error.rs +++ b/rust/tw_coin_entry/src/error.rs @@ -6,6 +6,7 @@ use std::fmt; use std::fmt::Formatter; +use tw_encoding::EncodingError; use tw_keypair::KeyPairError; use tw_number::NumberError; use tw_proto::Common::Proto; @@ -63,6 +64,12 @@ impl From for SigningError { } } +impl From for SigningError { + fn from(_e: EncodingError) -> Self { + SigningError(SigningErrorType::Error_input_parse) + } +} + impl From for SigningError { fn from(err: KeyPairError) -> Self { match err { diff --git a/rust/tw_encoding/Cargo.toml b/rust/tw_encoding/Cargo.toml index 2211e12bde9..4a1b666a551 100644 --- a/rust/tw_encoding/Cargo.toml +++ b/rust/tw_encoding/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] arbitrary = { version = "1", features = ["derive"], optional = true } +bcs = "0.1.6" bs58 = "0.4.0" ciborium = "0.2.1" data-encoding = "2.3.3" diff --git a/rust/tw_encoding/src/bcs.rs b/rust/tw_encoding/src/bcs.rs new file mode 100644 index 00000000000..8051ce4ac03 --- /dev/null +++ b/rust/tw_encoding/src/bcs.rs @@ -0,0 +1,16 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::{EncodingError, EncodingResult}; +use serde::Serialize; +use tw_memory::Data; + +pub fn encode(value: &T) -> EncodingResult +where + T: ?Sized + Serialize, +{ + bcs::to_bytes(value).map_err(|_| EncodingError::InvalidInput) +} diff --git a/rust/tw_encoding/src/lib.rs b/rust/tw_encoding/src/lib.rs index 3250e9ed4da..f658d9b019f 100644 --- a/rust/tw_encoding/src/lib.rs +++ b/rust/tw_encoding/src/lib.rs @@ -7,6 +7,7 @@ pub mod base32; pub mod base58; pub mod base64; +pub mod bcs; pub mod cbor; pub mod ffi; pub mod hex;