alloy_contract/
instance.rs1use crate::{CallBuilder, Event, Interface, Result};
2use alloy_dyn_abi::DynSolValue;
3use alloy_json_abi::{Function, JsonAbi};
4use alloy_network::{Ethereum, Network};
5use alloy_primitives::{Address, Selector};
6use alloy_provider::Provider;
7use alloy_rpc_types_eth::Filter;
8use alloy_sol_types::SolEvent;
9use std::marker::PhantomData;
10
11#[derive(Clone)]
16pub struct ContractInstance<P, N = Ethereum> {
17 address: Address,
18 provider: P,
19 interface: Interface,
20 network: PhantomData<N>,
21}
22
23impl<P, N> ContractInstance<P, N> {
24 #[inline]
26 pub const fn new(address: Address, provider: P, interface: Interface) -> Self {
27 Self { address, provider, interface, network: PhantomData }
28 }
29
30 #[inline]
32 pub const fn address(&self) -> &Address {
33 &self.address
34 }
35
36 #[inline]
38 pub fn set_address(&mut self, address: Address) {
39 self.address = address;
40 }
41
42 #[inline]
44 pub fn at(mut self, address: Address) -> Self {
45 self.set_address(address);
46 self
47 }
48
49 #[inline]
51 pub const fn abi(&self) -> &JsonAbi {
52 self.interface.abi()
53 }
54
55 #[inline]
57 pub const fn provider(&self) -> &P {
58 &self.provider
59 }
60}
61
62impl<P: Clone, N> ContractInstance<&P, N> {
63 #[inline]
65 pub fn with_cloned_provider(self) -> ContractInstance<P, N> {
66 ContractInstance {
67 address: self.address,
68 provider: self.provider.clone(),
69 interface: self.interface,
70 network: PhantomData,
71 }
72 }
73}
74
75impl<P: Provider<N>, N: Network> ContractInstance<P, N> {
76 pub fn function(
82 &self,
83 name: &str,
84 args: &[DynSolValue],
85 ) -> Result<CallBuilder<&P, Function, N>> {
86 let function = self.interface.get_from_name(name)?;
87 CallBuilder::new_dyn(&self.provider, &self.address, function, args)
88 }
89
90 pub fn function_from_selector(
92 &self,
93 selector: &Selector,
94 args: &[DynSolValue],
95 ) -> Result<CallBuilder<&P, Function, N>> {
96 let function = self.interface.get_from_selector(selector)?;
97 CallBuilder::new_dyn(&self.provider, &self.address, function, args)
98 }
99
100 pub const fn event<E: SolEvent>(&self, filter: Filter) -> Event<&P, E, N> {
102 Event::new(&self.provider, filter)
103 }
104}
105
106impl<P, N> std::ops::Deref for ContractInstance<P, N> {
107 type Target = Interface;
108
109 fn deref(&self) -> &Self::Target {
110 &self.interface
111 }
112}
113
114impl<P, N> std::fmt::Debug for ContractInstance<P, N> {
115 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116 f.debug_struct("ContractInstance").field("address", &self.address).finish()
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123 use alloy_network::TransactionBuilder;
124 use alloy_primitives::{hex, U256};
125 use alloy_provider::ProviderBuilder;
126 use alloy_rpc_types_eth::TransactionRequest;
127
128 #[tokio::test]
129 async fn contract_interface() {
130 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
131
132 let abi_str = r#"[{"inputs":[],"name":"counter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"increment","outputs":[],"stateMutability":"nonpayable","type":"function"}]"#;
133 let abi = serde_json::from_str::<JsonAbi>(abi_str).unwrap();
134 let bytecode = hex::decode("6080806040523460135760b2908160188239f35b5f80fdfe60808060405260043610156011575f80fd5b5f3560e01c90816361bc221a146065575063d09de08a14602f575f80fd5b346061575f3660031901126061575f5460018101809111604d575f55005b634e487b7160e01b5f52601160045260245ffd5b5f80fd5b346061575f3660031901126061576020905f548152f3fea2646970667358221220d802267a5f574e54a87a63d0ff8d733fdb275e6e6c502831d9e14f957bbcd7a264736f6c634300081a0033").unwrap();
135 let deploy_tx = TransactionRequest::default().with_deploy_code(bytecode);
136 let address = provider
137 .send_transaction(deploy_tx)
138 .await
139 .unwrap()
140 .get_receipt()
141 .await
142 .unwrap()
143 .contract_address
144 .unwrap();
145
146 let contract = ContractInstance::new(address, provider, Interface::new(abi));
147 assert_eq!(contract.abi().functions().count(), 2);
148
149 let result = contract.function("counter", &[]).unwrap().call().await.unwrap();
150 assert_eq!(result[0].as_uint().unwrap().0, U256::from(0));
151
152 contract.function("increment", &[]).unwrap().send().await.unwrap().watch().await.unwrap();
153
154 let result = contract.function("counter", &[]).unwrap().call().await.unwrap();
155 assert_eq!(result[0].as_uint().unwrap().0, U256::from(1));
156 }
157}