From ef8e318dafb3e3210041acc20f58e5b32db62dd3 Mon Sep 17 00:00:00 2001 From: bitzoic Date: Thu, 2 Nov 2023 13:33:59 +0300 Subject: [PATCH 1/4] Add single asset SRC-3 example --- examples/src_3/single_asset/Forc.toml | 9 + .../src_3/single_asset/src/single_asset.sw | 156 ++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 examples/src_3/single_asset/Forc.toml create mode 100644 examples/src_3/single_asset/src/single_asset.sw diff --git a/examples/src_3/single_asset/Forc.toml b/examples/src_3/single_asset/Forc.toml new file mode 100644 index 0000000..03765ff --- /dev/null +++ b/examples/src_3/single_asset/Forc.toml @@ -0,0 +1,9 @@ +[project] +authors = ["Fuel Labs "] +entry = "single_asset.sw" +license = "Apache-2.0" +name = "single_src3_asset" + +[dependencies] +src_20 = { path = "../../../standards/src_20" } +src_3 = { path = "../../../standards/src_3" } diff --git a/examples/src_3/single_asset/src/single_asset.sw b/examples/src_3/single_asset/src/single_asset.sw new file mode 100644 index 0000000..4f15a4a --- /dev/null +++ b/examples/src_3/single_asset/src/single_asset.sw @@ -0,0 +1,156 @@ +contract; + +use src_3::SRC3; +use src_20::SRC20; +use std::{ + call_frames::{ + contract_id, + msg_asset_id, + }, + constants::ZERO_B256, + context::msg_amount, + string::String, + token::{ + burn, + mint_to, + }, +}; + +configurable { + /// The decimals of the asset minted by this contract. + DECIMALS: u8 = 9u8, + /// The name of the asset minted by this contract. + NAME: str[7] = __to_str_array("MyToken"), + /// The symbol of the asset minted by this contract. + SYMBOL: str[5] = __to_str_array("MYTKN"), +} + +storage { + /// The total supply of the asset minted by this contract. + total_supply: u64 = 0, +} + +impl SRC3 for Contract { + /// Unconditionally mints new tokens using the default SubId. + /// + /// # Arguments + /// + /// * `recipient`: [Identity] - The user to which the newly minted tokens are transferred to. + /// * `sub_id`: [SubId] - The default SubId. + /// * `amount`: [u64] - The quantity of tokens to mint. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `1` + /// * Writes: `1` + /// + /// # Reverts + /// + /// * When the `sub_id` is not the default SubId. + /// + /// # Examples + /// + /// ```sway + /// use src3::SRC3; + /// + /// fn foo(contract: ContractId) { + /// let contract_abi = abi(SR3, contract); + /// contract_abi.mint(Identity::ContractId(this_contract()), ZERO_B256, 100); + /// } + /// ``` + #[storage(read, write)] + fn mint(recipient: Identity, sub_id: SubId, amount: u64) { + require(sub_id == ZERO_B256, "Incorrect Sub Id"); + + // Increment total supply of the asset and mint to the recipient. + storage.total_supply.write(amount + storage.total_supply.read()); + mint_to(recipient, ZERO_B256, amount); + } + + /// Unconditionally burns tokens sent with the default SubId. + /// + /// # Arguments + /// + /// * `sub_id`: [SubId] - The default SubId. + /// * `amount`: [u64] - The quantity of tokens to burn. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `1` + /// * Writes: `1` + /// + /// # Reverts + /// + /// * When the `sub_id` is not the default SubId. + /// * When the transaction did not include at least `amount` tokens. + /// * When the transaction did not include the asset minted by this contract. + /// + /// # Examples + /// + /// ```sway + /// use src3::SRC3; + /// + /// fn foo(contract: ContractId, asset_id: AssetId) { + /// let contract_abi = abi(SR3, contract); + /// contract_abi { + /// gas: 10000, + /// coins: 100, + /// asset_id: AssetId, + /// }.burn(ZERO_B256, 100); + /// } + /// ``` + #[storage(read, write)] + fn burn(sub_id: SubId, amount: u64) { + require(sub_id == ZERO_B256, "Incorrect Sub Id"); + require(msg_amount() >= amount, "Incorrect amount provided"); + require(msg_asset_id() == AssetId::default(contract_id()), "Incorrect asset provided"); + + // Decrement total supply of the asset and burn. + storage.total_supply.write(storage.total_supply.read() - amount); + burn(ZERO_B256, amount); + } +} + +// SRC3 extends SRC20, so this must be included +impl SRC20 for Contract { + #[storage(read)] + fn total_assets() -> u64 { + 1 + } + + #[storage(read)] + fn total_supply(asset: AssetId) -> Option { + if asset == AssetId::default(contract_id()) { + Some(storage.total_supply.read()) + } else { + None + } + } + + #[storage(read)] + fn name(asset: AssetId) -> Option { + if asset == AssetId::default(contract_id()) { + Some(String::from_ascii_str(from_str_array(NAME))) + } else { + None + } + } + + #[storage(read)] + fn symbol(asset: AssetId) -> Option { + if asset == AssetId::default(contract_id()) { + Some(String::from_ascii_str(from_str_array(SYMBOL))) + } else { + None + } + } + + #[storage(read)] + fn decimals(asset: AssetId) -> Option { + if asset == AssetId::default(contract_id()) { + Some(DECIMALS) + } else { + None + } + } +} From 3a76cc1e5b330906bcc33bc367d54a6c6d43bb43 Mon Sep 17 00:00:00 2001 From: bitzoic Date: Thu, 2 Nov 2023 13:34:16 +0300 Subject: [PATCH 2/4] Add multi-asset SRC-3 example --- examples/src_3/multi_asset/Forc.toml | 9 + examples/src_3/multi_asset/src/multi_asset.sw | 160 ++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 examples/src_3/multi_asset/Forc.toml create mode 100644 examples/src_3/multi_asset/src/multi_asset.sw diff --git a/examples/src_3/multi_asset/Forc.toml b/examples/src_3/multi_asset/Forc.toml new file mode 100644 index 0000000..a8033eb --- /dev/null +++ b/examples/src_3/multi_asset/Forc.toml @@ -0,0 +1,9 @@ +[project] +authors = ["Fuel Labs "] +entry = "multi_asset.sw" +license = "Apache-2.0" +name = "multi_src3_asset" + +[dependencies] +src_20 = { path = "../../../standards/src_20" } +src_3 = { path = "../../../standards/src_3" } diff --git a/examples/src_3/multi_asset/src/multi_asset.sw b/examples/src_3/multi_asset/src/multi_asset.sw new file mode 100644 index 0000000..9a02ecf --- /dev/null +++ b/examples/src_3/multi_asset/src/multi_asset.sw @@ -0,0 +1,160 @@ +contract; + +use src_3::SRC3; +use src_20::SRC20; +use std::{ + call_frames::{ + contract_id, + msg_asset_id, + }, + context::msg_amount, + hash::Hash, + storage::storage_string::*, + string::String, + token::{ + burn, + mint_to, + }, +}; + +// In this example, all assets minted from this contract have the same decimals, name, and symbol +configurable { + /// The decimals of every asset minted by this contract. + DECIMALS: u8 = 9u8, + /// The name of every asset minted by this contract. + NAME: str[12] = __to_str_array("ExampleAsset"), + /// The symbol of every asset minted by this contract. + SYMBOL: str[2] = __to_str_array("EA"), +} + +storage { + /// The total number of distinguishable assets this contract has minted. + total_assets: u64 = 0, + /// The total supply of a particular asset. + total_supply: StorageMap = StorageMap {}, +} + +impl SRC3 for Contract { + /// Unconditionally mints new tokens using the `sub_id` sub-identifier. + /// + /// # Arguments + /// + /// * `recipient`: [Identity] - The user to which the newly minted tokens are transferred to. + /// * `sub_id`: [SubId] - The sub-identifier of the newly minted token. + /// * `amount`: [u64] - The quantity of tokens to mint. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `2` + /// * Writes: `2` + /// + /// # Examples + /// + /// ```sway + /// use src3::SRC3; + /// + /// fn foo(contract: ContractId) { + /// let contract_abi = abi(SR3, contract); + /// contract_abi.mint(Identity::ContractId(this_contract()), ZERO_B256, 100); + /// } + /// ``` + #[storage(read, write)] + fn mint(recipient: Identity, sub_id: SubId, amount: u64) { + let asset_id = AssetId::new(contract_id(), sub_id); + + // If this SubId is new, increment the total number of distinguishable assets this contract has minted. + let asset_supply = storage.total_supply.get(asset_id).try_read(); + match asset_supply { + None => { + storage.total_assets.write(storage.total_assets.read() + 1) + }, + _ => {}, + } + + // Increment total supply of the asset and mint to the recipient. + storage.total_supply.insert(asset_id, amount + asset_supply.unwrap_or(0)); + mint_to(recipient, sub_id, amount); + } + + /// Unconditionally burns tokens sent with the `sub_id` sub-identifier. + /// + /// # Arguments + /// + /// * `sub_id`: [SubId] - The sub-identifier of the token to burn. + /// * `amount`: [u64] - The quantity of tokens to burn. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `1` + /// * Writes: `1` + /// + /// # Reverts + /// + /// * When the transaction did not include at least `amount` tokens. + /// * When the asset included in the transaction does not have the SubId `sub_id`. + /// + /// # Examples + /// + /// ```sway + /// use src3::SRC3; + /// + /// fn foo(contract: ContractId, asset_id: AssetId) { + /// let contract_abi = abi(SR3, contract); + /// contract_abi { + /// gas: 10000, + /// coins: 100, + /// asset_id: AssetId, + /// }.burn(ZERO_B256, 100); + /// } + /// ``` + #[storage(read, write)] + fn burn(sub_id: SubId, amount: u64) { + let asset_id = AssetId::new(contract_id(), sub_id); + require(msg_amount() == amount, "Incorrect amount provided"); + require(msg_asset_id() == asset_id, "Incorrect asset provided"); + + // Decrement total supply of the asset and burn. + storage.total_supply.insert(asset_id, storage.total_supply.get(asset_id).read() - amount); + burn(sub_id, amount); + } +} + +// SRC3 extends SRC20, so this must be included +impl SRC20 for Contract { + #[storage(read)] + fn total_assets() -> u64 { + storage.total_assets.read() + } + + #[storage(read)] + fn total_supply(asset: AssetId) -> Option { + storage.total_supply.get(asset).try_read() + } + + #[storage(read)] + fn name(asset: AssetId) -> Option { + if asset == AssetId::default(contract_id()) { + Some(String::from_ascii_str(from_str_array(NAME))) + } else { + None + } + } + + #[storage(read)] + fn symbol(asset: AssetId) -> Option { + if asset == AssetId::default(contract_id()) { + Some(String::from_ascii_str(from_str_array(SYMBOL))) + } else { + None + } + } + + #[storage(read)] + fn decimals(asset: AssetId) -> Option { + if asset == AssetId::default(contract_id()) { + Some(DECIMALS) + } else { + None + } + } +} From f1c99af90c2550d1df4437d5bba806361e78fa81 Mon Sep 17 00:00:00 2001 From: bitzoic Date: Fri, 3 Nov 2023 10:47:43 +0300 Subject: [PATCH 3/4] Update examples based on PR comments --- examples/src_3/multi_asset/src/multi_asset.sw | 14 ++++++++------ examples/src_3/single_asset/src/single_asset.sw | 12 +++++++----- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/examples/src_3/multi_asset/src/multi_asset.sw b/examples/src_3/multi_asset/src/multi_asset.sw index 9a02ecf..26e6862 100644 --- a/examples/src_3/multi_asset/src/multi_asset.sw +++ b/examples/src_3/multi_asset/src/multi_asset.sw @@ -52,10 +52,11 @@ impl SRC3 for Contract { /// /// ```sway /// use src3::SRC3; + /// use std::constants::ZERO_B256; /// - /// fn foo(contract: ContractId) { - /// let contract_abi = abi(SR3, contract); - /// contract_abi.mint(Identity::ContractId(this_contract()), ZERO_B256, 100); + /// fn foo(contract_id: ContractId) { + /// let contract_abi = abi(SR3, contract_id); + /// contract_abi.mint(Identity::ContractId(contract_id), ZERO_B256, 100); /// } /// ``` #[storage(read, write)] @@ -97,13 +98,14 @@ impl SRC3 for Contract { /// /// ```sway /// use src3::SRC3; + /// use std::constants::ZERO_B256; /// - /// fn foo(contract: ContractId, asset_id: AssetId) { - /// let contract_abi = abi(SR3, contract); + /// fn foo(contract_id: ContractId, asset_id: AssetId) { + /// let contract_abi = abi(SR3, contract_id); /// contract_abi { /// gas: 10000, /// coins: 100, - /// asset_id: AssetId, + /// asset_id: asset_id, /// }.burn(ZERO_B256, 100); /// } /// ``` diff --git a/examples/src_3/single_asset/src/single_asset.sw b/examples/src_3/single_asset/src/single_asset.sw index 4f15a4a..1797df9 100644 --- a/examples/src_3/single_asset/src/single_asset.sw +++ b/examples/src_3/single_asset/src/single_asset.sw @@ -52,10 +52,11 @@ impl SRC3 for Contract { /// /// ```sway /// use src3::SRC3; + /// use std::constants::ZERO_B256; /// - /// fn foo(contract: ContractId) { + /// fn foo(contract_id: ContractId) { /// let contract_abi = abi(SR3, contract); - /// contract_abi.mint(Identity::ContractId(this_contract()), ZERO_B256, 100); + /// contract_abi.mint(Identity::ContractId(contract_id), ZERO_B256, 100); /// } /// ``` #[storage(read, write)] @@ -89,13 +90,14 @@ impl SRC3 for Contract { /// /// ```sway /// use src3::SRC3; + /// use std::constants::ZERO_B256; /// - /// fn foo(contract: ContractId, asset_id: AssetId) { - /// let contract_abi = abi(SR3, contract); + /// fn foo(contract_id: ContractId, asset_id: AssetId) { + /// let contract_abi = abi(SR3, contract_id); /// contract_abi { /// gas: 10000, /// coins: 100, - /// asset_id: AssetId, + /// asset_id: asset_id, /// }.burn(ZERO_B256, 100); /// } /// ``` From 765a55452d8788233679aec96dec20c226304cd2 Mon Sep 17 00:00:00 2001 From: bitzoic Date: Fri, 3 Nov 2023 10:48:28 +0300 Subject: [PATCH 4/4] Update SRC3 inline docs from errors found in examples --- standards/src_3/src/src_3.sw | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/standards/src_3/src/src_3.sw b/standards/src_3/src/src_3.sw index 987be34..d832597 100644 --- a/standards/src_3/src/src_3.sw +++ b/standards/src_3/src/src_3.sw @@ -14,9 +14,9 @@ abi SRC3 { /// ```sway /// use src3::SRC3; /// - /// fn foo(contract: ContractId) { + /// fn foo(contract_id: ContractId) { /// let contract_abi = abi(SR3, contract); - /// contract_abi.mint(Identity::ContractId(this_contract()), ZERO_B256, 100); + /// contract_abi.mint(Identity::ContractId(contract_id), ZERO_B256, 100); /// } /// ``` #[storage(read, write)] @@ -39,12 +39,12 @@ abi SRC3 { /// ```sway /// use src3::SRC3; /// - /// fn foo(contract: ContractId, asset_id: AssetId) { - /// let contract_abi = abi(SR3, contract); + /// fn foo(contract_id: ContractId, asset_id: AssetId) { + /// let contract_abi = abi(SR3, contract_id); /// contract_abi { /// gas: 10000, /// coins: 100, - /// asset_id: AssetId, + /// asset_id: asset_id, /// }.burn(ZERO_B256, 100); /// } /// ```