multiversx_sc/storage/mappers/token/
token_mapper.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use crate::{
    api::{CallTypeApi, ErrorApiImpl, StorageMapperApi},
    contract_base::BlockchainWrapper,
    storage::StorageKey,
    storage_get, storage_get_len, storage_set,
    types::{
        system_proxy::ESDTSystemSCProxy, CallbackClosure, ESDTSystemSCAddress, EsdtLocalRole,
        EsdtTokenPayment, ManagedAddress, ManagedRef, ManagedVec, TokenIdentifier, Tx,
    },
};

use super::TokenMapperState;

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";

pub trait StorageTokenWrapper<SA>
where
    SA: StorageMapperApi + CallTypeApi,
{
    fn get_storage_key(&self) -> ManagedRef<SA, StorageKey<SA>>;

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

    fn get_token_state(&self) -> TokenMapperState<SA>;

    fn get_token_id(&self) -> TokenIdentifier<SA>;

    fn get_token_id_ref(&self) -> &TokenIdentifier<SA>;

    fn set_token_id(&mut self, token_id: TokenIdentifier<SA>);

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

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

    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);
        }
    }

    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);
            }
        }
    }

    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);
    }

    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()
    }

    fn get_sc_address() -> ManagedAddress<SA> {
        let b_wrapper = BlockchainWrapper::new();
        b_wrapper.get_sc_address()
    }
}

pub(crate) fn store_token_id<
    SA: StorageMapperApi + CallTypeApi,
    Mapper: StorageTokenWrapper<SA>,
>(
    mapper: &Mapper,
    token_id: &TokenIdentifier<SA>,
) {
    if mapper.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(
        mapper.get_storage_key(),
        &TokenMapperState::Token(token_id.clone()),
    );
}
pub(crate) fn check_not_set<SA: StorageMapperApi + CallTypeApi, Mapper: StorageTokenWrapper<SA>>(
    mapper: &Mapper,
) {
    let storage_value: TokenMapperState<SA> = storage_get(mapper.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);
        },
    }
}