2020-05-14 11:24:14 -07:00
|
|
|
use crate::{
|
2020-06-17 11:18:48 -07:00
|
|
|
checks::{calculate_fee, check_account_for_balance_with_commitment},
|
2020-05-14 11:24:14 -07:00
|
|
|
cli::CliError,
|
|
|
|
};
|
|
|
|
use clap::ArgMatches;
|
|
|
|
use solana_clap_utils::{input_parsers::lamports_of_sol, offline::SIGN_ONLY_ARG};
|
|
|
|
use solana_client::rpc_client::RpcClient;
|
|
|
|
use solana_sdk::{
|
2020-06-17 11:18:48 -07:00
|
|
|
commitment_config::CommitmentConfig, fee_calculator::FeeCalculator, message::Message,
|
|
|
|
native_token::lamports_to_sol, pubkey::Pubkey,
|
2020-05-14 11:24:14 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
|
|
|
pub enum SpendAmount {
|
|
|
|
All,
|
|
|
|
Some(u64),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for SpendAmount {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::Some(u64::default())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SpendAmount {
|
|
|
|
pub fn new(amount: Option<u64>, sign_only: bool) -> Self {
|
|
|
|
match amount {
|
|
|
|
Some(lamports) => Self::Some(lamports),
|
|
|
|
None if !sign_only => Self::All,
|
|
|
|
_ => panic!("ALL amount not supported for sign-only operations"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new_from_matches(matches: &ArgMatches<'_>, name: &str) -> Self {
|
|
|
|
let amount = lamports_of_sol(matches, name);
|
|
|
|
let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
|
|
|
|
SpendAmount::new(amount, sign_only)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct SpendAndFee {
|
|
|
|
spend: u64,
|
|
|
|
fee: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn resolve_spend_tx_and_check_account_balance<F>(
|
|
|
|
rpc_client: &RpcClient,
|
|
|
|
sign_only: bool,
|
|
|
|
amount: SpendAmount,
|
|
|
|
fee_calculator: &FeeCalculator,
|
|
|
|
from_pubkey: &Pubkey,
|
|
|
|
build_message: F,
|
2020-06-17 11:18:48 -07:00
|
|
|
commitment: CommitmentConfig,
|
2020-05-14 11:24:14 -07:00
|
|
|
) -> Result<(Message, u64), CliError>
|
|
|
|
where
|
|
|
|
F: Fn(u64) -> Message,
|
|
|
|
{
|
|
|
|
resolve_spend_tx_and_check_account_balances(
|
|
|
|
rpc_client,
|
|
|
|
sign_only,
|
|
|
|
amount,
|
|
|
|
fee_calculator,
|
|
|
|
from_pubkey,
|
|
|
|
from_pubkey,
|
|
|
|
build_message,
|
2020-06-17 11:18:48 -07:00
|
|
|
commitment,
|
2020-05-14 11:24:14 -07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn resolve_spend_tx_and_check_account_balances<F>(
|
|
|
|
rpc_client: &RpcClient,
|
|
|
|
sign_only: bool,
|
|
|
|
amount: SpendAmount,
|
|
|
|
fee_calculator: &FeeCalculator,
|
|
|
|
from_pubkey: &Pubkey,
|
|
|
|
fee_pubkey: &Pubkey,
|
|
|
|
build_message: F,
|
2020-06-17 11:18:48 -07:00
|
|
|
commitment: CommitmentConfig,
|
2020-05-14 11:24:14 -07:00
|
|
|
) -> Result<(Message, u64), CliError>
|
|
|
|
where
|
|
|
|
F: Fn(u64) -> Message,
|
|
|
|
{
|
|
|
|
if sign_only {
|
|
|
|
let (message, SpendAndFee { spend, fee: _ }) = resolve_spend_message(
|
|
|
|
amount,
|
|
|
|
fee_calculator,
|
|
|
|
0,
|
|
|
|
from_pubkey,
|
|
|
|
fee_pubkey,
|
|
|
|
build_message,
|
|
|
|
);
|
|
|
|
Ok((message, spend))
|
|
|
|
} else {
|
2020-06-17 11:18:48 -07:00
|
|
|
let from_balance = rpc_client
|
|
|
|
.get_balance_with_commitment(&from_pubkey, commitment)?
|
|
|
|
.value;
|
2020-05-14 11:24:14 -07:00
|
|
|
let (message, SpendAndFee { spend, fee }) = resolve_spend_message(
|
|
|
|
amount,
|
|
|
|
fee_calculator,
|
|
|
|
from_balance,
|
|
|
|
from_pubkey,
|
|
|
|
fee_pubkey,
|
|
|
|
build_message,
|
|
|
|
);
|
|
|
|
if from_pubkey == fee_pubkey {
|
|
|
|
if from_balance == 0 || from_balance < spend + fee {
|
|
|
|
return Err(CliError::InsufficientFundsForSpendAndFee(
|
|
|
|
lamports_to_sol(spend),
|
|
|
|
lamports_to_sol(fee),
|
2021-03-02 11:13:41 -08:00
|
|
|
*from_pubkey,
|
2020-05-14 11:24:14 -07:00
|
|
|
));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if from_balance < spend {
|
2021-03-02 11:13:41 -08:00
|
|
|
return Err(CliError::InsufficientFundsForSpend(
|
|
|
|
lamports_to_sol(spend),
|
|
|
|
*from_pubkey,
|
|
|
|
));
|
2020-05-14 11:24:14 -07:00
|
|
|
}
|
2020-06-17 11:18:48 -07:00
|
|
|
if !check_account_for_balance_with_commitment(rpc_client, fee_pubkey, fee, commitment)?
|
|
|
|
{
|
2021-03-02 11:13:41 -08:00
|
|
|
return Err(CliError::InsufficientFundsForFee(
|
|
|
|
lamports_to_sol(fee),
|
|
|
|
*fee_pubkey,
|
|
|
|
));
|
2020-05-14 11:24:14 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok((message, spend))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn resolve_spend_message<F>(
|
|
|
|
amount: SpendAmount,
|
|
|
|
fee_calculator: &FeeCalculator,
|
|
|
|
from_balance: u64,
|
|
|
|
from_pubkey: &Pubkey,
|
|
|
|
fee_pubkey: &Pubkey,
|
|
|
|
build_message: F,
|
|
|
|
) -> (Message, SpendAndFee)
|
|
|
|
where
|
|
|
|
F: Fn(u64) -> Message,
|
|
|
|
{
|
|
|
|
match amount {
|
|
|
|
SpendAmount::Some(lamports) => {
|
|
|
|
let message = build_message(lamports);
|
|
|
|
let fee = calculate_fee(fee_calculator, &[&message]);
|
|
|
|
(
|
|
|
|
message,
|
|
|
|
SpendAndFee {
|
|
|
|
spend: lamports,
|
|
|
|
fee,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
SpendAmount::All => {
|
|
|
|
let dummy_message = build_message(0);
|
|
|
|
let fee = calculate_fee(fee_calculator, &[&dummy_message]);
|
|
|
|
let lamports = if from_pubkey == fee_pubkey {
|
|
|
|
from_balance.saturating_sub(fee)
|
|
|
|
} else {
|
|
|
|
from_balance
|
|
|
|
};
|
|
|
|
(
|
|
|
|
build_message(lamports),
|
|
|
|
SpendAndFee {
|
|
|
|
spend: lamports,
|
|
|
|
fee,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|