From b9cac99411072329a85e6fc48af23a1c1ce2dcde Mon Sep 17 00:00:00 2001 From: hal3e Date: Fri, 17 Nov 2023 23:26:05 +0100 Subject: [PATCH] feat!: `beta-5` support (#1200) BREAKING CHANGE: - `TxParameters` are replaced with `TxPolicies` - `GasPrice` and `Maturity` fields are optional - `TxPolicies` introduced new fields: - `WitnessLimit` - allows the limitation of the maximum size of witnesses in bytes. - `MaxFee` - allows the upper bound for the maximum fee that users agree to pay for the transaction. - The `ScriptGasLimit` only limits the script execution. Previously, the `ScriptGasLimit` also limited the predicate execution time, but it is not valid anymore. So, it is not possible to use the `ScriptGasLimit` for transaction cost limitations. A new `MaxFee` policy is a way to do that. The `GasLimit` field was removed from the `Create` transaction because it only relates to the script execution(which the `Create` transaction doesn't have). - The new `WhitnessLimit` also impacts the `max_gas` and `max_fee` calculation along with the `ScriptGasLimit`(in the case of `Create` transaction only `WitnessLimit` affects the `max_gas` and `max_fee`). - The minimal gas also charges the user for transaction ID calculation. - Each transaction requires setting the `GasPrice` policy. - Previously, `GasLimit` should be less than the `MAX_GAS_PER_TX` constant. After removing this field from the `Create` transaction, it is impossible to require it. Instead, it requires that `max_gas <= MAX_GAS_PER_TX` for any transaction. Consequently, any `Script` transaction that uses `MAX_GAS_PER_TX` as a `GasLimit` will always fail because of a new rule. Setting the estimated gas usage instead solves the problem. - If the `max_fee > policies.max_fee`, then transaction will be rejected. - If the `witnessses_size > policies.witness_limit`, then transaction will be rejected. - `get_message_proof` not uses `Nonce` instead of the message_id - predicates do not use `ChainId` for address calculation - `manual_blocks_enabled` is replaced with `debug` in local chain config - `fee_checked_from_tx` uses `FeeParameters` - `fuel_tx::ConsensusParameters` were refactored - this also affects us - when building a transaction_builder the `BuildableTransacion` trait needs to be in scope - `utxo_validation` and `manual_blocks` are enabled by default for test providers - node config does not have `local_node` anymore use `default` - `let node_config = Config::default();` Thanks @MujkicA for updating the documentation. Co-authored-by: MujkicA Co-authored-by: Ahmed Sagdati <37515857+segfault-magnet@users.noreply.github.com> --- .github/workflows/ci.yml | 21 +- Cargo.toml | 28 +- docs/src/SUMMARY.md | 2 +- docs/src/accounts.md | 2 +- docs/src/calling-contracts/index.md | 2 +- docs/src/calling-contracts/multicalls.md | 4 +- docs/src/calling-contracts/tx-params.md | 31 -- docs/src/calling-contracts/tx-policies.md | 45 +++ docs/src/running-scripts.md | 6 +- examples/contracts/src/lib.rs | 79 ++-- examples/cookbook/src/lib.rs | 32 +- examples/predicates/src/lib.rs | 8 +- examples/providers/src/lib.rs | 1 + examples/wallets/src/lib.rs | 14 +- packages/fuels-accounts/src/account.rs | 111 +++--- packages/fuels-accounts/src/accounts_utils.rs | 14 +- packages/fuels-accounts/src/coin_cache.rs | 11 +- packages/fuels-accounts/src/predicate.rs | 22 +- packages/fuels-accounts/src/provider.rs | 89 ++--- .../src/provider/retryable_client.rs | 16 +- .../src/provider/supported_versions.rs | 2 +- packages/fuels-core/Cargo.toml | 1 + packages/fuels-core/src/codec/logs.rs | 3 +- packages/fuels-core/src/types/errors.rs | 12 +- packages/fuels-core/src/types/param_types.rs | 3 +- .../src/types/transaction_builders.rs | 365 +++++++++++++----- .../src/types/wrappers/chain_info.rs | 12 +- .../src/types/wrappers/coin_type.rs | 3 +- .../src/types/wrappers/transaction.rs | 273 ++++++++----- .../types/wrappers/transaction_response.rs | 6 +- packages/fuels-core/src/utils/constants.rs | 7 + packages/fuels-core/src/utils/offsets.rs | 6 +- .../src/setup_program_test/code_gen.rs | 15 +- packages/fuels-programs/src/call_utils.rs | 18 +- packages/fuels-programs/src/contract.rs | 48 +-- packages/fuels-programs/src/script_calls.rs | 26 +- packages/fuels-test-helpers/Cargo.toml | 4 +- packages/fuels-test-helpers/src/accounts.rs | 42 +- .../src/fuel_bin_service.rs | 2 +- packages/fuels-test-helpers/src/lib.rs | 56 ++- packages/fuels-test-helpers/src/node_types.rs | 32 +- packages/fuels/Forc.toml | 3 + packages/fuels/src/lib.rs | 5 +- packages/fuels/tests/configurables.rs | 4 +- packages/fuels/tests/contracts.rs | 70 ++-- .../revert_transaction_error/src/main.sw | 10 +- packages/fuels/tests/logs.rs | 9 +- packages/fuels/tests/predicates.rs | 75 ++-- packages/fuels/tests/providers.rs | 199 +++++----- packages/fuels/tests/scripts.rs | 61 +-- packages/fuels/tests/storage.rs | 4 +- .../types/contracts/vector_output/src/main.sw | 11 +- .../types/scripts/script_vectors/src/main.sw | 9 +- packages/fuels/tests/types_contracts.rs | 2 +- packages/fuels/tests/types_predicates.rs | 2 +- packages/fuels/tests/types_scripts.rs | 8 +- packages/fuels/tests/wallets.rs | 51 ++- packages/wasm-tests/src/lib.rs | 4 +- 58 files changed, 1143 insertions(+), 858 deletions(-) delete mode 100644 docs/src/calling-contracts/tx-params.md create mode 100644 docs/src/calling-contracts/tx-policies.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d97a4f400b..60bae477e6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,12 +16,13 @@ env: CARGO_TERM_COLOR: always DASEL_VERSION: https://github.com/TomWright/dasel/releases/download/v2.3.6/dasel_linux_amd64 RUSTFLAGS: "-D warnings" - FUEL_CORE_VERSION: 0.20.6 - RUST_VERSION: 1.72.1 + FUEL_CORE_VERSION: 0.21.0-rc.1 + FUEL_CORE_PATCH_BRANCH: + RUST_VERSION: 1.73.0 FORC_VERSION: 0.46.0 - FORC_PATCH_BRANCH: "xunilrj/fix-implicit-std-env-vars" + FORC_PATCH_BRANCH: "feat/transaction-policies" FORC_PATCH_REVISION: "" - FORC_IMPLICIT_STD_GIT_BRANCH: "xunilrj/fix-implicit-std-env-vars" + FORC_IMPLICIT_STD_GIT_BRANCH: "" jobs: setup-test-projects: @@ -202,10 +203,14 @@ jobs: - name: Install Fuel Core if: ${{ matrix.install_fuel_core }} run: | - curl -sSLf https://github.com/FuelLabs/fuel-core/releases/download/v${{ env.FUEL_CORE_VERSION }}/fuel-core-${{ env.FUEL_CORE_VERSION }}-x86_64-unknown-linux-gnu.tar.gz -L -o fuel-core.tar.gz - tar -xvf fuel-core.tar.gz - chmod +x fuel-core-${{ env.FUEL_CORE_VERSION }}-x86_64-unknown-linux-gnu/fuel-core - mv fuel-core-${{ env.FUEL_CORE_VERSION }}-x86_64-unknown-linux-gnu/fuel-core /usr/local/bin/fuel-core + if [[ -n $FUEL_CORE_PATCH_BRANCH ]]; then + cargo install --locked fuel-core-bin --git https://github.com/FuelLabs/fuel-core --branch "$FUEL_CORE_PATCH_BRANCH" + else + curl -sSLf https://github.com/FuelLabs/fuel-core/releases/download/v${{ env.FUEL_CORE_VERSION }}/fuel-core-${{ env.FUEL_CORE_VERSION }}-x86_64-unknown-linux-gnu.tar.gz -L -o fuel-core.tar.gz + tar -xvf fuel-core.tar.gz + chmod +x fuel-core-${{ env.FUEL_CORE_VERSION }}-x86_64-unknown-linux-gnu/fuel-core + mv fuel-core-${{ env.FUEL_CORE_VERSION }}-x86_64-unknown-linux-gnu/fuel-core /usr/local/bin/fuel-core + fi - name: Download sway example artifacts if: ${{ matrix.download_sway_artifacts }} diff --git a/Cargo.toml b/Cargo.toml index 4cf05718df..b2fe04e3f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ homepage = "https://fuel.network/" readme = "README.md" license = "Apache-2.0" repository = "https://github.com/FuelLabs/fuels-rs" -rust-version = "1.72.1" +rust-version = "1.73.0" version = "0.50.1" [workspace.dependencies] @@ -68,23 +68,25 @@ tokio = { version = "1.33.0", default-features = false } tracing = "0.1.37" trybuild = "1.0.85" uint = { version = "0.9.5", default-features = false } -which = { version = "4.4.2", default-features = false } +which = { version = "5.0.0", default-features = false } zeroize = "1.6.0" # Dependencies from the `fuel-core` repository: -fuel-core = { version = "0.20.6", default-features = false } -fuel-core-chain-config = { version = "0.20.6", default-features = false } -fuel-core-client = { version = "0.20.6", default-features = false } -fuel-core-types = { version = "0.20.6", default-features = false } +fuel-core = { version = "0.21.0-rc.1", default-features = false } +fuel-core-chain-config = { version = "0.21.0-rc.1", default-features = false } +fuel-core-client = { version = "0.21.0-rc.1", default-features = false } +fuel-core-poa = { version = "0.21.0-rc.1", default-features = false } +fuel-core-services = { version = "0.21.0-rc.1", default-features = false } +fuel-core-types = { version = "0.21.0-rc.1", default-features = false } # Dependencies from the `fuel-vm` repository: -fuel-asm = "0.35.4" -fuel-crypto = "0.35.4" -fuel-merkle = "0.35.4" -fuel-storage = "0.35.4" -fuel-tx = "0.35.4" -fuel-types = { version = "0.35.4", default-features = false } -fuel-vm = "0.35.4" +fuel-asm = { version = "0.42.0"} +fuel-crypto = { version = "0.42.0"} +fuel-merkle = { version = "0.42.0"} +fuel-storage = { version = "0.42.0"} +fuel-tx = { version = "0.42.0"} +fuel-types = { version = "0.42.0"} +fuel-vm = { version = "0.42.0"} # Workspace projects fuels = { version = "0.50.1", path = "./packages/fuels" } diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 7c899d51a2..da39b3de6e 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -28,7 +28,7 @@ - [The FuelVM Binary file](./deploying/the-fuelvm-binary-file.md) - [Calling contracts](./calling-contracts/index.md) - [Connecting wallets](./calling-contracts/calls-with-different-wallets.md) - - [Transaction parameters](./calling-contracts/tx-params.md) + - [Transaction policies](./calling-contracts/tx-policies.md) - [Call parameters](./calling-contracts/call-params.md) - [Custom asset transfer](./calling-contracts/custom-asset-transfer.md) - [Call response](./calling-contracts/call-response.md) diff --git a/docs/src/accounts.md b/docs/src/accounts.md index f1ac1ebc80..95e196b6f3 100644 --- a/docs/src/accounts.md +++ b/docs/src/accounts.md @@ -37,4 +37,4 @@ For transferring assets to the base layer chain, you can use `wallet.withdraw_to {{#include ../../examples/wallets/src/lib.rs:wallet_withdraw_to_base}} ``` -The above example creates an `Address` from a string and converts it to a `Bech32Address`. Next, it calls `wallet.withdraw_to_base_layer` by providing the address, the amount to be transferred, and the transaction parameters. Lastly, to verify that the transfer succeeded, the relevant message proof is retrieved with `provider.get_message_proof,` and the amount and the recipient are verified. +The above example creates an `Address` from a string and converts it to a `Bech32Address`. Next, it calls `wallet.withdraw_to_base_layer` by providing the address, the amount to be transferred, and the transaction policies. Lastly, to verify that the transfer succeeded, the relevant message proof is retrieved with `provider.get_message_proof,` and the amount and the recipient are verified. diff --git a/docs/src/calling-contracts/index.md b/docs/src/calling-contracts/index.md index 93c724c8de..969a5cb185 100644 --- a/docs/src/calling-contracts/index.md +++ b/docs/src/calling-contracts/index.md @@ -3,7 +3,7 @@ Once you've deployed your contract, as seen in the previous sections, you'll likely want to: 1. Call contract methods; -2. Configure call and transaction parameters such as gas price, byte price, and gas limit; +2. Configure call parameters and transaction policies; 3. Forward coins and gas in your contract calls; 4. Read and interpret returned values and logs. diff --git a/docs/src/calling-contracts/multicalls.md b/docs/src/calling-contracts/multicalls.md index 472de7203d..881e535190 100644 --- a/docs/src/calling-contracts/multicalls.md +++ b/docs/src/calling-contracts/multicalls.md @@ -8,13 +8,13 @@ With `MultiContractCallHandler`, you can execute multiple contract calls within You can also set call parameters, variable outputs, or external contracts for every contract call, as long as you don't execute it with `call()` or `simulate()`. -Next, you provide the prepared calls to your `MultiContractCallHandler` and optionally configure transaction parameters: +Next, you provide the prepared calls to your `MultiContractCallHandler` and optionally configure transaction policies: ```rust,ignore {{#include ../../../examples/contracts/src/lib.rs:multi_call_build}} ``` -> **Note:** any transaction parameters configured on separate contract calls are disregarded in favor of the parameters provided to `MultiContractCallHandler`. +> **Note:** any transaction policies configured on separate contract calls are disregarded in favor of the parameters provided to `MultiContractCallHandler`. Furthermore, if you need to separate submission from value retrieval for any reason, you can do so as follows: diff --git a/docs/src/calling-contracts/tx-params.md b/docs/src/calling-contracts/tx-params.md deleted file mode 100644 index 3b7bdb64a8..0000000000 --- a/docs/src/calling-contracts/tx-params.md +++ /dev/null @@ -1,31 +0,0 @@ -# Transaction parameters - - - -The parameters for a transaction are: - -1. Gas price -2. Gas limit -3. Maturity - -You can configure these parameters by creating an instance of `TxParameters` and passing it to a chain method called `tx_params`: - - -```rust,ignore -{{#include ../../../examples/contracts/src/lib.rs:tx_parameters}} -``` - - - -You can also use `TxParameters::default()` to use the default values. If `gas_price` or `gas_limit` is set to `None`, the SDK will use the network's default value: - - -This way: - -```rust,ignore -{{#include ../../../examples/contracts/src/lib.rs:tx_parameters_default}} -``` - -As you might have noticed already, `TxParameters` can also be specified when deploying contracts or transfering assets by passing it to the respective methods. - -> **Note:** whenever you perform an action that results in a transaction (contract deployment, contract call, asset transfer), the SDK will automatically estimate the fee based on the set gas limit and the transaction's byte size. This estimation is used when building the transaction. A side-effect of this is that your wallet must at least own a single coin of the base asset of any amount. diff --git a/docs/src/calling-contracts/tx-policies.md b/docs/src/calling-contracts/tx-policies.md new file mode 100644 index 0000000000..0a98301321 --- /dev/null +++ b/docs/src/calling-contracts/tx-policies.md @@ -0,0 +1,45 @@ +# Transaction policies + + + +Transaction policies are defined as follows: + +```rust,ignore +{{#include ../../../packages/fuels-core/src/types/wrappers/transaction.rs:tx_policies_struct}} +``` + +Where: + +1. **Gas Price** - Maximum gas price for transaction. +2. **Witness Limit** - The maximum amount of witness data allowed for the transaction. +3. **Maturity** - Block until which the transaction cannot be included. +4. **Max Fee** - The maximum fee payable by this transaction. +5. **Script Gas Limit** - The maximum amount of gas the transaction may consume for executing its script code. + +When the **Script Gas Limit** is not set, the Rust SDK will estimate the consumed gas in the background and set it as the limit. Similarly, if no **Gas Price** is defined, the Rust SDK defaults to the network's minimum gas price. + +**Witness Limit** makes use of the following default values for script and create transactions: + +```rust,ignore +{{#include ../../../packages/fuels-core/src/utils/constants.rs:witness_default}} +``` + +You can configure these parameters by creating an instance of `TxPolicies` and passing it to a chain method called `with_tx_policies`: + + +```rust,ignore +{{#include ../../../examples/contracts/src/lib.rs:tx_policies}} +``` + + + +You can also use `TxPolicies::default()` to use the default values. + + +This way: + +```rust,ignore +{{#include ../../../examples/contracts/src/lib.rs:tx_policies_default}} +``` + +As you might have noticed, `TxPolicies` can also be specified when deploying contracts or transferring assets by passing it to the respective methods. diff --git a/docs/src/running-scripts.md b/docs/src/running-scripts.md index 6f38973bb7..29a3b15ba3 100644 --- a/docs/src/running-scripts.md +++ b/docs/src/running-scripts.md @@ -12,12 +12,12 @@ Furthermore, if you need to separate submission from value retrieval for any rea {{#include ../../packages/fuels/tests/scripts.rs:submit_response_script}} ``` -## Running scripts with transaction parameters +## Running scripts with transaction policies -The method for passing transaction parameters is the same as [with contracts](./calling-contracts/tx-params.md). As a reminder, the workflow would look like this: +The method for passing transaction policies is the same as [with contracts](./calling-contracts/tx-policies.md). As a reminder, the workflow would look like this: ```rust,ignore -{{#include ../../packages/fuels/tests/scripts.rs:script_with_tx_params}} +{{#include ../../packages/fuels/tests/scripts.rs:script_with_tx_policies}} ``` ## Logs diff --git a/examples/contracts/src/lib.rs b/examples/contracts/src/lib.rs index 6735d35b89..f425990b8c 100644 --- a/examples/contracts/src/lib.rs +++ b/examples/contracts/src/lib.rs @@ -12,7 +12,7 @@ mod tests { use fuels::prelude::{FuelService, Provider}; // Run the fuel node. - let server = FuelService::start(Config::local_node()).await?; + let server = FuelService::start(Config::default()).await?; // Create a client that will talk to the node created above. let client = Provider::from(server.bound_address()).await?; @@ -35,7 +35,7 @@ mod tests { "../../packages/fuels/tests/contracts/contract_test/out/debug/contract_test.bin", LoadConfiguration::default(), )? - .deploy(&wallet, TxParameters::default()) + .deploy(&wallet, TxPolicies::default()) .await?; println!("Contract deployed @ {contract_id}"); @@ -89,7 +89,7 @@ mod tests { "../../packages/fuels/tests/contracts/contract_test/out/debug/contract_test.bin", LoadConfiguration::default(), )? - .deploy(&wallet, TxParameters::default()) + .deploy(&wallet, TxPolicies::default()) .await?; // ANCHOR: contract_call_cost_estimation @@ -103,7 +103,7 @@ mod tests { .await?; // ANCHOR_END: contract_call_cost_estimation - assert_eq!(transaction_cost.gas_used, 470); + assert_eq!(transaction_cost.gas_used, 574); Ok(()) } @@ -122,7 +122,7 @@ mod tests { "../../packages/fuels/tests/contracts/contract_test/out/debug/contract_test.bin", LoadConfiguration::default(), )? - .deploy(&wallet, TxParameters::default()) + .deploy(&wallet, TxPolicies::default()) .await?; println!("Contract deployed @ {contract_id_1}"); @@ -143,16 +143,16 @@ mod tests { .with_salt(salt); // Optional: Configure deployment parameters - let tx_parameters = TxParameters::default() + let tx_policies = TxPolicies::default() .with_gas_price(0) - .with_gas_limit(1_000_000) + .with_script_gas_limit(1_000_000) .with_maturity(0); let contract_id_2 = Contract::load_from( "../../packages/fuels/tests/contracts/contract_test/out/debug/contract_test.bin", configuration, )? - .deploy(&wallet, tx_parameters) + .deploy(&wallet, tx_policies) .await?; println!("Contract deployed @ {contract_id_2}"); @@ -222,7 +222,7 @@ mod tests { "../../packages/fuels/tests/contracts/contract_test/out/debug/contract_test.bin", LoadConfiguration::default(), )? - .deploy(&wallets[0], TxParameters::default()) + .deploy(&wallets[0], TxPolicies::default()) .await?; println!("Contract deployed @ {contract_id_1}"); @@ -231,7 +231,7 @@ mod tests { let response = contract_instance_1 .methods() .initialize_counter(42) - .tx_params(TxParameters::default().with_gas_limit(1_000_000)) + .with_tx_policies(TxPolicies::default().with_script_gas_limit(1_000_000)) .call() .await?; @@ -241,7 +241,7 @@ mod tests { "../../packages/fuels/tests/contracts/contract_test/out/debug/contract_test.bin", LoadConfiguration::default().with_salt([1; 32]), )? - .deploy(&wallets[1], TxParameters::default()) + .deploy(&wallets[1], TxPolicies::default()) .await?; println!("Contract deployed @ {contract_id_2}"); @@ -250,7 +250,7 @@ mod tests { let response = contract_instance_2 .methods() .initialize_counter(42) // Build the ABI call - .tx_params(TxParameters::default().with_gas_limit(1_000_000)) + .with_tx_policies(TxPolicies::default().with_script_gas_limit(1_000_000)) .call() .await?; @@ -273,38 +273,37 @@ mod tests { "../../packages/fuels/tests/contracts/contract_test/out/debug/contract_test.bin", LoadConfiguration::default(), )? - .deploy(&wallet, TxParameters::default()) + .deploy(&wallet, TxPolicies::default()) .await?; println!("Contract deployed @ {contract_id}"); - // ANCHOR: tx_parameters + // ANCHOR: tx_policies let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods(); - let my_tx_parameters = TxParameters::default() + let tx_policies = TxPolicies::default() .with_gas_price(1) - .with_gas_limit(1_000_000) + .with_script_gas_limit(1_000_000) .with_maturity(0); let response = contract_methods - .initialize_counter(42) // Our contract method. - .tx_params(my_tx_parameters) // Chain the tx params setting method. - .call() // Perform the contract call. - .await?; // This is an async call, `.await` for it. - // ANCHOR_END: tx_parameters + .initialize_counter(42) // Our contract method + .with_tx_policies(tx_policies) // Chain the tx policies + .call() // Perform the contract call + .await?; // This is an async call, `.await` it. + // ANCHOR_END: tx_policies - // ANCHOR: tx_parameters_default + // ANCHOR: tx_policies_default let response = contract_methods .initialize_counter(42) - .tx_params(TxParameters::default()) + .with_tx_policies(TxPolicies::default()) .call() .await?; - - // ANCHOR_END: tx_parameters_default + // ANCHOR_END: tx_policies_default // ANCHOR: call_parameters let contract_methods = MyContract::new(contract_id, wallet.clone()).methods(); - let tx_params = TxParameters::default(); + let tx_policies = TxPolicies::default(); // Forward 1_000_000 coin amount of base asset_id // this is a big number for checking that amount can be a u64 @@ -312,8 +311,8 @@ mod tests { let response = contract_methods .get_msg_amount() // Our contract method. - .tx_params(tx_params) // Chain the tx params setting method. - .call_params(call_params)? // Chain the call params setting method. + .with_tx_policies(tx_policies) // Chain the tx policies. + .call_params(call_params)? // Chain the call parameters. .call() // Perform the contract call. .await?; // ANCHOR_END: call_parameters @@ -344,7 +343,7 @@ mod tests { .bin", LoadConfiguration::default(), )? - .deploy(&wallet, TxParameters::default()) + .deploy(&wallet, TxPolicies::default()) .await?; println!("Contract deployed @ {contract_id}"); @@ -383,13 +382,13 @@ mod tests { "../../packages/fuels/tests/contracts/lib_contract/out/debug/lib_contract.bin", LoadConfiguration::default(), )? - .deploy(&wallet, TxParameters::default()) + .deploy(&wallet, TxPolicies::default()) .await? .into(); let bin_path = "../../packages/fuels/tests/contracts/lib_contract_caller/out/debug/lib_contract_caller.bin"; let caller_contract_id = Contract::load_from(bin_path, LoadConfiguration::default())? - .deploy(&wallet, TxParameters::default()) + .deploy(&wallet, TxPolicies::default()) .await?; let contract_methods = @@ -454,7 +453,7 @@ mod tests { "../../packages/fuels/tests/contracts/contract_test/out/debug/contract_test.bin", LoadConfiguration::default(), )? - .deploy(&wallet, TxParameters::default()) + .deploy(&wallet, TxPolicies::default()) .await?; let contract_methods = TestContract::new(contract_id, wallet).methods(); @@ -534,7 +533,7 @@ mod tests { "../../packages/fuels/tests/contracts/contract_test/out/debug/contract_test.bin", LoadConfiguration::default(), )? - .deploy(&wallet, TxParameters::default()) + .deploy(&wallet, TxPolicies::default()) .await?; let contract_methods = MyContract::new(contract_id, wallet.clone()).methods(); @@ -543,13 +542,13 @@ mod tests { // Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that // the contract call transaction may consume up to 1_000_000 gas, while the actual call may // only use 4300 gas - let tx_params = TxParameters::default().with_gas_limit(1_000_000); + let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000); let call_params = CallParameters::default().with_gas_forwarded(4300); let response = contract_methods .get_msg_amount() // Our contract method. - .tx_params(tx_params) // Chain the tx params setting method. - .call_params(call_params)? // Chain the call params setting method. + .with_tx_policies(tx_policies) // Chain the tx policies. + .call_params(call_params)? // Chain the call parameters. .call() // Perform the contract call. .await?; // ANCHOR_END: call_params_gas @@ -572,7 +571,7 @@ mod tests { "../../packages/fuels/tests/contracts/contract_test/out/debug/contract_test.bin", LoadConfiguration::default(), )? - .deploy(&wallet, TxParameters::default()) + .deploy(&wallet, TxPolicies::default()) .await?; // ANCHOR: multi_call_prepare @@ -628,7 +627,7 @@ mod tests { "../../packages/fuels/tests/contracts/contract_test/out/debug/contract_test.bin", LoadConfiguration::default(), )? - .deploy(&wallet, TxParameters::default()) + .deploy(&wallet, TxPolicies::default()) .await?; let contract_methods = MyContract::new(contract_id, wallet.clone()).methods(); @@ -649,7 +648,7 @@ mod tests { .await?; // ANCHOR_END: multi_call_cost_estimation - assert_eq!(transaction_cost.gas_used, 693); + assert_eq!(transaction_cost.gas_used, 797); Ok(()) } @@ -672,7 +671,7 @@ mod tests { "../../packages/fuels/tests/contracts/contract_test/out/debug/contract_test.bin", LoadConfiguration::default(), )? - .deploy(&wallet_1, TxParameters::default()) + .deploy(&wallet_1, TxPolicies::default()) .await?; // ANCHOR: connect_wallet diff --git a/examples/cookbook/src/lib.rs b/examples/cookbook/src/lib.rs index 71f84e945c..3eceacedb0 100644 --- a/examples/cookbook/src/lib.rs +++ b/examples/cookbook/src/lib.rs @@ -3,7 +3,7 @@ mod tests { use fuels::{ prelude::Result, types::{ - transaction_builders::{ScriptTransactionBuilder, TransactionBuilder}, + transaction_builders::{BuildableTransaction, ScriptTransactionBuilder}, Bits256, }, }; @@ -47,7 +47,7 @@ mod tests { "../../packages/fuels/tests/contracts/liquidity_pool/out/debug/liquidity_pool.bin", LoadConfiguration::default(), )? - .deploy(wallet, TxParameters::default()) + .deploy(wallet, TxPolicies::default()) .await?; let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods(); @@ -91,16 +91,26 @@ mod tests { #[tokio::test] async fn custom_chain() -> Result<()> { // ANCHOR: custom_chain_import - use fuels::{prelude::*, tx::ConsensusParameters}; + use fuels::{ + prelude::*, + tx::{ConsensusParameters, FeeParameters, TxParameters}, + }; // ANCHOR_END: custom_chain_import // ANCHOR: custom_chain_consensus - let consensus_parameters_config = ConsensusParameters::DEFAULT - .with_max_gas_per_tx(1000) - .with_gas_price_factor(10) + let tx_params = TxParameters::default() + .with_max_gas_per_tx(1_000) .with_max_inputs(2); + let fee_params = FeeParameters::default().with_gas_price_factor(10); + + let consensus_parameters = ConsensusParameters { + tx_params, + fee_params, + ..Default::default() + }; + let chain_config = ChainConfig { - transaction_parameters: consensus_parameters_config, + consensus_parameters, ..ChainConfig::default() }; // ANCHOR_END: custom_chain_consensus @@ -116,7 +126,7 @@ mod tests { // ANCHOR_END: custom_chain_coins // ANCHOR: custom_chain_provider - let node_config = Config::local_node(); + let node_config = Config::default(); let _provider = setup_test_provider(coins, vec![], Some(node_config), Some(chain_config)).await?; // ANCHOR_END: custom_chain_provider @@ -170,11 +180,11 @@ mod tests { let mut tb = ScriptTransactionBuilder::prepare_transfer( inputs, outputs, - TxParameters::default(), + TxPolicies::default(), network_info, ); wallet_1.sign_transaction(&mut tb); - let tx = tb.build()?; + let tx = tb.build(&provider).await?; provider.send_transaction_and_await_commit(tx).await?; @@ -198,7 +208,7 @@ mod tests { // ANCHOR: create_or_use_rocksdb let provider_config = Config { database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.spider/db"))), - ..Config::local_node() + ..Config::default() }; // ANCHOR_END: create_or_use_rocksdb diff --git a/examples/predicates/src/lib.rs b/examples/predicates/src/lib.rs index 26750e6e42..a07385389d 100644 --- a/examples/predicates/src/lib.rs +++ b/examples/predicates/src/lib.rs @@ -94,7 +94,7 @@ mod tests { predicate.address(), amount_to_predicate, asset_id, - TxParameters::default(), + TxPolicies::default(), ) .await?; @@ -108,7 +108,7 @@ mod tests { receiver.address(), amount_to_predicate, asset_id, - TxParameters::default(), + TxPolicies::default(), ) .await?; @@ -159,7 +159,7 @@ mod tests { // ANCHOR: predicate_data_lock_amount // First wallet transfers amount to predicate. first_wallet - .transfer(predicate.address(), 500, asset_id, TxParameters::default()) + .transfer(predicate.address(), 500, asset_id, TxPolicies::default()) .await?; // Check predicate balance. @@ -176,7 +176,7 @@ mod tests { second_wallet.address(), amount_to_unlock, asset_id, - TxParameters::default(), + TxPolicies::default(), ) .await?; diff --git a/examples/providers/src/lib.rs b/examples/providers/src/lib.rs index e2864ce3ce..65851a169d 100644 --- a/examples/providers/src/lib.rs +++ b/examples/providers/src/lib.rs @@ -5,6 +5,7 @@ mod tests { use fuels::prelude::Result; #[tokio::test] + #[ignore] //TODO: Enable this test once beta supports the new `fuel-core` >= `0.51.0` async fn connect_to_fuel_node() -> Result<()> { // ANCHOR: connect_to_testnet use std::str::FromStr; diff --git a/examples/wallets/src/lib.rs b/examples/wallets/src/lib.rs index 5590113a63..9c05df30a2 100644 --- a/examples/wallets/src/lib.rs +++ b/examples/wallets/src/lib.rs @@ -135,7 +135,7 @@ mod tests { // Transfer the base asset with amount 1 from wallet 1 to wallet 2 let asset_id = Default::default(); let (_tx_id, _receipts) = wallets[0] - .transfer(wallets[1].address(), 1, asset_id, TxParameters::default()) + .transfer(wallets[1].address(), 1, asset_id, TxPolicies::default()) .await?; let wallet_2_final_coins = wallets[1].get_coins(BASE_ASSET_ID).await?; @@ -178,7 +178,7 @@ mod tests { "../../packages/fuels/tests/contracts/contract_test/out/debug/contract_test.bin", LoadConfiguration::default(), )? - .deploy(&wallet, TxParameters::default()) + .deploy(&wallet, TxPolicies::default()) .await?; // ANCHOR: wallet_contract_transfer @@ -193,7 +193,7 @@ mod tests { let amount = 300; let asset_id = random_asset_id; let (_tx_id, _receipts) = wallet - .force_transfer_to_contract(&contract_id, amount, asset_id, TxParameters::default()) + .force_transfer_to_contract(&contract_id, amount, asset_id, TxPolicies::default()) .await?; // Check that the contract now has 1 coin @@ -344,13 +344,9 @@ mod tests { use fuels::prelude::*; - let config = Config { - manual_blocks_enabled: true, - ..Config::local_node() - }; let wallets = launch_custom_provider_and_get_wallets( WalletsConfig::new(Some(1), None, None), - Some(config), + None, None, ) .await?; @@ -363,7 +359,7 @@ mod tests { let base_layer_address = Bech32Address::from(base_layer_address); // Transfer an amount of 1000 to the specified base layer address let (tx_id, msg_id, _receipts) = wallet - .withdraw_to_base_layer(&base_layer_address, amount, TxParameters::default()) + .withdraw_to_base_layer(&base_layer_address, amount, TxPolicies::default()) .await?; let _block_height = wallet.try_provider()?.produce_blocks(1, None).await?; diff --git a/packages/fuels-accounts/src/account.rs b/packages/fuels-accounts/src/account.rs index 14edb72ee5..fb306308eb 100644 --- a/packages/fuels-accounts/src/account.rs +++ b/packages/fuels-accounts/src/account.rs @@ -6,7 +6,7 @@ use fuel_core_client::client::pagination::{PaginatedResult, PaginationRequest}; pub use fuel_crypto; use fuel_crypto::Signature; use fuel_tx::{Output, Receipt, TxId, TxPointer, UtxoId}; -use fuel_types::{AssetId, Bytes32, ContractId, MessageId}; +use fuel_types::{AssetId, Bytes32, ContractId, Nonce}; use fuels_core::{ constants::BASE_ASSET_ID, types::{ @@ -16,14 +16,16 @@ use fuels_core::{ errors::{Error, Result}, input::Input, message::Message, - transaction::TxParameters, - transaction_builders::{ScriptTransactionBuilder, TransactionBuilder}, + transaction::TxPolicies, + transaction_builders::{ + BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder, + }, transaction_response::TransactionResponse, }, }; use crate::{ - accounts_utils::{adjust_inputs_outputs, calculate_missing_base_amount, extract_message_id}, + accounts_utils::{adjust_inputs_outputs, calculate_missing_base_amount, extract_message_nonce}, provider::{Provider, ResourceFilter}, }; @@ -117,6 +119,27 @@ pub trait ViewOnlyAccount: std::fmt::Debug + Send + Sync + Clone { .await .map_err(Into::into) } + + /// Get some spendable resources (coins and messages) of asset `asset_id` owned by the account + /// that add up at least to amount `amount`. The returned coins (UTXOs) are actual coins that + /// can be spent. The number of UXTOs is optimized to prevent dust accumulation. + async fn get_spendable_resources( + &self, + asset_id: AssetId, + amount: u64, + ) -> Result> { + let filter = ResourceFilter { + from: self.address().clone(), + asset_id, + amount, + ..Default::default() + }; + + self.try_provider()? + .get_spendable_resources(filter) + .await + .map_err(Into::into) + } } #[cfg_attr(not(target_arch = "wasm32"), async_trait)] @@ -146,36 +169,16 @@ pub trait Account: ViewOnlyAccount { ] } - /// Get some spendable resources (coins and messages) of asset `asset_id` owned by the account - /// that add up at least to amount `amount`. The returned coins (UTXOs) are actual coins that - /// can be spent. The number of UXTOs is optimized to prevent dust accumulation. - async fn get_spendable_resources( - &self, - asset_id: AssetId, - amount: u64, - ) -> Result> { - let filter = ResourceFilter { - from: self.address().clone(), - asset_id, - amount, - ..Default::default() - }; - - self.try_provider()? - .get_spendable_resources(filter) - .await - .map_err(Into::into) - } - /// Add base asset inputs to the transaction to cover the estimated fee. /// Requires contract inputs to be at the start of the transactions inputs vec /// so that their indexes are retained - async fn adjust_for_fee( + async fn adjust_for_fee( &self, tb: &mut Tb, used_base_amount: u64, ) -> Result<()> { - let missing_base_amount = calculate_missing_base_amount(tb, used_base_amount)?; + let missing_base_amount = + calculate_missing_base_amount(tb, used_base_amount, self.try_provider()?).await?; if missing_base_amount > 0 { let new_base_inputs = self @@ -199,7 +202,7 @@ pub trait Account: ViewOnlyAccount { to: &Bech32Address, amount: u64, asset_id: AssetId, - tx_parameters: TxParameters, + tx_policies: TxPolicies, ) -> Result<(TxId, Vec)> { let provider = self.try_provider()?; let network_info = provider.network_info().await?; @@ -207,17 +210,13 @@ pub trait Account: ViewOnlyAccount { let inputs = self.get_asset_inputs_for_amount(asset_id, amount).await?; let outputs = self.get_asset_outputs_for_amount(to, asset_id, amount); - let mut tx_builder = ScriptTransactionBuilder::prepare_transfer( - inputs, - outputs, - tx_parameters, - network_info, - ); + let mut tx_builder = + ScriptTransactionBuilder::prepare_transfer(inputs, outputs, tx_policies, network_info); self.add_witnessses(&mut tx_builder); self.adjust_for_fee(&mut tx_builder, amount).await?; - let tx = tx_builder.build()?; + let tx = tx_builder.build(provider).await?; let tx_id = provider.send_transaction_and_await_commit(tx).await?; let receipts = provider @@ -242,7 +241,7 @@ pub trait Account: ViewOnlyAccount { to: &Bech32ContractId, balance: u64, asset_id: AssetId, - tx_parameters: TxParameters, + tx_policies: TxPolicies, ) -> std::result::Result<(String, Vec), Error> { let provider = self.try_provider()?; let network_info = provider.network_info().await?; @@ -272,13 +271,13 @@ pub trait Account: ViewOnlyAccount { asset_id, inputs, outputs, - tx_parameters, + tx_policies, network_info, ); self.add_witnessses(&mut tb); self.adjust_for_fee(&mut tb, balance).await?; - let tx = tb.build()?; + let tx = tb.build(provider).await?; let tx_id = provider.send_transaction_and_await_commit(tx).await?; @@ -297,8 +296,8 @@ pub trait Account: ViewOnlyAccount { &self, to: &Bech32Address, amount: u64, - tx_parameters: TxParameters, - ) -> std::result::Result<(TxId, MessageId, Vec), Error> { + tx_policies: TxPolicies, + ) -> std::result::Result<(TxId, Nonce, Vec), Error> { let provider = self.try_provider()?; let network_info = provider.network_info().await?; @@ -310,13 +309,13 @@ pub trait Account: ViewOnlyAccount { to.into(), amount, inputs, - tx_parameters, + tx_policies, network_info, ); self.add_witnessses(&mut tb); self.adjust_for_fee(&mut tb, amount).await?; - let tx = tb.build()?; + let tx = tb.build(provider).await?; let tx_id = provider.send_transaction_and_await_commit(tx).await?; let receipts = provider @@ -324,10 +323,10 @@ pub trait Account: ViewOnlyAccount { .await? .take_receipts_checked(None)?; - let message_id = extract_message_id(&receipts) + let nonce = extract_message_nonce(&receipts) .expect("MessageId could not be retrieved from tx receipts."); - Ok((tx_id, message_id, receipts)) + Ok((tx_id, nonce, receipts)) } } @@ -336,8 +335,11 @@ mod tests { use std::str::FromStr; use fuel_crypto::{Message, SecretKey}; - use fuel_tx::{Address, Output}; - use fuels_core::types::{transaction::Transaction, transaction_builders::NetworkInfo}; + use fuel_tx::{Address, Output, Transaction as FuelTransaction}; + use fuels_core::types::{ + transaction::Transaction, + transaction_builders::{DryRunner, NetworkInfo}, + }; use rand::{rngs::StdRng, RngCore, SeedableRng}; use super::*; @@ -378,6 +380,15 @@ mod tests { Ok(()) } + struct MockDryRunner; + + #[cfg_attr(not(target_arch = "wasm32"), async_trait)] + impl DryRunner for MockDryRunner { + async fn dry_run_and_get_used_gas(&self, _: FuelTransaction, _: f32) -> Result { + Ok(0) + } + } + #[tokio::test] async fn sign_tx_and_verify() -> std::result::Result<(), Box> { // ANCHOR: sign_tx @@ -388,9 +399,7 @@ mod tests { let network_info = NetworkInfo { consensus_parameters: Default::default(), - max_gas_per_tx: 0, min_gas_price: 0, - gas_costs: Default::default(), }; // Set up a transaction let mut tb = { @@ -420,7 +429,8 @@ mod tests { // Sign the transaction wallet.sign_transaction(&mut tb); // Add the private key to the transaction builder - let tx = tb.build()?; // Resolve signatures and add corresponding witness indexes + // ANCHOR_END: sign_tx + let tx = tb.build(MockDryRunner).await?; // Resolve signatures and add corresponding witness indexes // Extract the signature from the tx witnesses let bytes = <[u8; Signature::LEN]>::try_from(tx.witnesses().first().unwrap().as_ref())?; @@ -434,7 +444,7 @@ mod tests { assert_eq!(signature, tx_signature); // Check if the signature is what we expect it to be - assert_eq!(signature, Signature::from_str("51198e39c541cd3197785fd8add8cdbec3dc5aba7f8fbb23eb09455dd1003a8b78d94f247df8e1577805ea7eebd6d58336393942fd98484609e9e7d6d7a55f28")?); + assert_eq!(signature, Signature::from_str("ffd15da2db6668e95e3056c5526aaa37f582e2e5e55927879bf896e813d7f57f14398297ada9205847d16a8e3bcc299a372c9a69c4cf91c4ce2c3804dc900e96")?); // Recover the address that signed the transaction let recovered_address = signature.recover(&message)?; @@ -443,7 +453,6 @@ mod tests { // Verify signature signature.verify(&recovered_address, &message)?; - // ANCHOR_END: sign_tx Ok(()) } diff --git a/packages/fuels-accounts/src/accounts_utils.rs b/packages/fuels-accounts/src/accounts_utils.rs index eb515e19b6..5d818ece50 100644 --- a/packages/fuels-accounts/src/accounts_utils.rs +++ b/packages/fuels-accounts/src/accounts_utils.rs @@ -1,5 +1,5 @@ use fuel_tx::{Output, Receipt}; -use fuel_types::MessageId; +use fuel_types::Nonce; use fuels_core::{ constants::BASE_ASSET_ID, types::{ @@ -10,16 +10,20 @@ use fuels_core::{ }, }; -pub fn extract_message_id(receipts: &[Receipt]) -> Option { - receipts.iter().find_map(|m| m.message_id()) +use crate::provider::Provider; + +pub fn extract_message_nonce(receipts: &[Receipt]) -> Option { + receipts.iter().find_map(|m| m.nonce()).copied() } -pub fn calculate_missing_base_amount( +pub async fn calculate_missing_base_amount( tb: &impl TransactionBuilder, used_base_amount: u64, + provider: &Provider, ) -> Result { let transaction_fee = tb - .fee_checked_from_tx()? + .fee_checked_from_tx(provider) + .await? .ok_or(error!(InvalidData, "Error calculating TransactionFee"))?; let available_amount = available_base_amount(tb); diff --git a/packages/fuels-accounts/src/coin_cache.rs b/packages/fuels-accounts/src/coin_cache.rs index 921db892ab..5b2040bebb 100644 --- a/packages/fuels-accounts/src/coin_cache.rs +++ b/packages/fuels-accounts/src/coin_cache.rs @@ -1,10 +1,11 @@ -use std::collections::{HashMap, HashSet}; -use std::hash::{Hash, Hasher}; -use tokio::time::{Duration, Instant}; +use std::{ + collections::{HashMap, HashSet}, + hash::{Hash, Hasher}, +}; use fuel_types::AssetId; -use fuels_core::types::bech32::Bech32Address; -use fuels_core::types::coin_type_id::CoinTypeId; +use fuels_core::types::{bech32::Bech32Address, coin_type_id::CoinTypeId}; +use tokio::time::{Duration, Instant}; type CoinCacheKey = (Bech32Address, AssetId); diff --git a/packages/fuels-accounts/src/predicate.rs b/packages/fuels-accounts/src/predicate.rs index 1ccdea581d..cdbdb62baf 100644 --- a/packages/fuels-accounts/src/predicate.rs +++ b/packages/fuels-accounts/src/predicate.rs @@ -15,7 +15,6 @@ pub struct Predicate { address: Bech32Address, code: Vec, data: UnresolvedBytes, - chain_id: u64, #[cfg(feature = "std")] provider: Option, } @@ -33,20 +32,18 @@ impl Predicate { &self.data } - pub fn calculate_address(code: &[u8], chain_id: u64) -> Bech32Address { - fuel_tx::Input::predicate_owner(code, &chain_id.into()).into() + pub fn calculate_address(code: &[u8]) -> Bech32Address { + fuel_tx::Input::predicate_owner(code).into() } - /// Uses default `ChainId` pub fn load_from(file_path: &str) -> Result { let code = fs::read(file_path)?; - Ok(Self::from_code(code, 0)) + Ok(Self::from_code(code)) } - pub fn from_code(code: Vec, chain_id: u64) -> Self { + pub fn from_code(code: Vec) -> Self { Self { - address: Self::calculate_address(&code, chain_id), - chain_id, + address: Self::calculate_address(&code), code, data: Default::default(), #[cfg(feature = "std")] @@ -60,7 +57,7 @@ impl Predicate { } pub fn with_code(self, code: Vec) -> Self { - let address = Self::calculate_address(&code, self.chain_id); + let address = Self::calculate_address(&code); Self { code, address, @@ -71,7 +68,7 @@ impl Predicate { pub fn with_configurables(mut self, configurables: impl Into) -> Self { let configurables: Configurables = configurables.into(); configurables.update_constants_in(&mut self.code); - let address = Self::calculate_address(&self.code, self.chain_id); + let address = Self::calculate_address(&self.code); self.address = address; self } @@ -84,16 +81,11 @@ impl Predicate { } pub fn set_provider(&mut self, provider: Provider) { - self.address = Self::calculate_address(&self.code, provider.chain_id().into()); - self.chain_id = provider.chain_id().into(); self.provider = Some(provider); } pub fn with_provider(self, provider: Provider) -> Self { - let address = Self::calculate_address(&self.code, provider.chain_id().into()); Self { - address, - chain_id: provider.chain_id().into(), provider: Some(provider), ..self } diff --git a/packages/fuels-accounts/src/provider.rs b/packages/fuels-accounts/src/provider.rs index 237a20601f..0ae4f4610f 100644 --- a/packages/fuels-accounts/src/provider.rs +++ b/packages/fuels-accounts/src/provider.rs @@ -4,14 +4,22 @@ mod retry_util; mod retryable_client; mod supported_versions; +#[cfg(feature = "coin-cache")] +use std::sync::Arc; + use chrono::{DateTime, Utc}; use fuel_core_client::client::{ pagination::{PageDirection, PaginatedResult, PaginationRequest}, types::{balance::Balance, contract::ContractBalance, TransactionStatus}, }; -use fuel_tx::{AssetId, ConsensusParameters, Receipt, ScriptExecutionResult, TxId, UtxoId}; -use fuel_types::{Address, Bytes32, ChainId, MessageId, Nonce}; +use fuel_tx::{ + AssetId, ConsensusParameters, Receipt, ScriptExecutionResult, Transaction as FuelTransaction, + TxId, UtxoId, +}; +use fuel_types::{Address, Bytes32, ChainId, Nonce}; use fuel_vm::state::ProgramState; +#[cfg(feature = "coin-cache")] +use fuels_core::types::coin_type_id::CoinTypeId; use fuels_core::{ constants::{BASE_ASSET_ID, DEFAULT_GAS_ESTIMATION_TOLERANCE}, types::{ @@ -25,7 +33,7 @@ use fuels_core::{ message_proof::MessageProof, node_info::NodeInfo, transaction::Transaction, - transaction_builders::NetworkInfo, + transaction_builders::{DryRunner, NetworkInfo}, transaction_response::TransactionResponse, tx_status::TxStatus, }, @@ -34,17 +42,12 @@ pub use retry_util::{Backoff, RetryConfig}; use supported_versions::{check_fuel_core_version_compatibility, VersionCompatibility}; use tai64::Tai64; use thiserror::Error; - -use crate::provider::retryable_client::RetryableClient; +#[cfg(feature = "coin-cache")] +use tokio::sync::Mutex; #[cfg(feature = "coin-cache")] use crate::coin_cache::CoinsCache; -#[cfg(feature = "coin-cache")] -use fuels_core::types::coin_type_id::CoinTypeId; -#[cfg(feature = "coin-cache")] -use std::sync::Arc; -#[cfg(feature = "coin-cache")] -use tokio::sync::Mutex; +use crate::provider::retryable_client::RetryableClient; type ProviderResult = std::result::Result; @@ -87,7 +90,7 @@ impl ResourceQueries { Some((self.utxos.clone(), self.messages.clone())) } - pub fn spend_query(&self) -> Vec<(AssetId, u64, Option)> { + pub fn spend_query(&self) -> Vec<(AssetId, u64, Option)> { vec![(self.asset_id, self.amount, None)] } } @@ -186,7 +189,7 @@ impl Provider { /// Connects to an existing node at the given address. pub async fn connect(url: impl AsRef) -> Result { let client = RetryableClient::new(&url, Default::default())?; - let consensus_parameters = client.chain_info().await?.consensus_parameters.into(); + let consensus_parameters = client.chain_info().await?.consensus_parameters; Ok(Self { client, @@ -224,11 +227,11 @@ impl Provider { let chain_info = self.chain_info().await?; tx.check_without_signatures( chain_info.latest_block.header.height, - &self.consensus_parameters(), + self.consensus_parameters(), )?; if tx.is_using_predicates() { - tx.estimate_predicates(&self.consensus_parameters, &chain_info.gas_costs)?; + tx.estimate_predicates(&self.consensus_parameters)?; } self.validate_transaction(tx.clone()).await?; @@ -246,21 +249,7 @@ impl Provider { .estimate_transaction_cost(tx.clone(), Some(tolerance)) .await?; - if gas_used > tx.gas_limit() { - return Err(error!( - ProviderError, - "gas_limit({}) is lower than the estimated gas_used({})", - tx.gas_limit(), - gas_used - )); - } else if min_gas_price > tx.gas_price() { - return Err(error!( - ProviderError, - "gas_price({}) is lower than the required min_gas_price({})", - tx.gas_price(), - min_gas_price - )); - } + tx.validate_gas(min_gas_price, gas_used)?; Ok(()) } @@ -321,8 +310,8 @@ impl Provider { Ok(self.client.chain_info().await?.into()) } - pub fn consensus_parameters(&self) -> ConsensusParameters { - self.consensus_parameters + pub fn consensus_parameters(&self) -> &ConsensusParameters { + &self.consensus_parameters } pub async fn network_info(&self) -> ProviderResult { @@ -650,7 +639,7 @@ impl Provider { pub async fn produce_blocks( &self, - blocks_to_produce: u64, + blocks_to_produce: u32, start_time: Option>, ) -> io::Result { let start_time = start_time.map(|time| Tai64::from_unix(time.timestamp()).0); @@ -690,18 +679,12 @@ impl Provider { let gas_price = std::cmp::max(tx.gas_price(), min_gas_price); let tolerance = tolerance.unwrap_or(DEFAULT_GAS_ESTIMATION_TOLERANCE); - // Remove limits from an existing Transaction for accurate gas estimation - let dry_run_tx = self.generate_dry_run_tx(tx.clone()); let gas_used = self - .get_gas_used_with_tolerance(dry_run_tx.clone(), tolerance) + .get_gas_used_with_tolerance(tx.clone(), tolerance) .await?; - // Update the tx with estimated gas_used and correct gas price to calculate the total_fee - let dry_run_tx = dry_run_tx - .with_gas_price(gas_price) - .with_gas_limit(gas_used); - - let transaction_fee = dry_run_tx + let transaction_fee = tx + .clone() .fee_checked_from_tx(&self.consensus_parameters) .expect("Error calculating TransactionFee"); @@ -709,18 +692,11 @@ impl Provider { min_gas_price, gas_price, gas_used, - metered_bytes_size: dry_run_tx.metered_bytes_size() as u64, + metered_bytes_size: tx.metered_bytes_size() as u64, total_fee: transaction_fee.max_fee(), }) } - // Remove limits from an existing Transaction to get an accurate gas estimation - fn generate_dry_run_tx(&self, tx: T) -> T { - // Simulate the contract call with max gas to get the complete gas_used - let max_gas_per_tx = self.consensus_parameters.max_gas_per_tx; - tx.clone().with_gas_limit(max_gas_per_tx).with_gas_price(0) - } - // Increase estimated gas by the provided tolerance async fn get_gas_used_with_tolerance( &self, @@ -762,7 +738,7 @@ impl Provider { pub async fn get_message_proof( &self, tx_id: &TxId, - message_id: &MessageId, + nonce: &Nonce, commit_block_id: Option<&Bytes32>, commit_block_height: Option, ) -> ProviderResult> { @@ -770,7 +746,7 @@ impl Provider { .client .message_proof( tx_id, - message_id, + nonce, commit_block_id.map(Into::into), commit_block_height.map(Into::into), ) @@ -784,3 +760,12 @@ impl Provider { self } } + +#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] +impl DryRunner for Provider { + async fn dry_run_and_get_used_gas(&self, tx: FuelTransaction, tolerance: f32) -> Result { + let receipts = self.client.dry_run_opt(&tx, Some(false)).await?; + let gas_used = self.get_gas_used(&receipts); + Ok((gas_used as f64 * (1.0 + tolerance as f64)) as u64) + } +} diff --git a/packages/fuels-accounts/src/provider/retryable_client.rs b/packages/fuels-accounts/src/provider/retryable_client.rs index fae7610239..1ae5669f47 100644 --- a/packages/fuels-accounts/src/provider/retryable_client.rs +++ b/packages/fuels-accounts/src/provider/retryable_client.rs @@ -7,7 +7,7 @@ use fuel_core_client::client::{ FuelClient, }; use fuel_tx::{Receipt, Transaction, TxId, UtxoId}; -use fuel_types::{Address, AssetId, BlockHeight, ContractId, MessageId, Nonce}; +use fuel_types::{Address, AssetId, BlockHeight, ContractId, Nonce}; use fuels_core::{error, types::errors::Result}; use crate::provider::{retry_util, RetryConfig}; @@ -109,7 +109,7 @@ impl RetryableClient { pub async fn coins_to_spend( &self, owner: &Address, - spend_query: Vec<(AssetId, u64, Option)>, + spend_query: Vec<(AssetId, u64, Option)>, excluded_ids: Option<(Vec, Vec)>, ) -> io::Result>> { self.client @@ -168,7 +168,7 @@ impl RetryableClient { pub async fn produce_blocks( &self, - blocks_to_produce: u64, + blocks_to_produce: u32, start_timestamp: Option, ) -> io::Result { self.our_retry(|| { @@ -202,17 +202,13 @@ impl RetryableClient { pub async fn message_proof( &self, transaction_id: &TxId, - message_id: &MessageId, + nonce: &Nonce, commit_block_id: Option<&BlockId>, commit_block_height: Option, ) -> io::Result> { self.our_retry(|| { - self.client.message_proof( - transaction_id, - message_id, - commit_block_id, - commit_block_height, - ) + self.client + .message_proof(transaction_id, nonce, commit_block_id, commit_block_height) }) .await } diff --git a/packages/fuels-accounts/src/provider/supported_versions.rs b/packages/fuels-accounts/src/provider/supported_versions.rs index 5bf8e4f567..dc7e21a23d 100644 --- a/packages/fuels-accounts/src/provider/supported_versions.rs +++ b/packages/fuels-accounts/src/provider/supported_versions.rs @@ -1,7 +1,7 @@ use semver::Version; fn get_supported_fuel_core_version() -> Version { - "0.20.6".parse().unwrap() + "0.21.0-rc.1".parse().unwrap() } #[derive(Debug, PartialEq, Eq)] diff --git a/packages/fuels-core/Cargo.toml b/packages/fuels-core/Cargo.toml index 43054ea313..0bfaafa6b4 100644 --- a/packages/fuels-core/Cargo.toml +++ b/packages/fuels-core/Cargo.toml @@ -10,6 +10,7 @@ rust-version = { workspace = true } description = "Fuel Rust SDK core." [dependencies] +async-trait = { workspace = true, default-features = false } bech32 = { workspace = true } chrono = { workspace = true } fuel-abi-types = { workspace = true } diff --git a/packages/fuels-core/src/codec/logs.rs b/packages/fuels-core/src/codec/logs.rs index 02c791129a..dd3798eaf7 100644 --- a/packages/fuels-core/src/codec/logs.rs +++ b/packages/fuels-core/src/codec/logs.rs @@ -7,6 +7,7 @@ use std::{ use fuel_tx::{ContractId, Receipt}; +use super::ABIDecoder; use crate::{ codec::DecoderConfig, traits::{Parameterize, Tokenizable}, @@ -16,8 +17,6 @@ use crate::{ }, }; -use super::ABIDecoder; - #[derive(Clone)] pub struct LogFormatter { formatter: fn(DecoderConfig, &[u8]) -> Result, diff --git a/packages/fuels-core/src/types/errors.rs b/packages/fuels-core/src/types/errors.rs index 8299ef460d..afcb497c69 100644 --- a/packages/fuels-core/src/types/errors.rs +++ b/packages/fuels-core/src/types/errors.rs @@ -1,6 +1,7 @@ use std::{array::TryFromSliceError, str::Utf8Error}; -use fuel_tx::{CheckError, Receipt}; +use fuel_tx::{Receipt, ValidityError}; +use fuel_vm::checked_transaction::CheckError; use thiserror::Error; #[derive(Error, Debug)] @@ -26,7 +27,7 @@ pub enum Error { #[error("Provider error: {0}")] ProviderError(String), #[error("Validation error: {0}")] - ValidationError(#[from] CheckError), + ValidationError(String), #[error("Tried to forward assets to a contract method that is not payable.")] AssetsForwardedToNonPayableMethod, #[error("Revert transaction error: {reason},\n receipts: {receipts:?}")] @@ -64,5 +65,12 @@ macro_rules! impl_error_from { }; } +impl From for Error { + fn from(err: CheckError) -> Error { + Error::ValidationError(format!("{:?}", err)) + } +} + impl_error_from!(InvalidData, bech32::Error); impl_error_from!(InvalidData, TryFromSliceError); +impl_error_from!(ValidationError, ValidityError); diff --git a/packages/fuels-core/src/types/param_types.rs b/packages/fuels-core/src/types/param_types.rs index a09c12ada7..abbf8bb689 100644 --- a/packages/fuels-core/src/types/param_types.rs +++ b/packages/fuels-core/src/types/param_types.rs @@ -620,8 +620,7 @@ impl<'a> fmt::Debug for DebugWithDepth<'a> { #[cfg(test)] mod tests { use super::*; - use crate::constants::WORD_SIZE; - use crate::{codec::DecoderConfig, types::param_types::ParamType}; + use crate::{codec::DecoderConfig, constants::WORD_SIZE, types::param_types::ParamType}; const WIDTH_OF_B256: usize = 32; const WIDTH_OF_U32: usize = 8; diff --git a/packages/fuels-core/src/types/transaction_builders.rs b/packages/fuels-core/src/types/transaction_builders.rs index 9097161f4d..26ee5206d1 100644 --- a/packages/fuels-core/src/types/transaction_builders.rs +++ b/packages/fuels-core/src/types/transaction_builders.rs @@ -1,20 +1,25 @@ #![cfg(feature = "std")] -use std::collections::HashMap; +use std::{cmp::max, collections::HashMap, iter::repeat_with}; +use async_trait::async_trait; use fuel_asm::{op, GTFArgs, RegId}; use fuel_crypto::{Message as CryptoMessage, SecretKey, Signature}; use fuel_tx::{ - field::Witnesses, ConsensusParameters, Create, Input as FuelInput, Output, Script, StorageSlot, - Transaction as FuelTransaction, TransactionFee, TxPointer, UniqueIdentifier, Witness, + field::{Inputs, Witnesses}, + policies::{Policies, PolicyType}, + Buildable, Chargeable, ConsensusParameters, Create, Input as FuelInput, Output, Script, + StorageSlot, Transaction as FuelTransaction, TransactionFee, TxPointer, UniqueIdentifier, + Witness, }; -use fuel_types::{bytes::padded_len_usize, Bytes32, ChainId, MemLayout, Salt}; -use fuel_vm::gas::GasCosts; +use fuel_types::{bytes::padded_len_usize, canonical::Serialize, Bytes32, ChainId, Salt}; use zeroize::{Zeroize, ZeroizeOnDrop}; use super::{chain_info::ChainInfo, node_info::NodeInfo}; use crate::{ - constants::{BASE_ASSET_ID, WORD_SIZE}, + constants::{ + BASE_ASSET_ID, DEFAULT_CREATE_WITNESS_LIMIT, DEFAULT_SCRIPT_WITNESS_LIMIT, WORD_SIZE, + }, offsets, types::{ bech32::Bech32Address, @@ -23,30 +28,44 @@ use crate::{ errors::{error, Result}, input::Input, message::Message, - transaction::{CreateTransaction, ScriptTransaction, Transaction, TxParameters}, + transaction::{ + CreateTransaction, EstimablePredicates, ScriptTransaction, Transaction, TxPolicies, + }, unresolved_bytes::UnresolvedBytes, Address, AssetId, ContractId, }, }; +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait DryRunner: Send + Sync { + async fn dry_run_and_get_used_gas(&self, tx: FuelTransaction, tolerance: f32) -> Result; +} + +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl DryRunner for &T { + async fn dry_run_and_get_used_gas(&self, tx: FuelTransaction, tolerance: f32) -> Result { + (*self).dry_run_and_get_used_gas(tx, tolerance).await + } +} + #[derive(Debug, Clone)] pub struct NetworkInfo { pub consensus_parameters: ConsensusParameters, - pub max_gas_per_tx: u64, pub min_gas_price: u64, - pub gas_costs: GasCosts, } impl NetworkInfo { pub fn new(node_info: NodeInfo, chain_info: ChainInfo) -> Self { Self { - max_gas_per_tx: chain_info.consensus_parameters.max_gas_per_tx, - consensus_parameters: chain_info.consensus_parameters.into(), + consensus_parameters: chain_info.consensus_parameters, min_gas_price: node_info.min_gas_price, - gas_costs: chain_info.gas_costs, } } + pub fn max_gas_per_tx(&self) -> u64 { + self.consensus_parameters.tx_params().max_gas_per_tx + } + pub fn chain_id(&self) -> ChainId { self.consensus_parameters.chain_id } @@ -55,20 +74,49 @@ impl NetworkInfo { #[derive(Debug, Clone, Default, Zeroize, ZeroizeOnDrop)] struct UnresolvedSignatures { #[zeroize(skip)] - addr_idx_offset_map: HashMap, + addr_idx_offset_map: HashMap, secret_keys: Vec, } -pub trait TransactionBuilder: Send { +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait BuildableTransaction { + type TxType: Transaction; + + /// Build a `Transaction` from the `TransactionBuilder`. `DryRunner` is + /// used to return the actual `gas_used` which is set as the `script_gas_limit`. + async fn build(self, provider: impl DryRunner) -> Result; +} + +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl BuildableTransaction for ScriptTransactionBuilder { + type TxType = ScriptTransaction; + + async fn build(self, provider: impl DryRunner) -> Result { + self.build(provider).await + } +} + +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl BuildableTransaction for CreateTransactionBuilder { + type TxType = CreateTransaction; + + /// `CreateTransaction`s do not have `gas_limit` so the `DryRunner` + /// is not used in this case. + async fn build(self, _: impl DryRunner) -> Result { + self.build() + } +} + +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait TransactionBuilder: BuildableTransaction + Send + Clone { type TxType: Transaction; - fn build(self) -> Result; fn add_unresolved_signature(&mut self, owner: Bech32Address, secret_key: SecretKey); - fn fee_checked_from_tx(&self) -> Result>; + async fn fee_checked_from_tx(&self, provider: impl DryRunner) + -> Result>; fn with_maturity(self, maturity: u32) -> Self; fn with_gas_price(self, gas_price: u64) -> Self; - fn with_gas_limit(self, gas_limit: u64) -> Self; - fn with_tx_params(self, tx_params: TxParameters) -> Self; + fn with_tx_policies(self, tx_policies: TxPolicies) -> Self; fn with_inputs(self, inputs: Vec) -> Self; fn with_outputs(self, outputs: Vec) -> Self; fn with_witnesses(self, witnesses: Vec) -> Self; @@ -78,49 +126,36 @@ pub trait TransactionBuilder: Send { fn outputs_mut(&mut self) -> &mut Vec; fn witnesses(&self) -> &Vec; fn witnesses_mut(&mut self) -> &mut Vec; - fn consensus_parameters(&self) -> ConsensusParameters; + fn consensus_parameters(&self) -> &ConsensusParameters; } macro_rules! impl_tx_trait { ($ty: ty, $tx_ty: ident) => { + #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl TransactionBuilder for $ty { type TxType = $tx_ty; - fn build(self) -> Result<$tx_ty> { - let is_using_predicates = self.is_using_predicates(); - let base_offset = if is_using_predicates { - self.base_offset() - } else { - 0 - }; - - let num_witnesses = self.num_witnesses()?; - let tx = self.resolve_fuel_tx(base_offset, num_witnesses)?; - - Ok($tx_ty { - tx, - is_using_predicates, - }) - } fn add_unresolved_signature(&mut self, owner: Bech32Address, secret_key: SecretKey) { - let index_offset = self.unresolved_signatures.secret_keys.len() as u8; + let index_offset = self.unresolved_signatures.secret_keys.len() as u64; self.unresolved_signatures.secret_keys.push(secret_key); self.unresolved_signatures .addr_idx_offset_map .insert(owner, index_offset); } - fn fee_checked_from_tx(&self) -> Result> { - let mut tx = self.clone().build()?; + async fn fee_checked_from_tx( + &self, + provider: impl DryRunner, + ) -> Result> { + let mut tx = BuildableTransaction::build(self.clone(), provider).await?; + if tx.is_using_predicates() { - tx.estimate_predicates( - &self.consensus_parameters(), - &self.network_info.gas_costs, - )?; + tx.estimate_predicates(self.consensus_parameters())?; } Ok(TransactionFee::checked_from_tx( - &self.consensus_parameters(), + &self.consensus_parameters().gas_costs, + &self.consensus_parameters().fee_params, &tx.tx, )) } @@ -135,16 +170,8 @@ macro_rules! impl_tx_trait { self } - fn with_gas_limit(mut self, gas_limit: u64) -> Self { - self.gas_limit = Some(gas_limit); - self - } - - fn with_tx_params(mut self, tx_params: TxParameters) -> Self { - self.gas_limit = tx_params.gas_limit(); - self.gas_price = tx_params.gas_price(); - - self.with_maturity(tx_params.maturity().into()) + fn with_tx_policies(self, tx_policies: TxPolicies) -> Self { + self.with_tx_policies(tx_policies) } fn with_inputs(mut self, inputs: Vec) -> Self { @@ -186,12 +213,26 @@ macro_rules! impl_tx_trait { &mut self.witnesses } - fn consensus_parameters(&self) -> ConsensusParameters { - self.network_info.consensus_parameters + fn consensus_parameters(&self) -> &ConsensusParameters { + &self.network_info.consensus_parameters } } impl $ty { + fn generate_shared_fuel_policies(&self) -> Policies { + let mut policies = Policies::default(); + + policies.set(PolicyType::MaxFee, self.max_fee); + policies.set(PolicyType::Maturity, Some(self.maturity as u64)); + + policies.set( + PolicyType::GasPrice, + self.gas_price.or(Some(self.network_info.min_gas_price)), + ); + + policies + } + fn is_using_predicates(&self) -> bool { self.inputs() .iter() @@ -218,12 +259,15 @@ macro_rules! impl_tx_trait { pub struct ScriptTransactionBuilder { pub gas_price: Option, pub gas_limit: Option, + pub witness_limit: Option, + pub max_fee: Option, pub maturity: u32, pub script: Vec, pub script_data: Vec, pub inputs: Vec, pub outputs: Vec, pub witnesses: Vec, + pub gas_estimation_tolerance: f32, pub(crate) network_info: NetworkInfo, unresolved_signatures: UnresolvedSignatures, } @@ -231,8 +275,9 @@ pub struct ScriptTransactionBuilder { #[derive(Debug, Clone)] pub struct CreateTransactionBuilder { pub gas_price: Option, - pub gas_limit: Option, pub maturity: u32, + pub witness_limit: Option, + pub max_fee: Option, pub bytecode_length: u64, pub bytecode_witness_index: u8, pub storage_slots: Vec, @@ -252,6 +297,8 @@ impl ScriptTransactionBuilder { ScriptTransactionBuilder { gas_price: None, gas_limit: None, + witness_limit: None, + max_fee: None, maturity: 0, script: vec![], script_data: vec![], @@ -259,41 +306,135 @@ impl ScriptTransactionBuilder { outputs: vec![], witnesses: vec![], network_info, + gas_estimation_tolerance: 0.05, unresolved_signatures: Default::default(), } } - fn resolve_fuel_tx(self, base_offset: usize, num_witnesses: u8) -> Result