Skip to content

Commit

Permalink
Adding a non-fungible token standard (#116)
Browse files Browse the repository at this point in the history
## Type of change

<!--Delete points that do not apply-->

- New feature
- Improvement (refactoring, restructuring repository, cleaning tech
debt, ...)
- Documentation

## Changes

This adds a minimal standard (abi and usage guidelines) which have been
extracted from our existing NFT implementation.

## Notes

- Name is open to discussion
- location of this standard is open to discussion, but should be kept
together with the standard for Fungible tokens.

---------

Co-authored-by: bitzoic <[email protected]>
Co-authored-by: Cameron Carstens <[email protected]>
  • Loading branch information
3 people authored Apr 3, 2023
1 parent 4c0c99a commit 715a90d
Show file tree
Hide file tree
Showing 14 changed files with 285 additions and 0 deletions.
42 changes: 42 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,48 @@ jobs:
run: |
cargo test --manifest-path tests/Cargo.toml
build-standards:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.RUST_VERSION }}
override: true

- name: Init cache
uses: Swatinem/rust-cache@v1

- name: Install a modern linker (mold)
uses: rui314/setup-mold@v1

- name: Force Rust to use mold globally for compilation
run: |
touch ~/.cargo/config.toml
echo "[target.x86_64-unknown-linux-gnu]" > ~/.cargo/config.toml
echo 'linker = "clang"' >> ~/.cargo/config.toml
echo 'rustflags = ["-C", "link-arg=-fuse-ld=/usr/local/bin/mold"]' >> ~/.cargo/config.toml
- name: Install rustfmt
run: rustup component add rustfmt

- name: Install Fuel toolchain
uses: FuelLabs/[email protected]
with:
name: my-toolchain
components: forc@${{ env.FORC_VERSION }}, fuel-core@${{ env.CORE_VERSION }}

- name: Check Sway formatting
run: forc fmt --path standards --check

- name: Build All Standards
run: forc build --path standards

contributing-book:
runs-on: ubuntu-latest

Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ These libraries contain helper functions, generalized standards, and other tools
- [Unsigned Fixed Point Number](./libs/fixed_point/) is an interface to implement fixed-point numbers.
- [StorageMapVec](./libs/storagemapvec/) is a temporary workaround for a StorageMap<K, StorageVec<V>> type.

### Standards

- [Token Standard](./standards/frc20/) is used to add metadata to native assets on Fuel.
- [Non-Fungible Token(NFT) Standard](./standards/frc721/) is a standard ABI used to implement NFTs on Fuel.

## Using a library

To import the Merkle Proof library the following should be added to the project's `Forc.toml` file under `[dependencies]` with the most recent release:
Expand Down
2 changes: 2 additions & 0 deletions standards/Forc.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[workspace]
members = ["frc20", "frc721"]
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions standards/frc721/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
out
target
7 changes: 7 additions & 0 deletions standards/frc721/Forc.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
entry = "lib.sw"
license = "Apache-2.0"
name = "frc721"

[dependencies]
79 changes: 79 additions & 0 deletions standards/frc721/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset=".docs/frc-721-logo-dark-theme.png">
<img alt="SwayApps logo" width="400px" src=".docs/frc-721-logo-light-theme.png">
</picture>
</p>

## Overview

The FRC-721 standard defines and outlines NFTs on the Fuel Network.

The official specification can be found in the [frc_721.sw](./src/frc_721.sw) file.

To jumpstart the deployment of your own NFTs, the [NFT Library](../../libs/nft/) provides an easy to use outline following the FRC-721 standard and uses structs and traits for it's implementation.

## Functions

### `transfer()`

Transfers ownership of an NFT from one Identity to another.

> **NOTE:** At the time of a transfer, the approved Identity for that NFT (if any) **MUST** be reset to Option::None.
### `approve()`

Sets or reafirms the approved Identity for an NFT.

> **NOTE:** The approved Identity for the specified NFT **MAY** transfer the token to a new owner.
### `set_approval_for_all()`

Enables or disables approval for a third party "Operator" to manage all of `msg_sender()`'s NFTs.

> **NOTE:** An operator for an Identity **MAY** transfer and MAY set approved Identities for all tokens owned by the `msg_sender()`.
### `approved()`

Gets the approved Identity for a single NFT.

> **NOTE:** Option::None indicates there is no approved Identity.
### `balance_of()`

Returns the number of NFTs owned by an Identity.

### `is_approved_for_all()`

Queries if an Identity is an authorized operator for another Identity.

### `owner_of()`

Queries the owner of an NFT.

> **NOTE:** Option::None indicates there is no owner Identity.
## Events

### `ApprovalEvent`

The `ApprovalEvent` event **MUST** be logged when the approved Identity for an NFT is changed or modified.
The approved Identity for the specified NFT **MAY** transfer the token to a new owner.
Option::None indicates there is no approved Identity.

### `OperatorEvent`

The `OperatorEvent` event **MUST** be logged when an operator is enabled or disabled for an owner.
The operator can manage all NFTs of the owner.

### `TransferEvent`

The `TransferEvent` event **MUST** be logged when ownership of any NFT changes between two Identities.

> **NOTE:** Exception: Cases where there is no new or previous owner, more formally known as minting and burning, the event **SHALL NOT** be logged.
## Extensions

### Metadata

