From 4dadebf16b8fde7bd6e560d71fed59bb1c05a561 Mon Sep 17 00:00:00 2001
From: Call Delegation <106365423+calldelegation@users.noreply.github.com>
Date: Wed, 21 Feb 2024 15:39:11 -0500
Subject: [PATCH] feat: Demystifying Predicates (#180)
* init
* faster CI
* spell check
* intro to predicates
* on to rust testing
* CI fix
* test
* rust tests needs editing before importing into docs
* ci
* testing rust parts
* try again
* pause
* Should be working now
* review time
* again
* predicate update
---
.github/workflows/guides.yml | 1 +
.gitmodules | 3 +
docs/guides/docs/guides.json | 6 +
.../docs/intro-to-predicates/checkpoint.mdx | 56 ++++
.../intro-to-predicates/configurables.mdx | 30 ++
.../debugging-with-scripts-rust.mdx | 83 +++++
.../debugging-with-scripts.mdx | 84 +++++
.../docs/intro-to-predicates/imports.mdx | 97 ++++++
.../guides/docs/intro-to-predicates/index.mdx | 38 +++
docs/guides/docs/intro-to-predicates/main.mdx | 28 ++
.../predicate-limitations.mdx | 26 ++
.../intro-to-predicates/predicate-root.mdx | 44 +++
.../intro-to-predicates/prerequisites.mdx | 113 +++++++
.../docs/intro-to-predicates/rust-sdk.mdx | 288 ++++++++++++++++++
.../signature-verification.mdx | 51 ++++
docs/guides/docs/nav.json | 3 +-
docs/guides/examples/intro-to-predicates | 1 +
spell-check-custom-words.txt | 16 +-
tests/test.spec.ts | 40 +++
19 files changed, 1006 insertions(+), 2 deletions(-)
create mode 100644 docs/guides/docs/intro-to-predicates/checkpoint.mdx
create mode 100644 docs/guides/docs/intro-to-predicates/configurables.mdx
create mode 100644 docs/guides/docs/intro-to-predicates/debugging-with-scripts-rust.mdx
create mode 100644 docs/guides/docs/intro-to-predicates/debugging-with-scripts.mdx
create mode 100644 docs/guides/docs/intro-to-predicates/imports.mdx
create mode 100644 docs/guides/docs/intro-to-predicates/index.mdx
create mode 100644 docs/guides/docs/intro-to-predicates/main.mdx
create mode 100644 docs/guides/docs/intro-to-predicates/predicate-limitations.mdx
create mode 100644 docs/guides/docs/intro-to-predicates/predicate-root.mdx
create mode 100644 docs/guides/docs/intro-to-predicates/prerequisites.mdx
create mode 100644 docs/guides/docs/intro-to-predicates/rust-sdk.mdx
create mode 100644 docs/guides/docs/intro-to-predicates/signature-verification.mdx
create mode 160000 docs/guides/examples/intro-to-predicates
diff --git a/.github/workflows/guides.yml b/.github/workflows/guides.yml
index 475ddc34..ebab4c4b 100644
--- a/.github/workflows/guides.yml
+++ b/.github/workflows/guides.yml
@@ -44,6 +44,7 @@ jobs:
guide:
- "dev quickstart"
- "intro to sway"
+ - "intro to predicates"
steps:
# SETUP
diff --git a/.gitmodules b/.gitmodules
index 8a560df7..f303ccde 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -28,6 +28,9 @@
[submodule "docs/guides/examples/intro-to-sway"]
path = docs/guides/examples/intro-to-sway
url = https://github.com/FuelLabs/intro-to-sway.git
+[submodule "docs/guides/examples/intro-to-predicates"]
+ path = docs/guides/examples/intro-to-predicates
+ url = https://github.com/FuelLabs/intro-to-predicates.git
[submodule "docs/guides/docs/migration-guide/breaking-change-log"]
path = docs/guides/docs/migration-guide/breaking-change-log
url = https://github.com/FuelLabs/breaking-change-log
diff --git a/docs/guides/docs/guides.json b/docs/guides/docs/guides.json
index 2816ac95..705ac7ab 100644
--- a/docs/guides/docs/guides.json
+++ b/docs/guides/docs/guides.json
@@ -17,6 +17,12 @@
"featured": false,
"tags": ["Full Stack"]
},
+ "intro_to_predicates": {
+ "title": "Building stateless DeFI applications using Predicates",
+ "description": "Learn about predicates by building a MultiSig",
+ "featured": false,
+ "tags": ["Full Stack"]
+ },
"running_a_node": {
"title": "Running a Node",
"description": "Run a local Fuel node.",
diff --git a/docs/guides/docs/intro-to-predicates/checkpoint.mdx b/docs/guides/docs/intro-to-predicates/checkpoint.mdx
new file mode 100644
index 00000000..4a42032a
--- /dev/null
+++ b/docs/guides/docs/intro-to-predicates/checkpoint.mdx
@@ -0,0 +1,56 @@
+---
+title: Checkpoint
+category: Intro to Predicates
+parent:
+ label: All Guides
+ link: /guides
+---
+
+# Checkpoint
+
+If you have followed the steps properly, your predicate `main.sw` should look like the code below:
+
+
+
+## Building the predicate
+
+To format your contract, execute the command:
+
+
+
+```sh
+forc fmt
+```
+
+To get the predicate root, go to the predicate folder and run:
+
+
+
+```sh
+forc build
+```
+
+Your predicate root should be exactly:
+
+```sh
+0x9cdce04cdb323e5982bbd0c07f667c6ea2b97781a8ce6f3e2d96c0e1b5acde73
+```
+
+That's it! You've created your first **stateless** decentralized application, and we didn't even have to deploy it!
diff --git a/docs/guides/docs/intro-to-predicates/configurables.mdx b/docs/guides/docs/intro-to-predicates/configurables.mdx
new file mode 100644
index 00000000..2a79dddd
--- /dev/null
+++ b/docs/guides/docs/intro-to-predicates/configurables.mdx
@@ -0,0 +1,30 @@
+---
+title: Configurables
+category: Intro to Predicates
+parent:
+ label: All Guides
+ link: /guides
+---
+
+# Configurables
+
+Configurables are special constants that can be modified at compile time. This is where we can define the signers responsible for protecting the funds in the predicate as well as the number of signatures required.
+
+This information can later be configured with SDKs before building a transaction.
+
+
+
+
+
+Imagine you are a multisig provider assisting businesses and users in setting up their own multisigs. You wouldn't want to hard-code these details every time but rather provide a few parameters that users can configure themselves.
diff --git a/docs/guides/docs/intro-to-predicates/debugging-with-scripts-rust.mdx b/docs/guides/docs/intro-to-predicates/debugging-with-scripts-rust.mdx
new file mode 100644
index 00000000..64f00ff7
--- /dev/null
+++ b/docs/guides/docs/intro-to-predicates/debugging-with-scripts-rust.mdx
@@ -0,0 +1,83 @@
+---
+title: Logging in Rust tests
+category: Intro to Predicates
+parent:
+ label: All Guides
+ link: /guides
+---
+
+# Logging in Rust tests
+
+## Generating a Test Template in Rust
+
+To create your own test template using Rust, follow these steps with `cargo-generate` in the script project directory:
+
+1. Install `cargo-generate`:
+
+```bash
+cargo install cargo-generate --locked
+```
+
+{/*markdownlint-disable*/}
+2. Generate the template:
+{/*markdownlint-disable*/}
+
+
+
+```bash
+cargo generate --init fuellabs/sway templates/sway-test-rs --name sway-store
+```
+
+## Logging
+
+We previously covered imports and setting up the predicate in an earlier introduction to Sway tutorial, specifically in the [Rust testing section](https://docs.fuel.network/guides/intro-to-sway/rust-sdk/). If you haven't checked that out yet, I highly recommend doing so.
+
+Copy and paste the rust test below:
+
+
+
+
+
+Now, I want to draw your attention to a specific portion of the code here:
+
+
+
+We can now call `decode_logs` to extract our secret number, something we weren't able to do when testing with predicates.
+
+To enable print outputs to appear in the console during tests, you can use the `nocapture` flag.
+
+
+
+```sh
+cargo test -- --nocapture
+```
+
+Remembering this method is essential when developing more complex predicates, especially as debugging becomes increasingly challenging.
diff --git a/docs/guides/docs/intro-to-predicates/debugging-with-scripts.mdx b/docs/guides/docs/intro-to-predicates/debugging-with-scripts.mdx
new file mode 100644
index 00000000..ae68527b
--- /dev/null
+++ b/docs/guides/docs/intro-to-predicates/debugging-with-scripts.mdx
@@ -0,0 +1,84 @@
+---
+title: Debugging with Scripts
+category: Intro to Predicates
+parent:
+ label: All Guides
+ link: /guides
+---
+
+# Debugging with Scripts
+
+In every aspect of development, trade-offs are inevitable. As previously mentioned, logging is not feasible when dealing with predicates, since predicates are required to be pure. This raises an important question: how do we debug predicates?
+
+Sway, a programming language, categorizes programs into four types, with scripts being one of them. Unlike predicates, scripts allow for shared logic.
+
+Let's move outside our MultiSig project
+
+```sh
+cd ../..
+```
+
+and create a separate project called `predicate-script-logging`.
+
+
+
+```sh
+forc new --predicate predicate-script-logging
+```
+
+Copy and paste this new predicate in your `src/main.sw`. Attempting to build this predicate will result in an error, indicating that logging is an invalid operation.
+
+
+
+However, let's try switching the program type from a `predicate` to a `script`.
+
+
+
+Your code should now look like this:
+
+
+
+
+
+Now, if we attempt to build our script, it should compile without any issues.
+
+
+
+```sh
+forc build
+```
+
+Next, we'll generate a Rust template to see it in action!
diff --git a/docs/guides/docs/intro-to-predicates/imports.mdx b/docs/guides/docs/intro-to-predicates/imports.mdx
new file mode 100644
index 00000000..d65100cd
--- /dev/null
+++ b/docs/guides/docs/intro-to-predicates/imports.mdx
@@ -0,0 +1,97 @@
+---
+title: Imports
+category: Intro to Predicates
+parent:
+ label: All Guides
+ link: /guides
+---
+
+# Imports
+
+The predicate keyword is used to identify that the program is a predicate.
+
+
+
+
+
+We're going to utilize the [Sway standard library](https://github.com/FuelLabs/sway/tree/master/sway-lib-std) in our predicate. Delete the template code except for the predicate keyword and copy in the imports below:
+
+
+
+
+
+## Transactions
+
+To construct the MultiSig, it's essential for us to obtain three specific components from the transaction through the standard library:
+
+
+
+1. Transaction Witness Data: We'll use this to attach signatures on the transaction.
+2. Transaction Witness Count: This will help us determine the number of signatures attached.
+3. Transaction ID: The hash of the transaction.
+
+## Constants
+
+From the constants library, we'll be using `ZERO_B256` as a placeholder.
+
+
+
+## Signatures
+
+We'll need `b512` because signatures are of type `b512`.
+
+
+
+## Elliptical Curve
+
+Lastly, we will be using `ec_recover_address`, short for elliptical curve recovery address. It's a function that allows us to cryptographically recover the address that signed a piece of data:
+
+```rust
+signing_address = ec_recover_address(signed_data, original_data)
+```
+
+This step is crucial for safeguarding the funds and ensuring that only the correct wallets can provide the necessary signatures.
+
+
diff --git a/docs/guides/docs/intro-to-predicates/index.mdx b/docs/guides/docs/intro-to-predicates/index.mdx
new file mode 100644
index 00000000..fad3966e
--- /dev/null
+++ b/docs/guides/docs/intro-to-predicates/index.mdx
@@ -0,0 +1,38 @@
+---
+title: Intro to Predicates
+category: Intro to Predicates
+parent:
+ label: All Guides
+ link: /guides
+---
+
+# Demystifying to Predicates for blockchain developers
+
+
+> Note: This guide uses the `beta-5` toolchain and `beta-5` compatible versions.
+
+
+Predicates are Fuel's approach to stateless account abstraction. In the blockchain space, we are constantly faced with the exponential growth of state bloat that just isn't sustainable in the long term.
+In the Ethereum ecosystem, every contract deployed requires state storage on the blockchain indefinitely.
+To help with blockchain scalability, we need to consider different approaches to redefine state-minimized applications that are fundamental to the world of decentralized finance.
+
+This tutorial will specifically concentrate on the `predicate` program type, one of the [four program types](/docs/sway/sway-program-types/) in the Sway language and how we can solve this ever growing problem.
+
+## What are Predicates?
+
+To define Predicates into one sentence:
+
+> Predicates are **stateless** programs that return **true** or **false**.
+
+A predicate is represented by an Address type, identical to any EOA (Externally Owned Account) created by a private key. The bytecode of the program can be deterministically hashed and represented as an ordinary address, all calculated off-chain.
+Therefore, when this address contains assets, ANYONE can spend the assets locked behind the predicate if they can evaluate the predicate to be true. It might be helpful to think of the code as the private key to the wallet.
+
+If this concept is still a bit unclear don't worry!, let's explore a simple example in the next part of the project setup.
+
+📚 [Sway Standard Library](https://fuellabs.github.io/sway/master/std/): A native library equipped with useful types and methods.
+
+🧑🔧 [Fuelup](https://docs.fuel.network/docs/fuelup/): The official Fuel toolchain manager aids in installing and managing different versions.
+
+🦀 [Fuel's Rust SDK](https://docs.fuel.network/docs/fuels-rs/): Test and interact with your Sway contracts using Rust.
+
+⚡ [Fuel's TypeScript SDK](https://docs.fuel.network/docs/fuels-ts/): Test and interact with your Sway contracts using TypeScript.
diff --git a/docs/guides/docs/intro-to-predicates/main.mdx b/docs/guides/docs/intro-to-predicates/main.mdx
new file mode 100644
index 00000000..ab26a68b
--- /dev/null
+++ b/docs/guides/docs/intro-to-predicates/main.mdx
@@ -0,0 +1,28 @@
+---
+title: Main
+category: Intro to Predicates
+parent:
+ label: All Guides
+ link: /guides
+---
+
+# Main
+
+Now that we have all the components, let's put them together!
+
+We simply call the function across all the multisigs, tallying the number of valid signatures to see if it meets the threshold set in the configuration. It must return true or false in order to determine if assets can be unlocked.
+
+
+
+
diff --git a/docs/guides/docs/intro-to-predicates/predicate-limitations.mdx b/docs/guides/docs/intro-to-predicates/predicate-limitations.mdx
new file mode 100644
index 00000000..f1e71e9a
--- /dev/null
+++ b/docs/guides/docs/intro-to-predicates/predicate-limitations.mdx
@@ -0,0 +1,26 @@
+---
+title: Predicate Limitations
+category: Intro to Predicates
+parent:
+ label: All Guides
+ link: /guides
+---
+
+# Predicate Limitations
+
+Given that all operations are done off-chain, it's clear there are inherent limitations to the capabilities of a predicate.
+
+| | Predicates | Contracts |
+|--------------------------------|------------|-----------|
+| Access data on chain | ❌ | ✅ |
+| Read data from smart contracts | ❌ | ✅ |
+| Check date or time | ❌ | ✅ |
+| Read block hash or number | ❌ | ✅ |
+| Read input coins | ✅ | ✅ |
+| Read output coins | ✅ | ✅ |
+| Read transaction scripts | ✅ | ✅ |
+| Read transaction bytecode | ✅ | ✅ |
+
+These constraints mean that our MultiSig cannot "approve" interactions with contracts. However, it retains the ability to oversee the flow of funds when the correct wallets are involved.
+
+Now that we have a basic understanding of what a predicate is, we can start writing our MultiSig.
diff --git a/docs/guides/docs/intro-to-predicates/predicate-root.mdx b/docs/guides/docs/intro-to-predicates/predicate-root.mdx
new file mode 100644
index 00000000..bffbfa1c
--- /dev/null
+++ b/docs/guides/docs/intro-to-predicates/predicate-root.mdx
@@ -0,0 +1,44 @@
+---
+title: Predicate Root
+category: Intro to Predicates
+parent:
+ label: All Guides
+ link: /guides
+---
+
+# Predicate Root
+
+Let's pause here for a moment and build the predicate at the root of the predicate folder.
+
+
+
+```sh
+forc build
+```
+
+Unlike building a contract, constructing the predicate generates an additional piece of information: an address that is hashed from the predicate code of your templated project, known as the **predicate root**. Since this process is cryptographic, any changes to the code will result in a change in the predicate root.
+
+Since everyone is starting with the exact same templated code, the predicate root should be exactly this:
+
+```sh
+0x531b05021ee1ee564e6528cf6aaeb730a9adc5adc6a4e07b3c727f9fb9bf1460
+```
+
+Looking at the predicate, you can immediately notice several differences. There is no ABI or implementation, but simply a main function that returns true or false.
+
+
+
+Notice that we have not "deployed" anything on the Fuel blockchain, yet we already have an address that we can interact with. It is important to remember this:
+
+> Predicates are created, not deployed.
diff --git a/docs/guides/docs/intro-to-predicates/prerequisites.mdx b/docs/guides/docs/intro-to-predicates/prerequisites.mdx
new file mode 100644
index 00000000..c86e75b3
--- /dev/null
+++ b/docs/guides/docs/intro-to-predicates/prerequisites.mdx
@@ -0,0 +1,113 @@
+---
+title: Prerequisites
+category: Intro to Predicates
+parent:
+ label: All Guides
+ link: /guides
+---
+
+# Prerequisites
+
+## Installation
+
+
+
+
+
+
+
+
+
+Additionally for this guide, ensure you're using Node.js/npm version {props.nodeVersion}.
+You can check your Node.js version with:
+
+```sh
+node -v
+```
+
+## Project Setup
+
+Start with a new empty folder and name it `multisig-predicate`.
+
+
+
+```sh
+mkdir multisig-predicate
+```
+
+Go into the `multisig-predicate` folder:
+
+```sh
+cd multisig-predicate
+```
+
+Within your terminal start by creating a new sway project called `predicate`:
+
+
+
+```sh
+forc new --predicate predicate
+```
+
+> Tip: Notice the `--predicate` flag, which tells `forc` that you want to create a project based on a **predicate**, rather than the default **contract** program type.
+
+Your project structure generated from the `forc` command should like this:
+
+
+
+```sh
+tree predicate
+```
+
+```sh
+predicate
+├── Forc.toml
+└── src
+ └── main.sw
+
+1 directory, 2 files
+```
+
+Move into your predicate folder:
+
+```sh
+cd predicate
+```
+
+In VSCode, navigate to the `src` folder within the `predicate` folder, where you will find a file named `main.sw`. This is the file where your Sway predicate will be written.
diff --git a/docs/guides/docs/intro-to-predicates/rust-sdk.mdx b/docs/guides/docs/intro-to-predicates/rust-sdk.mdx
new file mode 100644
index 00000000..c80e4da4
--- /dev/null
+++ b/docs/guides/docs/intro-to-predicates/rust-sdk.mdx
@@ -0,0 +1,288 @@
+---
+title: Rust Testing
+category: Intro to Predicates
+parent:
+ label: All Guides
+ link: /guides
+---
+
+# Testing the predicate
+
+Let's jump back into our MultiSig project again!
+
+```sh
+cd ../../multisig-predicate/predicate
+```
+
+## Generating a Test Template in Rust
+
+Again follow these steps with `cargo-generate` in the predicate project directory like we did previously:
+
+1. Install `cargo-generate`:
+
+```bash
+cargo install cargo-generate --locked
+```
+
+{/*markdownlint-disable*/}
+2. Generate the template:
+{/*markdownlint-disable*/}
+
+
+
+```bash
+cargo generate --init fuellabs/sway templates/sway-test-rs --name sway-store
+```
+
+## Imports
+
+Delete the templated code and copy the following imports into your harness file. It's important to pay attention to two main imports: `predicates`, for obvious reasons, and the `ScriptTransactionBuilder`, which we'll use to create transactions. These transactions must be signed before being broadcasted to our local network.
+
+
+
+
+
+Similar to Rust testing for contracts, we'll import the predicate ABI (Application Binary Interface) to interact with it. Ensure the name of your predicate matches the one you're working with.
+
+
+
+
+
+## Setup
+
+If you're familiar with Rust testing for Sway projects, much of the setup will be similar. Copy and paste the `setup_wallets_and_network` function into your harness file.
+
+
+
+
+
+The three key setup steps include:
+
+1. Configuring the wallets that will act as owners of our multisig, through the configurables you'll see later in the tests.
+
+
+
+{/*markdownlint-disable*/}
+2. Setting up the default token (zeroth address) and loading some tokens into each wallet.
+{/*markdownlint-disable*/}
+
+
+
+{/*markdownlint-disable*/}
+3. Preparing the network to broadcast our transaction, enabling us to successfully unlock the tokens from the predicate later.
+{/*markdownlint-disable*/}
+
+
+
+Since the predicate address is deterministic, we don't need to copy it as we do with smart contracts, which are deployed with a different address each time. We can leverage SDKs to build the predicate, ensuring we're working with the correct address without error!
+
+## Test Cases
+
+### Valid 2 of 3 signatures
+
+Now, let's review the sequence of actions we'll take to simulate a real-world scenario, copy and paste the first test below and let's break it down step by step:
+
+
+
+
+
+1. A group or individuals create their multisig by specifying the wallets that will safeguard the funds.
+2. Funding the predicate.
+3. Extracting the tokens when needed by building a transaction and getting the original wallets to sign it.
+4. Broadcasting the transaction to unlock the funds from the predicate.
+
+For step 1, as mentioned earlier, when we configure the number of required signatures (up to 3) and the 3 addresses that will safeguard our funds. Importing the ABI will automatically load a `PredicateNameConfigurable` type. In our case, that will be `MultiSigConfigurables`. There will be a corresponding with_configurable function to help you load each configurable. In our case, `with_REQUIRED_SIGNATURES` and `with_SIGNERS` are both loaded in!
+
+How convenient!
+
+
+
+Next, we'll load our original predicate binary with our new configurables to generate our personalized predicate instance. Simply input your configurables using the `with_configurables` function, and this will give us a unique predicate root based on our inputs.
+
+
+
+For step 2, transferring funds to our newly generated predicate root is as straightforward as any other blockchain transfer.
+
+
+
+In step 3, when the multisig holders decide to use the locked funds, we build a transaction specifying the inputs and outputs. Pay close attention to the outputs; we need to specify where the tokens from the predicate are going, which native asset they involve, and the amount. We're essentially extracting a portion of the original base asset sent into the predicate.
+
+
+
+The correct wallet addresses configured in the configurables must sign the transactions. This information, loaded as witness data, will evaluate our predicate to true. It's crucial to provide enough correct, unique signatures; otherwise, the transaction will fail, as demonstrated in later tests. Since our test only requires 2 signatures, we need to provide just those.
+
+
+
+After the evaluation is correctly done, all we need to do is broadcast the transaction, and the requested funds should return to wallet 1.
+
+
+
+### Valid unordered 3 of 3 signatures
+
+The setup for the second test, `multisig_mixed_three_of_three`, follows the same scheme, showcasing that the transaction signing can be done in any order by valid wallets.
+
+
+
+
+
+### Insufficient valid Signatures
+
+The same principle applies to the third test, `multisig_not_enough_signatures_fails`, where the transaction will fail if there aren't enough signatures.
+
+
+
+
+
+## Checkpoint
+
+If you have followed the previous steps correctly, your `harness.rs` test file should look like this:
+
+
+
+## Running the Tests
+
+To run the test located in `tests/harness.rs`, use:
+
+
+
+```sh
+cargo test
+```
+
+If you want to print outputs to the console during tests, use the `nocapture` flag:
+
+```sh
+cargo test -- --nocapture
+```
+
+Congratulations on making it this far! We've confirmed that our Multisig works.
+
+Predicates aren't meant to be intimidating. State-minimized DeFi applications should be the standard, rather than resorting to gas golfing or writing assembly code for these optimizations. Now that you have predicates in your toolbox, go out and explore what other state-minimized DeFi applications you can build!
diff --git a/docs/guides/docs/intro-to-predicates/signature-verification.mdx b/docs/guides/docs/intro-to-predicates/signature-verification.mdx
new file mode 100644
index 00000000..c3cf2f88
--- /dev/null
+++ b/docs/guides/docs/intro-to-predicates/signature-verification.mdx
@@ -0,0 +1,51 @@
+---
+title: Signature Verification
+category: Intro to Predicates
+parent:
+ label: All Guides
+ link: /guides
+---
+
+## Signature Verification
+
+Let's define a helper function called `verify_signatures()` that checks the validity of each signature provided and rejects any that are invalid, ensuring all signatures are unique.
+
+Copy the signature verification helper function in your main.sw below:
+
+
+
+
+
+As mentioned earlier, we will utilize the transaction witnesses and the transaction hash to verify each signature, matching them to the wallets that were previously configured.
+
+1. Parameter `i`: This parameter represents the index of a signer in a predefined list of signers defined in the configurable. It's used to identify which signer's signature the function is currently attempting to verify.
+
+2. Signature Verification Loop: The function then enters a loop, iterating up to three times. This loop represents an attempt to verify the signature against up to three pieces of witness data (signatures) attached to the transaction.
+
+ - Signature Recovery: Inside the loop, for each iteration defined by `j`, it retrieves the current signature (`current_signature`) from the transaction's witness data using `tx_witness_data::(j)`. It then attempts to recover the address that generated this signature (`current_address`) by using the `ec_recover_address` function, which takes the current signature and the transaction hash as inputs.
+
+ - Address Matching: After recovering the address, the function checks if this address matches the address of the `i`th signer in the `SIGNERS` list. If a match is found, it means the signature from one of the signers has been successfully verified, and the function returns `1`.
+
+
+
+{/*markdownlint-disable*/}
+3. Return Value: The function returns `1` if a matching signature is found for the `i`th signer, indicating successful verification. If no matching signature is found after checking up to three signatures, the function returns `0`, indicating that the signature for the `i`th signer could not be verified.
+{/*markdownlint-disable*/}
+
+This allows for flexible signature verification, accommodating scenarios where signatures from the required signers can be presented in any order and ensuring that each signature is uniquely accounted for without allowing duplicates from the same wallet.
diff --git a/docs/guides/docs/nav.json b/docs/guides/docs/nav.json
index f1fecdc6..f60bbe18 100644
--- a/docs/guides/docs/nav.json
+++ b/docs/guides/docs/nav.json
@@ -4,5 +4,6 @@
"quickstart": ["Building a Smart Contract", "Building a Frontend"],
"running_a_node": ["Running a Local Node", "Running a Testnet Node"],
"migration_guide": ["Breaking Changes Log"],
- "intro_to_sway": ["Introduction to Sway", "Prerequisites", "Imports", "Structs", "ABI", "Storage", "Errors", "Functions", "Checkpoint", "Rust Testing", "Typescript Frontend", "Explore Fuel"]
+ "intro_to_sway": ["Introduction to Sway", "Prerequisites", "Imports", "Structs", "ABI", "Storage", "Errors", "Functions", "Checkpoint", "Rust Testing", "Typescript Frontend", "Explore Fuel"],
+ "intro_to_predicates": ["Introduction to Sway", "Prerequisites", "Predicate Root", "Predicate Limitations", "Imports", "Configurables", "Signature Verification", "Main", "Checkpoint", "Debugging with Scripts", "Logging in Rust tests", "Rust Testing"]
}
diff --git a/docs/guides/examples/intro-to-predicates b/docs/guides/examples/intro-to-predicates
new file mode 160000
index 00000000..85c2a467
--- /dev/null
+++ b/docs/guides/examples/intro-to-predicates
@@ -0,0 +1 @@
+Subproject commit 85c2a46785c8ad3b8fae5b13c2fc45952d815d91
diff --git a/spell-check-custom-words.txt b/spell-check-custom-words.txt
index 1273613b..856dc025 100644
--- a/spell-check-custom-words.txt
+++ b/spell-check-custom-words.txt
@@ -92,4 +92,18 @@ fuelCoreVersion
nodeVersion
nodeVersionMax
versionSet
-onboarding
\ No newline at end of file
+cryptographic
+scalability
+deterministically
+templated
+th
+sw
+MultiSig
+cryptographically
+Configurables
+multisig
+multisigs
+broadcasted
+configurables
+DeFi
+onboarding
diff --git a/tests/test.spec.ts b/tests/test.spec.ts
index 869eac06..5cde67de 100644
--- a/tests/test.spec.ts
+++ b/tests/test.spec.ts
@@ -59,4 +59,44 @@ test.describe('Guides', () => {
stopServers();
context.close();
});
+
+ test('intro to predicates', async ({ context, extensionId, page }) => {
+ const PREREQUISITES_PAGE_URL = 'guides/intro-to-predicates/prerequisites';
+ const PREDICATE_ROOT_PAGE_URL = 'guides/intro-to-predicates/predicate-root';
+ const IMPORTS_PAGE_URL = 'guides/intro-to-predicates/imports';
+ const CONFIGURABLES_PAGE_URL = 'guides/intro-to-predicates/configurables';
+ const SIGNATURE_VERIFICATION_PAGE_URL =
+ 'guides/intro-to-predicates/signature-verification';
+ const MAIN_PAGE_URL = 'guides/intro-to-predicates/main';
+ const CHECKPOINT_PAGE_URL = 'guides/intro-to-predicates/checkpoint';
+ const SCRIPT_DEBUG_PAGE_URL =
+ 'guides/intro-to-predicates/debugging-with-scripts';
+ const SCRIPT_LOGS_PAGE_URL =
+ 'guides/intro-to-predicates/debugging-with-scripts-rust';
+ const FUELS_RS_PAGE_URL = 'guides/intro-to-predicates/rust-sdk';
+
+ // SETUP
+ stopServers();
+ await useFuelWallet(context, extensionId, page);
+ await setupFolders('fuel-project');
+ await startServers(page);
+
+ // TEST CONTRACT
+ await runTest(page, context, PREREQUISITES_PAGE_URL);
+ await runTest(page, context, PREDICATE_ROOT_PAGE_URL);
+ await runTest(page, context, IMPORTS_PAGE_URL);
+ await runTest(page, context, CONFIGURABLES_PAGE_URL);
+ await runTest(page, context, SIGNATURE_VERIFICATION_PAGE_URL);
+ await runTest(page, context, MAIN_PAGE_URL);
+ await runTest(page, context, CHECKPOINT_PAGE_URL);
+
+ // TEST RUST
+ await runTest(page, context, SCRIPT_DEBUG_PAGE_URL);
+ await runTest(page, context, SCRIPT_LOGS_PAGE_URL);
+ await runTest(page, context, FUELS_RS_PAGE_URL);
+
+ // SHUT DOWN
+ stopServers();
+ context.close();
+ });
});