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
andfuel-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();