Skip to content

Releases: FuelLabs/fuels-rs

v0.37.0

23 Feb 00:56
7d15913
Compare
Choose a tag to compare

What's Changed

Full Changelog: v0.36.1...v0.37.0

New features

New script APIs: ScriptTransaction and CreateTransaction

You can now build a script transaction in a much easier fashion like this:

let mut tx = ScriptTransaction::new(&inputs, &outputs, params)
                .with_script(binary)
                .with_script_data(data);
                
// or to extend inputs/outputs
tx.inputs_mut().extend(other_inputs);
tx.outputs_mut().extend(other_outputs);

This means we've removed ExecutionScript since this abstraction lacked functionality. And also, constants and params were moved from fuels-core to fuels-types to avoid circular dep.

Configurable constants

An exciting new feature recently landed in Sway: constants. Now, we've added support in the Rust SDK so that you can configure these constants without needing to recompile your Sway code. Read more here: #844.

v0.36.1

15 Feb 17:34
89d9099
Compare
Choose a tag to compare

What's Changed

Full Changelog: v0.36.0...v0.36.1

v0.36.0

08 Feb 07:01
ef62117
Compare
Choose a tag to compare

What's Changed

Full Changelog: v0.35.1...v0.36.0

v0.35.1

03 Feb 22:07
4e92144
Compare
Choose a tag to compare

What's Changed

Full Changelog: v0.35.0...v0.35.1

v0.35.0

03 Feb 16:09
eea99f3
Compare
Choose a tag to compare

What's Changed

New Contributors

Full Changelog: v0.34.0...v0.35.0

v0.34.0

19 Jan 02:25
52f631b
Compare
Choose a tag to compare

What's Changed

New Contributors

Full Changelog: v0.33.0...v0.34.0

Breaking changes

New abigen! macro

This release includes a huge breaking change to how abigen! works and how users will interact with it. The main motivation for this change was a pretty big problem we recently discovered: what happens when you use a Sway library that shares types between contract A and contract B? You'd use a first abigen!() for the contract A and another abigen!() call for contract B. Since both contracts' JSON ABIs include the types coming from the library included... guess what? There's a conflict of generated types: the shared types in the library would be generated twice and, thus, be different types under the eyes of the Rust compiler. Not a good time!

To fix this, we've rebuilt the macro so that it's called only once, and it takes all necessary contracts, predicates, and scripts that you need. Now it looks like this:

abigen!(
    Contract(name="ContractA", abi="contract_a-abi.json"),
    Contract(name="ContractB", abi="contract_b-abi.json"),
    Script(name="SomeScript", abi="some_script-abi.json"),
    Predicate(name="SomePredicate", abi="some_predicate-abi.json"),
    // ...
);

Although it's a big breaking change, it's fairly easy to migrate:

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

Becomes

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

Read the updated SDK book for more details about the new abigen!.

New setup_contract_test!

The new abigen! opened up the possibility of improving the old setup_contract_test! macro as well. Similarly to the old abigen! macro, if you had multiple contracts, you'd call setup_contract_test! multiple times, so you'd have many blocks like this:

setup_contract_test!(
    contract_instance,
    wallet,
    "packages/fuels/tests/contracts/contract_test"
);

Now, you can call setup_contract_test! just once, passing all your contracts, scripts, predicates, and deployments, all at once:

setup_contract_test!(
    Wallets("wallet"),
    Abigen(
        name = "TestContract",
        abi = "packages/fuels/tests/contracts/contract_test"
    ),
    Deploy(
        name = "contract_instance",
        contract = "TestContract",
        wallet = "wallet"
    ),
);

Generated types with non-unique names can now be accessed only by qualifying the type path (i.e., abigen_bindings::some_contract_a_mod::SomeNonUniqueType). For more details, read the updated SDK book.

Predicate new send and receive API

Before, transferring the funds to a predicate looked like this:

wallet.transfer(
    predicate_address,
    amount_to_predicate,
    asset_id,
    TxParameters::default(),
).await?;

It started from the wallet, taking the predicate_address. Now, it starts from the predicate itself:

predicate.receive(&wallet, amount_to_predicate, asset_id, None).await?;

Similarly, spending a predicate also started from the wallet, which never really made much sense:

wallet.spend_predicate(
    predicate_address,
    predicate_code,
    amount_to_predicate,
    asset_id,
    receiver.address(),
    Some(predicate_data),
    TxParameters::default(),
).await?;

Now, it's a lot simpler, and it starts from the predicate, with the addition of a new encode_data() that you can use to pass the arguments of a predicate:

predicate
    .encode_data(signatures) // This is a new method; it'll encode the arguments for you.
    .spend(&receiver, amount_to_predicate, asset_id, None)
    .await?;

For more details, check the updated predicates in the latest docs.

Logging from external contracts

If your contract method is calling other contracts you will have to add the appropriate Inputs and Outputs to your transaction. For your convenience, the SDK provides two methods that can set those input and outputs for you: set_contracts(&[&contract_instance, ...]) and set_contract_ids(&[&contract_id, ...]).

set_contracts(&[&contract_instance, ...]) was changed and it now requires contract instances that were created using the abigen macro. When setting the external contracts with this method, logs and require revert errors originating from the external contract can be propagated and decoded by the calling contract. Here is an example:

let response = contract_caller_instance
    .methods()
    .increment_from_contract(lib_contract_id.into(), 42)
    .set_contracts(&[&lib_contract_instance])
    .call()
    .await?;

