Skip to content

Commit

Permalink
fix!: dry_run tx with non-zero base asset and tip (#1422)
Browse files Browse the repository at this point in the history
BREAKING CHANGE:
- Removed `dry_run_no_validation` in favor of `dry_run_opt`
- Removed `dry_run_no_validation_multiple` in favor of
`dry_run_opt_multiple`

### Checklist
- [x] I have added tests that prove my fix is effective or that my
feature works.
- [x] I have added necessary labels.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.

---------

Co-authored-by: MujkicA <[email protected]>
  • Loading branch information
hal3e and MujkicA authored Jun 13, 2024
1 parent 30221bd commit 3c37548
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 22 deletions.
51 changes: 51 additions & 0 deletions e2e/tests/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1868,3 +1868,54 @@ async fn variable_output_estimation_is_optimized() -> Result<()> {

Ok(())
}

#[tokio::test]
async fn contract_call_with_non_zero_base_asset_id_and_tip() -> Result<()> {
use fuels::prelude::*;
use fuels::tx::ConsensusParameters;

abigen!(Contract(
name = "MyContract",
abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
));

let asset_id = AssetId::new([1; 32]);

let mut consensus_parameters = ConsensusParameters::default();
consensus_parameters.set_base_asset_id(asset_id);

let config = ChainConfig {
consensus_parameters,
..Default::default()
};

let asset_base = AssetConfig {
id: asset_id,
num_coins: 1,
coin_amount: 10_000,
};

let wallet_config = WalletsConfig::new_multiple_assets(1, vec![asset_base]);
let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, Some(config)).await?;
let wallet = wallets.first().expect("has wallet");

let contract_id = Contract::load_from(
"sway/contracts/contract_test/out/release/contract_test.bin",
LoadConfiguration::default(),
)?
.deploy(wallet, TxPolicies::default())
.await?;

let contract_instance = MyContract::new(contract_id, wallet.clone());

let response = contract_instance
.methods()
.initialize_counter(42)
.with_tx_policies(TxPolicies::default().with_tip(10))
.call()
.await?;

assert_eq!(42, response.value);

Ok(())
}
3 changes: 1 addition & 2 deletions examples/contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,6 @@ mod tests {
let response = contract_instance_1
.methods()
.initialize_counter(42)
.with_tx_policies(TxPolicies::default().with_script_gas_limit(1_000_000))
.call()
.await?;

Expand All @@ -259,11 +258,11 @@ mod tests {
let response = contract_instance_2
.methods()
.initialize_counter(42) // Build the ABI call
.with_tx_policies(TxPolicies::default().with_script_gas_limit(1_000_000))
.call()
.await?;

assert_eq!(42, response.value);

Ok(())
}

Expand Down
23 changes: 17 additions & 6 deletions packages/fuels-accounts/src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,10 +309,19 @@ impl Provider {
.collect())
}

