Skip to content

Commit

Permalink
Create the asset_id.sw file and move AssetId out of `contract_id.…
Browse files Browse the repository at this point in the history
…sw` (FuelLabs#5439)

## Description

This PR removes the `AssetId` type from the `contract_id.sw` file and
into the new `asset_id.sw` file for better composability and navigation
of the standard library.

This PR causes breaking changes as the `transfer()` and `mint_to()`
functions from `Address`, `ContractId`, and `Identity` have been removed
due to circular dependencies. This was originally added in
[this](FuelLabs#4693) PR to give developers
a familiarity from solidity. It has been heavily debated whether this
should be kept in the standard library as it adds complexity and
duplicates the existing `transfer()` and `mint_to()` functions. For the
sake of simplicity and ensuring likeness between all asset management
functions, especially in audits, this has been removed.


## Checklist

- [x] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [ ] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [x] I have added tests that prove my fix is effective or that my
feature works.
- [x] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.
  • Loading branch information
bitzoic authored Jan 9, 2024
1 parent a164fe1 commit f6d8710
Show file tree
Hide file tree
Showing 17 changed files with 252 additions and 854 deletions.
85 changes: 1 addition & 84 deletions sway-lib-std/src/address.sw
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
//! A wrapper around the `b256` type to help enhance type-safety.
library;

use ::alias::SubId;
use ::call_frames::contract_id;
use ::contract_id::AssetId;
use ::convert::From;
use ::hash::*;
use ::error_signals::FAILED_TRANSFER_TO_ADDRESS_SIGNAL;
use ::hash::sha256;
use ::revert::revert;
use ::outputs::{Output, output_amount, output_count, output_type};
use ::hash::{Hash, Hasher};

/// The `Address` type, a struct wrapper around the inner `b256` value.
pub struct Address {
Expand Down Expand Up @@ -70,82 +63,6 @@ impl From<b256> for Address {
}
}

impl Address {
/// Transfer `amount` coins of type `asset_id` and send them to
/// the Address.
///
/// # Arguments
///
/// * `asset_id`: [AssetId] - The `AssetId` of the token to transfer.
/// * `amount`: [u64] - The amount of tokens to transfer.
///
/// # Reverts
///
/// * When `amount` is greater than the contract balance for `asset_id`.
/// * When `amount` is equal to zero.
/// * When there are no free variable outputs.
///
/// # Examples
///
/// ```sway
/// use std::constants::{BASE_ASSET_ID, ZERO_B256};
///
/// fn foo() {
/// let address = Address::from(ZERO_B256);
/// address.transfer(BASE_ASSET_ID, 500);
/// }
/// ```
pub fn transfer(self, asset_id: AssetId, amount: u64) {
// maintain a manual index as we only have `while` loops in sway atm:
let mut index = 0;

// If an output of type `OutputVariable` is found, check if its `amount` is
// zero. As one cannot transfer zero coins to an output without a panic, a
// variable output with a value of zero is by definition unused.
let number_of_outputs = output_count();
while index < number_of_outputs {
if let Output::Variable = output_type(index) {
if output_amount(index) == 0 {
asm(r1: self.value, r2: index, r3: amount, r4: asset_id.value) {
tro r1 r2 r3 r4;
};
return;
}
}
index += 1;
}

revert(FAILED_TRANSFER_TO_ADDRESS_SIGNAL);
}
}

impl Address {
/// Mint `amount` coins of the current contract's `asset_id` and send them to
/// the Address.
///
/// # Arguments
///
/// * `sub_id`: [SubId] - The sub id of the token to mint.
/// * `amount`: [u64] - The amount of tokens to mint.
///
/// # Examples
///
/// ```sway
/// use std::constants::ZERO_B256;
///
/// fn foo() {
/// let address = Address::from(ZERO_B256);
/// address.mint_to(ZERO_B256, 500);
/// }
/// ```
pub fn mint_to(self, sub_id: SubId, amount: u64) {
asm(r1: amount, r2: sub_id) {
mint r1 r2;
};
self.transfer(AssetId::new(contract_id(), sub_id), amount);
}
}

impl Hash for Address {
fn hash(self, ref mut state: Hasher) {
let Address { value } = self;
Expand Down
216 changes: 216 additions & 0 deletions sway-lib-std/src/asset_id.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
library;

use ::alias::SubId;
use ::contract_id::ContractId;
use ::convert::From;
use ::hash::{Hash, Hasher};

/// An AssetId is used for interacting with an asset on the network.
///
/// # Additional Information
///
/// It is calculated by taking the sha256 hash of the originating ContractId and a SubId.
/// i.e. sha256((contract_id, sub_id)).
///
/// An exception is the Base Asset, which is just the ZERO_B256 AssetId.
///
/// The SubId is used to differentiate between different assets that are created by the same contract.
pub struct AssetId {
value: b256,
}

impl Hash for AssetId {
fn hash(self, ref mut state: Hasher) {
let Self { value } = self;
value.hash(state);
}
}

impl core::ops::Eq for AssetId {
fn eq(self, other: Self) -> bool {
self.value == other.value
}
}

impl From<b256> for AssetId {
/// Casts raw `b256` data to an `AssetId`.
///
/// # Arguments
///
/// * `bits`: [b256] - The raw `b256` data to be casted.
///
/// # Returns
///
/// * [AssetId] - The newly created `AssetId` from the raw `b256`.
///
/// # Examples
///
/// ```sway
/// use std::constants::ZERO_B256;
///
/// fn foo() {
/// let asset_id = AssetId::from(ZERO_B256);
/// }
/// ```
fn from(bits: b256) -> Self {
Self { value: bits }
}

/// Casts an `AssetId` to raw `b256` data.
///
/// # Returns
///
/// * [b256] - The underlying raw `b256` data of the `AssetId`.
///
/// # Examples
///
/// ```sway
/// use std::constants::ZERO_B256;
///
/// fn foo() {
/// let asset_id = AssetId::from(ZERO_B256);
/// let b256_data = asset_id.into();
/// assert(b256_data == ZERO_B256);
/// }
/// ```
fn into(self) -> b256 {
self.value
}
}

impl AssetId {
/// Creates a new AssetId from a ContractId and SubId.
///
/// # Arguments
///
/// * `contract_id`: [ContractId] - The ContractId of the contract that created the asset.
/// * `sub_id`: [SubId] - The SubId of the asset.
///
/// # Returns
///
/// * [AssetId] - The AssetId of the asset. Computed by hashing the ContractId and SubId.
///
/// # Examples
///
/// ```sway
/// use std::{callframes::contract_id, constants::ZERO_B256};
///
/// fn foo() {
/// let contract_id = contract_id();
/// let sub_id = ZERO_B256;
///
/// let asset_id = AssetId::new(contract_id, sub_id);
/// }
/// ```
pub fn new(contract_id: ContractId, sub_id: SubId) -> Self {
let result_buffer = 0x0000000000000000000000000000000000000000000000000000000000000000;
asm(
asset_id: result_buffer,
ptr: (contract_id, sub_id),
bytes: 64,
) {
s256 asset_id ptr bytes;
};

Self {
value: result_buffer,
}
}

/// Creates a new AssetId with the default SubId for the current contract.
///
/// # Returns
///
/// * [AssetId] - The AssetId of the asset. Computed by hashing the ContractId and the default SubId.
///
/// # Examples
///
/// ```sway
/// use std::{callframes::contract_id, constants::DEFAULT_SUB_ID};
///
/// fn foo() {
/// let asset_id = AssetId::default();
/// assert(asset_id == AssetId::new(contract_id(), DEFAULT_SUB_ID));
/// }
/// ```
pub fn default() -> Self {
let contract_id = asm() {
fp: b256
};
let result_buffer = 0x0000000000000000000000000000000000000000000000000000000000000000;
asm(
asset_id: result_buffer,
ptr: (
contract_id,
0x0000000000000000000000000000000000000000000000000000000000000000,
),
bytes: 64,
) {
s256 asset_id ptr bytes;
};

Self {
value: result_buffer,
}
}

/// The base_asset_id represents the base asset of a chain.
///
/// # Additional Information
///
/// On the Fuel network, the base asset is Ether. It is hardcoded as the 0x00..00 AssetId.
///
/// # Returns
///
/// * [AssetId] - The AssetId of the base asset.
///
/// # Examples
///
/// ```sway
/// use std::{constants::ZERO_B256, token::transfer};
///
/// fn foo() {
/// let asset_id = AssetId::base_asset_id();
/// let amount = 100;
/// let recipient = Identity::ContractId(ContractId::from(ZERO_B256));
///
/// transfer(recipient, asset_id, amount);
/// ```
pub fn base_asset_id() -> Self {
Self {
value: 0x0000000000000000000000000000000000000000000000000000000000000000,
}
}
}

#[test()]
fn test_hasher_sha256_asset_id() {
use ::assert::assert;
let mut hasher = Hasher::new();
AssetId::from(0x0000000000000000000000000000000000000000000000000000000000000000)
.hash(hasher);
let s256 = hasher.sha256();
assert(s256 == 0x66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925);

let mut hasher = Hasher::new();
AssetId::from(0x0000000000000000000000000000000000000000000000000000000000000001)
.hash(hasher);
let s256 = hasher.sha256();
assert(s256 == 0xec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5);
}

#[test()]
fn test_hasher_sha256_contract_id() {
use ::assert::assert;
let mut hasher = Hasher::new();
ContractId::from(0x0000000000000000000000000000000000000000000000000000000000000000)
.hash(hasher);
let s256 = hasher.sha256();
assert(s256 == 0x66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925);

let mut hasher = Hasher::new();
ContractId::from(0x0000000000000000000000000000000000000000000000000000000000000001)
.hash(hasher);
let s256 = hasher.sha256();
assert(s256 == 0xec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5);
}
3 changes: 2 additions & 1 deletion sway-lib-std/src/call_frames.sw
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
//! [Call frames](https://fuellabs.github.io/fuel-specs/master/vm#call-frames) store metadata across untrusted inter-contract calls.
library;

use ::contract_id::{AssetId, ContractId};
use ::asset_id::AssetId;
use ::contract_id::ContractId;
use ::intrinsics::is_reference_type;
use ::registers::frame_ptr;

Expand Down
2 changes: 1 addition & 1 deletion sway-lib-std/src/constants.sw
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Base asset and zero address constants.
library;

use ::contract_id::AssetId;
use ::asset_id::AssetId;

/// The `BASE_ASSET_ID` represents the base asset of a chain.
///
Expand Down
3 changes: 2 additions & 1 deletion sway-lib-std/src/context.sw
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
//! Functionality for accessing context-specific information about the current contract or message.
library;

use ::asset_id::AssetId;
use ::call_frames::contract_id;
use ::contract_id::{AssetId, ContractId};
use ::contract_id::ContractId;
use ::registers::balance;

/// Get the balance of coin `asset_id` for the current contract.
Expand Down
Loading

0 comments on commit f6d8710

Please sign in to comment.