mirror of
https://github.com/serai-dex/serai.git
synced 2025-01-03 09:29:46 +00:00
Dex improvements (#422)
* remove dex traits&balance types * remove liq tokens pallet in favor of coins-pallet instance * fix tests & benchmarks * remove liquidity tokens trait * fix CI * fix pr comments * Slight renamings * Add burn_with_instruction as a negative to LiquidityTokens CallFilter * Remove use of One, Zero, Saturating taits in dex pallet --------- Co-authored-by: Luke Parker <lukeparker5132@gmail.com>
This commit is contained in:
parent
a43815f101
commit
d015ee96a3
30 changed files with 1063 additions and 2162 deletions
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
|
@ -63,8 +63,6 @@ jobs:
|
|||
-p serai-primitives \
|
||||
-p serai-coins-primitives \
|
||||
-p serai-coins-pallet \
|
||||
-p serai-liquidity-tokens-pallet \
|
||||
-p serai-dex-primitives \
|
||||
-p serai-dex-pallet \
|
||||
-p serai-validator-sets-primitives \
|
||||
-p serai-validator-sets-pallet \
|
||||
|
|
32
Cargo.lock
generated
32
Cargo.lock
generated
|
@ -7535,7 +7535,6 @@ dependencies = [
|
|||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"serai-coins-primitives",
|
||||
"serai-dex-primitives",
|
||||
"serai-primitives",
|
||||
"sp-core",
|
||||
"sp-runtime",
|
||||
|
@ -7626,8 +7625,6 @@ dependencies = [
|
|||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"serai-coins-pallet",
|
||||
"serai-dex-primitives",
|
||||
"serai-liquidity-tokens-pallet",
|
||||
"serai-primitives",
|
||||
"sp-api",
|
||||
"sp-arithmetic",
|
||||
|
@ -7637,19 +7634,6 @@ dependencies = [
|
|||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serai-dex-primitives"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"frame-benchmarking",
|
||||
"frame-support",
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"serai-primitives",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serai-docker-tests"
|
||||
version = "0.1.0"
|
||||
|
@ -7719,20 +7703,6 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serai-liquidity-tokens-pallet"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"frame-support",
|
||||
"frame-system",
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"serai-dex-primitives",
|
||||
"serai-primitives",
|
||||
"sp-core",
|
||||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serai-message-queue"
|
||||
version = "0.1.0"
|
||||
|
@ -7947,9 +7917,7 @@ dependencies = [
|
|||
"scale-info",
|
||||
"serai-coins-pallet",
|
||||
"serai-dex-pallet",
|
||||
"serai-dex-primitives",
|
||||
"serai-in-instructions-pallet",
|
||||
"serai-liquidity-tokens-pallet",
|
||||
"serai-primitives",
|
||||
"serai-signals-pallet",
|
||||
"serai-validator-sets-pallet",
|
||||
|
|
|
@ -208,8 +208,8 @@ async fn handle_batch_and_burns<D: Db, Pro: Processors>(
|
|||
}
|
||||
}
|
||||
|
||||
for burn in serai.coins().burn_events().await? {
|
||||
if let CoinsEvent::Burn { from: _, instruction } = burn {
|
||||
for burn in serai.coins().burn_with_instruction_events().await? {
|
||||
if let CoinsEvent::BurnWithInstruction { from: _, instruction } = burn {
|
||||
let network = instruction.balance.coin.network();
|
||||
network_had_event(&mut burns, &mut batches, network);
|
||||
|
||||
|
|
|
@ -54,8 +54,6 @@ exceptions = [
|
|||
{ allow = ["AGPL-3.0"], name = "serai-coordinator" },
|
||||
|
||||
{ allow = ["AGPL-3.0"], name = "serai-coins-pallet" },
|
||||
{ allow = ["AGPL-3.0"], name = "serai-liquidity-tokens-pallet" },
|
||||
{ allow = ["AGPL-3.0"], name = "serai-dex-primitives" },
|
||||
{ allow = ["AGPL-3.0"], name = "serai-dex-pallet" },
|
||||
|
||||
{ allow = ["AGPL-3.0"], name = "serai-in-instructions-pallet" },
|
||||
|
|
|
@ -24,8 +24,8 @@ impl<'a> SeraiCoins<'a> {
|
|||
self.0.events::<Coins, _>(|event| matches!(event, CoinsEvent::Mint { .. })).await
|
||||
}
|
||||
|
||||
pub async fn burn_events(&self) -> Result<Vec<CoinsEvent>, SeraiError> {
|
||||
self.0.events::<Coins, _>(|event| matches!(event, CoinsEvent::Burn { .. })).await
|
||||
pub async fn burn_with_instruction_events(&self) -> Result<Vec<CoinsEvent>, SeraiError> {
|
||||
self.0.events::<Coins, _>(|event| matches!(event, CoinsEvent::BurnWithInstruction { .. })).await
|
||||
}
|
||||
|
||||
pub async fn coin_supply(&self, coin: Coin) -> Result<Amount, SeraiError> {
|
||||
|
@ -64,7 +64,15 @@ impl<'a> SeraiCoins<'a> {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn burn(instruction: OutInstructionWithBalance) -> Payload<Composite<()>> {
|
||||
Payload::new(PALLET, "burn", scale_composite(coins::Call::<Runtime>::burn { instruction }))
|
||||
pub fn burn(balance: Balance) -> Payload<Composite<()>> {
|
||||
Payload::new(PALLET, "burn", scale_composite(coins::Call::<Runtime>::burn { balance }))
|
||||
}
|
||||
|
||||
pub fn burn_with_instruction(instruction: OutInstructionWithBalance) -> Payload<Composite<()>> {
|
||||
Payload::new(
|
||||
PALLET,
|
||||
"burn_with_instruction",
|
||||
scale_composite(coins::Call::<Runtime>::burn_with_instruction { instruction }),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,12 +31,11 @@ impl<'a> SeraiDex<'a> {
|
|||
PALLET,
|
||||
"add_liquidity",
|
||||
scale_composite(dex::Call::<Runtime>::add_liquidity {
|
||||
coin1: coin,
|
||||
coin2: Coin::Serai,
|
||||
amount1_desired: coin_amount.0,
|
||||
amount2_desired: sri_amount.0,
|
||||
amount1_min: min_coin_amount.0,
|
||||
amount2_min: min_sri_amount.0,
|
||||
coin,
|
||||
coin_desired: coin_amount.0,
|
||||
sri_desired: sri_amount.0,
|
||||
coin_min: min_coin_amount.0,
|
||||
sri_min: min_sri_amount.0,
|
||||
mint_to: address.into(),
|
||||
}),
|
||||
)
|
||||
|
|
|
@ -93,7 +93,7 @@ serai_test!(
|
|||
&serai
|
||||
.sign(
|
||||
&PairSigner::new(pair),
|
||||
&SeraiCoins::burn(instruction.clone()),
|
||||
&SeraiCoins::burn_with_instruction(instruction.clone()),
|
||||
0,
|
||||
BaseExtrinsicParamsBuilder::new(),
|
||||
)
|
||||
|
@ -102,8 +102,8 @@ serai_test!(
|
|||
.await;
|
||||
|
||||
let serai = serai.as_of(block).coins();
|
||||
let events = serai.burn_events().await.unwrap();
|
||||
assert_eq!(events, vec![CoinsEvent::Burn { from: address.into(), instruction }]);
|
||||
let events = serai.burn_with_instruction_events().await.unwrap();
|
||||
assert_eq!(events, vec![CoinsEvent::BurnWithInstruction { from: address.into(), instruction }]);
|
||||
assert_eq!(serai.coin_supply(coin).await.unwrap(), Amount(0));
|
||||
assert_eq!(serai.coin_balance(coin, address).await.unwrap(), Amount(0));
|
||||
})
|
||||
|
|
|
@ -36,24 +36,24 @@ serai_test!(
|
|||
events,
|
||||
vec![
|
||||
DexEvent::PoolCreated {
|
||||
pool_id: (Coin::Serai, Coin::Bitcoin),
|
||||
pool_account: PublicKey::from_raw(blake2_256(&(Coin::Serai, Coin::Bitcoin).encode())),
|
||||
lp_token: 0,
|
||||
pool_id: Coin::Bitcoin,
|
||||
pool_account: PublicKey::from_raw(blake2_256(&Coin::Bitcoin.encode())),
|
||||
lp_token: Coin::Bitcoin,
|
||||
},
|
||||
DexEvent::PoolCreated {
|
||||
pool_id: (Coin::Serai, Coin::Ether),
|
||||
pool_account: PublicKey::from_raw(blake2_256(&(Coin::Serai, Coin::Ether).encode())),
|
||||
lp_token: 1,
|
||||
pool_id: Coin::Ether,
|
||||
pool_account: PublicKey::from_raw(blake2_256(&Coin::Ether.encode())),
|
||||
lp_token: Coin::Ether,
|
||||
},
|
||||
DexEvent::PoolCreated {
|
||||
pool_id: (Coin::Serai, Coin::Dai),
|
||||
pool_account: PublicKey::from_raw(blake2_256(&(Coin::Serai, Coin::Dai).encode())),
|
||||
lp_token: 2,
|
||||
pool_id: Coin::Dai,
|
||||
pool_account: PublicKey::from_raw(blake2_256(&Coin::Dai.encode())),
|
||||
lp_token: Coin::Dai,
|
||||
},
|
||||
DexEvent::PoolCreated {
|
||||
pool_id: (Coin::Serai, Coin::Monero),
|
||||
pool_account: PublicKey::from_raw(blake2_256(&(Coin::Serai, Coin::Monero).encode())),
|
||||
lp_token: 3,
|
||||
pool_id: Coin::Monero,
|
||||
pool_account: PublicKey::from_raw(blake2_256(&Coin::Monero.encode())),
|
||||
lp_token: Coin::Monero,
|
||||
},
|
||||
]
|
||||
);
|
||||
|
@ -93,10 +93,10 @@ serai_test!(
|
|||
vec![DexEvent::LiquidityAdded {
|
||||
who: pair.public(),
|
||||
mint_to: pair.public(),
|
||||
pool_id: (Coin::Serai, Coin::Monero),
|
||||
amount1_provided: coin_amount.0,
|
||||
amount2_provided: sri_amount.0,
|
||||
lp_token: 3,
|
||||
pool_id: Coin::Monero,
|
||||
coin_amount: coin_amount.0,
|
||||
sri_amount: sri_amount.0,
|
||||
lp_token: Coin::Monero,
|
||||
lp_token_minted: 49_999999990000
|
||||
}]
|
||||
);
|
||||
|
@ -277,10 +277,10 @@ serai_test!(
|
|||
vec![DexEvent::LiquidityAdded {
|
||||
who: IN_INSTRUCTION_EXECUTOR.into(),
|
||||
mint_to: pair.public(),
|
||||
pool_id: (Coin::Serai, Coin::Bitcoin),
|
||||
amount1_provided: 6_947_918_403_646,
|
||||
amount2_provided: 10_000_000_000_000, // half of sent amount
|
||||
lp_token: 0,
|
||||
pool_id: Coin::Bitcoin,
|
||||
coin_amount: 10_000_000_000_000, // half of sent amount
|
||||
sri_amount: 6_947_918_403_646,
|
||||
lp_token: Coin::Bitcoin,
|
||||
lp_token_minted: 8333333333332
|
||||
}]
|
||||
);
|
||||
|
|
|
@ -24,8 +24,6 @@ sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features
|
|||
|
||||
pallet-transaction-payment = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
|
||||
dex-primitives = { package = "serai-dex-primitives", path = "../../dex/primitives", default-features = false }
|
||||
|
||||
serai-primitives = { path = "../../primitives", default-features = false }
|
||||
coins-primitives = { package = "serai-coins-primitives", path = "../primitives", default-features = false }
|
||||
|
||||
|
@ -40,8 +38,6 @@ std = [
|
|||
|
||||
"pallet-transaction-payment/std",
|
||||
|
||||
"dex-primitives/std",
|
||||
|
||||
"serai-primitives/std",
|
||||
"coins-primitives/std",
|
||||
]
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use sp_std::vec::Vec;
|
||||
use sp_std::{vec::Vec, any::TypeId};
|
||||
use sp_core::sr25519::Public;
|
||||
use sp_runtime::{
|
||||
traits::{DispatchInfoOf, PostDispatchInfoOf},
|
||||
|
@ -14,80 +14,82 @@ pub mod pallet {
|
|||
|
||||
use pallet_transaction_payment::{Config as TpConfig, OnChargeTransaction};
|
||||
|
||||
use dex_primitives::{Currency, Coins as CoinsTrait};
|
||||
|
||||
use serai_primitives::*;
|
||||
pub use coins_primitives as primitives;
|
||||
use primitives::*;
|
||||
|
||||
type LiquidityTokensInstance = crate::Instance1;
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config<AccountId = Public> {
|
||||
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||
pub trait Config<I: 'static = ()>: frame_system::Config<AccountId = Public> {
|
||||
type RuntimeEvent: From<Event<Self, I>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||
}
|
||||
|
||||
#[pallet::genesis_config]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||
pub struct GenesisConfig<T: Config> {
|
||||
pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
|
||||
pub accounts: Vec<(T::AccountId, Balance)>,
|
||||
pub _ignore: PhantomData<I>,
|
||||
}
|
||||
|
||||
impl<T: Config> Default for GenesisConfig<T> {
|
||||
impl<T: Config<I>, I: 'static> Default for GenesisConfig<T, I> {
|
||||
fn default() -> Self {
|
||||
GenesisConfig { accounts: Default::default() }
|
||||
GenesisConfig { accounts: Default::default(), _ignore: Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
#[pallet::error]
|
||||
pub enum Error<T> {
|
||||
pub enum Error<T, I = ()> {
|
||||
AmountOverflowed,
|
||||
NotEnoughCoins,
|
||||
SriBurnNotAllowed,
|
||||
BurnWithInstructionNotAllowed,
|
||||
}
|
||||
|
||||
#[pallet::event]
|
||||
#[pallet::generate_deposit(fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
pub enum Event<T: Config<I>, I: 'static = ()> {
|
||||
Mint { to: Public, balance: Balance },
|
||||
Burn { from: Public, instruction: OutInstructionWithBalance },
|
||||
SriBurn { from: Public, amount: Amount },
|
||||
Burn { from: Public, balance: Balance },
|
||||
BurnWithInstruction { from: Public, instruction: OutInstructionWithBalance },
|
||||
Transfer { from: Public, to: Public, balance: Balance },
|
||||
}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(PhantomData<T>);
|
||||
pub struct Pallet<T, I = ()>(_);
|
||||
|
||||
/// The amount of coins each account has.
|
||||
// Identity is used as the second key's hasher due to it being a non-manipulatable fixed-space
|
||||
// ID.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn balances)]
|
||||
pub type Balances<T: Config> =
|
||||
pub type Balances<T: Config<I>, I: 'static = ()> =
|
||||
StorageDoubleMap<_, Blake2_128Concat, Public, Identity, Coin, SubstrateAmount, ValueQuery>;
|
||||
|
||||
/// The total supply of each coin.
|
||||
// We use Identity type here again due to reasons stated in the Balances Storage.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn supply)]
|
||||
pub type Supply<T: Config> = StorageMap<_, Identity, Coin, SubstrateAmount, ValueQuery>;
|
||||
pub type Supply<T: Config<I>, I: 'static = ()> =
|
||||
StorageMap<_, Identity, Coin, SubstrateAmount, ValueQuery>;
|
||||
|
||||
#[pallet::genesis_build]
|
||||
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
|
||||
impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> {
|
||||
fn build(&self) {
|
||||
// initialize the supply of the coins
|
||||
// TODO: Don't use COINS yet GenesisConfig so we can safely expand COINS
|
||||
for c in &COINS {
|
||||
Supply::<T>::set(c, 0);
|
||||
Supply::<T, I>::set(c, 0);
|
||||
}
|
||||
|
||||
// initialize the genesis accounts
|
||||
for (account, balance) in &self.accounts {
|
||||
Pallet::<T>::mint(*account, *balance).unwrap();
|
||||
Pallet::<T, I>::mint(*account, *balance).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pallet::hooks]
|
||||
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
|
||||
impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
|
||||
fn on_initialize(_: BlockNumberFor<T>) -> Weight {
|
||||
// burn the fees collected previous block
|
||||
let coin = Coin::Serai;
|
||||
|
@ -95,66 +97,66 @@ pub mod pallet {
|
|||
// we can unwrap, we are not burning more then what we have
|
||||
// If this errors, it'll halt the runtime however (due to being called at the start of every
|
||||
// block), requiring extra care when reviewing
|
||||
Self::burn_sri(FEE_ACCOUNT.into(), amount).unwrap();
|
||||
Self::burn_internal(FEE_ACCOUNT.into(), Balance { coin, amount }).unwrap();
|
||||
Weight::zero() // TODO
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Pallet<T> {
|
||||
impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
/// Returns the balance of a given account for `coin`.
|
||||
pub fn balance(of: Public, coin: Coin) -> Amount {
|
||||
Amount(Self::balances(of, coin))
|
||||
}
|
||||
|
||||
fn decrease_balance_internal(from: Public, balance: Balance) -> Result<(), Error<T>> {
|
||||
fn decrease_balance_internal(from: Public, balance: Balance) -> Result<(), Error<T, I>> {
|
||||
let coin = &balance.coin;
|
||||
|
||||
// sub amount from account
|
||||
let new_amount = Self::balances(from, coin)
|
||||
.checked_sub(balance.amount.0)
|
||||
.ok_or(Error::<T>::NotEnoughCoins)?;
|
||||
.ok_or(Error::<T, I>::NotEnoughCoins)?;
|
||||
|
||||
// save
|
||||
if new_amount == 0 {
|
||||
Balances::<T>::remove(from, coin);
|
||||
Balances::<T, I>::remove(from, coin);
|
||||
} else {
|
||||
Balances::<T>::set(from, coin, new_amount);
|
||||
Balances::<T, I>::set(from, coin, new_amount);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn increase_balance_internal(to: Public, balance: Balance) -> Result<(), Error<T>> {
|
||||
fn increase_balance_internal(to: Public, balance: Balance) -> Result<(), Error<T, I>> {
|
||||
let coin = &balance.coin;
|
||||
|
||||
// sub amount from account
|
||||
let new_amount = Self::balances(to, coin)
|
||||
.checked_add(balance.amount.0)
|
||||
.ok_or(Error::<T>::AmountOverflowed)?;
|
||||
.ok_or(Error::<T, I>::AmountOverflowed)?;
|
||||
|
||||
// save
|
||||
Balances::<T>::set(to, coin, new_amount);
|
||||
Balances::<T, I>::set(to, coin, new_amount);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Mint `balance` to the given account.
|
||||
///
|
||||
/// Errors if any amount overflows.
|
||||
pub fn mint(to: Public, balance: Balance) -> Result<(), Error<T>> {
|
||||
pub fn mint(to: Public, balance: Balance) -> Result<(), Error<T, I>> {
|
||||
// update the balance
|
||||
Self::increase_balance_internal(to, balance)?;
|
||||
|
||||
// update the supply
|
||||
let new_supply = Self::supply(balance.coin)
|
||||
.checked_add(balance.amount.0)
|
||||
.ok_or(Error::<T>::AmountOverflowed)?;
|
||||
Supply::<T>::set(balance.coin, new_supply);
|
||||
.ok_or(Error::<T, I>::AmountOverflowed)?;
|
||||
Supply::<T, I>::set(balance.coin, new_supply);
|
||||
|
||||
Self::deposit_event(Event::Mint { to, balance });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Burn `balance` from the specified account.
|
||||
fn burn_internal(from: Public, balance: Balance) -> Result<(), Error<T>> {
|
||||
/// Burn `balance` from the specified account.
|
||||
fn burn_internal(from: Public, balance: Balance) -> Result<(), Error<T, I>> {
|
||||
// don't waste time if amount == 0
|
||||
if balance.amount.0 == 0 {
|
||||
return Ok(());
|
||||
|
@ -165,31 +167,17 @@ pub mod pallet {
|
|||
|
||||
// update the supply
|
||||
let new_supply = Self::supply(balance.coin).checked_sub(balance.amount.0).unwrap();
|
||||
Supply::<T>::set(balance.coin, new_supply);
|
||||
Supply::<T, I>::set(balance.coin, new_supply);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn burn_sri(from: Public, amount: Amount) -> Result<(), Error<T>> {
|
||||
Self::burn_internal(from, Balance { coin: Coin::Serai, amount })?;
|
||||
Self::deposit_event(Event::SriBurn { from, amount });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn burn_non_sri(
|
||||
from: Public,
|
||||
instruction: OutInstructionWithBalance,
|
||||
) -> Result<(), Error<T>> {
|
||||
if instruction.balance.coin == Coin::Serai {
|
||||
Err(Error::<T>::SriBurnNotAllowed)?;
|
||||
}
|
||||
Self::burn_internal(from, instruction.balance)?;
|
||||
Self::deposit_event(Event::Burn { from, instruction });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Transfer `balance` from `from` to `to`.
|
||||
pub fn transfer_internal(from: Public, to: Public, balance: Balance) -> Result<(), Error<T>> {
|
||||
pub fn transfer_internal(
|
||||
from: Public,
|
||||
to: Public,
|
||||
balance: Balance,
|
||||
) -> Result<(), Error<T, I>> {
|
||||
// update balances of accounts
|
||||
Self::decrease_balance_internal(from, balance)?;
|
||||
Self::increase_balance_internal(to, balance)?;
|
||||
|
@ -199,7 +187,7 @@ pub mod pallet {
|
|||
}
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
#[pallet::call_index(0)]
|
||||
#[pallet::weight((0, DispatchClass::Normal))] // TODO
|
||||
pub fn transfer(origin: OriginFor<T>, to: Public, balance: Balance) -> DispatchResult {
|
||||
|
@ -208,77 +196,35 @@ pub mod pallet {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Burn `balance` from the caller.
|
||||
#[pallet::call_index(1)]
|
||||
#[pallet::weight((0, DispatchClass::Normal))] // TODO
|
||||
pub fn burn(origin: OriginFor<T>, instruction: OutInstructionWithBalance) -> DispatchResult {
|
||||
pub fn burn(origin: OriginFor<T>, balance: Balance) -> DispatchResult {
|
||||
let from = ensure_signed(origin)?;
|
||||
Self::burn_non_sri(from, instruction)?;
|
||||
Self::burn_internal(from, balance)?;
|
||||
Self::deposit_event(Event::Burn { from, balance });
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Currency<T::AccountId> for Pallet<T> {
|
||||
type Balance = SubstrateAmount;
|
||||
/// Burn `balance` with `OutInstructionWithBalance` from the caller.
|
||||
/// Errors if called for SRI or Instance1 instance of this pallet.
|
||||
#[pallet::call_index(2)]
|
||||
#[pallet::weight((0, DispatchClass::Normal))] // TODO
|
||||
pub fn burn_with_instruction(
|
||||
origin: OriginFor<T>,
|
||||
instruction: OutInstructionWithBalance,
|
||||
) -> DispatchResult {
|
||||
if instruction.balance.coin == Coin::Serai {
|
||||
Err(Error::<T, I>::BurnWithInstructionNotAllowed)?;
|
||||
}
|
||||
if TypeId::of::<I>() == TypeId::of::<LiquidityTokensInstance>() {
|
||||
Err(Error::<T, I>::BurnWithInstructionNotAllowed)?;
|
||||
}
|
||||
|
||||
fn balance(of: &Public) -> Self::Balance {
|
||||
Self::balance(*of, Coin::Serai).0
|
||||
}
|
||||
|
||||
/// TODO: make sure of coin precision here.
|
||||
fn minimum_balance() -> Self::Balance {
|
||||
1
|
||||
}
|
||||
|
||||
fn transfer(
|
||||
from: &Public,
|
||||
to: &Public,
|
||||
amount: Self::Balance,
|
||||
) -> Result<Self::Balance, DispatchError> {
|
||||
let balance = Balance { coin: Coin::Serai, amount: Amount(amount) };
|
||||
Self::transfer_internal(*from, *to, balance)?;
|
||||
Ok(amount)
|
||||
}
|
||||
|
||||
fn mint(to: &Public, amount: Self::Balance) -> Result<Self::Balance, DispatchError> {
|
||||
Self::mint(*to, Balance { coin: Coin::native(), amount: Amount(amount) })?;
|
||||
Ok(amount)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Have DEX implement for Coins, not Coins implement for Coins
|
||||
impl<T: Config> CoinsTrait<T::AccountId> for Pallet<T> {
|
||||
type Balance = SubstrateAmount;
|
||||
type CoinId = Coin;
|
||||
|
||||
// TODO: Swap the order of these arguments
|
||||
fn balance(coin: Self::CoinId, of: &Public) -> Self::Balance {
|
||||
Self::balance(*of, coin).0
|
||||
}
|
||||
|
||||
fn minimum_balance(_: Self::CoinId) -> Self::Balance {
|
||||
1
|
||||
}
|
||||
|
||||
// TODO: Move coin next to amount
|
||||
fn transfer(
|
||||
coin: Self::CoinId,
|
||||
from: &Public,
|
||||
to: &Public,
|
||||
amount: Self::Balance,
|
||||
) -> Result<Self::Balance, DispatchError> {
|
||||
let balance = Balance { coin, amount: Amount(amount) };
|
||||
Self::transfer_internal(*from, *to, balance)?;
|
||||
Ok(amount)
|
||||
}
|
||||
|
||||
// TODO: Move coin next to amount
|
||||
fn mint(
|
||||
coin: Self::CoinId,
|
||||
to: &Public,
|
||||
amount: Self::Balance,
|
||||
) -> Result<Self::Balance, DispatchError> {
|
||||
Self::mint(*to, Balance { coin, amount: Amount(amount) })?;
|
||||
Ok(amount)
|
||||
let from = ensure_signed(origin)?;
|
||||
Self::burn_internal(from, instruction.balance)?;
|
||||
Self::deposit_event(Event::BurnWithInstruction { from, instruction });
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,13 +26,9 @@ frame-system = { git = "https://github.com/serai-dex/substrate", default-feature
|
|||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
frame-benchmarking = { git = "https://github.com/serai-dex/substrate", default-features = false, optional = true }
|
||||
|
||||
dex-primitives = { package = "serai-dex-primitives", path = "../primitives", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
serai-primitives = { path = "../../primitives", default-features = false }
|
||||
|
||||
coins-pallet = { package = "serai-coins-pallet", path = "../../coins/pallet", default-features = false }
|
||||
liquidity-tokens-pallet = { package = "serai-liquidity-tokens-pallet", path = "../../liquidity-tokens/pallet", default-features = false }
|
||||
|
||||
serai-primitives = { path = "../../primitives", default-features = false }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
|
@ -49,14 +45,11 @@ std = [
|
|||
|
||||
"serai-primitives/std",
|
||||
|
||||
"dex-primitives/std",
|
||||
|
||||
"frame-system/std",
|
||||
"frame-support/std",
|
||||
"frame-benchmarking?/std",
|
||||
|
||||
"coins-pallet/std",
|
||||
"liquidity-tokens-pallet/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"sp-runtime/runtime-benchmarks",
|
||||
|
@ -64,8 +57,6 @@ runtime-benchmarks = [
|
|||
"frame-system/runtime-benchmarks",
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-benchmarking/runtime-benchmarks",
|
||||
|
||||
"dex-primitives/runtime-benchmarks",
|
||||
]
|
||||
try-runtime = [
|
||||
"sp-runtime/try-runtime",
|
||||
|
|
|
@ -24,247 +24,206 @@ use super::*;
|
|||
use frame_benchmarking::{benchmarks, whitelisted_caller};
|
||||
use frame_support::{assert_ok, storage::bounded_vec::BoundedVec};
|
||||
use frame_system::RawOrigin as SystemOrigin;
|
||||
use sp_runtime::traits::{Bounded, StaticLookup};
|
||||
|
||||
use sp_runtime::traits::StaticLookup;
|
||||
use sp_std::{ops::Div, prelude::*};
|
||||
|
||||
use serai_primitives::{Amount, Balance};
|
||||
|
||||
use crate::Pallet as Dex;
|
||||
use coins_pallet::Pallet as Coins;
|
||||
|
||||
const INITIAL_COIN_BALANCE: u64 = 1_000_000_000;
|
||||
type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
|
||||
type BalanceOf<T> =
|
||||
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
|
||||
|
||||
fn get_lp_token_id<T: Config>() -> T::PoolCoinId
|
||||
where
|
||||
T::PoolCoinId: Into<u32>,
|
||||
{
|
||||
let next_id: u32 = Dex::<T>::get_next_pool_coin_id().into();
|
||||
(next_id - 1).into()
|
||||
}
|
||||
type LiquidityTokens<T> = coins_pallet::Pallet<T, coins_pallet::Instance1>;
|
||||
|
||||
fn create_coin<T: Config>(coin: &T::MultiCoinId) -> (T::AccountId, AccountIdLookupOf<T>)
|
||||
where
|
||||
T::CoinBalance: From<u64>,
|
||||
T::Currency: Currency<T::AccountId>,
|
||||
T::Coins: Coins<T::AccountId>,
|
||||
{
|
||||
fn create_coin<T: Config>(coin: &Coin) -> (T::AccountId, AccountIdLookupOf<T>) {
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
let caller_lookup = T::Lookup::unlookup(caller.clone());
|
||||
if let MultiCoinIdConversionResult::Converted(coin_id) =
|
||||
T::MultiCoinIdConverter::try_convert(coin)
|
||||
{
|
||||
assert_ok!(T::Currency::mint(&caller, BalanceOf::<T>::max_value().div(1000u32.into())));
|
||||
assert_ok!(T::Coins::mint(coin_id, &caller, INITIAL_COIN_BALANCE.into()));
|
||||
}
|
||||
let caller_lookup = T::Lookup::unlookup(caller);
|
||||
assert_ok!(Coins::<T>::mint(
|
||||
caller,
|
||||
Balance { coin: Coin::native(), amount: Amount(SubstrateAmount::max_value().div(1000u64)) }
|
||||
));
|
||||
assert_ok!(Coins::<T>::mint(
|
||||
caller,
|
||||
Balance { coin: *coin, amount: Amount(INITIAL_COIN_BALANCE) }
|
||||
));
|
||||
(caller, caller_lookup)
|
||||
}
|
||||
|
||||
fn create_coin_and_pool<T: Config>(
|
||||
coin1: &T::MultiCoinId,
|
||||
coin2: &T::MultiCoinId,
|
||||
) -> (T::PoolCoinId, T::AccountId, AccountIdLookupOf<T>)
|
||||
where
|
||||
T::CoinBalance: From<u64>,
|
||||
T::Currency: Currency<T::AccountId>,
|
||||
T::Coins: Coins<T::AccountId>,
|
||||
T::PoolCoinId: Into<u32>,
|
||||
{
|
||||
assert_eq!(coin1, &T::MultiCoinIdConverter::get_native());
|
||||
coin: &Coin,
|
||||
) -> (PoolCoinId, T::AccountId, AccountIdLookupOf<T>) {
|
||||
let (caller, caller_lookup) = create_coin::<T>(coin);
|
||||
assert_ok!(Dex::<T>::create_pool(*coin));
|
||||
|
||||
let (caller, caller_lookup) = create_coin::<T>(coin2);
|
||||
|
||||
assert_ok!(Dex::<T>::create_pool(coin2.clone()));
|
||||
let lp_token = get_lp_token_id::<T>();
|
||||
|
||||
(lp_token, caller, caller_lookup)
|
||||
(*coin, caller, caller_lookup)
|
||||
}
|
||||
|
||||
benchmarks! {
|
||||
where_clause {
|
||||
where
|
||||
T::CoinBalance: From<u64> + Into<u64>,
|
||||
T::Currency: Currency<T::AccountId>,
|
||||
T::Balance: From<u64> + Into<u64>,
|
||||
T::Coins: Coins<T::AccountId>,
|
||||
T::PoolCoinId: Into<u32>,
|
||||
}
|
||||
|
||||
add_liquidity {
|
||||
let coin1 = T::MultiCoinIdConverter::get_native();
|
||||
let coin2: T::MultiCoinId = T::BenchmarkHelper::coin_id(0).into();
|
||||
let (lp_token, caller, _) = create_coin_and_pool::<T>(&coin1, &coin2);
|
||||
let ed: u64 = T::Currency::minimum_balance().into();
|
||||
let add_amount = 1000 + ed;
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Bitcoin;
|
||||
let (lp_token, caller, _) = create_coin_and_pool::<T>(&coin2);
|
||||
let add_amount: u64 = 1000;
|
||||
}: _(
|
||||
SystemOrigin::Signed(caller.clone()),
|
||||
coin1.clone(),
|
||||
coin2.clone(),
|
||||
add_amount.into(),
|
||||
1000.into(),
|
||||
0.into(),
|
||||
0.into(),
|
||||
caller.clone()
|
||||
SystemOrigin::Signed(caller),
|
||||
coin2,
|
||||
1000u64,
|
||||
add_amount,
|
||||
0u64,
|
||||
0u64,
|
||||
caller
|
||||
)
|
||||
verify {
|
||||
let pool_id = (coin1.clone(), coin2.clone());
|
||||
let pool_id = Dex::<T>::get_pool_id(coin1, coin2).unwrap();
|
||||
let lp_minted = Dex::<T>::calc_lp_amount_for_zero_supply(
|
||||
&add_amount.into(),
|
||||
&1000.into()
|
||||
).unwrap().into();
|
||||
add_amount,
|
||||
1000u64,
|
||||
).unwrap();
|
||||
assert_eq!(
|
||||
T::PoolCoins::balance(lp_token, &caller),
|
||||
lp_minted.into()
|
||||
LiquidityTokens::<T>::balance(caller, lp_token).0,
|
||||
lp_minted
|
||||
);
|
||||
assert_eq!(
|
||||
T::Currency::balance(&Dex::<T>::get_pool_account(&pool_id)),
|
||||
add_amount.into()
|
||||
Coins::<T>::balance(Dex::<T>::get_pool_account(pool_id), Coin::native()).0,
|
||||
add_amount
|
||||
);
|
||||
assert_eq!(
|
||||
T::Coins::balance(
|
||||
T::BenchmarkHelper::coin_id(0),
|
||||
&Dex::<T>::get_pool_account(&pool_id)
|
||||
),
|
||||
1000.into()
|
||||
Coins::<T>::balance(
|
||||
Dex::<T>::get_pool_account(pool_id),
|
||||
Coin::Bitcoin,
|
||||
).0,
|
||||
1000
|
||||
);
|
||||
}
|
||||
|
||||
remove_liquidity {
|
||||
let coin1 = T::MultiCoinIdConverter::get_native();
|
||||
let coin2: T::MultiCoinId = T::BenchmarkHelper::coin_id(0).into();
|
||||
let (lp_token, caller, _) = create_coin_and_pool::<T>(&coin1, &coin2);
|
||||
let ed: u64 = T::Currency::minimum_balance().into();
|
||||
let add_amount = 100 * ed;
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Monero;
|
||||
let (lp_token, caller, _) = create_coin_and_pool::<T>(&coin2);
|
||||
let add_amount: u64 = 100;
|
||||
let lp_minted = Dex::<T>::calc_lp_amount_for_zero_supply(
|
||||
&add_amount.into(),
|
||||
&1000.into()
|
||||
).unwrap().into();
|
||||
let remove_lp_amount = lp_minted.checked_div(10).unwrap();
|
||||
add_amount,
|
||||
1000u64
|
||||
).unwrap();
|
||||
let remove_lp_amount: u64 = lp_minted.checked_div(10).unwrap();
|
||||
|
||||
Dex::<T>::add_liquidity(
|
||||
SystemOrigin::Signed(caller.clone()).into(),
|
||||
coin1.clone(),
|
||||
coin2.clone(),
|
||||
add_amount.into(),
|
||||
1000.into(),
|
||||
0.into(),
|
||||
0.into(),
|
||||
caller.clone(),
|
||||
SystemOrigin::Signed(caller).into(),
|
||||
coin2,
|
||||
1000u64,
|
||||
add_amount,
|
||||
0u64,
|
||||
0u64,
|
||||
caller,
|
||||
)?;
|
||||
let total_supply =
|
||||
<T::PoolCoins as LiquidityTokens<T::AccountId>>::total_issuance(lp_token.clone());
|
||||
let total_supply = LiquidityTokens::<T>::supply(lp_token);
|
||||
}: _(
|
||||
SystemOrigin::Signed(caller.clone()),
|
||||
coin1,
|
||||
SystemOrigin::Signed(caller),
|
||||
coin2,
|
||||
remove_lp_amount.into(),
|
||||
0.into(),
|
||||
0.into(),
|
||||
caller.clone()
|
||||
remove_lp_amount,
|
||||
0u64,
|
||||
0u64,
|
||||
caller
|
||||
)
|
||||
verify {
|
||||
let new_total_supply =
|
||||
<T::PoolCoins as LiquidityTokens<T::AccountId>>::total_issuance(lp_token.clone());
|
||||
let new_total_supply = LiquidityTokens::<T>::supply(lp_token);
|
||||
assert_eq!(
|
||||
new_total_supply,
|
||||
total_supply - remove_lp_amount.into()
|
||||
total_supply - remove_lp_amount
|
||||
);
|
||||
}
|
||||
|
||||
swap_exact_tokens_for_tokens {
|
||||
let native = T::MultiCoinIdConverter::get_native();
|
||||
let coin1: T::MultiCoinId = T::BenchmarkHelper::coin_id(1).into();
|
||||
let coin2: T::MultiCoinId = T::BenchmarkHelper::coin_id(2).into();
|
||||
let (_, caller, _) = create_coin_and_pool::<T>(&native, &coin1);
|
||||
let native = Coin::native();
|
||||
let coin1 = Coin::Bitcoin;
|
||||
let coin2 = Coin::Ether;
|
||||
let (_, caller, _) = create_coin_and_pool::<T>(&coin1);
|
||||
let (_, _) = create_coin::<T>(&coin2);
|
||||
let ed: u64 = T::Currency::minimum_balance().into();
|
||||
let ed_bump = 2u64;
|
||||
|
||||
Dex::<T>::add_liquidity(
|
||||
SystemOrigin::Signed(caller.clone()).into(),
|
||||
native.clone(),
|
||||
coin1.clone(),
|
||||
// TODO: this call otherwise fails with `InsufficientLiquidityMinted`.
|
||||
// might be again related to their expectance on ed being > 1.
|
||||
(100 * (ed + ed_bump)).into(),
|
||||
200.into(),
|
||||
0.into(),
|
||||
0.into(),
|
||||
caller.clone(),
|
||||
SystemOrigin::Signed(caller).into(),
|
||||
coin1,
|
||||
200u64,
|
||||
// TODO: this call otherwise fails with `InsufficientLiquidityMinted` if we don't multiply
|
||||
// with 3. Might be again related to their expectance on ed being > 1.
|
||||
100 * 3,
|
||||
0u64,
|
||||
0u64,
|
||||
caller,
|
||||
)?;
|
||||
|
||||
let swap_amount = 100.into();
|
||||
let swap_amount = 100u64;
|
||||
|
||||
// since we only allow the native-coin pools, then the worst case scenario would be to swap
|
||||
// coin1-native-coin2
|
||||
Dex::<T>::create_pool(coin2.clone())?;
|
||||
Dex::<T>::create_pool(coin2)?;
|
||||
Dex::<T>::add_liquidity(
|
||||
SystemOrigin::Signed(caller.clone()).into(),
|
||||
native.clone(),
|
||||
coin2.clone(),
|
||||
(500 * ed).into(),
|
||||
1000.into(),
|
||||
0.into(),
|
||||
0.into(),
|
||||
caller.clone(),
|
||||
SystemOrigin::Signed(caller).into(),
|
||||
coin2,
|
||||
1000u64,
|
||||
500,
|
||||
0u64,
|
||||
0u64,
|
||||
caller,
|
||||
)?;
|
||||
|
||||
let path = vec![coin1.clone(), native.clone(), coin2.clone()];
|
||||
let path = vec![coin1, native, coin2];
|
||||
let path = BoundedVec::<_, T::MaxSwapPathLength>::try_from(path).unwrap();
|
||||
let native_balance = T::Currency::balance(&caller);
|
||||
let coin1_balance = T::Coins::balance(T::BenchmarkHelper::coin_id(1), &caller);
|
||||
}: _(SystemOrigin::Signed(caller.clone()), path, swap_amount, 1.into(), caller.clone())
|
||||
let native_balance = Coins::<T>::balance(caller, native).0;
|
||||
let coin1_balance = Coins::<T>::balance(caller, Coin::Bitcoin).0;
|
||||
}: _(SystemOrigin::Signed(caller), path, swap_amount, 1u64, caller)
|
||||
verify {
|
||||
let ed_bump = 2u64;
|
||||
let new_coin1_balance = T::Coins::balance(T::BenchmarkHelper::coin_id(1), &caller);
|
||||
assert_eq!(new_coin1_balance, coin1_balance - 100.into());
|
||||
let new_coin1_balance = Coins::<T>::balance(caller, Coin::Bitcoin).0;
|
||||
assert_eq!(new_coin1_balance, coin1_balance - 100u64);
|
||||
}
|
||||
|
||||
swap_tokens_for_exact_tokens {
|
||||
let native = T::MultiCoinIdConverter::get_native();
|
||||
let coin1: T::MultiCoinId = T::BenchmarkHelper::coin_id(1).into();
|
||||
let coin2: T::MultiCoinId = T::BenchmarkHelper::coin_id(2).into();
|
||||
let (_, caller, _) = create_coin_and_pool::<T>(&native, &coin1);
|
||||
let native = Coin::native();
|
||||
let coin1 = Coin::Bitcoin;
|
||||
let coin2 = Coin::Ether;
|
||||
let (_, caller, _) = create_coin_and_pool::<T>(&coin1);
|
||||
let (_, _) = create_coin::<T>(&coin2);
|
||||
let ed: u64 = T::Currency::minimum_balance().into();
|
||||
|
||||
Dex::<T>::add_liquidity(
|
||||
SystemOrigin::Signed(caller.clone()).into(),
|
||||
native.clone(),
|
||||
coin1.clone(),
|
||||
(1000 * ed).into(),
|
||||
500.into(),
|
||||
0.into(),
|
||||
0.into(),
|
||||
caller.clone(),
|
||||
SystemOrigin::Signed(caller).into(),
|
||||
coin1,
|
||||
500u64,
|
||||
1000,
|
||||
0u64,
|
||||
0u64,
|
||||
caller,
|
||||
)?;
|
||||
|
||||
// since we only allow the native-coin pools, then the worst case scenario would be to swap
|
||||
// coin1-native-coin2
|
||||
Dex::<T>::create_pool(coin2.clone())?;
|
||||
Dex::<T>::create_pool(coin2)?;
|
||||
Dex::<T>::add_liquidity(
|
||||
SystemOrigin::Signed(caller.clone()).into(),
|
||||
native.clone(),
|
||||
coin2.clone(),
|
||||
(500 * ed).into(),
|
||||
1000.into(),
|
||||
0.into(),
|
||||
0.into(),
|
||||
caller.clone(),
|
||||
SystemOrigin::Signed(caller).into(),
|
||||
coin2,
|
||||
1000u64,
|
||||
500,
|
||||
0u64,
|
||||
0u64,
|
||||
caller,
|
||||
)?;
|
||||
let path = vec![coin1.clone(), native.clone(), coin2.clone()];
|
||||
let path = vec![coin1, native, coin2];
|
||||
|
||||
let path: BoundedVec<_, T::MaxSwapPathLength> = BoundedVec::try_from(path).unwrap();
|
||||
let coin2_balance = T::Coins::balance(T::BenchmarkHelper::coin_id(2), &caller);
|
||||
let coin2_balance = Coins::<T>::balance(caller, Coin::Ether).0;
|
||||
}: _(
|
||||
SystemOrigin::Signed(caller.clone()),
|
||||
SystemOrigin::Signed(caller),
|
||||
path.clone(),
|
||||
100.into(),
|
||||
(1000 * ed).into(),
|
||||
caller.clone()
|
||||
100u64,
|
||||
1000,
|
||||
caller
|
||||
)
|
||||
verify {
|
||||
let new_coin2_balance = T::Coins::balance(T::BenchmarkHelper::coin_id(2), &caller);
|
||||
assert_eq!(new_coin2_balance, coin2_balance + 100.into());
|
||||
let new_coin2_balance = Coins::<T>::balance(caller, Coin::Ether).0;
|
||||
assert_eq!(new_coin2_balance, coin2_balance + 100u64);
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite!(Dex, crate::mock::new_test_ext(), crate::mock::Test);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -37,7 +37,6 @@ use sp_runtime::{
|
|||
use serai_primitives::{Coin, Balance, Amount, system_address};
|
||||
|
||||
pub use coins_pallet as coins;
|
||||
pub use liquidity_tokens_pallet as liquidity_tokens;
|
||||
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
|
||||
|
@ -46,7 +45,7 @@ construct_runtime!(
|
|||
{
|
||||
System: frame_system,
|
||||
CoinsPallet: coins,
|
||||
LiquidityTokens: liquidity_tokens,
|
||||
LiquidityTokens: coins::<Instance1>::{Pallet, Call, Storage, Event<T>},
|
||||
Dex: dex,
|
||||
}
|
||||
);
|
||||
|
@ -81,54 +80,18 @@ impl coins::Config for Test {
|
|||
type RuntimeEvent = RuntimeEvent;
|
||||
}
|
||||
|
||||
impl liquidity_tokens::Config for Test {
|
||||
impl coins::Config<coins::Instance1> for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
}
|
||||
|
||||
pub struct CoinConverter;
|
||||
impl MultiCoinIdConverter<Coin, Coin> for CoinConverter {
|
||||
/// Returns the MultiCoinId representing the native currency of the chain.
|
||||
fn get_native() -> Coin {
|
||||
Coin::Serai
|
||||
}
|
||||
|
||||
/// Returns true if the given MultiCoinId is the native currency.
|
||||
fn is_native(coin: &Coin) -> bool {
|
||||
coin.is_native()
|
||||
}
|
||||
|
||||
/// If it's not native, returns the CoinId for the given MultiCoinId.
|
||||
fn try_convert(coin: &Coin) -> MultiCoinIdConversionResult<Coin, Coin> {
|
||||
if coin.is_native() {
|
||||
MultiCoinIdConversionResult::Native
|
||||
} else {
|
||||
MultiCoinIdConversionResult::Converted(*coin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type Currency = CoinsPallet;
|
||||
type CoinBalance = u64;
|
||||
type CoinId = Coin;
|
||||
type PoolCoinId = u32;
|
||||
type Coins = CoinsPallet;
|
||||
type PoolCoins = LiquidityTokens;
|
||||
|
||||
type WeightInfo = ();
|
||||
type LPFee = ConstU32<3>; // means 0.3%
|
||||
type MaxSwapPathLength = ConstU32<4>;
|
||||
// 100 is good enough when the main currency has 12 decimals.
|
||||
type MintMinLiquidity = ConstU64<100>;
|
||||
|
||||
type Balance = u64;
|
||||
type HigherPrecisionBalance = u128;
|
||||
|
||||
type MultiCoinId = Coin;
|
||||
type MultiCoinIdConverter = CoinConverter;
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type BenchmarkHelper = ();
|
||||
}
|
||||
|
||||
pub(crate) fn new_test_ext() -> sp_io::TestExternalities {
|
||||
|
@ -145,6 +108,7 @@ pub(crate) fn new_test_ext() -> sp_io::TestExternalities {
|
|||
.into_iter()
|
||||
.map(|a| (a, Balance { coin: Coin::Serai, amount: Amount(1 << 60) }))
|
||||
.collect(),
|
||||
_ignore: Default::default(),
|
||||
}
|
||||
.assimilate_storage(&mut t)
|
||||
.unwrap();
|
||||
|
|
File diff suppressed because it is too large
Load diff
64
substrate/dex/pallet/src/types.rs
Normal file
64
substrate/dex/pallet/src/types.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
// This file was originally:
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// It has been forked into a crate distributed under the AGPL 3.0.
|
||||
// Please check the current distribution for up-to-date copyright and licensing information.
|
||||
|
||||
use super::*;
|
||||
|
||||
use codec::{Decode, Encode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
/// Stores the lp_token coin id a particular pool has been assigned.
|
||||
#[derive(Decode, Encode, Default, PartialEq, Eq, MaxEncodedLen, TypeInfo)]
|
||||
pub struct PoolInfo<PoolCoinId> {
|
||||
/// Liquidity pool coin
|
||||
pub lp_token: PoolCoinId,
|
||||
}
|
||||
|
||||
/// Trait for providing methods to swap between the various coin classes.
|
||||
pub trait Swap<AccountId, Balance, MultiCoinId> {
|
||||
/// Swap exactly `amount_in` of coin `path[0]` for coin `path[1]`.
|
||||
/// If an `amount_out_min` is specified, it will return an error if it is unable to acquire
|
||||
/// the amount desired.
|
||||
///
|
||||
/// Withdraws the `path[0]` coin from `sender`, deposits the `path[1]` coin to `send_to`,
|
||||
///
|
||||
/// If successful, returns the amount of `path[1]` acquired for the `amount_in`.
|
||||
fn swap_exact_tokens_for_tokens(
|
||||
sender: AccountId,
|
||||
path: Vec<MultiCoinId>,
|
||||
amount_in: Balance,
|
||||
amount_out_min: Option<Balance>,
|
||||
send_to: AccountId,
|
||||
) -> Result<Balance, DispatchError>;
|
||||
|
||||
/// Take the `path[0]` coin and swap some amount for `amount_out` of the `path[1]`. If an
|
||||
/// `amount_in_max` is specified, it will return an error if acquiring `amount_out` would be
|
||||
/// too costly.
|
||||
///
|
||||
/// Withdraws `path[0]` coin from `sender`, deposits `path[1]` coin to `send_to`,
|
||||
///
|
||||
/// If successful returns the amount of the `path[0]` taken to provide `path[1]`.
|
||||
fn swap_tokens_for_exact_tokens(
|
||||
sender: AccountId,
|
||||
path: Vec<MultiCoinId>,
|
||||
amount_out: Balance,
|
||||
amount_in_max: Option<Balance>,
|
||||
send_to: AccountId,
|
||||
) -> Result<Balance, DispatchError>;
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
// It has been forked into a crate distributed under the AGPL 3.0.
|
||||
// Please check the current distribution for up-to-date copyright and licensing information.
|
||||
|
||||
//! Autogenerated weights for pallet_coin_conversion
|
||||
//! Autogenerated weights for Dex Pallet.
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
|
||||
//! DATE: 2023-07-18, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
||||
|
@ -36,10 +36,10 @@
|
|||
// --wasm-execution=compiled
|
||||
// --heap-pages=4096
|
||||
// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json
|
||||
// --pallet=pallet_coin_conversion
|
||||
// --pallet=serai_dex_pallet
|
||||
// --chain=dev
|
||||
// --header=./HEADER-APACHE2
|
||||
// --output=./frame/coin-conversion/src/weights.rs
|
||||
// --output=./substrate/dex/pallet/src/weights.rs
|
||||
// --template=./.maintain/frame-weight-template.hbs
|
||||
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
|
@ -50,7 +50,7 @@
|
|||
use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// Weight functions needed for pallet_coin_conversion.
|
||||
/// Weight functions needed for Dex Pallet.
|
||||
pub trait WeightInfo {
|
||||
fn create_pool() -> Weight;
|
||||
fn add_liquidity() -> Weight;
|
||||
|
@ -59,19 +59,19 @@ pub trait WeightInfo {
|
|||
fn swap_tokens_for_exact_tokens() -> Weight;
|
||||
}
|
||||
|
||||
/// Weights for pallet_coin_conversion using the Substrate node and recommended hardware.
|
||||
/// Weights for Dex Pallet using the Substrate node and recommended hardware.
|
||||
pub struct SubstrateWeight<T>(PhantomData<T>);
|
||||
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
||||
/// Storage: `CoinConversion::Pools` (r:1 w:1)
|
||||
/// Proof: `CoinConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
|
||||
/// Storage: `DexPallet::Pools` (r:1 w:1)
|
||||
/// Proof: `DexPallet::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Account` (r:2 w:2)
|
||||
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Coins::Account` (r:1 w:1)
|
||||
/// Proof: `Coins::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Coins::Coin` (r:1 w:1)
|
||||
/// Proof: `Coins::Coin` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
|
||||
/// Storage: `CoinConversion::NextPoolCoinId` (r:1 w:1)
|
||||
/// Proof: `CoinConversion::NextPoolCoinId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||
/// Storage: `DexPallet::NextPoolCoinId` (r:1 w:1)
|
||||
/// Proof: `DexPallet::NextPoolCoinId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||
/// Storage: `PoolCoins::Coin` (r:1 w:1)
|
||||
/// Proof: `PoolCoins::Coin` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
|
||||
/// Storage: `PoolCoins::Account` (r:1 w:1)
|
||||
|
@ -85,8 +85,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
|||
.saturating_add(T::DbWeight::get().reads(8_u64))
|
||||
.saturating_add(T::DbWeight::get().writes(8_u64))
|
||||
}
|
||||
/// Storage: `CoinConversion::Pools` (r:1 w:0)
|
||||
/// Proof: `CoinConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
|
||||
/// Storage: `DexPallet::Pools` (r:1 w:0)
|
||||
/// Proof: `DexPallet::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Account` (r:1 w:1)
|
||||
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Coins::Coin` (r:1 w:1)
|
||||
|
@ -106,8 +106,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
|||
.saturating_add(T::DbWeight::get().reads(8_u64))
|
||||
.saturating_add(T::DbWeight::get().writes(7_u64))
|
||||
}
|
||||
/// Storage: `CoinConversion::Pools` (r:1 w:0)
|
||||
/// Proof: `CoinConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
|
||||
/// Storage: `DexPallet::Pools` (r:1 w:0)
|
||||
/// Proof: `DexPallet::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Account` (r:1 w:1)
|
||||
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Coins::Coin` (r:1 w:1)
|
||||
|
@ -161,16 +161,16 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
|||
|
||||
// For backwards compatibility and tests.
|
||||
impl WeightInfo for () {
|
||||
/// Storage: `CoinConversion::Pools` (r:1 w:1)
|
||||
/// Proof: `CoinConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
|
||||
/// Storage: `DexPallet::Pools` (r:1 w:1)
|
||||
/// Proof: `DexPallet::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Account` (r:2 w:2)
|
||||
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Coins::Account` (r:1 w:1)
|
||||
/// Proof: `Coins::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Coins::Coin` (r:1 w:1)
|
||||
/// Proof: `Coins::Coin` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
|
||||
/// Storage: `CoinConversion::NextPoolCoinId` (r:1 w:1)
|
||||
/// Proof: `CoinConversion::NextPoolCoinId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||
/// Storage: `DexPallet::NextPoolCoinId` (r:1 w:1)
|
||||
/// Proof: `DexPallet::NextPoolCoinId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||
/// Storage: `PoolCoins::Coin` (r:1 w:1)
|
||||
/// Proof: `PoolCoins::Coin` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
|
||||
/// Storage: `PoolCoins::Account` (r:1 w:1)
|
||||
|
@ -184,8 +184,8 @@ impl WeightInfo for () {
|
|||
.saturating_add(RocksDbWeight::get().reads(8_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(8_u64))
|
||||
}
|
||||
/// Storage: `CoinConversion::Pools` (r:1 w:0)
|
||||
/// Proof: `CoinConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
|
||||
/// Storage: `DexPallet::Pools` (r:1 w:0)
|
||||
/// Proof: `DexPallet::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Account` (r:1 w:1)
|
||||
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Coins::Coin` (r:1 w:1)
|
||||
|
@ -205,8 +205,8 @@ impl WeightInfo for () {
|
|||
.saturating_add(RocksDbWeight::get().reads(8_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(7_u64))
|
||||
}
|
||||
/// Storage: `CoinConversion::Pools` (r:1 w:0)
|
||||
/// Proof: `CoinConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
|
||||
/// Storage: `DexPallet::Pools` (r:1 w:0)
|
||||
/// Proof: `DexPallet::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Account` (r:1 w:1)
|
||||
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Coins::Coin` (r:1 w:1)
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
[package]
|
||||
name = "serai-dex-primitives"
|
||||
version = "0.1.0"
|
||||
description = "Dex pallet primitives"
|
||||
license = "AGPL-3.0-only"
|
||||
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/dex/primitives"
|
||||
authors = ["Parity Technologies <admin@parity.io>, Akil Demir <aeg_asd@hotmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false }
|
||||
scale-info = { version = "2.5.0", default-features = false, features = ["derive"] }
|
||||
|
||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
frame-benchmarking = { git = "https://github.com/serai-dex/substrate", default-features = false, optional = true }
|
||||
|
||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
|
||||
serai-primitives = { path = "../../primitives", default-features = false }
|
||||
|
||||
[features]
|
||||
default = [ "std" ]
|
||||
std = [
|
||||
"codec/std",
|
||||
"scale-info/std",
|
||||
|
||||
"frame-support/std",
|
||||
"frame-benchmarking?/std",
|
||||
|
||||
"serai-primitives/std",
|
||||
|
||||
"sp-runtime/std",
|
||||
"sp-std/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"frame-benchmarking/runtime-benchmarks",
|
||||
"frame-support/runtime-benchmarks",
|
||||
]
|
|
@ -1,15 +0,0 @@
|
|||
AGPL-3.0-only license
|
||||
|
||||
Copyright (c) 2023 Luke Parker
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License Version 3 as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
@ -1,211 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
NOTE
|
||||
|
||||
Individual files contain the following tag instead of the full license
|
||||
text.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
This enables machine processing of license information based on the SPDX
|
||||
License Identifiers that are here available: http://spdx.org/licenses/
|
|
@ -1,221 +0,0 @@
|
|||
// This file was originally:
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// It has been forked into a crate distributed under the AGPL 3.0.
|
||||
// Please check the current distribution for up-to-date copyright and licensing information.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use codec::{Decode, Encode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
use sp_runtime::DispatchError;
|
||||
use sp_std::vec::Vec;
|
||||
|
||||
use frame_support::traits::tokens::{Balance, AssetId as CoinId};
|
||||
|
||||
use serai_primitives::Coin;
|
||||
|
||||
/// Stores the lp_token coin id a particular pool has been assigned.
|
||||
#[derive(Decode, Encode, Default, PartialEq, Eq, MaxEncodedLen, TypeInfo)]
|
||||
pub struct PoolInfo<PoolCoinId> {
|
||||
/// Liquidity pool coin
|
||||
pub lp_token: PoolCoinId,
|
||||
}
|
||||
|
||||
/// A trait that converts between a MultiCoinId and either the native currency or an CoinId.
|
||||
pub trait MultiCoinIdConverter<MultiCoinId, CoinId> {
|
||||
/// Returns the MultiCoinId representing the native currency of the chain.
|
||||
fn get_native() -> MultiCoinId;
|
||||
|
||||
/// Returns true if the given MultiCoinId is the native currency.
|
||||
fn is_native(coin: &MultiCoinId) -> bool;
|
||||
|
||||
/// If it's not native, returns the CoinId for the given MultiCoinId.
|
||||
fn try_convert(coin: &MultiCoinId) -> MultiCoinIdConversionResult<MultiCoinId, CoinId>;
|
||||
}
|
||||
|
||||
/// Result of `MultiCoinIdConverter::try_convert`.
|
||||
#[cfg_attr(feature = "std", derive(PartialEq, Debug))]
|
||||
pub enum MultiCoinIdConversionResult<MultiCoinId, CoinId> {
|
||||
/// Input coin is successfully converted. Means that converted coin is supported.
|
||||
Converted(CoinId),
|
||||
/// Means that input coin is the chain's native coin, if it has one, so no conversion (see
|
||||
/// `MultiCoinIdConverter::get_native`).
|
||||
Native,
|
||||
/// Means input coin is not supported for pool.
|
||||
Unsupported(MultiCoinId),
|
||||
}
|
||||
|
||||
/// Benchmark Helper
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
pub trait BenchmarkHelper<CoinId, MultiCoinId> {
|
||||
/// Returns an `CoinId` from a given integer.
|
||||
fn coin_id(coin_id: u32) -> CoinId;
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
mod runtime_benchmarks {
|
||||
use super::*;
|
||||
use serai_primitives::COINS;
|
||||
impl BenchmarkHelper<Coin, Coin> for () {
|
||||
fn coin_id(coin_id: u32) -> Coin {
|
||||
// we shift id 1 unit to the left, since id 0 is the native coin.
|
||||
COINS[(usize::try_from(coin_id).unwrap() % COINS.len()) + 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for providing methods to swap between the various coin classes.
|
||||
pub trait Swap<AccountId, Balance, MultiCoinId> {
|
||||
/// Swap exactly `amount_in` of coin `path[0]` for coin `path[1]`.
|
||||
/// If an `amount_out_min` is specified, it will return an error if it is unable to acquire
|
||||
/// the amount desired.
|
||||
///
|
||||
/// Withdraws the `path[0]` coin from `sender`, deposits the `path[1]` coin to `send_to`,
|
||||
///
|
||||
/// If successful, returns the amount of `path[1]` acquired for the `amount_in`.
|
||||
fn swap_exact_tokens_for_tokens(
|
||||
sender: AccountId,
|
||||
path: Vec<MultiCoinId>,
|
||||
amount_in: Balance,
|
||||
amount_out_min: Option<Balance>,
|
||||
send_to: AccountId,
|
||||
) -> Result<Balance, DispatchError>;
|
||||
|
||||
/// Take the `path[0]` coin and swap some amount for `amount_out` of the `path[1]`. If an
|
||||
/// `amount_in_max` is specified, it will return an error if acquiring `amount_out` would be
|
||||
/// too costly.
|
||||
///
|
||||
/// Withdraws `path[0]` coin from `sender`, deposits `path[1]` coin to `send_to`,
|
||||
///
|
||||
/// If successful returns the amount of the `path[0]` taken to provide `path[1]`.
|
||||
fn swap_tokens_for_exact_tokens(
|
||||
sender: AccountId,
|
||||
path: Vec<MultiCoinId>,
|
||||
amount_out: Balance,
|
||||
amount_in_max: Option<Balance>,
|
||||
send_to: AccountId,
|
||||
) -> Result<Balance, DispatchError>;
|
||||
}
|
||||
|
||||
// TODO: Sized should be there?
|
||||
/// Native coin trait for Dex pallet.
|
||||
pub trait Currency<AccountId>: Sized {
|
||||
/// Balance of an Account.
|
||||
type Balance: Balance;
|
||||
|
||||
/// Returns the balance of an account.
|
||||
fn balance(of: &AccountId) -> Self::Balance;
|
||||
|
||||
/// Returns the minimum allowed balance of an account
|
||||
fn minimum_balance() -> Self::Balance;
|
||||
|
||||
/// Transfers the given `amount` from `from` to `to`.
|
||||
fn transfer(
|
||||
from: &AccountId,
|
||||
to: &AccountId,
|
||||
amount: Self::Balance,
|
||||
) -> Result<Self::Balance, DispatchError>;
|
||||
|
||||
/// mints the given `amount` into `to`.
|
||||
fn mint(to: &AccountId, amount: Self::Balance) -> Result<Self::Balance, DispatchError>;
|
||||
}
|
||||
|
||||
/// External coin trait for Dex pallet.
|
||||
pub trait Coins<AccountId>: Sized {
|
||||
/// Balance of an Account.
|
||||
type Balance: Balance;
|
||||
|
||||
/// Coin identifier.
|
||||
type CoinId: CoinId;
|
||||
|
||||
/// Returns the balance of an account.
|
||||
fn balance(coin: Self::CoinId, of: &AccountId) -> Self::Balance;
|
||||
|
||||
/// Returns the minimum allowed balance of an account
|
||||
fn minimum_balance(coin: Self::CoinId) -> Self::Balance;
|
||||
|
||||
/// Transfers the given `amount` from `from` to `to`.
|
||||
fn transfer(
|
||||
coin: Self::CoinId,
|
||||
from: &AccountId,
|
||||
to: &AccountId,
|
||||
amount: Self::Balance,
|
||||
) -> Result<Self::Balance, DispatchError>;
|
||||
|
||||
/// mints the given `amount` of `coin` into `to`.
|
||||
fn mint(
|
||||
coin: Self::CoinId,
|
||||
to: &AccountId,
|
||||
amount: Self::Balance,
|
||||
) -> Result<Self::Balance, DispatchError>;
|
||||
}
|
||||
|
||||
/// Liquidity tokens trait for Dex pallet.
|
||||
pub trait LiquidityTokens<AccountId>: Sized {
|
||||
/// Amount type.
|
||||
type Balance: Balance;
|
||||
|
||||
/// Coin identifier.
|
||||
type CoinId: CoinId;
|
||||
|
||||
/// Returns the `token` balance of and account.
|
||||
fn balance(token: Self::CoinId, of: &AccountId) -> Self::Balance;
|
||||
|
||||
/// Mints `amount` to `to`.
|
||||
fn mint_into(
|
||||
token: Self::CoinId,
|
||||
to: &AccountId,
|
||||
amount: Self::Balance,
|
||||
) -> Result<Self::Balance, DispatchError>;
|
||||
|
||||
/// Burns `amount` from `from`.
|
||||
fn burn_from(
|
||||
token: Self::CoinId,
|
||||
from: &AccountId,
|
||||
amount: Self::Balance,
|
||||
) -> Result<Self::Balance, DispatchError>;
|
||||
|
||||
/// Returns total supply for `token`.
|
||||
fn total_issuance(token: Self::CoinId) -> Self::Balance;
|
||||
|
||||
/// Returns an iterator of the collections in existence.
|
||||
fn coin_ids() -> Vec<Self::CoinId>;
|
||||
}
|
||||
|
||||
pub struct CoinConverter;
|
||||
impl MultiCoinIdConverter<Coin, Coin> for CoinConverter {
|
||||
/// Returns the MultiCoinId representing the native currency of the chain.
|
||||
fn get_native() -> Coin {
|
||||
Coin::native()
|
||||
}
|
||||
|
||||
/// Returns true if the given MultiCoinId is the native currency.
|
||||
fn is_native(coin: &Coin) -> bool {
|
||||
coin.is_native()
|
||||
}
|
||||
|
||||
/// If it's not native, returns the CoinId for the given MultiCoinId.
|
||||
fn try_convert(coin: &Coin) -> MultiCoinIdConversionResult<Coin, Coin> {
|
||||
if coin.is_native() {
|
||||
MultiCoinIdConversionResult::Native
|
||||
} else {
|
||||
MultiCoinIdConversionResult::Converted(*coin)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@ pub mod pallet {
|
|||
use sp_runtime::traits::Zero;
|
||||
use sp_core::sr25519::Public;
|
||||
|
||||
use serai_primitives::{Coin, SubstrateAmount, Amount, Balance};
|
||||
use serai_primitives::{Coin, Amount, Balance};
|
||||
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::{pallet_prelude::*, RawOrigin};
|
||||
|
@ -46,12 +46,7 @@ pub mod pallet {
|
|||
use super::*;
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config:
|
||||
frame_system::Config
|
||||
+ CoinsConfig
|
||||
+ DexConfig<MultiCoinId = Coin, CoinBalance = SubstrateAmount>
|
||||
+ ValidatorSetsConfig
|
||||
{
|
||||
pub trait Config: frame_system::Config + CoinsConfig + DexConfig + ValidatorSetsConfig {
|
||||
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||
}
|
||||
|
||||
|
@ -127,7 +122,6 @@ pub mod pallet {
|
|||
Dex::<T>::add_liquidity(
|
||||
origin.clone().into(),
|
||||
coin,
|
||||
Coin::Serai,
|
||||
half,
|
||||
sri_amount,
|
||||
1,
|
||||
|
@ -185,7 +179,7 @@ pub mod pallet {
|
|||
// do the swap
|
||||
let origin = RawOrigin::Signed(IN_INSTRUCTION_EXECUTOR.into());
|
||||
Dex::<T>::swap_exact_tokens_for_tokens(
|
||||
origin.into(),
|
||||
origin.clone().into(),
|
||||
BoundedVec::try_from(path).unwrap(),
|
||||
instruction.balance.amount.0,
|
||||
out_balance.amount.0,
|
||||
|
@ -206,7 +200,7 @@ pub mod pallet {
|
|||
},
|
||||
balance: Balance { coin: out_balance.coin, amount: coin_balance },
|
||||
};
|
||||
Coins::<T>::burn_non_sri(IN_INSTRUCTION_EXECUTOR.into(), instruction)?;
|
||||
Coins::<T>::burn_with_instruction(origin.into(), instruction)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
[package]
|
||||
name = "serai-liquidity-tokens-pallet"
|
||||
version = "0.1.0"
|
||||
description = "liquidity tokens pallet for Serai"
|
||||
license = "AGPL-3.0-only"
|
||||
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/liquidity-tokens/pallet"
|
||||
authors = ["Akil Demir <aeg_asd@hotmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
parity-scale-codec = { version = "3", default-features = false, features = ["derive"] }
|
||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||
|
||||
frame-system = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
|
||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
|
||||
dex-primitives = { package = "serai-dex-primitives", path = "../../dex/primitives", default-features = false }
|
||||
|
||||
serai-primitives = { path = "../../primitives", default-features = false }
|
||||
|
||||
[features]
|
||||
std = [
|
||||
"frame-system/std",
|
||||
"frame-support/std",
|
||||
|
||||
"sp-core/std",
|
||||
"sp-std/std",
|
||||
|
||||
"dex-primitives/std",
|
||||
|
||||
"serai-primitives/std",
|
||||
]
|
||||
|
||||
runtime-benchmarks = [
|
||||
"frame-system/runtime-benchmarks",
|
||||
"frame-support/runtime-benchmarks",
|
||||
]
|
||||
|
||||
default = ["std"]
|
|
@ -1,15 +0,0 @@
|
|||
AGPL-3.0-only license
|
||||
|
||||
Copyright (c) 2023 Luke Parker
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License Version 3 as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
@ -1,152 +0,0 @@
|
|||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use sp_core::sr25519::Public;
|
||||
use sp_std::vec::Vec;
|
||||
use frame_support::pallet_prelude::*;
|
||||
|
||||
use dex_primitives::LiquidityTokens;
|
||||
use serai_primitives::*;
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config<AccountId = Public> {
|
||||
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||
}
|
||||
|
||||
#[pallet::error]
|
||||
pub enum Error<T> {
|
||||
AmountOverflowed,
|
||||
NotEnoughCoins,
|
||||
}
|
||||
|
||||
#[pallet::event]
|
||||
#[pallet::generate_deposit(fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
LtMint { to: Public, token: u32, amount: Amount },
|
||||
LtBurn { from: Public, token: u32, amount: Amount },
|
||||
}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(PhantomData<T>);
|
||||
|
||||
/// The amount of coins each account has.
|
||||
// Identity is used as the second key's hasher due to it being a non-manipulatable fixed-space
|
||||
// ID.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn balances)]
|
||||
pub type Balances<T: Config> = StorageDoubleMap<
|
||||
_,
|
||||
Blake2_128Concat,
|
||||
Public,
|
||||
Blake2_128Concat,
|
||||
u32,
|
||||
SubstrateAmount,
|
||||
OptionQuery,
|
||||
>;
|
||||
|
||||
/// The total supply of each coin.
|
||||
// We use Identity type here again due to reasons stated in the Balances Storage.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn supply)]
|
||||
pub type Supply<T: Config> = StorageMap<_, Blake2_128Concat, u32, SubstrateAmount, ValueQuery>;
|
||||
|
||||
// TODO: apis: supply, mint, burn, transfer
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// Returns the balance of a given account for `token`.
|
||||
pub fn balance(token: u32, of: T::AccountId) -> SubstrateAmount {
|
||||
Self::balances(of, token).unwrap_or(0)
|
||||
}
|
||||
|
||||
/// Mint `balance` to the given account.
|
||||
///
|
||||
/// Errors if any amount overflows.
|
||||
pub fn mint_into(token: u32, to: Public, amount: SubstrateAmount) -> Result<(), Error<T>> {
|
||||
let balance = Self::balances(to, token).unwrap_or(0);
|
||||
|
||||
// update the balance
|
||||
let new_amount = balance.checked_add(amount).ok_or(Error::<T>::AmountOverflowed)?;
|
||||
|
||||
// save
|
||||
Balances::<T>::set(to, token, Some(new_amount));
|
||||
|
||||
// update the supply
|
||||
let new_supply =
|
||||
Self::supply(token).checked_add(amount).ok_or(Error::<T>::AmountOverflowed)?;
|
||||
Supply::<T>::set(token, new_supply);
|
||||
|
||||
Self::deposit_event(Event::LtMint { to, token, amount: Amount(amount) });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Burn `balance` from the specified account.
|
||||
pub fn burn_from(token: u32, from: Public, amount: SubstrateAmount) -> Result<(), Error<T>> {
|
||||
let balance = Self::balances(from, token);
|
||||
if balance.is_none() {
|
||||
Err(Error::<T>::NotEnoughCoins)?;
|
||||
}
|
||||
|
||||
// update the balance
|
||||
let new_amount = balance.unwrap().checked_sub(amount).ok_or(Error::<T>::NotEnoughCoins)?;
|
||||
|
||||
// save
|
||||
if new_amount == 0 {
|
||||
Balances::<T>::remove(from, token);
|
||||
} else {
|
||||
Balances::<T>::set(from, token, Some(new_amount));
|
||||
}
|
||||
|
||||
// update the supply
|
||||
let new_supply = Self::supply(token).checked_sub(amount).unwrap();
|
||||
if new_supply == 0 {
|
||||
Supply::<T>::remove(token);
|
||||
} else {
|
||||
Supply::<T>::set(token, new_supply);
|
||||
}
|
||||
|
||||
Self::deposit_event(Event::LtBurn { from, token, amount: Amount(amount) });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn total_issuance(token: u32) -> SubstrateAmount {
|
||||
Supply::<T>::get(token)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> LiquidityTokens<T::AccountId> for Pallet<T> {
|
||||
type Balance = SubstrateAmount;
|
||||
type CoinId = u32;
|
||||
|
||||
fn mint_into(
|
||||
token: Self::CoinId,
|
||||
to: &Public,
|
||||
amount: Self::Balance,
|
||||
) -> Result<Self::Balance, DispatchError> {
|
||||
Self::mint_into(token, *to, amount)?;
|
||||
Ok(amount)
|
||||
}
|
||||
|
||||
fn burn_from(
|
||||
token: Self::CoinId,
|
||||
from: &Public,
|
||||
amount: Self::Balance,
|
||||
) -> Result<Self::Balance, DispatchError> {
|
||||
Self::burn_from(token, *from, amount)?;
|
||||
Ok(amount)
|
||||
}
|
||||
|
||||
fn total_issuance(token: Self::CoinId) -> Self::Balance {
|
||||
Self::total_issuance(token)
|
||||
}
|
||||
|
||||
fn coin_ids() -> Vec<Self::CoinId> {
|
||||
Supply::<T>::iter_keys().collect::<Vec<Self::CoinId>>()
|
||||
}
|
||||
|
||||
fn balance(token: Self::CoinId, of: &Public) -> Self::Balance {
|
||||
Self::balance(token, *of)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use pallet::*;
|
|
@ -41,9 +41,13 @@ fn testnet_genesis(
|
|||
.into_iter()
|
||||
.map(|a| (a, Balance { coin: Coin::Serai, amount: Amount(1 << 60) }))
|
||||
.collect(),
|
||||
_ignore: Default::default(),
|
||||
},
|
||||
|
||||
dex: DexConfig { pools: vec![Coin::Bitcoin, Coin::Ether, Coin::Dai, Coin::Monero] },
|
||||
dex: DexConfig {
|
||||
pools: vec![Coin::Bitcoin, Coin::Ether, Coin::Dai, Coin::Monero],
|
||||
_ignore: Default::default(),
|
||||
},
|
||||
|
||||
validator_sets: ValidatorSetsConfig {
|
||||
networks: serai_runtime::primitives::NETWORKS
|
||||
|
|
|
@ -40,14 +40,12 @@ frame-executive = { git = "https://github.com/serai-dex/substrate", default-feat
|
|||
frame-benchmarking = { git = "https://github.com/serai-dex/substrate", default-features = false, optional = true }
|
||||
|
||||
serai-primitives = { path = "../primitives", default-features = false }
|
||||
serai-dex-primitives = { path = "../dex/primitives", default-features = false }
|
||||
|
||||
pallet-timestamp = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
|
||||
pallet-transaction-payment = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
|
||||
coins-pallet = { package = "serai-coins-pallet", path = "../coins/pallet", default-features = false }
|
||||
liquidity-tokens-pallet = { package = "serai-liquidity-tokens-pallet", path = "../liquidity-tokens/pallet", default-features = false }
|
||||
dex-pallet = { package = "serai-dex-pallet", path = "../dex/pallet", default-features = false }
|
||||
|
||||
validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../validator-sets/pallet", default-features = false }
|
||||
|
@ -97,14 +95,12 @@ std = [
|
|||
"frame-executive/std",
|
||||
|
||||
"serai-primitives/std",
|
||||
"serai-dex-primitives/std",
|
||||
|
||||
"pallet-timestamp/std",
|
||||
|
||||
"pallet-transaction-payment/std",
|
||||
|
||||
"coins-pallet/std",
|
||||
"liquidity-tokens-pallet/std",
|
||||
"dex-pallet/std",
|
||||
|
||||
"validator-sets-pallet/std",
|
||||
|
@ -132,8 +128,6 @@ runtime-benchmarks = [
|
|||
|
||||
"pallet-timestamp/runtime-benchmarks",
|
||||
|
||||
"dex-pallet/runtime-benchmarks",
|
||||
|
||||
"pallet-babe/runtime-benchmarks",
|
||||
"pallet-grandpa/runtime-benchmarks",
|
||||
]
|
||||
|
|
|
@ -17,7 +17,6 @@ pub use pallet_timestamp as timestamp;
|
|||
pub use pallet_transaction_payment as transaction_payment;
|
||||
|
||||
pub use coins_pallet as coins;
|
||||
pub use liquidity_tokens_pallet as liquidity_tokens;
|
||||
pub use dex_pallet as dex;
|
||||
|
||||
pub use validator_sets_pallet as validator_sets;
|
||||
|
@ -47,7 +46,7 @@ use sp_runtime::{
|
|||
ApplyExtrinsicResult, Perbill,
|
||||
};
|
||||
|
||||
use primitives::{PublicKey, SeraiAddress, Coin, AccountLookup, Signature, SubstrateAmount};
|
||||
use primitives::{PublicKey, SeraiAddress, AccountLookup, Signature, SubstrateAmount};
|
||||
|
||||
use support::{
|
||||
traits::{ConstU8, ConstU32, ConstU64, Contains},
|
||||
|
@ -157,6 +156,12 @@ impl Contains<RuntimeCall> for CallFilter {
|
|||
|
||||
// All of these pallets are our own, and all of their written calls are intended to be called
|
||||
RuntimeCall::Coins(call) => !matches!(call, coins::Call::__Ignore(_, _)),
|
||||
RuntimeCall::LiquidityTokens(call) => match call {
|
||||
coins::Call::transfer { .. } => true,
|
||||
coins::Call::burn { .. } => true,
|
||||
coins::Call::burn_with_instruction { .. } => false,
|
||||
coins::Call::__Ignore(_, _) => false,
|
||||
},
|
||||
RuntimeCall::Dex(call) => !matches!(call, dex::Call::__Ignore(_, _)),
|
||||
RuntimeCall::ValidatorSets(call) => !matches!(call, validator_sets::Call::__Ignore(_, _)),
|
||||
RuntimeCall::InInstructions(call) => !matches!(call, in_instructions::Call::__Ignore(_, _)),
|
||||
|
@ -234,25 +239,12 @@ impl coins::Config for Runtime {
|
|||
type RuntimeEvent = RuntimeEvent;
|
||||
}
|
||||
|
||||
impl liquidity_tokens::Config for Runtime {
|
||||
impl coins::Config<coins::Instance1> for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
}
|
||||
|
||||
impl dex::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type Currency = Coins;
|
||||
type Balance = SubstrateAmount;
|
||||
type CoinBalance = SubstrateAmount;
|
||||
// TODO: Review if this should be u64/u128 or u64/u256 (and rounding in general).
|
||||
type HigherPrecisionBalance = u128;
|
||||
|
||||
type CoinId = Coin;
|
||||
type MultiCoinId = Coin;
|
||||
type MultiCoinIdConverter = serai_dex_primitives::CoinConverter;
|
||||
type PoolCoinId = u32;
|
||||
|
||||
type Coins = Coins;
|
||||
type PoolCoins = LiquidityTokens;
|
||||
|
||||
type LPFee = ConstU32<3>; // 0.3%
|
||||
type MintMinLiquidity = ConstU64<10000>;
|
||||
|
@ -260,9 +252,6 @@ impl dex::Config for Runtime {
|
|||
type MaxSwapPathLength = ConstU32<3>; // coin1 -> SRI -> coin2
|
||||
|
||||
type WeightInfo = dex::weights::SubstrateWeight<Runtime>;
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type BenchmarkHelper = ();
|
||||
}
|
||||
|
||||
impl validator_sets::Config for Runtime {
|
||||
|
@ -364,7 +353,7 @@ construct_runtime!(
|
|||
TransactionPayment: transaction_payment,
|
||||
|
||||
Coins: coins,
|
||||
LiquidityTokens: liquidity_tokens,
|
||||
LiquidityTokens: coins::<Instance1>::{Pallet, Call, Storage, Event<T>},
|
||||
Dex: dex,
|
||||
|
||||
ValidatorSets: validator_sets,
|
||||
|
|
|
@ -275,7 +275,12 @@ async fn sign_test() {
|
|||
serai
|
||||
.publish(
|
||||
&serai
|
||||
.sign(&serai_pair, &SeraiCoins::burn(out_instruction.clone()), 0, Default::default())
|
||||
.sign(
|
||||
&serai_pair,
|
||||
&SeraiCoins::burn_with_instruction(out_instruction.clone()),
|
||||
0,
|
||||
Default::default(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
|
@ -293,7 +298,7 @@ async fn sign_test() {
|
|||
let burn_events = serai
|
||||
.as_of(serai.block_by_number(last_serai_block).await.unwrap().unwrap().hash())
|
||||
.coins()
|
||||
.burn_events()
|
||||
.burn_with_instruction_events()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -301,7 +306,10 @@ async fn sign_test() {
|
|||
assert_eq!(burn_events.len(), 1);
|
||||
assert_eq!(
|
||||
burn_events[0],
|
||||
CoinsEvent::Burn { from: serai_addr.into(), instruction: out_instruction.clone() }
|
||||
CoinsEvent::BurnWithInstruction {
|
||||
from: serai_addr.into(),
|
||||
instruction: out_instruction.clone()
|
||||
}
|
||||
);
|
||||
break 'outer;
|
||||
}
|
||||
|
|
|
@ -508,7 +508,12 @@ async fn mint_and_burn_test() {
|
|||
serai
|
||||
.publish(
|
||||
&serai
|
||||
.sign(serai_pair, &SeraiCoins::burn(out_instruction), nonce, Default::default())
|
||||
.sign(
|
||||
serai_pair,
|
||||
&SeraiCoins::burn_with_instruction(out_instruction),
|
||||
nonce,
|
||||
Default::default(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
|
|
Loading…
Reference in a new issue