|
| 1 | +# Mvx-ESDT-Safe |
| 2 | + |
| 3 | + |
| 4 | +The ability to transfer tokens from the Main Chain to any Sovereign Chain is essential, since every Sovereign can connect to the Main MultiversX Chain. As a result, the customizable Sovereign can leverage any token available on the default network. Another great feature is the possibility of executing smart contracts inside the Sovereign Chain through this contract. |
| 5 | + |
| 6 | +This contract has three main modules: [`deposit`](#deposit), [`execute_operation`](#executing-an-operation) and [`register_token`](#registering-tokens). |
| 7 | + |
| 8 | + |
| 9 | +## Deposit |
| 10 | +### Main Chain deposit to Sovereign Chain transfer flow |
| 11 | +1. User deposits the tokens he wishes to transfer in the `Mvx-ESDT-Safe` contract deployed on the Main Chain. |
| 12 | +2. An observer is monitoring the Main Chain. |
| 13 | +3. Sovereign network receives extended shard header. |
| 14 | +4. Incoming transactions processor handles and processes the new transaction. |
| 15 | + |
| 16 | +### Deposit Endpoint |
| 17 | +```rust |
| 18 | + #[payable("*")] |
| 19 | + #[endpoint] |
| 20 | + fn deposit( |
| 21 | + &self, |
| 22 | + to: ManagedAddress, |
| 23 | + optional_transfer_data: OptionalValueTransferDataTuple<Self::Api>, |
| 24 | + ) |
| 25 | +``` |
| 26 | + |
| 27 | +One key aspect of cross chain transfers from MultiversX Main Chain to a Sovereign Chain is being able to transfer tokens and also execute a smart contract call within single transaction. The `#[payable("*")]` annotation means that the endpoint can receive any tokens that will transferred. If those tokens are from a Sovereign Chain they will be burned otherwise they will be saved in the Smart Contract`s account. The checks enabled for the transfer of tokens are the following: |
| 28 | + |
| 29 | +- If the token is whitelisted or not blacklisted, in that case the tokens can be transferred. |
| 30 | +- If the fee is enabled, the smart contract assures that the fee is paid. |
| 31 | +- If there are maximum 10 transfers in the transaction. |
| 32 | + |
| 33 | +If the deposit also includes the `optional_transfer_data` parameter it will also have some extra checks regarding the cross-chain execution of endpoints: |
| 34 | + |
| 35 | +- The gas limit must be under the specified limit. |
| 36 | +- The endpoint that has to be executed is not blacklisted. |
| 37 | + |
| 38 | + |
| 39 | +At the end of the `deposit` endpoint, all the extra tokens will be refunded to the caller and an event will be emitted since the bridging process is complete. |
| 40 | + |
| 41 | + |
| 42 | +```rust |
| 43 | +#[event("deposit")] |
| 44 | +fn deposit_event( |
| 45 | + &self, |
| 46 | + #[indexed] dest_address: &ManagedAddress, |
| 47 | + #[indexed] tokens: &MultiValueEncoded<MultiValue3<TokenIdentifier, u64, EsdtTokenData>>, |
| 48 | + event_data: OperationData<Self::Api>, |
| 49 | +) |
| 50 | +``` |
| 51 | + |
| 52 | +This log event will emit the destination address and the tokens which will be transferred to the Sovereign Chain. |
| 53 | + |
| 54 | +:::note |
| 55 | +The source code for the endpoint can be found [here](https://github.com/multiversx/mx-sovereign-sc/blob/main/mvx-esdt-safe/src/deposit.rs). |
| 56 | +::: |
| 57 | + |
| 58 | +## Executing an Operation |
| 59 | + |
| 60 | +```rust |
| 61 | +#[endpoint(executeBridgeOps)] |
| 62 | + fn execute_operations( |
| 63 | + &self, |
| 64 | + hash_of_hashes: ManagedBuffer, |
| 65 | + operation: Operation<Self::Api> |
| 66 | +) |
| 67 | +``` |
| 68 | +- `hash_of_hashes`: hash of all hashes of the operations that were sent in a round |
| 69 | +- `operation`: the details of the cross-chain execution |
| 70 | + |
| 71 | +To ensure that the cross-chain execution is will be successful, the following checks must be passed: |
| 72 | + |
| 73 | +1. Calculate the hash of the *Operation* received as a parameter. |
| 74 | +2. Verify that the given *Operation’s* hash is registered by the Header-Verifier smart contract. |
| 75 | +3. Mint tokens or get them from the account. |
| 76 | +4. Distribute the tokens. |
| 77 | +5. Emit confirmation event or fail event if needed. |
| 78 | + |
| 79 | +As the 2nd point specifies, the `Header-Verifier` smart contract plays an important role in the cross-chain execution mechanism. In the [`Header-Verifier`](header-verifier.md) section there will also be a description for the important endpoints within this contract. |
| 80 | + |
| 81 | +:::note |
| 82 | +The source code for the endpoint can be found [here](https://github.com/multiversx/mx-sovereign-sc/blob/main/mvx-esdt-safe/src/execute.rs). |
| 83 | +::: |
| 84 | + |
| 85 | +## Important Endpoint Related Structures |
| 86 | +This subsection outlines the key data structures that enable robust cross-chain operations. It details how an *Operation* is composed of its destination address, one or more token transfers defined by `OperationEsdtPayment`, and the contextual metadata provided by `OperationData`. Additionally, `TransferData` specifies the parameters needed for executing remote smart contract calls, collectively ensuring precise control over cross-chain interactions. |
| 87 | +```rust |
| 88 | +#[derive(TopEncode, TopDecode, NestedEncode, NestedDecode, TypeAbi, ManagedVecItem, Clone)] |
| 89 | +pub struct Operation<M: ManagedTypeApi> { |
| 90 | + pub to: ManagedAddress<M>, |
| 91 | + pub tokens: ManagedVec<M, OperationEsdtPayment<M>>, |
| 92 | + pub data: OperationData<M>, |
| 93 | +} |
| 94 | +``` |
| 95 | + |
| 96 | +- `to`: specifies the destination of the *Operation* |
| 97 | +- `tokens`: represents one or more token transfers associated with the operation |
| 98 | +- `data`: encapsulates additional instructions or parameters that guide the execution of the operation |
| 99 | + |
| 100 | +```rust |
| 101 | +pub struct OperationEsdtPayment<M: ManagedTypeApi> { |
| 102 | + pub token_identifier: TokenIdentifier<M>, |
| 103 | + pub token_nonce: u64, |
| 104 | + pub token_data: EsdtTokenData<M>, |
| 105 | +} |
| 106 | +``` |
| 107 | + |
| 108 | +This struct describes a single token transfer action within an *Operation*. Each Operation can have one or more of such payments, with that enabling the transfer of a variety of tokens during a cross-chain transaction. |
| 109 | + |
| 110 | +- `token_identifier`: used for the identification of the token |
| 111 | +- `token_nonce`: if the token is Non-Fungible or Semi-Fungible, it will have a custom nonce, if not the value will be 0 |
| 112 | +- `token_data`: a structure holding metadata and other token properties |
| 113 | + |
| 114 | +```rust |
| 115 | +pub struct OperationData<M: ManagedTypeApi> { |
| 116 | + pub op_nonce: TxId, |
| 117 | + pub op_sender: ManagedAddress<M>, |
| 118 | + pub opt_transfer_data: Option<TransferData<M>>, |
| 119 | +} |
| 120 | +``` |
| 121 | + |
| 122 | +`OperationData` encapsulates the needed information for the *Operation* that needs to be executed. This isn’t just another data definition, we’ve already seen data-related fields elsewhere. Instead, it centralizes the contextual information that *Operation* needs before, during, and after execution. |
| 123 | + |
| 124 | +- `op_nonce`: is used for the identification of each *Operation* |
| 125 | +- `op_sender`: represents the original sender of the *Operation* |
| 126 | +- `opt_transfer_data`: an optional `TransferData` field, when present, contains details about the cross-chain execution of another Smart Contract |
| 127 | + |
| 128 | +```rust |
| 129 | +pub struct TransferData<M: ManagedTypeApi> { |
| 130 | + pub gas_limit: GasLimit, |
| 131 | + pub function: ManagedBuffer<M>, |
| 132 | + pub args: ManagedVec<M, ManagedBuffer<M>>, |
| 133 | +} |
| 134 | +``` |
| 135 | + |
| 136 | +`TransferData` represents the description of the remote execution of another Smart Contract. |
| 137 | + |
| 138 | +- `gas_limit`: specifies the needed gas for the execution of all other endpoints. |
| 139 | +- `function`: the name of the endpoint that will be executed. |
| 140 | +- `args`: the arguments for the calls. |
| 141 | + |
| 142 | +:::note |
| 143 | +The source code for structures can be found [here](https://github.com/multiversx/mx-sovereign-sc/blob/main/common/structs/src/operation.rs). |
| 144 | +::: |
| 145 | + |
| 146 | +## Registering tokens |
| 147 | +As mentioned at the start of this section, in the scope a Sovereign Chain, a token that already exists inside the MultiversX Mainchain can be leveraged within the custom blockchain. It has to be firstly registered inside the `Mvx-ESDT-Safe` smart contract. The `register_token` module has the role of registering any token that will be later used inside the Sovereign Chain. |
| 148 | + |
| 149 | +### Register any token |
| 150 | + |
| 151 | +```rust |
| 152 | + #[payable("EGLD")] |
| 153 | + #[endpoint(registerToken)] |
| 154 | + fn register_token( |
| 155 | + &self, |
| 156 | + sov_token_id: TokenIdentifier, |
| 157 | + token_type: EsdtTokenType, |
| 158 | + token_display_name: ManagedBuffer, |
| 159 | + token_ticker: ManagedBuffer, |
| 160 | + num_decimals: usize, |
| 161 | + ) |
| 162 | +``` |
| 163 | + |
| 164 | +This endpoint is how an user from a Sovereign Chain registers a token on the MultiversX Mainchain. Every token registration costs 0.05 EGLD, that's why the endpoint is `payable`. |
| 165 | +The endpoint check if the token was not registered before and if it has a prefix. |
| 166 | + |
| 167 | +> Every token that was created in a Sovereign Chain has a prefix. Example: `sov-TOKEN-123456`. |
| 168 | + |
| 169 | +If everything is in order, the `Mvx-ESDT-Safe` smart contract will initiate an asynchronous call to the `issue_and_set_all_roles` endpoint from the _ESDTSystemSC_. When the system smart contract finishes the issue transaction, the callback inside the `Mvx-ESDT-Safe` smart contract will trigger and register the mapping of token identifier inside the token mappers: |
| 170 | + |
| 171 | +* `sovereign_to_multiversx_token_id_mapper(sov_token_id)` -> mvx_token_id |
| 172 | +* `multiversx_to_sovereign_token_id_mapper(mvx_token_id)` -> sov_token_id |
| 173 | + |
| 174 | +> After the execution of this endpoint, the `Mvx-ESDT-Safe` smart contract will have in its storage a pair of token identifiers. **Example**: `sov-TOKEN-123456` is the corresponding sovereign identifier for the `TOKEN-123456` identifier from the MultiversX Mainchain. You can view this feature as creating copies of MultiversX Mainchain tokens inside the Sovereign Chain. |
| 175 | + |
| 176 | +### Register the native token |
| 177 | +Since a Sovereign Chain is a separate blockchain from the MultiversX Mainchain, it has to have a its own native token. Registering the native token is a straightforward process of just one endpoint call. |
| 178 | + |
| 179 | +```rust |
| 180 | + #[payable("EGLD")] |
| 181 | + #[only_owner] |
| 182 | + #[endpoint(registerNativeToken)] |
| 183 | + fn register_native_token(&self, token_ticker: ManagedBuffer, token_name: ManagedBuffer) |
| 184 | +``` |
| 185 | + |
| 186 | +The owner will have to call the `register_native_token` from the `Mvx-ESDT-Safe` smart contract in order to register the token identifier that will be used inside the Sovereign Chain as the native one. There can only be one native token so the endpoint firstly checks if if was not already registered. The fee amount for registering is the same as registering any token, 0.05 EGLD. The parameters include the `token_ticker` and `token_name`. The endpoint then initiates an asynchronous call to the _ESDTSystemSC_ to `issue_and_set_all_roles`. The newly created token is always fungible and has 18 decimals. After the issue call is finished the callback inside the `Mvx-ESDT-Safe` smart contract inserts the newly issued token identifier inside its storage. |
| 187 | + |
| 188 | +:::note |
| 189 | +The source code for this module can be found [here](https://github.com/multiversx/mx-sovereign-sc/blob/main/mvx-esdt-safe/src/register_token.rs). |
| 190 | +::: |
0 commit comments