1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
mod externs;
pub mod pb;
pub mod rpc;

pub use substreams_ethereum_derive::EthabiContract;

#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
use std::num::NonZeroU32;

/// Builder struct for generating type-safe bindings from a contract's ABI
///
/// # Example
///
/// Running the code below will generate a file called `token.rs` containing the
/// bindings inside, which exports an `erc` struct, along with all its events. Put into a
/// `build.rs` file this will generate the bindings during `cargo build`.
///
/// ```no_run
///     use anyhow::{Ok, Result};
///     use substreams_ethereum::Abigen;
///
///     fn main() -> Result<(), anyhow::Error> {
///         Abigen::new("ERC721", "abi/erc721.json")?
///             .generate()?
///             .write_to_file("src/abi/erc721.rs")?;
///
///         Ok(())
///     }
/// ```
pub use substreams_ethereum_abigen::build::Abigen;

/// Represents the null address static array in bytes (20 bytes) which in hex is equivalent
/// to:
///
/// ```text
/// 0000000000000000000000000000000000000000
/// ```
pub const NULL_ADDRESS: [u8; 20] = [
    0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8,
    0u8,
];

/// This macro can be used to import an Ethereum ABI file in JSON format and generate all the
/// required bindings for ABI decoding/encoding in Rust, targetting `substreams` developer
/// experience.
///
/// ```no_run
///     use substreams_ethereum::use_contract;
///
///     use_contract!(erc721, "./examples/abi/erc721.json");
/// ```
///
/// This invocation will generate the following code (signatures only for consiscness):
///
/// ```rust,ignore
/// mod erc721 {
///     pub mod events {
///         #[derive(Debug, Clone, PartialEq)]
///         pub struct Transfer {
///             pub from: Vec<u8>,
///             pub to: Vec<u8>,
///             pub token_id: ethabi::Uint,
///         }
///
///         impl Transfer {
///             pub fn match_log(log: &substreams_ethereum::pb::eth::v1::Log) -> bool {
///                ...
///             }
///
///             pub fn decode(log: &substreams_ethereum::pb::eth::v1::Log) -> Result<Transfer, String> {
///                ...
///             }
///
///             pub fn must_decode(log: &substreams_ethereum::pb::eth::v1::Log) -> Transfer {
///                ...
///             }
///         }
///
///         ... Other events ...
///     }
/// }
/// ```
#[macro_export]
macro_rules! use_contract {
    ($module: ident, $path: expr) => {
        #[allow(dead_code)]
        #[allow(missing_docs)]
        #[allow(unused_imports)]
        #[allow(unused_mut)]
        #[allow(unused_variables)]
        pub mod $module {
            #[derive(substreams_ethereum::EthabiContract)]
            #[ethabi_contract_options(path = $path)]
            struct _Dummy;
        }
    };
}

#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
/// The `init` macro registers a custom get random function in the system which is required
/// because `ethabi` that we rely on for ABI decoding/encoding primitives use it somewhere in
/// its transitive set of dependencies and causes problem in `wasm32-unknown-unknown` target.
///
/// This macro must be invoked in the root crate so you must have the `substreams_ethereum::init!()`
/// call in your `lib.rs` of your Substreams.
///
/// In addition, you need to have `getrandom = { version = "0.2", features = ["custom"] }` dependency
/// in your Substreams `Cargo.toml` file:
///
/// ```toml
/// [dependencies]
/// ...
/// # Required so that ethabi > ethereum-types build correctly under wasm32-unknown-unknown
/// getrandom = { version = "0.2", features = ["custom"] }
///```
#[macro_export]
macro_rules! init {
    () => {
        getrandom::register_custom_getrandom!(substreams_ethereum::getrandom_unavailable);
    };
}

#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
const GETRANDOM_UNVAILABLE_IN_SUBSTREAMS: u32 = getrandom::Error::CUSTOM_START + 5545;

#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
pub fn getrandom_unavailable(_buf: &mut [u8]) -> Result<(), getrandom::Error> {
    let code = NonZeroU32::new(GETRANDOM_UNVAILABLE_IN_SUBSTREAMS).unwrap();
    Err(getrandom::Error::from(code))
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        let result = 2 + 2;
        assert_eq!(result, 4);
    }
}