Skip to content

Commit

Permalink
wip : fix e2e test
Browse files Browse the repository at this point in the history
  • Loading branch information
1xstj committed Jan 30, 2025
1 parent 9b5dd75 commit bb01014
Show file tree
Hide file tree
Showing 5 changed files with 356 additions and 144 deletions.
134 changes: 95 additions & 39 deletions node/tests/evm_restaking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use alloy::sol;
use anyhow::bail;
use sp_runtime::traits::AccountIdConversion;
use sp_tracing::{error, info};
use tangle_primitives::time::SECONDS_PER_BLOCK;
use tangle_runtime::PalletId;
use tangle_subxt::subxt;
use tangle_subxt::subxt::tx::TxStatus;
Expand Down Expand Up @@ -186,6 +187,11 @@ async fn deploy_tangle_lrt(
Ok(*token.address())
}

// Mock values for consistent testing
const EIGHTEEN_DECIMALS: u128 = 1_000_000_000_000_000_000_000;
const MOCK_DEPOSIT_CAP: u128 = 1000 * EIGHTEEN_DECIMALS; // 1M tokens with 18 decimals
const MOCK_APY: u8 = 10; // 10% APY

/// Setup the E2E test environment.
#[track_caller]
pub fn run_mad_test<TFn, F>(f: TFn)
Expand Down Expand Up @@ -249,29 +255,58 @@ where

// Create a new vault and these assets to it.
let vault_id = 0;
let deposit_cap = parse_ether("100").unwrap();
let incentive_cap = parse_ether("100").unwrap();
let update_vault_reward_config = api::tx().sudo().sudo(
// in Manual Sealing and fast runtime, we have 1 block per sec
// we consider 1 year as 50 blocks, for testing purposes
let one_year_blocks = SECONDS_PER_BLOCK * 50;

let set_apy_blocks = api::tx().sudo().sudo(
api::runtime_types::tangle_testnet_runtime::RuntimeCall::Rewards(
api::runtime_types::pallet_rewards::pallet::Call::update_apy_blocks {
blocks: one_year_blocks,
},
),
);

let mut result = subxt
.tx()
.sign_and_submit_then_watch_default(&set_apy_blocks, &alice.substrate_signer())
.await?;

while let Some(Ok(s)) = result.next().await {
if let TxStatus::InBestBlock(b) = s {
let evs = match b.wait_for_success().await {
Ok(evs) => evs,
Err(e) => {
error!("Error: {:?}", e);
break;
},
};
for ev in evs.iter() {
let metadata = ev.unwrap();
info!("{}.{}", metadata.pallet_name(), metadata.variant_name());
}
break;
}
}

let create_vault = api::tx().sudo().sudo(
api::runtime_types::tangle_testnet_runtime::RuntimeCall::Rewards(
api::runtime_types::pallet_rewards::pallet::Call::update_vault_reward_config {
api::runtime_types::pallet_rewards::pallet::Call::create_reward_vault {
vault_id,
new_config:
api::runtime_types::pallet_rewards::types::RewardConfigForAssetVault {
apy: api::runtime_types::sp_arithmetic::per_things::Percent(1),
incentive_cap: incentive_cap.to::<u128>(),
deposit_cap: deposit_cap.to::<u128>(),
boost_multiplier: None,
apy: api::runtime_types::sp_arithmetic::per_things::Percent(MOCK_APY),
deposit_cap: MOCK_DEPOSIT_CAP,
incentive_cap: 0,
boost_multiplier: Some(1),
},
},
),
);

let mut result = subxt
.tx()
.sign_and_submit_then_watch_default(
&update_vault_reward_config,
&alice.substrate_signer(),
)
.sign_and_submit_then_watch_default(&create_vault, &alice.substrate_signer())
.await?;

while let Some(Ok(s)) = result.next().await {
Expand All @@ -283,8 +318,10 @@ where
break;
},
};
evs.find_first::<api::rewards::events::VaultRewardConfigUpdated>()?
.expect("VaultRewardConfigUpdated event to be emitted");
for ev in evs.iter() {
let metadata = ev.unwrap();
info!("{}.{}", metadata.pallet_name(), metadata.variant_name());
}
break;
}
}
Expand Down Expand Up @@ -326,8 +363,10 @@ where
break;
},
};
evs.find_first::<api::rewards::events::AssetUpdatedInVault>()?
.expect("AssetRewardVault event to be emitted");
for ev in evs.iter() {
let metadata = ev.unwrap();
info!("{}.{}", metadata.pallet_name(), metadata.variant_name());
}
break;
}
}
Expand Down Expand Up @@ -884,14 +923,20 @@ fn lrt_deposit_withdraw_erc20() {
}

