diff --git a/crates/aptos/CHANGELOG.md b/crates/aptos/CHANGELOG.md index c5afa9e567e26..1e1bbd63fab1b 100644 --- a/crates/aptos/CHANGELOG.md +++ b/crates/aptos/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to the Aptos CLI will be captured in this file. This project ## Unreleased - Add balance command to easily get account balances for APT currently +- Add network to config file +- Add explorer links to initialized accounts, and transaction submissions ## [3.4.1] - 2024/05/31 - Upgraded indexer processors for localnet from ca60e51b53c3be6f9517de7c73d4711e9c1f7236 to 5244b84fa5ed872e5280dc8df032d744d62ad29d. Upgraded Hasura metadata accordingly. diff --git a/crates/aptos/src/common/init.rs b/crates/aptos/src/common/init.rs index df07f1c8a6abd..46a5a7da8f6ad 100644 --- a/crates/aptos/src/common/init.rs +++ b/crates/aptos/src/common/init.rs @@ -9,7 +9,7 @@ use crate::{ ConfigSearchMode, EncodingOptions, HardwareWalletOptions, PrivateKeyInputOptions, ProfileConfig, ProfileOptions, PromptOptions, RngArgs, DEFAULT_PROFILE, }, - utils::{fund_account, prompt_yes_with_override, read_line}, + utils::{explorer_account_link, fund_account, prompt_yes_with_override, read_line}, }, }; use aptos_crypto::{ed25519::Ed25519PrivateKey, PrivateKey, ValidCryptoMaterialStringExt}; @@ -22,7 +22,11 @@ use async_trait::async_trait; use clap::Parser; use reqwest::Url; use serde::{Deserialize, Serialize}; -use std::{collections::BTreeMap, str::FromStr}; +use std::{ + collections::BTreeMap, + fmt::{Display, Formatter}, + str::FromStr, +}; /// 1 APT (might not actually get that much, depending on the faucet) const NUM_DEFAULT_OCTAS: u64 = 100000000; @@ -340,7 +344,16 @@ impl CliCommand<()> for InitTool { .expect("Must have profiles, as created above") .insert(profile_name.to_string(), profile_config); config.save()?; - eprintln!("\n---\nAptos CLI is now set up for account {} as profile {}! Run `aptos --help` for more information about commands", address, self.profile_options.profile_name().unwrap_or(DEFAULT_PROFILE)); + let profile_name = self + .profile_options + .profile_name() + .unwrap_or(DEFAULT_PROFILE); + eprintln!( + "\n---\nAptos CLI is now set up for account {} as profile {}!\n See the account here: {}\n Run `aptos --help` for more information about commands", + address, + profile_name, + explorer_account_link(address, Some(network)) + ); Ok(()) } } @@ -434,6 +447,18 @@ pub enum Network { Custom, } +impl Display for Network { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", match self { + Network::Mainnet => "mainnet", + Network::Testnet => "testnet", + Network::Devnet => "devnet", + Network::Local => "local", + Network::Custom => "custom", + }) + } +} + impl FromStr for Network { type Err = CliError; diff --git a/crates/aptos/src/common/types.rs b/crates/aptos/src/common/types.rs index 3874153cf5970..9db5e99f90ff2 100644 --- a/crates/aptos/src/common/types.rs +++ b/crates/aptos/src/common/types.rs @@ -1,7 +1,7 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -use super::utils::fund_account; +use super::utils::{explorer_transaction_link, fund_account}; use crate::{ common::{ init::Network, @@ -1717,25 +1717,19 @@ impl TransactionOptions { adjusted_max_gas }; - // Sign and submit transaction + // Build a transaction let transaction_factory = TransactionFactory::new(chain_id) .with_gas_unit_price(gas_unit_price) .with_max_gas_amount(max_gas) .with_transaction_expiration_time(self.gas_options.expiration_secs); - match self.get_transaction_account_type() { + // Sign it with the appropriate signer + let transaction = match self.get_transaction_account_type() { Ok(AccountType::Local) => { let (private_key, _) = self.get_key_and_address()?; let sender_account = &mut LocalAccount::new(sender_address, private_key, sequence_number); - let transaction = sender_account - .sign_with_transaction_builder(transaction_factory.payload(payload)); - let response = client - .submit_and_wait(&transaction) - .await - .map_err(|err| CliError::ApiError(err.to_string()))?; - - Ok(response.into_inner()) + sender_account.sign_with_transaction_builder(transaction_factory.payload(payload)) }, Ok(AccountType::HardwareWallet) => { let sender_account = &mut HardwareWalletAccount::new( @@ -1748,17 +1742,33 @@ impl TransactionOptions { HardwareWalletType::Ledger, sequence_number, ); - let transaction = sender_account - .sign_with_transaction_builder(transaction_factory.payload(payload))?; - let response = client - .submit_and_wait(&transaction) - .await - .map_err(|err| CliError::ApiError(err.to_string()))?; - - Ok(response.into_inner()) + sender_account + .sign_with_transaction_builder(transaction_factory.payload(payload))? }, - Err(err) => Err(err), - } + Err(err) => return Err(err), + }; + + // Submit the transaction, printing out a useful transaction link + client + .submit_bcs(&transaction) + .await + .map_err(|err| CliError::ApiError(err.to_string()))?; + let transaction_hash = transaction.clone().committed_hash(); + let network = self + .profile_options + .profile() + .ok() + .and_then(|profile| profile.network); + eprintln!( + "Transaction submitted: {}", + explorer_transaction_link(transaction_hash, network) + ); + let response = client + .wait_for_signed_transaction(&transaction) + .await + .map_err(|err| CliError::ApiError(err.to_string()))?; + + Ok(response.into_inner()) } /// Simulates a transaction locally, using the debugger to fetch required data from remote. diff --git a/crates/aptos/src/common/utils.rs b/crates/aptos/src/common/utils.rs index 9fcd7bc507c08..fcdaf59d4267f 100644 --- a/crates/aptos/src/common/utils.rs +++ b/crates/aptos/src/common/utils.rs @@ -2,9 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ - common::types::{ - account_address_from_public_key, CliError, CliTypedResult, PromptOptions, - TransactionOptions, TransactionSummary, + common::{ + init::Network, + types::{ + account_address_from_public_key, CliError, CliTypedResult, PromptOptions, + TransactionOptions, TransactionSummary, + }, }, config::GlobalConfig, CliResult, @@ -563,3 +566,31 @@ pub fn view_json_option_str(option_ref: &serde_json::Value) -> CliTypedResult) -> String { + // For now, default to what the browser is already on, though the link could be wrong + if let Some(network) = network { + format!( + "https://explorer.aptoslabs.com/account/{}?network={}", + hash, network + ) + } else { + format!("https://explorer.aptoslabs.com/account/{}", hash) + } +} + +pub fn explorer_transaction_link( + hash: aptos_crypto::HashValue, + network: Option, +) -> String { + // For now, default to what the browser is already on, though the link could be wrong + if let Some(network) = network { + format!( + "https://explorer.aptoslabs.com/txn/{}?network={}", + hash.to_hex_literal(), + network + ) + } else { + format!("https://explorer.aptoslabs.com/txn/{}", hash) + } +}