Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ fuel-abi-types = "0.12"
# fuel-core-client as the GitHub Actions workflow parses this value to pull down
# the correct tarball.
fuel-core-client = { version = "0.44.0", default-features = false }
fuel-core-storage = { version = "0.44", default-features = false }
fuel-core-types = { version = "0.44", default-features = false }

# Dependencies from the `fuels-rs` repository:
Expand Down
157 changes: 137 additions & 20 deletions docs/book/src/testing/testing_with_forc_call.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ add(a: u64, b: u64) -> u64
add "0" "0"
```

### List functions from multiple contracts with additional ABIs

```bash
forc call 0xe18de7c7c8c61a1c706dccb3533caa00ba5c11b5230da4428582abf1b6831b4d \
--abi ./out/debug/counter-contract-abi.json \
--contract-abi 0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07:./token-abi.json \
--contract-abi 0x1234567890abcdef:https://example.com/pool-abi.json \
--list-functions
```

### Call a simple addition function on a deployed contract (in dry-run mode)

```bash
Expand All @@ -50,6 +60,17 @@ forc call 0xe18de7c7c8c61a1c706dccb3533caa00ba5c11b5230da4428582abf1b6831b4d \
add 1 2
```

### Call a contract with labeled addresses for better trace readability

```bash
forc call 0xe18de7c7c8c61a1c706dccb3533caa00ba5c11b5230da4428582abf1b6831b4d \
--abi ./out/debug/counter-contract-abi.json \
transfer 0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07 \
--label 0xe18de7c7c8c61a1c706dccb3533caa00ba5c11b5230da4428582abf1b6831b4d:MainContract \
--label 0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07:TokenContract \
-vvv
```

### Directly send funds to an address

```bash
Expand Down Expand Up @@ -78,10 +99,20 @@ Syntax for `forc call` for listing supported functions from the ABI - with examp
forc call --abi <ABI-PATH/URL> <CONTRACT_ID> --list-functions
```

You can also list functions from multiple contracts by providing additional contract ABIs:

```bash
forc call --abi <MAIN-ABI-PATH/URL> <MAIN-CONTRACT_ID> \
--contract-abi <CONTRACT_ID>:<ABI-PATH/URL> \
--contract-abi <ANOTHER-CONTRACT_ID>:<ABI-PATH/URL> \
--list-functions
```

Where the following arguments are required:

- `ABI-PATH/URL` is the path or URL to the contract's JSON ABI file
- `CONTRACT_ID` is the ID of the deployed contract you want to interact with
- `--contract-abi` (optional) allows you to specify additional contracts and their ABIs to list functions from multiple contracts at once

### Transfer assets

Expand Down Expand Up @@ -198,11 +229,11 @@ forc call <CONTRACT_ID> --abi <PATH> <FUNCTION> --max-fee 5000

### Transaction Tracing

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`).
When you need to debug contract interactions or understand the execution flow, `forc call` provides detailed transaction traces with verbosity level 2 or higher (`-vv` or `-v=2`).

```sh
# Enable transaction tracing
forc call <CONTRACT_ID> --abi <PATH> <FUNCTION> -vvv
forc call <CONTRACT_ID> --abi <PATH> <FUNCTION> -vv
```

The transaction trace provides a hierarchical view of all contract calls, showing:
Expand All @@ -214,30 +245,82 @@ The transaction trace provides a hierarchical view of all contract calls, showin
- Nested contract calls with proper indentation
- Overall transaction result and gas usage

#### Enhancing Traces with Labels

For better readability, you can label contract addresses in transaction traces using the `--label` flag:

```sh
# Add human-readable labels to contract addresses
forc call <CONTRACT_ID> \
--abi <PATH> \
<FUNCTION> \
--label <CONTRACT_ID>:MainContract \
--label <OTHER_CONTRACT_ID>:TokenContract \
-vv
```

**Without labels:**

```log
├─ [8793] 0x2af09151f8276611ba65f14650970657bc42c1503d6502ffbb4d085ec37065dd::transfer(100, 0x123)
│ └─ ← ()
```

**With labels:**

```log
├─ [8793] TokenContract::transfer(100, 0x123)
│ └─ ← ()
```

#### Improving Trace Decoding with Additional ABIs

For complex multi-contract interactions, you can provide additional contract ABIs to improve function signature and return data decoding:

```sh
# Add additional contract ABIs for better trace decoding
forc call <CONTRACT_ID> \
--abi <MAIN_ABI_PATH> \
<FUNCTION> \
--contract-abi <OTHER_CONTRACT_ID>:./external-abi.json \
--contract-abi <THIRD_CONTRACT_ID>:https://example.com/abi.json \
-vv
```

This helps decode:

- Function names
- Function parameters in readable format
- Return values in structured format instead of raw hex

#### Example Transaction Trace Output