You can also have the old behavior where you used contact ids with the set_contract_ids(&[&contract_id, ...]) method. Please note that you will not get logs or require errors propagated if you use this approach. Here is an example:

let response = contract_caller_instance
        .methods()
        .increment_from_contract(lib_contract_id.into(), 42)
        .set_contract_ids(&[lib_contract_id.clone()])
        .call()
        .await?;

Better types importing experience

One piece of feedback we've received often is how hard it is to find and import fundamentals types exported by the fuels-rs crate. We're slowly addressing this by moving these types around to places where it makes sense for them to be.

In this release, we're re-exporting types in a single place — users will only care about this single place to pull types: fuels_types::core

v0.33.0

16 Dec 15:33
fce0631
Compare
Choose a tag to compare

What's Changed

New Contributors

Full Changelog: v0.32.2...v0.33.0

v0.32.2

13 Dec 02:28
fe908dd
Compare
Choose a tag to compare

What's Changed

Full Changelog: v0.32.1...v0.32.2

v0.32.1

07 Dec 18:09
88549c0
Compare
Choose a tag to compare

What's Changed

Full Changelog: v0.32.0...v0.32.1

v0.32.0

07 Dec 04:55
85b5c07
Compare
Choose a tag to compare

What's Changed

Full Changelog: v0.31.1...v0.32.0

New features and breaking changes

Custom block time

Adds an optional parameter to produce_blocks to set the start time and the interval of the produced blocks. Useful for testing conditions that require specific times for a block to be produced.

Breaking change

  • produce_blocks() now takes an additional parameter: Option<TimeParameters>.

Parsed and readable revert errors

Revert errors used to be hard to debug, as the SDK would simply dump the hex value of the error coming from the client. Now, this revert error is properly parsed, making debugging a lot easier.

Script arguments support

This release also includes supporting script arguments. I.e., your script main() functions can take arguments; now they're supported by the SDK. This is done through the new script_abigen! macro:

    script_abigen!(
        MyScript,
        "packages/fuels/tests/scripts/script_with_arguments/out/debug/script_with_arguments-abi.json"
    );
    let wallet = launch_provider_and_get_wallet().await;
    let bin_path =
        "../fuels/tests/scripts/script_with_arguments/out/debug/script_with_arguments.bin";
    let instance = MyScript::new(wallet, bin_path);

    let bim = Bimbam { val: 90 };
    let bam = SugarySnack {
        twix: 100,
        mars: 1000,
    };
    let result = instance.main(bim, bam).call().await?;
    let expected = Bimbam { val: 2190 };
    assert_eq!(result.value, expected);

Pay for transaction fees using Messages

Before, you could only use Coins to pay for tx fees; now, you can use Messages as well. Which is useful, e.g., if you want to spend ETH through bridging to pay for fees.

Logs for scripts

Logs can now be decoded when using scripts. The interface is similar to the contract's interface.

Breaking changes

Logs now are pulled through the response returned from a contract call. Instead of pulling them from the contract instance itself:

let contract_methods = contract_instance.methods();
let response = contract_methods.produce_logs_values().call().await?;

let log_u64 = contract_instance.logs_with_type::<u64>(&response.receipts)?;
let log_u32 = contract_instance.logs_with_type::<u32>(&response.receipts)?;
let log_u16 = contract_instance.logs_with_type::<u16>(&response.receipts)?;
let log_u8 = contract_instance.logs_with_type::<u8>(&response.receipts)?;

Pull the logs from the response:

let contract_methods = contract_instance.methods();
let response = contract_methods.produce_logs_values().call().await?;

let log_u64 = response.get_logs_with_type::<u64>()?;
let log_u32 = response.get_logs_with_type::<u32>()?;
let log_u16 = response.get_logs_with_type::<u16>()?;
let log_u8 = response.get_logs_with_type::<u8>()?;

Predicate data encoder

Instead of having to use the SDK's ABIEncoder directly and deal with Tokens directly in order to encode the predicate_data that you're passing to your predicate call, you can now simply use the new interface predicate.encode_data().

So, we're going from:

// Here we have some abstraction leakage. Having to go to `Token`-land in order to get the job done.
let arg = Token::U32(12345_u32);
let args: Vec<Token> = vec![arg];
let predicate_data = ABIEncoder::encode(&args).unwrap();

let amount_to_unlock = 500;

let _result = second_wallet
    .spend_predicate(
        predicate_address,
        predicate_code,
        amount_to_unlock,
        AssetId::default(),
        second_wallet.address(),
        Some(predicate_data),
        TxParameters::default(),
    )
    .await?;

To this:

// No need to deal with `Token`s, just pass whatever Rust's native data here. 
let predicate_data: Vec<u8> = predicate.encode_data(42_u64)?;

let amount_to_unlock = 500;
let _result = second_wallet
    .spend_predicate(
        predicate_address,
        predicate_code,
        amount_to_unlock,
        AssetId::default(),
        second_wallet.address(),
        Some(predicate_data),
        TxParameters::default(),
    )
    .await?;

Other breaking changes

Type rename: CallResponse -> FuelCallResponse

Type rename: Script -> ExecutableFuelCall

The method get_call_execution_script is now get_executable_call.

We also go from:

let script = multi_call_handler.get_call_execution_script().await?;
let receipts = script.call(provider).await.unwrap();

to:

let execution_script = multi_call_handler.get_executable_call().await?;
let receipts = execution_script.execute(provider).await.unwrap();