You've probably noticed that you're often chaining .call().await.unwrap(). That's because:
.call() and .simulate() (more on this in the next section). .await it or perform concurrent tasks, making full use of Rust's async. .unwrap() the Result<CallResponse, Error> returned by the contract call. Once you unwrap the CallResponse, you have access to this struct:
pub struct CallResponse<D> {
pub value: D,
pub tx_status: Success,
pub tx_id: Option<TxId>,
pub log_decoder: LogDecoder,
} Where value will hold the value returned by its respective contract method, represented by the exact type returned by the FuelVM, E.g., if your contract returns a FuelVM's u64, value's D will be a u64. If it's a FuelVM's tuple (u8,bool), then D will be a (u8,bool). If it's a custom type, for instance, a Sway struct MyStruct containing two components, a u64, and a b256, D will be a struct generated at compile-time, called MyStruct with u64 and a [u8; 32] (the equivalent of b256 in Rust).
receipts will hold all receipts generated by that specific contract call. gas_used is the amount of gas consumed by the contract call. tx_id will hold the ID of the corresponding submitted transaction. You can use the is_ok and is_err methods to check if a contract call Result is Ok or contains an error. These methods will return either true or false.
let is_ok = response.is_ok();
let is_error = response.is_err(); If is_err returns true, you can use the unwrap_err method to unwrap the error message.
if response.is_err() {
let err = response.unwrap_err();
println!("ERROR: {:?}", err);
};