From 21589936a9c31a87cfe8622a37e26349ea737f0c Mon Sep 17 00:00:00 2001 From: akildemir Date: Sat, 26 Oct 2024 14:25:54 +0300 Subject: [PATCH 1/3] add serai rpc methods --- Cargo.lock | 7 + substrate/client/Cargo.toml | 2 +- substrate/client/src/serai/mod.rs | 21 ++- substrate/client/tests/serai-rpc.rs | 158 +++++++++++++++++++++ substrate/dex/pallet/src/lib.rs | 70 ++++++--- substrate/node/Cargo.toml | 8 ++ substrate/node/src/rpc.rs | 111 ++++++++++++++- substrate/primitives/src/dex.rs | 19 +++ substrate/primitives/src/lib.rs | 4 + substrate/runtime/src/lib.rs | 15 +- substrate/validator-sets/pallet/Cargo.toml | 2 + substrate/validator-sets/pallet/src/lib.rs | 21 +++ 12 files changed, 398 insertions(+), 40 deletions(-) create mode 100644 substrate/client/tests/serai-rpc.rs create mode 100644 substrate/primitives/src/dex.rs diff --git a/Cargo.lock b/Cargo.lock index 72526bc19..93d5bd752 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8347,14 +8347,19 @@ dependencies = [ name = "serai-node" version = "0.1.0" dependencies = [ + "bitcoin-serai", + "ciphersuite", "clap", + "curve25519-dalek", "frame-benchmarking", "futures-util", "hex", "jsonrpsee", "libp2p", "log", + "monero-wallet", "pallet-transaction-payment-rpc", + "parity-scale-codec", "rand_core", "sc-authority-discovery", "sc-basic-authorship", @@ -8375,6 +8380,7 @@ dependencies = [ "schnorrkel", "serai-env", "serai-runtime", + "serde", "sp-api", "sp-block-builder", "sp-blockchain", @@ -8600,6 +8606,7 @@ dependencies = [ "serai-dex-pallet", "serai-primitives", "serai-validator-sets-primitives", + "sp-api", "sp-application-crypto", "sp-core", "sp-io", diff --git a/substrate/client/Cargo.toml b/substrate/client/Cargo.toml index 629312c01..f11845516 100644 --- a/substrate/client/Cargo.toml +++ b/substrate/client/Cargo.toml @@ -47,7 +47,7 @@ hex = "0.4" blake2 = "0.10" -ciphersuite = { path = "../../crypto/ciphersuite", features = ["ristretto"] } +ciphersuite = { path = "../../crypto/ciphersuite", features = ["ristretto", "secp256k1"] } frost = { package = "modular-frost", path = "../../crypto/frost", features = ["tests"] } schnorrkel = { path = "../../crypto/schnorrkel", package = "frost-schnorrkel" } diff --git a/substrate/client/src/serai/mod.rs b/substrate/client/src/serai/mod.rs index c688bf365..b1894df18 100644 --- a/substrate/client/src/serai/mod.rs +++ b/substrate/client/src/serai/mod.rs @@ -16,7 +16,8 @@ pub use abi::{primitives, Transaction}; use abi::*; pub use primitives::{SeraiAddress, Signature, Amount}; -use primitives::{Header, NetworkId}; +use primitives::{Header, NetworkId, ExternalNetworkId, QuotePriceParams}; +use crate::in_instructions::primitives::Shorthand; pub mod coins; pub use coins::SeraiCoins; @@ -317,6 +318,24 @@ impl Serai { ) -> Result, SeraiError> { self.call("p2p_validators", network).await } + + // TODO: move this to SeraiValidatorSets? + pub async fn external_network_address( + &self, + network: ExternalNetworkId, + ) -> Result { + self.call("external_network_address", network).await + } + + // TODO: move this to SeraiInInstructions? + pub async fn encoded_shorthand(&self, shorthand: Shorthand) -> Result, SeraiError> { + self.call("encoded_shorthand", shorthand).await + } + + // TODO: move this to SeraiDex? + pub async fn quote_price(&self, params: QuotePriceParams) -> Result { + self.call("quote_price", params).await + } } impl<'a> TemporalSerai<'a> { diff --git a/substrate/client/tests/serai-rpc.rs b/substrate/client/tests/serai-rpc.rs new file mode 100644 index 000000000..ec398bfee --- /dev/null +++ b/substrate/client/tests/serai-rpc.rs @@ -0,0 +1,158 @@ +use std::str::FromStr; + +use scale::Decode; +use zeroize::Zeroizing; + +use ciphersuite::{ + group::{ff::Field, GroupEncoding}, + Ciphersuite, Ed25519, Secp256k1, +}; + +use sp_core::{ + Pair as PairTrait, + sr25519::{Public, Pair}, +}; + +use serai_abi::{ + in_instructions::primitives::Shorthand, + primitives::{ + insecure_pair_from_name, ExternalBalance, ExternalCoin, ExternalNetworkId, QuotePriceParams, + Amount, + }, + validator_sets::primitives::{ExternalValidatorSet, KeyPair, Session}, +}; +use serai_client::{Serai, SeraiAddress}; + +use rand_core::{RngCore, OsRng}; + +mod common; +use common::{validator_sets::set_keys, in_instructions::mint_coin, dex::add_liquidity}; + +serai_test!( + external_address: (|serai: Serai| async move { + test_external_address(serai).await; + }) + + encoded_shorthand: (|serai: Serai| async move { + test_encoded_shorthand(serai).await; + }) + + dex_quote_price: (|serai: Serai| async move { + test_dex_quote_price(serai).await; + }) +); + +async fn set_network_keys( + serai: &Serai, + set: ExternalValidatorSet, + pairs: &[Pair], +) { + // Ristretto key + let mut ristretto_key = [0; 32]; + OsRng.fill_bytes(&mut ristretto_key); + + // network key + let network_priv_key = Zeroizing::new(C::F::random(&mut OsRng)); + let network_key = (C::generator() * *network_priv_key).to_bytes().as_ref().to_vec(); + + let key_pair = KeyPair(Public(ristretto_key), network_key.try_into().unwrap()); + let _ = set_keys(serai, set, key_pair, pairs).await; +} + +async fn test_external_address(serai: Serai) { + let pair = insecure_pair_from_name("Alice"); + + // set btc keys + let network = ExternalNetworkId::Bitcoin; + set_network_keys::( + &serai, + ExternalValidatorSet { session: Session(0), network }, + &[pair.clone()], + ) + .await; + + // get the address from the node + let btc_address: String = serai.external_network_address(network).await.unwrap(); + + // make sure it is a valid address + let _ = bitcoin::Address::from_str(&btc_address) + .unwrap() + .require_network(bitcoin::Network::Bitcoin) + .unwrap(); + + // set monero keys + let network = ExternalNetworkId::Monero; + set_network_keys::( + &serai, + ExternalValidatorSet { session: Session(0), network }, + &[pair], + ) + .await; + + // get the address from the node + let xmr_address: String = serai.external_network_address(network).await.unwrap(); + + // make sure it is a valid address + let _ = monero_wallet::address::MoneroAddress::from_str( + monero_wallet::address::Network::Mainnet, + &xmr_address, + ) + .unwrap(); +} + +async fn test_encoded_shorthand(serai: Serai) { + let shorthand = Shorthand::transfer(None, SeraiAddress::new([0u8; 32])); + let encoded = serai.encoded_shorthand(shorthand.clone()).await.unwrap(); + + assert_eq!(Shorthand::decode::<&[u8]>(&mut encoded.as_slice()).unwrap(), shorthand); +} + +async fn test_dex_quote_price(serai: Serai) { + // make a liquid pool to get the quote on + let coin1 = ExternalCoin::Bitcoin; + let coin2 = ExternalCoin::Monero; + let amount1 = Amount(10u64.pow(coin1.decimals())); + let amount2 = Amount(10u64.pow(coin2.decimals())); + let pair = insecure_pair_from_name("Ferdie"); + + // mint sriBTC in the account so that we can add liq. + // Ferdie account is already pre-funded with SRI. + mint_coin( + &serai, + ExternalBalance { coin: coin1, amount: amount1 }, + 0, + pair.clone().public().into(), + ) + .await; + + // add liquidity + let coin_amount = Amount(amount1.0 / 2); + let sri_amount = Amount(amount1.0 / 2); + let _ = add_liquidity(&serai, coin1, coin_amount, sri_amount, 0, pair.clone()).await; + + // same for xmr + mint_coin( + &serai, + ExternalBalance { coin: coin2, amount: amount2 }, + 0, + pair.clone().public().into(), + ) + .await; + + // add liquidity + let coin_amount = Amount(amount2.0 / 2); + let sri_amount = Amount(amount2.0 / 2); + let _ = add_liquidity(&serai, coin2, coin_amount, sri_amount, 1, pair.clone()).await; + + // price for BTC -> SRI -> XMR path + let params = QuotePriceParams { + coin1: coin1.into(), + coin2: coin2.into(), + amount: coin_amount.0 / 2, + include_fee: true, + exact_in: true, + }; + + let res = serai.quote_price(params).await.unwrap(); + assert!(res > 0); +} diff --git a/substrate/dex/pallet/src/lib.rs b/substrate/dex/pallet/src/lib.rs index 8da63a7be..6d8139912 100644 --- a/substrate/dex/pallet/src/lib.rs +++ b/substrate/dex/pallet/src/lib.rs @@ -999,6 +999,25 @@ pub mod pallet { Ok(amounts) } + fn get_swap_path_from_coins( + coin1: Coin, + coin2: Coin, + ) -> Option> { + if coin1 == coin2 { + return None; + } + + let path = if coin1 == Coin::native() { + vec![coin2, coin1] + } else if coin2 == Coin::native() { + vec![coin1, coin2] + } else { + vec![coin1, Coin::native(), coin2] + }; + + Some(path.try_into().unwrap()) + } + /// Used by the RPC service to provide current prices. pub fn quote_price_exact_tokens_for_tokens( coin1: Coin, @@ -1006,20 +1025,24 @@ pub mod pallet { amount: SubstrateAmount, include_fee: bool, ) -> Option { - let pool_id = Self::get_pool_id(coin1, coin2).ok()?; - let pool_account = Self::get_pool_account(pool_id); + let path = Self::get_swap_path_from_coins(coin1, coin2)?; - let balance1 = Self::get_balance(&pool_account, coin1); - let balance2 = Self::get_balance(&pool_account, coin2); - if balance1 != 0 { - if include_fee { - Self::get_amount_out(amount, balance1, balance2).ok() - } else { - Self::quote(amount, balance1, balance2).ok() + let mut amounts: Vec = vec![amount]; + for coins_pair in path.windows(2) { + if let [coin1, coin2] = coins_pair { + let (reserve_in, reserve_out) = Self::get_reserves(coin1, coin2).ok()?; + let prev_amount = amounts.last().expect("Always has at least one element"); + let amount_out = if include_fee { + Self::get_amount_out(*prev_amount, reserve_in, reserve_out).ok()? + } else { + Self::quote(*prev_amount, reserve_in, reserve_out).ok()? + }; + + amounts.push(amount_out); } - } else { - None } + + Some(*amounts.last().unwrap()) } /// Used by the RPC service to provide current prices. @@ -1029,20 +1052,23 @@ pub mod pallet { amount: SubstrateAmount, include_fee: bool, ) -> Option { - let pool_id = Self::get_pool_id(coin1, coin2).ok()?; - let pool_account = Self::get_pool_account(pool_id); + let path = Self::get_swap_path_from_coins(coin1, coin2)?; - let balance1 = Self::get_balance(&pool_account, coin1); - let balance2 = Self::get_balance(&pool_account, coin2); - if balance1 != 0 { - if include_fee { - Self::get_amount_in(amount, balance1, balance2).ok() - } else { - Self::quote(amount, balance2, balance1).ok() + let mut amounts: Vec = vec![amount]; + for coins_pair in path.windows(2).rev() { + if let [coin1, coin2] = coins_pair { + let (reserve_in, reserve_out) = Self::get_reserves(coin1, coin2).ok()?; + let prev_amount = amounts.last().expect("Always has at least one element"); + let amount_in = if include_fee { + Self::get_amount_in(*prev_amount, reserve_in, reserve_out).ok()? + } else { + Self::quote(*prev_amount, reserve_out, reserve_in).ok()? + }; + amounts.push(amount_in); } - } else { - None } + + Some(*amounts.last().unwrap()) } /// Calculates the optimal amount from the reserves. diff --git a/substrate/node/Cargo.toml b/substrate/node/Cargo.toml index 0e551c72b..1e177d1fb 100644 --- a/substrate/node/Cargo.toml +++ b/substrate/node/Cargo.toml @@ -48,6 +48,9 @@ futures-util = "0.3" tokio = { version = "1", features = ["sync", "rt-multi-thread"] } jsonrpsee = { version = "0.16", features = ["server"] } +scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } +serde = { version = "1", default-features = false, features = ["derive", "alloc"] } + sc-offchain = { git = "https://github.com/serai-dex/substrate" } sc-transaction-pool = { git = "https://github.com/serai-dex/substrate" } sc-transaction-pool-api = { git = "https://github.com/serai-dex/substrate" } @@ -73,6 +76,11 @@ pallet-transaction-payment-rpc = { git = "https://github.com/serai-dex/substrate serai-env = { path = "../../common/env" } +bitcoin-serai = { path = "../../networks/bitcoin", default-features = false, features = ["std", "hazmat"] } +monero-wallet = { path = "../../networks/monero/wallet", default-features = false, features = ["std"] } +ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["ed25519", "secp256k1"] } +curve25519-dalek = { version = "4", default-features = false, features = ["alloc", "zeroize"] } + [build-dependencies] substrate-build-script-utils = { git = "https://github.com/serai-dex/substrate" } diff --git a/substrate/node/src/rpc.rs b/substrate/node/src/rpc.rs index b818c7981..77e6e2650 100644 --- a/substrate/node/src/rpc.rs +++ b/substrate/node/src/rpc.rs @@ -1,4 +1,4 @@ -use std::{sync::Arc, collections::HashSet}; +use std::{sync::Arc, ops::Deref, collections::HashSet}; use rand_core::{RngCore, OsRng}; @@ -7,13 +7,17 @@ use sp_block_builder::BlockBuilder; use sp_api::ProvideRuntimeApi; use serai_runtime::{ - primitives::{NetworkId, SubstrateAmount, PublicKey}, - Nonce, Block, SeraiRuntimeApi, + in_instructions::primitives::Shorthand, + primitives::{ExternalNetworkId, NetworkId, PublicKey, SubstrateAmount, QuotePriceParams}, + validator_sets::ValidatorSetsApi, + dex::DexApi, + Block, Nonce, }; use tokio::sync::RwLock; -use jsonrpsee::RpcModule; +use jsonrpsee::{RpcModule, core::Error}; +use scale::Encode; pub use sc_rpc_api::DenyUnsafe; use sc_transaction_pool_api::TransactionPool; @@ -40,11 +44,14 @@ pub fn create_full< where C::Api: substrate_frame_rpc_system::AccountNonceApi + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi - + SeraiRuntimeApi + + ValidatorSetsApi + + DexApi + BlockBuilder, { use substrate_frame_rpc_system::{System, SystemApiServer}; use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; + use ciphersuite::{Ciphersuite, Ed25519, Secp256k1}; + use bitcoin_serai::{bitcoin, crypto::x_only}; let mut module = RpcModule::new(()); let FullDeps { id, client, pool, deny_unsafe, authority_discovery } = deps; @@ -54,7 +61,7 @@ where if let Some(authority_discovery) = authority_discovery { let mut authority_discovery_module = - RpcModule::new((id, client, RwLock::new(authority_discovery))); + RpcModule::new((id, client.clone(), RwLock::new(authority_discovery))); authority_discovery_module.register_async_method( "p2p_validators", |params, context| async move { @@ -63,7 +70,7 @@ where let latest_block = client.info().best_hash; let validators = client.runtime_api().validators(latest_block, network).map_err(|_| { - jsonrpsee::core::Error::to_call_error(std::io::Error::other(format!( + Error::to_call_error(std::io::Error::other(format!( "couldn't get validators from the latest block, which is likely a fatal bug. {}", "please report this at https://github.com/serai-dex/serai", ))) @@ -99,5 +106,95 @@ where module.merge(authority_discovery_module)?; } + let mut serai_json_module = RpcModule::new(client); + + // add network address rpc + serai_json_module.register_async_method( + "external_network_address", + |params, context| async move { + let network: ExternalNetworkId = params.parse()?; + let client = &*context; + let latest_block = client.info().best_hash; + + let external_key = client + .runtime_api() + .external_network_key(latest_block, network) + .map_err(|_| Error::Custom("api call error".to_string()))? + .ok_or(Error::Custom("no address for the network".to_string()))?; + + match network { + ExternalNetworkId::Bitcoin => { + let key = ::read_G::<&[u8]>(&mut external_key.as_slice()) + .map_err(|_| Error::Custom("invalid key stored in db".to_string()))?; + + let addr = bitcoin::Address::p2tr_tweaked( + bitcoin::key::TweakedPublicKey::dangerous_assume_tweaked(x_only(&key)), + bitcoin::address::KnownHrp::Mainnet, + ); + + Ok(addr.to_string()) + } + // We don't know the eth address before the smart contract is deployed. + ExternalNetworkId::Ethereum => Ok(String::new()), + ExternalNetworkId::Monero => { + let view_private = zeroize::Zeroizing::new( + ::hash_to_F( + b"Serai DEX Additional Key", + &["Monero".as_bytes(), &0u64.to_le_bytes()].concat(), + ) + .0, + ); + + let spend = ::read_G::<&[u8]>(&mut external_key.as_slice()) + .map_err(|_| Error::Custom("invalid key stored in db".to_string()))?; + + let addr = monero_wallet::address::MoneroAddress::new( + monero_wallet::address::Network::Mainnet, + monero_wallet::address::AddressType::Featured { + subaddress: false, + payment_id: None, + guaranteed: true, + }, + *spend, + view_private.deref() * curve25519_dalek::constants::ED25519_BASEPOINT_TABLE, + ); + + Ok(addr.to_string()) + } + } + }, + )?; + + // add shorthand encoding rpc + serai_json_module.register_async_method("encoded_shorthand", |params, _| async move { + // decode using serde and encode back using scale + let shorthand: Shorthand = params.parse()?; + Ok(shorthand.encode()) + })?; + + // add simulating a swap path rpc + serai_json_module.register_async_method("quote_price", |params, context| async move { + let client = &*context; + let latest_block = client.info().best_hash; + let QuotePriceParams { coin1, coin2, amount, include_fee, exact_in } = params.parse()?; + + let amount = if exact_in { + client + .runtime_api() + .quote_price_exact_tokens_for_tokens(latest_block, coin1, coin2, amount, include_fee) + .map_err(|_| Error::Custom("api call error".to_string()))? + .ok_or(Error::Custom("invalid params or empty pool".to_string()))? + } else { + client + .runtime_api() + .quote_price_tokens_for_exact_tokens(latest_block, coin1, coin2, amount, include_fee) + .map_err(|_| Error::Custom("api call error".to_string()))? + .ok_or(Error::Custom("invalid params or empty pool".to_string()))? + }; + + Ok(amount) + })?; + + module.merge(serai_json_module)?; Ok(module) } diff --git a/substrate/primitives/src/dex.rs b/substrate/primitives/src/dex.rs new file mode 100644 index 000000000..4dd9de42f --- /dev/null +++ b/substrate/primitives/src/dex.rs @@ -0,0 +1,19 @@ +#[cfg(feature = "borsh")] +use borsh::{BorshSerialize, BorshDeserialize}; +#[cfg(feature = "serde")] +use serde::{Serialize, Deserialize}; + +use scale::{Encode, Decode, MaxEncodedLen}; + +use crate::{Coin, SubstrateAmount}; + +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode, MaxEncodedLen)] +#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct QuotePriceParams { + pub coin1: Coin, + pub coin2: Coin, + pub amount: SubstrateAmount, + pub include_fee: bool, + pub exact_in: bool, +} diff --git a/substrate/primitives/src/lib.rs b/substrate/primitives/src/lib.rs index d2c52219e..a92fa7e0b 100644 --- a/substrate/primitives/src/lib.rs +++ b/substrate/primitives/src/lib.rs @@ -40,6 +40,10 @@ pub use account::*; mod constants; pub use constants::*; +mod dex; +#[allow(unused_imports)] +pub use dex::*; + pub type BlockNumber = u64; pub type Header = sp_runtime::generic::Header; diff --git a/substrate/runtime/src/lib.rs b/substrate/runtime/src/lib.rs index 151242712..2ef3786d7 100644 --- a/substrate/runtime/src/lib.rs +++ b/substrate/runtime/src/lib.rs @@ -53,7 +53,7 @@ use sp_runtime::{ #[allow(unused_imports)] use primitives::{ - NetworkId, PublicKey, AccountLookup, SubstrateAmount, Coin, EXTERNAL_NETWORKS, + NetworkId, ExternalNetworkId, PublicKey, AccountLookup, SubstrateAmount, Coin, EXTERNAL_NETWORKS, MEDIAN_PRICE_WINDOW_LENGTH, HOURS, DAYS, MINUTES, TARGET_BLOCK_TIME, BLOCK_SIZE, FAST_EPOCH_DURATION, }; @@ -374,13 +374,6 @@ mod benches { ); } -sp_api::decl_runtime_apis! { - #[api_version(1)] - pub trait SeraiRuntimeApi { - fn validators(network_id: NetworkId) -> Vec; - } -} - sp_api::impl_runtime_apis! { impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { @@ -589,7 +582,7 @@ sp_api::impl_runtime_apis! { } } - impl crate::SeraiRuntimeApi for Runtime { + impl validator_sets::ValidatorSetsApi for Runtime { fn validators(network_id: NetworkId) -> Vec { if network_id == NetworkId::Serai { Babe::authorities() @@ -604,6 +597,10 @@ sp_api::impl_runtime_apis! { ) } } + + fn external_network_key(network: ExternalNetworkId) -> Option> { + ValidatorSets::external_network_key(network) + } } impl dex::DexApi for Runtime { diff --git a/substrate/validator-sets/pallet/Cargo.toml b/substrate/validator-sets/pallet/Cargo.toml index dd67d1bc3..c4c748b28 100644 --- a/substrate/validator-sets/pallet/Cargo.toml +++ b/substrate/validator-sets/pallet/Cargo.toml @@ -27,6 +27,7 @@ scale-info = { version = "2", default-features = false, features = ["derive"] } sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false } sp-io = { git = "https://github.com/serai-dex/substrate", default-features = false } sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false } +sp-api = { git = "https://github.com/serai-dex/substrate", default-features = false } sp-application-crypto = { git = "https://github.com/serai-dex/substrate", default-features = false } sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false } sp-session = { git = "https://github.com/serai-dex/substrate", default-features = false } @@ -52,6 +53,7 @@ std = [ "sp-core/std", "sp-io/std", "sp-std/std", + "sp-api/std", "sp-application-crypto/std", "sp-runtime/std", "sp-session/std", diff --git a/substrate/validator-sets/pallet/src/lib.rs b/substrate/validator-sets/pallet/src/lib.rs index 383f94a7d..3100c60eb 100644 --- a/substrate/validator-sets/pallet/src/lib.rs +++ b/substrate/validator-sets/pallet/src/lib.rs @@ -943,6 +943,14 @@ pub mod pallet { false } } + + /// Returns the external network key for a given external network + pub fn external_network_key(network: ExternalNetworkId) -> Option> { + let current_session = Self::session(NetworkId::from(network))?; + let keys = Keys::::get(ExternalValidatorSet { network, session: current_session })?; + + Some(keys.1.into_inner()) + } } #[pallet::call] @@ -1295,4 +1303,17 @@ pub mod pallet { } } +sp_api::decl_runtime_apis! { + #[api_version(1)] + pub trait ValidatorSetsApi { + /// Returns the validator set for a given network. + fn validators(network_id: NetworkId) -> Vec; + + /// Returns the external network key for a given external network. + fn external_network_key( + network: ExternalNetworkId, + ) -> Option>; + } +} + pub use pallet::*; From b0d00cf7ea666216a1d154d6c7b9882305ccbf1c Mon Sep 17 00:00:00 2001 From: akildemir Date: Mon, 28 Oct 2024 12:41:39 +0300 Subject: [PATCH 2/3] fix machete & dex quote price api --- Cargo.lock | 1 - substrate/dex/pallet/src/lib.rs | 6 +++--- substrate/node/Cargo.toml | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 93d5bd752..bf320e6fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8380,7 +8380,6 @@ dependencies = [ "schnorrkel", "serai-env", "serai-runtime", - "serde", "sp-api", "sp-block-builder", "sp-blockchain", diff --git a/substrate/dex/pallet/src/lib.rs b/substrate/dex/pallet/src/lib.rs index 6d8139912..71d105496 100644 --- a/substrate/dex/pallet/src/lib.rs +++ b/substrate/dex/pallet/src/lib.rs @@ -1007,9 +1007,9 @@ pub mod pallet { return None; } - let path = if coin1 == Coin::native() { - vec![coin2, coin1] - } else if coin2 == Coin::native() { + let path = if (coin1 == Coin::native() && coin2 != Coin::native()) || + (coin2 == Coin::native() && coin1 != Coin::native()) + { vec![coin1, coin2] } else { vec![coin1, Coin::native(), coin2] diff --git a/substrate/node/Cargo.toml b/substrate/node/Cargo.toml index 1e177d1fb..4ab04fde0 100644 --- a/substrate/node/Cargo.toml +++ b/substrate/node/Cargo.toml @@ -49,7 +49,6 @@ tokio = { version = "1", features = ["sync", "rt-multi-thread"] } jsonrpsee = { version = "0.16", features = ["server"] } scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } -serde = { version = "1", default-features = false, features = ["derive", "alloc"] } sc-offchain = { git = "https://github.com/serai-dex/substrate" } sc-transaction-pool = { git = "https://github.com/serai-dex/substrate" } From ec1fb9cf8d0a0ac9e7d458ab98e14a721b0db214 Mon Sep 17 00:00:00 2001 From: akildemir Date: Tue, 29 Oct 2024 10:37:15 +0300 Subject: [PATCH 3/3] fix validators api --- substrate/client/src/serai/validator_sets.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/client/src/serai/validator_sets.rs b/substrate/client/src/serai/validator_sets.rs index 5bfb3ed53..07339ef88 100644 --- a/substrate/client/src/serai/validator_sets.rs +++ b/substrate/client/src/serai/validator_sets.rs @@ -163,7 +163,7 @@ impl<'a> SeraiValidatorSets<'a> { &self, network: NetworkId, ) -> Result, SeraiError> { - self.0.runtime_api("SeraiRuntimeApi_validators", network).await + self.0.runtime_api("ValidatorSetsApi_validators", network).await } // TODO: Store these separately since we almost never need both at once?