Skip to content

Commit 6090702

Browse files
authored
Merge branch 'master' into bitzoic-unix
2 parents b0972d9 + a2f88d0 commit 6090702

File tree

8 files changed

+1424
-68
lines changed

8 files changed

+1424
-68
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/book/src/testing/testing_with_forc_call.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,74 @@ forc call <CONTRACT_ID> --abi <PATH> <FUNCTION> --gas-forwarded 1000
196196
forc call <CONTRACT_ID> --abi <PATH> <FUNCTION> --max-fee 5000
197197
```
198198

199+
### Transaction Tracing
200+
201+
When you need to debug contract interactions or understand the execution flow, `forc call` provides detailed transaction traces with verbosity level 3 or higher (`-vvv` or `-v=3`).
202+
203+
```sh
204+
# Enable transaction tracing
205+
forc call <CONTRACT_ID> --abi <PATH> <FUNCTION> -vvv
206+
```
207+
208+
The transaction trace provides a hierarchical view of all contract calls, showing:
209+
210+
- Gas consumption for each call (`[gas_amount]`)
211+
- Contract addresses being called
212+
- Return values and data
213+
- Emitted logs and events
214+
- Nested contract calls with proper indentation
215+
- Overall transaction result and gas usage
216+
217+
#### Example Transaction Trace Output
218+
219+
```bash
220+
forc call 0x9275a76531bce733cfafdbcb6727ea533ebbdc358d685152169b3c4eaa47b965 \
221+
--abi ./demo/demo-caller-abi.json \
222+
call_increment_count -vvv
223+
```
224+
225+
**Output:**
226+
227+
```log
228+
Traces:
229+
[Script]
230+
├─ [124116] 0x9275a76531bce733cfafdbcb6727ea533ebbdc358d685152169b3c4eaa47b965
231+
│ ├─ [111500] 0xb792b1e233a2c06bccec611711acc3bb61bdcb28f16abdde86d1478ee02f6e42
232+
│ │ └─ ← ()
233+
│ ├─ emit AsciiString { data: "incremented count" }
234+
│ ├─ [86284] 0xb792b1e233a2c06bccec611711acc3bb61bdcb28f16abdde86d1478ee02f6e42
235+
│ │ └─ ← 0x0000000000000002
236+
│ ├─ emit 2
237+
│ ├─ emit AsciiString { data: "done" }
238+
│ ├─ [72699] 0xb792b1e233a2c06bccec611711acc3bb61bdcb28f16abdde86d1478ee02f6e42
239+
│ │ └─ ← ()
240+
│ ├─ [48287] 0xb792b1e233a2c06bccec611711acc3bb61bdcb28f16abdde86d1478ee02f6e42
241+
│ │ └─ ← 0x0000000000000003
242+
│ └─ ← 0x0000000000000003
243+
└─ ← [Return] val: 1
244+
[ScriptResult] result: Success, gas_used: 89279
245+
246+
Transaction successfully executed.
247+
Gas used: 160676
248+
```
249+
250+
#### Understanding the Trace Format
251+
252+
- `[Script]` - The root transaction script
253+
- `├─ [gas_amount] 0xcontract_address` - A contract call with gas consumption
254+
- `│ └─ ← value` - Return value from the contract call
255+
- `emit data` - Log/event emitted by the contract
256+
- Indentation shows the call hierarchy (nested calls are indented further)
257+
- `[ScriptResult]` - Final transaction result with gas used by the script
258+
- `Gas used: <gas_used>` - Total gas used by the transaction
259+
260+
This tracing feature is particularly useful for:
261+
262+
- Debugging failed transactions
263+
- Understanding gas consumption patterns
264+
- Analyzing complex multi-contract interactions
265+
- Verifying expected contract behavior
266+
199267
### Common Use Cases
200268

201269
#### Contract State Queries

forc-plugins/forc-client/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ license.workspace = true
99
repository.workspace = true
1010

1111
[dependencies]
12+
ansiterm.workspace = true
1213
anyhow.workspace = true
1314
async-trait.workspace = true
1415
aws-config.workspace = true

forc-plugins/forc-client/src/op/call/call_function.rs

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,19 @@ use crate::{
33
op::call::{
44
missing_contracts::determine_missing_contracts,
55
parser::{param_type_val_to_token, token_to_string},
6-
CallResponse, Either,
6+
Abi, CallData, CallResponse, Either,
77
},
88
};
99
use anyhow::{anyhow, bail, Result};
10-
use fuel_abi_types::abi::{program::ProgramABI, unified_program::UnifiedProgramABI};
10+
use fuel_abi_types::abi::unified_program::UnifiedProgramABI;
1111
use fuels::{
1212
accounts::ViewOnlyAccount,
1313
programs::calls::{
1414
receipt_parser::ReceiptParser,
1515
traits::{ContractDependencyConfigurator, TransactionTuner},
1616
ContractCall,
1717
},
18+
types::tx_status::TxStatus,
1819
};
1920
use fuels_core::{
2021
codec::{
@@ -29,7 +30,7 @@ use fuels_core::{
2930
ContractId,
3031
},
3132
};
32-
use std::{collections::HashMap, path::PathBuf};
33+
use std::{collections::HashMap, path::PathBuf, str::FromStr};
3334
use url::Url;
3435

3536
/// Calls a contract function with the given parameters
@@ -46,20 +47,19 @@ pub async fn call_function(
4647
caller,
4748
call_parameters,
4849
gas,
49-
output,
50+
mut output,
5051
external_contracts,
5152
..
5253
} = cmd;
5354

5455
// Load ABI (already provided in the operation)
5556
let abi_str = super::load_abi(&abi).await?;
56-
let parsed_abi: ProgramABI = serde_json::from_str(&abi_str)?;
57-
let unified_program_abi = UnifiedProgramABI::from_counterpart(&parsed_abi)?;
57+
let abi = Abi::from_str(&abi_str).map_err(|e| anyhow!("Failed to parse ABI: {}", e))?;
5858

5959
let cmd::call::FuncType::Selector(selector) = function;
6060

6161
let (encoded_data, output_param) =
62-
prepare_contract_call_data(&unified_program_abi, &selector, &function_args)?;
62+
prepare_contract_call_data(&abi.unified, &selector, &function_args)?;
6363

6464
// Setup connection to node
6565
let (wallet, tx_policies, base_asset_id) = super::setup_connection(&node, caller, &gas).await?;
@@ -84,8 +84,10 @@ pub async fn call_function(
8484

8585
// Setup variable output policy and log decoder
8686
let variable_output_policy = VariableOutputPolicy::Exactly(call_parameters.amount as usize);
87-
let error_codes = unified_program_abi
87+
let error_codes = abi
88+
.unified
8889
.error_codes
90+
.as_ref()
8991
.map_or(HashMap::new(), |error_codes| {
9092
error_codes
9193
.iter()
@@ -193,7 +195,7 @@ pub async fn call_function(
193195
};
194196

195197
// Display the script JSON when verbosity level is 2 or higher (vv)
196-
let script = if cmd.verbosity >= 2 {
198+
let script_json = if cmd.verbosity >= 2 {
197199
let script_json = serde_json::to_value(&script).unwrap();
198200
forc_tracing::println_label_green(
199201
"transaction script:\n",
@@ -204,10 +206,28 @@ pub async fn call_function(
204206
None
205207
};
206208

209+
let abi_map = HashMap::from([(contract_id, abi)]);
210+
207211
// Process transaction results
208-
let receipts = tx_status
209-
.take_receipts_checked(Some(&log_decoder))
210-
.map_err(|e| anyhow!("Failed to take receipts: {e}"))?;
212+
let receipts = match tx_status.clone().take_receipts_checked(Some(&log_decoder)) {
213+
Ok(receipts) => receipts,
214+
Err(e) => {
215+
// Print receipts when an error occurs and verbosity is high enough
216+
super::print_receipts_and_trace(
217+
tx_status.total_gas(),
218+
&tx_status.clone().take_receipts(),
219+
cmd.verbosity,
220+
&abi_map,
221+
&mut output,
222+
)?;
223+
match tx_status {
224+
TxStatus::Failure(e) | TxStatus::PreconfirmationFailure(e) => {
225+
bail!("Failed to process transaction; reason: {:#?}", e.reason);
226+
}
227+
_ => bail!("Failed to process transaction: {:#?}", e),
228+
}
229+
}
230+
};
211231

212232
// Parse the result based on output format
213233
let mut receipt_parser = ReceiptParser::new(&receipts, DecoderConfig::default());
@@ -230,18 +250,21 @@ pub async fn call_function(
230250
};
231251

232252
// Process and return the final output
233-
let program_abi = sway_core::asm_generation::ProgramABI::Fuel(parsed_abi);
234253
let mut call_response = super::process_transaction_output(
235-
&receipts,
254+
tx_status,
236255
&tx_hash.to_string(),
237-
&program_abi,
238-
Some(result),
239256
&mode,
240257
&node,
241258
cmd.verbosity,
259+
&mut output,
260+
Some(CallData {
261+
contract_id,
262+
abis: abi_map,
263+
result,
264+
}),
242265
)?;
243266
if cmd.verbosity >= 2 {
244-
call_response.script = script;
267+
call_response.script_json = script_json;
245268
}
246269

247270
Ok(call_response)
@@ -855,7 +878,7 @@ pub mod tests {
855878
.await
856879
.unwrap_err()
857880
.to_string()
858-
.contains("PanicInstruction { reason: NotEnoughBalance"));
881+
.contains("Failed to process transaction; reason: \"NotEnoughBalance\""));
859882
assert_eq!(get_contract_balance(id, provider.clone()).await, 1);
860883

861884
// contract call transfer funds to another address

0 commit comments

Comments
 (0)