Hi @danielbate
here is the contract code
contract;
mod errors;
mod interface;
use errors::{MintError, SetError};
use interface::Constructor;
use standards::{src20::SRC20, src3::SRC3, src5::{SRC5, State}, src7::{Metadata, SRC7},};
use sway_libs::{
asset::{
base::{
_name,
_set_name,
_set_symbol,
_symbol,
_total_assets,
_total_supply,
SetAssetAttributes,
},
metadata::*,
supply::{
_burn,
_mint,
},
},
ownership::{
_owner,
initialize_ownership,
only_owner,
},
pausable::{
_is_paused,
_pause,
_unpause,
Pausable,
require_not_paused,
},
};
use std::{hash::Hash, storage::storage_string::*, string::String};
storage {
/// The total number of unique assets minted by this contract.
///
/// # Additional Information
///
/// This is the number of NFTs that have been minted.
total_assets: u64 = 0,
/// The total number of coins minted for a particular asset.
///
/// # Additional Information
///
/// This should always be 1 for any asset as this is an NFT contract.
total_supply: StorageMap<AssetId, u64> = StorageMap {},
/// The name associated with a particular asset.
name: StorageMap<AssetId, StorageString> = StorageMap {},
/// The symbol associated with a particular asset.
symbol: StorageMap<AssetId, StorageString> = StorageMap {},
/// The metadata associated with a particular asset.
///
/// # Additional Information
///
/// In this NFT contract, there is no metadata provided at compile time. All metadata
/// is added by users and stored into storage.
metadata: StorageMetadata = StorageMetadata {},
}
configurable {
/// The maximum number of NFTs that may be minted.
MAX_SUPPLY: u64 = 3,
}
impl SRC20 for Contract {
/// Returns the total number of individual NFTs for this contract.
///
/// # Returns
///
/// * [u64] - The number of assets that this contract has minted.
///
/// # Number of Storage Accesses
///
/// * Reads: `1`
///
/// # Examples
///
/// ```sway
/// use src20::SRC20;
///
/// fn foo(contract_id: ContractId) {
/// let contract_abi = abi(SRC20, contract_id);
/// let total_assets = contract_abi.total_assets();
/// assert(total_assets != 0);
/// }
/// ```
#[storage(read)]
fn total_assets() -> u64 {
_total_assets(storage.total_assets)
}
/// Returns the total supply of coins for an asset.
///
/// # Additional Information
///
/// This must always be at most 1 for NFTs.
///
/// # Arguments
///
/// * `asset`: [AssetId] - The asset of which to query the total supply.
///
/// # Returns
///
/// * [Option<u64>] - The total supply of coins for `asset`.
///
/// # Number of Storage Accesses
///
/// * Reads: `1`
///
/// # Examples
///
/// ```sway
/// use src20::SRC20;
///
/// fn foo(contract_id: ContractId, asset: AssetId) {
/// let contract_abi = abi(SRC20, contract_id);
/// let total_supply = contract_abi.total_supply(asset).unwrap();
/// assert(total_supply == 1);
/// }
/// ```
#[storage(read)]
fn total_supply(asset: AssetId) -> Option<u64> {
_total_supply(storage.total_supply, asset)
}
/// Returns the name of the asset, such as “Ether”.
///
/// # Arguments
///
/// * `asset`: [AssetId] - The asset of which to query the name.
///
/// # Returns
///
/// * [Option<String>] - The name of `asset`.
///
/// # Number of Storage Accesses
///
/// * Reads: `1`
///
/// # Examples
///
/// ```sway
/// use src20::SRC20;
/// use std::string::String;
///
/// fn foo(contract_ic: ContractId, asset: AssetId) {
/// let contract_abi = abi(SRC20, contract_id);
/// let name = contract_abi.name(asset).unwrap();
/// assert(name.len() != 0);
/// }
/// ```
#[storage(read)]
fn name(asset: AssetId) -> Option<String> {
_name(storage.name, asset)
}
/// Returns the symbol of the asset, such as “ETH”.
///
/// # Arguments
///
/// * `asset`: [AssetId] - The asset of which to query the symbol.
///
/// # Returns
///
/// * [Option<String>] - The symbol of `asset`.
///
/// # Number of Storage Accesses
///
/// * Reads: `1`
///
/// # Examples
///
/// ```sway
/// use src20::SRC20;
/// use std::string::String;
///
/// fn foo(contract_id: ContractId, asset: AssetId) {
/// let contract_abi = abi(SRC20, contract_id);
/// let symbol = contract_abi.symbol(asset).unwrap();
/// assert(symbol.len() != 0);
/// }
/// ```
#[storage(read)]
fn symbol(asset: AssetId) -> Option<String> {
_symbol(storage.symbol, asset)
}
/// Returns the number of decimals the asset uses.
///
/// # Additional Information
///
/// The standardized decimals for NFTs is 0u8.
///
/// # Arguments
///
/// * `asset`: [AssetId] - The asset of which to query the decimals.
///
/// # Returns
///
/// * [Option<u8>] - The decimal precision used by `asset`.
///
/// # Examples
///
/// ```sway
/// use src20::SRC20;
///
/// fn foo(contract_id: ContractId, asset: AssedId) {
/// let contract_abi = abi(SRC20, contract_id);
/// let decimals = contract_abi.decimals(asset).unwrap();
/// assert(decimals == 0u8);
/// }
/// ```
#[storage(read)]
fn decimals(_asset: AssetId) -> Option<u8> {
Some(0u8)
}
}
impl SRC3 for Contract {
/// Mints new assets using the `sub_id` sub-identifier.
///
/// # Additional Information
///
/// This conforms to the SRC-20 NFT portion of the standard for a maximum
/// mint amount of 1 coin per asset.
///
/// # Arguments
///
/// * `recipient`: [Identity] - The user to which the newly minted assets are transferred to.
/// * `sub_id`: [SubId] - The sub-identifier of the newly minted asset.
/// * `amount`: [u64] - The quantity of coins to mint.
///
/// # Reverts
///
/// * When the contract is paused.
/// * When amount is greater than one.
/// * When the asset has already been minted.
/// * When more than the MAX_SUPPLY NFTs have been minted.
///
/// # Number of Storage Accesses
///
/// * Reads: `3`
/// * Writes: `2`
///
/// # Examples
///
/// ```sway
/// use src3::SRC3;
///
/// fn foo(contract_id: ContractId) {
/// let contract_abi = abi(SR3, contract_id);
/// contract_abi.mint(Identity::ContractId(ContractId::this()), ZERO_B256, 1);
/// }
/// ```
#[storage(read, write)]
fn mint(recipient: Identity, sub_id: SubId, amount: u64) {
require_not_paused();
// Checks to ensure this is a valid mint.
let asset = AssetId::new(ContractId::this(), sub_id);
require(
storage
.total_supply
.get(asset)
.try_read()
.is_none(),
MintError::NFTAlreadyMinted,
);
require(
storage
.total_assets
.try_read()
.unwrap_or(0) + amount <= MAX_SUPPLY,
MintError::MaxNFTsMinted,
);
// Mint the NFT
let _ = _mint(
storage
.total_assets,
storage
.total_supply,
recipient,
sub_id,
amount,
);
}
/// Burns assets sent with the given `sub_id`.
///
/// # Additional Information
///
/// NOTE: The sha-256 hash of `(ContractId, SubId)` must match the `AssetId` where `ContractId` is the id of
/// the implementing contract and `SubId` is the given `sub_id` argument.
///
/// # Arguments
///
/// * `sub_id`: [SubId] - The sub-identifier of the asset to burn.
/// * `amount`: [u64] - The quantity of coins to burn.
///
/// # Reverts
///
/// * When the contract is paused.
///
/// # Number of Storage Accesses
///
/// * Reads: `1`
/// * Writes: `1`
///
/// # Examples
///
/// ```sway
/// use src3::SRC3;
///
/// fn foo(contract_id: ContractId, asset_id: AssetId) {
/// let contract_abi = abi(SR3, contract_id);
/// contract_abi.burn {
/// gas: 10000,
/// coins: 1,
/// asset_id: AssetId,
/// } (ZERO_B256, 1);
/// }
/// ```
#[payable]
#[storage(read, write)]
fn burn(sub_id: SubId, amount: u64) {
require_not_paused();
_burn(storage.total_supply, sub_id, amount);
}
}
impl SRC7 for Contract {
/// Returns metadata for the corresponding `asset` and `key`.
///
/// # Arguments
///
/// * `asset`: [AssetId] - The asset of which to query the metadata.
/// * `key`: [String] - The key to the specific metadata.
///
/// # Returns
///
/// * [Option<Metadata>] - `Some` metadata that corresponds to the `key` or `None`.
///
/// # Number of Storage Accesses
///
/// * Reads: `1`
///
/// # Examples
///
/// ```sway
/// use src_7::{SRC7, Metadata};
/// use std::string::String;
///
/// fn foo(contract_id: ContractId, asset: AssetId) {
/// let contract_abi = abi(SRC7, contract_id);
/// let key = String::from_ascii_str("image");
/// let data = contract_abi.metadata(asset, key);
/// assert(data.is_some());
/// }
/// ```
#[storage(read)]
fn metadata(asset: AssetId, key: String) -> Option<Metadata> {
storage.metadata.get(asset, key)
}
}
impl SRC5 for Contract {
/// Returns the owner.
///
/// # Return Values
///
/// * [State] - Represents the state of ownership for this contract.
///
/// # Number of Storage Accesses
///
/// * Reads: `1`
///
/// # Examples
///
/// ```sway
/// use standards::src5::SRC5;
///
/// fn foo(contract_id: ContractId) {
/// let ownership_abi = abi(contract_id, SRC_5);
///
/// match ownership_abi.owner() {
/// State::Uninitalized => log("The ownership is uninitalized"),
/// State::Initialized(owner) => log("The ownership is initalized"),
/// State::Revoked => log("The ownership is revoked"),
/// }
/// }
/// ```
#[storage(read)]
fn owner() -> State {
_owner()
}
}
impl SetAssetAttributes for Contract {
/// Sets the name of an asset.
///
/// # Arguments
///
/// * `asset`: [AssetId] - The asset of which to set the name.
/// * `name`: [String] - The name of the asset.
///
/// # Reverts
///
/// * When the caller is not the owner of the contract.
/// * When the name has already been set for an asset.
///
/// # Number of Storage Accesses
///
/// * Reads: `1`
/// * Writes: `2`
///
/// # Examples
///
/// ```sway
/// use asset::SetAssetAttributes;
/// use src20::SRC20;
/// use std::string::String;
///
/// fn foo(asset: AssetId, contract_id: ContractId) {
/// let set_abi = abi(SetAssetAttributes, contract_id);
/// let src_20_abi = abi(SRC20, contract_id);
/// let name = String::from_ascii_str("Ether");
/// set_abi.set_name(asset, name);
/// assert(src_20_abi.name(asset) == name);
/// }
/// ```
#[storage(write)]
fn set_name(asset: AssetId, name: String) {
only_owner();
require(
storage
.name
.get(asset)
.read_slice()
.is_none(),
SetError::ValueAlreadySet,
);
_set_name(storage.name, asset, name);
}
/// Sets the symbol of an asset.
///
/// # Arguments
///
/// * `asset`: [AssetId] - The asset of which to set the symbol.
/// * `symbol`: [String] - The symbol of the asset.
///
/// # Reverts
///
/// * When the caller is not the owner of the contract.
/// * When the symbol has already been set for an asset.
///
/// # Number of Storage Accesses
///
/// * Reads: `1`
/// * Writes: `2`
///
/// # Examples
///
/// ```sway
/// use asset::SetAssetAttributes;
/// use src20::SRC20;
/// use std::string::String;
///
/// fn foo(asset: AssetId, contract_id: ContractId) {
/// let set_abi = abi(SetAssetAttributes, contract_id);
/// let src_20_abi = abi(SRC20, contract_id);
/// let symbol = String::from_ascii_str("ETH");
/// set_abi.set_symbol(asset, symbol);
/// assert(src_20_abi.symbol(asset) == symbol);
/// }
/// ```
#[storage(write)]
fn set_symbol(asset: AssetId, symbol: String) {
only_owner();
require(
storage
.symbol
.get(asset)
.read_slice()
.is_none(),
SetError::ValueAlreadySet,
);
_set_symbol(storage.symbol, asset, symbol);
}
/// This function should never be called.
///
/// # Additional Information
///
/// NFT decimals are always `0u8` and thus must not be set.
/// This function is an artifact of the SetAssetAttributes ABI definition,
/// but does not have a use in this contract as the decimal value is hardcoded.
///
/// # Reverts
///
/// * When the function is called.
#[storage(write)]
fn set_decimals(_asset: AssetId, _decimals: u8) {
require(false, SetError::ValueAlreadySet);
}
}
impl SetAssetMetadata for Contract {
/// Stores metadata for a specific asset and key pair.
///
/// # Arguments
///
/// * `asset`: [AssetId] - The asset for the metadata to be stored.
/// * `key`: [String] - The key for the metadata to be stored.
/// * `metadata`: [Metadata] - The metadata to be stored.
///
/// # Reverts
///
/// * When the metadata has already been set for an asset.
///
/// # Number of Storage Accesses
///
/// * Reads: `1`
/// * Writes: `2`
///
/// # Example
///
/// ```sway
/// use asset::metdata::SetAssetMetadata;
/// use src_7::{SRC7, Metadata};
///
/// fn foo(asset: AssetId, key: String, contract_id: ContractId, metadata: Metadata) {
/// let set_abi = abi(SetAssetMetadata, contract_id);
/// let src_7_abi = abi(SRC7, contract);
/// set_abi.set_metadata(storage.metadata, asset, key, metadata);
/// assert(src_7_abi.metadata(asset, key) == metadata);
/// }
/// ```
#[storage(read, write)]
fn set_metadata(asset: AssetId, key: String, metadata: Metadata) {
require(
storage
.metadata
.get(asset, key)
.is_none(),
SetError::ValueAlreadySet,
);
_set_metadata(storage.metadata, asset, key, metadata);
}
}
impl Pausable for Contract {
/// Pauses the contract.
///
/// # Reverts
///
/// * When the caller is not the contract owner.
///
/// # Number of Storage Accesses
///
/// * Writes: `1`
///
/// # Examples
///
/// ```sway
/// use sway_libs::pausable::Pausable;
///
/// fn foo(contract_id: ContractId) {
/// let pausable_abi = abi(Pausable, contract_id);
/// pausable_abi.pause();
/// assert(pausable_abi.is_paused());
/// }
/// ```
#[storage(write)]
fn pause() {
only_owner();
_pause();
}
/// Returns whether the contract is paused.
///
/// # Returns
///
/// * [bool] - The pause state for the contract.
///
/// # Number of Storage Accesses
///
/// * Reads: `1`
///
/// # Examples
///
/// ```sway
/// use sway_libs::pausable::Pausable;
///
/// fn foo(contract_id: ContractId) {
/// let pausable_abi = abi(Pausable, contract_id);
/// assert(!pausable_abi.is_paused());
/// }
/// ```
#[storage(read)]
fn is_paused() -> bool {
_is_paused()
}
/// Unpauses the contract.
///
/// # Reverts
///
/// * When the caller is not the contract owner.
///
/// # Number of Storage Accesses
///
/// * Writes: `1`
///
/// # Examples
///
/// ```sway
/// use sway_libs::pausable::Pausable;
///
/// fn foo(contract_id: ContractId) {
/// let pausable_abi = abi(Pausable, contract_id);
/// pausable_abi.unpause();
/// assert(!pausable_abi.is_paused());
/// }
/// ```
#[storage(write)]
fn unpause() {
only_owner();
_unpause();
}
}
impl Constructor for Contract {
/// Sets the defaults for the contract.
///
/// # Arguments
///
/// * `owner`: [Identity] - The `Identity` that will be the first owner.
///
/// # Reverts
///
/// * When ownership has been set before.
///
/// # Number of Storage Acesses
///
/// * Reads: `1`
/// * Write: `1`
///
/// # Examples
///
/// ```sway
/// use standards::src5::SRC5;
/// use nft::Constructor;
///
/// fn foo(contract: ContractId, owner: Identity) {
/// let src_5_abi = abi(SRC5, contract.bits());
/// assert(src_5_abi.owner() == State::Uninitialized);
///
/// let constructor_abi = abi(Constructor, contract.bits());
/// constructor_abi.constructor(owner);
/// assert(src_5_abi.owner() == State::Initialized(owner));
/// }
/// ```
#[storage(read, write)]
fn constructor(owner: Identity) {
initialize_ownership(owner);
}
}