Skip to content

Commit

Permalink
Increase the accuracy of rewards by minting based on the real reward.
Browse files Browse the repository at this point in the history
  • Loading branch information
murisi committed Feb 12, 2025
1 parent 63665af commit 11a2498
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 48 deletions.
119 changes: 77 additions & 42 deletions crates/shielded_token/src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,15 +252,14 @@ where
fn update_native_conversions<S, TransToken>(
storage: &mut S,
token: &Address,
normed_inflation: u128,
normed_inflation: &mut u128,
masp_epochs_per_year: u64,
masp_epoch: MaspEpoch,
total_reward: &mut Amount,
current_convs: &mut BTreeMap<
(Address, Denomination, MaspDigitPos),
AllowedConversion,
>,
) -> Result<Denomination>
) -> Result<(Denomination, (u128, u128))>
where
S: StorageWrite + StorageRead + WithConversionState,
TransToken:
Expand All @@ -274,13 +273,13 @@ where
storage,
token,
denom,
normed_inflation,
*normed_inflation,
masp_epochs_per_year,
)?;
// The amount that will be given of the new native token for
// every amount of the native token given in the
// previous epoch
let inflation_uint = Uint::from(normed_inflation);
let inflation_uint = Uint::from(*normed_inflation);
let reward = Uint::from(reward);
let new_normed_inflation = checked!(inflation_uint + reward)?;
let new_normed_inflation = u128::try_from(new_normed_inflation)
Expand All @@ -291,7 +290,7 @@ where
Please check the inflation parameters.",
token
);
normed_inflation
*normed_inflation
});
for digit in MaspDigitPos::iter() {
// Provide an allowed conversion from previous timestamp. The
Expand All @@ -312,7 +311,7 @@ where
// tokens cancel/telescope out
let cur_conv = MaspAmount::from_pair(
old_asset,
i128::try_from(normed_inflation)
i128::try_from(*normed_inflation)
.ok()
.and_then(i128::checked_neg)
.ok_or_err_msg("Current inflation overflow")?,
Expand All @@ -338,29 +337,20 @@ where
},
);
}
// Dispense a transparent reward in parallel to the shielded rewards
let addr_bal = TransToken::read_balance(storage, token, &MASP)?;
// The reward for each reward.1 units of the current asset
// is reward.0 units of the reward token
let native_reward = addr_bal
.u128_eucl_div_rem((new_normed_inflation, normed_inflation))
.ok_or_else(|| Error::new_const("Three digit reward overflow"))?;
*total_reward = total_reward
.checked_add(
native_reward
.0
.checked_add(native_reward.1)
.unwrap_or(Amount::max())
.checked_sub(addr_bal)
.unwrap_or_default(),
)
.ok_or_else(|| Error::new_const("Three digit total reward overflow"))?;
// Note the fraction used to compute rewards from balance
let reward_frac = (
new_normed_inflation
.checked_sub(*normed_inflation)
.unwrap_or_default(),
*normed_inflation,
);
// Save the new normed inflation
let _ = storage
.conversion_state_mut()
.normed_inflation
.insert(new_normed_inflation);
Ok(denom)
*normed_inflation = new_normed_inflation;
Ok((denom, reward_frac))
}

/// Update the conversions for non-native tokens. Namely calculate the reward,
Expand Down Expand Up @@ -467,7 +457,7 @@ where
*total_reward = total_reward
.checked_add(
addr_bal
.u128_eucl_div_rem((reward, precision))
.u128_eucl_div_rem((real_reward, precision))
.ok_or_else(|| {
Error::new_const("Total reward calculation overflow")
})?
Expand Down Expand Up @@ -521,7 +511,7 @@ where
}
});
// The total transparent value of the rewards being distributed
let mut total_reward = Amount::zero();
let mut total_deflated_reward = Amount::zero();

// Construct MASP asset type for rewards. Always deflate and timestamp
// reward tokens with the zeroth epoch to minimize the number of convert
Expand All @@ -540,6 +530,11 @@ where
&native_token,
)?
.0;
// Get the last rewarded amount of the native token
let mut normed_inflation = *storage
.conversion_state_mut()
.normed_inflation
.get_or_insert(ref_inflation);

