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}