Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Allow to override the receiver target for PoS claim rewards transaction.
([\#4402](https://github.com/anoma/namada/pull/4402))
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- SDK and client and now optionally override the receiver target for PoS claim
rewards transaction. ([\#4427](https://github.com/anoma/namada/pull/4427))
9 changes: 9 additions & 0 deletions crates/apps_lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3627,6 +3627,7 @@ pub mod args {
pub const RAW_PUBLIC_KEY_HASH_OPT: ArgOpt<String> =
RAW_PUBLIC_KEY_HASH.opt();
pub const RECEIVER: Arg<String> = arg("receiver");
pub const RECEIVER_ADDR: ArgOpt<WalletAddress> = arg_opt("receiver");
pub const REFUND_TARGET: ArgOpt<WalletTransferTarget> =
arg_opt("refund-target");
pub const RELAYER: Arg<Address> = arg("relayer");
Expand Down Expand Up @@ -6359,6 +6360,7 @@ pub mod args {
tx,
validator: chain_ctx.get(&self.validator),
source: self.source.map(|x| chain_ctx.get(&x)),
receiver: self.receiver.map(|x| chain_ctx.get(&x)),
tx_code_path: self.tx_code_path.to_path_buf(),
})
}
Expand All @@ -6369,11 +6371,13 @@ pub mod args {
let tx = Tx::parse(matches);
let validator = VALIDATOR.parse(matches);
let source = SOURCE_OPT.parse(matches);
let receiver = RECEIVER_ADDR.parse(matches);
let tx_code_path = PathBuf::from(TX_CLAIM_REWARDS_WASM);
Self {
tx,
validator,
source,
receiver,
tx_code_path,
}
}
Expand All @@ -6385,6 +6389,11 @@ pub mod args {
"Source address for claiming rewards for a bond. For \
self-bonds, the validator is also the source."
)))
.arg(RECEIVER_ADDR.def().help(wrap!(
"An optional receiver address. If not given, the rewards \
will be received by the owner (i.e. the source of a \
delegation or the validator of a self-bond)."
)))
}
}

