Skip to content

Commit

Permalink
Account types token methods (#4693)
Browse files Browse the repository at this point in the history
## Description
Methods for all account types to easily transfer and mint tokens to
accounts.

## Checklist

- [ ] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [x] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [ ] I have added tests that prove my fix is effective or that my
feature works.
- [ ] 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
SwayStar123 authored Jul 7, 2023
1 parent 82b6e3d commit 1e66b16
Show file tree
Hide file tree
Showing 6 changed files with 476 additions and 13 deletions.
78 changes: 78 additions & 0 deletions sway-lib-std/src/address.sw
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
//! A wrapper around the `b256` type to help enhance type-safety.
library;

use ::call_frames::contract_id;
use ::contract_id::{AssetId};
use ::convert::From;
use ::error_signals::FAILED_TRANSFER_TO_ADDRESS_SIGNAL;
use ::revert::revert;
use ::outputs::{Output, output_amount, output_count, output_type};

/// The `Address` type, a struct wrapper around the inner `b256` value.
pub struct Address {
Expand All @@ -24,3 +29,76 @@ impl From<b256> for Address {
self.value
}
}

impl Address {
/// Transfer `amount` coins of type `asset_id` and send them to
/// the Address.
///
/// ### Arguments
///
/// * `amount` - The amount of tokens to transfer.
/// * `asset_id` - The `AssetId` of the token to transfer.
///
/// ### Reverts
///
/// * If `amount` is greater than the contract balance for `asset_id`.
/// * If `amount` is equal to zero.
/// * If there are no free variable outputs.
///
/// ### Examples
///
/// ```sway
/// use std::constants::{BASE_ASSET_ID, ZERO_B256};
///
/// // replace the zero Address with your desired Address
/// let address = Address::from(ZERO_B256);
/// address.transfer(500, BASE_ASSET_ID)
/// ```
pub fn transfer(self, amount: u64, asset_id: AssetId) {
// 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
///
/// * `amount` - The amount of tokens to mint.
///
/// ### Examples
///
/// ```sway
/// use std::constants::ZERO_B256;
///
/// // replace the zero Address with your desired Address
/// let address = Address::from(ZERO_B256);
/// address.mint_to(500);
/// ```
pub fn mint_to(self, amount: u64) {
asm(r1: amount) {
mint r1;
};
self.transfer(amount, contract_id());
}
}
68 changes: 68 additions & 0 deletions sway-lib-std/src/contract_id.sw
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,74 @@ impl From<b256> for ContractId {
}
}

impl ContractId {
/// UNCONDITIONAL transfer of `amount` coins of type `asset_id` to
/// the ContractId.
///
/// > **_WARNING:_**
/// >
/// > This will transfer coins to a contract even with no way to retrieve them
/// > (i.e. no withdrawal functionality on receiving contract), possibly leading
/// > to the **_PERMANENT LOSS OF COINS_** if not used with care.
///
/// ### Arguments
///
/// * `amount` - The amount of tokens to transfer.
/// * `asset_id` - The `AssetId` of the token to transfer.
///
/// ### Reverts
///
/// * If `amount` is greater than the contract balance for `asset_id`.
/// * If `amount` is equal to zero.
///
/// ### Examples
///
/// ```sway
/// use std::constants::{BASE_ASSET_ID, ZERO_B256};
///
/// // replace the zero ContractId with your desired ContractId
/// let contract_id = ContractId::from(ZERO_B256);
/// contract_id.transfer(500, BASE_ASSET_ID);
/// ```
pub fn transfer(self, amount: u64, asset_id: AssetId) {
asm(r1: amount, r2: asset_id.value, r3: self.value) {
tr r3 r1 r2;
}
}
}

impl ContractId {
/// Mint `amount` coins of the current contract's `asset_id` and send them
/// UNCONDITIONALLY to the contract at `to`.
///
/// > **_WARNING:_**
/// >
/// > This will transfer coins to a contract even with no way to retrieve them
/// > (i.e: no withdrawal functionality on the receiving contract), possibly leading to
/// > the **_PERMANENT LOSS OF COINS_** if not used with care.
///
/// ### Arguments
///
/// * `amount` - The amount of tokens to mint.
/// * `to` - The `ContractId` to which to send the tokens.
///
/// ### Examples
///
/// ```sway
/// use std::constants::ZERO_B256;
///
/// // replace the zero ContractId with your desired ContractId
/// let contract_id = ContractId::from(ZERO_B256);
/// contract_id.mint_to(500);
/// ```
pub fn mint_to(self, amount: u64) {
asm(r1: amount) {
mint r1;
};
self.transfer(amount, ContractId::from(asm() { fp: b256 })); // Transfer the self contract token
}
}

/// The `AssetId` type is simply an alias for `ContractId` that represents the ID of a native asset
/// which matches the ID of the contract that implements that asset.
pub type AssetId = ContractId;
88 changes: 80 additions & 8 deletions sway-lib-std/src/identity.sw
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ library;

use ::assert::assert;
use ::address::Address;
use ::call_frames::contract_id;
use ::constants::{ZERO_B256, BASE_ASSET_ID};
use ::contract_id::ContractId;
use ::contract_id::{AssetId, ContractId};
use ::option::Option;

/// The `Identity` type: either an `Address` or a `ContractId`.
Expand All @@ -18,8 +19,8 @@ pub enum Identity {
impl core::ops::Eq for Identity {
fn eq(self, other: Self) -> bool {
match (self, other) {
(Identity::Address(address1), Identity::Address(address2)) => address1 == address2,
(Identity::ContractId(asset1), Identity::ContractId(asset2)) => asset1 == asset2,
(Identity::Address(addr1), Identity::Address(addr2)) => addr1 == addr2,
(Identity::ContractId(id1), Identity::ContractId(id2)) => id1 == id2,
_ => false,
}
}
Expand All @@ -28,15 +29,15 @@ impl core::ops::Eq for Identity {
impl Identity {
pub fn as_address(self) -> Option<Address> {
match self {
Identity::Address(address) => Option::Some(address),
Identity::Address(addr) => Option::Some(addr),
Identity::ContractId(_) => Option::None,
}
}

pub fn as_contract_id(self) -> Option<ContractId> {
match self {
Identity::Address(_) => Option::None,
Identity::ContractId(contract_id) => Option::Some(contract_id),
Identity::ContractId(id) => Option::Some(id),
}
}

Expand All @@ -53,6 +54,77 @@ impl Identity {
Identity::ContractId(_) => true,
}
}

/// Transfer `amount` coins of the type `asset_id` and send them
/// to the Identity.
///
/// > **_WARNING:_**
/// >
/// > If the Identity is a contract this may transfer coins to the contract even with no way to retrieve them
/// > (i.e. no withdrawal functionality on receiving contract), possibly leading
/// > to the **_PERMANENT LOSS OF COINS_** if not used with care.
///
/// ### Arguments
///
/// * `amount` - The amount of tokens to transfer.
/// * `asset_id` - The `AssetId` of the token to transfer.
///
/// ### Reverts
///
/// * If `amount` is greater than the contract balance for `asset_id`.
/// * If `amount` is equal to zero.
/// * If there are no free variable outputs when transferring to an `Address`.
///
/// ### Examples
///
/// ```sway
/// use std::constants::{BASE_ASSET_ID, ZERO_B256};
///
/// // replace the zero Address/ContractId with your desired Address/ContractId
/// let to_address = Identity::Address(Address::from(ZERO_B256));
/// let to_contract_id = Identity::ContractId(ContractId::from(ZERO_B256));
/// to_address.transfer(500, BASE_ASSET_ID);
/// to_contract_id.transfer(500, BASE_ASSET_ID);
/// ```
pub fn transfer(self, amount: u64, asset_id: AssetId) {
match self {
Identity::Address(addr) => addr.transfer(amount, asset_id),
Identity::ContractId(id) => id.transfer(amount, asset_id),
};
}
}

impl Identity {
/// Mint `amount` coins of the current contract's `asset_id` and transfer them
/// to the Identity.
///
/// > **_WARNING:_**
/// >
/// > If the Identity is a contract, this will transfer coins to the contract even with no way to retrieve them
/// > (i.e: no withdrawal functionality on the receiving contract), possibly leading to
/// > the **_PERMANENT LOSS OF COINS_** if not used with care.
///
/// ### Arguments
///
/// * `amount` - The amount of tokens to mint.
///
/// ### Examples
///
/// ```sway
/// use std::constants::ZERO_B256;
///
/// // replace the zero Address/ContractId with your desired Address/ContractId
/// let address_identity = Identity::Address(Address::from(ZERO_B256));
/// let contract_identity = Identity::ContractId(ContractId::from(ZERO_B256));
/// address_identity.mint_to(500);
/// contract_identity.mint_to(500);
/// ```
pub fn mint_to(self, amount: u64) {
asm(r1: amount) {
mint r1;
};
self.transfer(amount, contract_id());
}
}

#[test]
Expand All @@ -67,10 +139,10 @@ fn test_address() {

#[test]
fn test_contract_id() {
let contract_id = BASE_ASSET_ID;
let identity = Identity::ContractId(contract_id);
let id = BASE_ASSET_ID;
let identity = Identity::ContractId(id);
assert(!identity.is_address());
assert(identity.is_contract_id());
assert(identity.as_contract_id().unwrap() == contract_id);
assert(identity.as_contract_id().unwrap() == id);
assert(identity.as_address().is_none());
}
4 changes: 2 additions & 2 deletions sway-lib-std/src/lib.sw
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@ pub mod call_frames;
pub mod context;
pub mod hash;
pub mod b512;
pub mod tx;
pub mod outputs;
pub mod address;
pub mod identity;
pub mod vec;
pub mod bytes;
pub mod string;
pub mod r#storage;
pub mod b256;
pub mod tx;
pub mod inputs;
pub mod outputs;
pub mod auth;
pub mod math;
pub mod block;
Expand Down
Loading

0 comments on commit 1e66b16

Please sign in to comment.