diff --git a/cartesi-rollups_versioned_docs/version-1.5/tutorials/counter.md b/cartesi-rollups_versioned_docs/version-1.5/tutorials/counter.md index 803616a0..5e79cf2e 100644 --- a/cartesi-rollups_versioned_docs/version-1.5/tutorials/counter.md +++ b/cartesi-rollups_versioned_docs/version-1.5/tutorials/counter.md @@ -112,11 +112,11 @@ cartesi build - Expected Logs: ```shell -user@user-MacBook-Pro counter % cartesi build -(node:4460) ExperimentalWarning: Importing JSON modules is an experimental feature and might change at any time -(Use `node --trace-warnings ...` to show where the warning was created) -✔ Build drives - ✔ Build drive root +View build details: docker-desktop://dashboard/build/multiarch/multiarch0/vzzzuxvcznba66icpyk3wyde9 + +What's next: + View a summary of image vulnerabilities and recommendations → docker scout quickview +copying from tar archive /tmp/input . / \ @@ -132,14 +132,13 @@ user@user-MacBook-Pro counter % cartesi build [INFO rollup_http_server::http_service] starting http dispatcher http service! [INFO actix_server::builder] starting 1 workers [INFO actix_server::server] Actix runtime found; starting in Actix runtime -[INFO actix_server::server] starting service: "actix-web-service-127.0.0.1:5004", workers: 1, listening on: 127.0.0.1:5004 [INFO rollup_http_server::dapp_process] starting dapp: python3 dapp.py INFO:__main__:HTTP rollup_server url is http://127.0.0.1:5004 INFO:__main__:Sending finish -Manual yield rx-accepted (1) (0x000020 data) -Cycles: 8108719633 -8108719633: 107174e04a294787e22b6864c61fedd845833e5c8bc9a244480f2996ddabb3c7 +Manual yield rx-accepted (0x100000000 data) +Cycles: 3272156820 +3272156820: 3903552ee499ef4a10b2c8ffba6b8d49088a0a8b9137b8d10be359910080432a Storing machine: please wait ``` @@ -154,22 +153,15 @@ cartesi run If the `run` command is successful, you should see logs similar to this: ```bash -user@user-MacBook-Pro counter % cartesi run -(node:5404) ExperimentalWarning: Importing JSON modules is an experimental feature and might change at any time -(Use `node --trace-warnings ...` to show where the warning was created) -WARNING: default block is set to 'latest', production configuration will likely use 'finalized' -[+] Pulling 4/0 - ✔ database Skipped - Image is already present locally - ✔ rollups-node Skipped - Image is already present locally - ✔ anvil Skipped - Image is already present locally - ✔ proxy Skipped - Image is already present locally -✔ counter starting at http://127.0.0.1:6751 -✔ anvil service ready at http://127.0.0.1:6751/anvil -✔ rpc service ready at http://127.0.0.1:6751/rpc -✔ inspect service ready at http://127.0.0.1:6751/inspect/counter -✔ counter machine hash is 0x107174e04a294787e22b6864c61fedd845833e5c8bc9a244480f2996ddabb3c7 -✔ counter contract deployed at 0x94b32605a405d690934eb4ecc91856febfa747cc -(l) View logs (b) Build and redeploy (q) Quit +Attaching to prompt-1, validator-1 +validator-1 | 2025-11-24 17-06-12 info remote-cartesi-machine pid:108 ppid:67 Initializing server on localhost:0 +prompt-1 | Anvil running at http://localhost:8545 +prompt-1 | GraphQL running at http://localhost:8080/graphql +prompt-1 | Inspect running at http://localhost:8080/inspect/ +prompt-1 | Explorer running at http://localhost:8080/explorer/ +prompt-1 | Bundler running at http://localhost:8080/bundler/rpc +prompt-1 | Paymaster running at http://localhost:8080/paymaster/ +prompt-1 | Press Ctrl+C to stop the node ``` ## Interacting with your Counter Application @@ -181,44 +173,43 @@ Interacting with your Counter application could be achieved either through initi We start by querying the current count value, this is done by making an inspect request to the counter application running locally, to achieve this we run the below command in a new terminal: ```bash -curl -X POST http://127.0.0.1:6751/inspect/counter \ - -H "Content-Type: application/json" \ - -d '{""}' +curl http://localhost:8080/inspect/counter ``` -:::note Inspect endpoint -Please note that if your application is running on a different port or your application is not named `counter` as in the guide, then you'll need to replace the inspect endpoint `http://127.0.0.1:6751/inspect/counter` with the endpoint provided after running the `cartesi run` command. -::: - -On success, we receive a confirmation response from the HTTP server, something similar to `{"status":"Accepted","reports":null,"processed_input_count":0}`, then on the terminal running our application when we press the `l` key to access our application logs we should get a log confirming that our application received the inspect call and should also contain a log of the current count value. +On success, we receive a confirmation response from the HTTP server, something similar to `{"status":"Accepted","exception_payload":null,"reports":[],"processed_input_count":0}`, then on the terminal running our application we should get a log confirming that our application received the inspect call and should also contain a log of the current count value. ```bash -[INFO rollup_http_server::http_service] received new request of type INSPECT -inspect_request.payload_length: 4 -[INFO actix_web::middleware::logger] 127.0.0.1 "POST /finish HTTP/1.1" 200 64 "-" "python-requests/2.31.0" 0.020096 -INFO:__main__:Received finish status 200 -INFO:__main__:Received inspect request data {'payload': '0x7b22227d'} -INFO:__main__:Current counter value: 0 -INFO:__main__:Sending finish -2025-11-09T17:47:44.661 INF Request executed service=inspect status=Accepted application=counter +validator-1 | [INFO rollup_http_server::http_service] Received new request of type INSPECT +validator-1 | [INFO actix_web::middleware::logger] 127.0.0.1 "POST /finish HTTP/1.1" 200 70 "-" "python-requests/2.31.0" 0.001536 +validator-1 | INFO:__main__:Received finish status 200 +validator-1 | INFO:__main__:Received inspect request data {'payload': '0x636f756e746572'} +validator-1 | INFO:__main__:Current counter value: 0 +validator-1 | INFO:__main__:Sending finish ``` -As seen in the third to last line of our received log, we can see the `count value` returned to be `0` +As seen in the second to last line of our received log, we can see the `counter value` returned to be `0` ### 2. Increase count value Now that we've confirmed our count value to be zero (0), we would be sending an advance request using the CLI to increase the value of our counter by running the below command: ```bash -cartesi send random_text +cartesi send generic ``` -The above command sends an advance request with the payload "random_text" to our application, which ignores this payload then proceeds to increase out count value by `one (1)`, if this command is successful and our application process this request graciously, we should get a log similar to what's presented below on the terminal running our application: +We then proceed by selecting Foundry and pressing Enter twice to accept the default RPC URL. Next, we choose Mnemonic as the authentication method and again press Enter twice to use the default mnemonic and wallet address. After that, we press Enter once more to confirm the application address. + +Once the basics are set, we navigate to and select String encoding as the input format. Finally, we type any random string (e.g., increase), press Enter, and the CLI sends the request to the application. + +The above process sends an advance request with the payload "increase" to our application, which ignores this payload then proceeds to increase out count value by `one (1)`, if this command is successful and our application process this request graciously, we should get a log similar to what's presented below on the terminal running our application: ```bash -INFO:__main__:Received advance request data {'metadata': {'chain_id': 13370, 'app_contract': '0x9d40cfc42bb386b531c5d4eb3ade360f4105c4a3', 'msg_sender': '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', 'block_number': 630, 'block_timestamp': 1762711409, 'prev_randao': '0x63a2bb3993d9f9c371624f995a10a6f493e33c2535e62b32fee565f812b4c4ab', 'input_index': 0}, 'payload': '0x72616e646f6d5f74657874'} -INFO:__main__:Counter increment requested, new count value: 1 -INFO:__main__:Sending finish +validator-1 | [INFO rollup_http_server::http_service] Received new request of type ADVANCE +validator-1 | [INFO actix_web::middleware::logger] 127.0.0.1 "POST /finish HTTP/1.1" 200 210 "-" "python-requests/2.31.0" 0.001664 +validator-1 | INFO:__main__:Received finish status 200 +validator-1 | INFO:__main__:Received advance request data {'metadata': {'msg_sender': '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', 'epoch_index': 0, 'input_index': 0, 'block_number': 2408, 'timestamp': 1764015746}, 'payload': '0x6f6b6179'} +validator-1 | INFO:__main__:Counter increment requested, new count value: 1 +validator-1 | INFO:__main__:Sending finish ``` The above logs prove that out application received the advance request, increased our count value, logs the updated count value then finishes that request successfully. @@ -226,14 +217,12 @@ The above logs prove that out application received the advance request, increase As seen in the second to last line of the log, our count value has been increased from 0 to 1. To confirm this increase, we can run an inspect request once more to verify the current count value, and on running the same inspect command as last time, we obtain the updated logs below. ```shell -[INFO rollup_http_server::http_service] received new request of type INSPECT -inspect_request.payload_length: 4 -[INFO actix_web::middleware::logger] 127.0.0.1 "POST /finish HTTP/1.1" 200 64 "-" "python-requests/2.31.0" 0.002048 -INFO:__main__:Received finish status 200 -INFO:__main__:Received inspect request data {'payload': '0x7b22227d'} -INFO:__main__:Current counter value: 1 -INFO:__main__:Sending finish -2025-11-09T18:22:45.142 INF Request executed service=inspect status=Accepted application=counter +validator-1 | [INFO rollup_http_server::http_service] Received new request of type INSPECT +validator-1 | [INFO actix_web::middleware::logger] 127.0.0.1 "POST /finish HTTP/1.1" 200 70 "-" "python-requests/2.31.0" 0.001280 +validator-1 | INFO:__main__:Received finish status 200 +validator-1 | INFO:__main__:Received inspect request data {'payload': '0x636f756e746572'} +validator-1 | INFO:__main__:Current counter value: 1 +validator-1 | INFO:__main__:Sending finish ``` From the latest application logs, it's now clear that the application's count value has been increased from 0 to one, and subsequent advance calls would further increase the count value. diff --git a/cartesi-rollups_versioned_docs/version-1.5/tutorials/marketplace.md b/cartesi-rollups_versioned_docs/version-1.5/tutorials/marketplace.md index f6b01be0..88a4613c 100644 --- a/cartesi-rollups_versioned_docs/version-1.5/tutorials/marketplace.md +++ b/cartesi-rollups_versioned_docs/version-1.5/tutorials/marketplace.md @@ -118,7 +118,7 @@ Since this project would be covering hex payload encoding and decoding, as well
```shell
- npm add viem
+ npm add viem ethers
```
@@ -129,13 +129,11 @@ Since this project would be covering hex payload encoding and decoding, as well
```shell
cat > requirements.txt << 'EOF'
---find-links https://prototyp3-dev.github.io/pip-wheels-riscv/wheels/
-requests==2.32.5
-eth-abi==5.2.0
-eth-utils==5.3.1
-regex==2025.11.3
-pycryptodome==3.23.0
-eth-hash==0.7.1
+requests==2.32.3
+eth-abi==2.2.0
+eth-utils==1.10.0
+eth-hash==0.3.3
+pycryptodome==3.20.0
EOF
```
@@ -146,13 +144,31 @@ EOF
```shell
-cargo add hex serde ethers-core
+cargo add json@0.12
+cargo add hyper@0.14 --features http1,runtime,client
+cargo add tokio@1.32 --features macros,rt-multi-thread
+cargo add ethers-core@0.5.3
+cargo add serde@1.0.228
+cargo add hex@0.4.3
```
+**NOTE::** For python developers, add the below snippet to `line 26` of your Dockerfile. It should come immediately after the line `COPY ./requirements.txt .`.
+
+This command would help install essential compilers to help compile some dependencies we'll be using.
+
+```Dockerfile
+# Install build dependencies for compiling native extensions
+RUN apt-get update && \
+ apt-get install -y --no-install-recommends \
+ build-essential \
+ python3-dev && \
+ rm -rf /var/lib/apt/lists/*
+```
+
## Implement the Application Logic
Based on the programming language you selected earlier, copy the appropriate code snippet, then paste in your local entry point file (`dapp.py` or `src/main.rs` or `src/index.js`), created in the setup step:
@@ -199,10 +215,11 @@ cartesi build
```shell
user@user-MacBook-Pro marketplace % cartesi build
-(node:4460) ExperimentalWarning: Importing JSON modules is an experimental feature and might change at any time
-(Use `node --trace-warnings ...` to show where the warning was created)
-✔ Build drives
- ✔ Build drive root
+View build details: docker-desktop://dashboard/build/multiarch/multiarch0/vzzzuxvcznba66icpyk3wyde9
+
+What's next:
+ View a summary of image vulnerabilities and recommendations → docker scout quickview
+copying from tar archive /tmp/input
.
/ \
@@ -218,14 +235,13 @@ user@user-MacBook-Pro marketplace % cartesi build
[INFO rollup_http_server::http_service] starting http dispatcher http service!
[INFO actix_server::builder] starting 1 workers
[INFO actix_server::server] Actix runtime found; starting in Actix runtime
-[INFO actix_server::server] starting service: "actix-web-service-127.0.0.1:5004", workers: 1, listening on: 127.0.0.1:5004
[INFO rollup_http_server::dapp_process] starting dapp: python3 dapp.py
INFO:__main__:HTTP rollup_server url is http://127.0.0.1:5004
INFO:__main__:Sending finish
-Manual yield rx-accepted (1) (0x000020 data)
-Cycles: 8108719633
-8108719633: 107174e04a294787e22b6864c61fedd845833e5c8bc9a244480f2996ddabb3c7
+Manual yield rx-accepted (0x100000000 data)
+Cycles: 3272156820
+3272156820: 3903552ee499ef4a10b2c8ffba6b8d49088a0a8b9137b8d10be359910080432a
Storing machine: please wait
```
@@ -241,21 +257,15 @@ If the `run` command is successful, you should see logs similar to this:
```bash
user@user-MacBook-Pro marketplace % cartesi run
-(node:5404) ExperimentalWarning: Importing JSON modules is an experimental feature and might change at any time
-(Use `node --trace-warnings ...` to show where the warning was created)
-WARNING: default block is set to 'latest', production configuration will likely use 'finalized'
-[+] Pulling 4/0
- ✔ database Skipped - Image is already present locally
- ✔ rollups-node Skipped - Image is already present locally
- ✔ anvil Skipped - Image is already present locally
- ✔ proxy Skipped - Image is already present locally
-✔ marketplace starting at http://127.0.0.1:6751
-✔ anvil service ready at http://127.0.0.1:6751/anvil
-✔ rpc service ready at http://127.0.0.1:6751/rpc
-✔ inspect service ready at http://127.0.0.1:6751/inspect/marketplace
-✔ marketplace machine hash is 0x107174e04a294787e22b6864c61fedd845833e5c8bc9a244480f2996ddabb3c7
-✔ marketplace contract deployed at 0x94b32605a405d690934eb4ecc91856febfa747cc
-(l) View logs (b) Build and redeploy (q) Quit
+Attaching to prompt-1, validator-1
+validator-1 | 2025-11-24 21-27-29 info remote-cartesi-machine pid:109 ppid:68 Initializing server on localhost:0
+prompt-1 | Anvil running at http://localhost:8545
+prompt-1 | GraphQL running at http://localhost:8080/graphql
+prompt-1 | Inspect running at http://localhost:8080/inspect/
+prompt-1 | Explorer running at http://localhost:8080/explorer/
+prompt-1 | Bundler running at http://localhost:8080/bundler/rpc
+prompt-1 | Paymaster running at http://localhost:8080/paymaster/
+prompt-1 | Press Ctrl+C to stop the node
```
## Interacting with your Marketplace Application
@@ -264,11 +274,6 @@ Once your Marketplace application is up and running (via cartesi run), you can i
In this section, we’ll focus on using the Cartesi CLI to send Advance requests, since it provides a much simpler and faster way to test your application locally.
-:::note Inspect endpoint
-If your application is running on a different port or has a different name from marketplace, remember to replace the inspect endpoint http://127.0.0.1:6751/inspect/marketplace with the one displayed after running the cartesi run command.
-Also, ensure that all CLI commands are executed from the root directory of your application.
-:::
-
### 1. Mint an ERC-721 Token and Grant Approval
With your Marketplace application now deployed, the first step is to mint the NFT you plan to list and grant approval for it to be transferred via the ERC-721 portal.
@@ -277,10 +282,10 @@ Since our app uses the test ERC-721 and ERC-20 contracts automatically deployed
- Mint token ID 1:
```bash
-cast send 0xBa46623aD94AB45850c4ecbA9555D26328917c3B \
+cast send 0xc6582A9b48F211Fa8c2B5b16CB615eC39bcA653B \
"safeMint(address, uint256, string)" \
0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 1 "" \
- --rpc-url http://127.0.0.1:6751/anvil \
+ --rpc-url http://localhost:8545 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
```
@@ -291,133 +296,159 @@ This command calls the `safeMint` function in the `testNFT` contract deployed by
Before an NFT can be deposited into the application, the portal contract must have permission to transfer it on behalf of the owner. Use the following command to grant that approval:
```bash
-cast send 0xBa46623aD94AB45850c4ecbA9555D26328917c3B \
+cast send 0xc6582A9b48F211Fa8c2B5b16CB615eC39bcA653B \
"setApprovalForAll(address,bool)" \
- 0xc700d52F5290e978e9CAe7D1E092935263b60051 true \
- --rpc-url http://127.0.0.1:6751/anvil \
+ 0xab7528bb862fB57E8A2BCd567a2e929a0Be56a5e true \
+ --rpc-url http://localhost:8545 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
```
-### 2. Deposit NFT with ID 1 to the Marketplace [advance request]
+### 2. Relay Application address
+
+Before any interaction with the application, it's important that the application logic is aware of Its contract address, as this is important during voucher generation. To register the application address, we use the Cartesi CLI to trigger a call from the `DappAddressRelayer` contract to our application, this can be achieved through the below process:
+
+```bash
+cartesi send
+```
+
+Run the command above, then choose `Send DApp address input to the application.` as the action. Press Enter twice to accept Foundry as the interaction chain. Next, press Enter to use the default RPC URL, then press Enter three more times to select Mnemonic as the authentication method and confirm both the default mnemonic and the default wallet address. Finally, press Enter one last time to confirm the displayed application address.
+
+At this point, the CLI will initiate the request and forward the application’s address to your Cartesi application. The application codebase already includes logic to verify the caller and ensure that the received application address is correctly recognized as the `dappAddressRelayer`.
+
+### 3. Deposit NFT with ID 1 to the Marketplace [advance request]
Now that the NFT is minted and approved, it’s time to list it on the marketplace.
We’ll do this by depositing it using the Cartesi CLI:
```bash
-cartesi deposit erc721
+cartesi send erc721
```
-The CLI will prompt you for the token ID (enter 1) and the token address (press Enter to use the default test token).
+The CLI will prompt you for the chain, RPC URL, Wallet, Mnemonic, Account and Application address, for those sections you can simply keep hitting enter to use the default values, when the CLI Prompts for token address, you enter the address `0xc6582A9b48F211Fa8c2B5b16CB615eC39bcA653B`, token ID (enter 1).
Under the hood, the CLI transfers the NFT from your address to the ERC-721 portal, which then sends the deposit payload to your application.
Once the deposit succeeds, the terminal running your application should show logs similar to:
```bash
-INFO rollup_http_server::http_service] received new request of type ADVANCE
-[INFO actix_web::middleware::logger] 127.0.0.1 "POST /finish HTTP/1.1" 200 751 "-" "-" 0.018688
-Received finish status 200 OK
-Received advance request data {"request_type":"advance_state","data":{"metadata":{"chain_id":13370,"app_contract":"0xb86306660e0be8e228c0cd14b8a1c5d5eddb8d20","msg_sender":"0xc700d52f5290e978e9cae7d1e092935263b60051","block_number":675,"block_timestamp":1762948794,"prev_randao":"0x3d144a3d5bb3125c92a230e7597e04e82eb5d5acbea185db2b1eadda3530d5c7","input_index":0},"payload":"0xba46623ad94ab45850c4ecba9555d26328917c3bf39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}}
-Token deposit and listing processed successfully
-[INFO actix_web::middleware::logger] 127.0.0.1 "POST /notice HTTP/1.1" 201 11 "-" "-" 0.001088
-Sending finish
+validator-1 | [INFO rollup_http_server::http_service] Received new request of type ADVANCE
+validator-1 | [INFO actix_web::middleware::logger] 127.0.0.1 "POST /finish HTTP/1.1" 200 601 "-" "node" 0.001152
+validator-1 | Received finish status 200
+validator-1 | Received advance request data {"metadata":{"msg_sender":"0x237f8dd094c0e47f4236f12b4fa01d6dae89fb87","epoch_index":0,"input_index":2,"block_number":298,"timestamp":1764095710},"payload":"0xc6582a9b48f211fa8c2b5b16cb615ec39bca653bf39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}
+validator-1 | Token deposit and Listing processed successfully
+validator-1 | [INFO actix_web::middleware::logger] 127.0.0.1 "POST /notice HTTP/1.1" 201 11 "-" "node" 0.000704
+validator-1 | [INFO rollup_http_server::http_service] Received new request of type INSPECT
+validator-1 | [INFO actix_web::middleware::logger] 127.0.0.1 "POST /finish HTTP/1.1" 200 98 "-" "node" 0.000960
+validator-1 | Received finish status 200
```
-### 3. View All NFTs Listed on the Marketplace [inspect request]
+### 4. View All NFTs Listed on the Marketplace [inspect request]
After depositing, your NFT is automatically listed.
To verify this, you can query the marketplace for all listed tokens using an Inspect request:
```bash
-curl -X POST http://127.0.0.1:6751/inspect/marketplace \
- -H "Content-Type: application/json" \
- -d '{"method":"get_all_listed_tokens"}'
+curl http://localhost:8080/inspect/get_all_listed_tokens
```
This call returns a hex payload containing a list of all listed tokens on the marketplace.
```bash
-{"status":"Accepted","reports":[{"payload":"0x416c6c206c697374656420746f6b656e73206172653a205b315d"}],"processed_input_count":6}
+{"status":"Accepted","reports":[{"payload":"0x416c6c206c697374656420746f6b656e73206172653a2031"}],"processed_input_count":6}
```
-The payload hex `0x416c6...205b315d` when decoded, returns `All listed tokens are: [1]`. Thereby confirming that the token with `Id 1` has successfully been listed.
+The payload hex `0x416c6...2653a2031` when decoded, returns `All listed tokens are: [1]`. Thereby confirming that the token with `Id 1` has successfully been listed.
-### 4. Deposit ERC20 token for making purchases [advance request]
+### 5. Deposit ERC20 token for making purchases [advance request]
With the NFT successfully listed for sale, it's time to attempt to purchase this token, but before we do that, we'll need first deposit the required amount of tokens to purchase the listed NFT. Since our marketplace lists all NFT's at the price of `100 testTokens` we'll be transferring 100 tokens to the new address we'll be using to purchase, before proceeding with the purchase.
- Transfer required tokens to purchase address.
```bash
-cast send 0xFBdB734EF6a23aD76863CbA6f10d0C5CBBD8342C \
+cast send 0x92C6bcA388E99d6B304f1Af3c3Cd749Ff0b591e2 \
"transfer(address,uint256)" 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 100000000000000000000 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
---rpc-url http://127.0.0.1:6751/anvil
+--rpc-url http://localhost:8545
```
- Deposit 100 tokens to the marketplace application.
```bash
-cartesi deposit --from 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
+ cartesi send erc20
```
-The CLI will prompt you for the token address (press Enter to use the default test token), and then the amount of tokens we intend to deposit `(100)`. The CLI would finally proceed to grant the necessary approvals after which it would deposit the tokens to our application.
+The CLI will prompt you for the interaction chain, select Foundry, then press Enter twice to accept the default RPC URL. Next, choose Mnemonic as the authentication method. When asked to select an account, choose the second address in the list: `0x70997970C51812dc3A010C7d01b50e0d17dc79C8`.
+
+After that, select the default application address. When prompted for the ERC-20 token address, enter: `0x92C6bcA388E99d6B304f1Af3c3Cd749Ff0b591e2`.
+
+Finally, enter the amount of tokens you want to deposit (100). The CLI will then automatically handle the necessary approvals and complete the deposit into your application.
On a successful deposit our application should return logs that look like this:
```bash
-[INFO rollup_http_server::http_service] received new request of type ADVANCE
-[INFO actix_web::middleware::logger] 127.0.0.1 "POST /finish HTTP/1.1" 200 496 "-" "-" 0.018176
-Received finish status 200 OK
-Received advance request data {"request_type":"advance_state","data":{"metadata":{"chain_id":13370,"app_contract":"0xb86306660e0be8e228c0cd14b8a1c5d5eddb8d20","msg_sender":"0xc700d6add016eecd59d989c028214eaa0fcc0051","block_number":2272,"block_timestamp":1762951994,"prev_randao":"0x0992ab8380b23c1c98928a76ae9a79c501ae27625943a53b0fd57455f10e5164","input_index":1},"payload":"0xfbdb734ef6a23ad76863cba6f10d0c5cbbd8342c70997970c51812dc3a010c7d01b50e0d17dc79c80000000000000000000000000000000000000000000000056bc75e2d63100000"}}
-Deposited token: 0xfbdb734ef6a23ad76863cba6f10d0c5cbbd8342c, Receiver: 0x70997970c51812dc3a010c7d01b50e0d17dc79c8, Amount: 100000000000000000000
-Token deposit processed successfully
-Sending finish
+validator-1 | [INFO rollup_http_server::http_service] Received new request of type ADVANCE
+validator-1 | [INFO actix_web::middleware::logger] 127.0.0.1 "POST /finish HTTP/1.1" 200 347 "-" "node" 0.001024
+validator-1 | Received finish status 200
+validator-1 | Received advance request data {"metadata":{"msg_sender":"0x9c21aeb2093c32ddbc53eef24b873bdcd1ada1db","epoch_index":0,"input_index":4,"block_number":305,"timestamp":1764095745},"payload":"0x0192c6bca388e99d6b304f1af3c3cd749ff0b591e270997970c51812dc3a010c7d01b50e0d17dc79c800000000000000000000000000000000000000000000000821ab0d4414980000"}
+validator-1 | Token deposit processed successfully
+validator-1 | [INFO rollup_http_server::http_service] Received new request of type ADVANCE
+validator-1 | [INFO actix_web::middleware::logger] 127.0.0.1 "POST /finish HTTP/1.1" 200 285 "-" "node" 0.001024
+validator-1 | Received finish status 200
```
-### 5. Purchase Token with ID 1 [advance request]
+### 6. Purchase Token with ID 1 [advance request]
Now that the buyer has deposited funds, we can proceed to purchase the NFT. To do this we make an advance request to the application using the Cartesi CLI by running the command:
```bash
- cartesi send "{"method": "purchase_token", "token_id": 1}" --from 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
+ cartesi send generic
+```
+
+The CLI will prompt you for the interaction chain, select Foundry, then press Enter twice to accept the default RPC URL. Next, choose Mnemonic as the authentication method. When asked to select an account, choose the second address in the list: `0x70997970C51812dc3A010C7d01b50e0d17dc79C8`.
+
+After that, select the default application address. When prompted for Input method, select `String encoding`.
+
+Finally, pass the below Json object to the terminal as the input:
+
+```bash
+{"method": "purchase_token", "token_id": "1"}
```
This command notifies the marketplace that the address `0x7099797....0d17dc79C8` which initially deposited 100 tokens, would want to purchase token ID 1, The marketplace proceeds to run necessary checks like verifying that the token is for sale, and that the buyer has sufficient tokens to make the purchase, after which it executes the purchase and finally emits a voucher that transfers the tokens to the buyer's address. On a successful purchase, you should get logs similar to the below.
```bash
-[INFO rollup_http_server::http_service] received new request of type ADVANCE
-[INFO actix_web::middleware::logger] 127.0.0.1 "POST /finish HTTP/1.1" 200 426 "-" "-" 0.001792
-Received finish status 200 OK
-Received advance request data {"request_type":"advance_state","data":{"metadata":{"chain_id":13370,"app_contract":"0xb86306660e0be8e228c0cd14b8a1c5d5eddb8d20","msg_sender":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","block_number":3576,"block_timestamp":1762954606,"prev_randao":"0xd29cd42cfd3c9ff4f31b39f497fc2f4d0a7add5a67da98be0d7841c37c7ad25f","input_index":2},"payload":"0x7b6d6574686f643a2070757263686173655f746f6b656e2c20746f6b656e5f69643a20317d"}}
-PURCHASE SUCCESSFUL, STRUCTURING VOUCHERS!!
-[INFO actix_web::middleware::logger] 127.0.0.1 "POST /voucher HTTP/1.1" 201 11 "-" "-" 0.015424
-Voucher generation successful
-Token purchased and Withdrawn successfully
+validator-1 | [INFO rollup_http_server::http_service] Received new request of type ADVANCE
+validator-1 | [INFO actix_web::middleware::logger] 127.0.0.1 "POST /finish HTTP/1.1" 200 285 "-" "node" 0.001024
+validator-1 | Received finish status 200
+validator-1 | Received advance request data {"metadata":{"msg_sender":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","epoch_index":0,"input_index":5,"block_number":306,"timestamp":1764095750},"payload":"0x7b226d6574686f64223a2270757263686173655f746f6b656e222c22746f6b656e5f6964223a2234227d"}
+validator-1 | Token purchased successfully
+validator-1 | [INFO actix_web::middleware::logger] 127.0.0.1 "POST /voucher HTTP/1.1" 201 11 "-" "node" 0.000896
+validator-1 | [INFO rollup_http_server::http_service] Received new request of type ADVANCE
+validator-1 | [INFO actix_web::middleware::logger] 127.0.0.1 "POST /finish HTTP/1.1" 200 285 "-" "node" 0.001024
+validator-1 | Received finish status 200
```
-### 6. Recheck NFTs Listed on the Marketplace [inspect request]
+### 7. Recheck NFTs Listed on the Marketplace [inspect request]
Finally, we can confirm that the purchased NFT has been removed from the listings by running the inspect query again:
```bash
-curl -X POST http://127.0.0.1:6751/inspect/marketplace \
- -H "Content-Type: application/json" \
- -d '{"method":"get_all_listed_tokens"}'
+curl http://localhost:8080/inspect/get_all_listed_tokens
```
This call returns a hex payload like below:
```bash
-{"status":"Accepted","reports":[{"payload":"0x416c6c206c697374656420746f6b656e73206172653a205b5d"}],"processed_input_count":7}
+ {"status":"Accepted","exception_payload":null,"reports":[{"payload":"0x416c6c206c697374656420746f6b656e73206172653a20"}],"processed_input_count":7}
```
-The payload hex `0x416c6...653a205b5d` when decoded, returns `All listed tokens are: []`. Thereby verifying that the token with `Id 1` has successfully been sold and no longer listed for sale in the marketplace.
+The payload hex `0x416c6...6172653a20` when decoded, returns `All listed tokens are: `. Thereby verifying that the token with `Id 1` has successfully been sold and no longer listed for sale in the marketplace.
## Conclusion
Congratulations!!!
-You’ve successfully built and interacted with your own Marketplace application on Cartesi.
+You’ve successfully built and interacted with your own Marketplace application on Cartesi.
This example covered essential Cartesi concepts such as routing, asset management, voucher generation, and the use of both Inspect and Advance routes.
diff --git a/cartesi-rollups_versioned_docs/version-1.5/tutorials/snippets/marketplace-js.md b/cartesi-rollups_versioned_docs/version-1.5/tutorials/snippets/marketplace-js.md
index d7434c14..f7e3ee38 100644
--- a/cartesi-rollups_versioned_docs/version-1.5/tutorials/snippets/marketplace-js.md
+++ b/cartesi-rollups_versioned_docs/version-1.5/tutorials/snippets/marketplace-js.md
@@ -1,5 +1,6 @@
```javascript
-import { encodeFunctionData, toHex, zeroHash } from "viem";
+import { encodeFunctionData, getAddress, toHex, zeroHash } from "viem";
+import { ethers } from "ethers";
const rollup_server = process.env.ROLLUP_HTTP_SERVER_URL;
console.log("HTTP rollup_server url is " + rollup_server);
@@ -22,13 +23,14 @@ const erc721Abi = [
];
class Storage {
- constructor(erc721_portal_address, erc20_portal_address, erc721_token, erc20_token, list_price) {
+ constructor(erc721_portal_address, erc20_portal_address, erc721_token, erc20_token, list_price, dappAddressRelay) {
this.erc721_portal_address = erc721_portal_address;
this.erc20_portal_address = erc20_portal_address;
this.erc721_token = erc721_token;
this.erc20_token = erc20_token;
this.application_address = normAddr(ZERO_ADDRESS);
- this.list_price = list_price
+ this.list_price = list_price;
+ this.dappAddressRelay = dappAddressRelay;
this.listed_tokens = [];
this.users_erc20_token_balance = new Map();
@@ -114,20 +116,19 @@ class Storage {
this.user_erc721_token_balance.set(oldAddr, oldOwnerTokens.filter((id) => id !== tid));
}
-
async purchaseERC721Token(buyerAddress, erc721TokenAddress, tokenId) {
const tid = asBigInt(tokenId);
if (!storage.listed_tokens.includes(tokenId)) {
await emitReport(`Token ${erc721TokenAddress} with id ${tid} is not for sale`);
console.log("Token is not for sale");
- return;
+ return false;
}
const owner = this.getERC721TokenOwner(tid);
if (!owner) {
await emitReport(`Token owner for token ${erc721TokenAddress} with id ${tid} not found`);
console.log("Token owner not found");
- return;
+ return false;
}
await this.reduceUserBalance(buyerAddress, storage.list_price);
@@ -169,36 +170,42 @@ async function handleERC721Deposit(depositorAddress, tokenId, tokenAddress) {
}
}
-
-function tokenDepositParse(payload) {
- const hexstr = payload.startsWith("0x") ? payload.slice(2) : payload;
- const bytes = Buffer.from(hexstr, "hex");
-
- if (bytes.length < 20 + 20 + 32) {
- console.log(`payload too short: ${bytes.length} bytes`);
+function erc721TokenDepositParse(payload) {
+ try {
+ const erc721 = getAddress(ethers.dataSlice(payload, 0, 20));
+ const account = getAddress(ethers.dataSlice(payload, 20, 40));
+ const tokenId = parseInt(ethers.dataSlice(payload, 40, 72));
+
+ return {
+ token: erc721,
+ receiver: account,
+ amount: tokenId,
+ };
+ } catch (e) {
+ emitReport(`Error parsing ERC721 deposit: ${e}`);
}
+}
- const token = bytes.slice(0, 20);
- const receiver = bytes.slice(20, 40);
- const amount_be = bytes.slice(40, 72);
-
- for (let i = 0; i < 16; i++) {
- if (amount_be[i] !== 0) {
- console.log("amount too large for u128");
+function erc20TokenDepositParse(payload) {
+ try {
+ let inputData = [];
+ inputData[0] = ethers.dataSlice(payload, 0, 1);
+ inputData[1] = ethers.dataSlice(payload, 1, 21);
+ inputData[2] = ethers.dataSlice(payload, 21, 41);
+ inputData[3] = ethers.dataSlice(payload, 41, 73);
+
+ if (!inputData[0]) {
+ emitReport("ERC20 deposit unsuccessful: invalid payload");
+ throw new Error("ERC20 deposit unsuccessful");
+ }
+ return {
+ token: getAddress(inputData[1]),
+ receiver: getAddress(inputData[2]),
+ amount: BigInt(inputData[3]),
+ };
+ } catch (e) {
+ emitReport(`Error parsing ERC20 deposit: ${e}`);
}
- }
-
- const lo = amount_be.slice(16);
- let amount = 0n;
- for (const b of lo) {
- amount = (amount << 8n) + BigInt(b);
- }
-
- return {
- token: "0x" + token.toString("hex"),
- receiver: "0x" + receiver.toString("hex"),
- amount: amount.toString(),
- };
}
async function extractField(json, field) {
@@ -212,14 +219,15 @@ async function extractField(json, field) {
}
}
-
async function handlePurchaseToken(callerAddress, userInput) {
try {
const erc721TokenAddress = normAddr(storage.erc721_token);
const tokenId = BigInt(await extractField(userInput, "token_id"));
try {
- await storage.purchaseERC721Token(callerAddress, erc721TokenAddress, tokenId);
+ if (await storage.purchaseERC721Token(callerAddress, erc721TokenAddress, tokenId) === false) {
+ return;
+ }
console.log("Token purchased successfully");
let voucher = structureVoucher({
abi: erc721Abi,
@@ -248,19 +256,18 @@ async function handle_advance(data) {
console.log("Received advance request data " + JSON.stringify(data));
const sender = data["metadata"]["msg_sender"];
- app_contract = normAddr(data["metadata"]["app_contract"])
-
- if (normAddr(storage.application_address) == normAddr(ZERO_ADDRESS)) {
- storage.setAppAddress(app_contract);
- }
const payload = hexToString(data.payload);
- if (normAddr(sender) == normAddr(storage.erc20_portal_address)) {
- let { token, receiver, amount } = tokenDepositParse(data.payload);
+ if (normAddr(sender) == normAddr(storage.dappAddressRelay)) {
+ if (normAddr(storage.application_address) == normAddr(ZERO_ADDRESS)) {
+ storage.setAppAddress(data.payload);
+ }
+ } else if (normAddr(sender) == normAddr(storage.erc20_portal_address)) {
+ let { token, receiver, amount } = erc20TokenDepositParse(data.payload);
await handleERC20Deposit(receiver, amount, token);
} else if (normAddr(sender) == normAddr(storage.erc721_portal_address)) {
- let { token, receiver, amount } = tokenDepositParse(data.payload)
+ let { token, receiver, amount } = erc721TokenDepositParse(data.payload)
await handleERC721Deposit(receiver, asBigInt(amount), token);
} else {
const payload_obj = JSON.parse(payload);
@@ -279,25 +286,19 @@ async function handle_advance(data) {
async function handle_inspect(data) {
console.log("Received inspect request data " + JSON.stringify(data));
- const payload = hexToString(data.payload);
- let payload_obj;
+ const payload = hexToString(data.payload).trim();
- try {
- payload_obj = JSON.parse(payload);
- } catch (e) {
- await emitReport("Invalid payload JSON");
- return "accept";
- }
+ let payload_arry = payload.split("/");
- switch (payload_obj.method) {
+ switch (payload_arry[0]) {
case "get_user_erc20_balance": {
- const user_address = payload_obj["user_address"];
+ const user_address = payload_arry[1];
const bal = storage.getUserERC20TokenBalance(normAddr(user_address));
await emitReport(`User: ${user_address} Balance: ${bal.toString()}`);
break;
}
case "get_token_owner": {
- const token_id = BigInt(payload_obj["token_id"]);
+ const token_id = BigInt(payload_arry[1]);
const token_owner = storage.getERC721TokenOwner(token_id);
await emitReport(`Token_id: ${token_id.toString()} owner: ${token_owner ?? "None"}`);
break;
@@ -330,12 +331,9 @@ function structureVoucher({ abi, functionName, args, destination, value = 0n })
args,
});
- const valueHex = value === 0n ? zeroHash : toHex(BigInt(value));
-
return {
destination,
- payload,
- value: valueHex,
+ payload
}
}
@@ -390,13 +388,14 @@ var handlers = {
inspect_state: handle_inspect,
};
-let erc721_portal_address = "0xc700d52F5290e978e9CAe7D1E092935263b60051";
-let erc20_portal_address = "0xc700D6aDd016eECd59d989C028214Eaa0fCC0051";
-let erc20_token = "0xFBdB734EF6a23aD76863CbA6f10d0C5CBBD8342C";
-let erc721_token = "0xBa46623aD94AB45850c4ecbA9555D26328917c3B";
+let erc721_portal_address = "0x237F8DD094C0e47f4236f12b4Fa01d6Dae89fb87";
+let erc20_portal_address = "0x9C21AEb2093C32DDbC53eEF24B873BDCd1aDa1DB";
+let erc20_token = "0x92C6bcA388E99d6B304f1Af3c3Cd749Ff0b591e2";
+let erc721_token = "0xc6582A9b48F211Fa8c2B5b16CB615eC39bcA653B";
+let dappAddressRelay = "0xF5DE34d6BbC0446E2a45719E718efEbaaE179daE";
let list_price = BigInt("100000000000000000000");
-var storage = new Storage(erc721_portal_address, erc20_portal_address, erc721_token, erc20_token, list_price);
+var storage = new Storage(erc721_portal_address, erc20_portal_address, erc721_token, erc20_token, list_price, dappAddressRelay);
var finish = { status: "accept" };
diff --git a/cartesi-rollups_versioned_docs/version-1.5/tutorials/snippets/marketplace-py.md b/cartesi-rollups_versioned_docs/version-1.5/tutorials/snippets/marketplace-py.md
index cbf84ad9..ee7c3aae 100644
--- a/cartesi-rollups_versioned_docs/version-1.5/tutorials/snippets/marketplace-py.md
+++ b/cartesi-rollups_versioned_docs/version-1.5/tutorials/snippets/marketplace-py.md
@@ -35,7 +35,32 @@ def hex_to_string(hexstr: str) -> str:
except UnicodeDecodeError:
return "0x" + hexstr
-def token_deposit_parse(payload_hex: str):
+def erc20_token_deposit_parse(payload_hex: str):
+ hexstr = payload_hex[2:] if payload_hex.startswith("0x") else payload_hex
+ b = binascii.unhexlify(hexstr)
+
+ if len(b) < 20 + 20 + 32:
+ logger.error(f"payload too short: {len(b)} bytes")
+ return
+
+ token = b[1:21]
+ receiver = b[21:41]
+ amount_be = b[41:73]
+
+ val = int.from_bytes(amount_be, byteorder="big", signed=False)
+
+ token_hex = "0x" + token.hex()
+ receiver_hex = "0x" + receiver.hex()
+
+ print("ERC20 Deposit Parsed:", token_hex, receiver_hex, str(val));
+
+ return {
+ "token": token_hex,
+ "receiver": receiver_hex,
+ "amount": str(val),
+ }
+
+def erc721_token_deposit_parse(payload_hex: str):
hexstr = payload_hex[2:] if payload_hex.startswith("0x") else payload_hex
b = binascii.unhexlify(hexstr)
@@ -100,8 +125,7 @@ def structure_voucher(function_signature, destination, types, values, value=0) -
return {
"destination": destination,
- "payload": payload,
- "value": f"0x{value:064x}"
+ "payload": payload
}
def emitVoucher(voucher: dict):
@@ -118,7 +142,7 @@ def emitVoucher(voucher: dict):
class Storage:
def __init__(self, erc721_portal_address: str, erc20_portal_address: str,
- erc721_token: str, erc20_token: str, list_price: int):
+ erc721_token: str, erc20_token: str, list_price: int, dappAddressRelay: str):
self.erc721_portal_address = norm_addr(erc721_portal_address)
self.erc20_portal_address = norm_addr(erc20_portal_address)
@@ -126,6 +150,7 @@ class Storage:
self.erc20_token = norm_addr(erc20_token)
self.application_address = norm_addr("0x" + "0" * 40)
self.list_price = list_price
+ self.dappAddressRelay = norm_addr(dappAddressRelay)
self.listed_tokens: list[int] = []
self.users_erc20_token_balance: dict[str, int] = {}
@@ -162,14 +187,15 @@ class Storage:
if current is None:
emitReport(f"User {addr} record not found")
logger.error("User balance record not found")
- return
+ return False
if current < amt:
emitReport(f"User {addr} has insufficient balance")
- logger.error("User has insufficient balance")
- return
+ logger.error(f"User {addr} has insufficient balance")
+ return False
self.users_erc20_token_balance[addr] = current - amt
+ return True
def depositERC721Token(self, userAddress: str, tokenId):
addr = norm_addr(userAddress)
@@ -215,7 +241,10 @@ class Storage:
logger.error("Token is not for sale")
return False
- self.reduceUserBalance(buyerAddress, self.list_price)
+ if not self.reduceUserBalance(buyerAddress, self.list_price):
+ emitReport(f"Buyer {buyerAddress} has insufficient balance to purchase token {erc721TokenAddress} with id {tid}")
+ logger.error("Buyer has insufficient balance")
+ return False
owner = self.getERC721TokenOwner(tid)
if not owner:
@@ -230,13 +259,14 @@ class Storage:
return True
-erc_721_portal_address = "0xc700d52F5290e978e9CAe7D1E092935263b60051"
-erc20_portal_address = "0xc700D6aDd016eECd59d989C028214Eaa0fCC0051"
-erc20_token = "0xFBdB734EF6a23aD76863CbA6f10d0C5CBBD8342C"
-erc721_token = "0xBa46623aD94AB45850c4ecbA9555D26328917c3B"
+erc_721_portal_address = "0x237F8DD094C0e47f4236f12b4Fa01d6Dae89fb87"
+erc20_portal_address = "0x9C21AEb2093C32DDbC53eEF24B873BDCd1aDa1DB"
+erc20_token = "0x92C6bcA388E99d6B304f1Af3c3Cd749Ff0b591e2"
+erc721_token = "0xc6582A9b48F211Fa8c2B5b16CB615eC39bcA653B"
+dappAddressRelay = "0xF5DE34d6BbC0446E2a45719E718efEbaaE179daE"
list_price = 100_000_000_000_000_000_000
-storage = Storage(erc_721_portal_address, erc20_portal_address, erc721_token, erc20_token, list_price)
+storage = Storage(erc_721_portal_address, erc20_portal_address, erc721_token, erc20_token, list_price, dappAddressRelay)
def handle_erc20_deposit(depositor_address: str, amount_deposited, token_address: str):
@@ -294,22 +324,20 @@ def handle_advance(data):
logger.info(f"Received advance request data {data}")
sender = norm_addr(data["metadata"]["msg_sender"])
- app_contract = norm_addr(data["metadata"]["app_contract"])
zero_addr = norm_addr("0x" + "0" * 40)
- if norm_addr(storage.application_address) == zero_addr:
- storage.setAppAddress(app_contract)
-
payload_hex = data.get("payload", "")
payload_str = hex_to_string(payload_hex)
-
- if sender == storage.erc20_portal_address:
- parsed = token_deposit_parse(payload_hex)
+ if sender == storage.dappAddressRelay:
+ if norm_addr(storage.application_address) == zero_addr:
+ storage.setAppAddress(payload_hex)
+ elif sender == storage.erc20_portal_address:
+ parsed = erc20_token_deposit_parse(payload_hex)
token, receiver, amount = parsed["token"], parsed["receiver"], parsed["amount"]
handle_erc20_deposit(receiver, int(amount), token)
elif sender == storage.erc721_portal_address:
- parsed = token_deposit_parse(payload_hex)
+ parsed = erc721_token_deposit_parse(payload_hex)
token, receiver, amount = parsed["token"], parsed["receiver"], parsed["amount"]
handle_erc721_deposit(receiver, int(amount), token)
@@ -334,18 +362,18 @@ def handle_inspect(data: dict):
payload_str = hex_to_string(data.get("payload", "0x"))
try:
- payload_obj = json.loads(payload_str)
+ payload_arr = payload_str.split("/")
except Exception:
emitReport("Invalid payload string")
return "accept"
- method = payload_obj.get("method", "")
+ method = payload_arr[0]
if method == "get_user_erc20_balance":
- user_address = norm_addr(payload_obj.get("user_address", ""))
+ user_address = norm_addr(payload_arr[1])
bal = storage.getUserERC20TokenBalance(user_address)
emitReport(f"User: {user_address} Balance: {bal}")
elif method == "get_token_owner":
- token_id = as_int(payload_obj.get("token_id", 0))
+ token_id = as_int(payload_arr[1])
owner = storage.getERC721TokenOwner(token_id)
emitReport(f"Token_id: {token_id} owner: {owner if owner else 'None'}")
elif method == "get_all_listed_tokens":
@@ -376,4 +404,5 @@ while True:
handler = handlers[rollup_request["request_type"]]
finish["status"] = handler(rollup_request["data"])
+
```
\ No newline at end of file
diff --git a/cartesi-rollups_versioned_docs/version-1.5/tutorials/snippets/marketplace-rs.md b/cartesi-rollups_versioned_docs/version-1.5/tutorials/snippets/marketplace-rs.md
index 14911d7d..9cf3fd9f 100644
--- a/cartesi-rollups_versioned_docs/version-1.5/tutorials/snippets/marketplace-rs.md
+++ b/cartesi-rollups_versioned_docs/version-1.5/tutorials/snippets/marketplace-rs.md
@@ -12,6 +12,7 @@ pub struct Storage {
pub erc721_token: String,
pub erc20_token: String,
pub app_address: String,
+ pub dapp_address_relay: String,
pub list_price: u128,
pub listed_tokens: Vec