diff --git a/Cargo.lock b/Cargo.lock index 441af10269..5f6049a272 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7456,6 +7456,27 @@ dependencies = [ "syn 2.0.79", ] +[[package]] +name = "obake" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c016d34f5be5713bfcaa23668b736865921606c2ddff17e158f0815e4cde091b" +dependencies = [ + "obake_macros", +] + +[[package]] +name = "obake_macros" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47056b8eb01b867a9fdc29f69c05799b9eafb50f73e440cafe624949e27cc7dc" +dependencies = [ + "proc-macro2", + "quote", + "semver 1.0.23", + "syn 1.0.109", +] + [[package]] name = "object" version = "0.30.4" @@ -13063,6 +13084,7 @@ dependencies = [ "hex-literal", "log", "nanorand", + "obake", "pallet-aura", "pallet-authorship", "pallet-cf-account-roles", diff --git a/Cargo.toml b/Cargo.toml index 0a765a32a5..1a6797d065 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -162,6 +162,7 @@ web3 = { version = "0.19" } x25519-dalek = { version = "2.0" } zeroize = { version = "1.7.0" } zmq = { git = "https://github.com/chainflip-io/rust-zmq.git", tag = "chainflip-v0.9.2+1" } +obake = { version = "1.0.5" } # PolkadotSdk Pallets pallet-aura = { git = "https://github.com/chainflip-io/polkadot-sdk.git", tag = "chainflip-substrate-1.15.2+2", default-features = false } diff --git a/state-chain/custom-rpc/src/lib.rs b/state-chain/custom-rpc/src/lib.rs index a8bc0cb48c..2562335e0f 100644 --- a/state-chain/custom-rpc/src/lib.rs +++ b/state-chain/custom-rpc/src/lib.rs @@ -36,7 +36,7 @@ use pallet_cf_pools::{ use pallet_cf_swapping::SwapLegInfo; use sc_client_api::{BlockchainEvents, HeaderBackend}; use serde::{Deserialize, Serialize}; -use sp_api::{ApiError, CallApiAt}; +use sp_api::{ApiError, ApiExt, CallApiAt}; use sp_core::U256; use sp_runtime::{ traits::{Block as BlockT, Header as HeaderT, UniqueSaturatedInto}, @@ -55,10 +55,10 @@ use state_chain_runtime::{ AuctionState, BoostPoolDepth, BoostPoolDetails, BrokerInfo, ChainAccounts, CustomRuntimeApi, DispatchErrorWithMessage, ElectoralRuntimeApi, FailingWitnessValidators, LiquidityProviderBoostPoolInfo, LiquidityProviderInfo, RuntimeApiPenalty, - TaintedTransactionEvents, ValidatorInfo, VaultSwapDetails, + SimulatedSwapInformation, TaintedTransactionEvents, ValidatorInfo, VaultSwapDetails, }, safe_mode::RuntimeSafeMode, - Hash, NetworkFee, SolanaInstance, + Block, Hash, NetworkFee, SolanaInstance, }; use std::{ collections::{BTreeMap, HashMap}, @@ -422,6 +422,26 @@ pub struct RpcSwapOutputV2 { pub egress_fee: RpcFee, pub broker_commission: RpcFee, } +fn into_rpc_swap_output( + simulated_swap_info: SimulatedSwapInformation, + from_asset: Asset, + to_asset: Asset, +) -> RpcSwapOutputV2 { + RpcSwapOutputV2 { + intermediary: simulated_swap_info.intermediary.map(Into::into), + output: simulated_swap_info.output.into(), + network_fee: RpcFee { + asset: cf_primitives::STABLE_ASSET, + amount: simulated_swap_info.network_fee.into(), + }, + ingress_fee: RpcFee { asset: from_asset, amount: simulated_swap_info.ingress_fee.into() }, + egress_fee: RpcFee { asset: to_asset, amount: simulated_swap_info.egress_fee.into() }, + broker_commission: RpcFee { + asset: cf_primitives::STABLE_ASSET, + amount: simulated_swap_info.broker_fee.into(), + }, + } +} #[derive(Serialize, Deserialize, Clone)] pub enum SwapRateV2AdditionalOrder { @@ -1424,71 +1444,66 @@ where additional_orders: Option>, at: Option, ) -> RpcResult { + let amount = amount + .try_into() + .map_err(|_| "Swap input amount too large.") + .and_then(|amount: u128| { + if amount == 0 { + Err("Swap input amount cannot be zero.") + } else { + Ok(amount) + } + }) + .map_err(|s| ErrorObject::owned(ErrorCode::InvalidParams.code(), s, None::<()>))?; + let additional_orders = additional_orders.map(|additional_orders| { + additional_orders + .into_iter() + .map(|additional_order| match additional_order { + SwapRateV2AdditionalOrder::LimitOrder { + base_asset, + quote_asset, + side, + tick, + sell_amount, + } => + state_chain_runtime::runtime_apis::SimulateSwapAdditionalOrder::LimitOrder { + base_asset, + quote_asset, + side, + tick, + sell_amount: sell_amount.unique_saturated_into(), + }, + }) + .collect() + }); self.with_runtime_api(at, |api, hash| { - Ok::<_, CfApiError>( - api.cf_pool_simulate_swap( + if api.api_version::>(hash).unwrap().unwrap() < 2 { + let old_result = api.cf_pool_simulate_swap_before_version_2( hash, from_asset, to_asset, - amount - .try_into() - .map_err(|_| "Swap input amount too large.") - .and_then(|amount: u128| { - if amount == 0 { - Err("Swap input amount cannot be zero.") - } else { - Ok(amount) - } - }) - .map_err(|s| { - ErrorObject::owned(ErrorCode::InvalidParams.code(), s, None::<()>) - })?, - broker_commission, - dca_parameters, - additional_orders.map(|additional_orders| { - additional_orders - .into_iter() - .map(|additional_order| { - match additional_order { - SwapRateV2AdditionalOrder::LimitOrder { - base_asset, - quote_asset, - side, - tick, - sell_amount, - } => state_chain_runtime::runtime_apis::SimulateSwapAdditionalOrder::LimitOrder { - base_asset, - quote_asset, - side, - tick, - sell_amount: sell_amount.unique_saturated_into(), - } - } - }) - .collect() - }), - )? - .map(|simulated_swap_info_v2| RpcSwapOutputV2 { - intermediary: simulated_swap_info_v2.intermediary.map(Into::into), - output: simulated_swap_info_v2.output.into(), - network_fee: RpcFee { - asset: cf_primitives::STABLE_ASSET, - amount: simulated_swap_info_v2.network_fee.into(), - }, - ingress_fee: RpcFee { - asset: from_asset, - amount: simulated_swap_info_v2.ingress_fee.into(), - }, - egress_fee: RpcFee { - asset: to_asset, - amount: simulated_swap_info_v2.egress_fee.into(), - }, - broker_commission: RpcFee { - asset: cf_primitives::STABLE_ASSET, - amount: simulated_swap_info_v2.broker_fee.into(), - }, - })?, - ) + amount, + additional_orders, + )?; + Ok(old_result.map(|old_version| { + into_rpc_swap_output(old_version.into(), from_asset, to_asset) + })?) + } else { + Ok::<_, CfApiError>( + api.cf_pool_simulate_swap( + hash, + from_asset, + to_asset, + amount, + broker_commission, + dca_parameters, + additional_orders, + )? + .map(|simulated_swap_info_v2| { + into_rpc_swap_output(simulated_swap_info_v2, from_asset, to_asset) + })?, + ) + } }) } diff --git a/state-chain/runtime/Cargo.toml b/state-chain/runtime/Cargo.toml index b70460b1a6..c6d04c315a 100644 --- a/state-chain/runtime/Cargo.toml +++ b/state-chain/runtime/Cargo.toml @@ -24,6 +24,7 @@ nanorand = { workspace = true, features = ["wyrand"] } serde = { workspace = true, features = ["derive", "alloc"] } ethabi = { workspace = true } bitvec = { workspace = true } +obake = { workspace = true } # Chainflip local dependencies cf-amm = { workspace = true } diff --git a/state-chain/runtime/src/runtime_apis.rs b/state-chain/runtime/src/runtime_apis.rs index b92a4638e1..fab669a164 100644 --- a/state-chain/runtime/src/runtime_apis.rs +++ b/state-chain/runtime/src/runtime_apis.rs @@ -163,6 +163,9 @@ pub struct BrokerInfo { } /// Struct that represents the estimated output of a Swap. +#[obake::versioned] +#[obake(version("1.0.0"))] +#[obake(version("2.0.0"))] #[derive(Encode, Decode, TypeInfo)] pub struct SimulatedSwapInformation { pub intermediary: Option, @@ -170,9 +173,23 @@ pub struct SimulatedSwapInformation { pub network_fee: AssetAmount, pub ingress_fee: AssetAmount, pub egress_fee: AssetAmount, + #[obake(cfg(">=2.0"))] pub broker_fee: AssetAmount, } +impl From for SimulatedSwapInformation { + fn from(value: SimulatedSwapInformation!["1.0.0"]) -> Self { + Self { + intermediary: value.intermediary, + output: value.output, + network_fee: value.network_fee, + ingress_fee: value.ingress_fee, + egress_fee: value.egress_fee, + broker_fee: Default::default(), + } + } +} + #[derive(Debug, Decode, Encode, TypeInfo)] pub enum DispatchErrorWithMessage { Module(Vec), @@ -299,7 +316,7 @@ decl_runtime_apis!( to: Asset, amount: AssetAmount, additional_limit_orders: Option>, - ) -> Result; + ) -> Result; fn cf_pool_simulate_swap( from: Asset, to: Asset,