1mod deploy;
6mod event;
7mod method;
8
9use crate::{
10 errors::{DeployError, LinkError},
11 tokens::Tokenize,
12};
13use ethcontract_common::hash::H32;
14use ethcontract_common::{
15 abi::{encode, Error as AbiError, Result as AbiResult},
16 contract::Interface,
17};
18use ethcontract_common::{Abi, Bytecode, Contract, DeploymentInformation};
19use std::hash::Hash;
20use std::sync::Arc;
21use web3::api::Web3;
22use web3::types::{Address, Bytes, H256};
23use web3::Transport;
24
25pub use self::deploy::{Deploy, DeployBuilder};
26pub use self::event::{
27 AllEventsBuilder, Event, EventBuilder, EventMetadata, EventStatus, ParseLog, RawLog,
28 StreamEvent, Topic,
29};
30pub use self::method::{MethodBuilder, MethodDefaults, ViewMethodBuilder};
31use std::marker::PhantomData;
32
33#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
41pub struct Signature<P, R>(pub H32, pub std::marker::PhantomData<(P, R)>);
42
43impl<P, R> Signature<P, R> {
44 pub fn new(signature: H32) -> Self {
46 Signature(signature, PhantomData)
47 }
48
49 pub fn into_inner(self) -> H32 {
51 self.0
52 }
53}
54
55impl<P, R> From<H32> for Signature<P, R> {
56 fn from(signature: H32) -> Self {
57 Signature::new(signature)
58 }
59}
60
61#[derive(Debug, Clone)]
64pub struct Instance<T: Transport> {
65 web3: Web3<T>,
66 address: Address,
67 deployment_information: Option<DeploymentInformation>,
68 pub defaults: MethodDefaults,
71 interface: Arc<Interface>,
72}
73
74impl<T: Transport> Instance<T> {
75 pub fn at(web3: Web3<T>, interface: Arc<Interface>, address: Address) -> Self {
81 Instance::with_deployment_info(web3, interface, address, None)
82 }
83
84 pub fn with_deployment_info(
93 web3: Web3<T>,
94 interface: Arc<Interface>,
95 address: Address,
96 deployment_information: Option<DeploymentInformation>,
97 ) -> Self {
98 Instance {
99 web3,
100 interface,
101 address,
102 deployment_information,
103 defaults: Default::default(),
104 }
105 }
106
107 pub async fn deployed(web3: Web3<T>, contract: Contract) -> Result<Self, DeployError> {
113 let network_id = web3.eth().chain_id().await?.to_string();
114 let network = contract
115 .networks
116 .get(&network_id)
117 .ok_or(DeployError::NotFound(network_id))?;
118
119 Ok(Instance::with_deployment_info(
120 web3,
121 contract.interface,
122 network.address,
123 network.deployment_information,
124 ))
125 }
126
127 pub fn builder<P>(
131 web3: Web3<T>,
132 contract: Contract,
133 params: P,
134 ) -> Result<DeployBuilder<T, Self>, DeployError>
135 where
136 P: Tokenize,
137 {
138 Linker::new(contract).deploy(web3, params)
139 }
140
141 pub fn link_and_deploy<'a, P, I>(
144 web3: Web3<T>,
145 contract: Contract,
146 params: P,
147 libraries: I,
148 ) -> Result<DeployBuilder<T, Self>, DeployError>
149 where
150 P: Tokenize,
151 I: Iterator<Item = (&'a str, Address)>,
152 {
153 let mut linker = Linker::new(contract);
154 for (name, address) in libraries {
155 linker = linker.library(name, address)?;
156 }
157
158 linker.deploy(web3, params)
159 }
160
161 pub fn web3(&self) -> Web3<T> {
163 self.web3.clone()
164 }
165
166 pub fn abi(&self) -> &Abi {
168 &self.interface.abi
169 }
170
171 pub fn address(&self) -> Address {
173 self.address
174 }
175
176 pub fn deployment_information(&self) -> Option<DeploymentInformation> {
179 self.deployment_information
180 }
181
182 pub fn method<P, R>(
186 &self,
187 signature: impl Into<Signature<P, R>>,
188 params: P,
189 ) -> AbiResult<MethodBuilder<T, R>>
190 where
191 P: Tokenize,
192 R: Tokenize,
193 {
194 let signature = signature.into().into_inner();
195 let function = self
196 .interface
197 .methods
198 .get(&signature)
199 .map(|(name, index)| &self.interface.abi.functions[name][*index])
200 .ok_or_else(|| AbiError::InvalidName(hex::encode(signature)))?;
201 let tokens = match params.into_token() {
202 ethcontract_common::abi::Token::Tuple(tokens) => tokens,
203 _ => unreachable!("function arguments are always tuples"),
204 };
205
206 let data = signature.iter().copied().chain(encode(&tokens)).collect();
207
208 let function = function.clone();
212 let data = Bytes(data);
213
214 Ok(
215 MethodBuilder::new(self.web3(), function, self.address, data)
216 .with_defaults(&self.defaults),
217 )
218 }
219
220 pub fn view_method<P, R>(
224 &self,
225 signature: impl Into<Signature<P, R>>,
226 params: P,
227 ) -> AbiResult<ViewMethodBuilder<T, R>>
228 where
229 P: Tokenize,
230 R: Tokenize,
231 {
232 Ok(self.method(signature, params)?.view())
233 }
234
235 pub fn fallback<D>(&self, data: D) -> AbiResult<MethodBuilder<T, ()>>
241 where
242 D: Into<Vec<u8>>,
243 {
244 if !self.interface.abi.fallback && !self.interface.abi.receive {
245 return Err(AbiError::InvalidName("fallback".into()));
246 }
247
248 Ok(MethodBuilder::fallback(
249 self.web3(),
250 self.address,
251 Bytes(data.into()),
252 ))
253 }
254
255 pub fn event<E>(&self, signature: H256) -> AbiResult<EventBuilder<T, E>>
258 where
259 E: Tokenize,
260 {
261 let event = self
262 .interface
263 .events
264 .get(&signature)
265 .map(|(name, index)| &self.interface.abi.events[name][*index])
266 .ok_or_else(|| AbiError::InvalidName(hex::encode(signature)))?;
267
268 Ok(EventBuilder::new(
269 self.web3(),
270 event.clone(),
271 self.address(),
272 ))
273 }
274
275 pub fn all_events(&self) -> AllEventsBuilder<T, RawLog> {
278 AllEventsBuilder::new(self.web3(), self.address(), self.deployment_information())
279 }
280}
281
282#[derive(Debug, Clone)]
284pub struct Linker {
285 interface: Arc<Interface>,
287 bytecode: Bytecode,
289}
290
291impl Linker {
292 pub fn new(contract: Contract) -> Linker {
294 Linker {
295 interface: contract.interface,
296 bytecode: contract.bytecode,
297 }
298 }
299
300 pub fn library<S>(mut self, name: S, address: Address) -> Result<Linker, LinkError>
309 where
310 S: AsRef<str>,
311 {
312 self.bytecode.link(name, address)?;
313 Ok(self)
314 }
315
316 pub fn deploy<T, P>(
319 self,
320 web3: Web3<T>,
321 params: P,
322 ) -> Result<DeployBuilder<T, Instance<T>>, DeployError>
323 where
324 T: Transport,
325 P: Tokenize,
326 {
327 DeployBuilder::new(web3, self, params)
328 }
329}
330
331impl<T: Transport> Deploy<T> for Instance<T> {
332 type Context = Linker;
333
334 fn abi(cx: &Self::Context) -> &Abi {
335 &cx.interface.abi
336 }
337
338 fn bytecode(cx: &Self::Context) -> &Bytecode {
339 &cx.bytecode
340 }
341
342 fn from_deployment(
343 web3: Web3<T>,
344 address: Address,
345 transaction_hash: H256,
346 cx: Self::Context,
347 ) -> Self {
348 Instance::with_deployment_info(
349 web3,
350 cx.interface,
351 address,
352 Some(DeploymentInformation::TransactionHash(transaction_hash)),
353 )
354 }
355}
356
357#[cfg(test)]
358mod tests {
359 use super::*;
360 use crate::test::prelude::*;
361 use ethcontract_common::contract::Network;
362
363 #[test]
364 fn deployed() {
365 let mut transport = TestTransport::new();
366 let web3 = Web3::new(transport.clone());
367
368 let address = addr!("0x0102030405060708091011121314151617181920");
369 let contract = {
370 let mut contract = Contract::empty();
371 contract.networks.insert(
372 "42".to_string(),
373 Network {
374 address,
375 deployment_information: Some(H256::repeat_byte(0x42).into()),
376 },
377 );
378 contract
379 };
380
381 transport.add_response(json!("0x2a")); let instance = Instance::deployed(web3, contract)
383 .immediate()
384 .expect("successful deployment");
385
386 transport.assert_request("eth_chainId", &[]);
387 transport.assert_no_more_requests();
388
389 assert_eq!(instance.address(), address);
390 assert_eq!(
391 instance.deployment_information(),
392 Some(DeploymentInformation::TransactionHash(H256::repeat_byte(
393 0x42
394 )))
395 );
396 }
397
398 #[test]
399 fn deployed_not_found() {
400 let mut transport = TestTransport::new();
401 let web3 = Web3::new(transport.clone());
402
403 transport.add_response(json!("0x2a")); let err = Instance::deployed(web3, Contract::empty())
405 .immediate()
406 .expect_err("unexpected success getting deployed contract");
407
408 transport.assert_request("eth_chainId", &[]);
409 transport.assert_no_more_requests();
410
411 assert!(
412 match &err {
413 DeployError::NotFound(id) => id == "42",
414 _ => false,
415 },
416 "expected network 42 not found error but got '{:?}'",
417 err
418 );
419 }
420}