Skip to content

Commit

Permalink
Merge pull request #1865 from multiversx/token-mappers-read-from-address
Browse files Browse the repository at this point in the history
token mappers migration to StorageMapperFromAddress
  • Loading branch information
mihaicalinluca authored Nov 18, 2024
2 parents fd86e74 + f96cf65 commit 8cc43c5
Show file tree
Hide file tree
Showing 6 changed files with 438 additions and 300 deletions.
5 changes: 3 additions & 2 deletions framework/base/src/storage/mappers/token.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#![allow(unused)]

mod error;
mod fungible_token_mapper;
mod non_fungible_token_mapper;
mod token_attributes_mapper;
mod token_mapper;
mod token_mapper_state;

pub use fungible_token_mapper::FungibleTokenMapper;
pub use non_fungible_token_mapper::NonFungibleTokenMapper;
pub use token_attributes_mapper::TokenAttributesMapper;
pub use token_mapper::StorageTokenWrapper;
pub use token_mapper_state::TokenMapperState;
5 changes: 5 additions & 0 deletions framework/base/src/storage/mappers/token/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub(crate) const TOKEN_ID_ALREADY_SET_ERR_MSG: &[u8] = b"Token ID already set";
pub(crate) const PENDING_ERR_MSG: &[u8] = b"Issue was already called";
pub(crate) const MUST_SET_TOKEN_ID_ERR_MSG: &[u8] = b"Must issue or set token ID first";
pub(crate) const INVALID_TOKEN_ID_ERR_MSG: &[u8] = b"Invalid token ID";
pub(crate) const INVALID_PAYMENT_TOKEN_ERR_MSG: &[u8] = b"Invalid payment token";
230 changes: 174 additions & 56 deletions framework/base/src/storage/mappers/token/fungible_token_mapper.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
use multiversx_chain_core::types::EsdtLocalRole;

use crate::{
abi::{TypeAbi, TypeAbiFrom},
api::ErrorApiImpl,
codec::{EncodeErrorHandler, TopEncodeMulti, TopEncodeMultiOutput},
storage_clear, storage_get, storage_set,
storage::mappers::{
set_mapper::{CurrentStorage, StorageAddress},
StorageMapperFromAddress,
},
storage_clear, storage_get, storage_get_len, storage_set,
types::{
system_proxy::{ESDTSystemSCProxy, FungibleTokenProperties},
ESDTSystemSCAddress, Tx,
ESDTSystemSCAddress, ManagedRef, Tx,
},
};

