From f3b62fdad4c933b32d15b544d1e039f8a21d975f Mon Sep 17 00:00:00 2001 From: indirection42 Date: Wed, 4 Jun 2025 22:18:14 +0800 Subject: [PATCH 1/2] update swap extension --- Cargo.lock | 10 ++++ Cargo.toml | 2 + guest-examples/Cargo.lock | 9 +++ guest-examples/Cargo.toml | 1 + guest-examples/test-swap-extension/Cargo.toml | 9 +++ .../test-swap-extension/src/main.rs | 59 +++++++++++++++++++ pvq-executor/src/executor.rs | 1 - pvq-extension-swap/Cargo.toml | 17 ++++++ pvq-extension-swap/src/lib.rs | 34 +++++++++++ pvq-extension/src/executor.rs | 1 + pvq-extension/src/macros.rs | 17 +++++- pvq-test-runner/Cargo.toml | 1 + pvq-test-runner/src/lib.rs | 38 ++++++++++++ vendor/polkadot-sdk | 2 +- 14 files changed, 198 insertions(+), 3 deletions(-) create mode 100644 guest-examples/test-swap-extension/Cargo.toml create mode 100644 guest-examples/test-swap-extension/src/main.rs create mode 100644 pvq-extension-swap/Cargo.toml create mode 100644 pvq-extension-swap/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 1cf0c1d..c3a2cdb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2828,6 +2828,15 @@ dependencies = [ "twox-hash", ] +[[package]] +name = "pvq-extension-swap" +version = "0.1.0" +dependencies = [ + "parity-scale-codec", + "pvq-extension", + "scale-info", +] + [[package]] name = "pvq-primitives" version = "0.1.0" @@ -2891,6 +2900,7 @@ dependencies = [ "pvq-extension", "pvq-extension-core", "pvq-extension-fungibles", + "pvq-extension-swap", "pvq-primitives", "scale-info", "serde", diff --git a/Cargo.toml b/Cargo.toml index d156cd9..ef99987 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [ "pvq-executor", "pvq-extension-core", "pvq-extension-fungibles", + "pvq-extension-swap", "pvq-extension", "pvq-primitives", "pvq-runtime-api", @@ -37,6 +38,7 @@ pvq-program-metadata-gen = { path = "pvq-program-metadata-gen" } pvq-executor = { path = "pvq-executor", default-features = false } pvq-extension-core = { path = "pvq-extension-core", default-features = false } pvq-extension-fungibles = { path = "pvq-extension-fungibles", default-features = false } +pvq-extension-swap = { path = "pvq-extension-swap", default-features = false } pvq-extension = { path = "pvq-extension", default-features = false } pvq-primitives = { path = "pvq-primitives", default-features = false } pvq-runtime-api = { path = "pvq-runtime-api", default-features = false } diff --git a/guest-examples/Cargo.lock b/guest-examples/Cargo.lock index d56ca0e..f082258 100644 --- a/guest-examples/Cargo.lock +++ b/guest-examples/Cargo.lock @@ -93,6 +93,15 @@ dependencies = [ "pvq-program", ] +[[package]] +name = "guest-test-swap-extension" +version = "0.1.0" +dependencies = [ + "parity-scale-codec", + "polkavm-derive", + "pvq-program", +] + [[package]] name = "guest-total-supply" version = "0.1.0" diff --git a/guest-examples/Cargo.toml b/guest-examples/Cargo.toml index 30ff9b9..786e079 100644 --- a/guest-examples/Cargo.toml +++ b/guest-examples/Cargo.toml @@ -6,6 +6,7 @@ members = [ "total-supply", "total-supply-hand-written", "transparent-call-hand-written", + "test-swap-extension", ] resolver = "2" diff --git a/guest-examples/test-swap-extension/Cargo.toml b/guest-examples/test-swap-extension/Cargo.toml new file mode 100644 index 0000000..0a7b090 --- /dev/null +++ b/guest-examples/test-swap-extension/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "guest-test-swap-extension" +version = "0.1.0" +edition = "2021" + +[dependencies] +parity-scale-codec = { workspace = true } +polkavm-derive = { workspace = true } +pvq-program = { workspace = true } diff --git a/guest-examples/test-swap-extension/src/main.rs b/guest-examples/test-swap-extension/src/main.rs new file mode 100644 index 0000000..6210c07 --- /dev/null +++ b/guest-examples/test-swap-extension/src/main.rs @@ -0,0 +1,59 @@ +#![no_std] +#![no_main] + +#[pvq_program::program] +mod query_pools { + + type AssetId = alloc::vec::Vec; + type Balance = u128; + const UNITS: Balance = 1_000_000_000_000; + + #[program::extension_fn(extension_id = 13206387959972970661u64, fn_index = 0)] + fn quote_price_tokens_for_exact_tokens( + asset1: AssetId, + asset2: AssetId, + amount: Balance, + include_fee: bool, + ) -> Option { + } + + #[program::extension_fn(extension_id = 13206387959972970661u64, fn_index = 1)] + fn quote_price_exact_tokens_for_tokens( + asset1: AssetId, + asset2: AssetId, + amount: Balance, + include_fee: bool, + ) -> Option { + } + + #[program::extension_fn(extension_id = 13206387959972970661u64, fn_index = 2)] + fn get_liquidity_pool(asset1: AssetId, asset2: AssetId) -> Option<(Balance, Balance)> {} + + #[program::extension_fn(extension_id = 13206387959972970661u64, fn_index = 3)] + fn list_pools() -> alloc::vec::Vec<(AssetId, AssetId, Balance, Balance)> {} + + #[program::entrypoint] + fn test_swap_extension(asset1: AssetId, asset2: AssetId) -> Option<(Balance, Balance)> { + // Check quote prices + let amount_in = quote_price_tokens_for_exact_tokens(asset1.clone(), asset2.clone(), 10 * UNITS, false) + .expect("Quote price exists"); + + assert!(amount_in == 20 * UNITS); + let amount_out = quote_price_exact_tokens_for_tokens(asset1.clone(), asset2.clone(), 20 * UNITS, false) + .expect("Quote price exists"); + assert!(amount_out == 10 * UNITS); + + // // Check list_pools + let pools = list_pools(); + assert!(pools.len() == 1); + let (pool_asset1, pool_asset2, pool_balance1, pool_balance2) = &pools[0]; + assert!(pool_asset1 == &asset1); + assert!(pool_asset2 == &asset2); + assert!(*pool_balance1 == 100 * UNITS); + assert!(*pool_balance2 == 50 * UNITS); + + // Check get_liquidity_pool + let (balance1, balance2) = get_liquidity_pool(asset1, asset2).expect("Pool exists"); + Some((balance1, balance2)) + } +} diff --git a/pvq-executor/src/executor.rs b/pvq-executor/src/executor.rs index 72e71a8..d2f1862 100644 --- a/pvq-executor/src/executor.rs +++ b/pvq-executor/src/executor.rs @@ -78,7 +78,6 @@ impl PvqExecutor { let result = instance.read_memory(res_ptr, res_size)?; - tracing::info!("Result: {:?}", result); Ok(result) })(); diff --git a/pvq-extension-swap/Cargo.toml b/pvq-extension-swap/Cargo.toml new file mode 100644 index 0000000..c2b7ef3 --- /dev/null +++ b/pvq-extension-swap/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "pvq-extension-swap" +description = "Swap extension for PVQ" +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +parity-scale-codec = { workspace = true } +pvq-extension = { workspace = true } +scale-info = { workspace = true } + +[features] +default = ["std"] +std = ["parity-scale-codec/std", "scale-info/std", "pvq-extension/std"] diff --git a/pvq-extension-swap/src/lib.rs b/pvq-extension-swap/src/lib.rs new file mode 100644 index 0000000..aa4ffb7 --- /dev/null +++ b/pvq-extension-swap/src/lib.rs @@ -0,0 +1,34 @@ +#![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + +use pvq_extension::extension_decl; + +#[extension_decl] +pub mod extension { + use alloc::vec::Vec; + + #[extension_decl::extension] + pub trait ExtensionSwap { + type AssetId; + type Balance; + + fn quote_price_tokens_for_exact_tokens( + asset1: Self::AssetId, + asset2: Self::AssetId, + amount: Self::Balance, + include_fee: bool, + ) -> Option; + + fn quote_price_exact_tokens_for_tokens( + asset1: Self::AssetId, + asset2: Self::AssetId, + amount: Self::Balance, + include_fee: bool, + ) -> Option; + + fn get_liquidity_pool(asset1: Self::AssetId, asset2: Self::AssetId) -> Option<(Self::Balance, Self::Balance)>; + + #[allow(clippy::type_complexity)] + fn list_pools() -> Vec<(Self::AssetId, Self::AssetId, Self::Balance, Self::Balance)>; + } +} diff --git a/pvq-extension/src/executor.rs b/pvq-extension/src/executor.rs index b9840b1..f83fd03 100644 --- a/pvq-extension/src/executor.rs +++ b/pvq-extension/src/executor.rs @@ -38,6 +38,7 @@ impl ExtensionsExecutor { /// The result of the execution or an error pub fn execute(&mut self, program: &[u8], args: &[u8], gas_limit: Option) -> (PvqResult, Option) { let (result, gas_remaining) = self.executor.execute(program, args, gas_limit); + tracing::info!("result: {:?}", result); (result.map_err(PvqError::from), gas_remaining) } } diff --git a/pvq-extension/src/macros.rs b/pvq-extension/src/macros.rs index 4d8e90e..447640a 100644 --- a/pvq-extension/src/macros.rs +++ b/pvq-extension/src/macros.rs @@ -17,8 +17,23 @@ pub trait CallDataTuple { fn dispatch(extension_id: ExtensionIdTy, data: &[u8]) -> Result, ExtensionError>; } -// Use the macro to implement ExtensionTuple for tuples of different lengths +impl CallDataTuple for Member0 +where + Member0: CallData, +{ + fn dispatch(extension_id: ExtensionIdTy, mut data: &[u8]) -> Result, ExtensionError> { + if extension_id == Member0::EXTENSION_ID { + return Member0::decode(&mut data) + .map_err(ExtensionError::DecodeError)? + .dispatch() + .map_err(ExtensionError::DispatchError); + } + Err(ExtensionError::UnsupportedExtension) + } +} + fortuples! { + #[tuples::min_size(1)] impl CallDataTuple for #Tuple where #(#Member: CallData),*{ #[allow(unused_variables)] #[allow(unused_mut)] diff --git a/pvq-test-runner/Cargo.toml b/pvq-test-runner/Cargo.toml index 9d5c327..da12207 100644 --- a/pvq-test-runner/Cargo.toml +++ b/pvq-test-runner/Cargo.toml @@ -21,6 +21,7 @@ pvq-executor = { workspace = true, features = ["std"] } pvq-extension = { workspace = true, features = ["std"] } pvq-extension-core = { workspace = true, features = ["std"] } pvq-extension-fungibles = { workspace = true, features = ["std"] } +pvq-extension-swap = { workspace = true, features = ["std"] } pvq-primitives = { workspace = true, features = ["std"] } polkavm = { workspace = true, features = ["std"] } diff --git a/pvq-test-runner/src/lib.rs b/pvq-test-runner/src/lib.rs index 4174fbe..33fb957 100644 --- a/pvq-test-runner/src/lib.rs +++ b/pvq-test-runner/src/lib.rs @@ -15,6 +15,7 @@ pub enum ExtensionFungiblesFunctions { #[extensions_impl] pub mod extensions { + use parity_scale_codec::Decode; #[extensions_impl::impl_struct] pub struct ExtensionsImpl; @@ -38,6 +39,38 @@ pub mod extensions { 100 } } + #[extensions_impl::extension] + impl pvq_extension_swap::extension::ExtensionSwap for ExtensionsImpl { + type AssetId = Vec; + type Balance = u64; + fn quote_price_tokens_for_exact_tokens( + _asset1: Self::AssetId, + _asset2: Self::AssetId, + _amount: Self::Balance, + _include_fee: bool, + ) -> Option { + None + } + + fn quote_price_exact_tokens_for_tokens( + _asset1: Self::AssetId, + _asset2: Self::AssetId, + _amount: Self::Balance, + _include_fee: bool, + ) -> Option { + None + } + + fn get_liquidity_pool(asset1: Self::AssetId, asset2: Self::AssetId) -> Option<(Self::Balance, Self::Balance)> { + let _asset1 = u32::decode(&mut &asset1[..]).expect("Failed to decode asset1"); + let _asset2 = u32::decode(&mut &asset2[..]).expect("Failed to decode asset2"); + Some((100, 100)) + } + + fn list_pools() -> Vec<(Self::AssetId, Self::AssetId, Self::Balance, Self::Balance)> { + vec![] + } + } } pub struct TestRunner { @@ -75,6 +108,11 @@ impl TestRunner { } .encode(), ); + } else if program_path.contains("liquidity-pool") { + let asset1 = u32::encode(&21); + let asset2 = u32::encode(&22); + input_data.extend_from_slice(&asset1.encode()); + input_data.extend_from_slice(&asset2.encode()); } tracing::info!("Input data (hex): {}", HexDisplay::from(&input_data)); input_data diff --git a/vendor/polkadot-sdk b/vendor/polkadot-sdk index 7b97572..87c2cc3 160000 --- a/vendor/polkadot-sdk +++ b/vendor/polkadot-sdk @@ -1 +1 @@ -Subproject commit 7b97572cc5afb7bf33a453dbe485e6016c2981e0 +Subproject commit 87c2cc3b7b49ee1f213483cf2cf00709b85bc18f From dac594a84723893b123645faae8f8c3affd07f77 Mon Sep 17 00:00:00 2001 From: indirection42 Date: Thu, 5 Jun 2025 08:23:18 +0800 Subject: [PATCH 2/2] update --- vendor/polkadot-sdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/polkadot-sdk b/vendor/polkadot-sdk index 87c2cc3..e0992ee 160000 --- a/vendor/polkadot-sdk +++ b/vendor/polkadot-sdk @@ -1 +1 @@ -Subproject commit 87c2cc3b7b49ee1f213483cf2cf00709b85bc18f +Subproject commit e0992eebf71209dafff233c0c78b0f74240556e8