Skip to content

Commit

Permalink
feat: add rpc to query user rewards (#878)
Browse files Browse the repository at this point in the history
* add rewards rpc

* working rpc

* fixrewards rpc
  • Loading branch information
1xstj authored Jan 13, 2025
1 parent bcba25e commit 44a3cc5
Show file tree
Hide file tree
Showing 13 changed files with 298 additions and 22 deletions.
27 changes: 27 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ members = [
"pallets/*",
"pallets/services/rpc",
"pallets/services/rpc/runtime-api",
"pallets/rewards/rpc",
"pallets/rewards/rpc/runtime-api",
"pallets/tangle-lst/benchmarking",
"pallets/multi-asset-delegation/fuzzer",
"precompiles/pallet-democracy",
Expand Down Expand Up @@ -127,6 +129,8 @@ pallet-multi-asset-delegation = { path = "pallets/multi-asset-delegation", defau
pallet-tangle-lst-benchmarking = { path = "pallets/tangle-lst/benchmarking", default-features = false }
pallet-oracle = { path = "pallets/oracle", default-features = false }
pallet-rewards = { path = "pallets/rewards", default-features = false }
pallet-rewards-rpc-runtime-api = { path = "pallets/rewards/rpc/runtime-api", default-features = false }
pallet-rewards-rpc = { path = "pallets/rewards/rpc" }

k256 = { version = "0.13.3", default-features = false }
p256 = { version = "0.13.2", default-features = false }
Expand Down
1 change: 1 addition & 0 deletions node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ sp-keystore = { workspace = true, features = ["std"] }
sp-runtime = { workspace = true, features = ["std"] }
sp-timestamp = { workspace = true, features = ["std"] }
pallet-services-rpc = { workspace = true }
pallet-rewards-rpc = { workspace = true }
sp-consensus-grandpa = { workspace = true }
sp-offchain = { workspace = true }
pallet-airdrop-claims = { workspace = true }
Expand Down
3 changes: 3 additions & 0 deletions node/src/rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ where
AccountId,
AssetId,
>,
C::Api: pallet_rewards_rpc::RewardsRuntimeApi<Block, AccountId, AssetId, Balance>,
C::Api: fp_rpc::ConvertTransactionRuntimeApi<Block>,
C::Api: fp_rpc::EthereumRuntimeRPCApi<Block>,
C::Api: rpc_primitives_debug::DebugRuntimeApi<Block>,
Expand All @@ -141,6 +142,7 @@ where
B::State: sc_client_api::backend::StateBackend<sp_runtime::traits::BlakeTwo256>,
CIDP: sp_inherents::CreateInherentDataProviders<Block, ()> + Send + Sync + 'static,
{
use pallet_rewards_rpc::{RewardsApiServer, RewardsClient};
use pallet_services_rpc::{ServicesApiServer, ServicesClient};
use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer};
use sc_consensus_babe_rpc::{Babe, BabeApiServer};
Expand All @@ -161,6 +163,7 @@ where
io.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?;
io.merge(TransactionPayment::new(client.clone()).into_rpc())?;
io.merge(ServicesClient::new(client.clone()).into_rpc())?;
io.merge(RewardsClient::new(client.clone()).into_rpc())?;

if let Some(babe) = babe {
let BabeDeps { babe_worker_handle, keystore } = babe;
Expand Down
20 changes: 20 additions & 0 deletions pallets/rewards/rpc/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "pallet-rewards-rpc"
version = { workspace = true }
authors = { workspace = true }
edition = { workspace = true }
homepage = { workspace = true }
license = { workspace = true }
repository = { workspace = true }

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
jsonrpsee = { workspace = true, features = ["client-core", "server", "macros"] }
pallet-rewards-rpc-runtime-api = { path = "./runtime-api", default-features = false }
parity-scale-codec = { workspace = true }
sp-api = { workspace = true }
sp-blockchain = { workspace = true }
sp-runtime = { workspace = true }
tangle-primitives = { workspace = true }
28 changes: 28 additions & 0 deletions pallets/rewards/rpc/runtime-api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "pallet-rewards-rpc-runtime-api"
version = { workspace = true }
authors = { workspace = true }
edition = { workspace = true }
homepage = { workspace = true }
license = { workspace = true }
repository = { workspace = true }

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
parity-scale-codec = { version = "3.6.12", default-features = false, features = ["derive"] }
sp-api = { workspace = true }
sp-runtime = { workspace = true }
sp-std = { workspace = true }
tangle-primitives = { workspace = true, default-features = false }

[features]
default = ["std"]
std = [
"parity-scale-codec/std",
"sp-api/std",
"sp-runtime/std",
"tangle-primitives/std",
"sp-std/std",
]
48 changes: 48 additions & 0 deletions pallets/rewards/rpc/runtime-api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// This file is part of Tangle.
// Copyright (C) 2022-2024 Tangle Foundation.
//
// Tangle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Tangle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Tangle. If not, see <http://www.gnu.org/licenses/>.

//! Runtime API definition for rewards pallet.
#![cfg_attr(not(feature = "std"), no_std)]
#![allow(clippy::type_complexity)]
use parity_scale_codec::Codec;
use sp_runtime::{traits::MaybeDisplay, Serialize};

pub type BlockNumberOf<Block> =
<<Block as sp_runtime::traits::HeaderProvider>::HeaderT as sp_runtime::traits::Header>::Number;

sp_api::decl_runtime_apis! {
pub trait RewardsApi<AccountId, AssetId, Balance>
where
AccountId: Codec + MaybeDisplay + Serialize,
AssetId: Codec + MaybeDisplay + Serialize,
Balance: Codec + MaybeDisplay + Serialize,
{
/// Query all the rewards that this operator is providing along with their blueprints.
///
/// ## Arguments
/// - `operator`: The operator account id.
/// ## Return
/// - [`RpcRewardsWithBlueprint`]: A list of rewards with their blueprints.
fn query_user_rewards(
account_id: AccountId,
asset_id: tangle_primitives::services::Asset<AssetId>
) -> Result<
Balance,
sp_runtime::DispatchError,
>;
}
}
110 changes: 110 additions & 0 deletions pallets/rewards/rpc/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// This file is part of Tangle.
// Copyright (C) 2022-2024 Tangle Foundation.
//
// Tangle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Tangle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Tangle. If not, see <http://www.gnu.org/licenses/>.

#![allow(clippy::unnecessary_mut_passed)]
#![allow(clippy::type_complexity)]

use jsonrpsee::{
core::RpcResult,
proc_macros::rpc,
types::error::{ErrorObject, ErrorObjectOwned},
};
use parity_scale_codec::Codec;
use sp_api::ProvideRuntimeApi;
use sp_blockchain::HeaderBackend;
use sp_runtime::{
traits::{Block as BlockT, MaybeDisplay},
Serialize,
};
use std::sync::Arc;
use tangle_primitives::Balance;

pub use pallet_rewards_rpc_runtime_api::RewardsApi as RewardsRuntimeApi;

/// RewardsClient RPC methods.
#[rpc(client, server)]
pub trait RewardsApi<BlockHash, AccountId, AssetId>
where
AccountId: Codec + MaybeDisplay + core::fmt::Debug + Send + Sync + 'static + Serialize,
AssetId: Codec + MaybeDisplay + core::fmt::Debug + Send + Sync + 'static + Serialize,
{
#[method(name = "rewards_queryUserRewards")]
fn query_user_rewards(
&self,
account_id: AccountId,
asset_id: tangle_primitives::services::Asset<AssetId>,
at: Option<BlockHash>,
) -> RpcResult<Balance>;
}

/// Provides RPC methods to query a dispatchable's class, weight and fee.
pub struct RewardsClient<C, P> {
/// Shared reference to the client.
client: Arc<C>,
_marker: std::marker::PhantomData<P>,
}

impl<C, P> RewardsClient<C, P> {
/// Creates a new instance of the RewardsClient Rpc helper.
pub fn new(client: Arc<C>) -> Self {
Self { client, _marker: Default::default() }
}
}

impl<C, Block, AccountId, AssetId> RewardsApiServer<<Block as BlockT>::Hash, AccountId, AssetId>
for RewardsClient<C, Block>
where
Block: BlockT,
AccountId: Codec + MaybeDisplay + core::fmt::Debug + Send + Sync + 'static + Serialize,
AssetId: Codec + MaybeDisplay + core::fmt::Debug + Send + Sync + 'static + Serialize,
C: HeaderBackend<Block> + ProvideRuntimeApi<Block> + Send + Sync + 'static,
C::Api: RewardsRuntimeApi<Block, AccountId, AssetId, Balance>,
{
fn query_user_rewards(
&self,
account_id: AccountId,
asset_id: tangle_primitives::services::Asset<AssetId>,
at: Option<<Block as BlockT>::Hash>,
) -> RpcResult<Balance> {
let api = self.client.runtime_api();
let at = at.unwrap_or_else(|| self.client.info().best_hash);

match api.query_user_rewards(at, account_id, asset_id) {
Ok(Ok(res)) => Ok(res),
Ok(Err(e)) => Err(map_err(format!("{:?}", e), "Unable to query user rewards")),
Err(e) => Err(map_err(format!("{:?}", e), "Unable to query user rewards")),
}
}
}

fn map_err(error: impl ToString, desc: &'static str) -> ErrorObjectOwned {
ErrorObject::owned(Error::RuntimeError.into(), desc, Some(error.to_string()))
}

/// Error type of this RPC api.
#[derive(Debug)]
pub enum Error {
/// The call to runtime failed.
RuntimeError,
}

impl From<Error> for i32 {
fn from(e: Error) -> i32 {
match e {
Error::RuntimeError => 1,
}
}
}
53 changes: 32 additions & 21 deletions pallets/rewards/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,37 @@ impl<T: Config> Pallet<T> {
Ok(())
}

pub fn calculate_rewards(
account_id: &T::AccountId,
asset: Asset<T::AssetId>,
) -> Result<(BalanceOf<T>, BalanceOf<T>), DispatchError> {
// find the vault for the asset id
// if the asset is not in a reward vault, do nothing
let vault_id =
AssetLookupRewardVaults::<T>::get(asset).ok_or(Error::<T>::AssetNotInVault)?;

// lets read the user deposits from the delegation manager
let deposit_info =
T::DelegationManager::get_user_deposit_with_locks(&account_id.clone(), asset)
.ok_or(Error::<T>::NoRewardsAvailable)?;

// read the asset reward config
let reward_config = RewardConfigStorage::<T>::get(vault_id);

// find the total vault score
let total_score = TotalRewardVaultScore::<T>::get(vault_id);

// get the users last claim
let last_claim = UserClaimedReward::<T>::get(account_id, vault_id);

Self::calculate_deposit_rewards_with_lock_multiplier(
total_score,
deposit_info,
reward_config.ok_or(Error::<T>::RewardConfigNotFound)?,
last_claim,
)
}

/// Calculates and pays out rewards for a given account and asset.
///
/// This function orchestrates the reward calculation and payout process by:
Expand Down Expand Up @@ -116,27 +147,7 @@ impl<T: Config> Pallet<T> {
let vault_id =
AssetLookupRewardVaults::<T>::get(asset).ok_or(Error::<T>::AssetNotInVault)?;

// lets read the user deposits from the delegation manager
let deposit_info =
T::DelegationManager::get_user_deposit_with_locks(&account_id.clone(), asset)
.ok_or(Error::<T>::NoRewardsAvailable)?;

// read the asset reward config
let reward_config = RewardConfigStorage::<T>::get(vault_id);

// find the total vault score
let total_score = TotalRewardVaultScore::<T>::get(vault_id);

// get the users last claim
let last_claim = UserClaimedReward::<T>::get(account_id, vault_id);

let (total_rewards, rewards_to_be_paid) =
Self::calculate_deposit_rewards_with_lock_multiplier(
total_score,
deposit_info,
reward_config.ok_or(Error::<T>::RewardConfigNotFound)?,
last_claim,
)?;
let (total_rewards, rewards_to_be_paid) = Self::calculate_rewards(account_id, asset)?;

// mint new TNT rewards and trasnfer to the user
let _ = T::Currency::deposit_creating(account_id, rewards_to_be_paid);
Expand Down
2 changes: 2 additions & 0 deletions runtime/mainnet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pallet-tangle-lst = { workspace = true }
pallet-airdrop-claims = { workspace = true }
pallet-services = { workspace = true }
pallet-services-rpc-runtime-api = { workspace = true }
pallet-rewards-rpc-runtime-api = { workspace = true }
tangle-primitives = { workspace = true, features = ["verifying"] }
tangle-crypto-primitives = { workspace = true }
pallet-multi-asset-delegation = { workspace = true }
Expand Down Expand Up @@ -231,6 +232,7 @@ std = [
"pallet-services/std",
"pallet-multi-asset-delegation/std",
"pallet-services-rpc-runtime-api/std",
"pallet-rewards-rpc-runtime-api/std",
"pallet-rewards/std",

# Frontier
Expand Down
Loading

0 comments on commit 44a3cc5

Please sign in to comment.