mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 20:29:23 +00:00
Add Dex pallet (#407)
* Move pallet-asset-conversion * update licensing * initial integration * Integrate Currency & Assets types * integrate liquidity tokens * fmt * integrate dex pallet tests * fmt * compilation error fixes * integrate dex benchmarks * fmt * cargo clippy * replace all occurrences of "asset" with "coin" * add the actual add liq/swap logic to in-instructions * add client side & tests * fix deny * Lint and changes - Renames InInstruction::AddLiquidity to InInstruction::SwapAndAddLiquidity - Makes create_pool an internal function - Makes dex-pallet exclusively create pools against a native coin - Removes various fees - Adds new crates to GH workflow * Fix rebase artifacts * Correct other rebase artifact * Correct CI specification for liquidity-tokens * Correct primitives' test to the standardized pallet account scheme --------- Co-authored-by: Luke Parker <lukeparker5132@gmail.com>
This commit is contained in:
71
substrate/client/src/serai/dex.rs
Normal file
71
substrate/client/src/serai/dex.rs
Normal file
@@ -0,0 +1,71 @@
|
||||
use sp_core::bounded_vec::BoundedVec;
|
||||
use serai_runtime::{
|
||||
primitives::{SeraiAddress, Amount, Coin},
|
||||
dex, Dex, Runtime,
|
||||
};
|
||||
|
||||
use subxt::tx::Payload;
|
||||
|
||||
use crate::{SeraiError, Composite, TemporalSerai, scale_composite};
|
||||
|
||||
const PALLET: &str = "Dex";
|
||||
|
||||
pub type DexEvent = dex::Event<Runtime>;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct SeraiDex<'a>(pub(crate) TemporalSerai<'a>);
|
||||
impl<'a> SeraiDex<'a> {
|
||||
pub async fn all_events(&self) -> Result<Vec<DexEvent>, SeraiError> {
|
||||
self.0.events::<Dex, _>(|_| true).await
|
||||
}
|
||||
|
||||
pub fn add_liquidity(
|
||||
coin: Coin,
|
||||
coin_amount: Amount,
|
||||
sri_amount: Amount,
|
||||
min_coin_amount: Amount,
|
||||
min_sri_amount: Amount,
|
||||
address: SeraiAddress,
|
||||
) -> Payload<Composite<()>> {
|
||||
Payload::new(
|
||||
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,
|
||||
mint_to: address.into(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn swap(
|
||||
from_coin: Coin,
|
||||
to_coin: Coin,
|
||||
amount_in: Amount,
|
||||
amount_out_min: Amount,
|
||||
address: SeraiAddress,
|
||||
) -> Payload<Composite<()>> {
|
||||
let path = if to_coin.is_native() {
|
||||
BoundedVec::try_from(vec![from_coin, Coin::Serai]).unwrap()
|
||||
} else if from_coin.is_native() {
|
||||
BoundedVec::try_from(vec![Coin::Serai, to_coin]).unwrap()
|
||||
} else {
|
||||
BoundedVec::try_from(vec![from_coin, Coin::Serai, to_coin]).unwrap()
|
||||
};
|
||||
|
||||
Payload::new(
|
||||
PALLET,
|
||||
"swap_exact_tokens_for_tokens",
|
||||
scale_composite(dex::Call::<Runtime>::swap_exact_tokens_for_tokens {
|
||||
path,
|
||||
amount_in: amount_in.0,
|
||||
amount_out_min: amount_out_min.0,
|
||||
send_to: address.into(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,8 @@ use serai_runtime::{
|
||||
|
||||
pub mod coins;
|
||||
pub use coins::SeraiCoins;
|
||||
pub mod dex;
|
||||
pub use dex::SeraiDex;
|
||||
pub mod in_instructions;
|
||||
pub use in_instructions::SeraiInInstructions;
|
||||
pub mod validator_sets;
|
||||
@@ -347,6 +349,10 @@ impl<'a> TemporalSerai<'a> {
|
||||
SeraiCoins(self)
|
||||
}
|
||||
|
||||
pub fn dex(self) -> SeraiDex<'a> {
|
||||
SeraiDex(self)
|
||||
}
|
||||
|
||||
pub fn in_instructions(self) -> SeraiInInstructions<'a> {
|
||||
SeraiInInstructions(self)
|
||||
}
|
||||
|
||||
55
substrate/client/tests/common/dex.rs
Normal file
55
substrate/client/tests/common/dex.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
use serai_runtime::primitives::{Coin, Amount};
|
||||
|
||||
use serai_client::{Serai, SeraiDex, PairSigner};
|
||||
use sp_core::{sr25519::Pair, Pair as PairTrait};
|
||||
|
||||
use subxt::config::extrinsic_params::BaseExtrinsicParamsBuilder;
|
||||
|
||||
use crate::common::tx::publish_tx;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn add_liquidity(
|
||||
serai: &Serai,
|
||||
coin: Coin,
|
||||
coin_amount: Amount,
|
||||
sri_amount: Amount,
|
||||
nonce: u32,
|
||||
pair: Pair,
|
||||
) -> [u8; 32] {
|
||||
let address = pair.public();
|
||||
|
||||
let tx = serai
|
||||
.sign(
|
||||
&PairSigner::new(pair),
|
||||
&SeraiDex::add_liquidity(coin, coin_amount, sri_amount, Amount(1), Amount(1), address.into()),
|
||||
nonce,
|
||||
BaseExtrinsicParamsBuilder::new(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
publish_tx(serai, &tx).await
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn swap(
|
||||
serai: &Serai,
|
||||
from_coin: Coin,
|
||||
to_coin: Coin,
|
||||
amount_in: Amount,
|
||||
amount_out_min: Amount,
|
||||
nonce: u32,
|
||||
pair: Pair,
|
||||
) -> [u8; 32] {
|
||||
let address = pair.public();
|
||||
|
||||
let tx = serai
|
||||
.sign(
|
||||
&PairSigner::new(pair),
|
||||
&SeraiDex::swap(from_coin, to_coin, amount_in, amount_out_min, address.into()),
|
||||
nonce,
|
||||
BaseExtrinsicParamsBuilder::new(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
publish_tx(serai, &tx).await
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
use rand_core::{RngCore, OsRng};
|
||||
use blake2::{
|
||||
digest::{consts::U32, Digest},
|
||||
Blake2b,
|
||||
@@ -8,10 +9,10 @@ use scale::Encode;
|
||||
use sp_core::Pair;
|
||||
|
||||
use serai_client::{
|
||||
primitives::insecure_pair_from_name,
|
||||
primitives::{insecure_pair_from_name, BlockHash, NetworkId, Balance, SeraiAddress},
|
||||
validator_sets::primitives::{Session, ValidatorSet},
|
||||
in_instructions::{
|
||||
primitives::{Batch, SignedBatch, batch_message},
|
||||
primitives::{Batch, SignedBatch, batch_message, InInstruction, InInstructionWithBalance},
|
||||
InInstructionsEvent,
|
||||
},
|
||||
SeraiInInstructions, Serai,
|
||||
@@ -60,3 +61,27 @@ pub async fn provide_batch(serai: &Serai, batch: Batch) -> [u8; 32] {
|
||||
|
||||
block
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn mint_coin(
|
||||
serai: &Serai,
|
||||
balance: Balance,
|
||||
network: NetworkId,
|
||||
batch_id: u32,
|
||||
address: SeraiAddress,
|
||||
) -> [u8; 32] {
|
||||
let mut block_hash = BlockHash([0; 32]);
|
||||
OsRng.fill_bytes(&mut block_hash.0);
|
||||
|
||||
let batch = Batch {
|
||||
network,
|
||||
id: batch_id,
|
||||
block: block_hash,
|
||||
instructions: vec![InInstructionWithBalance {
|
||||
instruction: InInstruction::Transfer(address),
|
||||
balance,
|
||||
}],
|
||||
};
|
||||
|
||||
provide_batch(serai, batch).await
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
pub mod tx;
|
||||
pub mod validator_sets;
|
||||
pub mod in_instructions;
|
||||
pub mod dex;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! serai_test {
|
||||
|
||||
452
substrate/client/tests/dex.rs
Normal file
452
substrate/client/tests/dex.rs
Normal file
@@ -0,0 +1,452 @@
|
||||
use rand_core::{RngCore, OsRng};
|
||||
use scale::Encode;
|
||||
|
||||
use sp_core::{Pair as PairTrait, bounded_vec::BoundedVec, hashing::blake2_256};
|
||||
|
||||
use serai_runtime::in_instructions::primitives::DexCall;
|
||||
|
||||
use serai_client::{
|
||||
primitives::{
|
||||
Amount, NetworkId, Coin, Balance, BlockHash, insecure_pair_from_name, ExternalAddress,
|
||||
SeraiAddress, PublicKey,
|
||||
},
|
||||
in_instructions::primitives::{
|
||||
InInstruction, InInstructionWithBalance, Batch, IN_INSTRUCTION_EXECUTOR, OutAddress,
|
||||
},
|
||||
dex::DexEvent,
|
||||
Serai,
|
||||
};
|
||||
|
||||
mod common;
|
||||
use common::{
|
||||
in_instructions::{provide_batch, mint_coin},
|
||||
dex::{add_liquidity as common_add_liquidity, swap as common_swap},
|
||||
};
|
||||
|
||||
// TODO: Calculate all constants in the following tests
|
||||
// TODO: Check LP token, coin balances
|
||||
// TODO: Modularize common code
|
||||
// TODO: Check Transfer events
|
||||
serai_test!(
|
||||
create_pool: (|serai: Serai| async move {
|
||||
let block = serai.block_by_number(0).await.unwrap().unwrap().hash();
|
||||
let events = serai.as_of(block).dex().all_events().await.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
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,
|
||||
},
|
||||
DexEvent::PoolCreated {
|
||||
pool_id: (Coin::Serai, Coin::Ether),
|
||||
pool_account: PublicKey::from_raw(blake2_256(&(Coin::Serai, Coin::Ether).encode())),
|
||||
lp_token: 1,
|
||||
},
|
||||
DexEvent::PoolCreated {
|
||||
pool_id: (Coin::Serai, Coin::Dai),
|
||||
pool_account: PublicKey::from_raw(blake2_256(&(Coin::Serai, Coin::Dai).encode())),
|
||||
lp_token: 2,
|
||||
},
|
||||
DexEvent::PoolCreated {
|
||||
pool_id: (Coin::Serai, Coin::Monero),
|
||||
pool_account: PublicKey::from_raw(blake2_256(&(Coin::Serai, Coin::Monero).encode())),
|
||||
lp_token: 3,
|
||||
},
|
||||
]
|
||||
);
|
||||
})
|
||||
|
||||
add_liquidity: (|serai: Serai| async move {
|
||||
let coin = Coin::Monero;
|
||||
let pair = insecure_pair_from_name("Ferdie");
|
||||
|
||||
// mint sriXMR in the account so that we can add liq.
|
||||
// Ferdie account is already pre-funded with SRI.
|
||||
mint_coin(
|
||||
&serai,
|
||||
Balance { coin, amount: Amount(100_000_000_000_000) },
|
||||
NetworkId::Monero,
|
||||
0,
|
||||
pair.clone().public().into(),
|
||||
)
|
||||
.await;
|
||||
|
||||
// add liquidity
|
||||
let coin_amount = Amount(50_000_000_000_000);
|
||||
let sri_amount = Amount(50_000_000_000_000);
|
||||
let block = common_add_liquidity(&serai,
|
||||
coin,
|
||||
coin_amount,
|
||||
sri_amount,
|
||||
0,
|
||||
pair.clone()
|
||||
).await;
|
||||
// get only the add liq events
|
||||
let mut events = serai.as_of(block).dex().all_events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::LiquidityAdded { .. }));
|
||||
|
||||
assert_eq!(
|
||||
events,
|
||||
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,
|
||||
lp_token_minted: 49_999999990000
|
||||
}]
|
||||
);
|
||||
})
|
||||
|
||||
// Tests coin -> SRI and SRI -> coin swaps.
|
||||
swap_coin_to_sri: (|serai: Serai| async move {
|
||||
let coin = Coin::Ether;
|
||||
let pair = insecure_pair_from_name("Ferdie");
|
||||
|
||||
// mint sriXMR in the account so that we can add liq.
|
||||
// Ferdie account is already pre-funded with SRI.
|
||||
mint_coin(
|
||||
&serai,
|
||||
Balance { coin, amount: Amount(100_000_000_000_000) },
|
||||
NetworkId::Ethereum,
|
||||
0,
|
||||
pair.clone().public().into(),
|
||||
)
|
||||
.await;
|
||||
|
||||
// add liquidity
|
||||
common_add_liquidity(&serai,
|
||||
coin,
|
||||
Amount(50_000_000_000_000),
|
||||
Amount(50_000_000_000_000),
|
||||
0,
|
||||
pair.clone()
|
||||
).await;
|
||||
|
||||
// now that we have our liquid pool, swap some coin to SRI.
|
||||
let mut amount_in = Amount(25_000_000_000_000);
|
||||
let mut block = common_swap(&serai, coin, Coin::Serai, amount_in, Amount(1), 1, pair.clone())
|
||||
.await;
|
||||
|
||||
// get only the swap events
|
||||
let mut events = serai.as_of(block).dex().all_events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::SwapExecuted { .. }));
|
||||
|
||||
let mut path = BoundedVec::try_from(vec![coin, Coin::Serai]).unwrap();
|
||||
assert_eq!(
|
||||
events,
|
||||
vec![DexEvent::SwapExecuted {
|
||||
who: pair.clone().public(),
|
||||
send_to: pair.public(),
|
||||
path,
|
||||
amount_in: amount_in.0,
|
||||
amount_out: 16633299966633
|
||||
}]
|
||||
);
|
||||
|
||||
// now swap some SRI to coin
|
||||
amount_in = Amount(10_000_000_000_000);
|
||||
block = common_swap(&serai, Coin::Serai, coin, amount_in, Amount(1), 2, pair.clone()).await;
|
||||
|
||||
// get only the swap events
|
||||
let mut events = serai.as_of(block).dex().all_events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::SwapExecuted { .. }));
|
||||
|
||||
path = BoundedVec::try_from(vec![Coin::Serai, coin]).unwrap();
|
||||
assert_eq!(
|
||||
events,
|
||||
vec![DexEvent::SwapExecuted {
|
||||
who: pair.clone().public(),
|
||||
send_to: pair.public(),
|
||||
path,
|
||||
amount_in: amount_in.0,
|
||||
amount_out: 17254428681101
|
||||
}]
|
||||
);
|
||||
})
|
||||
|
||||
swap_coin_to_coin: (|serai: Serai| async move {
|
||||
let coin1 = Coin::Monero;
|
||||
let coin2 = Coin::Dai;
|
||||
let pair = insecure_pair_from_name("Ferdie");
|
||||
|
||||
// mint coins
|
||||
mint_coin(
|
||||
&serai,
|
||||
Balance { coin: coin1, amount: Amount(100_000_000_000_000) },
|
||||
NetworkId::Monero,
|
||||
0,
|
||||
pair.clone().public().into(),
|
||||
)
|
||||
.await;
|
||||
mint_coin(
|
||||
&serai,
|
||||
Balance { coin: coin2, amount: Amount(100_000_000_000_000) },
|
||||
NetworkId::Ethereum,
|
||||
0,
|
||||
pair.clone().public().into(),
|
||||
)
|
||||
.await;
|
||||
|
||||
// add liquidity to pools
|
||||
common_add_liquidity(&serai,
|
||||
coin1,
|
||||
Amount(50_000_000_000_000),
|
||||
Amount(50_000_000_000_000),
|
||||
0,
|
||||
pair.clone()
|
||||
).await;
|
||||
common_add_liquidity(&serai,
|
||||
coin2,
|
||||
Amount(50_000_000_000_000),
|
||||
Amount(50_000_000_000_000),
|
||||
1,
|
||||
pair.clone()
|
||||
).await;
|
||||
|
||||
// swap coin1 -> coin2
|
||||
let amount_in = Amount(25_000_000_000_000);
|
||||
let block = common_swap(&serai, coin1, coin2, amount_in, Amount(1), 2, pair.clone()).await;
|
||||
|
||||
// get only the swap events
|
||||
let mut events = serai.as_of(block).dex().all_events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::SwapExecuted { .. }));
|
||||
|
||||
let path = BoundedVec::try_from(vec![coin1, Coin::Serai, coin2]).unwrap();
|
||||
assert_eq!(
|
||||
events,
|
||||
vec![DexEvent::SwapExecuted {
|
||||
who: pair.clone().public(),
|
||||
send_to: pair.public(),
|
||||
path,
|
||||
amount_in: amount_in.0,
|
||||
amount_out: 12453103964435,
|
||||
}]
|
||||
);
|
||||
})
|
||||
|
||||
add_liquidity_in_instructions: (|serai: Serai| async move {
|
||||
let coin = Coin::Bitcoin;
|
||||
let pair = insecure_pair_from_name("Ferdie");
|
||||
let mut batch_id = 0;
|
||||
|
||||
// mint sriBTC in the account so that we can add liq.
|
||||
// Ferdie account is already pre-funded with SRI.
|
||||
mint_coin(
|
||||
&serai,
|
||||
Balance { coin, amount: Amount(100_000_000_000_000) },
|
||||
NetworkId::Bitcoin,
|
||||
batch_id,
|
||||
pair.clone().public().into(),
|
||||
)
|
||||
.await;
|
||||
batch_id += 1;
|
||||
|
||||
// add liquidity
|
||||
common_add_liquidity(&serai,
|
||||
coin,
|
||||
Amount(50_000_000_000_000),
|
||||
Amount(50_000_000_000_000),
|
||||
0,
|
||||
pair.clone()
|
||||
).await;
|
||||
|
||||
// now that we have our liquid SRI/BTC pool, we can add more liquidity to it via an
|
||||
// InInstruction
|
||||
let mut block_hash = BlockHash([0; 32]);
|
||||
OsRng.fill_bytes(&mut block_hash.0);
|
||||
let batch = Batch {
|
||||
network: NetworkId::Bitcoin,
|
||||
id: batch_id,
|
||||
block: block_hash,
|
||||
instructions: vec![InInstructionWithBalance {
|
||||
instruction: InInstruction::Dex(DexCall::SwapAndAddLiquidity(pair.public().into())),
|
||||
balance: Balance { coin: Coin::Bitcoin, amount: Amount(20_000_000_000_000) },
|
||||
}],
|
||||
};
|
||||
|
||||
let block = provide_batch(&serai, batch).await;
|
||||
let mut events = serai.as_of(block).dex().all_events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::LiquidityAdded { .. }));
|
||||
assert_eq!(
|
||||
events,
|
||||
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,
|
||||
lp_token_minted: 8333333333332
|
||||
}]
|
||||
);
|
||||
})
|
||||
|
||||
swap_in_instructions: (|serai: Serai| async move {
|
||||
let coin1 = Coin::Monero;
|
||||
let coin2 = Coin::Ether;
|
||||
let pair = insecure_pair_from_name("Ferdie");
|
||||
let mut coin1_batch_id = 0;
|
||||
let mut coin2_batch_id = 0;
|
||||
|
||||
// mint coins
|
||||
mint_coin(
|
||||
&serai,
|
||||
Balance { coin: coin1, amount: Amount(100_000_000_000_000) },
|
||||
NetworkId::Monero,
|
||||
coin1_batch_id,
|
||||
pair.clone().public().into(),
|
||||
)
|
||||
.await;
|
||||
coin1_batch_id += 1;
|
||||
mint_coin(
|
||||
&serai,
|
||||
Balance { coin: coin2, amount: Amount(100_000_000_000_000) },
|
||||
NetworkId::Ethereum,
|
||||
coin2_batch_id,
|
||||
pair.clone().public().into(),
|
||||
)
|
||||
.await;
|
||||
coin2_batch_id += 1;
|
||||
|
||||
// add liquidity to pools
|
||||
common_add_liquidity(&serai,
|
||||
coin1,
|
||||
Amount(50_000_000_000_000),
|
||||
Amount(50_000_000_000_000),
|
||||
0,
|
||||
pair.clone()
|
||||
).await;
|
||||
common_add_liquidity(&serai,
|
||||
coin2,
|
||||
Amount(50_000_000_000_000),
|
||||
Amount(50_000_000_000_000),
|
||||
1,
|
||||
pair.clone()
|
||||
).await;
|
||||
|
||||
// rand address bytes
|
||||
let mut rand_bytes = vec![0; 32];
|
||||
OsRng.fill_bytes(&mut rand_bytes);
|
||||
|
||||
// XMR -> ETH
|
||||
{
|
||||
// make an out address
|
||||
let out_address = OutAddress::External(ExternalAddress::new(rand_bytes.clone()).unwrap());
|
||||
|
||||
// amount is the min out amount
|
||||
let out_balance = Balance { coin: coin2, amount: Amount(1) };
|
||||
|
||||
// now that we have our pools, we can try to swap
|
||||
let mut block_hash = BlockHash([0; 32]);
|
||||
OsRng.fill_bytes(&mut block_hash.0);
|
||||
let batch = Batch {
|
||||
network: NetworkId::Monero,
|
||||
id: coin1_batch_id,
|
||||
block: block_hash,
|
||||
instructions: vec![InInstructionWithBalance {
|
||||
instruction: InInstruction::Dex(DexCall::Swap(out_balance, out_address)),
|
||||
balance: Balance { coin: coin1, amount: Amount(20_000_000_000_000) },
|
||||
}],
|
||||
};
|
||||
|
||||
let block = provide_batch(&serai, batch).await;
|
||||
coin1_batch_id += 1;
|
||||
let mut events = serai.as_of(block).dex().all_events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::SwapExecuted { .. }));
|
||||
|
||||
let path = BoundedVec::try_from(vec![coin1, Coin::Serai, coin2]).unwrap();
|
||||
assert_eq!(
|
||||
events,
|
||||
vec![DexEvent::SwapExecuted {
|
||||
who: IN_INSTRUCTION_EXECUTOR.into(),
|
||||
send_to: IN_INSTRUCTION_EXECUTOR.into(),
|
||||
path,
|
||||
amount_in: 20_000_000_000_000,
|
||||
amount_out: 11066655622377
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
// ETH -> sriXMR
|
||||
{
|
||||
// make an out address
|
||||
let out_address =
|
||||
OutAddress::Serai(SeraiAddress::new(rand_bytes.clone().try_into().unwrap()));
|
||||
|
||||
// amount is the min out amount
|
||||
let out_balance = Balance { coin: coin1, amount: Amount(1) };
|
||||
|
||||
// now that we have our pools, we can try to swap
|
||||
let mut block_hash = BlockHash([0; 32]);
|
||||
OsRng.fill_bytes(&mut block_hash.0);
|
||||
let batch = Batch {
|
||||
network: NetworkId::Ethereum,
|
||||
id: coin2_batch_id,
|
||||
block: block_hash,
|
||||
instructions: vec![InInstructionWithBalance {
|
||||
instruction: InInstruction::Dex(DexCall::Swap(out_balance, out_address.clone())),
|
||||
balance: Balance { coin: coin2, amount: Amount(20_000_000_000_000) },
|
||||
}],
|
||||
};
|
||||
|
||||
let block = provide_batch(&serai, batch).await;
|
||||
let mut events = serai.as_of(block).dex().all_events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::SwapExecuted { .. }));
|
||||
|
||||
let path = BoundedVec::try_from(vec![coin2, Coin::Serai, coin1]).unwrap();
|
||||
assert_eq!(
|
||||
events,
|
||||
vec![DexEvent::SwapExecuted {
|
||||
who: IN_INSTRUCTION_EXECUTOR.into(),
|
||||
send_to: out_address.as_native().unwrap().into(),
|
||||
path,
|
||||
amount_in: 20_000_000_000_000,
|
||||
amount_out: 26440798801319
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
// XMR -> SRI
|
||||
{
|
||||
// make an out address
|
||||
let out_address = OutAddress::Serai(SeraiAddress::new(rand_bytes.try_into().unwrap()));
|
||||
|
||||
// amount is the min out amount
|
||||
let out_balance = Balance { coin: Coin::Serai, amount: Amount(1) };
|
||||
|
||||
// now that we have our pools, we can try to swap
|
||||
let mut block_hash = BlockHash([0; 32]);
|
||||
OsRng.fill_bytes(&mut block_hash.0);
|
||||
let batch = Batch {
|
||||
network: NetworkId::Monero,
|
||||
id: coin1_batch_id,
|
||||
block: block_hash,
|
||||
instructions: vec![InInstructionWithBalance {
|
||||
instruction: InInstruction::Dex(DexCall::Swap(out_balance, out_address.clone())),
|
||||
balance: Balance { coin: coin1, amount: Amount(10_000_000_000_000) },
|
||||
}],
|
||||
};
|
||||
|
||||
let block = provide_batch(&serai, batch).await;
|
||||
let mut events = serai.as_of(block).dex().all_events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::SwapExecuted { .. }));
|
||||
|
||||
let path = BoundedVec::try_from(vec![coin1, Coin::Serai]).unwrap();
|
||||
assert_eq!(
|
||||
events,
|
||||
vec![DexEvent::SwapExecuted {
|
||||
who: IN_INSTRUCTION_EXECUTOR.into(),
|
||||
send_to: out_address.as_native().unwrap().into(),
|
||||
path,
|
||||
amount_in: 10_000_000_000_000,
|
||||
amount_out: 10711005507065
|
||||
}]
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
Reference in New Issue
Block a user