Skip to main content

o2_tools/
contract_ext.rs

1use crate::{
2    call_handler_ext::{
3        TransactionConfig,
4        maybe_return_coins,
5    },
6    utxo_manager::SharedUtxoManager,
7    wallet_ext::{
8        BuilderData,
9        WalletExt,
10    },
11};
12use fuel_core_types::{
13    fuel_tx::{
14        Create,
15        Output,
16        TxPointer,
17        UniqueIdentifier,
18        Witness,
19    },
20    fuel_types::canonical::Serialize,
21};
22use fuels::{
23    accounts::ViewOnlyAccount,
24    prelude::{
25        Contract,
26        Error as FuelsError,
27        Result as FuelsResult,
28        Wallet,
29    },
30    programs::contract::{
31        DeployResponse,
32        Regular,
33    },
34};
35use std::future::Future;
36
37pub trait ContractExt {
38    fn almost_sync_deploy(
39        self,
40        account: &Wallet,
41        builder_data: &BuilderData,
42        utxo_manager: &SharedUtxoManager,
43        tx_config: &Option<TransactionConfig>,
44    ) -> impl Future<Output = FuelsResult<DeployResponse>>;
45}
46
47impl ContractExt for Contract<Regular> {
48    /// Deploys a compiled contract to a running node.
49    /// To deploy a contract, you need an account with enough assets to pay for deployment.
50    /// This account will also receive the change.
51    async fn almost_sync_deploy(
52        self,
53        account: &Wallet,
54        builder_data: &BuilderData,
55        utxo_manager: &SharedUtxoManager,
56        tx_config: &Option<TransactionConfig>,
57    ) -> FuelsResult<DeployResponse> {
58        let consensus_parameters = &builder_data.consensus_parameters;
59        let max_fee = builder_data.max_fee();
60        let base_asset_id = *consensus_parameters.base_asset_id();
61        let chain_id = consensus_parameters.chain_id();
62        let secret_key = account.signer().secret_key();
63        let owner = account.address();
64
65        let contract_id = self.contract_id();
66        let state_root = self.state_root();
67        let salt = self.salt();
68        let storage_slots = self.storage_slots();
69        let code = self.code();
70        let bytecode = Witness::from(code);
71
72        let witness_limit = bytecode.size() + crate::wallet_ext::SIGNATURE_MARGIN;
73
74        let mut builder = fuel_core_types::fuel_tx::TransactionBuilder::<Create>::create(
75            bytecode,
76            salt,
77            storage_slots.to_vec(),
78        );
79
80        builder
81            .with_chain_id(consensus_parameters.chain_id())
82            .max_fee_limit(max_fee)
83            .witness_limit(witness_limit as u64);
84
85        let input_coins = {
86            utxo_manager
87                .lock()
88                .await
89                .guaranteed_extract_coins(owner, base_asset_id, max_fee as u128)
90                .map_err(|e| FuelsError::Other(e.to_string()))
91        }?;
92
93        for coin in input_coins.iter() {
94            builder.add_unsigned_coin_input(
95                secret_key,
96                coin.utxo_id,
97                coin.amount,
98                coin.asset_id,
99                TxPointer::default(),
100            );
101        }
102
103        builder.add_output(Output::Change {
104            to: owner,
105            amount: 0,
106            asset_id: base_asset_id,
107        });
108
109        builder.add_output(Output::ContractCreated {
110            contract_id,
111            state_root,
112        });
113
114        let tx = builder.finalize_as_transaction();
115        let tx_id = tx.id(&chain_id);
116
117        maybe_return_coins(
118            account,
119            &tx,
120            tx_id,
121            tx_config.and_then(|c| c.expiration_height),
122            utxo_manager,
123        );
124
125        let result = account
126            .send_transaction(chain_id, &tx)
127            .await
128            .map_err(|e| FuelsError::Other(e.to_string()))?;
129        let tx_id = Some(result.tx_id);
130
131        {
132            let mut utxo_manager = utxo_manager.lock().await;
133            utxo_manager.load_from_coins(result.known_coins.into_iter());
134            utxo_manager.load_from_coins(result.dynamic_coins.into_iter());
135        }
136
137        Ok(DeployResponse {
138            tx_status: Some(result.tx_status.take_success_checked(None)?),
139            tx_id,
140            contract_id,
141        })
142    }
143}