ic_web3_rs/contract/
deploy.rs

1////! Contract deployment utilities
2//
3//use crate::{
4//    api::{Eth, Namespace},
5//    confirm,
6//    contract::{tokens::Tokenize, Contract, Options},
7//    error,
8//    transports::ic_http_client::CallOptions,
9//    types::{Address, Bytes, TransactionReceipt, TransactionRequest},
10//    Transport,
11//};
12//#[cfg(feature = "signing")]
13//use crate::{signing::Key, types::TransactionParameters};
14//use futures::{Future, TryFutureExt};
15//use std::{collections::HashMap, time};
16//
17//pub use crate::contract::error::deploy::Error;
18//
19///// A configuration builder for contract deployment.
20//#[derive(Debug)]
21//pub struct Builder<T: Transport> {
22//    pub(crate) eth: Eth<T>,
23//    pub(crate) abi: ethabi::Contract,
24//    pub(crate) options: Options,
25//    pub(crate) confirmations: usize,
26//    pub(crate) poll_interval: time::Duration,
27//    pub(crate) linker: HashMap<String, Address>,
28//}
29//
30//impl<T: Transport> Builder<T> {
31//    /// Number of confirmations required after code deployment.
32//    pub fn confirmations(mut self, confirmations: usize) -> Self {
33//        self.confirmations = confirmations;
34//        self
35//    }
36//
37//    /// Deployment transaction options.
38//    pub fn options(mut self, options: Options) -> Self {
39//        self.options = options;
40//        self
41//    }
42//
43//    /// Confirmations poll interval.
44//    pub fn poll_interval(mut self, interval: time::Duration) -> Self {
45//        self.poll_interval = interval;
46//        self
47//    }
48//
49//    /// Execute deployment passing code and contructor parameters.
50//    pub async fn execute<P, V>(
51//        self,
52//        code: V,
53//        params: P,
54//        from: Address,
55//        options: CallOptions,
56//    ) -> Result<Contract<T>, Error>
57//    where
58//        P: Tokenize,
59//        V: AsRef<str>,
60//    {
61//        let transport = self.eth.transport().clone();
62//        let poll_interval = self.poll_interval;
63//        let confirmations = self.confirmations;
64//
65//        self.do_execute(code, params, from, move |tx| {
66//            confirm::send_transaction_with_confirmation(transport, tx, poll_interval, confirmations, options)
67//        })
68//        .await
69//    }
70//    /// Execute deployment passing code and constructor parameters.
71//    ///
72//    /// Unlike the above `execute`, this method uses
73//    /// `sign_raw_transaction_with_confirmation` instead of
74//    /// `sign_transaction_with_confirmation`, which requires the account from
75//    /// which the transaction is sent to be unlocked.
76//    pub async fn sign_and_execute<P, V>(
77//        self,
78//        code: V,
79//        params: P,
80//        from: Address,
81//        password: &str,
82//        options: CallOptions,
83//    ) -> Result<Contract<T>, Error>
84//    where
85//        P: Tokenize,
86//        V: AsRef<str>,
87//    {
88//        let transport = self.eth.transport().clone();
89//        let poll_interval = self.poll_interval;
90//        let confirmations = self.confirmations;
91//
92//        self.do_execute(code, params, from, move |tx| {
93//            crate::api::Personal::new(transport.clone())
94//                .sign_transaction(tx, password, options.clone())
95//                .and_then(move |signed_tx| {
96//                    confirm::send_raw_transaction_with_confirmation(
97//                        transport,
98//                        signed_tx.raw,
99//                        poll_interval,
100//                        confirmations,
101//                        options,
102//                    )
103//                })
104//        })
105//        .await
106//    }
107//
108//    /// Execute deployment passing code and constructor parameters.
109//    ///
110//    /// Unlike the above `sign_and_execute`, this method allows the
111//    /// caller to pass in a private key to sign the transaction with
112//    /// and therefore allows deploying from an account that the
113//    /// ethereum node doesn't need to know the private key for.
114//    ///
115//    /// An optional `chain_id` parameter can be passed to provide
116//    /// replay protection for transaction signatures. Passing `None`
117//    /// would create a transaction WITHOUT replay protection and
118//    /// should be avoided.
119//    /// You can obtain `chain_id` of the network you are connected
120//    /// to using `web3.eth().chain_id()` method.
121//    #[cfg(feature = "signing")]
122//    pub async fn sign_with_key_and_execute<P, V, K>(
123//        self,
124//        code: V,
125//        params: P,
126//        from: K,
127//        chain_id: Option<u64>,
128//    ) -> Result<Contract<T>, Error>
129//    where
130//        P: Tokenize,
131//        V: AsRef<str>,
132//        K: Key,
133//    {
134//        let transport = self.eth.transport().clone();
135//        let poll_interval = self.poll_interval;
136//        let confirmations = self.confirmations;
137//
138//        self.do_execute(code, params, from.address(), move |tx| async move {
139//            let tx = TransactionParameters {
140//                nonce: tx.nonce,
141//                to: tx.to,
142//                gas: tx.gas.unwrap_or_else(|| 1_000_000.into()),
143//                gas_price: tx.gas_price,
144//                value: tx.value.unwrap_or_else(|| 0.into()),
145//                data: tx
146//                    .data
147//                    .expect("Tried to deploy a contract but transaction data wasn't set"),
148//                chain_id,
149//                transaction_type: tx.transaction_type,
150//                access_list: tx.access_list,
151//                max_fee_per_gas: tx.max_fee_per_gas,
152//                max_priority_fee_per_gas: tx.max_priority_fee_per_gas,
153//            };
154//            let signed_tx = crate::api::Accounts::new(transport.clone())
155//                .sign_transaction(tx, from)
156//                .await?;
157//            confirm::send_raw_transaction_with_confirmation(
158//                transport,
159//                signed_tx.raw_transaction,
160//                poll_interval,
161//                confirmations,
162//            )
163//            .await
164//        })
165//        .await
166//    }
167//
168//    async fn do_execute<P, V, Ft>(
169//        self,
170//        code: V,
171//        params: P,
172//        from: Address,
173//        send: impl FnOnce(TransactionRequest) -> Ft,
174//    ) -> Result<Contract<T>, Error>
175//    where
176//        P: Tokenize,
177//        V: AsRef<str>,
178//        Ft: Future<Output = error::Result<TransactionReceipt>>,
179//    {
180//        let options = self.options;
181//        let eth = self.eth;
182//        let abi = self.abi;
183//
184//        let mut code_hex = code.as_ref().to_string();
185//
186//        for (lib, address) in self.linker {
187//            if lib.len() > 38 {
188//                return Err(Error::Abi(ethabi::Error::InvalidName(
189//                    "The library name should be under 39 characters.".into(),
190//                )));
191//            }
192//            let replace = format!("__{:_<38}", lib); // This makes the required width 38 characters and will pad with `_` to match it.
193//            let address: String = hex::encode(address);
194//            code_hex = code_hex.replacen(&replace, &address, 1);
195//        }
196//        code_hex = code_hex.replace("\"", "").replace("0x", ""); // This is to fix truffle + serde_json redundant `"` and `0x`
197//        let code =
198//            hex::decode(&code_hex).map_err(|e| ethabi::Error::InvalidName(format!("hex decode error: {}", e)))?;
199//
200//        let params = params.into_tokens();
201//        let data = match (abi.constructor(), params.is_empty()) {
202//            (None, false) => {
203//                return Err(Error::Abi(ethabi::Error::InvalidName(
204//                    "Constructor is not defined in the ABI.".into(),
205//                )));
206//            }
207//            (None, true) => code,
208//            (Some(constructor), _) => constructor.encode_input(code, &params)?,
209//        };
210//
211//        let tx = TransactionRequest {
212//            from,
213//            to: None,
214//            gas: options.gas,
215//            gas_price: options.gas_price,
216//            value: options.value,
217//            nonce: options.nonce,
218//            data: Some(Bytes(data)),
219//            condition: options.condition,
220//            transaction_type: options.transaction_type,
221//            access_list: options.access_list,
222//            max_fee_per_gas: options.max_fee_per_gas,
223//            max_priority_fee_per_gas: options.max_priority_fee_per_gas,
224//        };
225//        let receipt = send(tx).await?;
226//        match receipt.status {
227//            Some(status) if status == 0.into() => Err(Error::ContractDeploymentFailure(receipt.transaction_hash)),
228//            // If the `status` field is not present we use the presence of `contract_address` to
229//            // determine if deployment was successfull.
230//            _ => match receipt.contract_address {
231//                Some(address) => Ok(Contract::new(eth, address, abi)),
232//                None => Err(Error::ContractDeploymentFailure(receipt.transaction_hash)),
233//            },
234//        }
235//    }
236//}
237//
238//#[cfg(test)]
239//mod tests {
240//    use crate::{
241//        api::{self, Namespace},
242//        contract::{Contract, Options},
243//        rpc,
244//        transports::{ic_http_client::CallOptions, test::TestTransport},
245//        types::{Address, Call, U256},
246//    };
247//    use serde_json::Value;
248//    use std::collections::HashMap;
249//
250//    #[test]
251//    fn should_deploy_a_contract() {
252//        // given
253//        let mut transport = TestTransport::default();
254//        // Transaction Hash
255//        transport.add_response(rpc::Value::String(
256//            "0x70ae45a5067fdf3356aa615ca08d925a38c7ff21b486a61e79d5af3969ebc1a1".into(),
257//        ));
258//        // BlockFilter
259//        transport.add_response(rpc::Value::String("0x0".into()));
260//        // getFilterChanges
261//        transport.add_response(rpc::Value::Array(vec![rpc::Value::String(
262//            "0xd5311584a9867d8e129113e1ec9db342771b94bd4533aeab820a5bcc2c54878f".into(),
263//        )]));
264//        transport.add_response(rpc::Value::Array(vec![rpc::Value::String(
265//            "0xd5311584a9867d8e129113e1ec9db342771b94bd4533aeab820a5bcc2c548790".into(),
266//        )]));
267//        // receipt
268//        let receipt = ::serde_json::from_str::<rpc::Value>(
269//            "{\"blockHash\":\"0xd5311584a9867d8e129113e1ec9db342771b94bd4533aeab820a5bcc2c54878f\",\"blockNumber\":\"0x256\",\"contractAddress\":\"0x600515dfe465f600f0c9793fa27cd2794f3ec0e1\",\"from\": \"0x407d73d8a49eeb85d32cf465507dd71d507100c1\",\"cumulativeGasUsed\":\"0xe57e0\",\"gasUsed\":\"0xe57e0\",\"logs\":[],\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"root\":null,\"transactionHash\":\"0x70ae45a5067fdf3356aa615ca08d925a38c7ff21b486a61e79d5af3969ebc1a1\",\"transactionIndex\":\"0x0\", \"status\": \"0x1\", \"effectiveGasPrice\": \"0x100\"}"
270//        ).unwrap();
271//        transport.add_response(receipt.clone());
272//        // block number
273//        transport.add_response(rpc::Value::String("0x25a".into()));
274//        // receipt again
275//        transport.add_response(receipt);
276//
277//        {
278//            let builder = Contract::deploy(api::Eth::new(&transport), include_bytes!("./res/token.json")).unwrap();
279//
280//            // when
281//            futures::executor::block_on(
282//                builder
283//                    .options(Options::with(|opt| opt.value = Some(5.into())))
284//                    .confirmations(1)
285//                    .execute(
286//                        "0x01020304",
287//                        (U256::from(1_000_000), "My Token".to_owned(), 3u64, "MT".to_owned()),
288//                        Address::from_low_u64_be(5),
289//                        CallOptions::default(),
290//                    ),
291//            )
292//            .unwrap()
293//        };
294//
295//        // then
296//        transport.assert_request("eth_sendTransaction", &[
297//      "{\"data\":\"0x0102030400000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000084d7920546f6b656e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024d54000000000000000000000000000000000000000000000000000000000000\",\"from\":\"0x0000000000000000000000000000000000000005\",\"value\":\"0x5\"}".into(),
298//    ]);
299//        transport.assert_request("eth_newBlockFilter", &[]);
300//        transport.assert_request("eth_getFilterChanges", &["\"0x0\"".into()]);
301//        transport.assert_request("eth_getFilterChanges", &["\"0x0\"".into()]);
302//        transport.assert_request(
303//            "eth_getTransactionReceipt",
304//            &["\"0x70ae45a5067fdf3356aa615ca08d925a38c7ff21b486a61e79d5af3969ebc1a1\"".into()],
305//        );
306//        transport.assert_request("eth_blockNumber", &[]);
307//        transport.assert_request(
308//            "eth_getTransactionReceipt",
309//            &["\"0x70ae45a5067fdf3356aa615ca08d925a38c7ff21b486a61e79d5af3969ebc1a1\"".into()],
310//        );
311//        transport.assert_no_more_requests();
312//    }
313//
314//    #[test]
315//    fn deploy_linked_contract() {
316//        use serde_json::{to_string, to_vec};
317//        let mut transport = TestTransport::default();
318//        let receipt = ::serde_json::from_str::<rpc::Value>(
319//        "{\"blockHash\":\"0xd5311584a9867d8e129113e1ec9db342771b94bd4533aeab820a5bcc2c54878f\",\"blockNumber\":\"0x256\",\"contractAddress\":\"0x600515dfe465f600f0c9793fa27cd2794f3ec0e1\",\"from\":\"0x407d73d8a49eeb85d32cf465507dd71d507100c1\",\"cumulativeGasUsed\":\"0xe57e0\",\"gasUsed\":\"0xe57e0\",\"logs\":[],\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"root\":null,\"transactionHash\":\"0x70ae45a5067fdf3356aa615ca08d925a38c7ff21b486a61e79d5af3969ebc1a1\",\"transactionIndex\":\"0x0\", \"status\": \"0x1\", \"effectiveGasPrice\": \"0x100\"}"
320//        ).unwrap();
321//
322//        for _ in 0..2 {
323//            transport.add_response(rpc::Value::String(
324//                "0x70ae45a5067fdf3356aa615ca08d925a38c7ff21b486a61e79d5af3969ebc1a1".into(),
325//            ));
326//            transport.add_response(rpc::Value::String("0x0".into()));
327//            transport.add_response(rpc::Value::Array(vec![rpc::Value::String(
328//                "0xd5311584a9867d8e129113e1ec9db342771b94bd4533aeab820a5bcc2c54878f".into(),
329//            )]));
330//            transport.add_response(rpc::Value::Array(vec![rpc::Value::String(
331//                "0xd5311584a9867d8e129113e1ec9db342771b94bd4533aeab820a5bcc2c548790".into(),
332//            )]));
333//            transport.add_response(receipt.clone());
334//            transport.add_response(rpc::Value::String("0x25a".into()));
335//            transport.add_response(receipt.clone());
336//        }
337//
338//        let lib: Value = serde_json::from_slice(include_bytes!("./res/MyLibrary.json")).unwrap();
339//        let lib_abi: Vec<u8> = to_vec(&lib["abi"]).unwrap();
340//        let lib_code = to_string(&lib["bytecode"]).unwrap();
341//
342//        let main: Value = serde_json::from_slice(include_bytes!("./res/Main.json")).unwrap();
343//        let main_abi: Vec<u8> = to_vec(&main["abi"]).unwrap();
344//        let main_code = to_string(&main["bytecode"]).unwrap();
345//
346//        let lib_address;
347//        {
348//            let builder = Contract::deploy(api::Eth::new(&transport), &lib_abi).unwrap();
349//            lib_address =
350//                futures::executor::block_on(builder.execute(lib_code, (), Address::zero(), CallOptions::default()))
351//                    .unwrap()
352//                    .address();
353//        }
354//
355//        transport.assert_request("eth_sendTransaction", &[
356//            "{\"data\":\"0x60ad61002f600b82828239805160001a6073146000811461001f57610021565bfe5b5030600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600436106056576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063f8a8fd6d14605b575b600080fd5b60616077565b6040518082815260200191505060405180910390f35b600061010090509056fea165627a7a72305820b50091adcb7ef9987dd8daa665cec572801bf8243530d70d52631f9d5ddb943e0029\",\"from\":\"0x0000000000000000000000000000000000000000\"}"
357//            .into()]);
358//        transport.assert_request("eth_newBlockFilter", &[]);
359//        transport.assert_request("eth_getFilterChanges", &["\"0x0\"".into()]);
360//        transport.assert_request("eth_getFilterChanges", &["\"0x0\"".into()]);
361//        transport.assert_request(
362//            "eth_getTransactionReceipt",
363//            &["\"0x70ae45a5067fdf3356aa615ca08d925a38c7ff21b486a61e79d5af3969ebc1a1\"".into()],
364//        );
365//        transport.assert_request("eth_blockNumber", &[]);
366//        transport.assert_request(
367//            "eth_getTransactionReceipt",
368//            &["\"0x70ae45a5067fdf3356aa615ca08d925a38c7ff21b486a61e79d5af3969ebc1a1\"".into()],
369//        );
370//        transport.assert_no_more_requests();
371//        {
372//            let builder = Contract::deploy_from_truffle(api::Eth::new(&transport), &main_abi, {
373//                let mut linker = HashMap::new();
374//                linker.insert("MyLibrary", lib_address);
375//                linker
376//            })
377//            .unwrap();
378//            let _ =
379//                futures::executor::block_on(builder.execute(main_code, (), Address::zero(), CallOptions::default()))
380//                    .unwrap();
381//        }
382//
383//        transport.assert_request("eth_sendTransaction", &[
384//            "{\"data\":\"0x608060405234801561001057600080fd5b5061013f806100206000396000f3fe608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063f8a8fd6d14610046575b600080fd5b34801561005257600080fd5b5061005b610071565b6040518082815260200191505060405180910390f35b600073600515dfe465f600f0c9793fa27cd2794f3ec0e163f8a8fd6d6040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b1580156100d357600080fd5b505af41580156100e7573d6000803e3d6000fd5b505050506040513d60208110156100fd57600080fd5b810190808051906020019092919050505090509056fea165627a7a72305820580d3776b3d132142f431e141a2e20bd4dd4907fa304feea7b604e8f39ed59520029\",\"from\":\"0x0000000000000000000000000000000000000000\"}"
385//            .into()]);
386//
387//        transport.assert_request("eth_newBlockFilter", &[]);
388//        transport.assert_request("eth_getFilterChanges", &["\"0x0\"".into()]);
389//        transport.assert_request("eth_getFilterChanges", &["\"0x0\"".into()]);
390//        transport.assert_request(
391//            "eth_getTransactionReceipt",
392//            &["\"0x70ae45a5067fdf3356aa615ca08d925a38c7ff21b486a61e79d5af3969ebc1a1\"".into()],
393//        );
394//        transport.assert_request("eth_blockNumber", &[]);
395//        transport.assert_request(
396//            "eth_getTransactionReceipt",
397//            &["\"0x70ae45a5067fdf3356aa615ca08d925a38c7ff21b486a61e79d5af3969ebc1a1\"".into()],
398//        );
399//        transport.assert_no_more_requests();
400//    }
401//}