apex_sdk_types/
lib.rs

1//! # Apex SDK Types
2//!
3//! Common types and data structures used across the Apex SDK.
4//!
5//! This crate provides fundamental types for representing blockchain entities
6//! across different chain types (Substrate, EVM, Hybrid).
7//!
8//! ## Core Types
9//!
10//! - **Chain**: Enumeration of supported blockchain networks
11//! - **ChainType**: Classification of chains (Substrate, EVM, Hybrid)
12//! - **Address**: Generic address type supporting multiple formats
13//! - **TransactionStatus**: Unified transaction status representation
14//! - **CrossChainTransaction**: Cross-chain transaction information
15//!
16//! ## Example
17//!
18//! ```rust
19//! use apex_sdk_types::{Chain, ChainType, Address};
20//!
21//! // Create addresses for different chains
22//! let eth_addr = Address::evm("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb7");
23//! let dot_addr = Address::substrate("15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5");
24//!
25//! // Check chain types
26//! assert_eq!(Chain::Ethereum.chain_type(), ChainType::Evm);
27//! assert_eq!(Chain::Polkadot.chain_type(), ChainType::Substrate);
28//! assert_eq!(Chain::Moonbeam.chain_type(), ChainType::Hybrid);
29//! ```
30
31use serde::{Deserialize, Serialize};
32
33/// Blockchain types
34#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
35pub enum ChainType {
36    /// Substrate-based chain
37    Substrate,
38    /// EVM-based chain
39    Evm,
40    /// Hybrid chain (both Substrate and EVM)
41    Hybrid,
42}
43
44/// Supported blockchain networks
45#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
46pub enum Chain {
47    // Substrate Relay Chains
48    /// Polkadot relay chain
49    Polkadot,
50    /// Kusama relay chain
51    Kusama,
52
53    // Substrate Parachains
54    /// Moonbeam (Polkadot parachain with EVM)
55    Moonbeam,
56    /// Astar (Polkadot parachain with EVM)
57    Astar,
58    /// Acala DeFi Hub
59    Acala,
60    /// Phala Privacy Cloud
61    Phala,
62    /// Bifrost Liquid Staking
63    Bifrost,
64    /// Westend testnet
65    Westend,
66
67    // EVM Layer 1
68    /// Ethereum mainnet
69    Ethereum,
70    /// Binance Smart Chain
71    BinanceSmartChain,
72    /// Polygon
73    Polygon,
74    /// Avalanche C-Chain
75    Avalanche,
76
77    // EVM Layer 2
78    /// Arbitrum One
79    Arbitrum,
80    /// Optimism
81    Optimism,
82    /// zkSync Era
83    ZkSync,
84    /// Base (Coinbase L2)
85    Base,
86}
87
88impl Chain {
89    /// Get the chain type
90    pub fn chain_type(&self) -> ChainType {
91        match self {
92            // Pure Substrate chains
93            Chain::Polkadot
94            | Chain::Kusama
95            | Chain::Acala
96            | Chain::Phala
97            | Chain::Bifrost
98            | Chain::Westend => ChainType::Substrate,
99
100            // Pure EVM chains
101            Chain::Ethereum
102            | Chain::BinanceSmartChain
103            | Chain::Polygon
104            | Chain::Avalanche
105            | Chain::Arbitrum
106            | Chain::Optimism
107            | Chain::ZkSync
108            | Chain::Base => ChainType::Evm,
109
110            // Hybrid chains (Substrate + EVM)
111            Chain::Moonbeam | Chain::Astar => ChainType::Hybrid,
112        }
113    }
114
115    /// Get the chain name
116    pub fn name(&self) -> &str {
117        match self {
118            // Substrate
119            Chain::Polkadot => "Polkadot",
120            Chain::Kusama => "Kusama",
121            Chain::Acala => "Acala",
122            Chain::Phala => "Phala",
123            Chain::Bifrost => "Bifrost",
124            Chain::Westend => "Westend",
125
126            // EVM L1
127            Chain::Ethereum => "Ethereum",
128            Chain::BinanceSmartChain => "Binance Smart Chain",
129            Chain::Polygon => "Polygon",
130            Chain::Avalanche => "Avalanche",
131
132            // EVM L2
133            Chain::Arbitrum => "Arbitrum",
134            Chain::Optimism => "Optimism",
135            Chain::ZkSync => "zkSync",
136            Chain::Base => "Base",
137
138            // Hybrid
139            Chain::Moonbeam => "Moonbeam",
140            Chain::Astar => "Astar",
141        }
142    }
143
144    /// Get default RPC endpoint for the chain
145    pub fn default_endpoint(&self) -> &str {
146        match self {
147            // Substrate
148            Chain::Polkadot => "wss://polkadot.api.onfinality.io/public-ws",
149            Chain::Kusama => "wss://kusama.api.onfinality.io/public-ws",
150            Chain::Acala => "wss://acala.api.onfinality.io/public-ws",
151            Chain::Phala => "wss://phala.api.onfinality.io/public-ws",
152            Chain::Bifrost => "wss://bifrost-polkadot.api.onfinality.io/public-ws",
153            Chain::Westend => "wss://westend-rpc.polkadot.io",
154
155            // EVM L1
156            Chain::Ethereum => "https://eth.llamarpc.com",
157            Chain::BinanceSmartChain => "https://bsc.publicnode.com",
158            Chain::Polygon => "https://polygon-rpc.com",
159            Chain::Avalanche => "https://api.avax.network/ext/bc/C/rpc",
160
161            // EVM L2
162            Chain::Arbitrum => "https://arb1.arbitrum.io/rpc",
163            Chain::Optimism => "https://mainnet.optimism.io",
164            Chain::ZkSync => "https://mainnet.era.zksync.io",
165            Chain::Base => "https://mainnet.base.org",
166
167            // Hybrid
168            Chain::Moonbeam => "wss://moonbeam.api.onfinality.io/public-ws",
169            Chain::Astar => "wss://astar.api.onfinality.io/public-ws",
170        }
171    }
172
173    /// Get multiple RPC endpoints for reliability and failover
174    pub fn rpc_endpoints(&self) -> Vec<&str> {
175        match self {
176            // Substrate
177            Chain::Polkadot => vec![
178                "wss://polkadot.api.onfinality.io/public-ws",
179                "wss://rpc.ibp.network/polkadot",
180                "wss://polkadot.dotters.network",
181            ],
182            Chain::Kusama => vec![
183                "wss://kusama.api.onfinality.io/public-ws",
184                "wss://rpc.ibp.network/kusama",
185                "wss://kusama.dotters.network",
186            ],
187            Chain::Westend => vec![
188                "wss://westend-rpc.polkadot.io",
189                "wss://rpc.ibp.network/westend",
190                "wss://westend.dotters.network",
191            ],
192            // For other chains, return the single default endpoint
193            _ => vec![self.default_endpoint()],
194        }
195    }
196
197    /// Check if chain is a Layer 2 solution
198    pub fn is_layer2(&self) -> bool {
199        matches!(
200            self,
201            Chain::Arbitrum | Chain::Optimism | Chain::ZkSync | Chain::Base
202        )
203    }
204
205    /// Check if chain supports smart contracts
206    pub fn supports_smart_contracts(&self) -> bool {
207        match self.chain_type() {
208            ChainType::Evm => true,
209            ChainType::Hybrid => true,
210            ChainType::Substrate => matches!(
211                self,
212                Chain::Acala | Chain::Phala | Chain::Moonbeam | Chain::Astar
213            ),
214        }
215    }
216}
217
218/// Generic address type for different chains
219#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
220pub enum Address {
221    /// Substrate SS58 address
222    Substrate(String),
223    /// EVM hex address (0x...)
224    Evm(String),
225}
226
227impl Address {
228    /// Create a Substrate address
229    pub fn substrate(addr: impl Into<String>) -> Self {
230        Address::Substrate(addr.into())
231    }
232
233    /// Create an EVM address
234    pub fn evm(addr: impl Into<String>) -> Self {
235        Address::Evm(addr.into())
236    }
237
238    /// Get the address as a string
239    pub fn as_str(&self) -> &str {
240        match self {
241            Address::Substrate(s) | Address::Evm(s) => s,
242        }
243    }
244}
245
246/// Transaction status
247#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
248pub enum TransactionStatus {
249    /// Transaction is pending.
250    ///
251    /// The transaction has been created but has not yet been broadcasted to the network.
252    /// This status typically indicates that the transaction is awaiting submission or signing.
253    Pending,
254    /// Transaction is in memory pool (mempool).
255    ///
256    /// The transaction has been broadcasted to the network and is waiting to be included in a block.
257    /// This status indicates that the transaction is known to the network but not yet confirmed.
258    InMempool,
259    /// Transaction is confirmed
260    Confirmed {
261        /// Block hash
262        block_hash: String,
263        /// Block number where transaction was included
264        block_number: Option<u64>,
265    },
266    /// Transaction is finalized (for Substrate chains)
267    Finalized {
268        /// Block hash
269        block_hash: String,
270        /// Block number
271        block_number: u64,
272    },
273    /// Transaction failed
274    Failed {
275        /// Error message
276        error: String,
277    },
278    /// Transaction status unknown
279    Unknown,
280}
281
282/// Represents a blockchain event emitted by a smart contract or runtime.
283///
284/// The `Event` struct captures details about an event, including its name, associated data,
285/// the block and transaction in which it occurred, and its index within the block.
286///
287/// # Fields
288/// - `name`: The name of the event (e.g., `"Transfer"`, `"Approval"`).
289/// - `data`: The event payload as a JSON value. This typically contains event parameters.
290/// - `block_number`: The block number in which the event was emitted, if available.
291/// - `tx_hash`: The transaction hash associated with the event, if available.
292/// - `index`: The index of the event within the block, if available.
293///
294/// # Example
295/// ```
296/// use apex_sdk_types::Event;
297/// use serde_json::json;
298///
299/// let event = Event {
300///     name: "Transfer".to_string(),
301///     data: json!({
302///         "from": "0x123...",
303///         "to": "0x456...",
304///         "value": 1000
305///     }),
306///     block_number: Some(123456),
307///     tx_hash: Some("0xabc...".to_string()),
308///     index: Some(0),
309/// };
310/// assert_eq!(event.name, "Transfer");
311/// ```
312#[derive(Debug, Clone, Serialize, Deserialize)]
313pub struct Event {
314    /// The name of the event (e.g., "Transfer", "Approval").
315    pub name: String,
316    /// The event payload as a JSON value, typically containing event parameters.
317    pub data: serde_json::Value,
318    /// The block number in which the event was emitted, if available.
319    pub block_number: Option<u64>,
320    /// The transaction hash associated with the event, if available.
321    pub tx_hash: Option<String>,
322    /// The index of the event within the block, if available.
323    pub index: Option<u32>,
324}
325
326/// Filter criteria for subscribing to blockchain events.
327///
328/// This struct allows you to specify which events to receive by name, contract address,
329/// and block range. All fields are optional; if a field is `None`, it will not be used
330/// as a filter criterion.
331#[derive(Debug, Clone, Serialize, Deserialize)]
332pub struct EventFilter {
333    /// List of event names to filter for.
334    ///
335    /// If specified, only events with names matching one of the strings in this list
336    /// will be included. If `None`, all event names are included.
337    pub event_names: Option<Vec<String>>,
338    /// List of contract addresses to filter for.
339    ///
340    /// If specified, only events emitted by contracts with addresses in this list
341    /// will be included. If `None`, events from all addresses are included.
342    pub addresses: Option<Vec<Address>>,
343    /// The starting block number (inclusive) for filtering events.
344    ///
345    /// If specified, only events from blocks with number greater than or equal to this
346    /// value will be included. If `None`, events from all blocks are included.
347    pub from_block: Option<u64>,
348    /// The ending block number (inclusive) for filtering events.
349    ///
350    /// If specified, only events from blocks with number less than or equal to this
351    /// value will be included. If `None`, events up to the latest block are included.
352    pub to_block: Option<u64>,
353}
354
355/// Cross-chain transaction info
356#[derive(Debug, Clone, Serialize, Deserialize)]
357pub struct CrossChainTransaction {
358    /// Transaction ID
359    pub id: String,
360    /// Source chain
361    pub source_chain: Chain,
362    /// Destination chain
363    pub destination_chain: Chain,
364    /// Source transaction hash
365    pub source_tx_hash: Option<String>,
366    /// Destination transaction hash
367    pub destination_tx_hash: Option<String>,
368    /// Transaction status
369    pub status: TransactionStatus,
370    /// Timestamp
371    pub timestamp: u64,
372}
373
374#[cfg(test)]
375mod tests {
376    use super::*;
377
378    #[test]
379    fn test_chain_type() {
380        assert_eq!(Chain::Polkadot.chain_type(), ChainType::Substrate);
381        assert_eq!(Chain::Ethereum.chain_type(), ChainType::Evm);
382        assert_eq!(Chain::Moonbeam.chain_type(), ChainType::Hybrid);
383    }
384
385    #[test]
386    fn test_address_creation() {
387        let sub_addr = Address::substrate("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY");
388        assert!(matches!(sub_addr, Address::Substrate(_)));
389
390        let evm_addr = Address::evm("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb7");
391        assert!(matches!(evm_addr, Address::Evm(_)));
392    }
393}