pub async fn dry_run_no_validation(&self, tx: impl Transaction) -> Result<TxStatus> {
pub async fn dry_run_opt(
&self,
tx: impl Transaction,
utxo_validation: bool,
gas_price: Option<u64>,
) -> Result<TxStatus> {
let [tx_status] = self
.client
.dry_run_opt(Transactions::new().insert(tx).as_slice(), Some(false))
.dry_run_opt(
Transactions::new().insert(tx).as_slice(),
Some(utxo_validation),
gas_price,
)
.await?
.into_iter()
.map(Into::into)
Expand All @@ -323,13 +332,15 @@ impl Provider {
Ok(tx_status)
}

pub async fn dry_run_no_validation_multiple(
pub async fn dry_run_opt_multiple(
&self,
transactions: Transactions,
utxo_validation: bool,
gas_price: Option<u64>,
) -> Result<Vec<(TxId, TxStatus)>> {
Ok(self
.client
.dry_run_opt(transactions.as_slice(), Some(false))
.dry_run_opt(transactions.as_slice(), Some(utxo_validation), gas_price)
.await?
.into_iter()
.map(|execution_status| (execution_status.id, execution_status.into()))
Expand Down Expand Up @@ -639,7 +650,7 @@ impl Provider {
tx: T,
tolerance: f64,
) -> Result<u64> {
let receipts = self.dry_run_no_validation(tx).await?.take_receipts();
let receipts = self.dry_run_opt(tx, false, None).await?.take_receipts();
let gas_used = self.get_script_gas_used(&receipts);

Ok((gas_used as f64 * (1.0 + tolerance)) as u64)
Expand Down Expand Up @@ -707,7 +718,7 @@ impl DryRunner for Provider {
async fn dry_run(&self, tx: FuelTransaction) -> Result<DryRun> {
let [tx_execution_status] = self
.client
.dry_run_opt(&vec![tx], Some(false))
.dry_run_opt(&vec![tx], Some(false), Some(0))
.await?
.try_into()
.expect("should have only one element");
Expand Down
3 changes: 2 additions & 1 deletion packages/fuels-accounts/src/provider/retryable_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,9 @@ impl RetryableClient {
&self,
tx: &[Transaction],
utxo_validation: Option<bool>,
gas_price: Option<u64>,
) -> RequestResult<Vec<TransactionExecutionStatus>> {
self.wrap(|| self.client.dry_run_opt(tx, utxo_validation, None))
self.wrap(|| self.client.dry_run_opt(tx, utxo_validation, gas_price))
.await
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use fuel_crypto::Signature;
use fuel_tx::{
field::{Inputs, Outputs, ScriptGasLimit, WitnessLimit, Witnesses},
input::coin::{CoinPredicate, CoinSigned},
Chargeable, Input as FuelInput, TxPointer, Witness,
AssetId, Chargeable, Input as FuelInput, TxPointer, Witness,
};
use itertools::Itertools;

Expand Down Expand Up @@ -84,7 +84,11 @@ impl<R: DryRunner> ScriptDryRunner<R> {
}

fn add_fake_coins(&mut self, tx: &mut fuel_tx::Script) {
if let Some(fake_input) = Self::needs_fake_spendable_input(tx.inputs()) {
let consensus_params = self.dry_runner.consensus_parameters();

if let Some(fake_input) =
Self::needs_fake_base_input(tx.inputs(), consensus_params.base_asset_id())
{
tx.inputs_mut().push(fake_input);

// Add an empty `Witness` for the `coin_signed` we just added
Expand All @@ -93,18 +97,22 @@ impl<R: DryRunner> ScriptDryRunner<R> {
}
}

fn needs_fake_spendable_input(inputs: &[FuelInput]) -> Option<fuel_tx::Input> {
let has_spendable_input = inputs.iter().any(|i| {
matches!(
i,
FuelInput::CoinSigned(CoinSigned { .. })
| FuelInput::CoinPredicate(CoinPredicate { .. })
| FuelInput::MessageCoinSigned(_)
| FuelInput::MessageCoinPredicate(_)
)
fn needs_fake_base_input(
inputs: &[FuelInput],
base_asset_id: &AssetId,
) -> Option<fuel_tx::Input> {
let has_base_asset = inputs.iter().any(|i| match i {
FuelInput::CoinSigned(CoinSigned { asset_id, .. })
| FuelInput::CoinPredicate(CoinPredicate { asset_id, .. })
if asset_id == base_asset_id =>
{
true
}
FuelInput::MessageCoinSigned(_) | FuelInput::MessageCoinPredicate(_) => true,
_ => false,
});

if has_spendable_input {
if has_base_asset {
return None;
}

Expand All @@ -128,7 +136,7 @@ impl<R: DryRunner> ScriptDryRunner<R> {
Default::default(),
fake_owner,
1_000_000_000,
Default::default(),
*base_asset_id,
TxPointer::default(),
0,
))
Expand Down

0 comments on commit 3c37548

Please sign in to comment.