Cli: transfer ALL; check spend+fee in client (#10012)
* lamports->SOL in user-facing error msg * Check for sufficient balance for spend and fee * Add ALL option to solana transfer * Rework TransferAmount to check for sign_only in parse * Refactor TransferAmount & fee-check handling to be more general * Add addl checks mechanism * Move checks out of cli.rs * Rename to SpendAmount to be more general & move * Impl ALL/spend helpers for create-nonce-account * Impl spend helpers for create-vote-account * Impl ALL/spend helpers for create-stake-account * Impl spend helpers for ping * Impl ALL/spend helpers for pay * Impl spend helpers for validator-info * Remove unused fns * Remove retry_get_balance * Add a couple unit tests * Rework send_util fn signatures
This commit is contained in:
327
cli/src/cli.rs
327
cli/src/cli.rs
@@ -1,9 +1,11 @@
|
||||
use crate::{
|
||||
checks::*,
|
||||
cli_output::{CliAccount, CliSignOnlyData, CliSignature, OutputFormat},
|
||||
cluster_query::*,
|
||||
display::println_name_value,
|
||||
nonce::{self, *},
|
||||
offline::{blockhash_query::BlockhashQuery, *},
|
||||
spend_utils::*,
|
||||
stake::*,
|
||||
storage::*,
|
||||
validator_info::*,
|
||||
@@ -24,7 +26,7 @@ use solana_clap_utils::{
|
||||
ArgConstant,
|
||||
};
|
||||
use solana_client::{
|
||||
client_error::{ClientErrorKind, Result as ClientResult},
|
||||
client_error::{ClientError, ClientErrorKind, Result as ClientResult},
|
||||
rpc_client::RpcClient,
|
||||
rpc_config::RpcLargestAccountsFilter,
|
||||
rpc_response::{RpcAccount, RpcKeyedAccount},
|
||||
@@ -163,7 +165,7 @@ pub fn nonce_authority_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||
|
||||
#[derive(Default, Debug, PartialEq)]
|
||||
pub struct PayCommand {
|
||||
pub lamports: u64,
|
||||
pub amount: SpendAmount,
|
||||
pub to: Pubkey,
|
||||
pub timestamp: Option<DateTime<Utc>>,
|
||||
pub timestamp_pubkey: Option<Pubkey>,
|
||||
@@ -257,7 +259,7 @@ pub enum CliCommand {
|
||||
nonce_account: SignerIndex,
|
||||
seed: Option<String>,
|
||||
nonce_authority: Option<Pubkey>,
|
||||
lamports: u64,
|
||||
amount: SpendAmount,
|
||||
},
|
||||
GetNonce(Pubkey),
|
||||
NewNonce {
|
||||
@@ -283,7 +285,7 @@ pub enum CliCommand {
|
||||
staker: Option<Pubkey>,
|
||||
withdrawer: Option<Pubkey>,
|
||||
lockup: Lockup,
|
||||
lamports: u64,
|
||||
amount: SpendAmount,
|
||||
sign_only: bool,
|
||||
blockhash_query: BlockhashQuery,
|
||||
nonce_account: Option<Pubkey>,
|
||||
@@ -432,7 +434,7 @@ pub enum CliCommand {
|
||||
},
|
||||
TimeElapsed(Pubkey, Pubkey, DateTime<Utc>), // TimeElapsed(to, process_id, timestamp)
|
||||
Transfer {
|
||||
lamports: u64,
|
||||
amount: SpendAmount,
|
||||
to: Pubkey,
|
||||
from: SignerIndex,
|
||||
sign_only: bool,
|
||||
@@ -451,14 +453,20 @@ pub struct CliCommandInfo {
|
||||
pub signers: CliSigners,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, PartialEq)]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CliError {
|
||||
#[error("bad parameter: {0}")]
|
||||
BadParameter(String),
|
||||
#[error(transparent)]
|
||||
ClientError(#[from] ClientError),
|
||||
#[error("command not recognized: {0}")]
|
||||
CommandNotRecognized(String),
|
||||
#[error("insuficient funds for fee")]
|
||||
InsufficientFundsForFee,
|
||||
#[error("insufficient funds for fee ({0} SOL)")]
|
||||
InsufficientFundsForFee(f64),
|
||||
#[error("insufficient funds for spend ({0} SOL)")]
|
||||
InsufficientFundsForSpend(f64),
|
||||
#[error("insufficient funds for spend ({0} SOL) and fee ({1} SOL)")]
|
||||
InsufficientFundsForSpendAndFee(f64, f64),
|
||||
#[error(transparent)]
|
||||
InvalidNonce(CliNonceError),
|
||||
#[error("dynamic program error: {0}")]
|
||||
@@ -852,7 +860,7 @@ pub fn parse_command(
|
||||
}
|
||||
}
|
||||
("pay", Some(matches)) => {
|
||||
let lamports = lamports_of_sol(matches, "amount").unwrap();
|
||||
let amount = SpendAmount::new_from_matches(matches, "amount");
|
||||
let to = pubkey_of_signer(matches, "to", wallet_manager)?.unwrap();
|
||||
let timestamp = if matches.is_present("timestamp") {
|
||||
// Parse input for serde_json
|
||||
@@ -888,7 +896,7 @@ pub fn parse_command(
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports,
|
||||
amount,
|
||||
to,
|
||||
timestamp,
|
||||
timestamp_pubkey,
|
||||
@@ -958,7 +966,7 @@ pub fn parse_command(
|
||||
})
|
||||
}
|
||||
("transfer", Some(matches)) => {
|
||||
let lamports = lamports_of_sol(matches, "amount").unwrap();
|
||||
let amount = SpendAmount::new_from_matches(matches, "amount");
|
||||
let to = pubkey_of_signer(matches, "to", wallet_manager)?.unwrap();
|
||||
let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
|
||||
let no_wait = matches.is_present("no_wait");
|
||||
@@ -984,7 +992,7 @@ pub fn parse_command(
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::Transfer {
|
||||
lamports,
|
||||
amount,
|
||||
to,
|
||||
sign_only,
|
||||
no_wait,
|
||||
@@ -1011,48 +1019,6 @@ pub fn parse_command(
|
||||
|
||||
pub type ProcessResult = Result<String, Box<dyn std::error::Error>>;
|
||||
|
||||
pub fn check_account_for_fee(
|
||||
rpc_client: &RpcClient,
|
||||
account_pubkey: &Pubkey,
|
||||
fee_calculator: &FeeCalculator,
|
||||
message: &Message,
|
||||
) -> Result<(), Box<dyn error::Error>> {
|
||||
check_account_for_multiple_fees(rpc_client, account_pubkey, fee_calculator, &[message])
|
||||
}
|
||||
|
||||
fn check_account_for_multiple_fees(
|
||||
rpc_client: &RpcClient,
|
||||
account_pubkey: &Pubkey,
|
||||
fee_calculator: &FeeCalculator,
|
||||
messages: &[&Message],
|
||||
) -> Result<(), Box<dyn error::Error>> {
|
||||
let balance = rpc_client.retry_get_balance(account_pubkey, 5)?;
|
||||
if let Some(lamports) = balance {
|
||||
let fee = messages
|
||||
.iter()
|
||||
.map(|message| fee_calculator.calculate_fee(message))
|
||||
.sum();
|
||||
if lamports != 0 && lamports >= fee {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Err(CliError::InsufficientFundsForFee.into())
|
||||
}
|
||||
|
||||
pub fn check_unique_pubkeys(
|
||||
pubkey0: (&Pubkey, String),
|
||||
pubkey1: (&Pubkey, String),
|
||||
) -> Result<(), CliError> {
|
||||
if pubkey0.0 == pubkey1.0 {
|
||||
Err(CliError::BadParameter(format!(
|
||||
"Identical pubkeys found: `{}` and `{}` must be unique",
|
||||
pubkey0.1, pubkey1.1
|
||||
)))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_blockhash_and_fee_calculator(
|
||||
rpc_client: &RpcClient,
|
||||
sign_only: bool,
|
||||
@@ -1173,21 +1139,10 @@ fn process_airdrop(
|
||||
build_balance_message(lamports, false, true),
|
||||
faucet_addr
|
||||
);
|
||||
let previous_balance = match rpc_client.retry_get_balance(&pubkey, 5)? {
|
||||
Some(lamports) => lamports,
|
||||
None => {
|
||||
return Err(CliError::RpcRequestError(
|
||||
"Received result of an unexpected type".to_string(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
};
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, faucet_addr, &pubkey, lamports, &config)?;
|
||||
|
||||
let current_balance = rpc_client
|
||||
.retry_get_balance(&pubkey, 5)?
|
||||
.unwrap_or(previous_balance);
|
||||
let current_balance = rpc_client.get_balance(&pubkey)?;
|
||||
|
||||
Ok(build_balance_message(current_balance, false, true))
|
||||
}
|
||||
@@ -1389,7 +1344,7 @@ fn process_deploy(
|
||||
fn process_pay(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
lamports: u64,
|
||||
amount: SpendAmount,
|
||||
to: &Pubkey,
|
||||
timestamp: Option<DateTime<Utc>>,
|
||||
timestamp_pubkey: Option<Pubkey>,
|
||||
@@ -1416,29 +1371,35 @@ fn process_pay(
|
||||
|
||||
if timestamp == None && *witnesses == None {
|
||||
let nonce_authority = config.signers[nonce_authority];
|
||||
let ix = system_instruction::transfer(&config.signers[0].pubkey(), to, lamports);
|
||||
let message = if let Some(nonce_account) = &nonce_account {
|
||||
Message::new_with_nonce(vec![ix], None, nonce_account, &nonce_authority.pubkey())
|
||||
} else {
|
||||
Message::new(&[ix])
|
||||
let build_message = |lamports| {
|
||||
let ix = system_instruction::transfer(&config.signers[0].pubkey(), to, lamports);
|
||||
if let Some(nonce_account) = &nonce_account {
|
||||
Message::new_with_nonce(vec![ix], None, nonce_account, &nonce_authority.pubkey())
|
||||
} else {
|
||||
Message::new(&[ix])
|
||||
}
|
||||
};
|
||||
|
||||
let (message, _) = resolve_spend_tx_and_check_account_balance(
|
||||
rpc_client,
|
||||
sign_only,
|
||||
amount,
|
||||
&fee_calculator,
|
||||
&config.signers[0].pubkey(),
|
||||
build_message,
|
||||
)?;
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
|
||||
if sign_only {
|
||||
tx.try_partial_sign(&config.signers, blockhash)?;
|
||||
return_signers(&tx, &config)
|
||||
} else {
|
||||
tx.try_sign(&config.signers, blockhash)?;
|
||||
if let Some(nonce_account) = &nonce_account {
|
||||
let nonce_account = rpc_client.get_account(nonce_account)?;
|
||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &blockhash)?;
|
||||
}
|
||||
check_account_for_fee(
|
||||
rpc_client,
|
||||
&config.signers[0].pubkey(),
|
||||
&fee_calculator,
|
||||
&tx.message,
|
||||
)?;
|
||||
|
||||
tx.try_sign(&config.signers, blockhash)?;
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||
log_instruction_custom_error::<SystemError>(result, &config)
|
||||
}
|
||||
@@ -1451,29 +1412,33 @@ fn process_pay(
|
||||
|
||||
let contract_state = Keypair::new();
|
||||
|
||||
// Initializing contract
|
||||
let ixs = budget_instruction::on_date(
|
||||
let build_message = |lamports| {
|
||||
// Initializing contract
|
||||
let ixs = budget_instruction::on_date(
|
||||
&config.signers[0].pubkey(),
|
||||
to,
|
||||
&contract_state.pubkey(),
|
||||
dt,
|
||||
&dt_pubkey,
|
||||
cancelable,
|
||||
lamports,
|
||||
);
|
||||
Message::new(&ixs)
|
||||
};
|
||||
let (message, _) = resolve_spend_tx_and_check_account_balance(
|
||||
rpc_client,
|
||||
sign_only,
|
||||
amount,
|
||||
&fee_calculator,
|
||||
&config.signers[0].pubkey(),
|
||||
to,
|
||||
&contract_state.pubkey(),
|
||||
dt,
|
||||
&dt_pubkey,
|
||||
cancelable,
|
||||
lamports,
|
||||
);
|
||||
let message = Message::new(&ixs);
|
||||
build_message,
|
||||
)?;
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
if sign_only {
|
||||
tx.try_partial_sign(&[config.signers[0], &contract_state], blockhash)?;
|
||||
return_signers(&tx, &config)
|
||||
} else {
|
||||
tx.try_sign(&[config.signers[0], &contract_state], blockhash)?;
|
||||
check_account_for_fee(
|
||||
rpc_client,
|
||||
&config.signers[0].pubkey(),
|
||||
&fee_calculator,
|
||||
&tx.message,
|
||||
)?;
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||
let signature = log_instruction_custom_error::<BudgetError>(result, &config)?;
|
||||
Ok(json!({
|
||||
@@ -1494,16 +1459,26 @@ fn process_pay(
|
||||
|
||||
let contract_state = Keypair::new();
|
||||
|
||||
// Initializing contract
|
||||
let ixs = budget_instruction::when_signed(
|
||||
let build_message = |lamports| {
|
||||
// Initializing contract
|
||||
let ixs = budget_instruction::when_signed(
|
||||
&config.signers[0].pubkey(),
|
||||
to,
|
||||
&contract_state.pubkey(),
|
||||
&witness,
|
||||
cancelable,
|
||||
lamports,
|
||||
);
|
||||
Message::new(&ixs)
|
||||
};
|
||||
let (message, _) = resolve_spend_tx_and_check_account_balance(
|
||||
rpc_client,
|
||||
sign_only,
|
||||
amount,
|
||||
&fee_calculator,
|
||||
&config.signers[0].pubkey(),
|
||||
to,
|
||||
&contract_state.pubkey(),
|
||||
&witness,
|
||||
cancelable,
|
||||
lamports,
|
||||
);
|
||||
let message = Message::new(&ixs);
|
||||
build_message,
|
||||
)?;
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
if sign_only {
|
||||
tx.try_partial_sign(&[config.signers[0], &contract_state], blockhash)?;
|
||||
@@ -1511,12 +1486,6 @@ fn process_pay(
|
||||
} else {
|
||||
tx.try_sign(&[config.signers[0], &contract_state], blockhash)?;
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||
check_account_for_fee(
|
||||
rpc_client,
|
||||
&config.signers[0].pubkey(),
|
||||
&fee_calculator,
|
||||
&tx.message,
|
||||
)?;
|
||||
let signature = log_instruction_custom_error::<BudgetError>(result, &config)?;
|
||||
Ok(json!({
|
||||
"signature": signature,
|
||||
@@ -1576,7 +1545,7 @@ fn process_time_elapsed(
|
||||
fn process_transfer(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
lamports: u64,
|
||||
amount: SpendAmount,
|
||||
to: &Pubkey,
|
||||
from: SignerIndex,
|
||||
sign_only: bool,
|
||||
@@ -1595,38 +1564,50 @@ fn process_transfer(
|
||||
|
||||
let (recent_blockhash, fee_calculator) =
|
||||
blockhash_query.get_blockhash_and_fee_calculator(rpc_client)?;
|
||||
let ixs = vec![system_instruction::transfer(&from.pubkey(), to, lamports)];
|
||||
|
||||
let nonce_authority = config.signers[nonce_authority];
|
||||
let fee_payer = config.signers[fee_payer];
|
||||
|
||||
let message = if let Some(nonce_account) = &nonce_account {
|
||||
Message::new_with_nonce(
|
||||
ixs,
|
||||
Some(&fee_payer.pubkey()),
|
||||
nonce_account,
|
||||
&nonce_authority.pubkey(),
|
||||
)
|
||||
} else {
|
||||
Message::new_with_payer(&ixs, Some(&fee_payer.pubkey()))
|
||||
let build_message = |lamports| {
|
||||
let ixs = vec![system_instruction::transfer(&from.pubkey(), to, lamports)];
|
||||
|
||||
if let Some(nonce_account) = &nonce_account {
|
||||
Message::new_with_nonce(
|
||||
ixs,
|
||||
Some(&fee_payer.pubkey()),
|
||||
nonce_account,
|
||||
&nonce_authority.pubkey(),
|
||||
)
|
||||
} else {
|
||||
Message::new_with_payer(&ixs, Some(&fee_payer.pubkey()))
|
||||
}
|
||||
};
|
||||
|
||||
let (message, _) = resolve_spend_tx_and_check_account_balances(
|
||||
rpc_client,
|
||||
sign_only,
|
||||
amount,
|
||||
&fee_calculator,
|
||||
&from.pubkey(),
|
||||
&fee_payer.pubkey(),
|
||||
build_message,
|
||||
)?;
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
|
||||
if sign_only {
|
||||
tx.try_partial_sign(&config.signers, recent_blockhash)?;
|
||||
return_signers(&tx, &config)
|
||||
} else {
|
||||
if let Some(nonce_account) = &nonce_account {
|
||||
let nonce_account = rpc_client.get_account(nonce_account)?;
|
||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||
}
|
||||
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
if let Some(nonce_account) = &nonce_account {
|
||||
let nonce_account = rpc_client.get_account(nonce_account)?;
|
||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||
}
|
||||
check_account_for_fee(
|
||||
rpc_client,
|
||||
&tx.message.account_keys[0],
|
||||
&fee_calculator,
|
||||
&tx.message,
|
||||
)?;
|
||||
let result = if no_wait {
|
||||
rpc_client.send_transaction(&tx)
|
||||
} else {
|
||||
@@ -1786,14 +1767,14 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||
nonce_account,
|
||||
seed,
|
||||
nonce_authority,
|
||||
lamports,
|
||||
amount,
|
||||
} => process_create_nonce_account(
|
||||
&rpc_client,
|
||||
config,
|
||||
*nonce_account,
|
||||
seed.clone(),
|
||||
*nonce_authority,
|
||||
*lamports,
|
||||
*amount,
|
||||
),
|
||||
// Get the current nonce
|
||||
CliCommand::GetNonce(nonce_account_pubkey) => {
|
||||
@@ -1845,7 +1826,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||
staker,
|
||||
withdrawer,
|
||||
lockup,
|
||||
lamports,
|
||||
amount,
|
||||
sign_only,
|
||||
blockhash_query,
|
||||
ref nonce_account,
|
||||
@@ -1860,7 +1841,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||
staker,
|
||||
withdrawer,
|
||||
lockup,
|
||||
*lamports,
|
||||
*amount,
|
||||
*sign_only,
|
||||
blockhash_query,
|
||||
nonce_account.as_ref(),
|
||||
@@ -2166,7 +2147,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||
CliCommand::DecodeTransaction(transaction) => process_decode_transaction(transaction),
|
||||
// If client has positive balance, pay lamports to another address
|
||||
CliCommand::Pay(PayCommand {
|
||||
lamports,
|
||||
amount,
|
||||
to,
|
||||
timestamp,
|
||||
timestamp_pubkey,
|
||||
@@ -2179,7 +2160,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||
}) => process_pay(
|
||||
&rpc_client,
|
||||
config,
|
||||
*lamports,
|
||||
*amount,
|
||||
&to,
|
||||
*timestamp,
|
||||
*timestamp_pubkey,
|
||||
@@ -2213,7 +2194,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||
process_time_elapsed(&rpc_client, config, &to, &pubkey, *dt)
|
||||
}
|
||||
CliCommand::Transfer {
|
||||
lamports,
|
||||
amount,
|
||||
to,
|
||||
from,
|
||||
sign_only,
|
||||
@@ -2225,7 +2206,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||
} => process_transfer(
|
||||
&rpc_client,
|
||||
config,
|
||||
*lamports,
|
||||
*amount,
|
||||
to,
|
||||
*from,
|
||||
*sign_only,
|
||||
@@ -2520,9 +2501,9 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
|
||||
.index(2)
|
||||
.value_name("AMOUNT")
|
||||
.takes_value(true)
|
||||
.validator(is_amount)
|
||||
.validator(is_amount_or_all)
|
||||
.required(true)
|
||||
.help("The amount to send, in SOL"),
|
||||
.help("The amount to send, in SOL; accepts keyword ALL"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("timestamp")
|
||||
@@ -2632,9 +2613,9 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
|
||||
.index(2)
|
||||
.value_name("AMOUNT")
|
||||
.takes_value(true)
|
||||
.validator(is_amount)
|
||||
.validator(is_amount_or_all)
|
||||
.required(true)
|
||||
.help("The amount to send, in SOL"),
|
||||
.help("The amount to send, in SOL; accepts keyword ALL"),
|
||||
)
|
||||
.arg(
|
||||
pubkey!(Arg::with_name("from")
|
||||
@@ -2986,7 +2967,7 @@ mod tests {
|
||||
parse_command(&test_pay, &keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50_000_000_000,
|
||||
amount: SpendAmount::Some(50_000_000_000),
|
||||
to: pubkey,
|
||||
..PayCommand::default()
|
||||
}),
|
||||
@@ -3009,7 +2990,7 @@ mod tests {
|
||||
parse_command(&test_pay_multiple_witnesses, &keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50_000_000_000,
|
||||
amount: SpendAmount::Some(50_000_000_000),
|
||||
to: pubkey,
|
||||
witnesses: Some(vec![witness0, witness1]),
|
||||
..PayCommand::default()
|
||||
@@ -3029,7 +3010,7 @@ mod tests {
|
||||
parse_command(&test_pay_single_witness, &keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50_000_000_000,
|
||||
amount: SpendAmount::Some(50_000_000_000),
|
||||
to: pubkey,
|
||||
witnesses: Some(vec![witness0]),
|
||||
..PayCommand::default()
|
||||
@@ -3053,7 +3034,7 @@ mod tests {
|
||||
parse_command(&test_pay_timestamp, &keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50_000_000_000,
|
||||
amount: SpendAmount::Some(50_000_000_000),
|
||||
to: pubkey,
|
||||
timestamp: Some(dt),
|
||||
timestamp_pubkey: Some(witness0),
|
||||
@@ -3079,7 +3060,7 @@ mod tests {
|
||||
parse_command(&test_pay, &keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50_000_000_000,
|
||||
amount: SpendAmount::Some(50_000_000_000),
|
||||
to: pubkey,
|
||||
blockhash_query: BlockhashQuery::None(blockhash),
|
||||
sign_only: true,
|
||||
@@ -3102,7 +3083,7 @@ mod tests {
|
||||
parse_command(&test_pay, &keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50_000_000_000,
|
||||
amount: SpendAmount::Some(50_000_000_000),
|
||||
to: pubkey,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(
|
||||
blockhash_query::Source::Cluster,
|
||||
@@ -3131,7 +3112,7 @@ mod tests {
|
||||
parse_command(&test_pay, &keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50_000_000_000,
|
||||
amount: SpendAmount::Some(50_000_000_000),
|
||||
to: pubkey,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(
|
||||
blockhash_query::Source::NonceAccount(pubkey),
|
||||
@@ -3164,7 +3145,7 @@ mod tests {
|
||||
parse_command(&test_pay, &keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50_000_000_000,
|
||||
amount: SpendAmount::Some(50_000_000_000),
|
||||
to: pubkey,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(
|
||||
blockhash_query::Source::NonceAccount(pubkey),
|
||||
@@ -3202,7 +3183,7 @@ mod tests {
|
||||
parse_command(&test_pay, &keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50_000_000_000,
|
||||
amount: SpendAmount::Some(50_000_000_000),
|
||||
to: pubkey,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(
|
||||
blockhash_query::Source::NonceAccount(pubkey),
|
||||
@@ -3271,7 +3252,7 @@ mod tests {
|
||||
parse_command(&test_pay_multiple_witnesses, &keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50_000_000_000,
|
||||
amount: SpendAmount::Some(50_000_000_000),
|
||||
to: pubkey,
|
||||
timestamp: Some(dt),
|
||||
timestamp_pubkey: Some(witness0),
|
||||
@@ -3389,7 +3370,7 @@ mod tests {
|
||||
unix_timestamp: 0,
|
||||
custodian,
|
||||
},
|
||||
lamports: 1234,
|
||||
amount: SpendAmount::Some(30),
|
||||
sign_only: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
@@ -3443,7 +3424,7 @@ mod tests {
|
||||
nonce_authority: 0,
|
||||
split_stake_account: 1,
|
||||
seed: None,
|
||||
lamports: 1234,
|
||||
lamports: 30,
|
||||
fee_payer: 0,
|
||||
};
|
||||
config.signers = vec![&keypair, &split_stake_account];
|
||||
@@ -3462,7 +3443,7 @@ mod tests {
|
||||
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Pay(PayCommand {
|
||||
lamports: 10,
|
||||
amount: SpendAmount::Some(10),
|
||||
to: bob_pubkey,
|
||||
..PayCommand::default()
|
||||
});
|
||||
@@ -3472,7 +3453,7 @@ mod tests {
|
||||
let date_string = "\"2018-09-19T17:30:59Z\"";
|
||||
let dt: DateTime<Utc> = serde_json::from_str(&date_string).unwrap();
|
||||
config.command = CliCommand::Pay(PayCommand {
|
||||
lamports: 10,
|
||||
amount: SpendAmount::Some(10),
|
||||
to: bob_pubkey,
|
||||
timestamp: Some(dt),
|
||||
timestamp_pubkey: Some(config.signers[0].pubkey()),
|
||||
@@ -3483,7 +3464,7 @@ mod tests {
|
||||
|
||||
let witness = Pubkey::new_rand();
|
||||
config.command = CliCommand::Pay(PayCommand {
|
||||
lamports: 10,
|
||||
amount: SpendAmount::Some(10),
|
||||
to: bob_pubkey,
|
||||
witnesses: Some(vec![witness]),
|
||||
cancelable: true,
|
||||
@@ -3605,14 +3586,14 @@ mod tests {
|
||||
assert!(process_command(&config).is_err());
|
||||
|
||||
config.command = CliCommand::Pay(PayCommand {
|
||||
lamports: 10,
|
||||
amount: SpendAmount::Some(10),
|
||||
to: bob_pubkey,
|
||||
..PayCommand::default()
|
||||
});
|
||||
assert!(process_command(&config).is_err());
|
||||
|
||||
config.command = CliCommand::Pay(PayCommand {
|
||||
lamports: 10,
|
||||
amount: SpendAmount::Some(10),
|
||||
to: bob_pubkey,
|
||||
timestamp: Some(dt),
|
||||
timestamp_pubkey: Some(config.signers[0].pubkey()),
|
||||
@@ -3621,7 +3602,7 @@ mod tests {
|
||||
assert!(process_command(&config).is_err());
|
||||
|
||||
config.command = CliCommand::Pay(PayCommand {
|
||||
lamports: 10,
|
||||
amount: SpendAmount::Some(10),
|
||||
to: bob_pubkey,
|
||||
witnesses: Some(vec![witness]),
|
||||
cancelable: true,
|
||||
@@ -3688,7 +3669,29 @@ mod tests {
|
||||
parse_command(&test_transfer, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Transfer {
|
||||
lamports: 42_000_000_000,
|
||||
amount: SpendAmount::Some(42_000_000_000),
|
||||
to: to_pubkey,
|
||||
from: 0,
|
||||
sign_only: false,
|
||||
no_wait: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
fee_payer: 0,
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||
}
|
||||
);
|
||||
|
||||
// Test Transfer ALL
|
||||
let test_transfer = test_commands
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "transfer", &to_string, "ALL"]);
|
||||
assert_eq!(
|
||||
parse_command(&test_transfer, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Transfer {
|
||||
amount: SpendAmount::All,
|
||||
to: to_pubkey,
|
||||
from: 0,
|
||||
sign_only: false,
|
||||
@@ -3714,7 +3717,7 @@ mod tests {
|
||||
parse_command(&test_transfer, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Transfer {
|
||||
lamports: 42_000_000_000,
|
||||
amount: SpendAmount::Some(42_000_000_000),
|
||||
to: to_pubkey,
|
||||
from: 0,
|
||||
sign_only: false,
|
||||
@@ -3744,7 +3747,7 @@ mod tests {
|
||||
parse_command(&test_transfer, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Transfer {
|
||||
lamports: 42_000_000_000,
|
||||
amount: SpendAmount::Some(42_000_000_000),
|
||||
to: to_pubkey,
|
||||
from: 0,
|
||||
sign_only: true,
|
||||
@@ -3779,7 +3782,7 @@ mod tests {
|
||||
parse_command(&test_transfer, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Transfer {
|
||||
lamports: 42_000_000_000,
|
||||
amount: SpendAmount::Some(42_000_000_000),
|
||||
to: to_pubkey,
|
||||
from: 0,
|
||||
sign_only: false,
|
||||
@@ -3818,7 +3821,7 @@ mod tests {
|
||||
parse_command(&test_transfer, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Transfer {
|
||||
lamports: 42_000_000_000,
|
||||
amount: SpendAmount::Some(42_000_000_000),
|
||||
to: to_pubkey,
|
||||
from: 0,
|
||||
sign_only: false,
|
||||
|
Reference in New Issue
Block a user