Skip to content

Commit

Permalink
tidy up
Browse files Browse the repository at this point in the history
  • Loading branch information
akildemir committed Jul 18, 2024
1 parent 1eee0a4 commit 9d50fe7
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 141 deletions.
2 changes: 1 addition & 1 deletion substrate/abi/src/genesis_liquidity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use primitives::*;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Call {
remove_coin_liquidity { balance: Balance },
oraclize_values { prices: Prices, signature: Signature },
oraclize_values { values: Values, signature: Signature },
}

#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
Expand Down
14 changes: 7 additions & 7 deletions substrate/client/src/serai/genesis_liquidity.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pub use serai_abi::genesis_liquidity::primitives;
use primitives::Prices;
use primitives::{Values, LiquidityAmount};

use serai_abi::primitives::*;

Expand Down Expand Up @@ -29,9 +29,9 @@ impl<'a> SeraiGenesisLiquidity<'a> {
.await
}

pub fn oraclize_values(prices: Prices, signature: Signature) -> Transaction {
pub fn oraclize_values(values: Values, signature: Signature) -> Transaction {
Serai::unsigned(serai_abi::Call::GenesisLiquidity(
serai_abi::genesis_liquidity::Call::oraclize_values { prices, signature },
serai_abi::genesis_liquidity::Call::oraclize_values { values, signature },
))
}

Expand All @@ -45,7 +45,7 @@ impl<'a> SeraiGenesisLiquidity<'a> {
&self,
address: &SeraiAddress,
coin: Coin,
) -> Result<(Amount, Amount), SeraiError> {
) -> Result<LiquidityAmount, SeraiError> {
Ok(
self
.0
Expand All @@ -55,11 +55,11 @@ impl<'a> SeraiGenesisLiquidity<'a> {
(coin, sp_core::hashing::blake2_128(&address.encode()), &address.0),
)
.await?
.unwrap_or((Amount(0), Amount(0))),
.unwrap_or(LiquidityAmount::zero()),
)
}

pub async fn supply(&self, coin: Coin) -> Result<(Amount, Amount), SeraiError> {
Ok(self.0.storage(PALLET, "Supply", coin).await?.unwrap_or((Amount(0), Amount(0))))
pub async fn supply(&self, coin: Coin) -> Result<LiquidityAmount, SeraiError> {
Ok(self.0.storage(PALLET, "Supply", coin).await?.unwrap_or(LiquidityAmount::zero()))
}
}
210 changes: 107 additions & 103 deletions substrate/client/tests/genesis_liquidity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ use schnorrkel::Schnorrkel;

use serai_client::{
genesis_liquidity::{
primitives::{GENESIS_LIQUIDITY_ACCOUNT, GENESIS_LP_SHARES},
primitives::{GENESIS_LIQUIDITY_ACCOUNT, INITIAL_GENESIS_LP_SHARES},
SeraiGenesisLiquidity,
},
validator_sets::primitives::{musig_context, Session, ValidatorSet},
};

use serai_abi::{
genesis_liquidity::primitives::{oraclize_values_message, Prices},
genesis_liquidity::primitives::{oraclize_values_message, Values},
primitives::COINS,
};

Expand All @@ -40,59 +40,53 @@ serai_test_fast_epoch!(
);

async fn test_genesis_liquidity(serai: Serai) {
// amounts
let amounts = vec![
Amount(5_53246991),
Amount(3_14562819),
Amount(9_33648912),
Amount(150_873639000000),
Amount(248_665228000000),
Amount(451_765529000000),
];

// addresses
let mut btc_addresses = vec![];
let mut xmr_addresses = vec![];
let addr_count = amounts.len();
for (i, amount) in amounts.into_iter().enumerate() {
let mut address = SeraiAddress::new([0; 32]);
OsRng.fill_bytes(&mut address.0);
if i < addr_count / 2 {
btc_addresses.push((address, amount));
} else {
xmr_addresses.push((address, amount));
// all coins except the native
let coins = COINS.into_iter().filter(|c| *c != Coin::native()).collect::<Vec<_>>();

// make accounts with amounts
let mut accounts = HashMap::new();
for coin in coins.clone() {
// make 5 accounts per coin
let mut values = vec![];
for _ in 0 .. 5 {
let mut address = SeraiAddress::new([0; 32]);
OsRng.fill_bytes(&mut address.0);
values.push((address, Amount(OsRng.next_u64() % 10u64.pow(coin.decimals()))));
}
accounts.insert(coin, values);
}

// btc batch
let mut block_hash = BlockHash([0; 32]);
OsRng.fill_bytes(&mut block_hash.0);
let btc_ins = btc_addresses
.iter()
.map(|(addr, amount)| InInstructionWithBalance {
instruction: InInstruction::GenesisLiquidity(*addr),
balance: Balance { coin: Coin::Bitcoin, amount: *amount },
})
.collect::<Vec<_>>();
let batch =
Batch { network: NetworkId::Bitcoin, id: 0, block: block_hash, instructions: btc_ins };
provide_batch(&serai, batch).await;

// xmr batch
let mut block_hash = BlockHash([0; 32]);
OsRng.fill_bytes(&mut block_hash.0);
let xmr_ins = xmr_addresses
.iter()
.map(|(addr, amount)| InInstructionWithBalance {
instruction: InInstruction::GenesisLiquidity(*addr),
balance: Balance { coin: Coin::Monero, amount: *amount },
})
.collect::<Vec<_>>();
let batch = Batch { network: NetworkId::Monero, id: 0, block: block_hash, instructions: xmr_ins };
provide_batch(&serai, batch).await;
// send a batch per coin
let mut batch_ids: HashMap<NetworkId, u32> = HashMap::new();
for coin in coins.clone() {
// set up instructions
let instructions = accounts[&coin]
.iter()
.map(|(addr, amount)| InInstructionWithBalance {
instruction: InInstruction::GenesisLiquidity(*addr),
balance: Balance { coin, amount: *amount },
})
.collect::<Vec<_>>();

// set up bloch hash
let mut block = BlockHash([0; 32]);
OsRng.fill_bytes(&mut block.0);

// set up batch id
batch_ids
.entry(coin.network())
.and_modify(|v| {
*v += 1;
})
.or_insert(0);

let batch =
Batch { network: coin.network(), id: batch_ids[&coin.network()], block, instructions };
provide_batch(&serai, batch).await;
}

// wait until genesis ends..
tokio::time::timeout(tokio::time::Duration::from_secs(300), async {
tokio::time::timeout(tokio::time::Duration::from_secs(3 * 10 * 6), async {
while serai.latest_finalized_block().await.unwrap().number() < 10 {
tokio::time::sleep(Duration::from_secs(6)).await;
}
Expand All @@ -101,18 +95,22 @@ async fn test_genesis_liquidity(serai: Serai) {
.unwrap();

// set prices
let prices = Prices { monero: 184100, ethereum: 4785000, dai: 1500 };
set_prices(&serai, &prices).await;

// wait little bit..
let values = Values { monero: 184100, ether: 4785000, dai: 1500 };
set_values(&serai, &values).await;
let values_map = HashMap::from([
(Coin::Monero, values.monero),
(Coin::Ether, values.ether),
(Coin::Dai, values.dai),
]);

// wait a little bit..
tokio::time::sleep(Duration::from_secs(12)).await;

// check total SRI supply is +100M
let last_block = serai.latest_finalized_block().await.unwrap().hash();
let serai = serai.as_of(last_block);
let sri = serai.coins().coin_supply(Coin::Serai).await.unwrap();
// there are 6 endowed accounts in dev-net. Take this into consideration when checking
// for the total sri minted at this time.
let serai = serai.as_of_latest_finalized_block().await.unwrap();
let sri = serai.coins().coin_supply(Coin::Serai).await.unwrap();
let endowed_amount: u64 = 1 << 60;
let total_sri = (6 * endowed_amount) + GENESIS_SRI;
assert_eq!(sri, Amount(total_sri));
Expand All @@ -124,60 +122,66 @@ async fn test_genesis_liquidity(serai: Serai) {
}

// check pools has proper liquidity
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;
let pool_xmr_value = (pool_xmr * u128::from(prices.monero)) / 10u128.pow(12);
let total_value = pool_btc_value + pool_xmr_value;

// calculated distributed SRI. We know that xmr is at the end of COINS array
// so it will be approximated to roof instead of floor after integer division.
let btc_sri = (pool_btc_value * u128::from(GENESIS_SRI)) / total_value;
let xmr_sri = ((pool_xmr_value * u128::from(GENESIS_SRI)) / total_value) + 1;

let btc_reserves = serai.dex().get_reserves(Coin::Bitcoin).await.unwrap().unwrap();
assert_eq!(u128::from(btc_reserves.0 .0), pool_btc);
assert_eq!(u128::from(btc_reserves.1 .0), btc_sri);

let xmr_reserves = serai.dex().get_reserves(Coin::Monero).await.unwrap().unwrap();
assert_eq!(u128::from(xmr_reserves.0 .0), pool_xmr);
assert_eq!(u128::from(xmr_reserves.1 .0), xmr_sri);

// check each btc liq provider got liq tokens proportional to their value
let btc_liq_supply = serai.genesis_liquidity().supply(Coin::Bitcoin).await.unwrap();
for (acc, amount) in btc_addresses {
let acc_liq_shares =
serai.genesis_liquidity().liquidity(&acc, Coin::Bitcoin).await.unwrap().0 .0;

// since we can't test the ratios directly(due to integer division giving 0)
// we test whether they give the same result when multiplied by another constant.
// Following test ensures the account in fact has the right amount of shares.
let shares_ratio = (GENESIS_LP_SHARES * acc_liq_shares) / btc_liq_supply.0 .0;
let amounts_ratio = (GENESIS_LP_SHARES * amount.0) / u64::try_from(pool_btc).unwrap();
assert_eq!(shares_ratio, amounts_ratio);
let mut pool_amounts = HashMap::new();
let mut total_value = 0u128;
for coin in coins.clone() {
let total_coin = accounts[&coin].iter().fold(0u128, |acc, value| acc + u128::from(value.1 .0));
let value = if coin != Coin::Bitcoin {
(total_coin * u128::from(values_map[&coin])) / 10u128.pow(coin.decimals())
} else {
total_coin
};

total_value += value;
pool_amounts.insert(coin, (total_coin, value));
}

// check each xmr liq provider got liq tokens proportional to their value
let xmr_liq_supply = serai.genesis_liquidity().supply(Coin::Monero).await.unwrap();
for (acc, amount) in xmr_addresses {
let acc_liq_shares =
serai.genesis_liquidity().liquidity(&acc, Coin::Monero).await.unwrap().0 .0;
// check distributed SRI per pool
let mut total_sri_distributed = 0u128;
for coin in coins.clone() {
let sri = if coin == *COINS.last().unwrap() {
u128::from(GENESIS_SRI).checked_sub(total_sri_distributed).unwrap()
} else {
(pool_amounts[&coin].1 * u128::from(GENESIS_SRI)) / total_value
};
total_sri_distributed += sri;

let shares_ratio = (GENESIS_LP_SHARES * acc_liq_shares) / xmr_liq_supply.0 .0;
let amounts_ratio = (GENESIS_LP_SHARES * amount.0) / u64::try_from(pool_xmr).unwrap();
assert_eq!(shares_ratio, amounts_ratio);
let reserves = serai.dex().get_reserves(coin).await.unwrap().unwrap();
assert_eq!(u128::from(reserves.0 .0), pool_amounts[&coin].0); // coin side
assert_eq!(u128::from(reserves.1 .0), sri); // SRI side
}

// check each liquidity provider got liquidity tokens proportional to their value
for coin in coins {
let liq_supply = serai.genesis_liquidity().supply(coin).await.unwrap();
for (acc, amount) in &accounts[&coin] {
let acc_liq_shares = serai.genesis_liquidity().liquidity(acc, coin).await.unwrap().shares;

// since we can't test the ratios directly(due to integer division giving 0)
// we test whether they give the same result when multiplied by another constant.
// Following test ensures the account in fact has the right amount of shares.
let mut shares_ratio = (INITIAL_GENESIS_LP_SHARES * acc_liq_shares) / liq_supply.shares;
let amounts_ratio =
(INITIAL_GENESIS_LP_SHARES * amount.0) / u64::try_from(pool_amounts[&coin].0).unwrap();

// we can tolerate 1 unit diff between them due to integer division.
if shares_ratio.abs_diff(amounts_ratio) == 1 {
shares_ratio = amounts_ratio;
}

assert_eq!(shares_ratio, amounts_ratio);
}
}

// TODO: test remove the liq before/after genesis ended.
}

async fn set_prices(serai: &Serai, prices: &Prices) {
async fn set_values(serai: &Serai, values: &Values) {
// prepare a Musig tx to set the initial prices
let pair = insecure_pair_from_name("Alice");
let public = pair.public();
// we publish the tx in set 2
let set = ValidatorSet { session: Session(2), network: NetworkId::Serai };
// we publish the tx in set 4
let set = ValidatorSet { session: Session(4), 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]>(
Expand All @@ -196,11 +200,11 @@ async fn set_prices(serai: &Serai, prices: &Prices) {
&Schnorrkel::new(b"substrate"),
&HashMap::from([(threshold_keys.params().i(), threshold_keys.into())]),
),
&oraclize_values_message(&set, prices),
&oraclize_values_message(&set, values),
);

// set initial prices
// oraclize values
let _ =
publish_tx(serai, &SeraiGenesisLiquidity::oraclize_values(*prices, Signature(sig.to_bytes())))
publish_tx(serai, &SeraiGenesisLiquidity::oraclize_values(*values, Signature(sig.to_bytes())))
.await;
}
Loading

0 comments on commit 9d50fe7

Please sign in to comment.