```bash
forc call 0x9275a76531bce733cfafdbcb6727ea533ebbdc358d685152169b3c4eaa47b965 \
--abi ./demo/demo-caller-abi.json \
call_increment_count -vvv
call_increment_count \
--label 0x9275a76531bce733cfafdbcb6727ea533ebbdc358d685152169b3c4eaa47b965:DemoCaller \
--label 0xb792b1e233a2c06bccec611711acc3bb61bdcb28f16abdde86d1478ee02f6e42:Counter \
--contract-abi 0xb792b1e233a2c06bccec611711acc3bb61bdcb28f16abdde86d1478ee02f6e42:./counter-abi.json \
-vv
```

**Output:**

```log
Traces:
[Script]
├─ [124116] 0x9275a76531bce733cfafdbcb6727ea533ebbdc358d685152169b3c4eaa47b965
│ ├─ [111500] 0xb792b1e233a2c06bccec611711acc3bb61bdcb28f16abdde86d1478ee02f6e42
├─ [124116] DemoCaller::call_increment_count()
│ ├─ [111500] Counter::increment()
│ │ └─ ← ()
│ ├─ emit AsciiString { data: "incremented count" }
│ ├─ [86284] 0xb792b1e233a2c06bccec611711acc3bb61bdcb28f16abdde86d1478ee02f6e42
│ ├─ [86284] Counter::get_count()
│ │ └─ ← 0x0000000000000002
│ ├─ emit 2
│ ├─ emit AsciiString { data: "done" }
│ ├─ [72699] 0xb792b1e233a2c06bccec611711acc3bb61bdcb28f16abdde86d1478ee02f6e42
│ ├─ [72699] Counter::increment()
│ │ └─ ← ()
│ ├─ [48287] 0xb792b1e233a2c06bccec611711acc3bb61bdcb28f16abdde86d1478ee02f6e42
│ ├─ [48287] Counter::get_count()
│ │ └─ ← 0x0000000000000003
│ └─ ← 0x0000000000000003
└─ ← [Return] val: 1
Expand All @@ -250,7 +333,7 @@ Gas used: 160676
#### Understanding the Trace Format

- `[Script]` - The root transaction script
- `├─ [gas_amount] 0xcontract_address` - A contract call with gas consumption
- `├─ [gas_amount] ContractLabel::function_name(args)` - A contract call with gas consumption and decoded function info
- `│ └─ ← value` - Return value from the contract call
- `emit data` - Log/event emitted by the contract
- Indentation shows the call hierarchy (nested calls are indented further)
Expand All @@ -274,35 +357,63 @@ forc call <CONTRACT_ID> --abi <PATH> get_balance

# Query with parameters
forc call <CONTRACT_ID> --abi <PATH> get_user_info 0x1234...

# Update contract state
forc call <CONTRACT_ID> --abi <PATH> update_state 42 --live
```

#### Token Operations

```sh
# Check token balance
forc call <CONTRACT_ID> --abi <PATH> balance_of 0x1234...

# Transfer tokens
forc call <CONTRACT_ID> --abi <PATH> transfer 0x1234... 100 --live
# Transfer assets/tokens to an address
forc call <ADDRESS> --amount 100 --live
```

#### Contract Administration
#### Multi-Contract Debugging

```sh
# Check contract owner
forc call <CONTRACT_ID> --abi <PATH> owner

# Update contract parameters
forc call <CONTRACT_ID> --abi <PATH> update_params 42 --live
# Debug complex multi-contract interactions
forc call <MAIN_CONTRACT_ID> \
--abi <MAIN_ABI_PATH> \
complex_operation \
--label <MAIN_CONTRACT_ID>:MainContract \
--label <TOKEN_CONTRACT_ID>:TokenContract \
--label <POOL_CONTRACT_ID>:LiquidityPool \
--contract-abi <TOKEN_CONTRACT_ID>:./token-abi.json \
--contract-abi <POOL_CONTRACT_ID>:./pool-abi.json \
-vv
```

### CLI Parameters Reference

#### Trace Enhancement Options

- `--label <contract_id>:<label>` - Add human-readable labels for contract addresses in traces
- Can be used multiple times
- Contract ID can include or omit `0x` prefix
- Example: `--label 0x123:MainContract`

- `--contract-abi <contract_id>:<abi_path>` - Specify additional contract ABIs for better trace decoding
- Can be used multiple times
- Supports both local files and URLs
- Contract ID can include or omit `0x` prefix
- Example: `--contract-abi 0x123:./abi.json` or `--contract-abi 0x456:https://example.com/abi.json`

- `-v, -vv, -vvv` - Verbosity levels for trace output
- `-v`: Show decoded logs
- `-vv`: Additionally show transaction traces
- `-vvv`: Additionally show receipts and script JSON

## Tips and Tricks

- Use `--mode simulate` to estimate gas costs before making live transactions
- External contracts are automatically detected (via internal simulations), but can be manually specified with `--external-contracts`
- For complex parameter types (tuples, structs, enums), refer to the parameter types table above
- Always verify contract addresses and ABIs before making live calls
- Use environment variables for sensitive data like signing keys: `SIGNING_KEY=<key>`
- **Use labels for better trace readability**: Add `--label` flags to replace long contract addresses with meaningful names in traces
- **Provide additional ABIs for multi-contract calls**: Use `--contract-abi` to decode function calls and return values from external contracts
- **Combine labels and ABIs**: Use both together for the most readable and informative transaction traces

