The abigen! macro

You might have noticed this section in the previous example:

        abigen!(
            MyContract,
            "packages/fuels/tests/contracts/contract_test/out/debug/contract_test-abi.json"
        );

The SDK lets you transform ABI methods of a smart contract, specified as JSON objects (which you can get from Forc), into Rust structs and methods that are type-checked at compile time.

For instance, a contract with two methods: initialize_counter(arg: u64) -> u64 and increment_counter(arg: u64) -> u64, with the following JSON ABI:

{
  "types": [
    {
      "typeId": 0,
      "type": "u64",
      "components": null,
      "typeParameters": null
    }
  ],
  "functions": [
    {
      "inputs": [
        {
          "name": "value",
          "type": 0,
          "typeArguments": null
        }
      ],
      "name": "initialize_counter",
      "output": {
        "name": "",
        "type": 0,
        "typeArguments": null
      }
    },
    {
      "inputs": [
        {
          "name": "value",
          "type": 0,
          "typeArguments": null
        }
      ],
      "name": "increment_counter",
      "output": {
        "name": "",
        "type": 0,
        "typeArguments": null
      }
    }
  ]
}

Can become this (shortened for brevity's sake):

pub struct MyContract {
    contract_id: ContractId,
    wallet: WalletUnlocked,
}
impl MyContract {
    pub fn new(contract_id: String, wallet: WalletUnlocked) -> Self {
        let contract_id = ContractId::from_str(&contract_id).expect("Invalid contract id");
        Self {
            contract_id,
            wallet,
        }
    }
    #[doc = "Calls the contract's `initialize_counter` (0x00000000ab64e5f2) function"]
    pub fn initialize_counter(&self, arg: u64) -> ContractCallHandler<u64> {
        Contract::method_hash(
            &self.wallet.get_provider().expect("Provider not set up"),
            self.contract_id,
            &self.wallet,
            [0, 0, 0, 0, 171, 100, 229, 242],
            &[ParamType::U64],
            &[arg.into_token()],
        )
        .expect("method not found (this should never happen)")
    }
    #[doc = "Calls the contract's `increment_counter` (0x00000000faf90dd3) function"]
    pub fn increment_counter(&self, arg: u64) -> ContractCallHandler<u64> {
        Contract::method_hash(
            &self.wallet.get_provider().expect("Provider not set up"),
            self.contract_id,
            &self.wallet,
            [0, 0, 0, 0, 250, 249, 13, 211],
            &[ParamType::U64],
            &[arg.into_token()],
        )
        .expect("method not found (this should never happen)")
    }
}

Note: that is all generated code. No need to write any of that. Ever. The generated code might look different from one version to another, this is just an example to give you an idea of what it looks like.

Then, you're able to use it to call the actual methods on the deployed contract:

        // This is an instance of your contract which you can use to make calls to your functions
        let contract_instance = MyContract::new(contract_id, wallet);

        let response = contract_instance
            .methods()
            .initialize_counter(42) // Build the ABI call
            .call() // Perform the network call
            .await?;

        assert_eq!(42, response.value);

        let response = contract_instance
            .methods()
            .increment_counter(10)
            .call()
            .await?;

        assert_eq!(52, response.value);

To generate these bindings, all you have to do is:

        use fuels::prelude::*;
        // Replace with your own JSON abi path (relative to the root of your crate)
        abigen!(MyContractName, "examples/rust_bindings/src/abi.json");

And this abigen! macro will expand the code with the type-safe Rust bindings. It takes two arguments:

  1. The name of the struct that will be generated (MyContractName);
  2. Either a path as a string to the JSON ABI file or the JSON ABI as a multiline string directly.

The same as the example above but passing the ABI definition directly:

        // Don't forget to import the `abigen` macro as above
        abigen!(
            MyContract,
            r#"
            {
                "types": [
                  {
                    "typeId": 0,
                    "type": "u64",
                    "components": null,
                    "typeParameters": null
                  }
                ],
                "functions": [
                  {
                    "inputs": [
                      {
                        "name": "value",
                        "type": 0,
                        "typeArguments": null
                      }
                    ],
                    "name": "initialize_counter",
                    "output": {
                      "name": "",
                      "type": 0,
                      "typeArguments": null
                    }
                  },
                  {
                    "inputs": [
                      {
                        "name": "value",
                        "type": 0,
                        "typeArguments": null
                      }
                    ],
                    "name": "increment_counter",
                    "output": {
                      "name": "",
                      "type": 0,
                      "typeArguments": null
                    }
                  }
                ]
              }
            "#
        );