We’ve built a simple swap demo showcases how PVQ can facilitate the development of multi-chain Dapps. It has the following core functions:
-
List Liquidity Pools: Displays the available token pair pools on the selected chain.
-
Get Pool Reserves: Shows the current liquidity reserves for a selected pool.
-
Get Swap Price: Show price quote for swapping a specific amount of one token for another.
The main takeaway is that the demo works seamlessly with both Acala and AssetHub, using the exact same front-end logic despite the DEX implementations on these chains being entirely different:
-
Acala: Uses its own custom
module-dexpallet. -
AssetHub: Uses the
AssetConversionpallet.
How It Works
Front-end side
From the client’s perspective, the process is simple:
-
Check for Extension: The UI first checks if the connected chain (Acala or AssetHub) implements the
swapextension. -
Construct PVQ program query arguments: If the extension is available, the UI constructs its query with the required arguments (e.g., token pair, amount to swap).
-
Call PVQ program Entrypoints: The UI sends the query to the entrypoints of the PVQ program.
The front-end code is completely agnostic of whether it’s talking to Acala’s DEX pallet or AssetHub’s AssetConversion pallet.
Runtime side
The heavy lifting is done at the runtime side, which is tailored for each chain. We implement swap extension on both Acala and AssetHub, which exposes five extension functions: quote_price_tokens_for_exact_tokens, quote_price_exact_tokens_for_tokens, get_liquidity_pool, list_pools, asset_info, assets_info.
PVQ Program
For the corresponding PVQ program, it only has four entrypoints. This is by design and demonstrates one of PVQ’s most powerful features: the ability to perform custom computations in PVQ program.
For example, the list_pools entrypoint is actually a composition of extension functions list_pools and assets_info:
#[program::entrypoint]
fn entrypoint_list_pools() -> Vec<(AssetInfo, AssetInfo)> {
let pools = list_pools();
let mut result = Vec::new();
let assets_info = assets_info();
for pool in pools {
let asset1_info = assets_info.get(&pool.0).cloned();
let asset2_info = assets_info.get(&pool.1).cloned();
if let (Some(a1), Some(a2)) = (asset1_info, asset2_info) {
result.push((a1, a2));
}
}
result
}
Key Advancement
This demo is a concrete validation of the PVQ vision. We have successfully created a unified, high-level interface for a non-trivial operation (DEX interactions) across two parachains with fundamentally different runtime implementations.
For dApp developers:
-
Reduced Complexity: No need to learn the low-level details of every parachain’s pallets.
-
Faster Development: Write code once and deploy it across multiple chains.
-
Easy Extensibility: As more chains adopt the PVQ swap extension, they will instantly work with existing UIs without requiring any front-end updates.
This advancement is a crucial step toward a more cohesive and developer-friendly Polkadot ecosystem.
Next Steps
We are targeting the first release of the PVQ and integrating it into the polkadot-sdk.
We welcome feedback, questions, and collaboration from the community. Please feel free to check out the demo and share your thoughts below.
Thank you!
