Skip to main content

tycho_common/models/
mod.rs

1pub mod blockchain;
2pub mod contract;
3pub mod error;
4pub mod protocol;
5pub mod token;
6
7use std::{collections::HashMap, fmt::Display, str::FromStr};
8
9use deepsize::DeepSizeOf;
10use serde::{Deserialize, Serialize};
11use strum_macros::{Display, EnumString};
12use thiserror::Error;
13use token::Token;
14
15use crate::{dto, Bytes};
16
17/// Address hash literal type to uniquely identify contracts/accounts on a
18/// blockchain.
19pub type Address = Bytes;
20
21/// Block hash literal type to uniquely identify a block in the chain and
22/// likely across chains.
23pub type BlockHash = Bytes;
24
25/// Transaction hash literal type to uniquely identify a transaction in the
26/// chain and likely across chains.
27pub type TxHash = Bytes;
28
29/// Smart contract code is represented as a byte vector containing opcodes.
30pub type Code = Bytes;
31
32/// The hash of a contract's code is used to identify it.
33pub type CodeHash = Bytes;
34
35/// The balance of an account is a big endian serialised integer of variable size.
36pub type Balance = Bytes;
37
38/// Key literal type of the contract store.
39pub type StoreKey = Bytes;
40
41/// Key literal type of the attribute store.
42pub type AttrStoreKey = String;
43
44/// Value literal type of the contract store.
45pub type StoreVal = Bytes;
46
47/// A binary key-value store for an account.
48pub type ContractStore = HashMap<StoreKey, StoreVal>;
49pub type ContractStoreDeltas = HashMap<StoreKey, Option<StoreVal>>;
50pub type AccountToContractStoreDeltas = HashMap<Address, ContractStoreDeltas>;
51
52/// Component id literal type to uniquely identify a component.
53pub type ComponentId = String;
54
55/// Protocol system literal type to uniquely identify a protocol system.
56pub type ProtocolSystem = String;
57
58/// Entry point id literal type to uniquely identify an entry point.
59pub type EntryPointId = String;
60
61#[derive(
62    Debug,
63    Clone,
64    Copy,
65    PartialEq,
66    Eq,
67    Hash,
68    Serialize,
69    Deserialize,
70    EnumString,
71    Display,
72    Default,
73    DeepSizeOf,
74)]
75#[serde(rename_all = "lowercase")]
76#[strum(serialize_all = "lowercase")]
77pub enum Chain {
78    #[default]
79    Ethereum,
80    Starknet,
81    ZkSync,
82    Arbitrum,
83    Base,
84    Bsc,
85    Unichain,
86    Polygon,
87}
88
89impl From<dto::Chain> for Chain {
90    fn from(value: dto::Chain) -> Self {
91        match value {
92            dto::Chain::Ethereum => Chain::Ethereum,
93            dto::Chain::Starknet => Chain::Starknet,
94            dto::Chain::ZkSync => Chain::ZkSync,
95            dto::Chain::Arbitrum => Chain::Arbitrum,
96            dto::Chain::Base => Chain::Base,
97            dto::Chain::Bsc => Chain::Bsc,
98            dto::Chain::Unichain => Chain::Unichain,
99            dto::Chain::Polygon => Chain::Polygon,
100        }
101    }
102}
103
104fn native_eth(chain: Chain) -> Token {
105    Token::new(
106        &Bytes::from_str("0x0000000000000000000000000000000000000000").unwrap(),
107        "ETH",
108        18,
109        0,
110        &[Some(2300)],
111        chain,
112        100,
113    )
114}
115
116fn native_bsc(chain: Chain) -> Token {
117    Token::new(
118        &Bytes::from_str("0x0000000000000000000000000000000000000000").unwrap(),
119        "BNB",
120        18,
121        0,
122        &[Some(2300)],
123        chain,
124        100,
125    )
126}
127
128fn wrapped_native_eth(chain: Chain, address: &str) -> Token {
129    Token::new(&Bytes::from_str(address).unwrap(), "WETH", 18, 0, &[Some(2300)], chain, 100)
130}
131
132fn native_pol(chain: Chain) -> Token {
133    Token::new(
134        &Bytes::from_str("0x0000000000000000000000000000000000000000").unwrap(),
135        "POL",
136        18,
137        0,
138        &[Some(2300)],
139        chain,
140        100,
141    )
142}
143
144fn wrapped_native_bsc(chain: Chain, address: &str) -> Token {
145    Token::new(&Bytes::from_str(address).unwrap(), "WBNB", 18, 0, &[Some(2300)], chain, 100)
146}
147
148fn wrapped_native_pol(chain: Chain, address: &str) -> Token {
149    Token::new(&Bytes::from_str(address).unwrap(), "WMATIC", 18, 0, &[Some(2300)], chain, 100)
150}
151
152impl Chain {
153    pub fn id(&self) -> u64 {
154        match self {
155            Chain::Ethereum => 1,
156            Chain::ZkSync => 324,
157            Chain::Arbitrum => 42161,
158            Chain::Starknet => 0,
159            Chain::Base => 8453,
160            Chain::Bsc => 56,
161            Chain::Unichain => 130,
162            Chain::Polygon => 137,
163        }
164    }
165
166    /// Returns the native token for the chain.
167    pub fn native_token(&self) -> Token {
168        match self {
169            Chain::Ethereum => native_eth(Chain::Ethereum),
170            // It was decided that STRK token will be tracked as a dedicated AccountBalance on
171            // Starknet accounts and ETH balances will be tracked as a native balance.
172            Chain::Starknet => native_eth(Chain::Starknet),
173            Chain::ZkSync => native_eth(Chain::ZkSync),
174            Chain::Arbitrum => native_eth(Chain::Arbitrum),
175            Chain::Base => native_eth(Chain::Base),
176            Chain::Bsc => native_bsc(Chain::Bsc),
177            Chain::Unichain => native_eth(Chain::Unichain),
178            Chain::Polygon => native_pol(Chain::Polygon),
179        }
180    }
181
182    /// Returns the wrapped native token for the chain.
183    pub fn wrapped_native_token(&self) -> Token {
184        match self {
185            Chain::Ethereum => {
186                wrapped_native_eth(Chain::Ethereum, "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")
187            }
188            // Starknet does not have a wrapped native token
189            Chain::Starknet => {
190                wrapped_native_eth(Chain::Starknet, "0x0000000000000000000000000000000000000000")
191            }
192            Chain::ZkSync => {
193                wrapped_native_eth(Chain::ZkSync, "0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91")
194            }
195            Chain::Arbitrum => {
196                wrapped_native_eth(Chain::Arbitrum, "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1")
197            }
198            Chain::Base => {
199                wrapped_native_eth(Chain::Base, "0x4200000000000000000000000000000000000006")
200            }
201            Chain::Bsc => {
202                wrapped_native_bsc(Chain::Bsc, "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c")
203            }
204            Chain::Unichain => {
205                wrapped_native_eth(Chain::Unichain, "0x4200000000000000000000000000000000000006")
206            }
207            Chain::Polygon => {
208                wrapped_native_pol(Chain::Polygon, "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270")
209            }
210        }
211    }
212}
213
214#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, Default)]
215pub struct ExtractorIdentity {
216    pub chain: Chain,
217    pub name: String,
218}
219
220impl ExtractorIdentity {
221    pub fn new(chain: Chain, name: &str) -> Self {
222        Self { chain, name: name.to_owned() }
223    }
224}
225
226impl std::fmt::Display for ExtractorIdentity {
227    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
228        write!(f, "{}:{}", self.chain, self.name)
229    }
230}
231
232impl From<ExtractorIdentity> for dto::ExtractorIdentity {
233    fn from(value: ExtractorIdentity) -> Self {
234        dto::ExtractorIdentity { chain: value.chain.into(), name: value.name }
235    }
236}
237
238impl From<dto::ExtractorIdentity> for ExtractorIdentity {
239    fn from(value: dto::ExtractorIdentity) -> Self {
240        Self { chain: value.chain.into(), name: value.name }
241    }
242}
243
244#[derive(Debug, PartialEq, Clone)]
245pub struct ExtractionState {
246    pub name: String,
247    pub chain: Chain,
248    pub attributes: serde_json::Value,
249    pub cursor: Vec<u8>,
250    pub block_hash: Bytes,
251}
252
253impl ExtractionState {
254    pub fn new(
255        name: String,
256        chain: Chain,
257        attributes: Option<serde_json::Value>,
258        cursor: &[u8],
259        block_hash: Bytes,
260    ) -> Self {
261        ExtractionState {
262            name,
263            chain,
264            attributes: attributes.unwrap_or_default(),
265            cursor: cursor.to_vec(),
266            block_hash,
267        }
268    }
269}
270
271#[derive(PartialEq, Debug, Clone, Default, Deserialize, Serialize)]
272pub enum ImplementationType {
273    #[default]
274    Vm,
275    Custom,
276}
277
278#[derive(PartialEq, Debug, Clone, Default, Deserialize, Serialize)]
279pub enum FinancialType {
280    #[default]
281    Swap,
282    Psm,
283    Debt,
284    Leverage,
285}
286
287#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
288pub struct ProtocolType {
289    pub name: String,
290    pub financial_type: FinancialType,
291    pub attribute_schema: Option<serde_json::Value>,
292    pub implementation: ImplementationType,
293}
294
295impl ProtocolType {
296    pub fn new(
297        name: String,
298        financial_type: FinancialType,
299        attribute_schema: Option<serde_json::Value>,
300        implementation: ImplementationType,
301    ) -> Self {
302        ProtocolType { name, financial_type, attribute_schema, implementation }
303    }
304}
305
306#[derive(Debug, PartialEq, Eq, Default, Copy, Clone, Deserialize, Serialize, DeepSizeOf)]
307pub enum ChangeType {
308    #[default]
309    Update,
310    Deletion,
311    Creation,
312}
313
314#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
315pub struct ContractId {
316    pub address: Address,
317    pub chain: Chain,
318}
319
320/// Uniquely identifies a contract on a specific chain.
321impl ContractId {
322    pub fn new(chain: Chain, address: Address) -> Self {
323        Self { address, chain }
324    }
325
326    pub fn address(&self) -> &Address {
327        &self.address
328    }
329}
330
331impl Display for ContractId {
332    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
333        write!(f, "{:?}: 0x{}", self.chain, hex::encode(&self.address))
334    }
335}
336
337#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
338pub struct PaginationParams {
339    pub page: i64,
340    pub page_size: i64,
341}
342
343impl PaginationParams {
344    pub fn new(page: i64, page_size: i64) -> Self {
345        Self { page, page_size }
346    }
347
348    pub fn offset(&self) -> i64 {
349        self.page * self.page_size
350    }
351}
352
353impl From<&dto::PaginationParams> for PaginationParams {
354    fn from(value: &dto::PaginationParams) -> Self {
355        PaginationParams { page: value.page, page_size: value.page_size }
356    }
357}
358
359#[derive(Error, Debug, PartialEq)]
360pub enum MergeError {
361    #[error("Can't merge {0} from differring idendities: Expected {1}, got {2}")]
362    IdMismatch(String, String, String),
363    #[error("Can't merge {0} from different blocks: 0x{1:x} != 0x{2:x}")]
364    BlockMismatch(String, Bytes, Bytes),
365    #[error("Can't merge {0} from the same transaction: 0x{1:x}")]
366    SameTransaction(String, Bytes),
367    #[error("Can't merge {0} with lower transaction index: {1} > {2}")]
368    TransactionOrderError(String, u64, u64),
369    #[error("Cannot merge: {0}")]
370    InvalidState(String),
371}