use super::{
super::StorageMapper,
token_mapper::{check_not_set, store_token_id, StorageTokenWrapper, INVALID_TOKEN_ID_ERR_MSG},
error::{
INVALID_PAYMENT_TOKEN_ERR_MSG, INVALID_TOKEN_ID_ERR_MSG, MUST_SET_TOKEN_ID_ERR_MSG,
PENDING_ERR_MSG, TOKEN_ID_ALREADY_SET_ERR_MSG,
},
TokenMapperState,
};
use crate::{
Expand All @@ -21,69 +30,51 @@ use crate::{
storage::StorageKey,
types::{
BigUint, CallbackClosure, EsdtTokenPayment, EsdtTokenType, ManagedAddress, ManagedBuffer,
ManagedType, TokenIdentifier,
ManagedType, ManagedVec, TokenIdentifier,
},
};

pub(crate) const DEFAULT_ISSUE_CALLBACK_NAME: &str = "default_issue_cb";
pub(crate) const DEFAULT_ISSUE_WITH_INIT_SUPPLY_CALLBACK_NAME: &str =
"default_issue_init_supply_cb";

pub struct FungibleTokenMapper<SA>
pub struct FungibleTokenMapper<SA, A = CurrentStorage>
where
SA: StorageMapperApi + CallTypeApi,
A: StorageAddress<SA>,
{
key: StorageKey<SA>,
token_state: TokenMapperState<SA>,
address: A,
}

impl<SA> StorageMapper<SA> for FungibleTokenMapper<SA>
impl<SA> StorageMapper<SA> for FungibleTokenMapper<SA, CurrentStorage>
where
SA: StorageMapperApi + CallTypeApi,
{
fn new(base_key: StorageKey<SA>) -> Self {
Self {
token_state: storage_get(base_key.as_ref()),
key: base_key,
address: CurrentStorage,
}
}
}

impl<SA> StorageTokenWrapper<SA> for FungibleTokenMapper<SA>
impl<SA> StorageMapperFromAddress<SA> for FungibleTokenMapper<SA, ManagedAddress<SA>>
where
SA: StorageMapperApi + CallTypeApi,
{
fn get_storage_key(&self) -> crate::types::ManagedRef<SA, StorageKey<SA>> {
self.key.as_ref()
}

fn get_token_state(&self) -> TokenMapperState<SA> {
self.token_state.clone()
}

fn get_token_id(&self) -> TokenIdentifier<SA> {
if let TokenMapperState::Token(token) = &self.token_state {
token.clone()
} else {
SA::error_api_impl().signal_error(INVALID_TOKEN_ID_ERR_MSG)
}
}

fn get_token_id_ref(&self) -> &TokenIdentifier<SA> {
if let TokenMapperState::Token(token) = &self.token_state {
token
} else {
SA::error_api_impl().signal_error(INVALID_TOKEN_ID_ERR_MSG);
fn new_from_address(address: ManagedAddress<SA>, base_key: StorageKey<SA>) -> Self {
Self {
token_state: storage_get(base_key.as_ref()),
key: base_key,
address,
}
}

fn set_token_id(&mut self, token_id: TokenIdentifier<SA>) {
store_token_id(self, &token_id);
self.token_state = TokenMapperState::Token(token_id);
}
}

impl<SA> FungibleTokenMapper<SA>
impl<SA> FungibleTokenMapper<SA, CurrentStorage>
where
SA: StorageMapperApi + CallTypeApi,
{
Expand Down Expand Up @@ -114,7 +105,7 @@ where
num_decimals: usize,
opt_callback: Option<CallbackClosure<SA>>,
) -> ! {
check_not_set(self);
self.check_not_set();

let callback = match opt_callback {
Some(cb) => cb,
Expand Down Expand Up @@ -166,7 +157,7 @@ where
num_decimals: usize,
opt_callback: Option<CallbackClosure<SA>>,
) -> ! {
check_not_set(self);
self.check_not_set();

let callback = match opt_callback {
Some(cb) => cb,
Expand Down Expand Up @@ -195,21 +186,6 @@ where
}
}

fn default_callback_closure_obj(&self, initial_supply: &BigUint<SA>) -> CallbackClosure<SA> {
let initial_caller = BlockchainWrapper::<SA>::new().get_caller();
let cb_name = if initial_supply > &0 {
DEFAULT_ISSUE_WITH_INIT_SUPPLY_CALLBACK_NAME
} else {
DEFAULT_ISSUE_CALLBACK_NAME
};

let mut cb_closure = CallbackClosure::new(cb_name);
cb_closure.push_endpoint_arg(&initial_caller);
cb_closure.push_endpoint_arg(&self.key.buffer);

cb_closure
}

pub fn mint(&self, amount: BigUint<SA>) -> EsdtTokenPayment<SA> {
let send_wrapper = SendWrapper::<SA>::new();
let token_id = self.get_token_id();
Expand Down Expand Up @@ -237,6 +213,63 @@ where
send_wrapper.esdt_local_burn(token_id, 0, amount);
}

pub fn send_payment(&self, to: &ManagedAddress<SA>, payment: &EsdtTokenPayment<SA>) {
Tx::new_tx_from_sc()
.to(to)
.single_esdt(&payment.token_identifier, 0, &payment.amount)
.transfer();
}

pub fn set_if_empty(&mut self, token_id: TokenIdentifier<SA>) {
if self.is_empty() {
self.set_token_id(token_id);
}
}

pub fn set_local_roles(
&self,
roles: &[EsdtLocalRole],
opt_callback: Option<CallbackClosure<SA>>,
) -> ! {
let own_sc_address = Self::get_sc_address();
self.set_local_roles_for_address(&own_sc_address, roles, opt_callback);
}

pub fn set_local_roles_for_address(
&self,
address: &ManagedAddress<SA>,
roles: &[EsdtLocalRole],
opt_callback: Option<CallbackClosure<SA>>,
) -> ! {
self.require_issued_or_set();

let token_id = self.get_token_id_ref();
Tx::new_tx_from_sc()
.to(ESDTSystemSCAddress)
.typed(ESDTSystemSCProxy)
.set_special_roles(address, token_id, roles[..].iter().cloned())
.callback(opt_callback)
.async_call_and_exit()
}

pub fn set_token_id(&mut self, token_id: TokenIdentifier<SA>) {
self.store_token_id(&token_id);
self.token_state = TokenMapperState::Token(token_id);
}

pub(crate) fn store_token_id(&self, token_id: &TokenIdentifier<SA>) {
if self.get_token_state().is_set() {
SA::error_api_impl().signal_error(TOKEN_ID_ALREADY_SET_ERR_MSG);
}
if !token_id.is_valid_esdt_identifier() {
SA::error_api_impl().signal_error(INVALID_TOKEN_ID_ERR_MSG);
}
storage_set(
self.get_storage_key(),
&TokenMapperState::Token(token_id.clone()),
);
}

pub fn get_balance(&self) -> BigUint<SA> {
let b_wrapper = BlockchainWrapper::new();
let own_sc_address = Self::get_sc_address();
Expand All @@ -245,11 +278,96 @@ where
b_wrapper.get_esdt_balance(&own_sc_address, token_id, 0)
}

fn send_payment(&self, to: &ManagedAddress<SA>, payment: &EsdtTokenPayment<SA>) {
Tx::new_tx_from_sc()
.to(to)
.single_esdt(&payment.token_identifier, 0, &payment.amount)
.transfer();
pub fn get_sc_address() -> ManagedAddress<SA> {
let b_wrapper = BlockchainWrapper::new();
b_wrapper.get_sc_address()
}
}

impl<SA, A> FungibleTokenMapper<SA, A>
where
SA: StorageMapperApi + CallTypeApi,
A: StorageAddress<SA>,
{
pub fn get_storage_key(&self) -> ManagedRef<SA, StorageKey<SA>> {
self.key.as_ref()
}

pub fn get_token_state(&self) -> TokenMapperState<SA> {
self.token_state.clone()
}

pub fn get_token_id(&self) -> TokenIdentifier<SA> {
if let TokenMapperState::Token(token) = &self.token_state {
token.clone()
} else {
SA::error_api_impl().signal_error(INVALID_TOKEN_ID_ERR_MSG)
}
}

pub fn get_token_id_ref(&self) -> &TokenIdentifier<SA> {
if let TokenMapperState::Token(token) = &self.token_state {
token
} else {
SA::error_api_impl().signal_error(INVALID_TOKEN_ID_ERR_MSG);
}
}

pub fn is_empty(&self) -> bool {
storage_get_len(self.get_storage_key()) == 0
}

pub fn require_issued_or_set(&self) {
if self.is_empty() {
SA::error_api_impl().signal_error(MUST_SET_TOKEN_ID_ERR_MSG);
}
}

pub fn require_same_token(&self, expected_token_id: &TokenIdentifier<SA>) {
let actual_token_id = self.get_token_id_ref();
if actual_token_id != expected_token_id {
SA::error_api_impl().signal_error(INVALID_PAYMENT_TOKEN_ERR_MSG);
}
}

pub fn require_all_same_token(&self, payments: &ManagedVec<SA, EsdtTokenPayment<SA>>) {
let actual_token_id = self.get_token_id_ref();
for p in payments {
if actual_token_id != &p.token_identifier {
SA::error_api_impl().signal_error(INVALID_PAYMENT_TOKEN_ERR_MSG);
}
}
}

pub fn default_callback_closure_obj(
&self,
initial_supply: &BigUint<SA>,
) -> CallbackClosure<SA> {
let initial_caller = BlockchainWrapper::<SA>::new().get_caller();
let cb_name = if initial_supply > &0 {
DEFAULT_ISSUE_WITH_INIT_SUPPLY_CALLBACK_NAME
} else {
DEFAULT_ISSUE_CALLBACK_NAME
};

let mut cb_closure = CallbackClosure::new(cb_name);
cb_closure.push_endpoint_arg(&initial_caller);
cb_closure.push_endpoint_arg(&self.key.buffer);

cb_closure
}

pub(crate) fn check_not_set(&self) {
let storage_value: TokenMapperState<SA> = storage_get(self.get_storage_key());
match storage_value {
TokenMapperState::NotSet => {},
TokenMapperState::Pending => {
SA::error_api_impl().signal_error(PENDING_ERR_MSG);
},
TokenMapperState::Token(_) => {
SA::error_api_impl().signal_error(TOKEN_ID_ALREADY_SET_ERR_MSG);
},
}
}
}

Expand Down
Loading

0 comments on commit 8cc43c5

Please sign in to comment.