-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat!: new
abigen!
, setup_contract_test!
, and handling of shared …
…types (#763) `abigen!` can now generate contracts, scripts or predicates depending on its input. Multiple bindings can be generated at once, identical types are shared between the bindings. `setup_contract_test!` is also adapted to conform better to `abigen!` changes.
- Loading branch information
1 parent
34f0ec4
commit a8c4586
Showing
121 changed files
with
6,219 additions
and
2,115 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# abigen! | ||
|
||
`abigen!` is a procedural macro -- it generates code. It accepts inputs in the format of: | ||
```text | ||
ProgramType(name="MyProgramType", abi="my_program-abi.json")... | ||
``` | ||
where: | ||
|
||
- `ProgramType` is one of: `Contract`, `Script` or `Predicate`, | ||
|
||
- `name` is the name that will be given to the generated bindings, | ||
|
||
- `abi` is either a path to the json abi file or its actual contents. | ||
|
||
--- | ||
So, an `abigen!` which generates bindings for two contracts and one script looks like this: | ||
```rust,ignore | ||
{{#include ../../../examples/abigen/src/lib.rs:multiple_abigen_program_types}} | ||
``` | ||
|
||
## How does the generated code look? | ||
|
||
A rough overview: | ||
```rust,ignore | ||
pub mod abigen_bindings { | ||
pub mod contract_a_mod { | ||
struct SomeCustomStruct{/*...*/}; | ||
// other custom types used in the contract | ||
struct ContractA {/*...*/}; | ||
impl ContractA {/*...*/}; | ||
// ... | ||
} | ||
pub mod contract_b_mod { | ||
// ... | ||
} | ||
pub mod my_script_mod { | ||
// ... | ||
} | ||
pub mod my_predicate_mod{ | ||
// ... | ||
} | ||
pub mod shared_types{ | ||
// ... | ||
} | ||
} | ||
pub use contract_a_mod::{/*..*/}; | ||
pub use contract_b_mod::{/*..*/}; | ||
pub use my_predicate_mod::{/*..*/}; | ||
pub use shared_types::{/*..*/}; | ||
``` | ||
|
||
Each `ProgramType` gets its own `mod` based on the `name` given in the `abigen!`. Inside the respective mods, the custom types used by that program are generated, and the bindings through which the actual calls can be made. | ||
|
||
One extra `mod` called `shared_types` is generated if `abigen!` detects that the given programs share types. Instead of each `mod` regenerating the type for itself, the type is lifted out into the `shared_types` module, generated only once, and then shared between all programs that use it. | ||
|
||
A type is deemed shared if its name and definition match up. This can happen either because you've used the same library (a custom one or a type from the stdlib) or because you've happened to define the exact same type. | ||
|
||
Finally, `pub use` statements are inserted, so you don't have to fully qualify the generated types. To avoid conflict, only types that have unique names will get a `pub use` statement. If you find rustc can't find your type, it might just be that there is another generated type with the same name. To fix the issue just qualify the path by doing `abigen_bindings::whatever_contract_mod::TheType`. | ||
|
||
> **Note:** | ||
> It is **highly** encouraged that you generate all your bindings in one `abigen!` call. Doing it in this manner will allow type sharing and avoid name collisions you'd normally get when calling `abigen!` multiple times inside the same namespace. If you choose to proceed otherwise, keep in mind the generated code overview presented above and appropriately separate the `abigen!` calls into different modules to resolve the collision. | ||
## Using the bindings | ||
Let's look at a contract with two methods: `initialize_counter(arg: u64) -> u64` and `increment_counter(arg: u64) -> u64`, with the following JSON ABI: | ||
|
||
```json,ignore | ||
{{#include ../../../examples/rust_bindings/src/abi.json}} | ||
``` | ||
|
||
By doing this: | ||
```rust,ignore | ||
{{#include ../../../examples/rust_bindings/src/lib.rs:use_abigen}} | ||
``` | ||
|
||
or this: | ||
|
||
```rust,ignore | ||
{{#include ../../../examples/rust_bindings/src/lib.rs:abigen_with_string}} | ||
``` | ||
|
||
|
||
you'll generate this (shortened for brevity's sake): | ||
|
||
```rust,ignore | ||
{{#include ../../../examples/rust_bindings/src/rust_bindings_formatted.rs}} | ||
``` | ||
|
||
> **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: | ||
|
||
```rust,ignore | ||
{{#include ../../../examples/contracts/src/lib.rs:use_deployed_contract}} | ||
``` |
File renamed without changes.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# Generating bindings with abigen! | ||
|
||
You might have noticed this snippet in the previous sections: | ||
|
||
```rust,ignore | ||
{{#include ../../../examples/contracts/src/lib.rs:abigen_example}} | ||
``` | ||
|
||
The SDK lets you transform ABI methods of a smart contract, specified as JSON objects (which you can get from [Forc](https://github.com/FuelLabs/sway/tree/master/forc)), into Rust structs and methods that are type-checked at compile time. | ||
In order to call your contracts, scripts or predicates, you first need to generate the Rust bindings for them. | ||
|
||
The following subsections contain more details about the `abigen!` syntax and the code generated from it. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
# API | ||
|
||
For a more in-depth look at the APIs provided by the Fuel Rust SDK, head over to the [official documentation](https://docs.rs/fuels/latest/fuels/). In the actual rust docs, you can see the most up-to-date information about the API, which is synced with the code as it changes. | ||
For a more in-depth look at the APIs provided by the Fuel Rust SDK, head over to the [official documentation](https://docs.rs/fuels/latest/fuels/). In the actual Rust docs, you can see the most up-to-date information about the API, which is synced with the code as it changes. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# The setup_contract_test! macro | ||
|
||
When deploying contracts with the `abigen!` macro, as shown in the previous sections, the user can: | ||
|
||
- change the default configuration parameters | ||
- launch several providers | ||
- create multiple wallets | ||
- create specific assets, etc. | ||
|
||
However, it is often the case that we want to test only the contract methods and we want to deploy the contract with the default configuration parameters. The `setup_contract_test!` macro can do exactly that. | ||
|
||
--- | ||
|
||
Used to reduce boilerplate in integration tests. Accepts input in the form | ||
of `COMMAND(ARG...)...` | ||
|
||
`COMMAND` is either `Wallets`, `Abigen` or `Deploy`. | ||
|
||
`ARG` is either a: | ||
|
||
* name-value (e.g. `name="MyContract"`), or, | ||
* a literal (e.g. `"some_str_literal"`, `true`, `5`, ...) | ||
|
||
Available `COMMAND`s: | ||
|
||
Wallets | ||
--- | ||
|
||
Example: `Wallets("a_wallet", "another_wallet"...)` | ||
|
||
Description: Launches a local provider and generates wallets with names taken from the provided `ARG`s. | ||
|
||
Cardinality: 0 or 1. | ||
|
||
Abigen | ||
--- | ||
|
||
Example: `Abigen(name="MyContract", abi="some_folder")` | ||
|
||
Description: Generates the contract bindings under the name `name`. `abi` | ||
should point to the folder containing the `out` directory of the forc build. | ||
|
||
Cardinality: 0 or N. | ||
|
||
Deploy | ||
--- | ||
|
||
Example: `Deploy(name="instance_name", contract="MyContract", wallet="a_wallet")` | ||
|
||
Description: Deploys the `contract` (with salt) using `wallet`. Will create a contract instance accessible via `name`. Due to salt usage, the same contract can be deployed multiple times. Requires that an `Abigen` command be present with `name` equal to `contract`. `wallet` can either be one of the wallets in the `Wallets` `COMMAND` or the name of a wallet you've previously generated yourself. | ||
|
||
Cardinality: 0 or N. | ||
|
||
|
||
The setup code that you have seen in previous sections gets reduced to: | ||
|
||
```rust,ignore | ||
{{#include ../../../examples/contracts/src/lib.rs:deploy_contract_setup_macro_short}} | ||
``` | ||
|
||
> **Note** The same contract can be deployed several times as the macro deploys the contracts with salt. You can also deploy different contracts to the same provider by referencing the same wallet in the `Deploy` command. | ||
```rust,ignore | ||
{{#include ../../../packages/fuels/tests/contracts.rs:contract_setup_macro_multi}} | ||
``` | ||
|
||
In this example, three contracts are deployed on the same provider using the `wallet` generated by the `Wallets` command. The second and third macros use the same contract but have different IDs because of the deployment with salt. Both of them can call the first contract by using their ID. | ||
|
||
In addition, you can manually create the `wallet` variable and then use it inside the macro. This is useful if you want to create custom wallets or providers but still want to use the macro to reduce boilerplate code. Below is an example of this approach. | ||
|
||
```rust,ignore | ||
{{#include ../../../packages/fuels/tests/contracts.rs:contract_setup_macro_manual_wallet}} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
[package] | ||
name = "fuels-example-abigen" | ||
publish = false | ||
version = "0.0.0" | ||
authors = ["Fuel Labs <contact@fuel.sh>"] | ||
edition = "2021" | ||
homepage = "https://fuel.network/" | ||
license = "Apache-2.0" | ||
repository = "https://github.com/FuelLabs/fuels-rs" | ||
description = "Fuel Rust SDK abigen examples." | ||
|
||
[dev-dependencies] | ||
fuels = { version = "0.33.0", path = "../../packages/fuels" } | ||
tokio = { version = "1.10", features = ["full"] } | ||
anyhow = "1.0.68" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
#[cfg(test)] | ||
mod tests { | ||
use anyhow::Result; | ||
use fuels::prelude::*; | ||
|
||
#[tokio::test] | ||
async fn example_of_abigen_usage() -> Result<()> { | ||
// ANCHOR: multiple_abigen_program_types | ||
abigen!( | ||
Contract(name="ContractA", abi="packages/fuels/tests/bindings/sharing_types/contract_a/out/debug/contract_a-abi.json"), | ||
Contract(name="ContractB", abi="packages/fuels/tests/bindings/sharing_types/contract_b/out/debug/contract_b-abi.json"), | ||
Script(name="MyScript", abi="packages/fuels/tests/scripts/script_with_arguments/out/debug/script_with_arguments-abi.json"), | ||
Predicate(name="MyPredicate", abi="packages/fuels/tests/predicates/predicate_basic/out/debug/predicate_basic-abi.json"), | ||
); | ||
// ANCHOR_END: multiple_abigen_program_types | ||
|
||
Ok(()) | ||
} | ||
} |
Oops, something went wrong.