Skip to content

Commit

Permalink
fix(kensetsu): KGOLD added
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexey-N-Chernyshov committed Apr 26, 2024
1 parent 365b878 commit 73b3df0
Show file tree
Hide file tree
Showing 10 changed files with 455 additions and 228 deletions.
4 changes: 3 additions & 1 deletion common/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ pub enum ComicAssetId {
MichaelJacksonCD,
JesterMarotte,
CrackedBrassBell,
PyriteStone,
}

impl crate::traits::IsRepresentation for ComicAssetId {
Expand Down Expand Up @@ -109,6 +110,7 @@ impl From<PredefinedAssetId> for ComicAssetId {
PredefinedAssetId::KEN => JesterMarotte,
PredefinedAssetId::TBCD => MichaelJacksonCD,
PredefinedAssetId::KUSD => CrackedBrassBell,
PredefinedAssetId::KGOLD => PyriteStone,
}
}
}
Expand Down Expand Up @@ -299,7 +301,7 @@ macro_rules! mock_frame_system_config {
type SystemWeightInfo = ();
type SS58Prefix = ConstU16<42>;
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
type MaxConsumers = frame_support::traits::ConstU32<65536>;
}
};
}
Expand Down
7 changes: 2 additions & 5 deletions common/src/primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ mod _allowed_deprecated {
TBCD = 10,
KEN = 11,
KUSD = 12,
KGOLD = 13,
}
}

