Skip to content

Commit

Permalink
make a claim tx
Browse files Browse the repository at this point in the history
  • Loading branch information
brentstone committed Oct 20, 2023
1 parent 6d689d7 commit 7e1084e
Show file tree
Hide file tree
Showing 9 changed files with 236 additions and 8 deletions.
59 changes: 59 additions & 0 deletions apps/src/lib/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ pub mod cmds {
Bond(Bond),
Unbond(Unbond),
Withdraw(Withdraw),
ClaimRewards(ClaimRewards),
Redelegate(Redelegate),
AddToEthBridgePool(AddToEthBridgePool),
TxUpdateStewardCommission(TxUpdateStewardCommission),
Expand Down Expand Up @@ -1429,6 +1430,28 @@ pub mod cmds {
}
}

#[derive(Clone, Debug)]
pub struct ClaimRewards(pub args::ClaimRewards<args::CliTypes>);

impl SubCmd for ClaimRewards {
const CMD: &'static str = "claim-rewards";

fn parse(matches: &ArgMatches) -> Option<Self> {
matches
.subcommand_matches(Self::CMD)
.map(|matches| ClaimRewards(args::ClaimRewards::parse(matches)))
}

fn def() -> App {
App::new(Self::CMD)
.about(
"Claim available rewards tokens from bonds that \
contributed in consensus.",
)
.add_args::<args::ClaimRewards<args::CliTypes>>()
}
}

#[derive(Clone, Debug)]
pub struct Redelegate(pub args::Redelegate<args::CliTypes>);

Expand Down Expand Up @@ -2583,6 +2606,7 @@ pub mod args {
"tx_update_steward_commission.wasm";
pub const TX_VOTE_PROPOSAL: &str = "tx_vote_proposal.wasm";
pub const TX_WITHDRAW_WASM: &str = "tx_withdraw.wasm";
pub const TX_CLAIM_REWARDS_WASM: &str = "tx_claim_rewards.wasm";
pub const TX_RESIGN_STEWARD: &str = "tx_resign_steward.wasm";

pub const VP_USER_WASM: &str = "vp_user.wasm";
Expand Down Expand Up @@ -4466,6 +4490,41 @@ pub mod args {
}
}

impl CliToSdk<ClaimRewards<SdkTypes>> for ClaimRewards<CliTypes> {
fn to_sdk(self, ctx: &mut Context) -> ClaimRewards<SdkTypes> {
ClaimRewards::<SdkTypes> {
tx: self.tx.to_sdk(ctx),
validator: ctx.get(&self.validator),
source: self.source.map(|x| ctx.get(&x)),
tx_code_path: self.tx_code_path.to_path_buf(),
}
}
}

impl Args for ClaimRewards<CliTypes> {
fn parse(matches: &ArgMatches) -> Self {
let tx = Tx::parse(matches);
let validator = VALIDATOR.parse(matches);
let source = SOURCE_OPT.parse(matches);
let tx_code_path = PathBuf::from(TX_CLAIM_REWARDS_WASM);
Self {
tx,
validator,
source,
tx_code_path,
}
}

fn def(app: App) -> App {
app.add_args::<Tx<CliTypes>>()
.arg(VALIDATOR.def().help("Validator address."))
.arg(SOURCE_OPT.def().help(
"Source address for claiming rewards for a bond. For \
self-bonds, the validator is also the source.",
))
}
}

