Skip to content

Commit

Permalink
feat: New command: staking - delegation (#227)
Browse files Browse the repository at this point in the history
  • Loading branch information
FroVolod authored Oct 31, 2023
1 parent 5962e2b commit 2e4226f
Show file tree
Hide file tree
Showing 26 changed files with 1,935 additions and 104 deletions.
318 changes: 290 additions & 28 deletions docs/GUIDE.en.md

Large diffs are not rendered by default.

321 changes: 296 additions & 25 deletions docs/GUIDE.ru.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,10 @@ impl From<SignerAccountIdContext> for crate::commands::ActionContext {
],
new_account_id.clone())
} else {
let args = json!({
let args = serde_json::to_vec(&json!({
"new_account_id": new_account_id.clone().to_string(),
"new_public_key": item.account_properties.public_key.to_string()
})
.to_string()
.into_bytes();
}))?;

if let Some(linkdrop_account_id) = &network_config.linkdrop_account_id {
if new_account_id.is_sub_account_of(linkdrop_account_id)
Expand Down
6 changes: 3 additions & 3 deletions src/commands/account/storage_management/storage_deposit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,9 @@ impl SignerAccountIdContext {
actions: vec![near_primitives::transaction::Action::FunctionCall(
near_primitives::transaction::FunctionCallAction {
method_name: "storage_deposit".to_string(),
args: serde_json::json!({ "account_id": &receiver_account_id })
.to_string()
.into_bytes(),
args: serde_json::to_vec(&serde_json::json!({
"account_id": &receiver_account_id
}))?,
gas: crate::common::NearGas::from_tgas(50).as_gas(),
deposit: deposit.as_yoctonear(),
},
Expand Down
6 changes: 2 additions & 4 deletions src/commands/account/storage_management/storage_withdraw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,9 @@ impl SignerAccountIdContext {
actions: vec![near_primitives::transaction::Action::FunctionCall(
near_primitives::transaction::FunctionCallAction {
method_name: "storage_withdraw".to_string(),
args: serde_json::json!({
args: serde_json::to_vec(&serde_json::json!({
"amount": amount.clone().as_yoctonear().to_string()
})
.to_string()
.into_bytes(),
}))?,
gas: crate::common::NearGas::from_tgas(50).as_gas(),
deposit: near_token::NearToken::from_yoctonear(1).as_yoctonear(),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,9 @@ impl AccountContext {
.blocking_call_view_function(
&contract_account_id,
"storage_balance_of",
serde_json::json!({
serde_json::to_vec(&serde_json::json!({
"account_id": account_id.to_string(),
})
.to_string()
.into_bytes(),
}))?,
block_reference.clone(),
)
.wrap_err_with(|| {
Expand Down
6 changes: 2 additions & 4 deletions src/commands/account/update_social_profile/sign_as.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,9 @@ impl From<SignerContext> for crate::commands::ActionContext {
.blocking_call_view_function(
&contract_account_id,
"get",
serde_json::json!({
serde_json::to_vec(&serde_json::json!({
"keys": vec![format!("{account_id}/profile/**")],
})
.to_string()
.into_bytes(),
}))?,
near_primitives::types::Finality::Final.into(),
)
.wrap_err_with(|| {format!("Failed to fetch query for view method: 'get {account_id}/profile/**'")})?
Expand Down
82 changes: 75 additions & 7 deletions src/commands/account/view_account_summary/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use color_eyre::eyre::Context;
use futures::{StreamExt, TryStreamExt};

use crate::common::{CallResultExt, JsonRpcClientExt, RpcQueryResponseExt};

Expand Down Expand Up @@ -26,9 +27,10 @@ impl ViewAccountSummaryContext {
let account_id: near_primitives::types::AccountId = scope.account_id.clone().into();

move |network_config, block_reference| {
let rpc_query_response = network_config
.json_rpc_client()
.blocking_call_view_account(&account_id, block_reference.clone())
let json_rpc_client = network_config.json_rpc_client();

let rpc_query_response = json_rpc_client
.blocking_call_view_account(&account_id.clone(), block_reference.clone())
.wrap_err_with(|| {
format!(
"Failed to fetch query ViewAccount for <{}>",
Expand All @@ -51,18 +53,43 @@ impl ViewAccountSummaryContext {
})?
.access_key_list_view()?;

let validators_stake = crate::common::get_validators_stake(&json_rpc_client)?;

let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()?;
let concurrency = 10;
let delegated_stake: std::collections::BTreeMap<near_primitives::types::AccountId, near_token::NearToken> = runtime
.block_on(
futures::stream::iter(validators_stake.into_keys())
.map(|validator_account_id| async {
let balance = get_delegated_staked_balance(&json_rpc_client, block_reference, &validator_account_id, &account_id).await?;
Ok::<_, color_eyre::eyre::Report>((
validator_account_id,
balance,
))
})
.buffer_unordered(concurrency)
.filter(|balance_result| futures::future::ready(
if let Ok((_, balance)) = balance_result {
!balance.is_zero()
} else {
true
}
))
.try_collect(),
)?;

let contract_account_id = network_config.get_near_social_account_id_from_network()?;

let social_db = network_config
.json_rpc_client()
.blocking_call_view_function(
&contract_account_id,
"get",
serde_json::json!({
serde_json::to_vec(&serde_json::json!({
"keys": vec![format!("{account_id}/profile/**")],
})
.to_string()
.into_bytes(),
}))?,
block_reference.clone(),
)
.wrap_err_with(|| {format!("Failed to fetch query for view method: 'get {account_id}/profile/**'")})?
Expand All @@ -75,6 +102,7 @@ impl ViewAccountSummaryContext {
&rpc_query_response.block_hash,
&rpc_query_response.block_height,
&account_id,
&delegated_stake,
&account_view,
&access_key_list.keys,
social_db.accounts.get(&account_id)
Expand Down Expand Up @@ -107,3 +135,43 @@ impl ViewAccountSummary {
)
}
}

async fn get_delegated_staked_balance(
json_rpc_client: &near_jsonrpc_client::JsonRpcClient,
block_reference: &near_primitives::types::BlockReference,
staking_pool_account_id: &near_primitives::types::AccountId,
account_id: &near_primitives::types::AccountId,
) -> color_eyre::eyre::Result<near_token::NearToken> {
let account_staked_balance_response = json_rpc_client
.call(near_jsonrpc_client::methods::query::RpcQueryRequest {
block_reference: block_reference.clone(),
request: near_primitives::views::QueryRequest::CallFunction {
account_id: staking_pool_account_id.clone(),
method_name: "get_account_staked_balance".to_string(),
args: near_primitives::types::FunctionArgs::from(serde_json::to_vec(
&serde_json::json!({
"account_id": account_id,
}),
)?),
},
})
.await;
match account_staked_balance_response {
Ok(response) => Ok(near_token::NearToken::from_yoctonear(
response
.call_result()?
.parse_result_from_json::<String>()
.wrap_err("Failed to parse return value of view function call for String.")?
.parse::<u128>()?,
)),
Err(near_jsonrpc_client::errors::JsonRpcError::ServerError(
near_jsonrpc_client::errors::JsonRpcServerError::HandlerError(
near_jsonrpc_client::methods::query::RpcQueryError::NoContractCode { .. }
| near_jsonrpc_client::methods::query::RpcQueryError::ContractExecutionError {
..
},
),
)) => Ok(near_token::NearToken::from_yoctonear(0)),
Err(err) => Err(err.into()),
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ pub fn function_args(
super::call_function_args_type::FunctionArgsType::JsonArgs => {
let data_json =
serde_json::Value::from_str(&args).wrap_err("Data not in JSON format!")?;
Ok(data_json.to_string().into_bytes())
serde_json::to_vec(&data_json).wrap_err("Internal error!")
}
super::call_function_args_type::FunctionArgsType::TextArgs => Ok(args.into_bytes()),
super::call_function_args_type::FunctionArgsType::Base64Args => {
Expand Down
6 changes: 6 additions & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use strum::{EnumDiscriminants, EnumIter, EnumMessage};
pub mod account;
mod config;
pub mod contract;
mod staking;
mod tokens;
mod transaction;

Expand All @@ -25,6 +26,11 @@ pub enum TopLevelCommand {
))]
/// Use this for token actions: send or view balances of NEAR, FT, or NFT
Tokens(self::tokens::TokensCommands),
#[strum_discriminants(strum(
message = "staking - Manage staking: view, add and withdraw stake"
))]
/// Use this for manage staking: view, add and withdraw stake
Staking(self::staking::Staking),
#[strum_discriminants(strum(
message = "contract - Manage smart-contracts: deploy code, call functions"
))]
Expand Down
89 changes: 89 additions & 0 deletions src/commands/staking/delegate/deposit_and_stake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#[derive(Debug, Clone, interactive_clap::InteractiveClap)]
#[interactive_clap(input_context = super::StakeDelegationContext)]
#[interactive_clap(output_context = DepositAndStakeContext)]
pub struct DepositAndStake {
/// Enter the attached amount to be deposited and then staked into the predecessor's internal account (example: 10NEAR or 0.5near or 10000yoctonear):
amount: near_token::NearToken,
#[interactive_clap(skip_default_input_arg)]
/// What is validator account ID?
validator_account_id: crate::types::account_id::AccountId,
#[interactive_clap(named_arg)]
/// Select network
network_config: crate::network_for_transaction::NetworkForTransactionArgs,
}

#[derive(Clone)]
pub struct DepositAndStakeContext(crate::commands::ActionContext);

impl DepositAndStakeContext {
pub fn from_previous_context(
previous_context: super::StakeDelegationContext,
scope: &<DepositAndStake as interactive_clap::ToInteractiveClapContextScope>::InteractiveClapContextScope,
) -> color_eyre::eyre::Result<Self> {
let on_after_getting_network_callback: crate::commands::OnAfterGettingNetworkCallback =
std::sync::Arc::new({
let signer_id = previous_context.account_id.clone();
let validator_account_id: near_primitives::types::AccountId =
scope.validator_account_id.clone().into();
let amount = scope.amount;

move |_network_config| {
Ok(crate::commands::PrepopulatedTransaction {
signer_id: signer_id.clone(),
receiver_id: validator_account_id.clone(),
actions: vec![near_primitives::transaction::Action::FunctionCall(
near_primitives::transaction::FunctionCallAction {
method_name: "deposit_and_stake".to_string(),
args: serde_json::to_vec(&serde_json::json!({}))?,
gas: crate::common::NearGas::from_tgas(50).as_gas(),
deposit: amount.as_yoctonear(),
},
)],
})
}
});

let on_after_sending_transaction_callback: crate::transaction_signature_options::OnAfterSendingTransactionCallback = std::sync::Arc::new({
let signer_id = previous_context.account_id.clone();
let validator_id = scope.validator_account_id.clone();
let amount = scope.amount;

move |outcome_view, _network_config| {
if let near_primitives::views::FinalExecutionStatus::SuccessValue(_) = outcome_view.status {
eprintln!("<{signer_id}> has successfully delegated {amount} to stake with <{validator_id}>.")
}
Ok(())
}
});

Ok(Self(crate::commands::ActionContext {
global_context: previous_context.global_context,
interacting_with_account_ids: vec![
previous_context.account_id.clone(),
scope.validator_account_id.clone().into(),
],
on_after_getting_network_callback,
on_before_signing_callback: std::sync::Arc::new(
|_prepolulated_unsinged_transaction, _network_config| Ok(()),
),
on_before_sending_transaction_callback: std::sync::Arc::new(
|_signed_transaction, _network_config, _message| Ok(()),
),
on_after_sending_transaction_callback,
}))
}
}

impl From<DepositAndStakeContext> for crate::commands::ActionContext {
fn from(item: DepositAndStakeContext) -> Self {
item.0
}
}

impl DepositAndStake {
pub fn input_validator_account_id(
context: &super::StakeDelegationContext,
) -> color_eyre::eyre::Result<Option<crate::types::account_id::AccountId>> {
crate::common::input_staking_pool_validator_account_id(&context.global_context.config)
}
}
Loading

0 comments on commit 2e4226f

Please sign in to comment.