Move serai-client off serai-runtime, MIT licensing it

Uses a full-fledged serai-abi to do so.

Removes use of UncheckedExtrinsic as a pointlessly (for us) length-prefixed
block with a more complicated signing algorithm than advantageous.

In the future, we should considering consolidating the various primitives
crates. I'm not convinced we benefit from one primitives crate per pallet.
This commit is contained in:
Luke Parker
2023-12-07 02:30:09 -05:00
parent 6416e0079b
commit c511a54d18
38 changed files with 484 additions and 378 deletions

View File

@@ -2,7 +2,7 @@
name = "serai-client"
version = "0.1.0"
description = "Client library for the Serai network"
license = "AGPL-3.0-only"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/client"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = ["serai"]
@@ -19,13 +19,16 @@ thiserror = { version = "1", optional = true }
hex = "0.4"
scale = { package = "parity-scale-codec", version = "3" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde = { version = "1", features = ["derive"], optional = true }
serde_json = { version = "1", optional = true }
sp-core = { git = "https://github.com/serai-dex/substrate" }
sp-runtime = { git = "https://github.com/serai-dex/substrate" }
serai-runtime = { path = "../runtime", version = "0.1" }
simple-request = { path = "../../common/request", version = "0.1" }
serai-abi = { path = "../abi", version = "0.1" }
sp-core = { git = "https://github.com/serai-dex/substrate", optional = true }
sp-runtime = { git = "https://github.com/serai-dex/substrate", optional = true }
frame-system = { git = "https://github.com/serai-dex/substrate", optional = true }
simple-request = { path = "../../common/request", version = "0.1", optional = true }
bitcoin = { version = "0.31", optional = true }
@@ -48,7 +51,7 @@ dockertest = "0.4"
serai-docker-tests = { path = "../../tests/docker" }
[features]
serai = ["thiserror"]
serai = ["thiserror", "serde", "serde_json", "sp-core", "sp-runtime", "frame-system", "simple-request"]
networks = []
bitcoin = ["networks", "dep:bitcoin"]

View File

@@ -1,15 +1,21 @@
AGPL-3.0-only license
MIT License
Copyright (c) 2022-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.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
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.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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/>.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -6,19 +6,18 @@ mod serai;
#[cfg(feature = "serai")]
pub use serai::*;
// If we aren't exposing the Serai client (subxt), still expose all primitives
#[cfg(not(feature = "serai"))]
pub use serai_runtime::primitives;
pub use serai_abi::primitives;
#[cfg(not(feature = "serai"))]
mod other_primitives {
pub mod in_instructions {
pub use serai_runtime::in_instructions::primitives;
}
pub mod coins {
pub use serai_runtime::coins::primitives;
pub use serai_abi::coins::primitives;
}
pub mod validator_sets {
pub use serai_runtime::validator_sets::primitives;
pub use serai_abi::validator_sets::primitives;
}
pub mod in_instructions {
pub use serai_abi::in_instructions::primitives;
}
}
#[cfg(not(feature = "serai"))]

View File

@@ -1,17 +1,14 @@
use scale::Encode;
use serai_runtime::{
primitives::{SeraiAddress, Amount, Coin, Balance},
coins, Runtime,
};
pub use coins::primitives;
use serai_abi::primitives::{SeraiAddress, Amount, Coin, Balance};
pub use serai_abi::coins::primitives;
use primitives::OutInstructionWithBalance;
use crate::{TemporalSerai, SeraiError};
const PALLET: &str = "Coins";
pub type CoinsEvent = coins::Event<Runtime>;
pub type CoinsEvent = serai_abi::coins::Event;
#[derive(Clone, Copy)]
pub struct SeraiCoins<'a>(pub(crate) TemporalSerai<'a>);
@@ -24,7 +21,7 @@ impl<'a> SeraiCoins<'a> {
self
.0
.events(|event| {
if let serai_runtime::RuntimeEvent::Coins(event) = event {
if let serai_abi::Event::Coins(event) = event {
Some(event).filter(|event| matches!(event, CoinsEvent::Mint { .. }))
} else {
None
@@ -37,7 +34,7 @@ impl<'a> SeraiCoins<'a> {
self
.0
.events(|event| {
if let serai_runtime::RuntimeEvent::Coins(event) = event {
if let serai_abi::Event::Coins(event) = event {
Some(event).filter(|event| matches!(event, CoinsEvent::BurnWithInstruction { .. }))
} else {
None
@@ -68,22 +65,15 @@ impl<'a> SeraiCoins<'a> {
)
}
pub fn transfer(to: SeraiAddress, balance: Balance) -> serai_runtime::RuntimeCall {
serai_runtime::RuntimeCall::Coins(serai_runtime::coins::Call::<Runtime>::transfer {
to: to.into(),
balance,
})
pub fn transfer(to: SeraiAddress, balance: Balance) -> serai_abi::Call {
serai_abi::Call::Coins(serai_abi::coins::Call::transfer { to, balance })
}
pub fn burn(balance: Balance) -> serai_runtime::RuntimeCall {
serai_runtime::RuntimeCall::Coins(serai_runtime::coins::Call::<Runtime>::burn { balance })
pub fn burn(balance: Balance) -> serai_abi::Call {
serai_abi::Call::Coins(serai_abi::coins::Call::burn { balance })
}
pub fn burn_with_instruction(
instruction: OutInstructionWithBalance,
) -> serai_runtime::RuntimeCall {
serai_runtime::RuntimeCall::Coins(
serai_runtime::coins::Call::<Runtime>::burn_with_instruction { instruction },
)
pub fn burn_with_instruction(instruction: OutInstructionWithBalance) -> serai_abi::Call {
serai_abi::Call::Coins(serai_abi::coins::Call::burn_with_instruction { instruction })
}
}

View File

@@ -1,12 +1,9 @@
use sp_core::bounded_vec::BoundedVec;
use serai_runtime::{
primitives::{SeraiAddress, Amount, Coin},
dex, Runtime,
};
use serai_abi::primitives::{SeraiAddress, Amount, Coin};
use crate::{SeraiError, TemporalSerai};
pub type DexEvent = dex::Event<Runtime>;
pub type DexEvent = serai_abi::dex::Event;
#[derive(Clone, Copy)]
pub struct SeraiDex<'a>(pub(crate) TemporalSerai<'a>);
@@ -14,15 +11,7 @@ impl<'a> SeraiDex<'a> {
pub async fn events(&self) -> Result<Vec<DexEvent>, SeraiError> {
self
.0
.events(
|event| {
if let serai_runtime::RuntimeEvent::Dex(event) = event {
Some(event)
} else {
None
}
},
)
.events(|event| if let serai_abi::Event::Dex(event) = event { Some(event) } else { None })
.await
}
@@ -33,14 +22,14 @@ impl<'a> SeraiDex<'a> {
min_coin_amount: Amount,
min_sri_amount: Amount,
address: SeraiAddress,
) -> serai_runtime::RuntimeCall {
serai_runtime::RuntimeCall::Dex(dex::Call::<Runtime>::add_liquidity {
) -> serai_abi::Call {
serai_abi::Call::Dex(serai_abi::dex::Call::add_liquidity {
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(),
mint_to: address,
})
}
@@ -50,7 +39,7 @@ impl<'a> SeraiDex<'a> {
amount_in: Amount,
amount_out_min: Amount,
address: SeraiAddress,
) -> serai_runtime::RuntimeCall {
) -> serai_abi::Call {
let path = if to_coin.is_native() {
BoundedVec::try_from(vec![from_coin, Coin::Serai]).unwrap()
} else if from_coin.is_native() {
@@ -59,11 +48,11 @@ impl<'a> SeraiDex<'a> {
BoundedVec::try_from(vec![from_coin, Coin::Serai, to_coin]).unwrap()
};
serai_runtime::RuntimeCall::Dex(dex::Call::<Runtime>::swap_exact_tokens_for_tokens {
serai_abi::Call::Dex(serai_abi::dex::Call::swap_exact_tokens_for_tokens {
path,
amount_in: amount_in.0,
amount_out_min: amount_out_min.0,
send_to: address.into(),
send_to: address,
})
}
}

View File

@@ -1,13 +1,12 @@
use serai_runtime::{in_instructions, Runtime};
pub use in_instructions::primitives;
pub use serai_abi::in_instructions::primitives;
use primitives::SignedBatch;
use crate::{
primitives::{BlockHash, NetworkId},
SeraiError, Serai, TemporalSerai,
Transaction, SeraiError, Serai, TemporalSerai,
};
pub type InInstructionsEvent = in_instructions::Event<Runtime>;
pub type InInstructionsEvent = serai_abi::in_instructions::Event;
const PALLET: &str = "InInstructions";
@@ -36,7 +35,7 @@ impl<'a> SeraiInInstructions<'a> {
self
.0
.events(|event| {
if let serai_runtime::RuntimeEvent::InInstructions(event) = event {
if let serai_abi::Event::InInstructions(event) = event {
Some(event).filter(|event| matches!(event, InInstructionsEvent::Batch { .. }))
} else {
None
@@ -45,9 +44,9 @@ impl<'a> SeraiInInstructions<'a> {
.await
}
pub fn execute_batch(batch: SignedBatch) -> Vec<u8> {
Serai::unsigned(&serai_runtime::RuntimeCall::InInstructions(
in_instructions::Call::<Runtime>::execute_batch { batch },
pub fn execute_batch(batch: SignedBatch) -> Transaction {
Serai::unsigned(serai_abi::Call::InInstructions(
serai_abi::in_instructions::Call::execute_batch { batch },
))
}
}

View File

@@ -10,11 +10,12 @@ pub use sp_core::{
sr25519::{Public, Pair},
};
pub use serai_runtime::primitives;
pub use primitives::{SeraiAddress, Signature, Amount};
pub use serai_abi as abi;
pub use abi::{primitives, Transaction};
use abi::*;
pub use serai_runtime as runtime;
use serai_runtime::{Header, Block as SeraiBlock};
pub use primitives::{SeraiAddress, Signature, Amount};
use primitives::Header;
pub mod coins;
pub use coins::SeraiCoins;
@@ -25,36 +26,28 @@ pub use in_instructions::SeraiInInstructions;
pub mod validator_sets;
pub use validator_sets::SeraiValidatorSets;
pub type Transaction = serai_runtime::UncheckedExtrinsic;
#[derive(Clone, Debug)]
pub struct Block(SeraiBlock);
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode)]
pub struct Block {
pub header: Header,
pub transactions: Vec<Transaction>,
}
impl Block {
pub fn hash(&self) -> [u8; 32] {
self.0.header.hash().into()
self.header.hash().into()
}
pub fn number(&self) -> u64 {
self.0.header.number
self.header.number
}
/// Returns the time of this block, set by its producer, in milliseconds since the epoch.
pub fn time(&self) -> Result<u64, SeraiError> {
for extrinsic in &self.0.extrinsics {
if let serai_runtime::RuntimeCall::Timestamp(serai_runtime::timestamp::Call::set { now }) =
&extrinsic.function
{
return Ok(*now);
for transaction in &self.transactions {
if let Call::Timestamp(timestamp::Call::set { now }) = &transaction.call {
return Ok(u64::from(*now));
}
}
Err(SeraiError::InvalidNode("no time was present in block".to_string()))
}
pub fn header(&self) -> &Header {
&self.0.header
}
pub fn transactions(&self) -> &[Transaction] {
&self.0.extrinsics
}
}
#[derive(Error, Debug)]
@@ -158,54 +151,37 @@ impl Serai {
Ok(res)
}
fn unsigned(call: &serai_runtime::RuntimeCall) -> Vec<u8> {
// TODO: Should Serai purge the old transaction code AND set this to 0/1?
const EXTRINSIC_FORMAT_VERSION: u8 = 4;
let mut tx = vec![EXTRINSIC_FORMAT_VERSION];
tx.extend(call.encode());
let mut length_prefixed = Compact(u32::try_from(tx.len()).unwrap()).encode();
length_prefixed.extend(tx);
length_prefixed
fn unsigned(call: Call) -> Transaction {
Transaction { call, signature: None }
}
pub fn sign(
&self,
signer: &Pair,
call: &serai_runtime::RuntimeCall,
nonce: u32,
tip: u64,
) -> Vec<u8> {
pub fn sign(&self, signer: &Pair, call: Call, nonce: u32, tip: u64) -> Transaction {
const SPEC_VERSION: u32 = 1;
const TX_VERSION: u32 = 1;
const EXTRINSIC_FORMAT_VERSION: u8 = 4;
let era = sp_runtime::generic::Era::Immortal;
let extra = (era, Compact(nonce), Compact(tip));
let genesis = self.genesis;
let mortality_checkpoint = genesis;
let mut signature_payload =
(call, extra, SPEC_VERSION, TX_VERSION, genesis, mortality_checkpoint).encode();
if signature_payload.len() > 256 {
signature_payload = sp_core::blake2_256(&signature_payload).to_vec();
}
let extra =
Extra { era: sp_runtime::generic::Era::Immortal, nonce: Compact(nonce), tip: Compact(tip) };
let signature_payload = (
&call,
&extra,
SignedPayloadExtra {
spec_version: SPEC_VERSION,
tx_version: TX_VERSION,
genesis: self.genesis,
mortality_checkpoint: self.genesis,
},
)
.encode();
let signature = signer.sign(&signature_payload);
let signed = 1 << 7;
let tx = (signed + EXTRINSIC_FORMAT_VERSION, signer.public(), signature, extra, call).encode();
let mut length_prefixed = Compact(u32::try_from(tx.len()).unwrap()).encode();
length_prefixed.extend(tx);
length_prefixed
Transaction { call, signature: Some((signer.public().into(), signature, extra)) }
}
// TODO: Move this to take in Transaction
pub async fn publish(&self, tx: &[u8]) -> Result<(), SeraiError> {
pub async fn publish(&self, tx: &Transaction) -> Result<(), SeraiError> {
// Drop the returned hash, which is the hash of the raw extrinsic, as extrinsics are allowed
// to share hashes and this hash is accordingly useless/unsafe
// If we are to return something, it should be block included in and position within block
let _: String = self.call("author_submitExtrinsic", [hex::encode(tx)]).await?;
let _: String = self.call("author_submitExtrinsic", [hex::encode(tx.encode())]).await?;
Ok(())
}
@@ -221,14 +197,15 @@ impl Serai {
}
pub async fn block(&self, hash: [u8; 32]) -> Result<Option<Block>, SeraiError> {
// TODO: Remove this wrapping from Serai?
#[derive(Deserialize)]
struct WrappedBlock {
block: SeraiBlock,
}
let block: Option<WrappedBlock> = self.call("chain_getBlock", [hex::encode(hash)]).await?;
let block: Option<String> = self.call("chain_getBlockBin", [hex::encode(hash)]).await?;
let Some(block) = block else { return Ok(None) };
Ok(Some(Block(block.block)))
let Ok(bytes) = Self::hex_decode(block) else {
Err(SeraiError::InvalidNode("didn't return a hex-encoded block".to_string()))?
};
let Ok(block) = Block::decode(&mut bytes.as_slice()) else {
Err(SeraiError::InvalidNode("didn't return a block".to_string()))?
};
Ok(Some(block))
}
pub async fn latest_finalized_block(&self) -> Result<Block, SeraiError> {
@@ -276,7 +253,7 @@ impl Serai {
let hash = self.block_hash(number).await?;
let Some(hash) = hash else { return Ok(None) };
let Some(block) = self.block(hash).await? else { return Ok(None) };
if !self.is_finalized(&block.0.header).await? {
if !self.is_finalized(&block.header).await? {
return Ok(None);
}
Ok(Some(block))
@@ -326,14 +303,10 @@ impl<'a> TemporalSerai<'a> {
self.0
}
async fn events<E>(
&self,
filter_map: impl Fn(serai_runtime::RuntimeEvent) -> Option<E>,
) -> Result<Vec<E>, SeraiError> {
async fn events<E>(&self, filter_map: impl Fn(Event) -> Option<E>) -> Result<Vec<E>, SeraiError> {
let mut res = vec![];
let all_events: Option<
Vec<serai_runtime::system::EventRecord<serai_runtime::RuntimeEvent, [u8; 32]>>,
> = self.storage("System", "Events", ()).await?;
let all_events: Option<Vec<frame_system::EventRecord<Event, [u8; 32]>>> =
self.storage("System", "Events", ()).await?;
#[allow(clippy::unwrap_or_default)]
for event in all_events.unwrap_or(vec![]) {
if let Some(event) = filter_map(event.event) {

View File

@@ -2,15 +2,18 @@ use scale::Encode;
use sp_core::sr25519::{Public, Signature};
use serai_runtime::{primitives::Amount, validator_sets, Runtime};
pub use validator_sets::primitives;
use serai_abi::primitives::Amount;
pub use serai_abi::validator_sets::primitives;
use primitives::{Session, ValidatorSet, KeyPair};
use crate::{primitives::NetworkId, Serai, TemporalSerai, SeraiError};
use crate::{
primitives::{NetworkId, SeraiAddress},
Transaction, Serai, TemporalSerai, SeraiError,
};
const PALLET: &str = "ValidatorSets";
pub type ValidatorSetsEvent = validator_sets::Event<Runtime>;
pub type ValidatorSetsEvent = serai_abi::validator_sets::Event;
#[derive(Clone, Copy)]
pub struct SeraiValidatorSets<'a>(pub(crate) TemporalSerai<'a>);
@@ -23,7 +26,7 @@ impl<'a> SeraiValidatorSets<'a> {
self
.0
.events(|event| {
if let serai_runtime::RuntimeEvent::ValidatorSets(event) = event {
if let serai_abi::Event::ValidatorSets(event) = event {
Some(event).filter(|event| matches!(event, ValidatorSetsEvent::NewSet { .. }))
} else {
None
@@ -36,7 +39,7 @@ impl<'a> SeraiValidatorSets<'a> {
self
.0
.events(|event| {
if let serai_runtime::RuntimeEvent::ValidatorSets(event) = event {
if let serai_abi::Event::ValidatorSets(event) = event {
Some(event).filter(|event| matches!(event, ValidatorSetsEvent::KeyGen { .. }))
} else {
None
@@ -49,7 +52,7 @@ impl<'a> SeraiValidatorSets<'a> {
self
.0
.events(|event| {
if let serai_runtime::RuntimeEvent::ValidatorSets(event) = event {
if let serai_abi::Event::ValidatorSets(event) = event {
Some(event).filter(|event| matches!(event, ValidatorSetsEvent::SetRetired { .. }))
} else {
None
@@ -107,20 +110,22 @@ impl<'a> SeraiValidatorSets<'a> {
self.0.storage(PALLET, "Keys", (sp_core::hashing::twox_64(&set.encode()), set)).await
}
pub fn set_keys(network: NetworkId, key_pair: KeyPair, signature: Signature) -> Vec<u8> {
Serai::unsigned(&serai_runtime::RuntimeCall::ValidatorSets(
validator_sets::Call::<Runtime>::set_keys { network, key_pair, signature },
))
pub fn set_keys(network: NetworkId, key_pair: KeyPair, signature: Signature) -> Transaction {
Serai::unsigned(serai_abi::Call::ValidatorSets(serai_abi::validator_sets::Call::set_keys {
network,
key_pair,
signature,
}))
}
pub fn remove_participant(
network: NetworkId,
to_remove: Public,
signers: Vec<Public>,
to_remove: SeraiAddress,
signers: Vec<SeraiAddress>,
signature: Signature,
) -> Vec<u8> {
Serai::unsigned(&serai_runtime::RuntimeCall::ValidatorSets(
validator_sets::Call::<Runtime>::remove_participant {
) -> Transaction {
Serai::unsigned(serai_abi::Call::ValidatorSets(
serai_abi::validator_sets::Call::remove_participant {
network,
to_remove,
signers,

View File

@@ -67,7 +67,7 @@ serai_test!(
let serai = serai.coins();
assert_eq!(
serai.mint_events().await.unwrap(),
vec![CoinsEvent::Mint { to: address.into(), balance }]
vec![CoinsEvent::Mint { to: address, balance }]
);
assert_eq!(serai.coin_supply(coin).await.unwrap(), amount);
assert_eq!(serai.coin_balance(coin, address).await.unwrap(), amount);

View File

@@ -7,7 +7,7 @@ use blake2::{
use scale::Encode;
use serai_runtime::coins::primitives::OutInstructionWithBalance;
use serai_abi::coins::primitives::OutInstructionWithBalance;
use sp_core::Pair;
use serai_client::{
@@ -68,7 +68,7 @@ serai_test!(
assert_eq!(
serai.coins().mint_events().await.unwrap(),
vec![CoinsEvent::Mint { to: address.into(), balance }]
vec![CoinsEvent::Mint { to: address, balance }]
);
assert_eq!(serai.coins().coin_supply(coin).await.unwrap(), amount);
assert_eq!(serai.coins().coin_balance(coin, address).await.unwrap(), amount);
@@ -86,22 +86,17 @@ serai_test!(
balance,
instruction: OutInstruction { address: external_address, data: Some(data) },
};
let serai = serai.into_inner();
let block = publish_tx(
serai,
&serai
.sign(
&pair,
&SeraiCoins::burn_with_instruction(instruction.clone()),
0,
0,
)
&serai.sign(&pair, SeraiCoins::burn_with_instruction(instruction.clone()), 0, 0),
)
.await;
let serai = serai.as_of(block).coins();
let events = serai.burn_with_instruction_events().await.unwrap();
assert_eq!(events, vec![CoinsEvent::BurnWithInstruction { from: address.into(), instruction }]);
assert_eq!(events, vec![CoinsEvent::BurnWithInstruction { from: address, instruction }]);
assert_eq!(serai.coin_supply(coin).await.unwrap(), Amount(0));
assert_eq!(serai.coin_balance(coin, address).await.unwrap(), Amount(0));
})

View File

@@ -1,4 +1,4 @@
use serai_runtime::primitives::{Coin, Amount};
use serai_abi::primitives::{Coin, Amount};
use serai_client::{Serai, SeraiDex};
use sp_core::{sr25519::Pair, Pair as PairTrait};
@@ -18,7 +18,7 @@ pub async fn add_liquidity(
let tx = serai.sign(
&pair,
&SeraiDex::add_liquidity(coin, coin_amount, sri_amount, Amount(1), Amount(1), address.into()),
SeraiDex::add_liquidity(coin, coin_amount, sri_amount, Amount(1), Amount(1), address.into()),
nonce,
0,
);
@@ -40,7 +40,7 @@ pub async fn swap(
let tx = serai.sign(
&pair,
&SeraiDex::swap(from_coin, to_coin, amount_in, amount_out_min, address.into()),
SeraiDex::swap(from_coin, to_coin, amount_in, amount_out_min, address.into()),
nonce,
Default::default(),
);

View File

@@ -2,12 +2,10 @@ use core::time::Duration;
use tokio::time::sleep;
use scale::Encode;
use serai_client::Serai;
use serai_client::{Transaction, Serai};
#[allow(dead_code)]
pub async fn publish_tx(serai: &Serai, tx: &[u8]) -> [u8; 32] {
pub async fn publish_tx(serai: &Serai, tx: &Transaction) -> [u8; 32] {
let mut latest = serai
.block(serai.latest_finalized_block_hash().await.unwrap())
.await
@@ -39,8 +37,8 @@ pub async fn publish_tx(serai: &Serai, tx: &[u8]) -> [u8; 32] {
block.unwrap()
};
for transaction in block.transactions() {
if transaction.encode() == tx {
for transaction in &block.transactions {
if transaction == tx {
return block.hash();
}
}

View File

@@ -3,7 +3,7 @@ use scale::Encode;
use sp_core::{Pair as PairTrait, bounded_vec::BoundedVec, hashing::blake2_256};
use serai_runtime::in_instructions::primitives::DexCall;
use serai_abi::in_instructions::primitives::DexCall;
use serai_client::{
primitives::{
@@ -37,22 +37,22 @@ serai_test!(
vec![
DexEvent::PoolCreated {
pool_id: Coin::Bitcoin,
pool_account: PublicKey::from_raw(blake2_256(&Coin::Bitcoin.encode())),
pool_account: PublicKey::from_raw(blake2_256(&Coin::Bitcoin.encode())).into(),
lp_token: Coin::Bitcoin,
},
DexEvent::PoolCreated {
pool_id: Coin::Ether,
pool_account: PublicKey::from_raw(blake2_256(&Coin::Ether.encode())),
pool_account: PublicKey::from_raw(blake2_256(&Coin::Ether.encode())).into(),
lp_token: Coin::Ether,
},
DexEvent::PoolCreated {
pool_id: Coin::Dai,
pool_account: PublicKey::from_raw(blake2_256(&Coin::Dai.encode())),
pool_account: PublicKey::from_raw(blake2_256(&Coin::Dai.encode())).into(),
lp_token: Coin::Dai,
},
DexEvent::PoolCreated {
pool_id: Coin::Monero,
pool_account: PublicKey::from_raw(blake2_256(&Coin::Monero.encode())),
pool_account: PublicKey::from_raw(blake2_256(&Coin::Monero.encode())).into(),
lp_token: Coin::Monero,
},
]
@@ -91,8 +91,8 @@ serai_test!(
assert_eq!(
events,
vec![DexEvent::LiquidityAdded {
who: pair.public(),
mint_to: pair.public(),
who: pair.public().into(),
mint_to: pair.public().into(),
pool_id: Coin::Monero,
coin_amount: coin_amount.0,
sri_amount: sri_amount.0,
@@ -140,8 +140,8 @@ serai_test!(
assert_eq!(
events,
vec![DexEvent::SwapExecuted {
who: pair.clone().public(),
send_to: pair.public(),
who: pair.clone().public().into(),
send_to: pair.public().into(),
path,
amount_in: amount_in.0,
amount_out: 16633299966633
@@ -160,8 +160,8 @@ serai_test!(
assert_eq!(
events,
vec![DexEvent::SwapExecuted {
who: pair.clone().public(),
send_to: pair.public(),
who: pair.clone().public().into(),
send_to: pair.public().into(),
path,
amount_in: amount_in.0,
amount_out: 17254428681101
@@ -220,8 +220,8 @@ serai_test!(
assert_eq!(
events,
vec![DexEvent::SwapExecuted {
who: pair.clone().public(),
send_to: pair.public(),
who: pair.clone().public().into(),
send_to: pair.public().into(),
path,
amount_in: amount_in.0,
amount_out: 12453103964435,
@@ -275,8 +275,8 @@ serai_test!(
assert_eq!(
events,
vec![DexEvent::LiquidityAdded {
who: IN_INSTRUCTION_EXECUTOR.into(),
mint_to: pair.public(),
who: IN_INSTRUCTION_EXECUTOR,
mint_to: pair.public().into(),
pool_id: Coin::Bitcoin,
coin_amount: 10_000_000_000_000, // half of sent amount
sri_amount: 6_947_918_403_646,
@@ -363,8 +363,8 @@ serai_test!(
assert_eq!(
events,
vec![DexEvent::SwapExecuted {
who: IN_INSTRUCTION_EXECUTOR.into(),
send_to: IN_INSTRUCTION_EXECUTOR.into(),
who: IN_INSTRUCTION_EXECUTOR,
send_to: IN_INSTRUCTION_EXECUTOR,
path,
amount_in: 20_000_000_000_000,
amount_out: 11066655622377
@@ -402,8 +402,8 @@ serai_test!(
assert_eq!(
events,
vec![DexEvent::SwapExecuted {
who: IN_INSTRUCTION_EXECUTOR.into(),
send_to: out_address.as_native().unwrap().into(),
who: IN_INSTRUCTION_EXECUTOR,
send_to: out_address.as_native().unwrap(),
path,
amount_in: 20_000_000_000_000,
amount_out: 26440798801319
@@ -440,8 +440,8 @@ serai_test!(
assert_eq!(
events,
vec![DexEvent::SwapExecuted {
who: IN_INSTRUCTION_EXECUTOR.into(),
send_to: out_address.as_native().unwrap().into(),
who: IN_INSTRUCTION_EXECUTOR,
send_to: out_address.as_native().unwrap(),
path,
amount_in: 10_000_000_000_000,
amount_out: 10711005507065