#[test]
fn lrt_rewards() {
fn mad_rewards() {
run_mad_test(|t| async move {
let alice = TestAccount::Alice;
let alice_provider = alloy_provider_with_wallet(&t.provider, alice.evm_wallet());
// Join operators
let tnt = U256::from(100_000u128);
assert!(join_as_operator(&t.subxt, alice.substrate_signer(), tnt.to::<u128>()).await?);

let vault_id = 0;
let cfg_addr = api::storage().rewards().reward_config_storage(vault_id);
let cfg = t.subxt.storage().at_latest().await?.fetch(&cfg_addr).await?.unwrap();

let deposit = U256::from(tnt) * U256::from(2);

// Setup a LRT Vault for Alice.
let lrt_address = deploy_tangle_lrt(
alice_provider.clone(),
Expand All @@ -906,37 +951,48 @@ fn lrt_rewards() {
let bob = TestAccount::Bob;
let bob_provider = alloy_provider_with_wallet(&t.provider, bob.evm_wallet());
// Mint WETH for Bob
let weth_amount = parse_ether("10").unwrap();
let weth = MockERC20::new(t.weth, &bob_provider);
weth.mint(bob.address(), weth_amount).send().await?.get_receipt().await?;
let usdc_amount = deposit;
let usdc = MockERC20::new(t.usdc, &bob_provider);
usdc.mint(bob.address(), usdc_amount).send().await?.get_receipt().await?;

// // Approve LRT contract to spend WETH
// let deposit_amount = weth_amount;
// let approve_result =
// weth.approve(lrt_address, deposit_amount).send().await?.get_receipt().await?;
// assert!(approve_result.status());
// info!("Approved {} WETH for deposit in LRT", format_ether(deposit_amount));

// // Deposit WETH to LRT
// let lrt = TangleLiquidRestakingVault::new(lrt_address, &bob_provider);
// let deposit_result = lrt
// .deposit(deposit_amount, bob.address())
// .send()
// .await?
// .with_timeout(Some(Duration::from_secs(5)))
// .get_receipt()
// .await?;
// assert!(deposit_result.status());
// info!("Deposited {} WETH in LRT", format_ether(deposit_amount));

// Approve LRT contract to spend WETH
let deposit_amount = weth_amount.div(U256::from(2));
let approve_result =
weth.approve(lrt_address, deposit_amount).send().await?.get_receipt().await?;
assert!(approve_result.status());
info!("Approved {} WETH for deposit in LRT", format_ether(deposit_amount));
// Delegate assets
let precompile = MultiAssetDelegation::new(MULTI_ASSET_DELEGATION, &bob_provider);
let deposit_amount = U256::from(100_000_000u128);

// Deposit WETH to LRT
let lrt = TangleLiquidRestakingVault::new(lrt_address, &bob_provider);
let deposit_result = lrt
.deposit(deposit_amount, bob.address())
// Deposit and delegate using asset ID
let deposit_result = precompile
.deposit(U256::from(t.usdc_asset_id), Address::ZERO, U256::from(deposit_amount), 0)
.from(bob.address())
.send()
.await?
.with_timeout(Some(Duration::from_secs(5)))
.get_receipt()
.await?;
assert!(deposit_result.status());
info!("Deposited {} WETH in LRT", format_ether(deposit_amount));

// Wait for two new sessions to happen
let session_index = wait_for_next_session(&t.subxt).await?;
info!("New session started: {}", session_index);
// Wait for one year to pass
wait_for_more_blocks(&t.provider, 51).await;

let vault_id = 0;
let cfg_addr = api::storage().rewards().reward_config_storage(vault_id);
let cfg = t.subxt.storage().at_latest().await?.fetch(&cfg_addr).await?;
let apy = cfg.map(|c| c.apy).unwrap();
let apy = cfg.apy;
info!("APY: {}%", apy.0);

let rewards_addr = api::apis().rewards_api().query_user_rewards(
Expand All @@ -957,4 +1013,4 @@ fn lrt_rewards() {

anyhow::Ok(())
});
}
}
42 changes: 24 additions & 18 deletions pallets/rewards/src/functions/rewards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ impl<T: Config> Pallet<T> {

let rewards_to_be_paid = Self::calculate_rewards(account_id, asset)?;

log::debug!("rewards_to_be_paid: {:?}", rewards_to_be_paid.saturated_into::<u128>());
log::info!(target: "rewards", "rewards_to_be_paid: {:?}", rewards_to_be_paid.saturated_into::<u128>());

// Get the pot account for this vault
let pot_account =
Expand Down Expand Up @@ -181,6 +181,8 @@ impl<T: Config> Pallet<T> {
return None;
}

log::info!(target: "rewards", "calculate_propotional_apy : total_deposit: {:?}, deposit_cap: {:?}, original_apy: {:?}",
total_deposit, deposit_cap, original_apy);
let propotion = Percent::from_rational(total_deposit, deposit_cap);
original_apy.checked_mul(&propotion)
}
Expand All @@ -198,7 +200,7 @@ impl<T: Config> Pallet<T> {
return None;
}

log::debug!("calculate_reward_per_block : total_reward: {:?}", total_reward);
log::info!(target: "rewards", "calculate_reward_per_block : total_reward: {:?}", total_reward);

let apy_blocks_balance = BalanceOf::<T>::from(apy_blocks.saturated_into::<u32>());
Some(total_reward / apy_blocks_balance)
Expand Down Expand Up @@ -263,13 +265,17 @@ impl<T: Config> Pallet<T> {
return Err(Error::<T>::TotalDepositLessThanIncentiveCap.into());
}

log::info!(target: "rewards", "total_deposit: {:?}, total_asset_score: {:?}, deposit: {:?}, reward: {:?}, last_claim: {:?}",
total_deposit, total_asset_score, deposit, reward, last_claim);
log::info!(target: "rewards", "deposit_cap: {:?}, apy: {:?}",
deposit_cap, reward.apy);
let apy = Self::calculate_propotional_apy(total_deposit, deposit_cap, reward.apy)
.ok_or(Error::<T>::CannotCalculatePropotionalApy)?;
log::debug!("apy: {:?}", apy);
log::info!(target: "rewards", "Calculated propotional apy: {:?}", apy);

// Calculate total rewards pool from total issuance
let tnt_total_supply = T::Currency::total_issuance();
log::debug!("tnt_total_supply: {:?}", tnt_total_supply);
log::info!(target: "rewards", "tnt_total_supply: {:?}", tnt_total_supply);

let total_annual_rewards = apy.mul_floor(tnt_total_supply);

Expand All @@ -278,17 +284,17 @@ impl<T: Config> Pallet<T> {
frame_system::Pallet::<T>::block_number(),
last_claim.map(|(block, _)| block).unwrap_or_default(),
);
log::debug!("total annual rewards before decay: {:?}", total_annual_rewards);
log::debug!("decay_factor: {:?}", decay_factor);
log::info!(target: "rewards", "total annual rewards before decay: {:?}", total_annual_rewards);
log::info!(target: "rewards", "decay_factor: {:?}", decay_factor);

// Apply decay to total rewards
let total_annual_rewards = decay_factor.mul_floor(total_annual_rewards);
log::debug!("total annual rewards after decay: {:?}", total_annual_rewards);
log::info!(target: "rewards", "total annual rewards after decay: {:?}", total_annual_rewards);

// Calculate per block reward pool first to minimize precision loss
let total_reward_per_block = Self::calculate_reward_per_block(total_annual_rewards)
.ok_or(Error::<T>::CannotCalculateRewardPerBlock)?;
log::debug!("total_reward_per_block: {:?}", total_reward_per_block);
log::info!(target: "rewards", "total_reward_per_block: {:?}", total_reward_per_block);

// Start with unlocked amount as base score
let user_unlocked_score = deposit.unlocked_amount;
Expand All @@ -298,14 +304,14 @@ impl<T: Config> Pallet<T> {
let current_block = frame_system::Pallet::<T>::block_number();
let last_claim_block = last_claim.map(|(block, _)| block).unwrap_or(current_block);
let blocks_to_be_paid = current_block.saturating_sub(last_claim_block);
log::debug!(
log::info!(target: "rewards",
"Current Block {:?}, Last Claim Block {:?}, Blocks to be paid {:?}",
current_block,
last_claim_block,
blocks_to_be_paid
);

log::debug!("User unlocked score {:?}", user_score);
log::info!(target: "rewards", "User unlocked score {:?}", user_score);

// array of (score, blocks)
let mut user_rewards_score_by_blocks: Vec<(BalanceOf<T>, BlockNumberFor<T>)> = vec![];
Expand All @@ -323,7 +329,7 @@ impl<T: Config> Pallet<T> {
// (remaining_lock_time / total_lock_time)
let multiplier = BalanceOf::<T>::from(lock.lock_multiplier.value());
let lock_score = lock.amount.saturating_mul(multiplier);
log::debug!("user lock has not expired and still active, lock_multiplier: {:?}, lock_score: {:?}", lock.lock_multiplier, lock_score);
log::info!(target: "rewards", "user lock has not expired and still active, lock_multiplier: {:?}, lock_score: {:?}", lock.lock_multiplier, lock_score);

user_rewards_score_by_blocks.push((lock_score, blocks_to_be_paid));
} else {
Expand All @@ -334,7 +340,7 @@ impl<T: Config> Pallet<T> {
let multiplier_applied_blocks =
lock.expiry_block.saturating_sub(last_claim_block);

log::debug!("user lock has partially expired, lock_multiplier: {:?}, lock_score: {:?}, multiplier_applied_blocks: {:?}, blocks_to_be_paid: {:?}",
log::info!(target: "rewards", "user lock has partially expired, lock_multiplier: {:?}, lock_score: {:?}, multiplier_applied_blocks: {:?}, blocks_to_be_paid: {:?}",
lock.lock_multiplier, lock_score, multiplier_applied_blocks, blocks_to_be_paid);

user_rewards_score_by_blocks
Expand All @@ -354,38 +360,38 @@ impl<T: Config> Pallet<T> {
}
}

log::debug!("user rewards array {:?}", user_rewards_score_by_blocks);
log::info!(target: "rewards", "user rewards array {:?}", user_rewards_score_by_blocks);

// if the user has no score, return 0
// calculate the total score for the user
let total_score_for_user = user_rewards_score_by_blocks
.iter()
.fold(BalanceOf::<T>::zero(), |acc, (score, _blocks)| acc.saturating_add(*score));
log::debug!("total score: {:?}", total_score_for_user);
log::info!(target: "rewards", "total score: {:?}", total_score_for_user);
ensure!(!total_score_for_user.is_zero(), Error::<T>::NoRewardsAvailable);

// Calculate user's proportion of rewards based on their score

let mut total_rewards_to_be_paid_to_user = BalanceOf::<T>::zero();
for (score, blocks) in user_rewards_score_by_blocks {
let user_proportion = Percent::from_rational(score, total_asset_score);
log::debug!("user_proportion: {:?}", user_proportion);
log::info!(target: "rewards", "user_proportion: {:?}", user_proportion);
let user_reward_per_block = user_proportion.mul_floor(total_reward_per_block);

// Calculate total rewards for the period
log::debug!("last_claim_block: {:?}, total_reward_per_block: {:?}, user reward per block: {:?}, blocks: {:?}",
log::info!(target: "rewards", "last_claim_block: {:?}, total_reward_per_block: {:?}, user reward per block: {:?}, blocks: {:?}",
last_claim_block, total_reward_per_block, user_reward_per_block, blocks);

let rewards_to_be_paid = user_reward_per_block
.saturating_mul(BalanceOf::<T>::from(blocks.saturated_into::<u32>()));

log::debug!("rewards_to_be_paid: {:?}", rewards_to_be_paid);
log::info!(target: "rewards", "rewards_to_be_paid: {:?}", rewards_to_be_paid);

total_rewards_to_be_paid_to_user =
total_rewards_to_be_paid_to_user.saturating_add(rewards_to_be_paid);
}

log::debug!("total_rewards_to_be_paid_to_user: {:?}", total_rewards_to_be_paid_to_user);
log::info!(target: "rewards", "total_rewards_to_be_paid_to_user: {:?}", total_rewards_to_be_paid_to_user);
Ok(total_rewards_to_be_paid_to_user)
}
}
Loading

0 comments on commit bb01014

Please sign in to comment.