-
Notifications
You must be signed in to change notification settings - Fork 43
/
Copy pathbegin.rs
105 lines (91 loc) · 3.7 KB
/
begin.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// Copyright 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0
use serde::{Deserialize, Serialize};
use crate::{
client::{api::PreparedTransactionData, secret::SecretManage},
types::block::{
output::{feature::StakingFeature, AccountId, AccountOutputBuilder},
slot::EpochIndex,
},
utils::serde::string,
wallet::{types::TransactionWithMetadata, TransactionOptions, Wallet},
};
/// Parameters for beginning a staking period.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BeginStakingParams {
/// The account id which will begin staking.
pub account_id: AccountId,
/// The amount of tokens to stake.
#[serde(with = "string")]
pub staked_amount: u64,
/// The fixed cost of the validator, which it receives as part of its Mana rewards.
#[serde(with = "string")]
pub fixed_cost: u64,
/// The staking period (in epochs). Will default to the staking unbonding period.
pub staking_period: Option<u32>,
}
impl<S: 'static + SecretManage> Wallet<S>
where
crate::wallet::Error: From<S::Error>,
crate::client::Error: From<S::Error>,
{
pub async fn begin_staking(
&self,
params: BeginStakingParams,
options: impl Into<Option<TransactionOptions>> + Send,
) -> crate::wallet::Result<TransactionWithMetadata> {
let options = options.into();
let prepared = self.prepare_begin_staking(params, options.clone()).await?;
self.sign_and_submit_transaction(prepared, options).await
}
/// Prepares the transaction for [Wallet::begin_staking()].
pub async fn prepare_begin_staking(
&self,
params: BeginStakingParams,
options: impl Into<Option<TransactionOptions>> + Send,
) -> crate::wallet::Result<PreparedTransactionData> {
log::debug!("[TRANSACTION] prepare_begin_staking");
let account_id = params.account_id;
let account_output_data = self
.ledger()
.await
.unspent_account_output(&account_id)
.cloned()
.ok_or_else(|| crate::wallet::Error::AccountNotFound)?;
if account_output_data
.output
.features()
.map_or(false, |f| f.staking().is_some())
{
return Err(crate::wallet::Error::StakingFailed(format!(
"account id {account_id} already has a staking feature"
)));
}
let protocol_parameters = self.client().get_protocol_parameters().await?;
if let Some(staking_period) = params.staking_period {
if staking_period < protocol_parameters.staking_unbonding_period() {
return Err(crate::wallet::Error::StakingFailed(format!(
"staking period {staking_period} is less than the minimum {}",
protocol_parameters.staking_unbonding_period()
)));
}
}
let slot_commitment_id = self.client().get_issuance().await?.latest_commitment.id();
let start_epoch = protocol_parameters.epoch_index_of(protocol_parameters.past_bounded_slot(slot_commitment_id));
let output = AccountOutputBuilder::from(account_output_data.output.as_account())
.with_account_id(account_id)
.add_feature(StakingFeature::new(
params.staked_amount,
params.fixed_cost,
start_epoch,
params
.staking_period
.map(|period| start_epoch + period)
.unwrap_or(EpochIndex(u32::MAX)),
))
.finish_output()?;
let transaction = self.prepare_transaction([output], None, options).await?;
Ok(transaction)
}
}