Expand Down
97 changes: 59 additions & 38 deletions crates/node/src/shell/finalize_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2444,15 +2444,18 @@ mod test_finalize_block {
.unwrap();

// Claim the rewards from the initial epoch
let reward_1 =
proof_of_stake::claim_reward_tokens::<
_,
governance::Store<_>,
token::Store<_>,
>(
&mut shell.state, None, &validator.address, current_epoch
)
.unwrap();
let reward_1 = proof_of_stake::claim_reward_tokens::<
_,
governance::Store<_>,
token::Store<_>,
>(
&mut shell.state,
None,
&validator.address,
None,
current_epoch,
)
.unwrap();
total_claimed += reward_1;
assert_eq!(reward_1, query_rewards);
assert!(is_reward_equal_enough(total_rewards, total_claimed, 1));
Expand Down Expand Up @@ -2481,7 +2484,11 @@ mod test_finalize_block {
governance::Store<_>,
token::Store<_>,
>(
&mut shell.state, None, &validator.address, current_epoch
&mut shell.state,
None,
&validator.address,
None,
current_epoch,
)
.unwrap();
assert_eq!(att, token::Amount::zero());
Expand Down Expand Up @@ -2520,7 +2527,11 @@ mod test_finalize_block {
governance::Store<_>,
token::Store<_>,
>(
&mut shell.state, None, &validator.address, current_epoch
&mut shell.state,
None,
&validator.address,
None,
current_epoch,
)
.unwrap();
total_claimed += rew;
Expand Down Expand Up @@ -2601,15 +2612,18 @@ mod test_finalize_block {
.unwrap();

// Claim tokens
let reward_2 =
proof_of_stake::claim_reward_tokens::<
_,
governance::Store<_>,
token::Store<_>,
>(
&mut shell.state, None, &validator.address, current_epoch
)
.unwrap();
let reward_2 = proof_of_stake::claim_reward_tokens::<
_,
governance::Store<_>,
token::Store<_>,
>(
&mut shell.state,
None,
&validator.address,
None,
current_epoch,
)
.unwrap();
total_claimed += reward_2;
assert_eq!(query_rewards, reward_2);

Expand Down Expand Up @@ -2730,15 +2744,18 @@ mod test_finalize_block {
}

// Claim the rewards for the validator for the first two epochs
let val_reward_1 =
proof_of_stake::claim_reward_tokens::<
_,
governance::Store<_>,
token::Store<_>,
>(
&mut shell.state, None, &validator.address, current_epoch
)
.unwrap();
let val_reward_1 = proof_of_stake::claim_reward_tokens::<
_,
governance::Store<_>,
token::Store<_>,
>(
&mut shell.state,
None,
&validator.address,
None,
current_epoch,
)
.unwrap();
total_claimed += val_reward_1;
assert!(is_reward_equal_enough(
total_rewards,
Expand All @@ -2758,15 +2775,18 @@ mod test_finalize_block {
total_rewards += inflation_3;

// Claim again for the validator
let val_reward_2 =
proof_of_stake::claim_reward_tokens::<
_,
governance::Store<_>,
token::Store<_>,
>(
&mut shell.state, None, &validator.address, current_epoch
)
.unwrap();
let val_reward_2 = proof_of_stake::claim_reward_tokens::<
_,
governance::Store<_>,
token::Store<_>,
>(
&mut shell.state,
None,
&validator.address,
None,
current_epoch,
)
.unwrap();

// Claim for the delegator
let del_reward_1 = proof_of_stake::claim_reward_tokens::<
Expand All @@ -2777,6 +2797,7 @@ mod test_finalize_block {
&mut shell.state,
Some(&delegator),
&validator.address,
None,
current_epoch,
)
.unwrap();
Expand Down
23 changes: 18 additions & 5 deletions crates/proof_of_stake/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2805,6 +2805,7 @@ pub fn claim_reward_tokens<S, Gov, Token>(
storage: &mut S,
source: Option<&Address>,
validator: &Address,
receiver: Option<&Address>,
current_epoch: Epoch,
) -> Result<token::Amount>
where
Expand All @@ -2814,8 +2815,14 @@ where
{
tracing::debug!("Claiming rewards in epoch {current_epoch}");

let source = source.cloned().unwrap_or_else(|| validator.clone());
tracing::debug!("Source {} --> Validator {}", source, validator);
let source = source.unwrap_or(validator).clone();
let receiver = receiver.unwrap_or(&source).clone();
tracing::debug!(
"Source {} --> Validator {}, Receiver {}",
source,
validator,
receiver
);

let mut reward_tokens = compute_current_rewards_from_bonds::<S, Gov>(
storage,
Expand All @@ -2832,17 +2839,23 @@ where
// Update the last claim epoch in storage
write_last_reward_claim_epoch(storage, &source, validator, current_epoch)?;

// Transfer the bonded tokens from PoS to the source
// Transfer the bonded tokens from PoS to the receiver
let staking_token = staking_token_address(storage);
Token::transfer(storage, &staking_token, &ADDRESS, &source, reward_tokens)?;
Token::transfer(
storage,
&staking_token,
&ADDRESS,
&receiver,
reward_tokens,
)?;
Token::emit_transfer_event(
storage,
CLAIM_REWARDS_EVENT_DESC.into(),
trans_token::EventLevel::Tx,
&staking_token,
reward_tokens,
trans_token::UserAccount::Internal(ADDRESS),
trans_token::UserAccount::Internal(source),
trans_token::UserAccount::Internal(receiver),
)?;

Ok(reward_tokens)
Expand Down
7 changes: 5 additions & 2 deletions crates/proof_of_stake/src/vp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@ where
// The key is src bond ID and value is pair of (dest_validator, amount)
let mut redelegations: BTreeMap<BondId, (Address, token::Amount)> =
Default::default();
let mut claimed_rewards: BTreeSet<BondId> = Default::default();
// The value is an optional rewards receiver
let mut claimed_rewards: BTreeMap<BondId, Option<Address>> =
Default::default();
let mut changed_commission: BTreeSet<Address> = Default::default();
let mut changed_metadata: BTreeSet<Address> = Default::default();
let mut changed_consensus_key: BTreeSet<Address> = Default::default();
Expand Down Expand Up @@ -241,6 +243,7 @@ where
PosAction::ClaimRewards(ClaimRewards {
validator,
source,
receiver,
}) => {
let bond_id = BondId {
source: source.unwrap_or_else(|| validator.clone()),
Expand All @@ -256,7 +259,7 @@ where
)
.into());
}
claimed_rewards.insert(bond_id);
claimed_rewards.insert(bond_id, receiver);
}
PosAction::CommissionChange(validator) => {
if !verifiers.contains(&validator) {
Expand Down
4 changes: 4 additions & 0 deletions crates/sdk/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1880,6 +1880,10 @@ pub struct ClaimRewards<C: NamadaTypes = SdkTypes> {
/// Source address for claiming rewards due to bonds. For self-bonds, the
/// validator is also the source
pub source: Option<C::Address>,
/// An optional receiver address. If not given, the rewards will be
/// received by the owner (i.e. the source of a delegation or the
/// validator of a self-bond).
pub receiver: Option<C::Address>,
/// Path to the TX WASM code file
pub tx_code_path: PathBuf,
}
Expand Down
1 change: 1 addition & 0 deletions crates/sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,7 @@ pub trait Namada: NamadaIo {
args::ClaimRewards {
validator,
source: None,
receiver: None,
tx_code_path: PathBuf::from(TX_CLAIM_REWARDS_WASM),
tx: self.tx_builder(),
}
Expand Down
19 changes: 10 additions & 9 deletions crates/sdk/src/queries/vp/pos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1248,14 +1248,15 @@ mod test {
.expect("Test failed");
client.state.in_mem_mut().block.height = height + 1;

let claimed = namada_proof_of_stake::claim_reward_tokens::<
_,
governance::Store<_>,
namada_token::Store<_>,
>(
&mut client.state, Some(&delegator), &validator, epoch
)
.expect("Claiming rewards failed");
let claimed =
namada_proof_of_stake::claim_reward_tokens::<
_,
governance::Store<_>,
namada_token::Store<_>,
>(
&mut client.state, Some(&delegator), &validator, None, epoch
)
.expect("Claiming rewards failed");

assert_eq!(claimed, del_reward_epoch_3 + del_reward_epoch_2);

Expand All @@ -1264,7 +1265,7 @@ mod test {
_,
governance::Store<_>,
namada_token::Store<_>,
>(&mut client.state, None, &validator, epoch)
>(&mut client.state, None, &validator, None, epoch)
.expect("Claiming validator rewards failed");

assert_eq!(claimed_validator, val_reward_epoch_3 + val_reward_epoch_2);
Expand Down
26 changes: 25 additions & 1 deletion crates/sdk/src/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1726,6 +1726,7 @@ pub async fn build_claim_rewards(
tx: tx_args,
validator,
source,
receiver,
tx_code_path,
}: &args::ClaimRewards,
) -> Result<(Tx, SigningTxData)> {
Expand Down Expand Up @@ -1757,7 +1758,30 @@ pub async fn build_claim_rewards(
None => Ok(source.clone()),
}?;

let data = pos::ClaimRewards { validator, source };
// Check that the receiver address exists on chain
let receiver = match receiver.clone() {
Some(receiver) => {
let message = format!(
"The receiver address {receiver} doesn't exist on chain."
);
address_exists_or_err(
receiver,
tx_args.force,
context,
message,
|err| Error::from(TxSubmitError::LocationDoesNotExist(err)),
)
.await
.map(Some)
}
None => Ok(receiver.clone()),
}?;

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

build(
context,
Expand Down
7 changes: 6 additions & 1 deletion crates/trans_token/src/vp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,11 @@ where
fn has_bal_inc_protocol_action(action: &Action, owner: Owner<'_>) -> bool {
match action {
Action::Pos(
PosAction::ClaimRewards(ClaimRewards { validator, source })
PosAction::ClaimRewards(ClaimRewards {
validator,
source,
receiver: _,
})
| PosAction::Withdraw(Withdraw { validator, source }),
) => match owner {
Owner::Account(owner) => {
Expand Down Expand Up @@ -1099,6 +1103,7 @@ mod tests {
.push_action(Action::Pos(PosAction::ClaimRewards(ClaimRewards {
validator: established_address_1(),
source: None,
receiver: None,
})))
.unwrap();

Expand Down
2 changes: 2 additions & 0 deletions crates/tx/src/data/pos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ pub struct ClaimRewards {
/// Source address for claiming rewards from a bond. For self-bonds, the
/// validator is also the source
pub source: Option<Address>,
/// Optional rewards receiver address
pub receiver: Option<Address>,
}

/// A redelegation of bonded tokens from one validator to another.
Expand Down
Loading
Loading