// Reward all tokens according to above reward rates
let masp_epoch_multiplier = Params::masp_epoch_multiplier(storage)?;
Expand All @@ -554,39 +549,79 @@ where
let epochs_per_year = Params::epochs_per_year(storage)?;
let masp_epochs_per_year =
checked!(epochs_per_year / masp_epoch_multiplier)?;
let mut native_reward_frac = None;
for token in &masp_reward_keys {
// Get the last rewarded amount of the native token
let normed_inflation = *storage
.conversion_state_mut()
.normed_inflation
.get_or_insert(ref_inflation);

// Generate conversions from the last epoch to the current and update
// the reward backing accumulator
let denom = if *token == native_token {
update_native_conversions::<_, TransToken>(
if *token == native_token {
let (denom, frac) = update_native_conversions::<_, TransToken>(
storage,
token,
normed_inflation,
&mut normed_inflation,
masp_epochs_per_year,
masp_epoch,
&mut total_reward,
&mut current_convs,
)
)?;
masp_reward_denoms.insert(token.clone(), denom);
native_reward_frac = Some(frac);
} else {
update_non_native_conversions::<_, TransToken>(
let denom = update_non_native_conversions::<_, TransToken>(
storage,
token,
ref_inflation,
normed_inflation,
masp_epochs_per_year,
masp_epoch,
reward_assets,
&mut total_reward,
&mut total_deflated_reward,
&mut current_convs,
)?;
masp_reward_denoms.insert(token.clone(), denom);
}
}
// Inflate the non-native rewards for all tokens in one operation
let non_native_reward = total_deflated_reward
.raw_amount()
.checked_mul_div(normed_inflation.into(), ref_inflation.into())
.ok_or_else(|| Error::new_const("Total reward calculation overflow"))?;
// The total transparent value of the rewards being distributed
let mut total_reward = Amount::zero();
// Accumulate the integer part of the non-native reward
checked!(total_reward += non_native_reward.0.into())?;
// And finally accumulate the fractional parts of the native and non-native
// rewards if their sum is more than one
if let Some(native_reward_frac) = native_reward_frac {
// Dispense a transparent reward in parallel to the shielded rewards
let addr_bal = TransToken::read_balance(storage, &native_token, &MASP)?;
// The reward for each reward.1 units of the current asset is reward.0
// units of the reward token
let native_reward = addr_bal
.raw_amount()
.checked_mul_div(
native_reward_frac.0.into(),
native_reward_frac.1.into(),
)
}?;
masp_reward_denoms.insert(token.clone(), denom);
.ok_or_else(|| Error::new_const("Three digit reward overflow"))?;
// Accumulate the integer part of the native reward
checked!(total_reward += native_reward.0.into())?;

let ref_inflation = Uint::from(ref_inflation);
// Compute the fraction obtained by adding the fractional parts of the
// native reward and the non-native reward:
// native_reward.1/native_reward_frac.1 +
// non_native_reward.1/ref_inflation
let numerator = checked!(
native_reward.1 * ref_inflation
+ Uint::from(native_reward_frac.1) * non_native_reward.1
)?;
let denominator =
checked!(ref_inflation * Uint::from(native_reward_frac.1))?;
// A fraction greater than or equal to one corresponds to the situation
// where combining non-native rewards with pre-existing NAM balance
// gives a greater reward than treating each separately.
if numerator >= denominator {
checked!(total_reward += 1.into())?;
}
}

// Try to distribute Merkle leaf updating as evenly as possible across
Expand Down
12 changes: 6 additions & 6 deletions crates/tests/src/integration/masp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1728,7 +1728,7 @@ fn masp_incentives() -> Result<()> {
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.18963"));
assert!(captured.contains("nam: 0.18887"));

// Wait till epoch boundary
node.next_masp_epoch();
Expand Down Expand Up @@ -1870,7 +1870,7 @@ fn masp_incentives() -> Result<()> {
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 1.384131"));
assert!(captured.contains("nam: 1.383286"));

// Wait till epoch boundary
node.next_masp_epoch();
Expand Down Expand Up @@ -1979,7 +1979,7 @@ fn masp_incentives() -> Result<()> {
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 3.270374"));
assert!(captured.contains("nam: 3.267817"));

// Wait till epoch boundary
node.next_masp_epoch();
Expand Down Expand Up @@ -2072,7 +2072,7 @@ fn masp_incentives() -> Result<()> {
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 3.774374"));
assert!(captured.contains("nam: 3.77117"));

// Wait till epoch boundary
node.next_masp_epoch();
Expand Down Expand Up @@ -2143,7 +2143,7 @@ fn masp_incentives() -> Result<()> {
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 3.774374"));
assert!(captured.contains("nam: 3.77117"));

// Wait till epoch boundary to prevent conversion expiry during transaction
// construction
Expand Down Expand Up @@ -2282,7 +2282,7 @@ fn masp_incentives() -> Result<()> {
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.003216"));
assert!(captured.contains("nam: 0.000012"));

Ok(())
}
Expand Down

0 comments on commit 11a2498

Please sign in to comment.