fix last pr comments

This commit is contained in:
akildemir
2024-07-04 16:41:19 +03:00
parent ef703843a6
commit 1eee0a4d92
9 changed files with 138 additions and 93 deletions

1
Cargo.lock generated
View File

@@ -7998,6 +7998,7 @@ dependencies = [
"serai-coins-pallet",
"serai-dex-pallet",
"serai-genesis-liquidity-pallet",
"serai-genesis-liquidity-primitives",
"serai-in-instructions-primitives",
"serai-primitives",
"serai-validator-sets-pallet",

View File

@@ -7,7 +7,7 @@ use primitives::*;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Call {
remove_coin_liquidity { balance: Balance },
set_initial_price { prices: Prices, signature: Signature },
oraclize_values { prices: Prices, signature: Signature },
}
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]

View File

@@ -29,9 +29,9 @@ impl<'a> SeraiGenesisLiquidity<'a> {
.await
}
pub fn set_initial_price(prices: Prices, signature: Signature) -> Transaction {
pub fn oraclize_values(prices: Prices, signature: Signature) -> Transaction {
Serai::unsigned(serai_abi::Call::GenesisLiquidity(
serai_abi::genesis_liquidity::Call::set_initial_price { prices, signature },
serai_abi::genesis_liquidity::Call::oraclize_values { prices, signature },
))
}

View File

@@ -16,7 +16,7 @@ use serai_client::{
};
use serai_abi::{
genesis_liquidity::primitives::{set_initial_price_message, Prices},
genesis_liquidity::primitives::{oraclize_values_message, Prices},
primitives::COINS,
};
@@ -101,7 +101,7 @@ async fn test_genesis_liquidity(serai: Serai) {
.unwrap();
// set prices
let prices = Prices { bitcoin: 10u64.pow(8), monero: 184100, ethereum: 4785000, dai: 1500 };
let prices = Prices { monero: 184100, ethereum: 4785000, dai: 1500 };
set_prices(&serai, &prices).await;
// wait little bit..
@@ -127,7 +127,7 @@ async fn test_genesis_liquidity(serai: Serai) {
let pool_btc = btc_addresses.iter().fold(0u128, |acc, value| acc + u128::from(value.1 .0));
let pool_xmr = xmr_addresses.iter().fold(0u128, |acc, value| acc + u128::from(value.1 .0));
let pool_btc_value = (pool_btc * u128::from(prices.bitcoin)) / 10u128.pow(8);
let pool_btc_value = pool_btc;
let pool_xmr_value = (pool_xmr * u128::from(prices.monero)) / 10u128.pow(12);
let total_value = pool_btc_value + pool_xmr_value;
@@ -176,7 +176,8 @@ async fn set_prices(serai: &Serai, prices: &Prices) {
// prepare a Musig tx to set the initial prices
let pair = insecure_pair_from_name("Alice");
let public = pair.public();
let set = ValidatorSet { session: Session(0), network: NetworkId::Serai };
// we publish the tx in set 2
let set = ValidatorSet { session: Session(2), network: NetworkId::Serai };
let public_key = <Ristretto as Ciphersuite>::read_G::<&[u8]>(&mut public.0.as_ref()).unwrap();
let secret_key = <Ristretto as Ciphersuite>::read_F::<&[u8]>(
@@ -195,13 +196,11 @@ async fn set_prices(serai: &Serai, prices: &Prices) {
&Schnorrkel::new(b"substrate"),
&HashMap::from([(threshold_keys.params().i(), threshold_keys.into())]),
),
&set_initial_price_message(&set, prices),
&oraclize_values_message(&set, prices),
);
// set initial prices
let _ = publish_tx(
serai,
&SeraiGenesisLiquidity::set_initial_price(*prices, Signature(sig.to_bytes())),
)
.await;
let _ =
publish_tx(serai, &SeraiGenesisLiquidity::oraclize_values(*prices, Signature(sig.to_bytes())))
.await;
}

View File

@@ -47,7 +47,7 @@ pub mod pallet {
pub enum Event<T: Config> {
GenesisLiquidityAdded { by: SeraiAddress, balance: Balance },
GenesisLiquidityRemoved { by: SeraiAddress, balance: Balance },
GenesisLiquidityAddedToPool { coin1: Balance, coin2: Balance },
GenesisLiquidityAddedToPool { coin1: Balance, sri: Amount },
EconomicSecurityReached { network: NetworkId },
}
@@ -56,20 +56,12 @@ pub mod pallet {
/// Keeps shares and the amount of coins per account.
#[pallet::storage]
pub(crate) type Liquidity<T: Config> = StorageDoubleMap<
_,
Identity,
Coin,
Blake2_128Concat,
PublicKey,
(SubstrateAmount, SubstrateAmount),
OptionQuery,
>;
pub(crate) type Liquidity<T: Config> =
StorageDoubleMap<_, Identity, Coin, Blake2_128Concat, PublicKey, LiquidityAmount, OptionQuery>;
/// Keeps the total shares and the total amount of coins per coin.
#[pallet::storage]
pub(crate) type Supply<T: Config> =
StorageMap<_, Identity, Coin, (SubstrateAmount, SubstrateAmount), OptionQuery>;
pub(crate) type Supply<T: Config> = StorageMap<_, Identity, Coin, LiquidityAmount, OptionQuery>;
#[pallet::storage]
pub(crate) type EconomicSecurityReached<T: Config> =
@@ -83,7 +75,7 @@ pub mod pallet {
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_finalize(n: BlockNumberFor<T>) {
fn on_initialize(n: BlockNumberFor<T>) -> Weight {
#[cfg(feature = "fast-epoch")]
let final_block = 10u64;
@@ -115,9 +107,14 @@ pub mod pallet {
continue;
};
let pool_amount = u128::from(Supply::<T>::get(coin).unwrap_or((0, 0)).1);
let pool_value = pool_amount.saturating_mul(value.into()) / 10u128.pow(coin.decimals());
total_value = total_value.saturating_add(pool_value);
let pool_amount =
u128::from(Supply::<T>::get(coin).unwrap_or(LiquidityAmount::zero()).coins);
let pool_value = pool_amount
.checked_mul(value.into())
.unwrap()
.checked_div(10u128.pow(coin.decimals()))
.unwrap();
total_value = total_value.checked_add(pool_value).unwrap();
pool_values.push((coin, pool_amount, pool_value));
}
@@ -127,11 +124,18 @@ pub mod pallet {
for (i, (coin, pool_amount, pool_value)) in pool_values.into_iter().enumerate() {
// whatever sri left for the last coin should be ~= it's ratio
let sri_amount = if i == (pool_values_len - 1) {
GENESIS_SRI - total_sri_distributed
GENESIS_SRI.checked_sub(total_sri_distributed).unwrap()
} else {
u64::try_from(u128::from(GENESIS_SRI).saturating_mul(pool_value) / total_value).unwrap()
u64::try_from(
u128::from(GENESIS_SRI)
.checked_mul(pool_value)
.unwrap()
.checked_div(total_value)
.unwrap(),
)
.unwrap()
};
total_sri_distributed += sri_amount;
total_sri_distributed = total_sri_distributed.checked_add(sri_amount).unwrap();
// we can't add 0 liquidity
if !(pool_amount > 0 && sri_amount > 0) {
@@ -154,7 +158,7 @@ pub mod pallet {
// let everyone know about the event
Self::deposit_event(Event::GenesisLiquidityAddedToPool {
coin1: Balance { coin, amount: Amount(u64::try_from(pool_amount).unwrap()) },
coin2: Balance { coin: Coin::Serai, amount: Amount(sri_amount) },
sri: Amount(sri_amount),
});
}
assert_eq!(total_sri_distributed, GENESIS_SRI);
@@ -169,6 +173,7 @@ pub mod pallet {
}
// we accept we reached economic security once we can mint smallest amount of a network's coin
// TODO: move EconomicSecurity to a separate pallet
for coin in COINS {
let existing = EconomicSecurityReached::<T>::get(coin.network());
if existing.is_none() &&
@@ -178,6 +183,8 @@ pub mod pallet {
Self::deposit_event(Event::EconomicSecurityReached { network: coin.network() });
}
}
Weight::zero() // TODO
}
}
@@ -190,33 +197,37 @@ pub mod pallet {
Err(Error::<T>::GenesisPeriodEnded)?;
}
// mint the coins
Coins::<T>::mint(GENESIS_LIQUIDITY_ACCOUNT.into(), balance)?;
// calculate new shares & supply
let (new_shares, new_supply) = if let Some(supply) = Supply::<T>::get(balance.coin) {
let (new_liquidity, new_supply) = if let Some(supply) = Supply::<T>::get(balance.coin) {
// calculate amount of shares for this amount
let shares = Self::mul_div(supply.0, balance.amount.0, supply.1)?;
let shares = Self::mul_div(supply.shares, balance.amount.0, supply.coins)?;
// get new shares for this account
let existing = Liquidity::<T>::get(balance.coin, account).unwrap_or((0, 0));
let existing =
Liquidity::<T>::get(balance.coin, account).unwrap_or(LiquidityAmount::zero());
(
(
existing.0.checked_add(shares).ok_or(Error::<T>::AmountOverflowed)?,
existing.1.checked_add(balance.amount.0).ok_or(Error::<T>::AmountOverflowed)?,
),
(
supply.0.checked_add(shares).ok_or(Error::<T>::AmountOverflowed)?,
supply.1.checked_add(balance.amount.0).ok_or(Error::<T>::AmountOverflowed)?,
),
LiquidityAmount {
shares: existing.shares.checked_add(shares).ok_or(Error::<T>::AmountOverflowed)?,
coins: existing
.coins
.checked_add(balance.amount.0)
.ok_or(Error::<T>::AmountOverflowed)?,
},
LiquidityAmount {
shares: supply.shares.checked_add(shares).ok_or(Error::<T>::AmountOverflowed)?,
coins: supply
.coins
.checked_add(balance.amount.0)
.ok_or(Error::<T>::AmountOverflowed)?,
},
)
} else {
let first_amounts = (GENESIS_LP_SHARES, balance.amount.0);
(first_amounts, first_amounts)
let first_amount = LiquidityAmount { shares: GENESIS_LP_SHARES, coins: balance.amount.0 };
(first_amount, first_amount)
};
// save
Liquidity::<T>::set(balance.coin, account, Some(new_shares));
Liquidity::<T>::set(balance.coin, account, Some(new_liquidity));
Supply::<T>::set(balance.coin, Some(new_supply));
Self::deposit_event(Event::GenesisLiquidityAdded { by: account.into(), balance });
Ok(())
@@ -280,16 +291,16 @@ pub mod pallet {
let supply = Supply::<T>::get(balance.coin).ok_or(Error::<T>::NotEnoughLiquidity)?;
// check we are still in genesis period
let (new_shares, new_supply) = if Self::genesis_ended() {
let (new_liquidity, new_supply) = if Self::genesis_ended() {
// see how much liq tokens we have
let total_liq_tokens =
LiquidityTokens::<T>::balance(GENESIS_LIQUIDITY_ACCOUNT.into(), Coin::Serai).0;
// get how much user wants to remove
let (user_shares, user_coins) =
Liquidity::<T>::get(balance.coin, account).unwrap_or((0, 0));
let total_shares = Supply::<T>::get(balance.coin).unwrap_or((0, 0)).0;
let user_liq_tokens = Self::mul_div(total_liq_tokens, user_shares, total_shares)?;
let LiquidityAmount { shares, coins } =
Liquidity::<T>::get(balance.coin, account).unwrap_or(LiquidityAmount::zero());
let total_shares = Supply::<T>::get(balance.coin).unwrap_or(LiquidityAmount::zero()).shares;
let user_liq_tokens = Self::mul_div(total_liq_tokens, shares, total_shares)?;
let amount_to_remove = Self::mul_div(user_liq_tokens, balance.amount.0, GENESIS_LP_SHARES)?;
// remove liquidity from pool
@@ -308,18 +319,25 @@ pub mod pallet {
// burn the SRI if necessary
// TODO: take into consideration movement between pools.
let mut sri = current_sri.0.saturating_sub(prev_sri.0);
let mut sri: u64 = current_sri.0.saturating_sub(prev_sri.0);
let distance_to_full_pay =
GENESIS_SRI_TRICKLE_FEED.saturating_sub(Self::blocks_since_ec_security().unwrap_or(0));
let burn_sri_amount = sri.saturating_mul(distance_to_full_pay) / GENESIS_SRI_TRICKLE_FEED;
let burn_sri_amount = u64::try_from(
u128::from(sri)
.checked_mul(u128::from(distance_to_full_pay))
.ok_or(Error::<T>::AmountOverflowed)?
.checked_div(u128::from(GENESIS_SRI_TRICKLE_FEED))
.ok_or(Error::<T>::AmountOverflowed)?,
)
.map_err(|_| Error::<T>::AmountOverflowed)?;
Coins::<T>::burn(
origin.clone().into(),
Balance { coin: Coin::Serai, amount: Amount(burn_sri_amount) },
)?;
sri -= burn_sri_amount;
sri = sri.checked_sub(burn_sri_amount).ok_or(Error::<T>::AmountOverflowed)?;
// transfer to owner
let coin_out = current_coin.0 - prev_coin.0;
let coin_out = current_coin.0.saturating_sub(prev_coin.0);
Coins::<T>::transfer(
origin.clone().into(),
account,
@@ -333,14 +351,17 @@ pub mod pallet {
// return new amounts
(
(
user_shares.checked_sub(amount_to_remove).ok_or(Error::<T>::AmountOverflowed)?,
user_coins.checked_sub(coin_out).ok_or(Error::<T>::AmountOverflowed)?,
),
(
supply.0.checked_sub(amount_to_remove).ok_or(Error::<T>::AmountOverflowed)?,
supply.1.checked_sub(coin_out).ok_or(Error::<T>::AmountOverflowed)?,
),
LiquidityAmount {
shares: shares.checked_sub(amount_to_remove).ok_or(Error::<T>::AmountOverflowed)?,
coins: coins.checked_sub(coin_out).ok_or(Error::<T>::AmountOverflowed)?,
},
LiquidityAmount {
shares: supply
.shares
.checked_sub(amount_to_remove)
.ok_or(Error::<T>::AmountOverflowed)?,
coins: supply.coins.checked_sub(coin_out).ok_or(Error::<T>::AmountOverflowed)?,
},
)
} else {
if balance.amount.0 != GENESIS_LP_SHARES {
@@ -353,23 +374,26 @@ pub mod pallet {
Coins::<T>::transfer(
origin.into(),
account,
Balance { coin: balance.coin, amount: Amount(existing.1) },
Balance { coin: balance.coin, amount: Amount(existing.coins) },
)?;
(
(0, 0),
(
supply.0.checked_sub(existing.0).ok_or(Error::<T>::AmountOverflowed)?,
supply.1.checked_sub(existing.1).ok_or(Error::<T>::AmountOverflowed)?,
),
LiquidityAmount::zero(),
LiquidityAmount {
shares: supply
.shares
.checked_sub(existing.shares)
.ok_or(Error::<T>::AmountOverflowed)?,
coins: supply.coins.checked_sub(existing.coins).ok_or(Error::<T>::AmountOverflowed)?,
},
)
};
// save
if new_shares.0 == 0 {
if new_liquidity == LiquidityAmount::zero() {
Liquidity::<T>::set(balance.coin, account, None);
} else {
Liquidity::<T>::set(balance.coin, account, Some(new_shares));
Liquidity::<T>::set(balance.coin, account, Some(new_liquidity));
}
Supply::<T>::set(balance.coin, Some(new_supply));
@@ -380,7 +404,7 @@ pub mod pallet {
/// A call to submit the initial coin values in terms of BTC.
#[pallet::call_index(1)]
#[pallet::weight((0, DispatchClass::Operational))] // TODO
pub fn set_initial_price(
pub fn oraclize_values(
origin: OriginFor<T>,
prices: Prices,
_signature: Signature,
@@ -388,7 +412,7 @@ pub mod pallet {
ensure_none(origin)?;
// set the prices
Oracle::<T>::set(Coin::Bitcoin, Some(prices.bitcoin));
Oracle::<T>::set(Coin::Bitcoin, Some(10u64.pow(8)));
Oracle::<T>::set(Coin::Monero, Some(prices.monero));
Oracle::<T>::set(Coin::Ether, Some(prices.ethereum));
Oracle::<T>::set(Coin::Dai, Some(prices.dai));
@@ -402,11 +426,14 @@ pub mod pallet {
fn validate_unsigned(_: TransactionSource, call: &Self::Call) -> TransactionValidity {
match call {
Call::set_initial_price { ref prices, ref signature } => {
// TODO: if this is supposed to be called after a month, serai set won't still be
// in the session 0? Ideally this should pull the session from Vs pallet?
let set = ValidatorSet { network: NetworkId::Serai, session: Session(0) };
let signers = ValidatorSets::<T>::participants_for_latest_decided_set(NetworkId::Serai)
Call::oraclize_values { ref prices, ref signature } => {
let network = NetworkId::Serai;
let Some(session) = ValidatorSets::<T>::session(network) else {
return Err(TransactionValidityError::from(InvalidTransaction::Custom(0)));
};
let set = ValidatorSet { network, session };
let signers = ValidatorSets::<T>::participants_for_latest_decided_set(network)
.expect("no participant in the current set")
.into_iter()
.map(|(p, _)| p)
@@ -414,17 +441,17 @@ pub mod pallet {
// check this didn't get called before
if Self::oraclization_is_done() {
Err(InvalidTransaction::Custom(0))?;
Err(InvalidTransaction::Custom(1))?;
}
// make sure signers settings the price at the end of the genesis period.
// we don't need this check for tests.
#[cfg(not(feature = "fast-epoch"))]
if <frame_system::Pallet<T>>::block_number().saturated_into::<u64>() < MONTHS {
Err(InvalidTransaction::Custom(1))?;
Err(InvalidTransaction::Custom(2))?;
}
if !musig_key(set, &signers).verify(&set_initial_price_message(&set, prices), signature) {
if !musig_key(set, &signers).verify(&oraclize_values_message(&set, prices), signature) {
Err(InvalidTransaction::BadProof)?;
}

View File

@@ -28,13 +28,27 @@ pub const GENESIS_LIQUIDITY_ACCOUNT: SeraiAddress = system_address(b"Genesis-liq
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Prices {
pub bitcoin: u64,
pub monero: u64,
pub ethereum: u64,
pub dai: u64,
}
/// The message for the set_initial_price signature.
pub fn set_initial_price_message(set: &ValidatorSet, prices: &Prices) -> Vec<u8> {
(b"GenesisLiquidity-set_initial_price", set, prices).encode()
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
#[cfg_attr(feature = "std", derive(Zeroize))]
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LiquidityAmount {
pub shares: u64,
pub coins: u64,
}
impl LiquidityAmount {
pub fn zero() -> Self {
LiquidityAmount { shares: 0, coins: 0 }
}
}
/// The message for the oraclize_values signature.
pub fn oraclize_values_message(set: &ValidatorSet, prices: &Prices) -> Vec<u8> {
(b"GenesisLiquidity-oraclize_values", set, prices).encode()
}

View File

@@ -33,6 +33,7 @@ frame-support = { git = "https://github.com/serai-dex/substrate", default-featur
serai-primitives = { path = "../../primitives", default-features = false }
in-instructions-primitives = { package = "serai-in-instructions-primitives", path = "../primitives", default-features = false }
genesis-liquidity-primitives = { package = "serai-genesis-liquidity-primitives", path = "../../genesis-liquidity/primitives", default-features = false }
coins-pallet = { package = "serai-coins-pallet", path = "../../coins/pallet", default-features = false }
dex-pallet = { package = "serai-dex-pallet", path = "../../dex/pallet", default-features = false }
@@ -55,6 +56,7 @@ std = [
"serai-primitives/std",
"in-instructions-primitives/std",
"genesis-liquidity-primitives/std",
"coins-pallet/std",
"dex-pallet/std",

View File

@@ -19,6 +19,7 @@ pub mod pallet {
use sp_core::sr25519::Public;
use serai_primitives::{Coin, Amount, Balance};
use genesis_liquidity_primitives::GENESIS_LIQUIDITY_ACCOUNT;
use frame_support::pallet_prelude::*;
use frame_system::{pallet_prelude::*, RawOrigin};
@@ -205,6 +206,7 @@ pub mod pallet {
}
}
InInstruction::GenesisLiquidity(address) => {
Coins::<T>::mint(GENESIS_LIQUIDITY_ACCOUNT.into(), instruction.balance)?;
GenesisLiq::<T>::add_coin_liquidity(address.into(), instruction.balance)?;
}
}

View File

@@ -93,8 +93,8 @@ impl From<Call> for RuntimeCall {
serai_abi::genesis_liquidity::Call::remove_coin_liquidity { balance } => {
RuntimeCall::GenesisLiquidity(genesis_liquidity::Call::remove_coin_liquidity { balance })
}
serai_abi::genesis_liquidity::Call::set_initial_price { prices, signature } => {
RuntimeCall::GenesisLiquidity(genesis_liquidity::Call::set_initial_price {
serai_abi::genesis_liquidity::Call::oraclize_values { prices, signature } => {
RuntimeCall::GenesisLiquidity(genesis_liquidity::Call::oraclize_values {
prices,
signature,
})
@@ -276,8 +276,8 @@ impl TryInto<Call> for RuntimeCall {
genesis_liquidity::Call::remove_coin_liquidity { balance } => {
serai_abi::genesis_liquidity::Call::remove_coin_liquidity { balance }
}
genesis_liquidity::Call::set_initial_price { prices, signature } => {
serai_abi::genesis_liquidity::Call::set_initial_price { prices, signature }
genesis_liquidity::Call::oraclize_values { prices, signature } => {
serai_abi::genesis_liquidity::Call::oraclize_values { prices, signature }
}
_ => Err(())?,
}),