// SPDX-License-Identifier: Apache-2.0 contract; /* ____ _ ____ __ ___ _ _ _ _ | _ \| | | _ \ \ \ / (_) ___| | __| | / \ ___ ___ ___| |_ | |_) | | | |_) | _____ \ V /| |/ _ \ |/ _` | / _ \ / __/ __|/ _ \ __| | _ <| |___| __/ |_____| | | | | __/ | (_| | / ___ \\__ \__ \ __/ |_ |_| \_\_____|_| |_| |_|\___|_|\__,_| /_/ \_\___/___/\___|\__| */ mod errors; mod events; use std::{ asset::{ mint_to, burn as asset_burn }, context::*, revert::require, storage::{ storage_string::*, storage_vec::*, }, call_frames::msg_asset_id, string::String }; use std::hash::*; use helpers::{ utils::*, transfer::*, zero::* }; use standards::{ src20::SRC20, src3::SRC3, }; use sway_libs::{ asset::{ base::{ _total_supply as sl_total_supply, }, supply::{ _burn as sl_burn, _mint as sl_mint, }, }, }; use asset_interfaces::rlp::RLP; use errors::*; use events::*; const DECIMALS: u8 = 9; const DEFAULT_SUB_ID: SubId = SubId::zero(); storage { gov: Identity = ZERO_IDENTITY, is_initialized: bool = false, name: StorageString = StorageString {}, symbol: StorageString = StorageString {}, /// total supply of RLP /// only really 1 SubId is utilized for RLP minting total_supply: StorageMap = StorageMap {}, /// value for this is ALWAYS 1 total_assets: u64 = 0, approved_minters: StorageMap = StorageMap {}, } impl RLP for Contract { #[storage(read, write)] fn initialize() { require( !storage.is_initialized.read(), Error::RLPAlreadyInitialized ); storage.is_initialized.write(true); storage.name.write_slice(String::from_ascii_str("RLP")); storage.symbol.write_slice(String::from_ascii_str("RLP")); let sender = get_sender(); log(SetNameEvent { asset: _get_id(), name: storage.name.read_slice(), sender }); log(SetSymbolEvent { asset: _get_id(), symbol: storage.symbol.read_slice(), sender }); log(SetDecimalsEvent { asset: _get_id(), decimals: DECIMALS, sender }); let sender = get_sender(); storage.gov.write(sender); storage.approved_minters.insert(sender, true); log(SetGov { gov: sender }); log(SetApprovedMinter { minter: sender, is_active: true }); } /* ____ _ _ _ / / / / \ __| |_ __ ___ (_)_ __ / / / / _ \ / _` | '_ ` _ \| | '_ \ / / / / ___ \ (_| | | | | | | | | | | /_/_/ /_/ \_\__,_|_| |_| |_|_|_| |_| */ #[storage(read, write)] fn set_gov(gov: Identity) { _only_gov(); storage.gov.write(gov); log(SetGov { gov }); } #[storage(read, write)] fn set_minter(minter: Identity, is_active: bool) { _only_gov(); storage.approved_minters.insert(minter, is_active); log(SetApprovedMinter { minter, is_active }); } /* ____ __ ___ / / / \ \ / (_) _____ __ / / / \ \ / /| |/ _ \ \ /\ / / / / / \ V / | | __/\ V V / /_/_/ \_/ |_|\___| \_/\_/ */ /// Returns the AssetId of the RLP token fn get_id() -> AssetId { _get_id() } /// Returns the total supply of the RLP asset #[storage(read)] fn total_rlp_supply() -> u64 { storage.total_supply.get(_get_id()).try_read().unwrap_or(0) } } // https://docs.fuel.network/docs/sway-standards/src-20-native-asset/ impl SRC20 for Contract { #[storage(read)] fn name(_asset: AssetId) -> Option { Some(storage.name.read_slice().unwrap()) } #[storage(read)] fn symbol(_asset: AssetId) -> Option { Some(storage.symbol.read_slice().unwrap()) } #[storage(read)] fn decimals(_asset: AssetId) -> Option { Some(DECIMALS) } #[storage(read)] fn total_supply(asset: AssetId) -> Option { sl_total_supply(storage.total_supply, asset) } /// @dev only 1 DEFAULT_SUB_ID is utilized for RLP minting /// @dev value should always yield 1 #[storage(read)] fn total_assets() -> u64 { storage.total_assets.read() } } // https://docs.fuel.network/docs/sway-standards/src-3-minting-and-burning/ impl SRC3 for Contract { #[storage(read, write)] fn mint( recipient: Identity, _sub_id: SubId, amount: u64 ) { _only_minter(); _mint(recipient, amount) } #[payable] #[storage(read, write)] fn burn( _sub_id: SubId, amount: u64 ) { _burn(amount) } } /* ____ ___ _ _ / / / |_ _|_ __ | |_ ___ _ __ _ __ __ _| | / / / | || '_ \| __/ _ \ '__| '_ \ / _` | | / / / | || | | | || __/ | | | | | (_| | | /_/_/ |___|_| |_|\__\___|_| |_| |_|\__,_|_| */ #[storage(read)] fn _only_gov() { require( get_sender() == storage.gov.read(), Error::RLPForbidden ); } #[storage(read)] fn _only_minter() { require( storage.approved_minters.get(get_sender()).try_read().unwrap_or(false), Error::RLPOnlyMinter ); } fn _get_id() -> AssetId { // AssetId::new(ContractId::this(), DEFAULT_SUB_ID) AssetId::default() } #[storage(read, write)] fn _mint( recipient: Identity, amount: u64 ) { require( amount > 0, Error::RLPMintZeroAmount ); // require(recipient != ZERO_IDENTITY, Error::RLPMintToZeroIdentity); let _ = sl_mint( storage.total_assets, storage.total_supply, recipient, DEFAULT_SUB_ID, amount, ); } #[storage(read, write)] fn _burn(amount: u64) { require( msg_asset_id() == _get_id(), Error::RLPInvalidBurnAssetForwarded ); require( msg_amount() == amount, Error::RLPInvalidBurnAmountForwarded ); sl_burn( storage.total_supply, DEFAULT_SUB_ID, amount ); }