use subxt::ext::sp_runtime::MultiAddress;
use crate::{
api,
connections::{AsConnection, TxInfo},
frame_support::PalletId,
pallet_treasury::pallet::Call::{approve_proposal, reject_proposal},
pallets::{committee_management::CommitteeManagementApi, staking::StakingApi},
sp_core::TypeId,
sp_runtime::{traits::AccountIdConversion, Perbill},
AccountId, Balance, BlockHash,
Call::Treasury,
ConnectionApi, RootConnection, SignedConnectionApi, SudoCall, TxStatus, MILLISECS_PER_BLOCK,
};
impl TypeId for PalletId {
const TYPE_ID: [u8; 4] = *b"modl";
}
#[async_trait::async_trait]
pub trait TreasuryApi {
async fn treasury_account(&self) -> AccountId;
async fn proposals_count(&self, at: Option<BlockHash>) -> Option<u32>;
async fn approvals(&self, at: Option<BlockHash>) -> Vec<u32>;
}
#[async_trait::async_trait]
pub trait TreasuryUserApi {
async fn propose_spend(
&self,
amount: Balance,
beneficiary: AccountId,
status: TxStatus,
) -> anyhow::Result<TxInfo>;
async fn approve(&self, proposal_id: u32, status: TxStatus) -> anyhow::Result<TxInfo>;
async fn reject(&self, proposal_id: u32, status: TxStatus) -> anyhow::Result<TxInfo>;
}
#[async_trait::async_trait]
pub trait TreasureApiExt {
async fn possible_treasury_payout(&self) -> anyhow::Result<Balance>;
}
#[async_trait::async_trait]
pub trait TreasurySudoApi {
async fn approve(&self, proposal_id: u32, status: TxStatus) -> anyhow::Result<TxInfo>;
async fn reject(&self, proposal_id: u32, status: TxStatus) -> anyhow::Result<TxInfo>;
}
#[async_trait::async_trait]
impl<C: ConnectionApi> TreasuryApi for C {
async fn treasury_account(&self) -> AccountId {
PalletId(*b"a0/trsry").into_account_truncating()
}
async fn proposals_count(&self, at: Option<BlockHash>) -> Option<u32> {
let addrs = api::storage().treasury().proposal_count();
self.get_storage_entry_maybe(&addrs, at).await
}
async fn approvals(&self, at: Option<BlockHash>) -> Vec<u32> {
let addrs = api::storage().treasury().approvals();
self.get_storage_entry(&addrs, at).await.0
}
}
#[async_trait::async_trait]
impl<S: SignedConnectionApi> TreasuryUserApi for S {
async fn propose_spend(
&self,
amount: Balance,
beneficiary: AccountId,
status: TxStatus,
) -> anyhow::Result<TxInfo> {
let tx = api::tx()
.treasury()
.propose_spend(amount, MultiAddress::Id(beneficiary));
self.send_tx(tx, status).await
}
async fn approve(&self, proposal_id: u32, status: TxStatus) -> anyhow::Result<TxInfo> {
let tx = api::tx().treasury().approve_proposal(proposal_id);
self.send_tx(tx, status).await
}
async fn reject(&self, proposal_id: u32, status: TxStatus) -> anyhow::Result<TxInfo> {
let tx = api::tx().treasury().reject_proposal(proposal_id);
self.send_tx(tx, status).await
}
}
#[async_trait::async_trait]
impl TreasurySudoApi for RootConnection {
async fn approve(&self, proposal_id: u32, status: TxStatus) -> anyhow::Result<TxInfo> {
let call = Treasury(approve_proposal { proposal_id });
self.sudo_unchecked(call, status).await
}
async fn reject(&self, proposal_id: u32, status: TxStatus) -> anyhow::Result<TxInfo> {
let call = Treasury(reject_proposal { proposal_id });
self.sudo_unchecked(call, status).await
}
}
#[async_trait::async_trait]
impl<C: AsConnection + Sync> TreasureApiExt for C {
async fn possible_treasury_payout(&self) -> anyhow::Result<Balance> {
let session_period = self.get_session_period().await?;
let sessions_per_era = self.get_session_per_era().await?;
let millisecs_per_era =
MILLISECS_PER_BLOCK * session_period as u64 * sessions_per_era as u64;
Ok(era_payout(millisecs_per_era))
}
}
fn era_payout(miliseconds_per_era: u64) -> Balance {
const MILLISECONDS_PER_YEAR: u64 = 1000 * 3600 * 24 * 36525 / 100;
const TOKEN: u128 = 10u128.pow(12);
const YEARLY_INFLATION: Balance = 30_000_000 * TOKEN;
const VALIDATOR_REWARD: Perbill = Perbill::from_percent(90);
let portion = Perbill::from_rational(miliseconds_per_era, MILLISECONDS_PER_YEAR);
let total_payout = portion * YEARLY_INFLATION;
VALIDATOR_REWARD * total_payout
}