impl CliToSdk<QueryConversions<SdkTypes>> for QueryConversions<CliTypes> {
fn to_sdk(self, ctx: &mut Context) -> QueryConversions<SdkTypes> {
QueryConversions::<SdkTypes> {
Expand Down
11 changes: 11 additions & 0 deletions apps/src/lib/cli/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,17 @@ impl CliApi {
let namada = ctx.to_sdk(&client, io);
tx::submit_withdraw(&namada, args).await?;
}
Sub::ClaimRewards(ClaimRewards(mut args)) => {
let client = client.unwrap_or_else(|| {
C::from_tendermint_address(
&mut args.tx.ledger_address,
)
});
client.wait_until_node_is_synced(io).await?;
let args = args.to_sdk(&mut ctx);
let namada = ctx.to_sdk(&client, io);
tx::submit_claim_rewards(&namada, args).await?;
}
Sub::Redelegate(Redelegate(mut args)) => {
let client = client.unwrap_or_else(|| {
C::from_tendermint_address(
Expand Down
22 changes: 22 additions & 0 deletions apps/src/lib/client/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,28 @@ where
Ok(())
}

pub async fn submit_claim_rewards<'a, N: Namada<'a>>(
namada: &N,
args: args::ClaimRewards,
) -> Result<(), error::Error>
where
<N::Client as namada::ledger::queries::Client>::Error: std::fmt::Display,
{
let (mut tx, signing_data, _fee_unshield_epoch) =
args.build(namada).await?;
signing::generate_test_vector(namada, &tx).await?;

if args.tx.dump_tx {
tx::dump_tx(namada.io(), &args.tx, tx);
} else {
namada.sign(&mut tx, &args.tx, signing_data).await?;

namada.submit(tx, &args.tx).await?;
}

Ok(())
}

pub async fn submit_redelegate<'a, N: Namada<'a>>(
namada: &N,
args: args::Redelegate,
Expand Down
1 change: 1 addition & 0 deletions benches/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ pub const TX_CHANGE_VALIDATOR_COMMISSION_WASM: &str =
pub const TX_IBC_WASM: &str = "tx_ibc.wasm";
pub const TX_UNJAIL_VALIDATOR_WASM: &str = "tx_unjail_validator.wasm";
pub const TX_WITHDRAW_WASM: &str = "tx_withdraw.wasm";
pub const TX_CLAIM_REWARDS_WASM: &str = "tx_claim_rewards.wasm";
pub const TX_INIT_ACCOUNT_WASM: &str = "tx_init_account.wasm";
pub const TX_INIT_VALIDATOR_WASM: &str = "tx_init_validator.wasm";

Expand Down
21 changes: 21 additions & 0 deletions core/src/types/transaction/pos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,27 @@ pub struct Withdraw {
pub source: Option<Address>,
}

/// A claim of pending rewards.
#[derive(
Debug,
Clone,
PartialEq,
BorshSerialize,
BorshDeserialize,
BorshSchema,
Hash,
Eq,
Serialize,
Deserialize,
)]
pub struct ClaimRewards {
/// Validator address
pub validator: Address,
/// Source address for claiming rewards from a bond. For self-bonds, the
/// validator is also the source
pub source: Option<Address>,
}

/// A redelegation of bonded tokens from one validator to another.
#[derive(
Debug,
Expand Down
37 changes: 37 additions & 0 deletions sdk/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1076,6 +1076,43 @@ impl Withdraw {
}
}

/// Claim arguments
#[derive(Clone, Debug)]
pub struct ClaimRewards<C: NamadaTypes = SdkTypes> {
/// Common tx arguments
pub tx: Tx<C>,
/// Validator address
pub validator: C::Address,
/// Source address for claiming rewards due to bonds. For self-bonds, the
/// validator is also the source
pub source: Option<C::Address>,
/// Path to the TX WASM code file
pub tx_code_path: PathBuf,
}

impl<C: NamadaTypes> TxBuilder<C> for ClaimRewards<C> {
fn tx<F>(self, func: F) -> Self
where
F: FnOnce(Tx<C>) -> Tx<C>,
{
ClaimRewards {
tx: func(self.tx),
..self
}
}
}

impl ClaimRewards {
/// Build a transaction from this builder
pub async fn build<'a>(
&self,
context: &impl Namada<'a>,
) -> crate::error::Result<(crate::proto::Tx, SigningTxData, Option<Epoch>)>
{
tx::build_claim_rewards(context, self).await
}
}

/// Query asset conversions
#[derive(Clone, Debug)]
pub struct QueryConversions<C: NamadaTypes = SdkTypes> {
Expand Down
20 changes: 15 additions & 5 deletions sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ use crate::signing::SigningTxData;
use crate::token::DenominatedAmount;
use crate::tx::{
ProcessTxResponse, TX_BOND_WASM, TX_BRIDGE_POOL_WASM,
TX_CHANGE_COMMISSION_WASM, TX_IBC_WASM, TX_INIT_PROPOSAL,
TX_INIT_VALIDATOR_WASM, TX_RESIGN_STEWARD, TX_REVEAL_PK, TX_TRANSFER_WASM,
TX_UNBOND_WASM, TX_UNJAIL_VALIDATOR_WASM, TX_UPDATE_ACCOUNT_WASM,
TX_UPDATE_STEWARD_COMMISSION, TX_VOTE_PROPOSAL, TX_WITHDRAW_WASM,
VP_USER_WASM,
TX_CHANGE_COMMISSION_WASM, TX_CLAIM_REWARDS_WASM, TX_IBC_WASM,
TX_INIT_PROPOSAL, TX_INIT_VALIDATOR_WASM, TX_RESIGN_STEWARD, TX_REVEAL_PK,
TX_TRANSFER_WASM, TX_UNBOND_WASM, TX_UNJAIL_VALIDATOR_WASM,
TX_UPDATE_ACCOUNT_WASM, TX_UPDATE_STEWARD_COMMISSION, TX_VOTE_PROPOSAL,
TX_WITHDRAW_WASM, VP_USER_WASM,
};
use crate::wallet::{Wallet, WalletIo, WalletStorage};

Expand Down Expand Up @@ -323,6 +323,16 @@ pub trait Namada<'a>: Sized {
}
}

/// Make a Claim-rewards builder from the given minimum set of arguments
fn new_claim_rewards(&self, validator: Address) -> args::ClaimRewards {
args::ClaimRewards {
validator,
source: None,
tx_code_path: PathBuf::from(TX_CLAIM_REWARDS_WASM),
tx: self.tx_builder(),
}
}

/// Make a Withdraw builder from the given minimum set of arguments
fn new_add_erc20_transfer(
&self,
Expand Down
59 changes: 58 additions & 1 deletion sdk/src/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ pub const TX_BOND_WASM: &str = "tx_bond.wasm";
pub const TX_UNBOND_WASM: &str = "tx_unbond.wasm";
/// Withdraw WASM path
pub const TX_WITHDRAW_WASM: &str = "tx_withdraw.wasm";
/// Claim-rewards WASM path
pub const TX_CLAIM_REWARDS_WASM: &str = "tx_claim_rewards.wasm";
/// Bridge pool WASM path
pub const TX_BRIDGE_POOL_WASM: &str = "tx_bridge_pool.wasm";
/// Change commission WASM path
Expand Down Expand Up @@ -982,11 +984,18 @@ pub async fn build_withdraw<'a>(

let epoch = rpc::query_epoch(context.client()).await?;

// Check that the validator address is actually a validator
let validator =
known_validator_or_err(validator.clone(), tx_args.force, context)
.await?;

let source = source.clone();
// Check that the source address exists on chain
let source = match source.clone() {
Some(source) => source_exists_or_err(source, tx_args.force, context)
.await
.map(Some),
None => Ok(source.clone()),
}?;

// Check the source's current unbond amount
let bond_source = source.clone().unwrap_or_else(|| validator.clone());
Expand Down Expand Up @@ -1036,6 +1045,54 @@ pub async fn build_withdraw<'a>(
.map(|(tx, epoch)| (tx, signing_data, epoch))
}

/// Submit transaction to withdraw an unbond
pub async fn build_claim_rewards<'a>(
context: &impl Namada<'a>,
args::ClaimRewards {
tx: tx_args,
validator,
source,
tx_code_path,
}: &args::ClaimRewards,
) -> Result<(Tx, SigningTxData, Option<Epoch>)> {
let default_address = source.clone().unwrap_or(validator.clone());
let default_signer = Some(default_address.clone());
let signing_data = signing::aux_signing_data(
context,
tx_args,
Some(default_address),
default_signer,
)
.await?;

// Check that the validator address is actually a validator
let validator =
known_validator_or_err(validator.clone(), tx_args.force, context)
.await?;

// Check that the source address exists on chain
let source = match source.clone() {
Some(source) => source_exists_or_err(source, tx_args.force, context)
.await
.map(Some),
None => Ok(source.clone()),
}?;

let data = pos::ClaimRewards { validator, source };

build(
context,
tx_args,
tx_code_path.clone(),
data,
do_nothing,
&signing_data.fee_payer,
None,
)
.await
.map(|(tx, epoch)| (tx, signing_data, epoch))
}

/// Submit a transaction to unbond
pub async fn build_unbond<'a>(
context: &impl Namada<'a>,
Expand Down
14 changes: 12 additions & 2 deletions tx_prelude/src/proof_of_stake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use namada_core::types::{key, token};
pub use namada_proof_of_stake::parameters::PosParams;
use namada_proof_of_stake::{
become_validator, bond_tokens, change_validator_commission_rate,
read_pos_params, redelegate_tokens, unbond_tokens, unjail_validator,
withdraw_tokens, BecomeValidator,
claim_reward_tokens, read_pos_params, redelegate_tokens, unbond_tokens,
unjail_validator, withdraw_tokens, BecomeValidator,
};
pub use namada_proof_of_stake::{parameters, types, ResultSlashing};

Expand Down Expand Up @@ -88,6 +88,16 @@ impl Ctx {
)
}

/// Claim available reward tokens
pub fn claim_reward_tokens(
&mut self,
source: Option<&Address>,
validator: &Address,
) -> EnvResult<token::Amount> {
let current_epoch = self.get_block_epoch()?;
claim_reward_tokens(self, source, validator, current_epoch)
}

/// Attempt to initialize a validator account. On success, returns the
/// initialized validator account's address.
pub fn init_validator(
Expand Down

0 comments on commit 7e1084e

Please sign in to comment.