Skip to main content

perpl_sdk/
lib.rs

1//! [`Perpl`] DEX SDK.
2//!
3//! # Overview
4//!
5//! Convenient in-memory cache of on-chain exchange state.
6//!
7//! Use [`state::SnapshotBuilder`] to capture initial state snapshot, then
8//! [`stream::raw`] to catch up with the recent state and keep snapshot
9//! up to date.
10//!
11//! Use [`types::OrderRequest`] to prepare order requests to send them with
12//! [`crate::abi::dex::Exchange::ExchangeInstance::execOrdersV2`].
13//!
14//! The deployed contract may lag behind the revision the SDK targets, so the
15//! snapshot detects the contract's [`state::ContractFeatures`] and degrades
16//! gracefully rather than failing on a missing selector.
17//!
18//! See `./tests` for examples.
19//!
20//! # Limitations/follow-ups
21//!
22//! * Funding events processing is to follow.
23//!
24//! * Current version relies on log polling to implement reliably continuous
25//!   stream of events. Future versions could improve indexing latency by
26//!   utilizing WebSocket subscriptions and/or Monad [`execution events`].
27//!
28//! * Test coverage is far below reasonable.
29//!
30//! # Features
31//!
32//! | Feature | Default | Description |
33//! | --- | --- | --- |
34//! | `display` | yes | Enables [`std::fmt::Display`] implementation for state types. |
35//! | `testing` | yes | Enables [`testing`] module. |
36//!
37//! # Testing
38//!
39//! [`testing`] module provides a local testing environment with collateral
40//! token and exchange smart contracts deployed.
41//!
42//!
43//! [`Perpl`]: https://perpl.xyz
44//! [`execution events`]: https://docs.monad.xyz/execution-events/
45
46pub mod abi;
47pub mod error;
48pub mod num;
49pub mod state;
50pub mod stream;
51#[cfg(feature = "testing")]
52pub mod testing;
53#[cfg(test)]
54mod tests;
55pub mod types;
56
57use alloy::primitives::{Address, address};
58
59#[derive(Clone, Debug)]
60/// Chain the exchange is operating on.
61pub struct Chain {
62    chain_id: u64,
63    collateral_token: Address,
64    deployed_at_block: u64,
65    exchange: Address,
66    perpetuals: Vec<types::PerpetualId>,
67    excluded_perpetuals: Vec<types::PerpetualId>,
68}
69
70impl Chain {
71    pub fn mainnet() -> Self {
72        Self {
73            chain_id: 143,
74            collateral_token: address!("0x00000000eFE302BEAA2b3e6e1b18d08D69a9012a"),
75            deployed_at_block: 54773010,
76            exchange: address!("0x34B6552d57a35a1D042CcAe1951BD1C370112a6F"),
77            perpetuals: vec![],
78            excluded_perpetuals: vec![30],
79        }
80    }
81
82    pub fn testnet() -> Self {
83        Self {
84            chain_id: 10143,
85            collateral_token: address!("0xa9012a055bd4e0eDfF8Ce09f960291C09D5322dC"),
86            deployed_at_block: 62953,
87            exchange: address!("0x1964C32f0bE608E7D29302AFF5E61268E72080cc"),
88            perpetuals: vec![],
89            excluded_perpetuals: vec![],
90        }
91    }
92
93    /// Chain the exchange is operating on, with the perpetual contracts to
94    /// track.
95    ///
96    /// An empty `perpetuals` list means *every* perpetual listed on the
97    /// exchange, discovered on-chain - see [`Chain::perpetuals`].
98    pub fn custom(
99        chain_id: u64,
100        collateral_token: Address,
101        deployed_at_block: u64,
102        exchange: Address,
103        perpetuals: Vec<types::PerpetualId>,
104    ) -> Self {
105        Self {
106            chain_id,
107            collateral_token,
108            deployed_at_block,
109            exchange,
110            perpetuals,
111            excluded_perpetuals: vec![],
112        }
113    }
114
115    pub fn chain_id(&self) -> u64 { self.chain_id }
116
117    pub fn collateral_token(&self) -> Address { self.collateral_token }
118
119    pub fn deployed_at_block(&self) -> u64 { self.deployed_at_block }
120
121    pub fn exchange(&self) -> Address { self.exchange }
122
123    /// Perpetual contracts to track, empty (the default) meaning every
124    /// perpetual listed on the exchange.
125    ///
126    /// The exchange reports the set of listed contracts on-chain, so the SDK
127    /// does not need to be told: an empty list makes
128    /// [`state::SnapshotBuilder`] discover them at snapshot time. Configure it
129    /// explicitly only to deliberately track a *subset*.
130    pub fn perpetuals(&self) -> &[types::PerpetualId] { &self.perpetuals }
131
132    /// Same chain, tracking only the given subset of perpetual contracts.
133    pub fn with_perpetuals(mut self, perpetuals: Vec<types::PerpetualId>) -> Self {
134        self.perpetuals = perpetuals;
135        self
136    }
137
138    /// Perpetual contracts to leave out of on-chain discovery.
139    ///
140    /// Applies to discovery *only*: an explicitly configured
141    /// [`Chain::perpetuals`] list is taken as given, exclusions and all, since
142    /// naming a contract is a clearer statement of intent than the default set
143    /// it would otherwise be filtered out of.
144    pub fn excluded_perpetuals(&self) -> &[types::PerpetualId] { &self.excluded_perpetuals }
145
146    /// Same chain, skipping the given perpetual contracts when discovering the
147    /// set to track - see [`Chain::excluded_perpetuals`].
148    ///
149    /// Replaces the chain's default exclusions rather than adding to them, so
150    /// passing an empty list discovers everything the exchange lists.
151    pub fn with_excluded_perpetuals(mut self, perpetuals: Vec<types::PerpetualId>) -> Self {
152        self.excluded_perpetuals = perpetuals;
153        self
154    }
155}