From b39e0981072687fe4ea28fc99b0f5a7a120a6d80 Mon Sep 17 00:00:00 2001 From: Maximilian Schneider Date: Thu, 17 Oct 2024 19:38:26 +0300 Subject: [PATCH] document dex interface (#27) * document dex interface * document swap instruction --- lib/router-lib/src/dex/interface.rs | 79 +++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 11 deletions(-) diff --git a/lib/router-lib/src/dex/interface.rs b/lib/router-lib/src/dex/interface.rs index 0fc0878..09cd8bd 100644 --- a/lib/router-lib/src/dex/interface.rs +++ b/lib/router-lib/src/dex/interface.rs @@ -12,10 +12,20 @@ use std::sync::Arc; #[derive(Clone, Serialize, Deserialize)] pub struct SwapInstruction { + /// Instruction to be executed by the user to swap through an edge. pub instruction: Instruction, + /// Address of the user's associated token account that will receive + /// the proceeds of the swap after invoking instruction. pub out_pubkey: Pubkey, + /// Mint of the tokens received from the swap. pub out_mint: Pubkey, + /// Byte offset in Instruction.data that the onchain executor program + /// will use to replace the input amount with the proceeds of the + /// previous swap before cpi-invocation of this edge. + /// instruction.data\[in_amount_offset..in_amount_offset+8\] = in_amount pub in_amount_offset: u16, + /// Conservative upper bound estimate of compute cost. If it is too low + /// transactions will fail, if it is too high, they will confirm slower. pub cu_estimate: Option, } @@ -144,6 +154,16 @@ pub type AccountProviderView = Arc; #[async_trait::async_trait] pub trait DexInterface: Sync + Send { + /// Called on router boot, with the options read from the dex adapters's + /// config file. Can use RPC to initialize. The result contains usually + /// self. After calling initialize the returned DexInterface needs to be + /// able to respond the following methods: + /// - name() + /// - subscription_mode() + /// - edges_per_pk() + /// - program_ids() + /// - supports_exact_out() + /// - load() async fn initialize( rpc: &mut RouterRpcClient, options: HashMap, @@ -153,18 +173,43 @@ pub trait DexInterface: Sync + Send { fn name(&self) -> String; + /// Defines the kind of grpc/quic subscription that should be established + /// to the RPC/Validator to keep this adapter updated. Also defines the + /// accounts included in a snapshot for simulation tests. + /// Right now the subscription mode is static per adapter and changes post + /// initialization have no effect. This might change in the future. fn subscription_mode(&self) -> DexSubscriptionMode; + /// Defines the relationship between account updates and which + /// DexEdgeIndentifiers will be reloaded. Once a batch of account updates + /// has been added to ChainData the corresponding edge identifies will be + /// passed to DexInterface::load(). + /// The identifies are a symbolic representation for edges, meaning they + /// should not store any mutable data related to the generation of actual + /// quotes, but merely expose the immutable description of the possibility + /// to quote a trade from input mint to output mint. fn edges_per_pk(&self) -> HashMap>>; + + /// Defines the programs that should be included in a snapshot for + /// simulation tests. fn program_ids(&self) -> HashSet; + + /// Initializes an Edge from ChainData (production) or BanksClient (test). + /// The Edge will be dropped once a new Edge for the same EdgeIndentifier + /// has been initialized. After calling initialize the DexInterface needs + /// to be able to respond to quote() and supports_exact_out() calls that + /// pass this Edge. It can store immutable data locally. + /// Performance is critical, optimize implementations well. fn load( &self, id: &Arc, - // TODO: put behind interface so we can adapt for BanksClient chain_data: &AccountProviderView, ) -> anyhow::Result>; + /// Calculates the output amount for a given input amount, will be called + /// multiple times after an edge has been loaded. + /// Performance is critical, optimize implementations well. fn quote( &self, id: &Arc, @@ -173,25 +218,37 @@ pub trait DexInterface: Sync + Send { in_amount: u64, ) -> anyhow::Result; - fn build_swap_ix( + /// Calculates the input amount for a given output amount, will be called + /// multiple times after an edge has been loaded. + /// Performance is critical, optimize implementations well. + fn quote_exact_out( &self, id: &Arc, - // TODO: put behind interface so we can adapt for BanksClient + edge: &Arc, chain_data: &AccountProviderView, - wallet_pk: &Pubkey, - in_amount: u64, out_amount: u64, - max_slippage_bps: i32, - ) -> anyhow::Result; + ) -> anyhow::Result; + /// Returns true, if the edge supports both quote() and quote_exact_out(). + /// Returns false, if the edge only support quote(). fn supports_exact_out(&self, id: &Arc) -> bool; - fn quote_exact_out( + /// Constructs a description for call-data passed to the executor program. + /// Once a route has been selected for the end-user to swap through, the + /// router will invoke DexInterface::build_swap_ix for every edge in the + /// route with with it's DexEdgeIdentifier rather than the initialized + /// DexEdge. The build_swap_ix implementation should use the most recent + /// data possible to avoid latency between account update, load & quote. + /// Exact-out is only used during route selection, onchain execution is + /// always using exact input amounts that get adjusted between edge + /// cpi-invocations using the in_amount_offset. + fn build_swap_ix( &self, id: &Arc, - edge: &Arc, chain_data: &AccountProviderView, + wallet_pk: &Pubkey, + in_amount: u64, out_amount: u64, - ) -> anyhow::Result; - // TODO: list of all program_ids to fetch for testing + max_slippage_bps: i32, + ) -> anyhow::Result; }