The metadata extension specification can be found [here](./src/extensions/frc721_metadata.sw).
21 changes: 21 additions & 0 deletions standards/frc721/src/extensions/frc721_metadata.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
library frc721_metadata;

/**
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”,
“SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be
interpreted as described in RFC 2119: https://www.ietf.org/rfc/rfc2119.txt
*/

/// Any contract that implements FRC721_metadata SHALL also implement FRC721.
abi FRC721_metadata {
/// Get the name of the token
/// Example (with trailing padding): "MY_TOKEN "
fn name() -> str[64];
/// Get the symbol of the token
/// Example (with trailing padding): "TKN "
fn symbol() -> str[32];
/// A distinct Uniform Resource Identifier (URI) for a given asset.
/// URIs are defined in FRC-3986.
/// NOTE: This will be updated with StorageString once https://github.com/FuelLabs/sway-libs/issues/40 is merged.
fn uri(token_id: u64) -> str[2048];
}
123 changes: 123 additions & 0 deletions standards/frc721/src/frc_721.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
library frc721;

/**
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”,
“SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be
interpreted as described in RFC 2119: https://www.ietf.org/rfc/rfc2119.txt
*/

/// This event MUST be logged when the approved Identity for an NFT is changed or modified.
/// Option::None indicates there is no approved Identity.
pub struct ApprovalEvent {
approved: Option<Identity>,
owner: Identity,
token_id: u64,
}
/// This event MUST be logged when an operator is enabled or disabled for an owner.
/// The operator can manage all NFTs of the owner.
pub struct OperatorEvent {
approved: bool,
operator: Identity,
owner: Identity,
}
/// This event MUST be logged when ownership of any NFT changes between two Identities.
/// Exception: Cases where there is no new or previous owner, formally known as minting and burning,
/// the event SHALL NOT be logged.
pub struct TransferEvent {
from: Identity,
sender: Identity,
to: Identity,
token_id: u64,
}

abi FRC721 {
/// Transfer ownership of an NFT from one Identity to another.
/// At the time of a transfer, the approved Identity for that NFT (if any) MUST be reset to Option::None.
///
/// -- THE CALLER IS RESPONSIBLE
/// FOR CONFIRMING THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE
/// THEY MAY BE PERMANENTLY LOST! --
///
/// # Arguments
///
/// * `to` - The Identity which the ownership of this token SHALL be set to.
/// * `token_id` - The token of which ownership SHALL change.
///
/// # Reverts
///
/// * It is REQUIRED that `msg_sender()` is not the owner of this token.
/// * It is REQUIRED that `msg_sender()` is not approved to transfer this token on the owner's behalf.
/// * It is REQUIRED that `msg_sender()` is not approved to transfer all tokens on the owner's behalf.
///
/// # Events
///
/// * The TransferEvent event MUST be emitted when the function is not reverted.
fn transfer(to: Identity, token_id: u64);
/// Set or reafirm the approved Identity for an NFT.
/// The approved Identity for the specified NFT MAY transfer the token to a new owner.
///
/// # Arguments
///
/// * `approved` - The Identity that SHALL be approved as an NFT controller, or Option::None.
/// * `token_id` - The token of which the NFT approval SHALL change.
///
/// # Reverts
///
/// * It is REQUIRED that `msg_sender()` is the owner of (or an approved Operator for) this NFT.
///
/// # Events
///
/// * The ApprovalEvent event MUST be emitted when the function is not reverted.
fn approve(approved: Option<Identity>, token_id: u64);
/// Enable or disable approval for a third party "Operator" to manage all
/// of `msg_sender()`'s NFTs.
/// An operator for an Identity MAY transfer and MAY set approved Identities for all tokens
/// owned by the `msg_sender()`.
///
/// -- The contract MUST allow multiple operators per owner. --
///
/// # Arguments
///
/// * `approve` - MUST be `True` if the operator is approved and MUST be `False` to revoke approval.
/// * `operator` - The Identity that SHALL be added to the set of authorized operators.
///
/// # Events
///
/// * The OperatorEvent event MUST be emitted.
fn set_approval_for_all(approve: bool, operator: Identity);
/// Get the approved Identity for a single NFT.
/// Option::None indicates there is no approved Identity.
///
/// # Arguments
///
/// * `token_id` - The NFT to find the approved Identity for.
///
/// # Reverts
///
/// * It is REQUIRED that `token_id` is valid NFT.
fn approved(token_id: u64) -> Option<Identity>;
/// The number of NFTs owned by an Identity.
///
/// # Arguments
///
/// * `owner` - The Identity of which to query the balance.
fn balance_of(owner: Identity) -> u64;
/// Query if an Identity is an authorized operator for another Identity.
///
/// # Arguments
///
/// * `operator` - The Identity that acts on behalf of the owner.
/// * `owner` - The Identity that owns the NFT/NFTs.
fn is_approved_for_all(operator: Identity, owner: Identity) -> bool;
/// Query the owner of an NFT.
/// Option::None indicates there is no owner Identity.
///
/// # Arguments
///
/// * `token_id` - The NFT to find the owner for.
///
/// # Reverts
///
/// * It is REQUIRED that `token_id` is valid NFT.
fn owner_of(token_id: u64) -> Option<Identity>;
}
4 changes: 4 additions & 0 deletions standards/frc721/src/lib.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
library frc721;

dep frc_721;
dep extensions/frc721_metadata;

0 comments on commit 715a90d

Please sign in to comment.