diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 1d9c458c50..03fc6e9be9 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -49,6 +49,7 @@ - [Pre-uploading code](./preuploading-code.md) - [Custom transactions](./custom-transactions/index.md) - [Transaction builders](./custom-transactions/transaction-builders.md) + - [Assemble transactions](./custom-transactions/assemble_tx.md) - [Custom contract and script calls](./custom-transactions/custom-calls.md) - [Types](./types/index.md) - [`Bytes32`](./types/bytes32.md) diff --git a/docs/src/calling-contracts/other-contracts.md b/docs/src/calling-contracts/other-contracts.md index 28cfb902db..f6848648a2 100644 --- a/docs/src/calling-contracts/other-contracts.md +++ b/docs/src/calling-contracts/other-contracts.md @@ -1,15 +1,13 @@ # Calling other contracts -If your contract method is calling other contracts you will have to add the appropriate `Inputs` and `Outputs` to your transaction. For your convenience, the `CallHandler` provides methods that prepare those inputs and outputs for you. You have two methods that you can use: `with_contracts(&[&contract_instance, ...])` and `with_contract_ids(&[&contract_id, ...])`. - -`with_contracts(&[&contract_instance, ...])` requires contract instances that were created using the `abigen` macro. When setting the external contracts with this method, logs and require revert errors originating from the external contract can be propagated and decoded by the calling contract. +If your contract method is calling other contracts you will have to add the appropriate `Inputs` and `Outputs` to your transaction. For your convenience, the `CallHandler` will fill in all missing `Inputs`/`Outputs` before sending the transaction. ```rust,ignore {{#include ../../../e2e/tests/contracts.rs:external_contract}} ``` - If however, you do not need to decode logs or you do not have a contract instance that was generated using the `abigen` macro you can use `with_contract_ids(&[&contract_id, ...])` and provide the required contract ids. +If you need to decode logs and require revert errors originating from the external contract you will need to pass the `LogDecoder` from the external contract to the contract instance making the call. ```rust,ignore -{{#include ../../../e2e/tests/contracts.rs:external_contract_ids}} +{{#include ../../../e2e/tests/contracts.rs:external_contract_logs}} ``` diff --git a/docs/src/calling-contracts/tx-dependency-estimation.md b/docs/src/calling-contracts/tx-dependency-estimation.md index b583559dac..60ce5358fd 100644 --- a/docs/src/calling-contracts/tx-dependency-estimation.md +++ b/docs/src/calling-contracts/tx-dependency-estimation.md @@ -1,28 +1,9 @@ # Transaction dependency estimation -Previously, we mentioned that a contract call might require you to manually specify external contracts, variable outputs, or output messages. The SDK can also attempt to estimate and set these dependencies for you at the cost of running multiple simulated calls in the background. +Previously, we mentioned that a contract call might require you to manually specify external contracts, variable outputs, or output messages. The SDK will estimate and set these dependencies for you. -The following example uses a contract call that calls an external contract and later mints assets to a specified address. Calling it without including the dependencies will result in a revert: - -```rust,ignore -{{#include ../../../examples/contracts/src/lib.rs:dependency_estimation_fail}} -``` - -As mentioned in previous chapters, you can specify the external contract and add an output variable to resolve this: - -```rust,ignore -{{#include ../../../examples/contracts/src/lib.rs:dependency_estimation_manual}} -``` - -But this requires you to know the contract ID of the external contract and the needed number of output variables. Alternatively, by chaining - -- `.with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)` and -- `.determine_missing_contracts()` - -the dependencies will be estimated by the SDK and set automatically. +The following example uses a contract call that calls an external contract and later mints assets to a specified address. ```rust,ignore {{#include ../../../examples/contracts/src/lib.rs:dependency_estimation}} ``` - -> **Note:** Both `with_variable_output_policy` and `determine_missing_contracts` can also be used when working with script calls or multi calls. `determine_missing_contracts()` will not enable logging from an external contract. For more information, see [here](./other-contracts.md). diff --git a/docs/src/calling-contracts/tx-policies.md b/docs/src/calling-contracts/tx-policies.md index 12eadfd2db..5c0b370b8f 100644 --- a/docs/src/calling-contracts/tx-policies.md +++ b/docs/src/calling-contracts/tx-policies.md @@ -15,9 +15,6 @@ Where: 3. **Maturity** - Block until which the transaction cannot be included. 4. **Expiration** - Block after which the transaction cannot be included. 5. **Max Fee** - The maximum fee payable by this transaction. -6. **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. If the **Witness Limit** is not set, the SDK will set it to the size of all witnesses and signatures defined in the transaction builder. @@ -40,3 +37,8 @@ This way: ``` As you might have noticed, `TxPolicies` can also be specified when deploying contracts or transferring assets by passing it to the respective methods. + +> **Note:** The `Script Gas Limit` is set directly on the call handler: + +```rust,ignore +{{#include ../../../examples/contracts/src/lib.rs:tx_script_gas_limit}} diff --git a/docs/src/calling-contracts/variable-outputs.md b/docs/src/calling-contracts/variable-outputs.md index 7dc42cdcb9..8b6e9bc1b9 100644 --- a/docs/src/calling-contracts/variable-outputs.md +++ b/docs/src/calling-contracts/variable-outputs.md @@ -11,15 +11,14 @@ Let's say you deployed a contract with the following method: {{#include ../../../e2e/sway/contracts/token_ops/src/main.sw:variable_outputs}} ``` -When calling `transfer_coins_to_output` with the SDK, you can specify the number of variable outputs: +When calling `transfer_coins_to_output` with the SDK, the correct number of variable outputs will be estimated and you will not have to change your code. ```rust,ignore {{#include ../../../examples/contracts/src/lib.rs:variable_outputs}} ``` - -`with_variable_output_policy` sets the policy regarding variable outputs. You can either set the number of variable outputs yourself by providing `VariableOutputPolicy::Exactly(n)` or let the SDK estimate it for you with `VariableOutputPolicy::EstimateMinimum`. A variable output indicates that the amount and the owner may vary based on transaction execution. +However, if you convert the contract call into a `TransactionBuilder` you have to make sure that you have the appropriate number of variable outputs present. You can use the `with_variable_output_policy` method to either set the number of variable outputs yourself by providing `VariableOutputPolicy::Exactly(n)` or let the SDK estimate it for you with `VariableOutputPolicy::EstimateMinimum`. A variable output indicates that the amount and the owner may vary based on transaction execution. > **Note:** that the Sway `lib-std` function `mint_to_address` calls `transfer_to_address` under the hood, so you need to call `with_variable_output_policy` in the Rust SDK tests like you would for `transfer_to_address`. diff --git a/docs/src/custom-transactions/assemble_tx.md b/docs/src/custom-transactions/assemble_tx.md new file mode 100644 index 0000000000..bf27675642 --- /dev/null +++ b/docs/src/custom-transactions/assemble_tx.md @@ -0,0 +1,35 @@ +# Assemble Transactions + +Assemble transactions makes it possible to create a minimal `TransactionBuilder` and let the fuel node fill in the missing details. The node will add missing inputs, outputs, set transactions limits etc. Below is an example how the assemble strategy can be used to create a transfer. + +Let's first launch a local node with a funded wallet and create a random wallet that will receive some base asset. + +```rust,ignore +{{#include ../../../e2e/tests/providers.rs:assemble_wallets}} +``` + +Next, we create an base asset output to the receiver wallet. + +```rust,ignore +{{#include ../../../e2e/tests/providers.rs:assemble_output}} +``` + +Now we tell the node what kind of inputs do we require. Note that we do not specify any inputs just the amount, asset id and which require balance should be used to pay for the fees. + +```rust,ignore +{{#include ../../../e2e/tests/providers.rs:assemble_req_balance}} +``` + +We can now build the transaction using the assemble strategy. + +```rust,ignore +{{#include ../../../e2e/tests/providers.rs:assemble_tb}} +``` + +> **Note** The assemble strategy will make sure that we have enough base asset coins in the inputs to cover the transfer and the fee. Also a change output is added to the transaction. + +At the end, we send the transaction and make sure that the receiver balance matches the sent amount. + +```rust,ignore +{{#include ../../../e2e/tests/providers.rs:assemble_response}} +``` diff --git a/docs/src/predicates/index.md b/docs/src/predicates/index.md index bbe033fe52..f139dba934 100644 --- a/docs/src/predicates/index.md +++ b/docs/src/predicates/index.md @@ -54,3 +54,5 @@ Each configurable constant will get a dedicated `with` method in the SDK. For ex ```rust,ignore {{#include ../../../e2e/tests/predicates.rs:predicate_configurables}} ``` + +> **Note:** if a custom transaction is using predicates where the execution is dependant on some malleable fields and the fields are changed, then you will have to re-estimate the predicates to set the right gas limit. diff --git a/docs/src/running-scripts.md b/docs/src/running-scripts.md index e94449541d..a983b654bd 100644 --- a/docs/src/running-scripts.md +++ b/docs/src/running-scripts.md @@ -40,20 +40,12 @@ Script calls provide the same logging functions, `decode_logs()` and `decode_log ## Calling contracts from scripts -Scripts use the same interfaces for setting external contracts as [contract methods](./calling-contracts/other-contracts.md). - -Below is an example that uses `with_contracts(&[&contract_instance, ...])`. +Similarly to [contract methods](./calling-contracts/other-contracts.md), script calls will automatically estimate all missing contracts inputs. If you need to decode logs or revert errors from the external contract you should add the `LogDecoder` with `add_log_decoder`. ```rust,ignore {{#include ../../e2e/tests/logs.rs:external_contract}} ``` -And this is an example that uses `with_contract_ids(&[&contract_id, ...])`. - -```rust,ignore -{{#include ../../e2e/tests/logs.rs:external_contract_ids}} -``` - ## Configurable constants Same as contracts, you can define `configurable` constants in `scripts` which can be changed during the script execution. Here is an example how the constants are defined. diff --git a/e2e/tests/contracts.rs b/e2e/tests/contracts.rs index 0a38d02516..cc90206a68 100644 --- a/e2e/tests/contracts.rs +++ b/e2e/tests/contracts.rs @@ -91,8 +91,6 @@ async fn test_contract_calling_contract() -> Result<()> { let response = contract_caller_instance .methods() .increment_from_contracts(lib_contract_id, lib_contract_id2, 42) - // Note that the two lib_contract_instances have different types - .with_contracts(&[&lib_contract_instance, &lib_contract_instance2]) .call() .await?; @@ -102,21 +100,20 @@ async fn test_contract_calling_contract() -> Result<()> { let response = contract_caller_instance .methods() .increment_from_contract(lib_contract_id, 42) - .with_contracts(&[&lib_contract_instance]) .call() .await?; // ANCHOR_END: external_contract assert_eq!(43, response.value); - // ANCHOR: external_contract_ids + // ANCHOR: external_contract_logs let response = contract_caller_instance .methods() - .increment_from_contract(lib_contract_id, 42) - .with_contract_ids(&[lib_contract_id.clone()]) + .increment_from_contract(lib_contract_instance.contract_id(), 42) + .add_log_decoder(lib_contract_instance.log_decoder()) .call() .await?; - // ANCHOR_END: external_contract_ids + // ANCHOR_END: external_contract_logs assert_eq!(43, response.value); Ok(()) @@ -276,7 +273,7 @@ async fn test_multi_call_pro() -> Result<()> { } #[tokio::test] -async fn test_contract_call_fee_estimation() -> Result<()> { +async fn contract_call_fee_estimation() -> Result<()> { setup_program_test!( Wallets("wallet"), Abigen(Contract( @@ -291,21 +288,20 @@ async fn test_contract_call_fee_estimation() -> Result<()> { ), ); - let gas_limit = 800; + let gas_limit = 3800; let tolerance = Some(0.2); let block_horizon = Some(1); - let expected_script_gas = 800; - let expected_total_gas = 8463; + let expected_total_gas = 10500; let expected_metered_bytes_size = 824; let estimated_transaction_cost = contract_instance .methods() .initialize_counter(42) - .with_tx_policies(TxPolicies::default().with_script_gas_limit(gas_limit)) + .with_script_gas_limit(gas_limit) .estimate_transaction_cost(tolerance, block_horizon) .await?; - assert_eq!(estimated_transaction_cost.script_gas, expected_script_gas); + assert!(estimated_transaction_cost.script_gas < gas_limit); assert_eq!(estimated_transaction_cost.total_gas, expected_total_gas); assert_eq!( estimated_transaction_cost.metered_bytes_size, @@ -601,7 +597,6 @@ async fn test_contract_setup_macro_deploy_with_salt() -> Result<()> { let response = contract_caller_instance .methods() .increment_from_contract(lib_contract_id, 42) - .with_contracts(&[&lib_contract_instance]) .call() .await?; @@ -610,7 +605,6 @@ async fn test_contract_setup_macro_deploy_with_salt() -> Result<()> { let response = contract_caller_instance2 .methods() .increment_from_contract(lib_contract_id, 42) - .with_contracts(&[&lib_contract_instance]) .call() .await?; @@ -666,9 +660,7 @@ async fn test_connect_wallet() -> Result<()> { // ANCHOR_END: contract_setup_macro_manual_wallet // pay for call with wallet - let tx_policies = TxPolicies::default() - .with_tip(100) - .with_script_gas_limit(1_000_000); + let tx_policies = TxPolicies::default().with_tip(100); contract_instance .methods() @@ -724,7 +716,7 @@ async fn setup_output_variable_estimation_test() } #[tokio::test] -async fn test_output_variable_estimation() -> Result<()> { +async fn output_variable_estimation() -> Result<()> { abigen!(Contract( name = "MyContract", abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json" @@ -737,38 +729,22 @@ async fn test_output_variable_estimation() -> Result<()> { let contract_methods = contract_instance.methods(); let amount = 1000; - { - // Should fail due to lack of output variables - let response = contract_methods - .mint_to_addresses(amount, addresses) - .call() - .await; - - assert!(matches!( - response, - Err(Error::Transaction(Reason::Failure { .. })) - )); - } - - { - // Should add 3 output variables automatically - let _ = contract_methods - .mint_to_addresses(amount, addresses) - .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum) - .call() - .await?; + // Should add 3 output variables automatically + let _ = contract_methods + .mint_to_addresses(amount, addresses) + .call() + .await?; - for wallet in wallets.iter() { - let balance = wallet.get_asset_balance(&mint_asset_id).await?; - assert_eq!(balance, amount); - } + for wallet in wallets.iter() { + let balance = wallet.get_asset_balance(&mint_asset_id).await?; + assert_eq!(balance, amount); } Ok(()) } #[tokio::test] -async fn test_output_variable_estimation_multicall() -> Result<()> { +async fn output_variable_estimation_multicall() -> Result<()> { abigen!(Contract( name = "MyContract", abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json" @@ -803,10 +779,7 @@ async fn test_output_variable_estimation_multicall() -> Result<()> { let call_handler = contract_methods.send_message(base_layer_address, amount); multi_call_handler = multi_call_handler.add_call(call_handler); - let _ = multi_call_handler - .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum) - .call::<((), (), ())>() - .await?; + let _ = multi_call_handler.call::<((), (), ())>().await?; for wallet in wallets.iter() { let balance = wallet.get_asset_balance(&mint_asset_id).await?; @@ -896,7 +869,7 @@ async fn contract_call_futures_implement_send() -> Result<()> { } #[tokio::test] -async fn test_contract_set_estimation() -> Result<()> { +async fn external_contract_estimation() -> Result<()> { setup_program_test!( Wallets("wallet"), Abigen( @@ -927,25 +900,9 @@ async fn test_contract_set_estimation() -> Result<()> { let res = lib_contract_instance.methods().increment(42).call().await?; assert_eq!(43, res.value); - { - // Should fail due to missing external contracts - let res = contract_caller_instance - .methods() - .increment_from_contract(lib_contract_id, 42) - .call() - .await; - - assert!(matches!( - res, - Err(Error::Transaction(Reason::Failure { .. })) - )); - } - let res = contract_caller_instance .methods() .increment_from_contract(lib_contract_id, 42) - .determine_missing_contracts() - .await? .call() .await?; @@ -954,7 +911,7 @@ async fn test_contract_set_estimation() -> Result<()> { } #[tokio::test] -async fn test_output_variable_contract_id_estimation_multicall() -> Result<()> { +async fn output_variable_contract_id_estimation_multicall() -> Result<()> { setup_program_test!( Wallets("wallet"), Abigen( @@ -1009,11 +966,7 @@ async fn test_output_variable_contract_id_estimation_multicall() -> Result<()> { multi_call_handler = multi_call_handler.add_call(call_handler); - let call_response = multi_call_handler - .determine_missing_contracts() - .await? - .call::<(u64, u64, u64, u64)>() - .await?; + let call_response = multi_call_handler.call::<(u64, u64, u64, u64)>().await?; assert_eq!(call_response.value, (43, 43, 43, 11)); @@ -1274,12 +1227,10 @@ async fn low_level_call() -> Result<()> { caller_contract_instance .methods() .call_low_level_call( - target_contract_instance.id(), + target_contract_instance.contract_id(), Bytes(function_selector), Bytes(call_data), ) - .determine_missing_contracts() - .await? .call() .await?; @@ -1303,12 +1254,10 @@ async fn low_level_call() -> Result<()> { caller_contract_instance .methods() .call_low_level_call( - target_contract_instance.id(), + target_contract_instance.contract_id(), Bytes(function_selector), Bytes(call_data), ) - .determine_missing_contracts() - .await? .call() .await?; @@ -1743,11 +1692,7 @@ async fn contract_custom_call_no_signatures_strategy() -> Result<()> { // ANCHOR_END: tx_sign_with // ANCHOR_END: tb_no_signatures_strategy - let tx_id = provider.send_transaction(tx).await?; - tokio::time::sleep(Duration::from_millis(500)).await; - - let tx_status = provider.tx_status(&tx_id).await?; - + let tx_status = provider.send_transaction_and_await_commit(tx).await?; let response = call_handler.get_response(tx_status)?; assert_eq!(counter, response.value); @@ -1919,11 +1864,7 @@ async fn variable_output_estimation_is_optimized() -> Result<()> { let coins = 252; let recipient = Identity::Address(wallet.address().into()); let start = Instant::now(); - let _ = contract_methods - .mint(coins, recipient) - .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum) - .call() - .await?; + let _ = contract_methods.mint(coins, recipient).call().await?; // debug builds are slower (20x for `fuel-core-lib`, 4x for a release-fuel-core-binary) // we won't validate in that case so we don't have to maintain two expectations @@ -2516,13 +2457,7 @@ async fn loader_works_via_proxy() -> Result<()> { .call() .await?; - let response = proxy - .methods() - .something() - .with_contract_ids(&[contract_id]) - .call() - .await? - .value; + let response = proxy.methods().something().call().await?.value; assert_eq!(response, 1001); @@ -2573,62 +2508,19 @@ async fn loader_storage_works_via_proxy() -> Result<()> { .call() .await?; - let response = proxy - .methods() - .read_some_u64() - .with_contract_ids(&[contract_id.clone()]) - .call() - .await? - .value; + let response = proxy.methods().read_some_u64().call().await?.value; assert_eq!(response, 42); - let _res = proxy - .methods() - .write_some_u64(36) - .with_contract_ids(&[contract_id.clone()]) - .call() - .await?; + let _res = proxy.methods().write_some_u64(36).call().await?; - let response = proxy - .methods() - .read_some_u64() - .with_contract_ids(&[contract_id]) - .call() - .await? - .value; + let response = proxy.methods().read_some_u64().call().await?.value; assert_eq!(response, 36); Ok(()) } -#[tokio::test] -async fn adjust_for_fee_errors() -> Result<()> { - setup_program_test!( - Wallets("wallet"), - Abigen(Contract( - name = "MyContract", - project = "e2e/sway/contracts/contract_test" - )), - ); - - let contract_binary = "sway/contracts/contract_test/out/release/contract_test.bin"; - - let err = Contract::load_from(contract_binary, LoadConfiguration::default())? - .deploy(&wallet, TxPolicies::default().with_tip(10_000_000_000_000)) - .await - .expect_err("should return error"); - - assert!( - matches!(err, Error::Provider(s) if s.contains("failed to adjust inputs to cover for missing \ - base asset: failed to get base asset \ - (0000000000000000000000000000000000000000000000000000000000000000) inputs with amount:")) - ); - - Ok(()) -} - #[tokio::test] async fn tx_input_output() -> Result<()> { let [wallet_1, wallet_2] = launch_custom_provider_and_get_wallets( diff --git a/e2e/tests/logs.rs b/e2e/tests/logs.rs index aceba07ed9..ab1578ad6b 100644 --- a/e2e/tests/logs.rs +++ b/e2e/tests/logs.rs @@ -390,13 +390,13 @@ async fn multi_call_contract_with_contract_logs() -> Result<()> { let call_handler_1 = contract_caller_instance .methods() - .logs_from_external_contract(contract_instance.id()) - .with_contracts(&[&contract_instance]); + .logs_from_external_contract(contract_instance.contract_id()) + .add_log_decoder(contract_instance.log_decoder()); let call_handler_2 = contract_caller_instance2 .methods() - .logs_from_external_contract(contract_instance.id()) - .with_contracts(&[&contract_instance]); + .logs_from_external_contract(contract_instance.contract_id()) + .add_log_decoder(contract_instance.log_decoder()); let multi_call_handler = CallHandler::new_multi_call(wallet.clone()) .add_call(call_handler_1) @@ -750,8 +750,8 @@ async fn contract_with_contract_logs() -> Result<()> { let logs = contract_caller_instance .methods() - .logs_from_external_contract(contract_instance.id()) - .with_contracts(&[&contract_instance]) + .logs_from_external_contract(contract_instance.contract_id()) + .add_log_decoder(contract_instance.log_decoder()) .call() .await? .decode_logs(); @@ -802,21 +802,13 @@ async fn script_logs_with_contract_logs() -> Result<()> { ]; // ANCHOR: instance_to_contract_id - let contract_id: ContractId = contract_instance.id().into(); + let contract_id: ContractId = contract_instance.contract_id().into(); // ANCHOR_END: instance_to_contract_id - // ANCHOR: external_contract_ids - let response = script_instance - .main(contract_id, MatchEnum::Logs) - .with_contract_ids(&[contract_id.into()]) - .call() - .await?; - // ANCHOR_END: external_contract_ids - // ANCHOR: external_contract let response = script_instance .main(contract_id, MatchEnum::Logs) - .with_contracts(&[&contract_instance]) + .add_log_decoder(contract_instance.log_decoder()) .call() .await?; // ANCHOR_END: external_contract @@ -1024,8 +1016,8 @@ async fn contract_require_from_contract() -> Result<()> { let error = contract_caller_instance .methods() - .require_from_contract(contract_instance.id()) - .with_contracts(&[&contract_instance]) + .require_from_contract(contract_instance.contract_id()) + .add_log_decoder(contract_instance.log_decoder()) .call() .await .expect_err("should return a revert error"); @@ -1077,8 +1069,8 @@ async fn multi_call_contract_require_from_contract() -> Result<()> { let call_handler_2 = contract_caller_instance .methods() - .require_from_contract(lib_contract_instance.id()) - .with_contracts(&[&lib_contract_instance]); + .require_from_contract(lib_contract_instance.contract_id()) + .add_log_decoder(lib_contract_instance.log_decoder()); let multi_call_handler = CallHandler::new_multi_call(wallet.clone()) .add_call(call_handler_1) @@ -1122,8 +1114,8 @@ async fn script_require_from_contract() -> Result<()> { ); let error = script_instance - .main(contract_instance.id()) - .with_contracts(&[&contract_instance]) + .main(contract_instance.contract_id()) + .add_log_decoder(contract_instance.log_decoder()) .call() .await .expect_err("should return a revert error"); @@ -1167,8 +1159,8 @@ async fn loader_script_require_from_loader_contract() -> Result<()> { script_instance.convert_into_loader().await?; let error = script_instance - .main(contract_instance.id()) - .with_contracts(&[&contract_instance]) + .main(contract_instance.contract_id()) + .add_log_decoder(contract_instance.log_decoder()) .call() .await .expect_err("should return a revert error"); @@ -1573,8 +1565,8 @@ async fn log_results() -> Result<()> { let expected_err = format!( "codec: missing log formatter for log_id: `LogId({:?}, \"128\")`, data: `{:?}`. \ - Consider adding external contracts using `with_contracts()`", - contract_instance.id().hash, + Consider adding external log decoder using `add_log_decoder()`", + contract_instance.contract_id().hash, [0u8; 8] ); @@ -1933,8 +1925,8 @@ async fn contract_with_contract_panic() -> Result<()> { ($method:ident, call, $msg:expr) => { let error = contract_caller_instance .methods() - .$method(contract_instance.id()) - .with_contracts(&[&contract_instance]) + .$method(contract_instance.contract_id()) + .add_log_decoder(contract_instance.log_decoder()) .call() .await .expect_err("should return a revert error"); @@ -1944,8 +1936,8 @@ async fn contract_with_contract_panic() -> Result<()> { ($method:ident, simulate, $msg:expr) => { let error = contract_caller_instance .methods() - .$method(contract_instance.id()) - .with_contracts(&[&contract_instance]) + .$method(contract_instance.contract_id()) + .add_log_decoder(contract_instance.log_decoder()) .simulate(Execution::realistic()) .await .expect_err("should return a revert error"); @@ -2057,7 +2049,7 @@ async fn script_with_contract_panic() -> Result<()> { ($arg1:expr, $arg2:expr, call, $msg:expr) => { let error = script_instance .main($arg1, $arg2) - .with_contracts(&[&contract_instance]) + .add_log_decoder(contract_instance.log_decoder()) .call() .await .expect_err("should return a revert error"); @@ -2066,19 +2058,19 @@ async fn script_with_contract_panic() -> Result<()> { ($arg1:expr, $arg2:expr, simulate, $msg:expr) => { let error = script_instance .main($arg1, $arg2) - .with_contracts(&[&contract_instance]) + .add_log_decoder(contract_instance.log_decoder()) .simulate(Execution::realistic()) .await .expect_err("should return a revert error"); assert_revert_containing_msg($msg, error); }; } - let contract_id = contract_instance.id(); + let contract_id = contract_instance.contract_id(); { - reverts_with_msg!(&contract_id, MatchEnum::Panic, call, "some panic message"); + reverts_with_msg!(contract_id, MatchEnum::Panic, call, "some panic message"); reverts_with_msg!( - &contract_id, + contract_id, MatchEnum::Panic, simulate, "some panic message" @@ -2086,13 +2078,13 @@ async fn script_with_contract_panic() -> Result<()> { } { reverts_with_msg!( - &contract_id, + contract_id, MatchEnum::PanicError, call, "some complex error B: B { id: 42, val: 36 }" ); reverts_with_msg!( - &contract_id, + contract_id, MatchEnum::PanicError, simulate, "some complex error B: B { id: 42, val: 36 }" diff --git a/e2e/tests/predicates.rs b/e2e/tests/predicates.rs index 33209c6b30..43efa5be4b 100644 --- a/e2e/tests/predicates.rs +++ b/e2e/tests/predicates.rs @@ -236,7 +236,6 @@ async fn pay_with_predicate() -> Result<()> { )? .deploy_if_not_exists(&predicate, TxPolicies::default()) .await?; - let contract_methods = MyContract::new(deploy_response.contract_id.clone(), predicate.clone()).methods(); @@ -921,6 +920,9 @@ async fn predicate_can_access_manually_added_witnesses() -> Result<()> { tx.append_witness(witness.into())?; tx.append_witness(witness2.into())?; + // As we have changed the witnesses and the predicate code depends on them we need + // to estimate the predicates again before sending the tx. + tx.estimate_predicates(&provider).await?; let tx_status = provider.send_transaction_and_await_commit(tx).await?; let fee = tx_status.total_fee(); @@ -993,7 +995,10 @@ async fn tx_id_not_changed_after_adding_witnesses() -> Result<()> { tx.append_witness(witness2.into())?; let tx_id_after_witnesses = tx.id(chain_id); - let tx_id_from_provider = provider.send_transaction(tx).await?; + // As we have changed the witnesses and the predicate code depends on them we need + // to estimate the predicates again before sending the tx. + tx.estimate_predicates(&provider).await?; + let tx_id_from_provider = provider.submit(tx).await?; assert_eq!(tx_id, tx_id_after_witnesses); assert_eq!(tx_id, tx_id_from_provider); @@ -1355,7 +1360,7 @@ async fn predicate_tx_input_output() -> Result<()> { Deploy( name = "contract_instance", contract = "TestContract", - wallet = "wallet_1", + wallet = "wallet_2", random_salt = false, ), ); @@ -1402,7 +1407,6 @@ async fn predicate_tx_input_output() -> Result<()> { .methods() .initialize_counter(36) .with_inputs(custom_inputs) - .add_signer(wallet_2.signer().clone()) .with_outputs(custom_output) .call() .await? @@ -1432,6 +1436,7 @@ async fn predicate_tx_input_output() -> Result<()> { .methods() .initialize_counter(36) .with_inputs(custom_inputs) + .add_signer(wallet_1.signer().clone()) .call() .await .unwrap_err(); diff --git a/e2e/tests/providers.rs b/e2e/tests/providers.rs index 0a79e6bbf4..5cf7db8470 100644 --- a/e2e/tests/providers.rs +++ b/e2e/tests/providers.rs @@ -15,6 +15,7 @@ use fuels::{ Bits256, coin_type::CoinType, message::Message, + output::Output, transaction_builders::{BuildableTransaction, ScriptTransactionBuilder}, tx_status::{Success, TxStatus}, }, @@ -361,7 +362,7 @@ async fn test_gas_forwarded_defaults_to_tx_limit() -> Result<()> { let response = contract_instance .methods() .initialize_counter(42) - .with_tx_policies(TxPolicies::default().with_script_gas_limit(gas_limit)) + .with_script_gas_limit(gas_limit) .call() .await?; @@ -412,14 +413,12 @@ async fn test_amount_and_asset_forwarding() -> Result<()> { .await?; assert_eq!(balance_response.value, 5_000_000); - let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000); // Forward 1_000_000 coin amount of base asset_id - // this is a big number for checking that amount can be a u64 let call_params = CallParameters::default().with_amount(1_000_000); let response = contract_methods .get_msg_amount() - .with_tx_policies(tx_policies) + .with_script_gas_limit(1_000_000) .call_params(call_params)? .call() .await?; @@ -445,7 +444,6 @@ async fn test_amount_and_asset_forwarding() -> Result<()> { // withdraw some tokens to wallet contract_methods .transfer(1_000_000, asset_id, address.into()) - .with_variable_output_policy(VariableOutputPolicy::Exactly(1)) .call() .await?; @@ -453,11 +451,10 @@ async fn test_amount_and_asset_forwarding() -> Result<()> { let call_params = CallParameters::default() .with_amount(0) .with_asset_id(asset_id); - let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000); let response = contract_methods .get_msg_amount() - .with_tx_policies(tx_policies) + .with_script_gas_limit(1_000_000) .call_params(call_params)? .call() .await?; @@ -509,28 +506,6 @@ async fn test_gas_errors() -> Result<()> { ), ); - // Test running out of gas. Gas price as `None` will be 0. - let gas_limit = 42; - let contract_instance_call = contract_instance - .methods() - .initialize_counter(42) // Build the ABI call - .with_tx_policies(TxPolicies::default().with_script_gas_limit(gas_limit)); - - // Test that the call will use more gas than the gas limit - let total_gas = contract_instance_call - .estimate_transaction_cost(None, None) - .await? - .total_gas; - assert!(total_gas > gas_limit); - - let response = contract_instance_call - .call() - .await - .expect_err("should error"); - - let expected = "transaction reverted: OutOfGas"; - assert!(response.to_string().starts_with(expected)); - // Test for insufficient base asset amount to pay for the transaction fee let response = contract_instance .methods() @@ -540,7 +515,7 @@ async fn test_gas_errors() -> Result<()> { .await .expect_err("should error"); - let expected = "Response errors; Validity(InsufficientFeeAmount"; + let expected = "the target cannot be met"; assert!(response.to_string().contains(expected)); Ok(()) @@ -566,7 +541,7 @@ async fn test_call_param_gas_errors() -> Result<()> { let contract_methods = contract_instance.methods(); let response = contract_methods .initialize_counter(42) - .with_tx_policies(TxPolicies::default().with_script_gas_limit(446000)) + .with_script_gas_limit(446000) .call_params(CallParameters::default().with_gas_forwarded(1))? .call() .await @@ -578,7 +553,7 @@ async fn test_call_param_gas_errors() -> Result<()> { // Call params gas_forwarded exceeds transaction limit let response = contract_methods .initialize_counter(42) - .with_tx_policies(TxPolicies::default().with_script_gas_limit(1)) + .with_script_gas_limit(1) .call_params(CallParameters::default().with_gas_forwarded(1_000))? .call() .await @@ -624,7 +599,7 @@ async fn test_parse_block_time() -> Result<()> { let coins = setup_single_asset_coins(signer.address(), asset_id, 1, DEFAULT_COIN_AMOUNT); let provider = setup_test_provider(coins.clone(), vec![], None, None).await?; let wallet = Wallet::new(signer, provider.clone()); - let tx_policies = TxPolicies::default().with_script_gas_limit(2000); + let tx_policies = TxPolicies::default(); let wallet_2 = wallet.lock(); let tx_response = wallet @@ -818,8 +793,8 @@ async fn transactions_with_the_same_utxo() -> Result<()> { let tx_1 = create_transfer(&wallet_1, 100, wallet_2.address()).await?; let tx_2 = create_transfer(&wallet_1, 101, wallet_2.address()).await?; - let _tx_id = provider.send_transaction(tx_1).await?; - let res = provider.send_transaction(tx_2).await; + let _tx_id = provider.submit(tx_1).await?; + let res = provider.submit(tx_2).await; let err = res.expect_err("is error"); @@ -835,6 +810,43 @@ async fn transactions_with_the_same_utxo() -> Result<()> { Ok(()) } +#[cfg(feature = "coin-cache")] +#[tokio::test] +async fn transfers_at_same_time_with_cache() -> Result<()> { + let amount = 1000; + let num_coins = 10; + let mut wallets = launch_custom_provider_and_get_wallets( + WalletsConfig::new(Some(1), Some(num_coins), Some(amount)), + Some(NodeConfig::default()), + None, + ) + .await?; + let wallet_1 = wallets.pop().unwrap(); + let provider = wallet_1.provider(); + let wallet_2 = Wallet::random(&mut thread_rng(), provider.clone()); + let asset_id = AssetId::zeroed(); + + let tx_1 = create_transfer(&wallet_1, 100, wallet_2.address()).await?; + let _tx_id = provider.submit(tx_1).await?; + + // will use assemble tx and exclude coins from the above submit + wallet_1 + .transfer( + // will use assemble tx and exclude coins from the above submit + wallet_2.address(), + 101, + AssetId::zeroed(), + TxPolicies::default(), + ) + .await?; + + let balance = wallet_2.get_asset_balance(&asset_id).await?; + + assert_eq!(201, balance); + + Ok(()) +} + #[cfg(feature = "coin-cache")] #[tokio::test] async fn coin_caching() -> Result<()> { @@ -858,7 +870,7 @@ async fn coin_caching() -> Result<()> { let mut tx_ids = vec![]; for _ in 0..num_iterations { let tx = create_transfer(&wallet_1, amount_to_send, wallet_2.address()).await?; - let tx_id = provider.send_transaction(tx).await?; + let tx_id = provider.submit(tx).await?; tx_ids.push(tx_id); } @@ -1080,7 +1092,7 @@ async fn send_transaction_and_subscribe_status() -> Result<()> { // When let mut statuses = provider.subscribe_transaction_status(&tx_id, true).await?; - let _ = provider.send_transaction(tx).await?; + let _ = provider.submit(tx).await?; // Then assert!(matches!( @@ -1127,7 +1139,7 @@ async fn can_produce_blocks_with_trig_never() -> Result<()> { let tx = tb.build(provider).await?; let tx_id = tx.id(consensus_parameters.chain_id()); - provider.send_transaction(tx).await?; + provider.submit(tx).await?; provider.produce_blocks(1, None).await?; tokio::time::sleep(std::time::Duration::from_millis(500)).await; @@ -1181,7 +1193,7 @@ async fn can_upload_executor_and_trigger_upgrade() -> Result<()> { wallet.adjust_for_fee(&mut builder, 0).await?; let tx = builder.build(provider.clone()).await?; - provider.send_transaction(tx).await?; + provider.submit(tx).await?; Ok(()) } @@ -1214,7 +1226,6 @@ async fn tx_respects_policies() -> Result<()> { Some(maturity), Some(expiration), Some(max_fee), - Some(script_gas_limit), ); // advance the block height to ensure the maturity is respected @@ -1227,6 +1238,7 @@ async fn tx_respects_policies() -> Result<()> { .methods() .initialize_counter(42) .with_tx_policies(tx_policies) + .with_script_gas_limit(script_gas_limit) .call() .await?; @@ -1244,7 +1256,7 @@ async fn tx_respects_policies() -> Result<()> { assert_eq!(script.tip().unwrap(), tip); assert_eq!(script.witness_limit().unwrap(), witness_limit); assert_eq!(script.max_fee().unwrap(), max_fee); - assert_eq!(script.gas_limit(), script_gas_limit); + assert_eq!(script.gas_limit(), 3000); Ok(()) } @@ -1453,3 +1465,51 @@ async fn is_account_query_test() -> Result<()> { Ok(()) } + +#[tokio::test] +async fn assemble_tx_transfer() -> Result<()> { + // ANCHOR: assemble_wallets + let wallet = launch_provider_and_get_wallet().await?; + let provider = wallet.provider().clone(); + let receiver = Wallet::random(&mut thread_rng(), provider.clone()); + // ANCHOR_END: assemble_wallets + + // ANCHOR: assemble_output + let consensus_parameters = provider.consensus_parameters().await?; + let base_asset_id = *consensus_parameters.base_asset_id(); + + let amount_to_send = 78; + let outputs = vec![Output::Coin { + to: receiver.address().into(), + asset_id: base_asset_id, + amount: amount_to_send, + }]; + // ANCHOR_END: assemble_output + + // ANCHOR: assemble_req_balance + let fee_index = 0u16; + let required_balances = vec![wallet.required_balance(amount_to_send, base_asset_id, None)]; + // ANCHOR_END: assemble_req_balance + + // ANCHOR: assemble_tb + let mut tb = ScriptTransactionBuilder::prepare_transfer(vec![], outputs, TxPolicies::default()) + .with_build_strategy(ScriptBuildStrategy::AssembleTx { + required_balances, + fee_index, + }); + tb.add_signer(wallet.signer().clone())?; + + let tx = tb.build(&provider).await?; + // ANCHOR_END: assemble_tb + + // ANCHOR: assemble_response + let _tx_status = provider.send_transaction_and_await_commit(tx).await?; + + assert_eq!( + amount_to_send, + receiver.get_asset_balance(&base_asset_id).await? + ); + // ANCHOR_END: assemble_response + + Ok(()) +} diff --git a/e2e/tests/scripts.rs b/e2e/tests/scripts.rs index c426aa62df..e6ce9444d4 100644 --- a/e2e/tests/scripts.rs +++ b/e2e/tests/scripts.rs @@ -95,7 +95,7 @@ async fn test_basic_script_with_tx_policies() -> Result<()> { assert_eq!(result.value, "hello"); // ANCHOR: script_with_tx_policies - let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000); + let tx_policies = TxPolicies::default().with_expiration(1_000); let result = script_instance .main(a, b) .with_tx_policies(tx_policies) @@ -139,7 +139,6 @@ async fn test_output_variable_estimation() -> Result<()> { let _ = script_call .with_inputs(inputs) .with_outputs(vec![output]) - .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum) .call() .await?; @@ -324,7 +323,7 @@ async fn test_script_transaction_builder() -> Result<()> { let tx = tb.build(provider).await?; - let tx_id = provider.send_transaction(tx).await?; + let tx_id = provider.submit(tx).await?; tokio::time::sleep(Duration::from_millis(500)).await; let tx_status = provider.tx_status(&tx_id).await?; @@ -549,9 +548,14 @@ async fn high_level_blob_upload_sets_max_fee_tolerance() -> Result<()> { }) .unwrap(); - assert_eq!( - max_fee_of_sent_blob_tx, - (zero_tolerance_fee as f32 * (1.0 + DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE)).ceil() as u64, + let expected_fee_with_tolerance = (zero_tolerance_fee as f64 + * (1.0 + DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE as f64)) + .ceil() as u64; + + let gas_error_margin = 2; + assert!( + (max_fee_of_sent_blob_tx as i64 - expected_fee_with_tolerance as i64).abs() + < gas_error_margin, "the blob upload tx should have had the max fee increased by the default estimation tolerance" ); @@ -623,7 +627,6 @@ async fn loader_script_calling_loader_proxy() -> Result<()> { .convert_into_loader() .await? .main(proxy_id.clone()) - .with_contract_ids(&[contract_id, proxy_id]) .call() .await?; diff --git a/e2e/tests/types_contracts.rs b/e2e/tests/types_contracts.rs index 0e3f382471..2ef91ec18a 100644 --- a/e2e/tests/types_contracts.rs +++ b/e2e/tests/types_contracts.rs @@ -1857,7 +1857,6 @@ async fn contract_std_lib_string() -> Result<()> { // confirm encoding/decoding a string wasn't faulty and led to too high gas consumption let _resp = contract_methods .echoes_dynamic_string(String::from("Hello Fuel")) - .with_tx_policies(TxPolicies::default().with_script_gas_limit(3600)) .call() .await?; } diff --git a/e2e/tests/wallets.rs b/e2e/tests/wallets.rs index 124a960014..2d39c4ac1c 100644 --- a/e2e/tests/wallets.rs +++ b/e2e/tests/wallets.rs @@ -123,6 +123,27 @@ async fn adjust_fee_empty_transaction() -> Result<()> { Ok(()) } +#[tokio::test] +async fn adjust_for_fee_error() -> Result<()> { + let wallet = launch_provider_and_get_wallet().await?; + let tx_policies = TxPolicies::default().with_tip(10_000_000_000_000); + + let mut tb = ScriptTransactionBuilder::prepare_transfer(vec![], vec![], tx_policies); + + wallet.add_witnesses(&mut tb)?; + let err = wallet + .adjust_for_fee(&mut tb, 0) + .await + .expect_err("should return error"); + + assert!( + matches!(err, Error::Provider(s) if s.contains("failed to get base asset \ + (0000000000000000000000000000000000000000000000000000000000000000) inputs with amount:")) + ); + + Ok(()) +} + #[tokio::test] async fn adjust_for_fee_with_message_data_input() -> Result<()> { let wallet_signer = PrivateKeySigner::random(&mut rand::thread_rng()); @@ -264,13 +285,9 @@ async fn send_transfer_transactions() -> Result<()> { // Configure transaction policies let tip = 2; - let script_gas_limit = 500_000; let maturity = 0; - let tx_policies = TxPolicies::default() - .with_tip(tip) - .with_maturity(maturity) - .with_script_gas_limit(script_gas_limit); + let tx_policies = TxPolicies::default().with_tip(tip).with_maturity(maturity); // Transfer 1 from wallet 1 to wallet 2. let amount_to_send = 1; @@ -296,8 +313,6 @@ async fn send_transfer_transactions() -> Result<()> { TransactionType::Script(tx) => tx, _ => panic!("Received unexpected tx type!"), }; - // Transfer scripts uses set `script_gas_limit` despite not having script code - assert_eq!(script.gas_limit(), script_gas_limit); assert_eq!(script.maturity().unwrap(), maturity); let wallet_1_spendable_resources = wallet_1 diff --git a/examples/contracts/src/lib.rs b/examples/contracts/src/lib.rs index 47211c9e65..c0f3e3a833 100644 --- a/examples/contracts/src/lib.rs +++ b/examples/contracts/src/lib.rs @@ -8,10 +8,7 @@ mod tests { prelude::{LoadConfiguration, NodeConfig, StorageConfiguration}, programs::debug::ScriptType, test_helpers::{ChainConfig, StateConfig}, - types::{ - Bits256, - errors::{Result, transaction::Reason}, - }, + types::{Bits256, errors::Result}, }; use rand::{Rng, thread_rng}; @@ -162,7 +159,6 @@ mod tests { // Optional: Configure deployment parameters let tx_policies = TxPolicies::default() .with_tip(1) - .with_script_gas_limit(1_000_000) .with_maturity(0) .with_expiration(10_000); @@ -301,7 +297,6 @@ mod tests { let tx_policies = TxPolicies::default() .with_tip(1) - .with_script_gas_limit(1_000_000) .with_maturity(0) .with_expiration(10_000); @@ -320,13 +315,20 @@ mod tests { .await?; // ANCHOR_END: tx_policies_default + // ANCHOR: tx_script_gas_limit + let response = contract_methods + .initialize_counter(42) + .with_script_gas_limit(42_000) + .call() + .await?; + // ANCHOR_END: tx_script_gas_limit + // ANCHOR: call_parameters let contract_methods = MyContract::new(contract_id, wallet.clone()).methods(); 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 let call_params = CallParameters::default().with_amount(1_000_000); let response = contract_methods @@ -437,7 +439,6 @@ mod tests { // withdraw some tokens to wallet let response = contract_methods .transfer(1_000_000, asset_id, address.into()) - .with_variable_output_policy(VariableOutputPolicy::Exactly(1)) .call() .await?; // ANCHOR_END: variable_outputs @@ -474,46 +475,19 @@ mod tests { let contract_methods = MyContract::new(caller_contract_id.clone(), wallet.clone()).methods(); - // ANCHOR: dependency_estimation_fail let address = wallet.address(); let amount = 100; - let response = contract_methods - .mint_then_increment_from_contract(called_contract_id, amount, address.into()) - .call() - .await; - - assert!(matches!( - response, - Err(Error::Transaction(Reason::Failure { .. })) - )); - // ANCHOR_END: dependency_estimation_fail - - // ANCHOR: dependency_estimation_manual - let response = contract_methods - .mint_then_increment_from_contract(called_contract_id, amount, address.into()) - .with_variable_output_policy(VariableOutputPolicy::Exactly(1)) - .with_contract_ids(&[called_contract_id.into()]) - .call() - .await?; - // ANCHOR_END: dependency_estimation_manual - - let asset_id = caller_contract_id.asset_id(&Bits256::zeroed()); - let balance = wallet.get_asset_balance(&asset_id).await?; - assert_eq!(balance, amount); - // ANCHOR: dependency_estimation let response = contract_methods .mint_then_increment_from_contract(called_contract_id, amount, address.into()) - .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum) - .determine_missing_contracts() - .await? .call() .await?; // ANCHOR_END: dependency_estimation + let asset_id = caller_contract_id.asset_id(&Bits256::zeroed()); let balance = wallet.get_asset_balance(&asset_id).await?; - assert_eq!(balance, 2 * amount); + assert_eq!(balance, amount); Ok(()) } @@ -576,12 +550,11 @@ 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_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. - .with_tx_policies(tx_policies) // Chain the tx policies. + .with_script_gas_limit(1_000_000) .call_params(call_params)? // Chain the call parameters. .call() // Perform the contract call. .await?; @@ -819,12 +792,10 @@ mod tests { caller_contract_instance .methods() .call_low_level_call( - target_contract_instance.id(), + target_contract_instance.contract_id(), Bytes(function_selector), Bytes(call_data), ) - .determine_missing_contracts() - .await? .call() .await?; // ANCHOR_END: low_level_call @@ -953,7 +924,7 @@ mod tests { let tx = tb.build(provider).await?; - let tx_id = provider.send_transaction(tx).await?; + let tx_id = provider.submit(tx).await?; tokio::time::sleep(Duration::from_millis(500)).await; let tx_status = provider.tx_status(&tx_id).await?; diff --git a/examples/cookbook/src/lib.rs b/examples/cookbook/src/lib.rs index 91296b479c..4cd743a571 100644 --- a/examples/cookbook/src/lib.rs +++ b/examples/cookbook/src/lib.rs @@ -75,7 +75,6 @@ mod tests { contract_methods .deposit(wallet.address().into()) .call_params(call_params)? - .with_variable_output_policy(VariableOutputPolicy::Exactly(1)) .call() .await?; // ANCHOR_END: liquidity_deposit @@ -91,7 +90,6 @@ mod tests { contract_methods .withdraw(wallet.address().into()) .call_params(call_params)? - .with_variable_output_policy(VariableOutputPolicy::Exactly(1)) .call() .await?; @@ -326,7 +324,7 @@ mod tests { // ANCHOR: custom_tx_build let tx = tb.build(&provider).await?; - let tx_id = provider.send_transaction(tx).await?; + let tx_id = provider.submit(tx).await?; // ANCHOR_END: custom_tx_build tokio::time::sleep(Duration::from_millis(500)).await; diff --git a/packages/fuels-accounts/src/account.rs b/packages/fuels-accounts/src/account.rs index 1ee6534e47..81793b3a6a 100644 --- a/packages/fuels-accounts/src/account.rs +++ b/packages/fuels-accounts/src/account.rs @@ -2,9 +2,10 @@ use std::collections::HashMap; use async_trait::async_trait; use fuel_core_client::client::pagination::{PaginatedResult, PaginationRequest}; -use fuel_tx::{Output, TxId, TxPointer, UtxoId}; +use fuel_tx::{Address, Output, TxId, TxPointer, UtxoId}; use fuel_types::{AssetId, Bytes32, ContractId, Nonce}; use fuels_core::types::{ + assemble_tx::{Account as ClientAccount, ChangePolicy, RequiredBalance}, bech32::{Bech32Address, Bech32ContractId}, coin::Coin, coin_type::CoinType, @@ -13,7 +14,9 @@ use fuels_core::types::{ input::Input, message::Message, transaction::{Transaction, TxPolicies}, - transaction_builders::{BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder}, + transaction_builders::{ + BuildableTransaction, ScriptBuildStrategy, ScriptTransactionBuilder, TransactionBuilder, + }, transaction_response::TransactionResponse, tx_response::TxResponse, tx_status::Success, @@ -170,6 +173,22 @@ pub trait Account: ViewOnlyAccount { Ok(()) } + /// Create a required balance for assemble tx using a specific account type + fn required_balance( + &self, + amount: u64, + asset_id: AssetId, + change_address: Option
, + ) -> RequiredBalance { + let account_address = self.address().into(); + RequiredBalance { + asset_id, + amount, + account: ClientAccount::Address(account_address), + change_policy: ChangePolicy::Change(change_address.unwrap_or(account_address)), + } + } + /// Transfer funds from this account to another `Address`. /// Fails if amount for asset ID is larger than address's spendable coins. /// Returns the transaction ID that was sent and the list of receipts. @@ -181,27 +200,32 @@ pub trait Account: ViewOnlyAccount { tx_policies: TxPolicies, ) -> Result