Sway Libraries

The purpose of Sway Libraries is to contain libraries which users can import and use that are not part of the standard library.

These libraries contain helper functions and other tools valuable to blockchain development.

NOTE: Sway is a language under heavy development therefore the libraries may not be the most ergonomic. Over time they should receive updates / improvements in order to demonstrate how Sway can be used in real use cases.

Getting Started

Adding Sway Libs as a Dependency

To import any library, the following dependency should be added to the project's Forc.toml file under [dependencies].

sway_libs = { git = "https://github.com/FuelLabs/sway-libs", tag = "v0.22.0" }

For reference, here is a complete Forc.toml file:

[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
name = "MyProject"

[dependencies]
sway_libs = { git = "https://github.com/FuelLabs/sway-libs", tag = "v0.22.0" }

NOTE: Be sure to set the tag to the latest release.

Importing Sway Libs to Your Project

Once Sway Libs is a dependency to your project, you may then import a library in your Sway Smart Contract as so:

use sway_libs::<library>::<library_function>;

For example, to import the only_owner() from the Ownership Library, use the following statement at the top of your Sway file:

use sway_libs::ownership::only_owner;

NOTE: All projects currently use forc v0.60.0, fuels-rs v0.62.0 and fuel-core 0.26.0.

Using Sway Libs

Once the library you require has been imported to your project, you may call or use any functions and structures the library provides.

In the following example, we import the Pausable Library and implement the Pausable ABI with it's associated functions.

use sway_libs::pausable::{_is_paused, _pause, _unpause, Pausable};

// Implement the Pausable ABI for our contract
impl Pausable for Contract {
    #[storage(write)]
    fn pause() {
        _pause(); // Call the provided pause function.
    }

    #[storage(write)]
    fn unpause() {
        _unpause(); // Call the provided unpause function.
    }

    #[storage(read)]
    fn is_paused() -> bool {
        _is_paused() // Call the provided is paused function.
    }
}

Any instructions related to using a specific library should be found within the libraries section of the Sway Libs Book.

For implementation details on the libraries please see the Sway Libs Docs.

Running Tests

There are two sets of tests that should be run: inline tests and sdk-harness tests.

In order to run the inline tests, make sure you are in the libs/ folder of this repository sway-libs/libs/<you are here>.

Run the tests:

forc test

Once these tests have passed, make sure you are in the tests/ folder of this repository sway-libs/tests/<you are here>.

Run the tests:

forc test && cargo test

NOTE: This may take a while depending on your hardware, future improvements to Sway will decrease build times. After this has been run once, individual test projects may be built on their own to save time.

Libraries

There are several types of libraries that Sway Libs encompases. These include libraries that provide convenience functions, standards supporting libraries, data type libraries, security functionality libraries.

For implementation details on the libraries please see the Sway Libs Docs.

Assets Libraries

Asset Libraries are any libraries that use Native Assets on the Fuel Network.

Asset Library

The Asset Library provides helper functions for the SRC-20, SRC-3, and SRC-7 standards.

Access Control and Security Libraries

Access Control and Security Libraries are any libraries that are built and intended to provide additional safety when developing smart contracts.

Ownership Library

The Ownership Library is used to apply restrictions on functions such that only a single user may call them.

Admin Library

The Admin Library is used to apply restrictions on functions such that only a select few users may call them like a whitelist.

Pausable Library

The Pausable Library allows contracts to implement an emergency stop mechanism.

Reentrancy Guard Library

The Reentrancy Guard Library is used to detect and prevent reentrancy attacks.

Cryptography Libraries

Cryptography Libraries are any libraries that provided cryptographic functionality beyond what the std-lib provides.

Bytecode Library

The Bytecode Library is used for on-chain verification and computation of bytecode roots for contracts and predicates.

Merkle Library

The Merkle Proof Library is used to verify Binary Merkle Trees computed off-chain.

Math Libraries

Math Libraries are libraries which provide mathematic functions or number types that are outside of the std-lib's scope.

Fixed Point Number Library

The Fixed Point Number Library is an interface to implement fixed-point numbers.

Signed Integers

The Signed Integers Library is an interface to implement signed integers.

Data Structures Libraries

Data Structure Libraries are libraries which provide complex data structures which unlock additional functionality for Smart Contracts.

Queue

The Queue Library is a linear data structure that provides First-In-First-Out (FIFO) operations.

Asset Library

The Asset Library provides basic helper functions for the SRC-20; Native Asset Standard, SRC-3; Mint and Burn Standard, and the SRC-7; Arbitrary Asset Metadata Standard. It is intended to make development of Native Assets using Sway quick and easy while following the standard's specifications.

For implementation details on the Asset Library please see the Sway Libs Docs.

SRC-20 Functionality

The Base or core of any Asset on the Fuel Network must follow the SRC-20; Native Asset Standard. The Asset Library's Base section supports the SRC-20's implementation.

SRC-3 Functionality

The SRC-3; Mint and Burn Standard prescribes an ABI for how Native Assets on the Fuel Network are minted and burned. The Asset Library's supply section supports the SRC-3's implementation.

SRC-7 Functionality

The SRC-7; Arbitrary Asset Metadata Standard prescribes an ABI for metadata associated with Native Assets on the Fuel Network. The Asset Library's metadata section supports the SRC-7's implementation.

Base Functionality

For implementation details on the Asset Library base functionality please see the Sway Libs Docs.

Importing the Asset Library Base Functionality

In order to use the Asset Library, Sway Libs and Sway Standards must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started. To add Sway Standards as a dependency please see the Sway Standards Book.

To import the Asset Library Base Functionality and SRC-20 Standard to your Sway Smart Contract, add the following to your Sway file:

use sway_libs::asset::base::*;
use standards::src20::*;

Integration with the SRC-20 Standard

The SRC-20 definition states that the following abi implementation is required for any Native Asset on Fuel:

abi SRC20 {
    #[storage(read)]
    fn total_assets() -> u64;
    #[storage(read)]
    fn total_supply(asset: AssetId) -> Option<u64>;
    #[storage(read)]
    fn name(asset: AssetId) -> Option<String>;
    #[storage(read)]
    fn symbol(asset: AssetId) -> Option<String>;
    #[storage(read)]
    fn decimals(asset: AssetId) -> Option<u8>;
}

The Asset Library has the following complimentary functions for each function in the SRC20 abi:

  • _total_assets()
  • _total_supply()
  • _name()
  • _symbol()
  • _decimals()

The following ABI and functions are also provided to set your SRC-20 standard storage values:

abi SetAssetAttributes {
    #[storage(write)]
    fn set_name(asset: AssetId, name: String);
    #[storage(write)]
    fn set_symbol(asset: AssetId, symbol: String);
    #[storage(write)]
    fn set_decimals(asset: AssetId, decimals: u8);
}
  • _set_name()
  • _set_symbol()
  • _set_decimals()

NOTE The _set_name(), _set_symbol(), and _set_decimals() functions will set the attributes of an asset unconditionally. External checks should be applied to restrict the setting of attributes.

Setting Up Storage

Once imported, the Asset Library's base functionality should be available. To use them, be sure to add the storage block bellow to your contract which enables the SRC-20 standard.

storage {
    total_assets: u64 = 0,
    total_supply: StorageMap<AssetId, u64> = StorageMap {},
    name: StorageMap<AssetId, StorageString> = StorageMap {},
    symbol: StorageMap<AssetId, StorageString> = StorageMap {},
    decimals: StorageMap<AssetId, u8> = StorageMap {},
}

Implementing the SRC-20 Standard with the Asset Library

To use the Asset Library's base functionly, simply pass the StorageKey from the prescribed storage block. The example below shows the implementation of the SRC-20 standard in combination with the Asset Library with no user defined restrictions or custom functionality.

use sway_libs::asset::base::{_decimals, _name, _symbol, _total_assets, _total_supply};
use standards::src20::SRC20;
use std::{hash::Hash, storage::storage_string::*, string::String};

// The SRC-20 storage block
storage {
    total_assets: u64 = 0,
    total_supply: StorageMap<AssetId, u64> = StorageMap {},
    name: StorageMap<AssetId, StorageString> = StorageMap {},
    symbol: StorageMap<AssetId, StorageString> = StorageMap {},
    decimals: StorageMap<AssetId, u8> = StorageMap {},
}

// Implement the SRC-20 Standard for this contract
impl SRC20 for Contract {
    #[storage(read)]
    fn total_assets() -> u64 {
        // Pass the `total_assets` StorageKey to `_total_assets()` from the Asset Library.
        _total_assets(storage.total_assets)
    }

    #[storage(read)]
    fn total_supply(asset: AssetId) -> Option<u64> {
        // Pass the `total_supply` StorageKey to `_total_supply()` from the Asset Library.
        _total_supply(storage.total_supply, asset)
    }

    #[storage(read)]
    fn name(asset: AssetId) -> Option<String> {
        // Pass the `name` StorageKey to `_name_()` from the Asset Library.
        _name(storage.name, asset)
    }

    #[storage(read)]
    fn symbol(asset: AssetId) -> Option<String> {
        // Pass the `symbol` StorageKey to `_symbol_()` function from the Asset Library.
        _symbol(storage.symbol, asset)
    }

    #[storage(read)]
    fn decimals(asset: AssetId) -> Option<u8> {
        // Pass the `decimals` StorageKey to `_decimals_()` function from the Asset Library.
        _decimals(storage.decimals, asset)
    }
}

Setting an Asset's SRC-20 Attributes

To set some the asset attributes for an Asset, use the SetAssetAttributes ABI provided by the Asset Library. The example below shows the implementation of the SetAssetAttributes ABI with no user defined restrictions or custom functionality. It is recommended that the Ownership Library is used in conjunction with the SetAssetAttributes ABI to ensure only a single user has permissions to set an Asset's attributes.

use sway_libs::asset::base::*;
use std::{hash::Hash, storage::storage_string::*, string::String};

storage {
    name: StorageMap<AssetId, StorageString> = StorageMap {},
    symbol: StorageMap<AssetId, StorageString> = StorageMap {},
    decimals: StorageMap<AssetId, u8> = StorageMap {},
}

impl SetAssetAttributes for Contract {
    #[storage(write)]
    fn set_name(asset: AssetId, name: String) {
        _set_name(storage.name, asset, name);
    }

    #[storage(write)]
    fn set_symbol(asset: AssetId, symbol: String) {
        _set_symbol(storage.symbol, asset, symbol);
    }

    #[storage(write)]
    fn set_decimals(asset: AssetId, decimals: u8) {
        _set_decimals(storage.decimals, asset, decimals);
    }
}

NOTE The _set_name(), _set_symbol(), and _set_decimals() functions will set the attributes of an asset unconditionally. External checks should be applied to restrict the setting of attributes.

Supply Functionality

For implementation details on the Asset Library supply functionality please see the Sway Libs Docs.

Importing the Asset Library Supply Functionality

In order to use the Asset Library, Sway Libs and Sway Standards must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started. To add Sway Standards as a dependency please see the Sway Standards Book.

To import the Asset Library Supply Functionality and SRC-3 Standard to your Sway Smart Contract, add the following to your Sway file:

use sway_libs::asset::supply::*;
use standards::src3::*;

Integration with the SRC-3 Standard

The SRC-3 definition states that the following abi implementation is required for any Native Asset on Fuel which mints and burns tokens:

abi SRC3 {
    #[storage(read, write)]
    fn mint(recipient: Identity, vault_sub_id: SubId, amount: u64);
    #[payable]
    #[storage(read, write)]
    fn burn(vault_sub_id: SubId, amount: u64);
}

The Asset Library has the following complimentary functions for each function in the SRC3 abi:

  • _mint()
  • _burn()

NOTE The _mint() and _burn() functions will mint and burn assets unconditionally. External checks should be applied to restrict the minting and burning of assets.

Setting Up Storage

Once imported, the Asset Library's supply functionality should be available. To use them, be sure to add the storage block bellow to your contract which enables the SRC-3 standard.

storage {
    total_assets: u64 = 0,
    total_supply: StorageMap<AssetId, u64> = StorageMap {},
}

Implementing the SRC-3 Standard with the Asset Library

To use a base function, simply pass the StorageKey from the prescribed storage block. The example below shows the implementation of the SRC-3 standard in combination with the Asset Library with no user defined restrictions or custom functionality. It is recommended that the Ownership Library is used in conjunction with the Asset Library;s supply functionality to ensure only a single user has permissions to mint an Asset.

use sway_libs::asset::supply::{_burn, _mint};
use standards::src3::SRC3;

storage {
    total_assets: u64 = 0,
    total_supply: StorageMap<AssetId, u64> = StorageMap {},
}

// Implement the SRC-3 Standard for this contract
impl SRC3 for Contract {
    #[storage(read, write)]
    fn mint(recipient: Identity, sub_id: SubId, amount: u64) {
        // Pass the StorageKeys to the `_mint()` function from the Asset Library.
        _mint(
            storage
                .total_assets,
            storage
                .total_supply,
            recipient,
            sub_id,
            amount,
        );
    }

    // Pass the StorageKeys to the `_burn_()` function from the Asset Library.
    #[payable]
    #[storage(read, write)]
    fn burn(sub_id: SubId, amount: u64) {
        _burn(storage.total_supply, sub_id, amount);
    }
}

NOTE The _mint() and _burn() functions will mint and burn assets unconditionally. External checks should be applied to restrict the minting and burning of assets.

Metadata Functionality

For implementation details on the Asset Library metadata functionality please see the Sway Libs Docs.

Importing the Asset Library Metadata Functionality

In order to use the Asset Library, Sway Libs and Sway Standards must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started. To add Sway Standards as a dependency please see the Sway Standards Book.

To import the Asset Library Base Functionality and SRC-7 Standard to your Sway Smart Contract, add the following to your Sway file:

use sway_libs::asset::metadata::*;
use standards::src7::*;

Integration with the SRC-7 Standard

The SRC-7 definition states that the following abi implementation is required for any Native Asset on Fuel:

abi SRC7 {
    #[storage(read)]
    fn metadata(asset: AssetId, key: String) -> Option<Metadata>;
}

The Asset Library has the following complimentary data type for the SRC-7 standard:

  • StorageMetadata

The following additional functionality for the SRC-7's Metadata type is provided:

  • as_string()
  • is_string()
  • as_u64()
  • is_u64()
  • as_bytes()
  • is_bytes()
  • as_b256()
  • is_b256()

Setting Up Storage

Once imported, the Asset Library's metadata functionality should be available. To use them, be sure to add the storage block bellow to your contract which enables the SRC-7 standard.

storage {
    metadata: StorageMetadata = StorageMetadata {},
}

Using the StorageMetadata Type

Setting Metadata

To set some metadata for an Asset, use the SetAssetMetadata ABI provided by the Asset Library. Be sure to follow the SRC-9 standard for your key. It is recommended that the Ownership Library is used in conjunction with the SetAssetMetadata ABI to ensure only a single user has permissions to set an Asset's metadata.

use sway_libs::asset::metadata::*;
use standards::src7::Metadata;

storage {
    metadata: StorageMetadata = StorageMetadata {},
}

impl SetAssetMetadata for Contract {
    #[storage(read, write)]
    fn set_metadata(asset: AssetId, key: String, metadata: Metadata) {
        _set_metadata(storage.metadata, asset, key, metadata);
    }
}

NOTE The _set_metadata() function will set the metadata of an asset unconditionally. External checks should be applied to restrict the setting of metadata.

Implementing the SRC-7 Standard with StorageMetadata

To use the StorageMetadata type, simply get the stored metadata with the associated key and AssetId. The example below shows the implementation of the SRC-7 standard in combination with the Asset Library's StorageMetadata type with no user defined restrictions or custom functionality.

use sway_libs::asset::metadata::*;
use standards::src7::{Metadata, SRC7};

storage {
    metadata: StorageMetadata = StorageMetadata {},
}

// Implement the SRC-7 Standard for this contract
impl SRC7 for Contract {
    #[storage(read)]
    fn metadata(asset: AssetId, key: String) -> Option<Metadata> {
        // Return the stored metadata
        storage.metadata.get(asset, key)
    }
}

Using the Metadata Extensions

The Metadata type defined by the SRC-7 standard can be one of 4 states:

pub enum Metadata {
    B256: b256,
    Bytes: Bytes,
    Int: u64,
    String: String,
}

The Asset Library enables the following functionality for the Metadata type:

is_b256() and as_b256()

The is_b256() check enables checking whether the Metadata type is a b256. The as_b256() returns the b256 of the Metadata type.

fn b256_type(my_metadata: Metadata) {
    assert(my_metadata.is_b256());

    let my_b256: b256 = my_metadata.as_b256().unwrap();
}

is_bytes() and as_bytes()

The is_bytes() check enables checking whether the Metadata type is a Bytes. The as_bytes() returns the Bytes of the Metadata type.

fn bytes_type(my_metadata: Metadata) {
    assert(my_metadata.is_bytes());

    let my_bytes: Bytes = my_metadata.as_bytes().unwrap();
}

is_u64() and as_u64()

The is_u64() check enables checking whether the Metadata type is a u64. The as_u64() returns the u64 of the Metadata type.

fn u64_type(my_metadata: Metadata) {
    assert(my_metadata.is_u64());

    let my_u64: u64 = my_metadata.as_u64().unwrap();
}

is_string() and as_string()

The is_string() check enables checking whether the Metadata type is a String. The as_string() returns the String of the Metadata type.

fn string_type(my_metadata: Metadata) {
    assert(my_metadata.is_string());

    let my_string: String = my_metadata.as_string().unwrap();
}

Admin Library

The Admin library provides a way to block users without an "administrative status" from calling functions within a contract. The Admin Library differs from the Ownership Library as multiple users may have administrative status. The Admin Library is often used when needing administrative calls on a contract that involve multiple users or a whitelist.

This library extends the Ownership Library. The Ownership library must be imported and used to enable the Admin library. Only the contract's owner may add and remove administrative users.

For implementation details on the Admin Library please see the Sway Libs Docs.

Importing the Admin Library

In order to use the Admin Library, Sway Libs must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started.

To import the Admin Library, be sure to include both the Admin and Ownership Libraries in your import statements.

use sway_libs::{admin::*, ownership::*};

Integrating the Admin Library into the Ownership Library

To use the Admin library, be sure to set a contract owner for your contract. The following demonstrates setting a contract owner using the Ownership Library.

use sway_libs::{admin::add_admin, ownership::initialize_ownership};

#[storage(read, write)]
fn my_constructor(new_owner: Identity) {
    initialize_ownership(new_owner);
}

#[storage(read, write)]
fn add_a_admin(new_admin: Identity) {
    // Can only be called by contract's owner set in the constructor above.
    add_admin(new_admin);
}

Basic Functionality

Adding an Admin

To add a new admin to a contract, call the add_admin() function.

#[storage(read, write)]
fn add_a_admin(new_admin: Identity) {
    // Can only be called by contract's owner.
    add_admin(new_admin);
}

NOTE Only the contract's owner may call this function. Please see the example above to set a contract owner.

Removing an Admin

To remove an admin from a contract, call the revoke_admin() function.

#[storage(read, write)]
fn remove_an_admin(old_admin: Identity) {
    // Can only be called by contract's owner.
    revoke_admin(old_admin);
}

NOTE Only the contract's owner may call this function. Please see the example above to set a contract owner.

Applying Restrictions

To restrict a function to only an admin, call the only_admin() function.

#[storage(read)]
fn only_owner_may_call() {
    only_admin();
    // Only an admin may reach this line.
}

NOTE: Admins and the contract's owner are independent of one another. only_admin() will revert if called by the contract's owner.

To restrict a function to only an admin or the contract's owner, call the only_owner_or_admin() function.

#[storage(read)]
fn both_owner_or_admin_may_call() {
    only_owner_or_admin();
    // Only an admin may reach this line.
}

Checking Admin Status

To check the administrative privileges of a user, call the is_admin() function.

#[storage(read)]
fn check_if_admin(admin: Identity) {
    let status = is_admin(admin);
    assert(status);
}

Ownership Library

The Ownership Library provides a way to block anyone other than a single "owner" from calling functions. The Ownership Library is often used when needing administrative calls on a contract by a single user.

For implementation details on the Ownership Library please see the Sway Libs Docs.

Importing the Ownership Library

In order to use the Ownership library, Sway Libs and Sway Standards must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started. To add Sway Standards as a dependency please see the Sway Standards Book.

To import the Ownership Library and SRC-5 Standard to your Sway Smart Contract, add the following to your Sway file:

use sway_libs::ownership::*;
use standards::src5::*;

Integrating the Ownership Library into the SRC-5 Standard

To implement the SRC-5 standard with the Ownership library, be sure to add the Sway Standards dependency to your contract. The following demonstrates the integration of the Ownership library with the SRC-5 standard.

use sway_libs::ownership::_owner;
use standards::src5::{SRC5, State};

impl SRC5 for Contract {
    #[storage(read)]
    fn owner() -> State {
        _owner()
    }
}

NOTE A constructor method must be implemented to initialize the owner.

Basic Functionality

Setting a Contract Owner

Once imported, the Ownership Library's functions will be available. To use them initialize the owner for your contract by calling the initialize_ownership() function in your own constructor method.

#[storage(read, write)]
fn my_constructor(new_owner: Identity) {
    initialize_ownership(new_owner);
}

Applying Restrictions

To restrict a function to only the owner, call the only_owner() function.

#[storage(read)]
fn only_owner_may_call() {
    only_owner();
    // Only the contract's owner may reach this line.
}

Checking the Ownership Status

To return the ownership state from storage, call the _owner() function.

#[storage(read)]
fn get_owner_state() {
    let owner: State = _owner();
}

Pausable Library

The Pausable library allows contracts to implement an emergency stop mechanism. This can be useful for scenarios such as having an emergency switch to freeze all transactions in the event of a large bug.

It is highly encouraged to use the Ownership Library in combination with the Pausable Library to ensure that only a single administrative user has the ability to pause your contract.

For implementation details on the Pausable Library please see the Sway Libs Docs.

Importing the Pausable Library

In order to use the Pausable library, Sway Libs must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started.

To import the Pausable Library to your Sway Smart Contract, add the following to your Sway file:

use sway_libs::pausable::*;

Basic Functionality

Implementing the Pausable abi

The Pausable Library has two states:

  • Paused
  • Unpaused

By default, your contract will start in the Unpaused state. To pause your contract, you may call the _pause() function. The example below provides a basic pausable contract using the Pausable Library's Pausable abi without any restrictions such as an administrator.

use sway_libs::pausable::{_is_paused, _pause, _unpause, Pausable};

impl Pausable for Contract {
    #[storage(write)]
    fn pause() {
        _pause();
    }

    #[storage(write)]
    fn unpause() {
        _unpause();
    }

    #[storage(read)]
    fn is_paused() -> bool {
        _is_paused()
    }
}

Applying Paused Restrictions

When developing a contract, you may want to lock functions down to a specific state. To do this, you may call either of the require_paused() or require_not_paused() functions. The example below shows these functions in use.

use sway_libs::pausable::require_paused;

#[storage(read)]
fn require_paused_state() {
    require_paused();
    // This comment will only ever be reached if the contract is in the paused state
}
use sway_libs::pausable::require_not_paused;

#[storage(read)]
fn require_not_paused_state() {
    require_not_paused();
    // This comment will only ever be reached if the contract is in the unpaused state
}

Using the Ownership Library with the Pausable Library

It is highly recommended to integrate the Ownership Library with the Pausable Library and apply restrictions the pause() and unpause() functions. This will ensure that only a single user may pause and unpause a contract in cause of emergency. Failure to apply this restriction will allow any user to obstruct a contract's functionality.

The follow example implements the Pausable abi and applies restrictions to it's pause/unpause functions. The owner of the contract must be set in an constructor defined by MyConstructor in this example.

use sway_libs::{
    ownership::{
        initialize_ownership,
        only_owner,
    },
    pausable::{
        _is_paused,
        _pause,
        _unpause,
        Pausable,
    },
};

abi MyConstructor {
    #[storage(read, write)]
    fn my_constructor(new_owner: Identity);
}

impl MyConstructor for Contract {
    #[storage(read, write)]
    fn my_constructor(new_owner: Identity) {
        initialize_ownership(new_owner);
    }
}

impl Pausable for Contract {
    #[storage(write)]
    fn pause() {
        // Add the `only_owner()` check to ensure only the owner may unpause this contract.
        only_owner();
        _pause();
    }

    #[storage(write)]
    fn unpause() {
        // Add the `only_owner()` check to ensure only the owner may unpause this contract.
        only_owner();
        _unpause();
    }

    #[storage(read)]
    fn is_paused() -> bool {
        _is_paused()
    }
}

Reentrancy Guard Library

The Reentrancy Guard Library provides an API to check for and disallow reentrancy on a contract. A reentrancy attack happens when a function is externally invoked during its execution, allowing it to be run multiple times in a single transaction.

The reentrancy check is used to check if a contract ID has been called more than once in the current call stack.

A reentrancy, or "recursive call" attack can cause some functions to behave in unexpected ways. This can be prevented by asserting a contract has not yet been called in the current transaction. An example can be found here.

For implementation details on the Reentrancy Guard Library please see the Sway Libs Docs.

Known Issues

While this can protect against both single-function reentrancy and cross-function reentrancy attacks, it WILL NOT PREVENT a cross-contract reentrancy attack.

Importing the Reentrancy Guard Library

In order to use the Reentrancy Guard library, Sway Libs must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started.

To import the Reentrancy Guard Library to your Sway Smart Contract, add the following to your Sway file:

use sway_libs::reentrancy::*;

Basic Functionality

Once imported, using the Reentrancy Library can be done by calling one of the two functions:

  • is_reentrant() -> bool
  • reentrancy_guard()

Using the Reentrancy Guard

Once imported, using the Reentrancy Guard Library can be used by calling the reentrancy_guard() in your Sway Smart Contract. The following shows a Sway Smart Contract that applies the Reentrancy Guard Library:

use sway_libs::reentrancy::reentrancy_guard;

abi MyContract {
    fn my_non_reentrant_function();
}

impl MyContract for Contract {
    fn my_non_reentrant_function() {
        reentrancy_guard();

        // my code here
    }
}

Checking Reentrancy Status

To check if the current caller is a reentrant, you may call the is_reentrant() function.

use sway_libs::reentrancy::is_reentrant;

fn check_if_reentrant() {
    assert(!is_reentrant());
}

Bytecode Library

The Bytecode Library allows for on-chain verification and computation of bytecode roots for contracts and predicates.

A bytecode root for a contract and predicate is the Merkle root of the binary Merkle tree with each leaf being 16KiB of instructions. This library will compute any contract's or predicate's bytecode root/address allowing for the verification of deployed contracts and generation of predicate addresses on-chain.

For implementation details on the Bytecode Library please see the Sway Libs Docs.

Importing the Bytecode Library

In order to use the Bytecode Library, Sway Libs must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started.

To import the Bytecode Library to your Sway Smart Contract, add the following to your Sway file:

use sway_libs::bytecode::*;

Using the Bytecode Library In Sway

Once imported, using the Bytecode Library is as simple as calling the desired function. Here is a list of function definitions that you may use.

  • compute_bytecode_root()
  • compute_bytecode_root_with_configurables()
  • compute_predicate_address()
  • compute_predicate_address_with_configurables()
  • predicate_address_from_root()
  • swap_configurables()
  • verify_contract_bytecode()
  • verify_contract_bytecode_with_configurables()
  • verify_predicate_address()
  • verify_predicate_address_with_configurables()

Known Issues

Please note that if you are passing the bytecode from the SDK and are including configurable values, the Vec<u8> bytecode provided must be copied to be mutable. The following can be added to make your bytecode mutable:

fn make_mutable(not_mutable_bytecode: Vec<u8>) {
    // Copy the bytecode to a newly allocated memory to avoid memory ownership error.
    let mut bytecode_slice = raw_slice::from_parts::<u8>(
        alloc_bytes(not_mutable_bytecode.len()),
        not_mutable_bytecode
            .len(),
    );
    not_mutable_bytecode
        .ptr()
        .copy_bytes_to(bytecode_slice.ptr(), not_mutable_bytecode.len());
    let mut bytecode_vec = Vec::from(bytecode_slice);
    // You may now use `bytecode_vec` in your computation and verification function calls
}

Basic Functionality

The examples below are intended for internal contract calls. If you are passing bytecode from the SDK, please follow the steps listed above in known issues to avoid the memory ownership error.

Swapping Configurables

Given some bytecode, you may swap the configurables of both Contracts and Predicates by calling the swap_configurables() function.

fn swap(my_bytecode: Vec<u8>, my_configurables: Vec<(u64, Vec<u8>)>) {
    let mut my_bytecode = my_bytecode;
    let resulting_bytecode: Vec<u8> = swap_configurables(my_bytecode, my_configurables);
}

Contracts

Computing the Bytecode Root

To compute a contract's bytecode root you may call the compute_bytecode_root() or compute_bytecode_root_with_configurables() functions.

fn compute_bytecode(my_bytecode: Vec<u8>) {
    let root: b256 = compute_bytecode_root(my_bytecode);
}

fn compute_bytecode_configurables(my_bytecode: Vec<u8>, my_configurables: Vec<(u64, Vec<u8>)>) {
    let mut my_bytecode = my_bytecode;
    let root: b256 = compute_bytecode_root_with_configurables(my_bytecode, my_configurables);
}

Verifying a Contract's Bytecode Root

To verify a contract's bytecode root you may call verify_bytecode_root() or verify_contract_bytecode_with_configurables() functions.

fn verify_contract(my_contract: ContractId, my_bytecode: Vec<u8>) {
    verify_contract_bytecode(my_contract, my_bytecode);
    // By reaching this line the contract has been verified to match the bytecode provided.
}

fn verify_contract_configurables(
    my_contract: ContractId,
    my_bytecode: Vec<u8>,
    my_configurables: Vec<(u64, Vec<u8>)>,
) {
    let mut my_bytecode = my_bytecode;
    verify_contract_bytecode_with_configurables(my_contract, my_bytecode, my_configurables);
    // By reaching this line the contract has been verified to match the bytecode provided.
}

Predicates

Computing the Address from Bytecode

To compute a predicates's address you may call the compute_predicate_address() or compute_predicate_address_with_configurables() functions.

fn compute_predicate(my_bytecode: Vec<u8>) {
    let address: Address = compute_predicate_address(my_bytecode);
}

fn compute_predicate_configurables(my_bytecode: Vec<u8>, my_configurables: Vec<(u64, Vec<u8>)>) {
    let mut my_bytecode = my_bytecode;
    let address: Address = compute_predicate_address_with_configurables(my_bytecode, my_configurables);
}

Computing the Address from a Root

If you have the root of a predicate, you may compute it's corresponding predicate address by calling the predicate_address_from_root() function.

fn predicate_address(my_root: b256) {
    let address: Address = predicate_address_from_root(my_root);
}

Verifying the Address

To verify a predicates's address you may call verify_predicate_address() or verify_predicate_address_with_configurables() functions.

fn verify_predicate(my_predicate: Address, my_bytecode: Vec<u8>) {
    verify_predicate_address(my_predicate, my_bytecode);
    // By reaching this line the predicate bytecode matches the address provided.
}

fn verify_predicate_configurables(
    my_predicate: Address,
    my_bytecode: Vec<u8>,
    my_configurables: Vec<(u64, Vec<u8>)>,
) {
    let mut my_bytecode = my_bytecode;
    verify_predicate_address_with_configurables(my_predicate, my_bytecode, my_configurables);
    // By reaching this line the predicate bytecode matches the address provided.
}

Merkle Library

Merkle trees allow for on-chain verification of off-chain data. With the merkle root posted on-chain, the generation of proofs off-chain can provide verifiably true data.

For implementation details on the Merkle Library please see the Sway Libs Docs.

Importing the Merkle Library

In order to use the Merkle Library, Sway Libs must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started.

To import the Merkle Library to your Sway Smart Contract, add the following to your Sway file:

use sway_libs::merkle::binary_proof::*;

Using the Merkle Proof Library In Sway

Once imported, using the Merkle Proof library is as simple as calling the desired function. Here is a list of function definitions that you may use.

  • leaf_digest()
  • node_digest()
  • process_proof()
  • verify_proof()

Basic Functionality

Computing Leaves and Nodes

The Binary Proof currently allows for you to compute leaves and nodes of a merkle tree given the appropriate hash digest.

To compute a leaf use the leaf_digest() function:

fn compute_leaf(hashed_data: b256) {
    let leaf: b256 = leaf_digest(hashed_data);
}

To compute a node given two leaves, use the node_digest() function:

fn compute_node(leaf_a: b256, leaf_b: b256) {
    let node: b256 = node_digest(leaf_a, leaf_b);
}

NOTE Order matters when computing a node.

Computing the Merkle Root

To compute a Merkle root given a proof, use the process_proof() function.

fn process(key: u64, leaf: b256, num_leaves: u64, proof: Vec<b256>) {
    let merkle_root: b256 = process_proof(key, leaf, num_leaves, proof);
}

Verifying a Proof

To verify a proof against a merkle root, use the verify_proof() function.

fn verify(
    merkle_root: b256,
    key: u64,
    leaf: b256,
    num_leaves: u64,
    proof: Vec<b256>,
) {
    assert(verify_proof(key, leaf, merkle_root, num_leaves, proof));
}

Using the Merkle Proof Library with Fuels-rs

To generate a Merkle Tree and corresponding proof for your Sway Smart Contract, use the Fuel-Merkle crate.

Importing Into Your Project

The import the Fuel-Merkle crate, the following should be added to the project's Cargo.toml file under [dependencies]:

fuel-merkle = { version = "0.50.0" }

NOTE Make sure to use the latest version of the fuel-merkle crate.

Importing Into Your Rust File

The following should be added to your Rust file to use the Fuel-Merkle crate.

use fuel_merkle::binary::in_memory::MerkleTree;

Using Fuel-Merkle

Generating A Tree

To create a merkle tree using Fuel-Merkle is as simple as pushing your leaves in increasing order.

    // Create a new Merkle Tree and define leaves
    let mut tree = MerkleTree::new();
    let leaves = ["A".as_bytes(), "B".as_bytes(), "C".as_bytes()].to_vec();

    // Hash the leaves and then push to the merkle tree
    for datum in leaves.iter() {
        let mut hasher = Sha256::new();
        hasher.update(&datum);
        let hash = hasher.finalize();
        tree.push(&hash);
    }

Generating And Verifying A Proof

To generate a proof for a specific leaf, you must have the index or key of the leaf. Simply call the prove function:

    // Define the key or index of the leaf you want to prove and the number of leaves
    let key: u64 = 0;

    // Get the merkle root and proof set
    let (merkle_root, proof_set) = tree.prove(key).unwrap();

    // Convert the proof set from Vec<Bytes32> to Vec<Bits256>
    let mut bits256_proof: Vec<Bits256> = Vec::new();
    for itterator in proof_set {
        bits256_proof.push(Bits256(itterator.clone()));
    }

Once the proof has been generated, you may call the Sway Smart Contract's verify_proof function:

    // Create the merkle leaf
    let mut leaf_hasher = Sha256::new();
    leaf_hasher.update(&leaves[key as usize]);
    let hashed_leaf_data = leaf_hasher.finalize();
    let merkle_leaf = leaf_sum(&hashed_leaf_data);

    // Get the number of leaves or data points
    let num_leaves: u64 = leaves.len() as u64;

    // Call the Sway contract to verify the generated merkle proof
    let result: bool = contract_instance
        .methods()
        .verify(
            Bits256(merkle_root),
            key,
            Bits256(merkle_leaf),
            num_leaves,
            bits256_proof,
        )
        .call()
        .await
        .unwrap()
        .value;
    assert!(result);

Fixed Point Number Library

The Fixed Point Number Library provides a library to use fixed-point numbers in Sway. It has 3 distinct unsigned types: UFP32, UFP64 and UFP128 as well as 3 signed types IFP64, IFP128 and IFP256. These types are stack allocated.

This type is stored as a u32, u64 or U128 under the hood. Therefore the size can be known at compile time and the length is static.

For implementation details on the Fixed Point Number Library please see the Sway Libs Docs.

Importing the Fixed Point Number Library

In order to use the Fixed Point Number Library, Sway Libs must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started.

To import the Fixed Point Number Library to your Sway Smart Contract, add the following to your Sway file:

use sway_libs::fixed_point::*;

Supported Fixed Point Numbers

Signed Fixed Point Numbers

We currently support the following signed Fixed Point numbers:

  • IFP64
  • IFP128
  • IFP256

In order to use the IFP64, IFP128 or IFP256 types, import them into your Sway project like so:

use sway_libs::fixed_point::{ifp128::IFP128, ifp256::IFP256, ifp64::IFP64,};

Unsigned Fixed Point Numbers

We currently support the following unsigned Fixed Point numbers:

  • UFP32
  • UFP64
  • UFP128

In order to use the UFP32, UFP64 or UFP128 types, import them into your Sway project like so:

use sway_libs::fixed_point::{ufp128::UFP128, ufp32::UFP32, ufp64::UFP64,};

Basic Functionality

Instantiating a New Fixed Point Number

Once imported, any signed or unsigned Fixed Point number type can be instantiated by defining a new variable and calling the from function.

    let mut ufp32_value = UFP32::from(0u32);
    let mut ufp64_value = UFP64::from(0u64);
    let mut ufp128_value = UFP128::from((0u64, 0u64));

Basic mathematical Functions

Basic arithmetic operations are working as usual.

fn add_ufp(val1: UFP64, val2: UFP64) {
    let result: UFP64 = val1 + val2;
}

fn subtract_ufp(val1: UFP64, val2: UFP64) {
    let result: UFP64 = val1 - val2;
}

fn multiply_ufp(val1: UFP64, val2: UFP64) {
    let result: UFP64 = val1 * val2;
}

fn divide_ufp(val1: UFP64, val2: UFP64) {
    let result: UFP64 = val1 / val2;
}

Advanced mathematical Functions Supported

We currently support the following advanced mathematical functions:

Exponential

    let ten = UFP64::from_uint(10);
    let res = UFP64::exp(ten);

Square Root

    let ufp64_169 = UFP64::from_uint(169);
    let res = UFP64::sqrt(ufp64_169);

Power

    let five = UFP64::from_uint(5);
    let res = five.pow(3u32);

Signed Integers Library

The Signed Integers library provides a library to use signed numbers in Sway. It has 6 distinct types: I8, I16, I32, I64, I128, I256. These types are stack allocated.

These types are stored as unsigned integers, therefore either u64 or a number of them. Therefore the size can be known at compile time and the length is static.

For implementation details on the Signed Integers Library please see the Sway Libs Docs.

Importing the Signed Integer Library

In order to use the Signed Integer Number Library, Sway Libs must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started.

To import the Signed Integer Number Library to your Sway Smart Contract, add the following to your Sway file:

use sway_libs::signed_integers::*;

In order to use the any of the Signed Integer types, import them into your Sway project like so:

use sway_libs::signed_integers::i8::I8;

Basic Functionality

Instantiating a New Fixed Point Number

Once imported, a Signed Integer type can be instantiated defining a new variable and calling the new function.

    let mut i8_value = I8::new();

Basic Mathematical Functions

Basic arithmetic operations are working as usual.

fn add_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 + val2;
}