## Troubleshooting

Expand All @@ -311,6 +422,7 @@ forc call <CONTRACT_ID> --abi <PATH> update_params 42 --live
- **ABI Mismatch**:
- Ensure the ABI matches the deployed contract
- Verify function selectors match exactly
- Check that additional contract ABIs match their respective contracts

- **Parameter Type Errors**:
- Check parameter formats in the types table
Expand All @@ -324,3 +436,8 @@ forc call <CONTRACT_ID> --abi <PATH> update_params 42 --live
- Use simulation mode to debug
- Check gas settings
- Verify wallet has sufficient balance

- **Trace Decoding Issues**:
- Ensure contract ABIs are correctly specified
- Verify contract addresses in labels match deployed contracts
- Check that ABI files are accessible (for both local files and URLs)
1 change: 1 addition & 0 deletions forc-plugins/forc-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ forc-util = { workspace = true, features = ["tx"] }
forc-wallet.workspace = true
fuel-abi-types.workspace = true
fuel-core-client = { workspace = true, features = ["subscriptions"] }
fuel-core-storage = { workspace = true }
fuel-core-types.workspace = true
fuel-crypto.workspace = true
fuel-tx = { workspace = true, features = ["test-helpers"] }
Expand Down
68 changes: 68 additions & 0 deletions forc-plugins/forc-client/src/cmd/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,16 @@ forc call 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97 \
-vv
```

### Call a contract with address labels for better trace readability
```sh
forc call 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97 \
--abi ./contract-abi.json \
transfer 0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07 \
--label 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97:MainContract \
--label 0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07:TokenContract \
-vv
```

### Call a contract without function parameters
```sh
forc call 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97 \
Expand All @@ -196,6 +206,15 @@ forc call 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97 \
--contracts 0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07
```

### Call a contract with additional contract ABIs for better tracing
```sh
forc call 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97 \
--abi ./contract-abi.json \
transfer 0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07 \
--contract-abi 0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07:./external-abi.json \
--contract-abi 0x1234:https://example.com/abi.json
```

### Call a contract in simulation mode
```sh
forc call 0x0dcba78d7b09a1f77353f51367afd8b8ab94b5b2bb6c9437d9ba9eea47dede97 \
Expand Down Expand Up @@ -267,6 +286,20 @@ pub struct Command {
#[clap(long, value_parser = parse_abi_path)]
pub abi: Option<Either<PathBuf, Url>>,

/// Additional contract IDs and their ABI paths for better tracing and debugging.
/// Format: contract_id:abi_path (can be used multiple times)
/// Example: --contract-abi 0x123:./abi1.json --contract-abi 0x456:https://example.com/abi2.json
/// Contract IDs can be provided with or without 0x prefix
#[clap(long = "contract-abi", value_parser = parse_contract_abi, action = clap::ArgAction::Append, help_heading = "CONTRACT")]
pub contract_abis: Option<Vec<(ContractId, Either<PathBuf, Url>)>>,

/// Label addresses in the trace output for better readability.
/// Format: address:label (can be used multiple times)
/// Example: --label 0x123:MainContract --label 0x456:TokenContract
/// Addresses can be provided with or without 0x prefix
#[clap(long, value_parser = parse_label, action = clap::ArgAction::Append, help_heading = "OUTPUT")]
pub label: Option<Vec<(ContractId, String)>>,

/// The function selector to call.
/// The function selector is the name of the function to call (e.g. "transfer").
/// Not required when --list-functions is specified or when --amount is provided for direct transfer
Expand Down Expand Up @@ -381,3 +414,38 @@ fn parse_abi_path(s: &str) -> Result<Either<PathBuf, Url>, String> {
Ok(Either::Left(PathBuf::from(s)))
}
}

fn parse_contract_abi(s: &str) -> Result<(ContractId, Either<PathBuf, Url>), String> {
let parts: Vec<&str> = s.trim().split(':').collect();
let [contract_id_str, abi_path_str] = parts.try_into().map_err(|_| {
format!(
"Invalid contract ABI format: '{}'. Expected format: contract_id:abi_path",
s
)
})?;

let contract_id =
ContractId::from_str(&format!("0x{}", contract_id_str.trim_start_matches("0x")))
.map_err(|e| format!("Invalid contract ID '{}': {}", contract_id_str, e))?;

let abi_path = parse_abi_path(abi_path_str)
.map_err(|e| format!("Invalid ABI path '{}': {}", abi_path_str, e))?;

Ok((contract_id, abi_path))
}

fn parse_label(s: &str) -> Result<(ContractId, String), String> {
let parts: Vec<&str> = s.trim().split(':').collect();
let [contract_id_str, label] = parts.try_into().map_err(|_| {
format!(
"Invalid label format: '{}'. Expected format: contract_id:label",
s
)
})?;

let contract_id =
ContractId::from_str(&format!("0x{}", contract_id_str.trim_start_matches("0x")))
.map_err(|e| format!("Invalid contract ID '{}': {}", contract_id_str, e))?;

Ok((contract_id, label.to_string()))
}
Loading
Loading