mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 04:09:23 +00:00
Remove subxt (#460)
* Remove subxt Removes ~20 crates from our Cargo.lock. Removes downloading the metadata and enables removing the getMetadata RPC route (relevant to #379). Moves forward #337. Done now due to distinctions in the subxt 0.32 API surface which make it justifiable to not update. * fmt, update due to deny triggering on a yanked crate * Correct the handling of substrate_block_notifier now that it's ephemeral, not long-lived * Correct URL in tests/coordinator from ws to http
This commit is contained in:
@@ -17,17 +17,15 @@ rustdoc-args = ["--cfg", "docsrs"]
|
||||
zeroize = "^1.5"
|
||||
thiserror = { version = "1", optional = true }
|
||||
|
||||
futures = "0.3"
|
||||
|
||||
hex = "0.4"
|
||||
scale = { package = "parity-scale-codec", version = "3" }
|
||||
scale-info = { version = "2", optional = true }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
|
||||
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" }
|
||||
|
||||
subxt = { version = "0.29", default-features = false, features = ["jsonrpsee-ws"], optional = true }
|
||||
simple-request = { path = "../../common/request", version = "0.1" }
|
||||
|
||||
bitcoin = { version = "0.31", optional = true }
|
||||
|
||||
@@ -50,7 +48,7 @@ dockertest = "0.4"
|
||||
serai-docker-tests = { path = "../../tests/docker" }
|
||||
|
||||
[features]
|
||||
serai = ["thiserror", "scale-info", "subxt"]
|
||||
serai = ["thiserror"]
|
||||
|
||||
networks = []
|
||||
bitcoin = ["networks", "dep:bitcoin"]
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
use scale::Encode;
|
||||
|
||||
use serai_runtime::{
|
||||
primitives::{SeraiAddress, SubstrateAmount, Amount, Coin, Balance},
|
||||
coins, Coins, Runtime,
|
||||
primitives::{SeraiAddress, Amount, Coin, Balance},
|
||||
coins, Runtime,
|
||||
};
|
||||
pub use coins::primitives;
|
||||
use primitives::OutInstructionWithBalance;
|
||||
|
||||
use crate::{TemporalSerai, SeraiError, scale_value};
|
||||
use crate::{TemporalSerai, SeraiError};
|
||||
|
||||
const PALLET: &str = "Coins";
|
||||
|
||||
@@ -19,21 +21,33 @@ impl<'a> SeraiCoins<'a> {
|
||||
}
|
||||
|
||||
pub async fn mint_events(&self) -> Result<Vec<CoinsEvent>, SeraiError> {
|
||||
self.0.events::<Coins, _>(|event| matches!(event, CoinsEvent::Mint { .. })).await
|
||||
self
|
||||
.0
|
||||
.events(|event| {
|
||||
if let serai_runtime::RuntimeEvent::Coins(event) = event {
|
||||
Some(event).filter(|event| matches!(event, CoinsEvent::Mint { .. }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn burn_with_instruction_events(&self) -> Result<Vec<CoinsEvent>, SeraiError> {
|
||||
self.0.events::<Coins, _>(|event| matches!(event, CoinsEvent::BurnWithInstruction { .. })).await
|
||||
self
|
||||
.0
|
||||
.events(|event| {
|
||||
if let serai_runtime::RuntimeEvent::Coins(event) = event {
|
||||
Some(event).filter(|event| matches!(event, CoinsEvent::BurnWithInstruction { .. }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn coin_supply(&self, coin: Coin) -> Result<Amount, SeraiError> {
|
||||
Ok(Amount(
|
||||
self
|
||||
.0
|
||||
.storage::<SubstrateAmount>(PALLET, "Supply", Some(vec![scale_value(coin)]))
|
||||
.await?
|
||||
.unwrap_or(0),
|
||||
))
|
||||
Ok(self.0.storage(PALLET, "Supply", coin).await?.unwrap_or(Amount(0)))
|
||||
}
|
||||
|
||||
pub async fn coin_balance(
|
||||
@@ -41,17 +55,17 @@ impl<'a> SeraiCoins<'a> {
|
||||
coin: Coin,
|
||||
address: SeraiAddress,
|
||||
) -> Result<Amount, SeraiError> {
|
||||
Ok(Amount(
|
||||
Ok(
|
||||
self
|
||||
.0
|
||||
.storage::<SubstrateAmount>(
|
||||
.storage(
|
||||
PALLET,
|
||||
"Balances",
|
||||
Some(vec![scale_value(address), scale_value(coin)]),
|
||||
(sp_core::hashing::blake2_128(&address.encode()), &address.0, coin),
|
||||
)
|
||||
.await?
|
||||
.unwrap_or(0),
|
||||
))
|
||||
.unwrap_or(Amount(0)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn transfer(to: SeraiAddress, balance: Balance) -> serai_runtime::RuntimeCall {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use sp_core::bounded_vec::BoundedVec;
|
||||
use serai_runtime::{
|
||||
primitives::{SeraiAddress, Amount, Coin},
|
||||
dex, Dex, Runtime,
|
||||
dex, Runtime,
|
||||
};
|
||||
|
||||
use crate::{SeraiError, TemporalSerai};
|
||||
@@ -11,8 +11,19 @@ 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 async fn events(&self) -> Result<Vec<DexEvent>, SeraiError> {
|
||||
self
|
||||
.0
|
||||
.events(
|
||||
|event| {
|
||||
if let serai_runtime::RuntimeEvent::Dex(event) = event {
|
||||
Some(event)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub fn add_liquidity(
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use serai_runtime::{in_instructions, InInstructions, Runtime};
|
||||
use serai_runtime::{in_instructions, Runtime};
|
||||
pub use in_instructions::primitives;
|
||||
use primitives::SignedBatch;
|
||||
|
||||
use crate::{
|
||||
primitives::{BlockHash, NetworkId},
|
||||
SeraiError, Serai, TemporalSerai, scale_value,
|
||||
SeraiError, Serai, TemporalSerai,
|
||||
};
|
||||
|
||||
pub type InInstructionsEvent = in_instructions::Event<Runtime>;
|
||||
@@ -22,20 +22,26 @@ impl<'a> SeraiInInstructions<'a> {
|
||||
&self,
|
||||
network: NetworkId,
|
||||
) -> Result<Option<BlockHash>, SeraiError> {
|
||||
self.0.storage(PALLET, "LatestNetworkBlock", Some(vec![scale_value(network)])).await
|
||||
self.0.storage(PALLET, "LatestNetworkBlock", network).await
|
||||
}
|
||||
|
||||
pub async fn last_batch_for_network(
|
||||
&self,
|
||||
network: NetworkId,
|
||||
) -> Result<Option<u32>, SeraiError> {
|
||||
self.0.storage(PALLET, "LastBatch", Some(vec![scale_value(network)])).await
|
||||
self.0.storage(PALLET, "LastBatch", network).await
|
||||
}
|
||||
|
||||
pub async fn batch_events(&self) -> Result<Vec<InInstructionsEvent>, SeraiError> {
|
||||
self
|
||||
.0
|
||||
.events::<InInstructions, _>(|event| matches!(event, InInstructionsEvent::Batch { .. }))
|
||||
.events(|event| {
|
||||
if let serai_runtime::RuntimeEvent::InInstructions(event) = event {
|
||||
Some(event).filter(|event| matches!(event, InInstructionsEvent::Batch { .. }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
|
||||
@@ -1,36 +1,20 @@
|
||||
use core::ops::Deref;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use futures::stream::{Stream, StreamExt};
|
||||
use simple_request::{hyper, Request, Client};
|
||||
|
||||
use scale::{Encode, Decode, Compact};
|
||||
mod scale_value;
|
||||
pub(crate) use scale_value::{Value, scale_value};
|
||||
use serde::{Serialize, Deserialize, de::DeserializeOwned};
|
||||
|
||||
pub use sp_core::{
|
||||
Pair as PairTrait,
|
||||
sr25519::{Public, Pair},
|
||||
};
|
||||
|
||||
use subxt::{
|
||||
error::Error as SubxtError,
|
||||
config::{
|
||||
Header as HeaderTrait,
|
||||
substrate::{BlakeTwo256, SubstrateHeader},
|
||||
extrinsic_params::BaseExtrinsicParams,
|
||||
},
|
||||
rpc::types::{ChainBlock, ChainBlockExtrinsic},
|
||||
Config as SubxtConfig, OnlineClient,
|
||||
};
|
||||
|
||||
pub use serai_runtime::primitives;
|
||||
pub use primitives::{SeraiAddress, Signature, Amount};
|
||||
|
||||
pub use serai_runtime as runtime;
|
||||
use serai_runtime::{
|
||||
system::Config, support::traits::PalletInfo as PalletInfoTrait, PalletInfo, Runtime,
|
||||
};
|
||||
use serai_runtime::{Header, Block as SeraiBlock};
|
||||
|
||||
pub mod coins;
|
||||
pub use coins::SeraiCoins;
|
||||
@@ -41,42 +25,11 @@ pub use in_instructions::SeraiInInstructions;
|
||||
pub mod validator_sets;
|
||||
pub use validator_sets::SeraiValidatorSets;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Encode, Decode)]
|
||||
pub struct Tip {
|
||||
#[codec(compact)]
|
||||
pub tip: u64,
|
||||
}
|
||||
pub type Transaction = serai_runtime::UncheckedExtrinsic;
|
||||
|
||||
pub type Header = SubstrateHeader<serai_runtime::BlockNumber, BlakeTwo256>;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub struct SeraiConfig;
|
||||
impl SubxtConfig for SeraiConfig {
|
||||
type Hash = <Runtime as Config>::Hash;
|
||||
type Hasher = BlakeTwo256;
|
||||
|
||||
type Index = serai_runtime::Nonce;
|
||||
type AccountId = <Runtime as Config>::AccountId;
|
||||
// TODO: Bech32m
|
||||
type Address = SeraiAddress;
|
||||
|
||||
type Header = Header;
|
||||
type Signature = Signature;
|
||||
|
||||
type ExtrinsicParams = BaseExtrinsicParams<SeraiConfig, Tip>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Block(ChainBlock<SeraiConfig>);
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Block(SeraiBlock);
|
||||
impl Block {
|
||||
fn new(block: ChainBlock<SeraiConfig>) -> Result<Block, SeraiError> {
|
||||
for extrinsic in &block.extrinsics {
|
||||
if extrinsic.0.len() < 3 {
|
||||
Err(SeraiError::InvalidNode)?;
|
||||
}
|
||||
}
|
||||
Ok(Block(block))
|
||||
}
|
||||
pub fn hash(&self) -> [u8; 32] {
|
||||
self.0.header.hash().into()
|
||||
}
|
||||
@@ -87,74 +40,134 @@ impl Block {
|
||||
/// 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 {
|
||||
// Inherent/unsigned
|
||||
let inherent = (extrinsic.0[0] >> 7) == 0;
|
||||
|
||||
// To timestamp pallet
|
||||
use serai_runtime::Timestamp;
|
||||
let timestamp =
|
||||
extrinsic.0[1] == u8::try_from(PalletInfo::index::<Timestamp>().unwrap()).unwrap();
|
||||
|
||||
// set call
|
||||
let set = extrinsic.0[2] == 0;
|
||||
|
||||
if inherent && timestamp && set {
|
||||
if extrinsic.0.len() < 4 {
|
||||
Err(SeraiError::InvalidNode)?;
|
||||
}
|
||||
return Ok(
|
||||
Compact::<u64>::decode(&mut &extrinsic.0[3 ..]).map_err(|_| SeraiError::InvalidNode)?.0,
|
||||
);
|
||||
if let serai_runtime::RuntimeCall::Timestamp(serai_runtime::timestamp::Call::set { now }) =
|
||||
&extrinsic.function
|
||||
{
|
||||
return Ok(*now);
|
||||
}
|
||||
}
|
||||
Err(SeraiError::InvalidNode)
|
||||
Err(SeraiError::InvalidNode("no time was present in block".to_string()))
|
||||
}
|
||||
|
||||
pub fn header(&self) -> &Header {
|
||||
&self.0.header
|
||||
}
|
||||
pub fn transactions(&self) -> &[ChainBlockExtrinsic] {
|
||||
pub fn transactions(&self) -> &[Transaction] {
|
||||
&self.0.extrinsics
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Block {
|
||||
fn clone(&self) -> Block {
|
||||
Block(ChainBlock::<SeraiConfig> {
|
||||
header: self.0.header.clone(),
|
||||
extrinsics: self.0.extrinsics.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum SeraiError {
|
||||
#[error("failed to communicate with serai: {0}")]
|
||||
RpcError(SubxtError),
|
||||
#[error("serai-client library was intended for a different runtime version")]
|
||||
InvalidRuntime,
|
||||
#[error("node is faulty")]
|
||||
InvalidNode,
|
||||
#[error("failed to communicate with serai")]
|
||||
ConnectionError,
|
||||
#[error("node is faulty: {0}")]
|
||||
InvalidNode(String),
|
||||
#[error("error in response: {0}")]
|
||||
ErrorInResponse(String),
|
||||
#[error("serai-client library was intended for a different runtime version: {0}")]
|
||||
InvalidRuntime(String),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Serai(OnlineClient<SeraiConfig>);
|
||||
pub struct Serai {
|
||||
url: String,
|
||||
client: Client,
|
||||
genesis: [u8; 32],
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct TemporalSerai<'a>(pub(crate) &'a Serai, pub(crate) [u8; 32]);
|
||||
|
||||
impl Serai {
|
||||
pub async fn new(url: &str) -> Result<Self, SeraiError> {
|
||||
Ok(Serai(OnlineClient::<SeraiConfig>::from_url(url).await.map_err(SeraiError::RpcError)?))
|
||||
pub async fn call<Req: Serialize, Res: DeserializeOwned>(
|
||||
&self,
|
||||
method: &str,
|
||||
params: Req,
|
||||
) -> Result<Res, SeraiError> {
|
||||
let request = Request::from(
|
||||
hyper::Request::post(&self.url)
|
||||
.header("Content-Type", "application/json")
|
||||
.body(
|
||||
serde_json::to_vec(
|
||||
&serde_json::json!({ "jsonrpc": "2.0", "id": 1, "method": method, "params": params }),
|
||||
)
|
||||
.unwrap()
|
||||
.into(),
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Error {
|
||||
message: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum RpcResponse<T> {
|
||||
Ok { result: T },
|
||||
Err { error: Error },
|
||||
}
|
||||
|
||||
let mut res = self
|
||||
.client
|
||||
.request(request)
|
||||
.await
|
||||
.map_err(|_| SeraiError::ConnectionError)?
|
||||
.body()
|
||||
.await
|
||||
.map_err(|_| SeraiError::ConnectionError)?;
|
||||
|
||||
let res: RpcResponse<Res> = serde_json::from_reader(&mut res).map_err(|e| {
|
||||
SeraiError::InvalidRuntime(format!(
|
||||
"response was a different type than expected: {:?}",
|
||||
e.classify()
|
||||
))
|
||||
})?;
|
||||
match res {
|
||||
RpcResponse::Ok { result } => Ok(result),
|
||||
RpcResponse::Err { error } => Err(SeraiError::ErrorInResponse(error.message)),
|
||||
}
|
||||
}
|
||||
|
||||
fn hex_decode(str: String) -> Result<Vec<u8>, SeraiError> {
|
||||
(if let Some(stripped) = str.strip_prefix("0x") {
|
||||
hex::decode(stripped)
|
||||
} else {
|
||||
hex::decode(str)
|
||||
})
|
||||
.map_err(|_| SeraiError::InvalidNode("expected hex from node wasn't hex".to_string()))
|
||||
}
|
||||
|
||||
pub async fn block_hash(&self, number: u64) -> Result<Option<[u8; 32]>, SeraiError> {
|
||||
let hash: Option<String> = self.call("chain_getBlockHash", [number]).await?;
|
||||
let Some(hash) = hash else { return Ok(None) };
|
||||
Self::hex_decode(hash)?
|
||||
.try_into()
|
||||
.map_err(|_| SeraiError::InvalidNode("didn't respond to getBlockHash with hash".to_string()))
|
||||
.map(Some)
|
||||
}
|
||||
|
||||
pub async fn new(url: String) -> Result<Self, SeraiError> {
|
||||
let client = Client::with_connection_pool();
|
||||
let mut res = Serai { url, client, genesis: [0xfe; 32] };
|
||||
res.genesis = res.block_hash(0).await?.ok_or_else(|| {
|
||||
SeraiError::InvalidNode("node didn't have the first block's hash".to_string())
|
||||
})?;
|
||||
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 bytes = vec![EXTRINSIC_FORMAT_VERSION];
|
||||
bytes.extend(call.encode());
|
||||
bytes
|
||||
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
|
||||
}
|
||||
|
||||
pub fn sign(
|
||||
@@ -162,15 +175,15 @@ impl Serai {
|
||||
signer: &Pair,
|
||||
call: &serai_runtime::RuntimeCall,
|
||||
nonce: u32,
|
||||
tip: Tip,
|
||||
tip: u64,
|
||||
) -> Vec<u8> {
|
||||
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), tip);
|
||||
let genesis = self.0.genesis_hash();
|
||||
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();
|
||||
@@ -180,115 +193,104 @@ impl Serai {
|
||||
let signature = signer.sign(&signature_payload);
|
||||
|
||||
let signed = 1 << 7;
|
||||
(signed + EXTRINSIC_FORMAT_VERSION, signer.public(), signature, extra, call).encode()
|
||||
}
|
||||
let tx = (signed + EXTRINSIC_FORMAT_VERSION, signer.public(), signature, extra, call).encode();
|
||||
|
||||
pub async fn publish(&self, tx: &[u8]) -> Result<(), SeraiError> {
|
||||
let mut length_prefixed = Compact(u32::try_from(tx.len()).unwrap()).encode();
|
||||
length_prefixed.extend(tx);
|
||||
self
|
||||
.0
|
||||
.rpc()
|
||||
.deref()
|
||||
.request::<String>(
|
||||
"author_submitExtrinsic",
|
||||
subxt::rpc::rpc_params![hex::encode(length_prefixed)],
|
||||
)
|
||||
.await
|
||||
// Drop the 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
|
||||
.map(|_| ())
|
||||
.map_err(SeraiError::RpcError)
|
||||
length_prefixed
|
||||
}
|
||||
|
||||
pub async fn latest_block_hash(&self) -> Result<[u8; 32], SeraiError> {
|
||||
Ok(self.0.rpc().finalized_head().await.map_err(SeraiError::RpcError)?.into())
|
||||
// TODO: Move this to take in Transaction
|
||||
pub async fn publish(&self, tx: &[u8]) -> 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?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn latest_block(&self) -> Result<Block, SeraiError> {
|
||||
Block::new(
|
||||
self
|
||||
.0
|
||||
.rpc()
|
||||
.block(Some(self.0.rpc().finalized_head().await.map_err(SeraiError::RpcError)?))
|
||||
.await
|
||||
.map_err(SeraiError::RpcError)?
|
||||
.ok_or(SeraiError::InvalidNode)?
|
||||
.block,
|
||||
)
|
||||
pub async fn latest_finalized_block_hash(&self) -> Result<[u8; 32], SeraiError> {
|
||||
let hash: String = self.call("chain_getFinalizedHead", ()).await?;
|
||||
Self::hex_decode(hash)?.try_into().map_err(|_| {
|
||||
SeraiError::InvalidNode("didn't respond to getFinalizedHead with hash".to_string())
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn header(&self, hash: [u8; 32]) -> Result<Option<Header>, SeraiError> {
|
||||
self.call("chain_getHeader", [hex::encode(hash)]).await
|
||||
}
|
||||
|
||||
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 Some(block) = block else { return Ok(None) };
|
||||
Ok(Some(Block(block.block)))
|
||||
}
|
||||
|
||||
pub async fn latest_finalized_block(&self) -> Result<Block, SeraiError> {
|
||||
let latest = self.latest_finalized_block_hash().await?;
|
||||
let Some(block) = self.block(latest).await? else {
|
||||
Err(SeraiError::InvalidNode("node didn't have a latest block".to_string()))?
|
||||
};
|
||||
Ok(block)
|
||||
}
|
||||
|
||||
// There is no provided method for this
|
||||
// TODO: Add one to Serai
|
||||
pub async fn is_finalized(&self, header: &Header) -> Result<Option<bool>, SeraiError> {
|
||||
pub async fn is_finalized(&self, header: &Header) -> Result<bool, SeraiError> {
|
||||
// Get the latest finalized block
|
||||
let finalized = self.latest_block_hash().await?.into();
|
||||
let finalized = self.latest_finalized_block_hash().await?;
|
||||
// If the latest finalized block is this block, return true
|
||||
if finalized == header.hash() {
|
||||
return Ok(Some(true));
|
||||
if finalized == header.hash().as_ref() {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let Some(finalized) =
|
||||
self.0.rpc().header(Some(finalized)).await.map_err(SeraiError::RpcError)?
|
||||
else {
|
||||
return Ok(None);
|
||||
let Some(finalized) = self.header(finalized).await? else {
|
||||
Err(SeraiError::InvalidNode("couldn't get finalized header".to_string()))?
|
||||
};
|
||||
|
||||
// If the finalized block has a lower number, this block can't be finalized
|
||||
if finalized.number() < header.number() {
|
||||
return Ok(Some(false));
|
||||
if finalized.number < header.number {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
// This block, if finalized, comes before the finalized block
|
||||
// If we request the hash of this block's number, Substrate will return the hash on the main
|
||||
// chain
|
||||
// If that hash is this hash, this block is finalized
|
||||
let Some(hash) =
|
||||
self.0.rpc().block_hash(Some(header.number().into())).await.map_err(SeraiError::RpcError)?
|
||||
else {
|
||||
// This is an error since there is a block at this index
|
||||
Err(SeraiError::InvalidNode)?
|
||||
let Some(hash) = self.block_hash(header.number).await? else {
|
||||
// This is an error since there is a finalized block at this index
|
||||
Err(SeraiError::InvalidNode(
|
||||
"couldn't get block hash for a block number below the finalized block".to_string(),
|
||||
))?
|
||||
};
|
||||
|
||||
Ok(Some(header.hash() == hash))
|
||||
Ok(header.hash().as_ref() == hash)
|
||||
}
|
||||
|
||||
pub async fn block(&self, hash: [u8; 32]) -> Result<Option<Block>, SeraiError> {
|
||||
let Some(res) = self.0.rpc().block(Some(hash.into())).await.map_err(SeraiError::RpcError)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// Only return finalized blocks
|
||||
if self.is_finalized(&res.block.header).await? != Some(true) {
|
||||
pub async fn finalized_block_by_number(&self, number: u64) -> Result<Option<Block>, SeraiError> {
|
||||
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? {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
Ok(Some(Block::new(res.block)?))
|
||||
}
|
||||
|
||||
// Ideally, this would be block_hash, not block_by_number
|
||||
// Unfortunately, in order to only operate over only finalized data, we have to check the
|
||||
// returned hash is for a finalized block. We can only do that by calling the extensive
|
||||
// is_finalized method, which at least requires the header
|
||||
// In practice, the block is likely more useful than the header
|
||||
pub async fn block_by_number(&self, number: u64) -> Result<Option<Block>, SeraiError> {
|
||||
let Some(hash) =
|
||||
self.0.rpc().block_hash(Some(number.into())).await.map_err(SeraiError::RpcError)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
self.block(hash.into()).await
|
||||
Ok(Some(block))
|
||||
}
|
||||
|
||||
/*
|
||||
/// A stream which yields whenever new block(s) have been finalized.
|
||||
pub async fn newly_finalized_block(
|
||||
&self,
|
||||
) -> Result<impl Stream<Item = Result<(), SeraiError>>, SeraiError> {
|
||||
Ok(self.0.rpc().subscribe_finalized_block_headers().await.map_err(SeraiError::RpcError)?.map(
|
||||
Ok(self.0.rpc().subscribe_finalized_block_headers().await
|
||||
.map_err(|_| SeraiError::ConnectionError)?.map(
|
||||
|next| {
|
||||
next.map_err(SeraiError::RpcError)?;
|
||||
next.map_err(|_| SeraiError::ConnectionError)?;
|
||||
Ok(())
|
||||
},
|
||||
))
|
||||
@@ -300,12 +302,16 @@ impl Serai {
|
||||
.rpc()
|
||||
.system_account_next_index(&sp_core::sr25519::Public(address.0).to_string())
|
||||
.await
|
||||
.map_err(SeraiError::RpcError)
|
||||
.map_err(|_| SeraiError::ConnectionError)
|
||||
}
|
||||
*/
|
||||
|
||||
/// Create a TemporalSerai using whatever is currently the latest block.
|
||||
pub async fn with_current_latest_block(&self) -> Result<TemporalSerai, SeraiError> {
|
||||
let latest = self.latest_block_hash().await?;
|
||||
/// Create a TemporalSerai bound to whatever is currently the latest finalized block.
|
||||
///
|
||||
/// The binding occurs at time of call. This does not track the latest finalized block and update
|
||||
/// itself.
|
||||
pub async fn as_of_latest_finalized_block(&self) -> Result<TemporalSerai, SeraiError> {
|
||||
let latest = self.latest_finalized_block_hash().await?;
|
||||
Ok(TemporalSerai(self, latest))
|
||||
}
|
||||
|
||||
@@ -320,43 +326,41 @@ impl<'a> TemporalSerai<'a> {
|
||||
self.0
|
||||
}
|
||||
|
||||
async fn events<P: 'static, E: Decode>(
|
||||
async fn events<E>(
|
||||
&self,
|
||||
filter: impl Fn(&E) -> bool,
|
||||
filter_map: impl Fn(serai_runtime::RuntimeEvent) -> Option<E>,
|
||||
) -> Result<Vec<E>, SeraiError> {
|
||||
let mut res = vec![];
|
||||
for event in self.0 .0.events().at(self.1.into()).await.map_err(SeraiError::RpcError)?.iter() {
|
||||
let event = event.map_err(|_| SeraiError::InvalidRuntime)?;
|
||||
if PalletInfo::index::<P>().unwrap() == usize::from(event.pallet_index()) {
|
||||
let mut with_variant: &[u8] =
|
||||
&[[event.variant_index()].as_ref(), event.field_bytes()].concat();
|
||||
let event = E::decode(&mut with_variant).map_err(|_| SeraiError::InvalidRuntime)?;
|
||||
if filter(&event) {
|
||||
res.push(event);
|
||||
}
|
||||
let all_events: Option<
|
||||
Vec<serai_runtime::system::EventRecord<serai_runtime::RuntimeEvent, [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) {
|
||||
res.push(event);
|
||||
}
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
async fn storage<R: Decode>(
|
||||
async fn storage<K: Encode, R: Decode>(
|
||||
&self,
|
||||
pallet: &'static str,
|
||||
name: &'static str,
|
||||
keys: Option<Vec<Value>>,
|
||||
key: K,
|
||||
) -> Result<Option<R>, SeraiError> {
|
||||
let storage = self.0 .0.storage();
|
||||
#[allow(clippy::unwrap_or_default)]
|
||||
let address = subxt::dynamic::storage(pallet, name, keys.unwrap_or(vec![]));
|
||||
debug_assert!(storage.validate(&address).is_ok(), "invalid storage address");
|
||||
// TODO: Make this const?
|
||||
let mut full_key = sp_core::hashing::twox_128(pallet.as_bytes()).to_vec();
|
||||
full_key.extend(sp_core::hashing::twox_128(name.as_bytes()));
|
||||
full_key.extend(key.encode());
|
||||
|
||||
storage
|
||||
.at(self.1.into())
|
||||
.fetch(&address)
|
||||
.await
|
||||
.map_err(SeraiError::RpcError)?
|
||||
.map(|res| R::decode(&mut res.encoded()).map_err(|_| SeraiError::InvalidRuntime))
|
||||
.transpose()
|
||||
let res: Option<String> =
|
||||
self.0.call("state_getStorage", [hex::encode(full_key), hex::encode(self.1)]).await?;
|
||||
let Some(res) = res else { return Ok(None) };
|
||||
let res = Serai::hex_decode(res)?;
|
||||
Ok(Some(R::decode(&mut res.as_slice()).map_err(|_| {
|
||||
SeraiError::InvalidRuntime("different type present at storage location".to_string())
|
||||
})?))
|
||||
}
|
||||
|
||||
pub fn coins(self) -> SeraiCoins<'a> {
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
use ::scale::Encode;
|
||||
use scale_info::{MetaType, TypeInfo, Registry, PortableRegistry};
|
||||
|
||||
use subxt::ext::scale_value;
|
||||
pub(crate) use scale_value::Value;
|
||||
use scale_value::scale;
|
||||
|
||||
pub(crate) fn scale_value<V: 'static + Encode + TypeInfo>(value: V) -> Value {
|
||||
let mut registry = Registry::new();
|
||||
let id = registry.register_type(&MetaType::new::<V>()).id;
|
||||
let registry = PortableRegistry::from(registry);
|
||||
scale::decode_as_type(&mut value.encode().as_ref(), id, ®istry).unwrap().remove_context()
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
use scale::Encode;
|
||||
|
||||
use sp_core::sr25519::{Public, Signature};
|
||||
|
||||
use serai_runtime::{primitives::Amount, validator_sets, ValidatorSets, Runtime};
|
||||
use serai_runtime::{primitives::Amount, validator_sets, Runtime};
|
||||
pub use validator_sets::primitives;
|
||||
use primitives::{Session, ValidatorSet, KeyPair};
|
||||
|
||||
use crate::{primitives::NetworkId, Serai, TemporalSerai, SeraiError, scale_value};
|
||||
use crate::{primitives::NetworkId, Serai, TemporalSerai, SeraiError};
|
||||
|
||||
const PALLET: &str = "ValidatorSets";
|
||||
|
||||
@@ -20,47 +22,65 @@ impl<'a> SeraiValidatorSets<'a> {
|
||||
pub async fn new_set_events(&self) -> Result<Vec<ValidatorSetsEvent>, SeraiError> {
|
||||
self
|
||||
.0
|
||||
.events::<ValidatorSets, _>(|event| matches!(event, ValidatorSetsEvent::NewSet { .. }))
|
||||
.events(|event| {
|
||||
if let serai_runtime::RuntimeEvent::ValidatorSets(event) = event {
|
||||
Some(event).filter(|event| matches!(event, ValidatorSetsEvent::NewSet { .. }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn key_gen_events(&self) -> Result<Vec<ValidatorSetsEvent>, SeraiError> {
|
||||
self
|
||||
.0
|
||||
.events::<ValidatorSets, _>(|event| matches!(event, ValidatorSetsEvent::KeyGen { .. }))
|
||||
.events(|event| {
|
||||
if let serai_runtime::RuntimeEvent::ValidatorSets(event) = event {
|
||||
Some(event).filter(|event| matches!(event, ValidatorSetsEvent::KeyGen { .. }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn set_retired_events(&self) -> Result<Vec<ValidatorSetsEvent>, SeraiError> {
|
||||
self
|
||||
.0
|
||||
.events::<ValidatorSets, _>(|event| matches!(event, ValidatorSetsEvent::SetRetired { .. }))
|
||||
.events(|event| {
|
||||
if let serai_runtime::RuntimeEvent::ValidatorSets(event) = event {
|
||||
Some(event).filter(|event| matches!(event, ValidatorSetsEvent::SetRetired { .. }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn session(&self, network: NetworkId) -> Result<Option<Session>, SeraiError> {
|
||||
self.0.storage(PALLET, "CurrentSession", Some(vec![scale_value(network)])).await
|
||||
self.0.storage(PALLET, "CurrentSession", network).await
|
||||
}
|
||||
|
||||
pub async fn participants(
|
||||
&self,
|
||||
network: NetworkId,
|
||||
) -> Result<Option<Vec<(Public, u64)>>, SeraiError> {
|
||||
self.0.storage(PALLET, "Participants", Some(vec![scale_value(network)])).await
|
||||
self.0.storage(PALLET, "Participants", network).await
|
||||
}
|
||||
|
||||
pub async fn allocation_per_key_share(
|
||||
&self,
|
||||
network: NetworkId,
|
||||
) -> Result<Option<Amount>, SeraiError> {
|
||||
self.0.storage(PALLET, "AllocationPerKeyShare", Some(vec![scale_value(network)])).await
|
||||
self.0.storage(PALLET, "AllocationPerKeyShare", network).await
|
||||
}
|
||||
|
||||
pub async fn total_allocated_stake(
|
||||
&self,
|
||||
network: NetworkId,
|
||||
) -> Result<Option<Amount>, SeraiError> {
|
||||
self.0.storage(PALLET, "TotalAllocatedStake", Some(vec![scale_value(network)])).await
|
||||
self.0.storage(PALLET, "TotalAllocatedStake", network).await
|
||||
}
|
||||
|
||||
pub async fn allocation(
|
||||
@@ -68,16 +88,23 @@ impl<'a> SeraiValidatorSets<'a> {
|
||||
network: NetworkId,
|
||||
key: Public,
|
||||
) -> Result<Option<Amount>, SeraiError> {
|
||||
self.0.storage(PALLET, "Allocations", Some(vec![scale_value(network), scale_value(key)])).await
|
||||
self
|
||||
.0
|
||||
.storage(
|
||||
PALLET,
|
||||
"Allocations",
|
||||
(sp_core::hashing::blake2_128(&(network, key).encode()), (network, key)),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn musig_key(&self, set: ValidatorSet) -> Result<Option<[u8; 32]>, SeraiError> {
|
||||
self.0.storage(PALLET, "MuSigKeys", Some(vec![scale_value(set)])).await
|
||||
self.0.storage(PALLET, "MuSigKeys", (sp_core::hashing::twox_64(&set.encode()), set)).await
|
||||
}
|
||||
|
||||
// TODO: Store these separately since we almost never need both at once?
|
||||
pub async fn keys(&self, set: ValidatorSet) -> Result<Option<KeyPair>, SeraiError> {
|
||||
self.0.storage(PALLET, "Keys", Some(vec![scale_value(set)])).await
|
||||
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> {
|
||||
|
||||
@@ -50,7 +50,8 @@ serai_test!(
|
||||
let serai = serai.as_of(block);
|
||||
{
|
||||
let serai = serai.in_instructions();
|
||||
assert_eq!(serai.latest_block_for_network(network).await.unwrap(), Some(block_hash));
|
||||
let latest_finalized = serai.latest_block_for_network(network).await.unwrap();
|
||||
assert_eq!(latest_finalized, Some(block_hash));
|
||||
let batches = serai.batch_events().await.unwrap();
|
||||
assert_eq!(
|
||||
batches,
|
||||
|
||||
@@ -94,7 +94,7 @@ serai_test!(
|
||||
&pair,
|
||||
&SeraiCoins::burn_with_instruction(instruction.clone()),
|
||||
0,
|
||||
Default::default(),
|
||||
0,
|
||||
)
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -20,7 +20,7 @@ pub async fn add_liquidity(
|
||||
&pair,
|
||||
&SeraiDex::add_liquidity(coin, coin_amount, sri_amount, Amount(1), Amount(1), address.into()),
|
||||
nonce,
|
||||
Default::default(),
|
||||
0,
|
||||
);
|
||||
|
||||
publish_tx(serai, &tx).await
|
||||
|
||||
@@ -26,7 +26,7 @@ pub async fn provide_batch(serai: &Serai, batch: Batch) -> [u8; 32] {
|
||||
let set = ValidatorSet { session: Session(0), network: batch.network };
|
||||
let pair = insecure_pair_from_name(&format!("ValidatorSet {:?}", set));
|
||||
let keys = if let Some(keys) =
|
||||
serai.with_current_latest_block().await.unwrap().validator_sets().keys(set).await.unwrap()
|
||||
serai.as_of_latest_finalized_block().await.unwrap().validator_sets().keys(set).await.unwrap()
|
||||
{
|
||||
keys
|
||||
} else {
|
||||
|
||||
@@ -42,18 +42,18 @@ macro_rules! serai_test {
|
||||
test.run_async(|ops| async move {
|
||||
// Sleep until the Substrate RPC starts
|
||||
let serai_rpc = ops.handle(handle).host_port(9944).unwrap();
|
||||
let serai_rpc = format!("ws://{}:{}", serai_rpc.0, serai_rpc.1);
|
||||
let serai_rpc = format!("http://{}:{}", serai_rpc.0, serai_rpc.1);
|
||||
// Bound execution to 60 seconds
|
||||
for _ in 0 .. 60 {
|
||||
tokio::time::sleep(core::time::Duration::from_secs(1)).await;
|
||||
let Ok(client) = Serai::new(&serai_rpc).await else { continue };
|
||||
if client.latest_block_hash().await.is_err() {
|
||||
let Ok(client) = Serai::new(serai_rpc.clone()).await else { continue };
|
||||
if client.latest_finalized_block_hash().await.is_err() {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#[allow(clippy::redundant_closure_call)]
|
||||
$test(Serai::new(&serai_rpc).await.unwrap()).await;
|
||||
$test(Serai::new(serai_rpc).await.unwrap()).await;
|
||||
}).await;
|
||||
}
|
||||
)*
|
||||
|
||||
@@ -2,12 +2,18 @@ use core::time::Duration;
|
||||
|
||||
use tokio::time::sleep;
|
||||
|
||||
use scale::Encode;
|
||||
|
||||
use serai_client::Serai;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn publish_tx(serai: &Serai, tx: &[u8]) -> [u8; 32] {
|
||||
let mut latest =
|
||||
serai.block(serai.latest_block_hash().await.unwrap()).await.unwrap().unwrap().number();
|
||||
let mut latest = serai
|
||||
.block(serai.latest_finalized_block_hash().await.unwrap())
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.number();
|
||||
|
||||
serai.publish(tx).await.unwrap();
|
||||
|
||||
@@ -20,7 +26,7 @@ pub async fn publish_tx(serai: &Serai, tx: &[u8]) -> [u8; 32] {
|
||||
let block = {
|
||||
let mut block;
|
||||
while {
|
||||
block = serai.block_by_number(latest).await.unwrap();
|
||||
block = serai.finalized_block_by_number(latest).await.unwrap();
|
||||
block.is_none()
|
||||
} {
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
@@ -34,7 +40,7 @@ pub async fn publish_tx(serai: &Serai, tx: &[u8]) -> [u8; 32] {
|
||||
};
|
||||
|
||||
for transaction in block.transactions() {
|
||||
if transaction.0 == tx {
|
||||
if transaction.encode() == tx {
|
||||
return block.hash();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ pub async fn set_keys(serai: &Serai, set: ValidatorSet, key_pair: KeyPair) -> [u
|
||||
let public_key = <Ristretto as Ciphersuite>::read_G::<&[u8]>(&mut public.0.as_ref()).unwrap();
|
||||
assert_eq!(
|
||||
serai
|
||||
.with_current_latest_block()
|
||||
.as_of_latest_finalized_block()
|
||||
.await
|
||||
.unwrap()
|
||||
.validator_sets()
|
||||
@@ -48,7 +48,7 @@ pub async fn set_keys(serai: &Serai, set: ValidatorSet, key_pair: KeyPair) -> [u
|
||||
musig::<Ristretto>(&musig_context(set), &Zeroizing::new(secret_key), &[public_key]).unwrap();
|
||||
assert_eq!(
|
||||
serai
|
||||
.with_current_latest_block()
|
||||
.as_of_latest_finalized_block()
|
||||
.await
|
||||
.unwrap()
|
||||
.validator_sets()
|
||||
@@ -69,7 +69,7 @@ pub async fn set_keys(serai: &Serai, set: ValidatorSet, key_pair: KeyPair) -> [u
|
||||
&set_keys_message(&set, &key_pair),
|
||||
);
|
||||
|
||||
// Vote in a key pair
|
||||
// Set the key pair
|
||||
let block = publish_tx(
|
||||
serai,
|
||||
&SeraiValidatorSets::set_keys(set.network, key_pair.clone(), Signature(sig.to_bytes())),
|
||||
|
||||
@@ -29,8 +29,8 @@ use common::{
|
||||
// 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();
|
||||
let block = serai.finalized_block_by_number(0).await.unwrap().unwrap().hash();
|
||||
let events = serai.as_of(block).dex().events().await.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
events,
|
||||
@@ -85,7 +85,7 @@ serai_test!(
|
||||
pair.clone()
|
||||
).await;
|
||||
// get only the add liq events
|
||||
let mut events = serai.as_of(block).dex().all_events().await.unwrap();
|
||||
let mut events = serai.as_of(block).dex().events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::LiquidityAdded { .. }));
|
||||
|
||||
assert_eq!(
|
||||
@@ -133,7 +133,7 @@ serai_test!(
|
||||
.await;
|
||||
|
||||
// get only the swap events
|
||||
let mut events = serai.as_of(block).dex().all_events().await.unwrap();
|
||||
let mut events = serai.as_of(block).dex().events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::SwapExecuted { .. }));
|
||||
|
||||
let mut path = BoundedVec::try_from(vec![coin, Coin::Serai]).unwrap();
|
||||
@@ -153,7 +153,7 @@ serai_test!(
|
||||
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();
|
||||
let mut events = serai.as_of(block).dex().events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::SwapExecuted { .. }));
|
||||
|
||||
path = BoundedVec::try_from(vec![Coin::Serai, coin]).unwrap();
|
||||
@@ -213,7 +213,7 @@ serai_test!(
|
||||
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();
|
||||
let mut events = serai.as_of(block).dex().events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::SwapExecuted { .. }));
|
||||
|
||||
let path = BoundedVec::try_from(vec![coin1, Coin::Serai, coin2]).unwrap();
|
||||
@@ -270,7 +270,7 @@ serai_test!(
|
||||
};
|
||||
|
||||
let block = provide_batch(&serai, batch).await;
|
||||
let mut events = serai.as_of(block).dex().all_events().await.unwrap();
|
||||
let mut events = serai.as_of(block).dex().events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::LiquidityAdded { .. }));
|
||||
assert_eq!(
|
||||
events,
|
||||
@@ -356,7 +356,7 @@ serai_test!(
|
||||
|
||||
let block = provide_batch(&serai, batch).await;
|
||||
coin1_batch_id += 1;
|
||||
let mut events = serai.as_of(block).dex().all_events().await.unwrap();
|
||||
let mut events = serai.as_of(block).dex().events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::SwapExecuted { .. }));
|
||||
|
||||
let path = BoundedVec::try_from(vec![coin1, Coin::Serai, coin2]).unwrap();
|
||||
@@ -395,7 +395,7 @@ serai_test!(
|
||||
};
|
||||
|
||||
let block = provide_batch(&serai, batch).await;
|
||||
let mut events = serai.as_of(block).dex().all_events().await.unwrap();
|
||||
let mut events = serai.as_of(block).dex().events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::SwapExecuted { .. }));
|
||||
|
||||
let path = BoundedVec::try_from(vec![coin2, Coin::Serai, coin1]).unwrap();
|
||||
@@ -433,7 +433,7 @@ serai_test!(
|
||||
};
|
||||
|
||||
let block = provide_batch(&serai, batch).await;
|
||||
let mut events = serai.as_of(block).dex().all_events().await.unwrap();
|
||||
let mut events = serai.as_of(block).dex().events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::SwapExecuted { .. }));
|
||||
|
||||
let path = BoundedVec::try_from(vec![coin1, Coin::Serai]).unwrap();
|
||||
|
||||
@@ -8,11 +8,11 @@ mod common;
|
||||
|
||||
serai_test!(
|
||||
time: (|serai: Serai| async move {
|
||||
let mut number = serai.latest_block().await.unwrap().number();
|
||||
let mut number = serai.latest_finalized_block().await.unwrap().number();
|
||||
let mut done = 0;
|
||||
while done < 3 {
|
||||
// Wait for the next block
|
||||
let block = serai.latest_block().await.unwrap();
|
||||
let block = serai.latest_finalized_block().await.unwrap();
|
||||
if block.number() == number {
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
continue;
|
||||
|
||||
@@ -33,7 +33,7 @@ serai_test!(
|
||||
// Make sure the genesis is as expected
|
||||
assert_eq!(
|
||||
serai
|
||||
.as_of(serai.block_by_number(0).await.unwrap().unwrap().hash())
|
||||
.as_of(serai.finalized_block_by_number(0).await.unwrap().unwrap().hash())
|
||||
.validator_sets()
|
||||
.new_set_events()
|
||||
.await
|
||||
@@ -48,7 +48,7 @@ serai_test!(
|
||||
);
|
||||
|
||||
{
|
||||
let vs_serai = serai.with_current_latest_block().await.unwrap().validator_sets();
|
||||
let vs_serai = serai.as_of_latest_finalized_block().await.unwrap().validator_sets();
|
||||
let participants = vs_serai.participants(set.network).await
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
|
||||
Reference in New Issue
Block a user