Expand All @@ -199,6 +200,7 @@ pub const XST: AssetId32<PredefinedAssetId> = AssetId32::from_asset_id(Predefine
pub const TBCD: AssetId32<PredefinedAssetId> = AssetId32::from_asset_id(PredefinedAssetId::TBCD);
pub const KEN: AssetId32<PredefinedAssetId> = AssetId32::from_asset_id(PredefinedAssetId::KEN);
pub const KUSD: AssetId32<PredefinedAssetId> = AssetId32::from_asset_id(PredefinedAssetId::KUSD);
pub const KGOLD: AssetId32<PredefinedAssetId> = AssetId32::from_asset_id(PredefinedAssetId::KGOLD);
pub const CERES_ASSET_ID: AssetId32<PredefinedAssetId> = AssetId32::from_bytes(hex!(
"008bcfd2387d3fc453333557eecb0efe59fcba128769b2feefdd306e98e66440"
));
Expand Down Expand Up @@ -651,11 +653,6 @@ impl SymbolName {
Self::from_str("USD").expect("`USD` is a valid symbol name")
}

/// MakeDAO DAI stablecoin
pub fn dai() -> Self {
Self::from_str("DAI").expect("`DAI` is a valid symbol name")
}

/// Troy ounce of gold
pub fn xau() -> Self {
Self::from_str("XAU").expect("`XAU` is a valid symbol name")
Expand Down
28 changes: 26 additions & 2 deletions node/chain_spec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@
use common::prelude::{Balance, DEXInfo, FixedWrapper};
use common::{
balance, fixed, hash, our_include, our_include_bytes, vec_push, BalancePrecision, DEXId, Fixed,
TechPurpose, APOLLO_ASSET_ID, DAI, DEFAULT_BALANCE_PRECISION, ETH, HERMES_ASSET_ID, KEN, KUSD,
PSWAP, TBCD, USDT, VAL, XOR, XST, XSTUSD,
TechPurpose, APOLLO_ASSET_ID, DAI, DEFAULT_BALANCE_PRECISION, ETH, HERMES_ASSET_ID, KEN, KGOLD,
KUSD, PSWAP, TBCD, USDT, VAL, XOR, XST, XSTUSD,
};
use frame_support::sp_runtime::Percent;
use framenode_runtime::eth_bridge::{AssetConfig, BridgeAssetData, NetworkConfig};
Expand Down Expand Up @@ -1438,6 +1438,18 @@ fn testnet_genesis(
None,
None,
),
#[cfg(feature = "ready-to-test")] // kensetsu
(
KGOLD.into(),
assets_and_permissions_account_id.clone(),
AssetSymbol(b"KGOLD".to_vec()),
AssetName(b"Kensetsu Gold".to_vec()),
DEFAULT_BALANCE_PRECISION,
Balance::zero(),
true,
None,
None,
),
(
common::AssetId32::from_bytes(hex!(
"008bcfd2387d3fc453333557eecb0efe59fcba128769b2feefdd306e98e66440"
Expand Down Expand Up @@ -2123,6 +2135,18 @@ fn mainnet_genesis(
None,
None,
),
#[cfg(feature = "ready-to-test")] // kensetsu
(
KGOLD.into(),
assets_and_permissions_account_id.clone(),
AssetSymbol(b"KGOLD".to_vec()),
AssetName(b"Kensetsu Gold".to_vec()),
DEFAULT_BALANCE_PRECISION,
Balance::zero(),
true,
None,
None,
),
(
common::AssetId32::from_bytes(hex!(
"008bcfd2387d3fc453333557eecb0efe59fcba128769b2feefdd306e98e66440"
Expand Down
10 changes: 5 additions & 5 deletions pallets/kensetsu/benchmarking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@
use assets::AssetIdOf;
use codec::Decode;
use common::{
balance, AssetId32, Balance, DEXId, PredefinedAssetId, PriceToolsProvider, PriceVariant,
SymbolName, DAI, KEN, KUSD, XOR,
balance, AssetId32, Balance, DEXId, PredefinedAssetId, PriceToolsProvider, PriceVariant, DAI,
KEN, KUSD, XOR,
};
use frame_benchmarking::benchmarks;
use frame_system::RawOrigin;
use hex_literal::hex;
use kensetsu::{
BorrowTax, CdpId, CollateralInfos, CollateralRiskParameters, Event, StablecoinInfos,
BorrowTax, CdpId, CollateralInfos, CollateralRiskParameters, Event, PegAsset, StablecoinInfos,
StablecoinParameters,
};
use price_tools::AVG_BLOCK_SPAN;
Expand Down Expand Up @@ -80,7 +80,7 @@ fn set_xor_as_collateral_type<T: Config>() {
bad_debt: 0,
stablecoin_parameters: StablecoinParameters {
hard_cap: Balance::MAX,
peg_symbol: SymbolName::dai(),
peg_asset: PegAsset::SoraAssetId(DAI.into()),
minimal_stability_fee_accrue: balance!(0),
},
}),
Expand Down Expand Up @@ -264,7 +264,7 @@ benchmarks! {
set_xor_as_collateral_type::<T>();
let cdp_id = create_cdp_with_xor::<T>();
}: {
kensetsu::Pallet::<T>::close_cdp(RawOrigin::Signed(caller::<T>()).into(), cdp_id).unwrap();
kensetsu::Pallet::<T>::close_cdp(RawOrigin::Signed(caller::<T>()).into(), cdp_id, balance!(0)).unwrap();
}

deposit_collateral {
Expand Down
145 changes: 88 additions & 57 deletions pallets/kensetsu/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,27 +72,35 @@ const VALIDATION_ERROR_CDP_SAFE: u8 = 4;
/// Liquidation limit reached
const VALIDATION_ERROR_LIQUIDATION_LIMIT: u8 = 5;

/// Staiblecoin may be pegged either to Oracle (like XAU, BTC) or Price tools AssetId (like XOR,
/// DAI).
#[derive(Debug, Clone, Encode, Decode, TypeInfo)]
pub enum PegAsset<AssetId> {
OracleSymbol(SymbolName),
SoraAssetId(AssetId),
}

/// Parameters of the tokens created by the protocol.
#[derive(Debug, Clone, Encode, Decode, TypeInfo)]
pub struct StablecoinParameters {
pub struct StablecoinParameters<AssetId> {
/// Maximum amount of tokens issued by the system.
pub hard_cap: Balance,

/// Symbol of the rate on the Oracle the stablecoin pegged to. 'DAI' pegged to USD.
pub peg_symbol: SymbolName,
/// Peg of stablecoin.
pub peg_asset: PegAsset<AssetId>,

/// Minimal uncollected fee in stablecoins that triggers offchain worker to call accrue.
pub minimal_stability_fee_accrue: Balance,
}

/// Parameters and additional variables related to stablecoins.
#[derive(Debug, Clone, Encode, Decode, TypeInfo)]
pub struct StablecoinInfo {
pub struct StablecoinInfo<AssetId> {
/// System bad debt, the amount of stablecoins not secured with collateral.
pub bad_debt: Balance,

/// Configurable parameters
pub stablecoin_parameters: StablecoinParameters,
pub stablecoin_parameters: StablecoinParameters<AssetId>,
}

#[derive(
Expand Down Expand Up @@ -376,7 +384,8 @@ pub mod pallet {
#[pallet::getter(fn stablecoin_infos)]
// TODO bound
#[pallet::unbounded]
pub type StablecoinInfos<T: Config> = StorageMap<_, Identity, AssetIdOf<T>, StablecoinInfo>;
pub type StablecoinInfos<T: Config> =
StorageMap<_, Identity, AssetIdOf<T>, StablecoinInfo<T::AssetId>>;

/// Parameters for collaterals, include risk parameters and interest recalculation coefficients.
/// Map (Collateral asset id, Stablecoin asset id => CollateralInfo)
Expand Down Expand Up @@ -516,6 +525,8 @@ pub mod pallet {
/// Risk management team size exceeded
TooManyManagers,
OperationNotPermitted,
/// Outstanding debt prevents closing CDP
OutstandingDebt,
/// Uncollected stability fee is too small for accrue
UncollectedStabilityFeeTooSmall,
HardCapSupply,
Expand Down Expand Up @@ -608,13 +619,16 @@ pub mod pallet {
///
/// - `origin`: The origin of the transaction, only CDP owner is allowed.
/// - `cdp_id`: The ID of the CDP to be closed.
/// - `amount`: The amount of stablecoins to repay outstanding debt, only min(amount, debt)
/// will be transferred.
#[pallet::call_index(1)]
#[pallet::weight(<T as Config>::WeightInfo::close_cdp())]
pub fn close_cdp(origin: OriginFor<T>, cdp_id: CdpId) -> DispatchResult {
pub fn close_cdp(origin: OriginFor<T>, cdp_id: CdpId, amount: Balance) -> DispatchResult {
let who = ensure_signed(origin)?;
let cdp = Self::cdp(cdp_id).ok_or(Error::<T>::CDPNotFound)?;
ensure!(who == cdp.owner, Error::<T>::OperationNotPermitted);
Self::repay_debt_internal(cdp_id, cdp.debt)?;
ensure!(amount >= cdp.debt, Error::<T>::OutstandingDebt);
Self::repay_debt_internal(cdp_id, amount)?;
Self::delete_cdp(cdp_id)
}

Expand Down Expand Up @@ -712,19 +726,22 @@ pub mod pallet {
)?;
// stablecoin supply change for collateral.
let stablecoin_supply_change: Balance;
if cdp.debt >= proceeds {
if cdp.debt > proceeds {
Self::burn_treasury(&cdp.stablecoin_asset_id, proceeds)?;
let shortage = cdp
.debt
.checked_sub(proceeds)
.ok_or(Error::<T>::CDPNotFound)?;
.ok_or(Error::<T>::ArithmeticError)?;
if cdp.collateral_amount <= collateral_liquidated {
// no collateral, total default
// CDP debt is not covered with liquidation, now it is a protocol bad debt
Self::cover_with_protocol(&cdp.stablecoin_asset_id, shortage)?;
let profit_burnt =
Self::cover_with_protocol(&cdp.stablecoin_asset_id, shortage)?;
// close empty CDP, debt == 0, collateral == 0
Self::delete_cdp(cdp_id)?;
stablecoin_supply_change = cdp.debt;
stablecoin_supply_change = proceeds
.checked_add(profit_burnt)
.ok_or(Error::<T>::ArithmeticError)?;
} else {
// partly covered
Self::update_cdp_debt(cdp_id, shortage)?;
Expand Down Expand Up @@ -1034,16 +1051,31 @@ pub mod pallet {
// TODO decide on visibility (pub)
pub fn add_stablecoin(
stablecoin_asset_id: &T::AssetId,
new_stablecoin_parameters: StablecoinParameters,
new_stablecoin_parameters: StablecoinParameters<T::AssetId>,
) -> DispatchResult {
ensure!(
(new_stablecoin_parameters.peg_symbol == SymbolName::dai())
|| (<T>::Oracle::list_enabled_symbols()?
.iter()
.find(|(symbol, _)| { *symbol == new_stablecoin_parameters.peg_symbol })
.is_some()),
Error::<T>::SymbolNotEnabledByOracle
);
match &new_stablecoin_parameters.peg_asset {
PegAsset::OracleSymbol(symbol) => {
ensure!(
<T>::Oracle::list_enabled_symbols()?
.iter()
.find(|(supported_symbol, _)| { *supported_symbol == *symbol })
.is_some(),
Error::<T>::SymbolNotEnabledByOracle
);
}
PegAsset::SoraAssetId(asset_id) => {
ensure!(
T::AssetInfoProvider::asset_exists(asset_id),
Error::<T>::WrongAssetId
);
// cannot be pegged to KEN or other stablecoin
ensure!(
*asset_id != T::KenAssetId::get()
|| !StablecoinInfos::<T>::contains_key(stablecoin_asset_id),
Error::<T>::WrongAssetId
);
}
}

let technical_account_id = technical::Pallet::<T>::tech_account_id_to_account_id(
&T::TreasuryTechAccount::get(),
Expand Down Expand Up @@ -1102,30 +1134,37 @@ pub mod pallet {
.risk_parameters
.liquidation_ratio;

// collateral price in DAI
let mut collateral_reference_price =
FixedU128::from_inner(T::PriceTools::get_average_price(
&collateral_asset_id,
&DAI.into(),
PriceVariant::Sell,
)?);

// stablecoin price in DAI
let symbol = Self::stablecoin_infos(stablecoin_asset_id)
let peg_asset = Self::stablecoin_infos(stablecoin_asset_id)
.ok_or(Error::<T>::StablecoinInfoNotFound)?
.stablecoin_parameters
.peg_symbol;
if symbol != SymbolName::dai() {
let stablecoin_price = FixedU128::from_inner(
<T>::Oracle::quote(&symbol)?
.ok_or(Error::<T>::SymbolNotEnabledByOracle)?
.value,
);
collateral_reference_price = collateral_reference_price
.checked_div(&stablecoin_price)
.ok_or(Error::<T>::ArithmeticError)?;
}

.peg_asset;
let collateral_reference_price = match peg_asset {
PegAsset::OracleSymbol(symbol) => {
// collateral price in DAI
let collateral_price_dai =
FixedU128::from_inner(T::PriceTools::get_average_price(
&collateral_asset_id,
&DAI.into(),
PriceVariant::Sell,
)?);
let stablecoin_price = FixedU128::from_inner(
<T>::Oracle::quote(&symbol)?
.ok_or(Error::<T>::SymbolNotEnabledByOracle)?
.value,
);
collateral_price_dai
.checked_div(&stablecoin_price)
.ok_or(Error::<T>::ArithmeticError)?
}
PegAsset::SoraAssetId(asset_id) => {
FixedU128::from_inner(T::PriceTools::get_average_price(
&collateral_asset_id,
&asset_id,
PriceVariant::Sell,
)?)
}
};
let collateral_volume = collateral_reference_price
.checked_mul(&FixedU128::from_inner(collateral))
.ok_or(Error::<T>::ArithmeticError)?;
Expand Down Expand Up @@ -1293,19 +1332,10 @@ pub mod pallet {
)?;
Self::mint_to(who, &cdp.stablecoin_asset_id, borrow_amount)?;
Self::update_cdp_debt(cdp_id, new_debt)?;
CollateralInfos::<T>::try_mutate(
cdp.collateral_asset_id,
cdp.stablecoin_asset_id,
|collateral_info| {
let collateral_info = collateral_info
.as_mut()
.ok_or(Error::<T>::CollateralInfoNotFound)?;
collateral_info.stablecoin_supply = collateral_info
.stablecoin_supply
.checked_add(borrow_amount_with_tax)
.ok_or(Error::<T>::ArithmeticError)?;
DispatchResult::Ok(())
},
Self::increase_stablecoin_supply(
&cdp.collateral_asset_id,
&cdp.stablecoin_asset_id,
borrow_amount_with_tax,
)?;
Self::deposit_event(Event::DebtIncreased {
cdp_id,
Expand Down Expand Up @@ -1711,10 +1741,11 @@ pub mod pallet {

/// Cover CDP debt with protocol balance
/// If protocol balance is less than amount to cover, it is a bad debt
/// Returns amount burnt.
fn cover_with_protocol(
stablecoin_asset_id: &AssetIdOf<T>,
amount: Balance,
) -> DispatchResult {
) -> Result<Balance, DispatchError> {
let treasury_account_id = technical::Pallet::<T>::tech_account_id_to_account_id(
&T::TreasuryTechAccount::get(),
)?;
Expand All @@ -1741,7 +1772,7 @@ pub mod pallet {
};
Self::burn_treasury(stablecoin_asset_id, to_burn)?;

Ok(())
Ok(to_burn)
}

/// Increments CDP Id counter, changes storage state.
Expand Down
Loading

0 comments on commit 73b3df0

Please sign in to comment.