ethcontract/contract/
deploy.rs1use crate::errors::{DeployError, ExecutionError};
5use crate::tokens::Tokenize;
6use crate::transaction::{Account, GasPrice, TransactionBuilder, TransactionResult};
7use ethcontract_common::abi::Error as AbiError;
8use ethcontract_common::{Abi, Bytecode};
9use std::marker::PhantomData;
10use web3::api::Web3;
11use web3::types::{Address, Bytes, H256, U256};
12use web3::Transport;
13
14pub trait Deploy<T: Transport>: Sized {
21 type Context;
23
24 fn bytecode(cx: &Self::Context) -> &Bytecode;
26
27 fn abi(cx: &Self::Context) -> &Abi;
29
30 fn from_deployment(
32 web3: Web3<T>,
33 address: Address,
34 transaction_hash: H256,
35 cx: Self::Context,
36 ) -> Self;
37}
38
39#[derive(Debug, Clone)]
41#[must_use = "deploy builers do nothing unless you `.deploy()` them"]
42pub struct DeployBuilder<T, I>
43where
44 T: Transport,
45 I: Deploy<T>,
46{
47 web3: Web3<T>,
49 context: I::Context,
51 tx: TransactionBuilder<T>,
53 _instance: PhantomData<I>,
54}
55
56impl<T, I> DeployBuilder<T, I>
57where
58 T: Transport,
59 I: Deploy<T>,
60{
61 pub fn new<P>(web3: Web3<T>, context: I::Context, params: P) -> Result<Self, DeployError>
64 where
65 P: Tokenize,
66 {
67 let bytecode = I::bytecode(&context);
72 if bytecode.is_empty() {
73 return Err(DeployError::EmptyBytecode);
74 }
75
76 let code = bytecode.to_bytes()?;
77 let params = match params.into_token() {
78 ethcontract_common::abi::Token::Tuple(tokens) => tokens,
79 _ => unreachable!("function arguments are always tuples"),
80 };
81 let data = match (I::abi(&context).constructor(), params.is_empty()) {
82 (None, false) => return Err(AbiError::InvalidData.into()),
83 (None, true) => code,
84 (Some(ctor), _) => Bytes(ctor.encode_input(code.0, ¶ms)?),
85 };
86
87 Ok(DeployBuilder {
88 web3: web3.clone(),
89 context,
90 tx: TransactionBuilder::new(web3).data(data).confirmations(0),
91 _instance: PhantomData,
92 })
93 }
94
95 pub fn from(mut self, value: Account) -> Self {
98 self.tx = self.tx.from(value);
99 self
100 }
101
102 pub fn gas(mut self, value: U256) -> Self {
105 self.tx = self.tx.gas(value);
106 self
107 }
108
109 pub fn gas_price(mut self, value: GasPrice) -> Self {
112 self.tx = self.tx.gas_price(value);
113 self
114 }
115
116 pub fn value(mut self, value: U256) -> Self {
119 self.tx = self.tx.value(value);
120 self
121 }
122
123 pub fn nonce(mut self, value: U256) -> Self {
126 self.tx = self.tx.nonce(value);
127 self
128 }
129
130 pub fn confirmations(mut self, value: usize) -> Self {
134 self.tx = self.tx.confirmations(value);
135 self
136 }
137
138 pub fn into_inner(self) -> TransactionBuilder<T> {
141 self.tx
142 }
143
144 pub async fn deploy(self) -> Result<I, DeployError> {
147 let tx = match self.tx.send().await? {
148 TransactionResult::Receipt(tx) => tx,
149 TransactionResult::Hash(tx) => return Err(DeployError::Pending(tx)),
150 };
151
152 let transaction_hash = tx.transaction_hash;
153 let address = tx
154 .contract_address
155 .ok_or_else(|| ExecutionError::Failure(Box::new(tx)))?;
156
157 Ok(I::from_deployment(
158 self.web3,
159 address,
160 transaction_hash,
161 self.context,
162 ))
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169 use crate::contract::{Instance, Linker};
170 use crate::test::prelude::*;
171 use ethcontract_common::Contract;
172
173 type InstanceDeployBuilder<T> = DeployBuilder<T, Instance<T>>;
174
175 #[test]
176 fn deploy_tx_options() {
177 let transport = TestTransport::new();
178 let web3 = Web3::new(transport.clone());
179
180 let from = addr!("0x9876543210987654321098765432109876543210");
181 let bytecode = Bytecode::from_hex_str("0x42").unwrap();
182 let contract = Contract {
183 bytecode: bytecode.clone(),
184 ..Contract::empty()
185 };
186 let linker = Linker::new(contract);
187 let tx = InstanceDeployBuilder::new(web3, linker, ())
188 .expect("error creating deploy builder")
189 .from(Account::Local(from, None))
190 .gas(1.into())
191 .gas_price(2.0.into())
192 .value(28.into())
193 .nonce(42.into())
194 .into_inner();
195
196 assert_eq!(tx.from.map(|a| a.address()), Some(from));
197 assert_eq!(tx.to, None);
198 assert_eq!(tx.gas, Some(1.into()));
199 assert_eq!(tx.gas_price, Some(2.0.into()));
200 assert_eq!(tx.value, Some(28.into()));
201 assert_eq!(tx.data, Some(bytecode.to_bytes().unwrap()));
202 assert_eq!(tx.nonce, Some(42.into()));
203 transport.assert_no_more_requests();
204 }
205
206 #[test]
207 fn deploy() {
208 }
211
212 #[test]
213 fn deploy_fails_on_empty_bytecode() {
214 let transport = TestTransport::new();
215 let web3 = Web3::new(transport.clone());
216
217 let contract = Contract::empty();
218 let linker = Linker::new(contract);
219 let error = InstanceDeployBuilder::new(web3, linker, ()).err().unwrap();
220
221 assert_eq!(error.to_string(), DeployError::EmptyBytecode.to_string());
222 transport.assert_no_more_requests();
223 }
224}