fn subtract_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 - val2;
}

fn multiply_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 * val2;
}

fn divide_signed_int(val1: I8, val2: I8) {
    let result: I8 = val1 / val2;
}

Known Issues

The current implementation of U128 will compile large bytecode sizes when performing mathematical computations. As a result, I128 and I256 inherit the same issue and could cause high transaction costs. This should be resolved with future optimizations of the Sway compiler.

Queue Library

A Queue is a linear structure which follows the First-In-First-Out (FIFO) principle. This means that the elements added first are the ones that get removed first.

For implementation details on the Queue Library please see the Sway Libs Docs.

Importing the Queue Library

In order to use the Queue Library, Sway Libs must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started.

To import the Queue Library to your Sway Smart Contract, add the following to your Sway file:

use sway_libs::queue::*;

Basic Functionality

Instantiating a New Queue

Once the Queue has been imported, you can create a new queue instance by calling the new function.

    let mut queue = Queue::new();

Enqueuing elements

Adding elements to the Queue can be done using the enqueue function.

    // Enqueue an element to the queue
    queue.enqueue(10u8);

Dequeuing Elements

To remove elements from the Queue, the dequeue function is used. This function follows the FIFO principle.

    // Dequeue the first element and unwrap the value
    let first_item = queue.dequeue().unwrap();

Fetching the Head Element

To retrieve the element at the head of the Queue without removing it, you can use the peek function.

    // Peek at the head of the queue
    let head_item = queue.peek();

Checking the Queue's Length

The is_empty and len functions can be used to check if the queue is empty and to get the number of elements in the queue respectively.

    // Checks if queue is empty (returns True or False)
    let is_queue_empty = queue.is_empty();

    // Returns length of queue
    let queue_length = queue.len();