diff --git a/docs/developers/relayed-transactions.md b/docs/developers/relayed-transactions.md index ec26daa00..3060317a6 100644 --- a/docs/developers/relayed-transactions.md +++ b/docs/developers/relayed-transactions.md @@ -253,136 +253,66 @@ Decoding the arguments ([useful resources here](/developers/sc-calls-format/)) w This feature is not yet available on **Mainnet**. See [Spica Protocol Upgrade](https://governance.multiversx.com/proposal/erd1qqqqqqqqqqqqqpgq4qvrwlr2e6ld50f3qfc94am38p8298kthg4s3f0vfn/1). ::: -Relayed transactions v3 feature comes with a change on the entire transaction structure, adding a new optional field `InnerTransactions`, which is a collection of inner transactions. That being said, relayed transactions v3 allow the user to send multiple inner transactions on the same relayed transaction which will be executed as normal transactions, without the gas consuming data field of the old relayed transactions versions. +Relayed transactions v3 feature comes with a change on the entire transaction structure, adding two new optional fields: +- `relayer`, which is the relayer address that will pay the fees. +- `relayerSignature`, the signature of the relayer that proves the agreement of the relayer. -In terms of gas limit computation, let's consider the following example: relayed transaction with one inner transaction of type move balance, that also has a data field `test` of length 4. +That being said, relayed transactions v3 will look and behave very similar to a regular transaction, the only difference being the gas consumption from the relayer. It is no longer needed to specify the user transaction in the data field. + +In terms of gas limit computation, an extra base cost will be consumed. Let's consider the following example: relayed transaction with inner transaction of type move balance, that also has a data field `test` of length 4. ```js - gasLimitInnerTxs = + * length(txData) - gasLimitInnerTxs = 50_000 + 4 * 1_500 - gasLimitInnerTxs = 56_000 + gasLimitInnerTx = + * length(txData) + gasLimitInnerTx = 50_000 + 4 * 1_500 + gasLimitInnerTx = 56_000 - gasLimitRelayedTx = * len(inner_transactions) + - gasLimitRelayedTx = 50_000 * 1 + 56_000 + gasLimitRelayedTx = + + gasLimitRelayedTx = 50_000 + 56_000 gasLimitRelayedTx = 106_000 ``` -Similar for a relayed transaction v3 that has 3 inner transactions of type move balance(empty data field on each): -```js - gasLimitInnerTxs = + + - gasLimitInnerTxs = 50_000 + 50_000 + 50_000 - gasLimitInnerTxs = 150_000 - - gasLimitRelayedTx = * len(inner_transactions) + - gasLimitRelayedTx = 50_000 * 3 + 150_000 - gasLimitRelayedTx = 300_000 -``` - It would look like: ```rust RelayedV3Transaction { - Sender: - Receiver: - Value: 0 - GasLimit: * len(InnerTransactions) + sum() - InnerTransactions: []Transaction { - { - "nonce": , - "value": , - "receiver": , - "sender": , - "gasLimit": , - "data": , - "relayer": , - }, - { - "nonce": , - "value": , - "receiver": , - "sender": , - "gasLimit": , - "data": , - "relayer": , - }, - ... - } + Sender: + Receiver: + Value: + GasLimit: + + * length(txData) + Relayer: + RelayerSignature: + Signature: } ``` Therefore, in order to build such a transaction, one has to follow the next steps: - -- create the inner transactions. Make sure that: - - all inner transactions have the `relayer` field set to the address that would pay the gas - - all inner transactions senders are in **the same shard** with the relayer - - in case there are multiple inner transactions from the same sender, the transactions are in the order of the execution, with increasing correct nonces, starting from the current sender's nonce - - all inner transactions are valid and signed -- create the relayed transactions: - - the receiver of the relayed transactions is the relayer - - value must be 0 and data field empty - - add all inner transactions on the `innerTransactions` field - - compute the gas limit: `move balance cost * number of inner transactions + sum of all inner transactions gas limits` + - set the `relayer` field to the address that would pay the gas + - add the extra base cost for the relayed operation + - add sender's signature + - add relayer's signature :::note -1. If the relayed transaction v3 is guarded, the cost of the guarded transaction will be consumed only once, not for each inner transaction. -2. There are situations where inner transactions are initially accepted, but before their execution, the underlying state changes (due to the execution of other transactions), rendering some or all of these transactions invalid. In such cases, the relayer will still incur gas costs (pays the fee), even though these inner transactions produce no effects. +1. For a guarded relayed transaction, the guarded operation fee will also be consumed from the relayer. +2. Relayer must be different from guardian, in case of guarded sender. +3. Guarded relayers are not allowed. ::: ### Example -Here's an example of a relayed v3 transaction. Its intent is: -- 2 sc calls of method `add@01` of a previously deployed adder contract -- 1 move balance +Here's an example of a relayed v3 transaction. Its intent is to call the `add` method of a previously deployed adder contract, with parameter `01` ```json { "nonce": 0, "value": "0", - "receiver": "erd1dcad0dlle658mggthhpvaypjag2et25yk3c2r7p9gjqkgqpxtukqcqakt0", - "sender": "erd1dcad0dlle658mggthhpvaypjag2et25yk3c2r7p9gjqkgqpxtukqcqakt0", + "receiver": "erd1qqqqqqqqqqqqqpgqeunf87ar9nqeey5ssvpqwe74ehmztx74qtxqs63nmx", + "sender": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "data": "YWRkQDAx", "gasPrice": 1000000000, - "gasLimit": 10200000, + "gasLimit": 5000000, "signature": "...", "chainID": "T", "version": 2, - "innerTransactions": [ - { - "nonce": 1, - "value": "0", - "receiver": "erd1qqqqqqqqqqqqqpgq3tssqmcde5w6626mkrm9cehcqgquwmvwl0jq3umv7s", - "sender": "erd1ws52y4t8pmwx9zcuwg0swxymxkg6dwsfpcf3qxmsjvzjtmwkl0jqpn44dn", - "gasPrice": 1000000000, - "gasLimit": 5000000, - "data": "YWRkQDAx", - "signature": "...", - "chainID": "T", - "version": 2, - "relayer": "erd1dcad0dlle658mggthhpvaypjag2et25yk3c2r7p9gjqkgqpxtukqcqakt0" - }, - { - "nonce": 2, - "value": "0", - "receiver": "erd1qqqqqqqqqqqqqpgq3tssqmcde5w6626mkrm9cehcqgquwmvwl0jq3umv7s", - "sender": "erd1ws52y4t8pmwx9zcuwg0swxymxkg6dwsfpcf3qxmsjvzjtmwkl0jqpn44dn", - "gasPrice": 1000000000, - "gasLimit": 5000000, - "data": "YWRkQDAx", - "signature": "...", - "chainID": "T", - "version": 2, - "relayer": "erd1dcad0dlle658mggthhpvaypjag2et25yk3c2r7p9gjqkgqpxtukqcqakt0" - }, - { - "nonce": 0, - "value": "1000000000000000000", - "receiver": "erd1v7p7uefggz04qst7fzd8hwrl95v03g5uh5fj9vka6ydmdguqy6ws28at85", - "sender": "erd1xfr34s4yft7pk4gf8mt7a2kkfz4z2f3rvuy9pwlgkeymue8pcfkqfxv2mv", - "gasPrice": 1000000000, - "gasLimit": 50000, - "signature": "...", - "chainID": "T", - "version": 2, - "relayer": "erd1dcad0dlle658mggthhpvaypjag2et25yk3c2r7p9gjqkgqpxtukqcqakt0" - } - ] + "relayer": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + "relayerSignature": "..." } ``` diff --git a/docs/developers/transactions/tx-migration.md b/docs/developers/transactions/tx-migration.md index e0c8dec47..ac1c88bef 100644 --- a/docs/developers/transactions/tx-migration.md +++ b/docs/developers/transactions/tx-migration.md @@ -5,14 +5,14 @@ title: Migration [comment]: # (mx-abstract) -There is an older syntax for producing contract calls, detailed [here](tx-legacy-calls). It is already in use in many projects. +There is an older syntax for producing contract calls, detailed [here](./tx-legacy-calls.md). It is already in use in many projects. Upgrading to framework version 0.49.0 is almost completely backwards compatible, but the new syntax is nicer and more reliable, so we encourage everyone to migrate. This will be a migration guide, as well as some frequently encountered pitfalls. :::caution -Even though the syntax is backwards compatible, the implementation of the [old syntax](tx-legacy-calls) has been replaced. +Even though the syntax is backwards compatible, the implementation of the [old syntax](./tx-legacy-calls.md) has been replaced. To the best of our knowledge, all code should continue to behave the same. However, if you upgrade beyond 0.49.0, please make sure to **test your smart contract fully** once again, even if you do not change a single line of code in your code base. @@ -71,7 +71,7 @@ The solution in this case is simple: replace `Proxy` with `ProxyTo` in code. ## Replace `#[derive(TypeAbi)]` with `#[type_abi]` -To use the new proxies, one must first [generate](tx-proxies#how-to-generate) them. The proxy is designed to be self-contained, so unless configured otherwise, it will also output a copy of the contract types involved in the ABI. +To use the new proxies, one must first [generate](./tx-proxies.md#how-to-generate) them. The proxy is designed to be self-contained, so unless configured otherwise, it will also output a copy of the contract types involved in the ABI. No methods of these types are copied, but the annotations are important most of the time, so they need to be copied too. These types will need the encode/decode annotations, as well as `Clone`, `Eq`, etc. @@ -81,14 +81,11 @@ The solution is to have another annotation, called `#[type_abi]` **before** the Currently, `#[type_abi]` takes no arguments and works the same way as `#[derive(TypeAbi)]`, but it might be extended in the future. -This is yet the case, but `#[derive(TypeAbi)]` might become deprecated at some point in the future, after most projects will have been migrate. - - [comment]: # (mx-context-auto) ## Generate the new proxies -Just like for a new project, you will need to [generate](tx-proxies#how-to-generate) the new proxies and embed them in your project. +Just like for a new project, you will need to [generate](./tx-proxies.md#how-to-generate) the new proxies and embed them in your project. [comment]: # (mx-context-auto) @@ -98,53 +95,51 @@ Just like for a new project, you will need to [generate](tx-proxies#how-to-gener You might have this kind of syntax in your contract. You can easily find it by searching in your project for `#[proxy]` or `.contract(`. ```rust title="Variant A" - #[proxy] - fn vault_proxy(&self) -> vault::Proxy; - - #[endpoint] - fn do_call(&self, to: ManagedAddress, args: MultiValueEncoded) { - self.vault_proxy() - .contract(to) - .echo_arguments(args) - .async_call() - .with_callback(self.callbacks().echo_args_callback()) - .call_and_exit(); - } +#[proxy] +fn vault_proxy(&self) -> vault::Proxy; + +#[endpoint] +fn do_call(&self, to: ManagedAddress, args: MultiValueEncoded) { + self.vault_proxy() + .contract(to) + .echo_arguments(args) + .async_call() + .with_callback(self.callbacks().echo_args_callback()) + .call_and_exit(); +} ``` ```rust title="Variant B" - #[proxy] - fn vault_proxy(&self, sc_address: ManagedAddress) -> vault::Proxy; - - #[endpoint] - fn do_call(&self, to: ManagedAddress, args: MultiValueEncoded) { - self.vault_proxy(to) - .echo_arguments(args) - .async_call() - .with_callback(self.callbacks().echo_args_callback()) - .call_and_exit(); - } - - +#[proxy] +fn vault_proxy(&self, sc_address: ManagedAddress) -> vault::Proxy; + +#[endpoint] +fn do_call(&self, to: ManagedAddress, args: MultiValueEncoded) { + self.vault_proxy(to) + .echo_arguments(args) + .async_call() + .with_callback(self.callbacks().echo_args_callback()) + .call_and_exit(); +} ``` ```rust title="Replace by" - #[endpoint] - fn do_call(&self, to: ManagedAddress, args: MultiValueEncoded) { - self.tx() - .to(&to) - .typed(vault_proxy::VaultProxy) - .echo_arguments(args) - .async_call() - .with_callback(self.callbacks().echo_args_callback()) - .call_and_exit(); - } +#[endpoint] +fn do_call(&self, to: ManagedAddress, args: MultiValueEncoded) { + self.tx() + .to(&to) + .typed(vault_proxy::VaultProxy) + .echo_arguments(args) + .async_call() + .with_callback(self.callbacks().echo_args_callback()) + .call_and_exit(); +} ``` Both variants above should be replaced by this pattern. :::info -The new proxies no longer have a recipient field in them. The [recipient (to) field](tx-to) is completely independent. It is set the same way for transactions with or without proxies. +The new proxies no longer have a recipient field in them. The [recipient (to) field](./tx-to.md) is completely independent. It is set the same way for transactions with or without proxies. This feature has proven not worth the complication, we are happy to see it go. ::: @@ -168,22 +163,19 @@ A solution is planned for the near future. In case you want to migrate to the unified syntax and you cannot, or do not want to get rid of the old proxies, this is an alternative transitional syntax: ```rust title="Transitional variant" - #[proxy] - fn vault_proxy(&self, sc_address: ManagedAddress) -> vault::Proxy; - - #[endpoint] - fn do_call(&self, to: ManagedAddress, args: MultiValueEncoded) { - self.tx() - .legacy_proxy_call(self.vault_proxy(to).echo_arguments(args)) - .async_call() - .with_callback(self.callbacks().echo_args_callback()) - .call_and_exit(); - } +#[proxy] +fn vault_proxy(&self, sc_address: ManagedAddress) -> vault::Proxy; + +#[endpoint] +fn do_call(&self, to: ManagedAddress, args: MultiValueEncoded) { + self.tx() + .legacy_proxy_call(self.vault_proxy(to).echo_arguments(args)) + .async_call() + .with_callback(self.callbacks().echo_args_callback()) + .call_and_exit(); +} ``` - - - [comment]: # (mx-context-auto) ## (Optional) Remove contract dependencies @@ -206,23 +198,23 @@ To achieve backwards compatibility, all the old methods were kept (even though t We did not yet deprecate the old ones, but we encourage everyone to switch to the new ones. They have shorter names and tend to be more expressive. -| Old method | New method | Comments | -| ---------- | ---------- | -----| -| `.with_egld_transfer(amount)` | `.egld(amount)` | | -| `.with_esdt_transfer((token, nonce, amount))` | `.esdt((token, nonce, amount))`
or
`.single_esdt(&token, nonce, &amount)` | `single_esdt` can deal with references, instead of taking owned objects. | -| `.with_multi_token_transfer(p)` | `.payment(p)` | `payment` is universal. | -| `.with_egld_or_single_esdt_transfer(p)` | `.payment(p)` | Method `payment` is universal. | -| `.with_gas_limi(gas)` | `.gas(gas)` | | -| `.with_extra_gas_for_callback(gas)` | `.gas_for_callback(gas)` | Method `payment` is universal. | -| `.async_call()` | - | Does nothing, can be removed with no consequences. | -| `.async_call_promise()` | - | Does nothing, can be removed with no consequences. | -| `.with_callback(cb)` | `.callback(cb)` | | -| `.deploy_contract(code, code_metadata)` | `.code(code)`
`.code_metadata(code_metadata)`
`.sync_call()` | Also add result handlers for decoding the result. | -| `.deploy_from_source(address, code_metadata)` | `.from_source(code)`
`.code_metadata(code_metadata) .sync_call()` | Also add result handlers for decoding the result. | -| `.upgrade_contract(code, code_metadata)` | `.code(code) .code_metadata(code_metadata) .upgrade_async_call_and_exit()` | Upgrades are async calls. | -| `.upgrade_from_source(address, code_metadata)` | `.from_source(code)`
`.code_metadata(code_metadata)`
`.upgrade_async_call_and_exit()` | Upgrades are async calls. | -| `.execute_on_dest_context()` | `.sync_call()` | Also add result handlers for decoding the result. | -| `.execute_on_dest_context`
`_with_back_transfers()` | `.returns(ReturnsBackTransfers)`
`.sync_call()` | Add additional result handlers for decoding the result. | +| Old method | New method | Comments | +| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | +| `.with_egld_transfer(amount)` | `.egld(amount)` | | +| `.with_esdt_transfer((token, nonce, amount))` | `.esdt((token, nonce, amount))`
or
`.single_esdt(&token, nonce, &amount)` | `single_esdt` can deal with references, instead of taking owned objects. | +| `.with_multi_token_transfer(p)` | `.payment(p)` | `payment` is universal. | +| `.with_egld_or_single_esdt_transfer(p)` | `.payment(p)` | Method `payment` is universal. | +| `.with_gas_limi(gas)` | `.gas(gas)` | | +| `.with_extra_gas_for_callback(gas)` | `.gas_for_callback(gas)` | Method `payment` is universal. | +| `.async_call()` | - | Does nothing, can be removed with no consequences. | +| `.async_call_promise()` | - | Does nothing, can be removed with no consequences. | +| `.with_callback(cb)` | `.callback(cb)` | | +| `.deploy_contract(code, code_metadata)` | `.code(code)`
`.code_metadata(code_metadata)`
`.sync_call()` | Also add result handlers for decoding the result. | +| `.deploy_from_source(address, code_metadata)` | `.from_source(code)`
`.code_metadata(code_metadata) .sync_call()` | Also add result handlers for decoding the result. | +| `.upgrade_contract(code, code_metadata)` | `.code(code) .code_metadata(code_metadata) .upgrade_async_call_and_exit()` | Upgrades are async calls. | +| `.upgrade_from_source(address, code_metadata)` | `.from_source(code)`
`.code_metadata(code_metadata)`
`.upgrade_async_call_and_exit()` | Upgrades are async calls. | +| `.execute_on_dest_context()` | `.sync_call()` | Also add result handlers for decoding the result. | +| `.execute_on_dest_context`
`_with_back_transfers()` | `.returns(ReturnsBackTransfers)`
`.sync_call()` | Add additional result handlers for decoding the result. | diff --git a/docs/developers/tutorials/your-first-dapp.md b/docs/developers/tutorials/your-first-dapp.md index 4c14374bb..c22696725 100644 --- a/docs/developers/tutorials/your-first-dapp.md +++ b/docs/developers/tutorials/your-first-dapp.md @@ -5,273 +5,325 @@ title: Build a dApp in 15 minutes [comment]: # (mx-abstract) -Let's build your first decentralized App on the MultiversX Blockchain! +Let's build your first decentralized application(dApp) on the MultiversX Blockchain! -### Prerequisites - -[comment]: # (mx-context-auto) - -#### mxpy - -We're going to use [**mxpy**](/sdk-and-tools/sdk-py/mxpy-cli) to create a wallet and deploy a contract. Follow the installation guide [here](/sdk-and-tools/sdk-py/installing-mxpy) - make sure to use the latest version available. - -[comment]: # (mx-context-auto) - -#### Rust +## Prerequisites +:::important +Before starting this tutorial, make sure you have the following: +- `stable` Rust version `≥ 1.78.0` (install via [rustup](https://docs.multiversx.com/sdk-and-tools/troubleshooting/rust-setup/#without-mxpy)) +- `multiversx-sc-meta` (cargo install [multiversx-sc-meta](https://docs.multiversx.com/developers/meta/sc-meta-cli/#introduction)) +- `Node.js` with version `≥ 20`(guide [here](https://nodejs.org/en/download/package-manager)) +- `yarn` ([npm install --global yarn](https://classic.yarnpkg.com/lang/en/docs/install/#debian-stable) ) +::: -Install **Rust** and [**sc-meta**](/developers/meta/sc-meta) as depicted [here](/sdk-and-tools/troubleshooting/rust-setup). They are required to build smart contracts. +You are going to use `sc-meta` to: +1. **Create a wallet** to handle your transactions. +2. **Build** and **deploy** a contract. [comment]: # (mx-context-auto) -### **dApp Description** +## Description ![img](/developers/tutorial/dapp-problem.png) -The **Ping-Pong app** is a very simple decentralized application that will allow the user to deposit a specific number of tokens (default is 1 xEGLD) to a smart contract address and to lock them for a specific amount of time (default is 10 minutes). After this time interval passes, the user can claim back the same amount of tokens. -Sending funds to the contract is called `ping`. -Claiming the same amount back is called `pong`. +The **Ping-Pong app** is a very simple decentralized application that allows users to deposit a specific number of tokens to a smart contract address and to lock them for a specific amount of time. After this time interval passes, users can claim back the same amount of tokens. -Other rules: +Endpoints available: +- `ping`: sending funds to the contract. +- `pong`: claiming the same amount back. -- the user can only `ping` **once** before `pong` (so no multiple pings). -- only **the set amount** can be `ping`-ed, no more, no less. +Rules: +- Each user can only `ping` once before they `pong`. +- The `ping` amount must be exactly the specified value—no more, no less. +- `pong` becomes available only after a set waiting period following a `ping`. [comment]: # (mx-exclude-context) -## **MultiversX dApp architecture** +## Architecture ![img](/developers/tutorial/dapp-architecture.png) [comment]: # (mx-context-auto) -### **Application Layer (The Frontend)** +### Application Layer - Frontend -For the web app we'll have two pages: +For the web application, we will have two pages: -- The _Sign in_ page - here we can authenticate with ledger, web wallet or with xPortal app -- The _Dashboard_ page - here we can either ping or pong, if we already deposited, then we will see a countdown timer until the time interval clears out. +- **Sign in** - The page where you can authenticate using the **xPortal app**, **Ledger**, **DeFi Wallet**, **xAlias**, **Web Wallet**, **Passkey Proxy** or with **Metamask proxy**; +- **Dashboard** - Here, you can either `ping` or `pong`. If you have already deposited, you will see a countdown timer until the time interval resets. [comment]: # (mx-context-auto) -### **Blockchain Layer (The Backend)** +### Blockchain Layer - Backend + +You will interact with a smart contract that provides the following features: +- `ping`: users send tokens to the contract, locking them for a specific period; +- `pong`: users retrieve their funds, but only after the lock period expires. + +The contract also includes several views, storage mappers and one event: +- `didUserPing`: **view** that tells if a specific user has already `ping`-ed (_true_) or not (_false_); +- `getPongEnableTimestamp`: **view** that provides the timestamp when `pong` will be available for a given address; +- `getTimeToPong`: **view** that shows the remaining time until `pong` is enabled for a specific address; +- `getAcceptedPaymentToken`: **storage mapper** that saves the token type allowed for deposits; +- `getPingAmount`: **storage mapper** that records recording the total amount of tokens deposited; +- `getDurationTimestamp`: **storage mapper** that saves the lock duration (in seconds) before `pong` can be called after a `ping`; +- `getUserPingTimestamp`: **storage mapper** that saves the timestamp of the block where the user `ping`-ed; +- `pongEvent`: **event** that signals a successful `pong` by the user with amount. -We will create a smart contract that can handle the deposit (`ping`), claim (`pong`) and status actions (`did_user_ping`, `get_time_to_pong`). -Let's say that, for now, this smart contract plays the role of an API in a dApp. Also, this is where our business logic resides. +Think of this smart contract as the API for our dApp, handling all the core business logic. -The MultiversX _devnet_ is a public test network maintained by our community where any developer can test their smart contracts and dApps in a real world environment. +To test it out, we will use [MultiversX Blockchain Devnet Explorer](https://devnet-explorer.multiversx.com/)—a public test network maintained by our community. [comment]: # (mx-context-auto) -## **Set up the environment** +## Set up the environment -Let's set up the environment for getting your first dapp up and running. +Let's set up the environment for getting your first dApp up and running. [comment]: # (mx-context-auto) -### **Project Structure** +### Project Structure -First let's create a new folder for our project, I'll name it `ping-pong`. +Start by creating a new folder for your project. Let's call it `ping-pong`. ```sh -mkdir -p ping-pong/wallet +mkdir -p ping-pong cd ping-pong ``` -In the end, we'll have three subfolders: wallet, contract and dapp. For convenience, we'll save our owner's PEM wallet in the wallet folder. +By the time we are done, our project will have three subfolders: wallet, contract, and dapp. ![img](/developers/tutorial/folder-structure.png) [comment]: # (mx-context-auto) -### **Create the owner wallet** +### Create wallet -The smart contract can only be deployed on the blockchain by an owner, so let's create an owner's wallet **PEM file**. The owner can also update the contract, later on, if needed. Keep in mind we only use PEM wallets for testing and playing around with non-production code. For real applications please follow best practices, use secure wallets that can be generated [here](https://wallet.multiversx.com). +To deploy a smart contract to the blockchain, you will need a wallet-a PEM file is recommended for simplicity and ease of testing. -First, make sure you are in the `ping-pong` folder. +Make sure you are in the `ping-pong` folder. ```sh -mxpy wallet new --format pem --outfile=./wallet/wallet-owner.pem +mkdir -p wallet +sc-meta wallet new --format pem --outfile ./wallet/wallet-owner.pem ``` -In order to initiate transactions on the blockchain, we need some funds, every transaction costs a very small fee, on the blockchain this is called **gas**. -On the devnet wallet we have a **faucet** that allows you to get free test funds for our applications. We can request 5 xEGLD every 24 hours, so let's request 5 xEGLD now. You can log in with your PEM using the newly generated PEM file [here](https://devnet-wallet.multiversx.com/unlock/pem). Use the faucet from the menu as you see below and you are all set. - -![img](/developers/tutorial/faucet-screenshot.png) +:::info +PEM wallets are recommended only for testing and experimenting with non-production code. For real applications, always follow best practices and use secure wallets that can be generated [here](https://wallet.multiversx.com). +::: -[comment]: # (mx-exclude-context) +To initiate transactions on the blockchain, your wallet needs funds to cover transaction fees, commonly referred to as **gas**. -## **The Blockchain Layer - The Smart Contract** +The [MultiversX Devnet](https://devnet-wallet.multiversx.com/dashboard) offers a **faucet** where you can claim **5 EGLD every 24 hours**. Here’s how to fund your wallet: +1. Go to [Devnet Wallet MultiversX](https://devnet-wallet.multiversx.com/unlock/pem) and log in using your newly generated **PEM** file; +2. Once logged in, open the **Faucet** from the **Tools**; +3. Request **5 xEGLD** to top up your wallet with test EGLD. -Our owner wallet is completely set now, we can move on to our backend, the blockchain layer. +![img](/developers/tutorial/faucet-screenshot.png) -[comment]: # (mx-context-auto) +[comment]: # (mx-exclude-context) -### **Clone the template** +## The Blockchain Layer -Clone the Ping-Pong Sample Smart Contract +With the wallet setup complete, let's move on to the backend—the blockchain layer. -Let's start with our smart contract. We'll first clone the sample contract repository from here [https://github.com/multiversx/mx-ping-pong-sc](https://github.com/multiversx/mx-ping-pong-sc) +Let's start with the smart contract. You will first clone the Ping-Pong sample contract repository from [here](https://github.com/multiversx/mx-ping-pong-sc). -Also make sure you are still in the `ping-pong` folder. +Make sure you are still in the **ping-pong** folder. ```sh git clone https://github.com/multiversx/mx-ping-pong-sc contract ``` +This will create a **contract** folder within ping-pong, containing all the necessary files for the Ping-Pong smart contract. [comment]: # (mx-context-auto) -### **Build the Smart Contract** +### Build the Smart Contract -We now have the source code for the smart contract, but we need to compile it into a _binary_ that the MultiversX Virtual Machine can run. The VM can run Web Assembly code, so we need to compile our Rust source code into Web Assembly (WASM). +Now that you have the source code for the smart contract, you need to compile it into a **binary** that the **MultiversX Virtual Machine** can execute. Since the VM runs Web Assembly (WASM) code, you need to compile our Rust source code into a WASM file. -Run the following command in order to build the rust smart contract into a _wasm file_. +At path `ping-pong/`, run the following command to build the smart contract into a WASM file. ```sh cd contract/ping-pong sc-meta all build ``` -When running the build command, a WASM file gets created: `output/ping-pong.wasm`. This file contains the bytecode of our smart contract. +After running the build command, a WASM file will be created at `output/ping-pong.wasm`. + +This file contains the bytecode for the smart contract, ready to be deployed on the blockchain. [comment]: # (mx-context-auto) -### **Customize and Deploy** +### Deploy the Smart Contract -Deploy the smart contract on MultiversX Devnet -Next step is to deploy the contract to the blockchain. +Next, let's deploy the smart contract to the blockchain. -Make sure your _owner wallet PEM file_ is in the right folder, the smart contract is built and let's get to the deployment. -For now let's continue with the default values. -We will run: +Make sure `wallet_owner.pem` is in the `wallet/` folder and that the smart contract is built. -```bash -mxpy --verbose contract deploy \ - --bytecode output/ping-pong.wasm \ - --pem ../../wallet/wallet-owner.pem \ - --recall-nonce \ - --gas-limit 60000000 \ - --arguments 1000000000000000000 600 \ - --chain D \ - --proxy https://devnet-api.multiversx.com \ - --outfile deploy-devnet.interaction.json \ - --send -``` +Before deploying, you will need to modify the wallet from which transactions are made. Currently, they are made from a test wallet. To use the wallet you created [earlier](./your-first-dapp.md#create-wallet), you will need to make the following changes: -We'll take a look at the log output. We have 2 elements that need our attention: the contract address and the transaction hash. Let's check them in the [Devnet Explorer](https://devnet-explorer.multiversx.com). +At the path `/ping-pong/contract/ping-pong/interactor/src` you will run: -Devnet Explorer will be your best friend in developing dApps on the MultiversX Blockchain, as you'll first deploy and test your dApps on Devnet. +In the file `interact.rs` located at the path `/ping-pong/contract/ping-pong/interactor/src`, the variable `alice_wallet_address` from `new` function will be modified from: -```sh -INFO:accounts:Account.sync_nonce() -INFO:accounts:Account.sync_nonce() done: 32 -INFO:cli.contracts:Contract address: erd1qqqqqqqqqqqqqpgq0hmfvuygs34cgqsvgg6fpq9c5mffh4y04cysagr6cn -INFO:utils:View this contract address in the MultiversX Devnet Explorer: https://devnet-explorer.multiversx.com/accounts/erd1qqqqqqqqqqqqqpgq0hmfvuygs34cgqsvgg6fpq9c5mffh4y04cysagr6cn -INFO:transactions:Transaction.send: nonce=32 -INFO:transactions:Hash: ee84f3e833d439e159c9619fd76e26d2afcdad62c197d87e4940072f18558153 -INFO:utils:View this transaction in the MultiversX Devnet Explorer: https://devnet-explorer.multiversx.com/transactions/ee84f3e833d439e159c9619fd76e26d2afcdad62c197d87e4940072f18558153 +```rust title="Before" +let alice_wallet_address = interactor.register_wallet(test_wallets::alice()).await; +``` +```rust title="After" +let alice_wallet_address = interactor + .register_wallet(Wallet::from_pem_file("/ping-pong/wallet/wallet-owner.pem").unwrap()) + .await; ``` +This next command deploys the Ping-Pong contract with the following settings: +- Ping Amount: **1 EGLD**. +- Lock Duration: **180 seconds** (3 minutes). + +```bash +cargo run deploy --ping-amount 1000000000000000000 --duration-in-seconds 180 +``` + +```sh title=output + Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.48s + Running `/ping-pong/contract/target/debug/ping-pong-interact deploy --ping-amount 1000000000000000000 --duration-in-seconds 180` +sender's recalled nonce: 12422 +-- tx nonce: 12422 +sc deploy tx hash: b6ca6c8e6ac54ed168bcd6929e762610e2360674f562115107cf3702b8a22467 +deploy address: erd1qqqqqqqqqqqqqpgqymj43x6anzr38jfz7kw3td2ew33v9jtrd8sse5zzk6 +new address: erd1qqqqqqqqqqqqqpgqymj43x6anzr38jfz7kw3td2ew33v9jtrd8sse5zzk6 +``` +Once the command runs, review the log output carefully. Two key details to note: +- Contract Address: in the example presented below is erd1qqqqqqqqqqqqqpgqymj43x6anzr38jfz7kw3td2ew33v9jtrd8sse5zzk6 +- Transaction Hash: in the example presented below is b6ca6c8e6ac54ed168bcd6929e762610e2360674f562115107cf3702b8a22467 + +We will take a look at the transaction details. Let's check them in the [Devnet Explorer](https://devnet-explorer.multiversx.com). -The smart contract is now deployed on the blockchain. We can interact with it using blockchain transactions in order to invoke smart contract functions `ping` or `pong`. +![img](/developers/tutorial/transaction_details.png) -The smart contract source code resides in -`ping-pong-smart-contract/ping-pong/src/ping_pong.rs` +The Devnet Explorer will be your go-to tool for developing dApps on the MultiversX Blockchain. It allows you to test and monitor your deployments on the Devnet, ensuring everything works as expected. -There are two main functions: `ping` and `pong`, these are invoked using blockchain transactions. +With the smart contract successfully deployed, you can now interact with it using blockchain transactions to invoke its main functions: `ping` and `pong`. -We also have two other functions defined in the smart contract: `get_time_to_pong` and `did_user_ping`, these view functions are invoked using **MultiversX API** (*https://devnet-api.multiversx.com/vm-values/query*). +The smart contract source code resides in `ping-pong/contract/ping-pong/src/ping_pong.rs`. + +The contract includes several view functions for querying information. These are invoked using the [MultiversX API](https://devnet-gateway.multiversx.com/#/vm-values/post_vm_values_query): +- `didUserPing`; +- `getPongEnableTimestamp`; +- `getTimeToPong`; +- `getAcceptedPaymentToken`; +- `getPingAmount`; +- `getDurationTimestamp`; +- `getUserPingTimestamp`. [comment]: # (mx-exclude-context) -## **The Application Layer - The Web App** +## The Application Layer -All right, let's move on to the application layer. +Now that the backend is ready, let’s move on to the application layer! [comment]: # (mx-context-auto) -### **Clone the Sample App** +### Set Up the dApp Template -First make sure to go back to the root `ping-pong` folder. +To get started, navigate back to the root `ping-pong` folder. -We will clone a very simple dApp template that implements the calls to our newly deployed smart contract. +Next, clone a simple dApp template that includes the necessary calls to interact with your newly deployed smart contract: ```sh git clone https://github.com/multiversx/mx-template-dapp dapp cd dapp ``` -[comment]: # (mx-context-auto) - -### **Configure the app** - -Use the preferred editor and customize the Smart Contract Address located in `src/config/config-devnet.tsx` - -```sh -code . -``` +Use the preferred editor and customize the Smart Contract address located in `src/config/config.devnet.ts` -Then edit this instruction, and change it to the contract address that was shown after `mxpy contract deploy`: +Then edit this instruction and change it to the contract address that you created in the [deploy step](./your-first-dapp.md#deploy-the-smart-contract). ![img](/developers/tutorial/config-screenshot.png) -[comment]: # (mx-context-auto) - -### **Build the dApp** - :::important -[Please make sure you have **yarn installed**](https://classic.yarnpkg.com/lang/en/docs/install) on your machine. +Make sure you have [**yarn installed**](https://classic.yarnpkg.com/lang/en/docs/install) on your machine. ::: -We'll first install the dependencies: +Navigate to the `ping-pong/dapp` folder and install the required dependencies: ```sh -yarn install +npm install --global yarn +yarn add vite --dev ``` -and then we'll start a development server to test our new dApp +### Start the Development Server + +To test your dApp locally, start a development server with the following command: ```sh yarn start:devnet ``` -**Run it on local machine (or host it on your server)** -If you start the development server on the local machine, then open [http://localhost:3000](http://localhost:3000) in your browser. -If you start it on your own server, then you can access [http://ip:3000](http://ip:3000). The built version only contains static files, so any hosting provider will do. +### Running and Accessing the dApp + +If you run the development server on your local machine, simply open [https://localhost:3000](https://localhost:3000) in your browser. + +If the server is hosted on a remote machine, access it using the server's IP address, like [http://ip:3000](http://ip:3000). + +The production build of the app consists only of static files, so you can deploy it on any hosting platform you prefer. -After you start the development server, when you see the Sign in screen, this means the application is up and running. +Once the development server is up and running, seeing the _Template dApp_ screen confirms that your application is live and ready! + +![img](/developers/tutorial/tutorial_dapp_page.png) [comment]: # (mx-context-auto) -## **Test Your Application** +## Try your Application + +You will log in using the wallet created [previously](./your-first-dapp.md#create-wallet). + +To do this, you will press **Connect** button and then choose **Web Wallet** option. + +![img](/developers/tutorial/wallet_connect.png) -We will sign in with a test wallet. -You can reuse the same owner's wallet if you want to, or create a new one, following the same steps you followed when creating the owner's wallet. +After you access **Web Wallet** connection, you will be forwarded to login on [Multiversx Wallet](https://devnet-wallet.multiversx.com/unlock/pem). + +![img](/developers/tutorial/wallet_login.png) + +You will choose **PEM** option to login. [comment]: # (mx-context-auto) -### **Ping Feature** +### Ping Feature + +After signing in, you will be directed to the dashboard where the **Ping** button will be visible. -After signing in, we'll see the dashboard where we can see the **Ping** button. +![img](/developers/tutorial/dapp_dashboard.png) -Click the Ping button and you'll be redirected to the authentication page on the web wallet, maiar wallet or your authentication device. -A new transaction will be created and you'll be asked to confirm it. This transaction transfers balance from your wallet to the smart contract address. Those funds will be locked for the specified period of time. Pay attention to the data field, where we call the smart contract function `ping`. -After you confirm the transaction, a success message will appear and the funds are locked. +Click the **Ping** button and you will be redirected to the authentication page on the web wallet. -**Wait the time interval** -You can see the amount of time you'll have to wait until you can pong. +A new transaction will be created, and you will be asked to confirm it. This transaction transfers the balance from your wallet to the smart contract address. Those funds will be locked for a specified period of time. Pay attention to the data field, where you call the smart contract function `ping`. + +After you confirm the transaction, a success message will appear and the funds are locked. You can view the transaction shown in the image [here](https://devnet-explorer.multiversx.com/transactions/af85e8a26f78f1a26d03377b85bd611b218d5e864d5c8788121beb74a9972aee). + +![img](/developers/tutorial/success_tx.png) +You can see **the amount of time** you will have to wait until you can **Pong**. [comment]: # (mx-context-auto) -### **Pong Feature** +### Pong Feature + +After the time interval has passed, you can claim the funds by clicking the **Pong** button. + +![img](/developers/tutorial/pong_button.png) -After the time interval has passed, you can claim the funds by clicking the Pong button. -Another blockchain transaction will wait to be processed, this time the amount will be zero, as we only have to invoke the `pong` function (specified in the _data_ field). -The transaction will trigger a success message and the funds will be returned to the wallet. +Another blockchain transaction will wait to be processed and again you will be asked to confirm it. This time the amount will be zero, as you only have to invoke the `pong` function (specified in the _data_ field). + +Once the transaction is complete, a **success** message will appear, and your funds will be returned to your wallet. You can view the transaction shown in the image [here](https://devnet-explorer.multiversx.com/transactions/3959fb90f08265465cf7c87314ff595357e984d636b023fa530a93733d0c3d6e). + +![img](/developers/tutorial/pong_tx.png) [comment]: # (mx-context-auto) -## **Where to go next?** +## Where to go next? The purpose of this guide is to provide a starting point for you to discover the MultiversX technology capabilities and devkit. Keep reading the next docs to dive in deeper. + We welcome your questions and inquiries on Stack Overflow: [https://stackoverflow.com/questions/tagged/multiversx](https://stackoverflow.com/questions/tagged/multiversx). -Break down this guide and learn more about how to extend the smart contract, the wallet and the MultiversX tools. [https://docs.multiversx.com](/) +Dive into more advanced topics and discover how to extend the smart contract, customize the wallet, and leverage MultiversX tools here: [https://docs.multiversx.com](/). diff --git a/docs/sdk-and-tools/sdk-py/mxpy-cli.md b/docs/sdk-and-tools/sdk-py/mxpy-cli.md index 94c1d581b..1aa633cc8 100644 --- a/docs/sdk-and-tools/sdk-py/mxpy-cli.md +++ b/docs/sdk-and-tools/sdk-py/mxpy-cli.md @@ -530,35 +530,58 @@ If your address is guarded by another wallet, you'll still need to provide the ` [comment]: # (mx-context-auto) -## Relayed V3 transactions +## Relayed transactions V3 Relayed transactions are transactions with the fee paid by a so-called relayer. In other words, if a relayer is willing to pay for a transaction, it is not mandatory for the sender to have any EGLD for fees. To learn more about relayed transactions check out [this page](/developers/relayed-transactions/). -In this section we'll see how we can send `Relayed V3` transactions using `mxpy`. For a more detailed look on `Relayed V3` transactions, take a look [here](/developers/relayed-transactions/#relayed-transactions-version-3). For these kind of transactions a new transaction field has been introduced, called `innerTransactions`. In this example we'll see how we can create both the inner transactions and the relayed transaction. +In this section we'll see how we can send `Relayed V3` transactions using `mxpy`. For a more detailed look on `Relayed V3` transactions, take a look [here](/developers/relayed-transactions/#relayed-transactions-version-3). For these kind of transactions two new transaction fields were introduced, `relayer` and `relayerSignature`. In this example we'll see how we can create the relayed transaction. -### Creating the inner transactions +For this, a new command `mxpy tx relay` has been added. The command can be used to relay a previously signed transaction. The saved transaction can be loaded from a file using the `--infile` argument. -We can simply create the inner transactions the same way we did above, by using the `mxpy tx new` command. The only difference is that we'll have to provide an additional argument called `--inner-transactions-outfile`, which represents the file where the inner transactions are saved to be later used by the relayer. To keep it simple, we'll send 1 EGLD from Alice to Bob, and Carol will be the relayer. To create the EGLD transfer transaction from Alice to Bob, we run the following command: +There are two options when creating the relayed transaction: +1. Create the relayed transaction separately. (Sender signature and relayer signature are added by different entities.) +2. Create the complete relayed transaction. (Both signatures are added by the same entity.) + +### Creating the inner transaction + +The inner transaction is any regular transaction, with the following notes: +- relayer address must be added +- extra 50000 (base cost) gas must be added for the relayed operation. For more details on how the gas is computed, check out [this page](/developers/relayed-transactions/#relayed-transactions-version-3). + +This can be generated through `mxpy tx new` command. A new argument `--relayer` has been added for this feature. ```sh mxpy tx new --pem ~/multiversx-sdk/testwallets/latest/users/alice.pem --recall-nonce \ --receiver erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx \ - --gas-limit 50000 --value 1000000000000000000 \ + --gas-limit 100000 --value 1000000000000000000 \ --proxy https://devnet-gateway.multiversx.com --chain D \ - --relayer erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8 - --inner-transactions-outfile inner_transactions.json + --relayer erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8 \ + --outfile inner_tx.json ``` -After creating the inner transaction, we are ready to create the relayed transaction. We have to keep in mind that for `Relayed V3` transactions, the receiver has to be the same as the relayer, in our case, Carol. Another requirement is that the relayed transaction has to have enough gas. The gas is computed by multiplying the base cost (50_000) with the number of inner transactions plus the gasLimit for each inner transaction. For more details on how the gas is computed, check out [this page](/developers/relayed-transactions/#relayed-transactions-version-3). +After creating the inner transaction, we are ready to create the relayed transaction. + +### Creating the relayed transaction We can create the relayed transaction by running the following command: ```sh -mxpy tx new --pem ~/multiversx-sdk/testwallets/latest/users/carol.pem --recall-nonce \ - --receiver erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8 \ - --gas-limit 1000000 --value 0 \ +mxpy tx relay --relayer-pem ~/multiversx-sdk/testwallets/latest/users/carol.pem \ + --proxy https://devnet-gateway.multiversx.com --chain D \ + --infile inner_tx.json \ + --send +``` + +### Creating the relayed transaction in one step + +This can be done through `mxpy tx new` command, as follows: +```sh +mxpy tx new --pem ~/multiversx-sdk/testwallets/latest/users/alice.pem --recall-nonce \ + --receiver erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx \ + --relayer erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8 \ + --relayer-pem ~/multiversx-sdk/testwallets/latest/users/carol.pem \ + --gas-limit 100000 --value 1000000000000000000 \ --proxy https://devnet-gateway.multiversx.com --chain D \ - --inner-transactions inner_transactions.json \ --send ``` diff --git a/docs/tokens/nft-tokens.mdx b/docs/tokens/nft-tokens.mdx index 27f75929e..c97674ee6 100644 --- a/docs/tokens/nft-tokens.mdx +++ b/docs/tokens/nft-tokens.mdx @@ -15,9 +15,6 @@ import TableWrapper from "@site/src/components/TableWrapper"; MultiversX NFTs(non-fungible tokens) are a breed of digital assets that are revolutionizing the world of art, collectibles, and more. These NFTs are unique, one-of-a-kind tokens that are built on blockchain technology, allowing for secure ownership and transfer of these assets. With MultiversX NFTs, every token is assigned a unique identification code(ticker) and metadata that distinguishes it from every other token, making each NFT truly one-of-a-kind. Read the full page for a comprehensive guide on how to brand, issue, transfer, assign roles and many other features, for both NFTs and SFTs. -:::note -Features related to dynamic NFTs are not yet available on **Mainnet**. See [Spica Protocol Upgrade](https://governance.multiversx.com/proposal/erd1qqqqqqqqqqqqqpgq4qvrwlr2e6ld50f3qfc94am38p8298kthg4s3f0vfn/1). -::: [comment]: # (mx-context-auto) diff --git a/static/developers/tutorial/dapp_dashboard.png b/static/developers/tutorial/dapp_dashboard.png new file mode 100644 index 000000000..a978f3263 Binary files /dev/null and b/static/developers/tutorial/dapp_dashboard.png differ diff --git a/static/developers/tutorial/pong_button.png b/static/developers/tutorial/pong_button.png new file mode 100644 index 000000000..a0355e541 Binary files /dev/null and b/static/developers/tutorial/pong_button.png differ diff --git a/static/developers/tutorial/pong_tx.png b/static/developers/tutorial/pong_tx.png new file mode 100644 index 000000000..8d66dd1e3 Binary files /dev/null and b/static/developers/tutorial/pong_tx.png differ diff --git a/static/developers/tutorial/success_tx.png b/static/developers/tutorial/success_tx.png new file mode 100644 index 000000000..e83168a0f Binary files /dev/null and b/static/developers/tutorial/success_tx.png differ diff --git a/static/developers/tutorial/transaction_details.png b/static/developers/tutorial/transaction_details.png new file mode 100644 index 000000000..c9707f62f Binary files /dev/null and b/static/developers/tutorial/transaction_details.png differ diff --git a/static/developers/tutorial/tutorial_dapp_page.png b/static/developers/tutorial/tutorial_dapp_page.png new file mode 100644 index 000000000..de64accc9 Binary files /dev/null and b/static/developers/tutorial/tutorial_dapp_page.png differ diff --git a/static/developers/tutorial/wallet_connect.png b/static/developers/tutorial/wallet_connect.png new file mode 100644 index 000000000..9468533fe Binary files /dev/null and b/static/developers/tutorial/wallet_connect.png differ diff --git a/static/developers/tutorial/wallet_login.png b/static/developers/tutorial/wallet_login.png new file mode 100644 index 000000000..f373a1d5c Binary files /dev/null and b/static/developers/tutorial/wallet_login.png differ