From 89bff9c722f893d5a7e99057225760282c8a9dd5 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Sun, 12 Oct 2025 02:32:22 +0100 Subject: [PATCH 01/10] docs(staging): Setup a new staging branch with v2.0 default --- .../version-2.0/development/asset-handling.md | 2 +- cartesi-rollups_versions.json | 1 + docusaurus.config.js | 10 +++++----- fraud-proofs/honeypot/introduction.md | 1 - 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/asset-handling.md b/cartesi-rollups_versioned_docs/version-2.0/development/asset-handling.md index 106a2771..d7e66fa2 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/development/asset-handling.md +++ b/cartesi-rollups_versioned_docs/version-2.0/development/asset-handling.md @@ -50,7 +50,7 @@ Vouchers are crucial in allowing applications in the execution layer to interact The application’s off-chain layer often requires knowledge of its address to facilitate on-chain interactions for withdrawals, for example: `transferFrom(sender, recipient, amount)`. In this case, the sender is the application itself. -By calling [`relayDAppAddress()`](../api-reference/json-rpc/relays/relays.md), function of the `DAppAddressRelay` contract, it adds the dApp’s address as a new input for the Cartesi dApp to process. Next, the off-chain machine uses this address to generate a voucher for execution at the [`executeVoucher()`](../api-reference/json-rpc/application.md/#executevoucher) function of the `CartesiDApp` contract. +Next, the off-chain machine uses the address of the application on the base layer to generate a voucher for execution at the executeOutput() function of the Application contract. This address is known to the offchain machine because it is embedded in the metadata of every input sent to the application, though the developer will need to implement extra logic fetch this address from the metadata then properly store and retrieve it when needed in situations like generating the above Voucher. Below is a sample JavaScript code with the implementations to transfer tokens to whoever calls the application, notice that the `const call` variable is an encoded function data containing the token contract ABI, function name and also arguments like recipient and amount, while the actual `voucher` structure itself contains a destination (erc20 token contract where the transfer execution should occur), the payload (encoded function data in `call`) and finally a value field which is initialized to `0` meaning no Ether is intended to be sent alongside this transfer request. diff --git a/cartesi-rollups_versions.json b/cartesi-rollups_versions.json index a4a86398..4af9e6da 100644 --- a/cartesi-rollups_versions.json +++ b/cartesi-rollups_versions.json @@ -1,4 +1,5 @@ [ + "2.0", "1.5", "1.3", "1.0", diff --git a/docusaurus.config.js b/docusaurus.config.js index 6a241ee6..dade6442 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -185,7 +185,7 @@ const config = { }, { label: "Rollups", - to: "/cartesi-rollups/1.5", + to: "/cartesi-rollups/2.0", activeBaseRegex: "^/cartesi-rollups", position: "left", }, @@ -361,9 +361,9 @@ const config = { ], }, versions: { - "1.5": { - label: "1.5", - path: "1.5", + "2.0": { + label: "2.0", + path: "2.0", }, }, showLastUpdateTime: true, @@ -375,7 +375,7 @@ const config = { redirects: [ { from: '/cartesi-rollups', // the old/base route - to: '/cartesi-rollups/1.5/', // the new route to redirect to + to: '/cartesi-rollups/2.0/', // the new route to redirect to }, ], }, diff --git a/fraud-proofs/honeypot/introduction.md b/fraud-proofs/honeypot/introduction.md index 9710e0a0..7e80d389 100644 --- a/fraud-proofs/honeypot/introduction.md +++ b/fraud-proofs/honeypot/introduction.md @@ -6,7 +6,6 @@ This section focuses on the Cartesi application - the Honeypot - its introduction, application logic and how it demonstrates the PRT fraud-proof system's capabilities in a real-world scenario. - ## What is the Honeypot App? Cartesi’s Honeypot application is a [Stage-2](https://medium.com/l2beat/introducing-stages-a-framework-to-evaluate-rollups-maturity-d290bb22befe) app-specific rollup that is built to test the security of Cartesi Rollups. The application is a secure ERC-20 token vault that operates with asymmetric access controls: it accepts token deposits from any Ethereum address but restricts withdrawals exclusively to a single pre-configured address. This creates a "honeypot" mechanism where tokens accumulate in the vault until the authorized withdrawal address claims them. From a10711e317f0e8af3779461ae1ec8d53db6727f9 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Fri, 26 Sep 2025 15:48:30 +0100 Subject: [PATCH 02/10] docs(fix) Updated ether wallet tutorial to use the appropriate rollups version --- .../version-1.5/tutorials/ether-wallet.md | 15 +++++++-------- .../version-2.0/tutorials/ether-wallet.md | 10 +++++++--- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/cartesi-rollups_versioned_docs/version-1.5/tutorials/ether-wallet.md b/cartesi-rollups_versioned_docs/version-1.5/tutorials/ether-wallet.md index 44893fb4..35f16ddb 100644 --- a/cartesi-rollups_versioned_docs/version-1.5/tutorials/ether-wallet.md +++ b/cartesi-rollups_versioned_docs/version-1.5/tutorials/ether-wallet.md @@ -33,11 +33,9 @@ Now, navigate to the project directory and install [`ethers`](https://docs.ether ```bash yarn add ethers viem -yarn add -D @cartesi/rollups +yarn add -D @cartesi/rollups@1.4.3 ``` - - ## Define the ABIs Let's write a configuration to generate the ABIs of the Cartesi Rollups Contracts. @@ -108,14 +106,15 @@ This script will look for all specified `.sol` files and create a TypeScript fil Now, let's make the script executable: - chmod +x generate_abis.sh - +```bash + chmod +x generate_abis.sh +``` And run it: - - ./generate_abis.sh - +```bash + ./generate_abis.sh +``` ## Building the Ether wallet diff --git a/cartesi-rollups_versioned_docs/version-2.0/tutorials/ether-wallet.md b/cartesi-rollups_versioned_docs/version-2.0/tutorials/ether-wallet.md index b2aa03ef..b86c98d0 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/tutorials/ether-wallet.md +++ b/cartesi-rollups_versioned_docs/version-2.0/tutorials/ether-wallet.md @@ -33,7 +33,7 @@ Now, navigate to the project directory and install [`ethers`](https://docs.ether ```bash yarn add ethers viem -yarn add -D @cartesi/rollups +yarn add -D @cartesi/rollups@1.4.3 ``` ## Define the ABIs @@ -106,11 +106,15 @@ This script will look for all specified `.sol` files and create a TypeScript fil Now, let's make the script executable: - chmod +x generate_abis.sh +```bash + chmod +x generate_abis.sh +``` And run it: - ./generate_abis.sh +```bash + ./generate_abis.sh +``` ## Building the Ether wallet From d886a0a2a37da80c693d5d287968dbfcd4eddba2 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Sun, 2 Nov 2025 23:35:24 +0100 Subject: [PATCH 03/10] impl(snippets): Implemented rust and python code snippets for development section --- .../api-reference/backend/introduction.md | 2 +- .../development/creating-an-application.md | 149 +++---------- .../version-2.0/development/query-outputs.md | 144 ++++++++++++ .../development/send-inputs-and-assets.md | 77 +++++++ .../snippets/implementing_outputs_js.md | 112 ++++++++++ .../snippets/implementing_outputs_py.md | 129 +++++++++++ .../snippets/implementing_outputs_rs.md | 211 ++++++++++++++++++ 7 files changed, 708 insertions(+), 116 deletions(-) create mode 100644 cartesi-rollups_versioned_docs/version-2.0/development/snippets/implementing_outputs_js.md create mode 100644 cartesi-rollups_versioned_docs/version-2.0/development/snippets/implementing_outputs_py.md create mode 100644 cartesi-rollups_versioned_docs/version-2.0/development/snippets/implementing_outputs_rs.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/introduction.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/introduction.md index a09ef8a5..d78b0734 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/introduction.md +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/introduction.md @@ -213,7 +213,7 @@ An **Inspect** request involves making an external HTTP API call to the rollups You can make a simple inspect call from your frontend client to retrieve reports. -To perform an Inspect call, send an HTTP POST request to `
/inspect/` with a payload in the request body. For example: +To perform an Inspect call, make a HTTP POST request to `
/inspect/` with a payload in the request body. For example: ```shell curl -X POST http://localhost:8080/inspect/ \ diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/creating-an-application.md b/cartesi-rollups_versioned_docs/version-2.0/development/creating-an-application.md index 7adef785..ae169ae9 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/development/creating-an-application.md +++ b/cartesi-rollups_versioned_docs/version-2.0/development/creating-an-application.md @@ -60,118 +60,37 @@ If your application needs to emit Outputs like; notices, vouchers, or reports, m Below is a sample application that has been modified to include the logic to simply receive an input from a user in both inspect and advance route then, emits a notice, voucher and a report. For your application you'll need to include your personal logic and also emit outputs when necessary: -```javascript - -import { stringToHex, encodeFunctionData, erc20Abi, hexToString, zeroHash } from "viem"; - -const rollup_server = process.env.ROLLUP_HTTP_SERVER_URL; -console.log("HTTP rollup_server url is " + rollup_server); - -async function handle_advance(data) { - console.log("Received advance request data " + JSON.stringify(data)); - - const sender = data["metadata"]["msg_sender"]; - const payload = hexToString(data.payload); - const erc20Token = "0x784f0c076CC55EAD0a585a9A13e57c467c91Dc3a"; // Sample ERC20 token address - - await emitNotice(payload); - await emitReport(payload); - - const call = encodeFunctionData({ - abi: erc20Abi, - functionName: "transfer", - args: [sender, BigInt(10)], - }); - - let voucher = { - destination: erc20Token, - payload: call, - value: zeroHash, - }; - - await emitVoucher(voucher); - return "accept"; -} - -async function handle_inspect(data) { - console.log("Received inspect request data " + JSON.stringify(data)); - const payload = data.payload; - await emitReport(payload); - return "accept"; -} - -const emitNotice = async (inputPayload) => { - let hexPayload = stringToHex(inputPayload); - try { - await fetch(rollup_server + "/notice", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ payload: hexPayload }), - }); - } catch (error) { - //Do something when there is an error - } -} - -const emitVoucher = async (voucher) => { - try { - await fetch(rollup_server + "/voucher", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(voucher), - }); - } catch (error) { - //Do something when there is an error - } -}; - -const emitReport = async (payload) => { - let hexPayload = stringToHex(payload); - try { - await fetch(rollup_server + "/report", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ payload: hexPayload }), - }); - } catch (error) { - //Do something when there is an error - } -}; - -var handlers = { - advance_state: handle_advance, - inspect_state: handle_inspect, -}; - -var finish = { status: "accept" }; - -(async () => { - while (true) { - const finish_req = await fetch(rollup_server + "/finish", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ status: "accept" }), - }); - - console.log("Received finish status " + finish_req.status); - - if (finish_req.status == 202) { - console.log("No pending rollup request, trying again"); - } else { - const rollup_req = await finish_req.json(); - var handler = handlers[rollup_req["request_type"]]; - finish["status"] = await handler(rollup_req["data"]); - } - } -})(); - - -``` \ No newline at end of file + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import ImplementingOutputsJS from './snippets/implementing_outputs_js.md'; +import ImplementingOutputsPY from './snippets/implementing_outputs_py.md'; +import ImplementingOutputsRS from './snippets/implementing_outputs_rs.md'; + + + + +

+
+
+
+
+
+ + +

+
+
+
+
+
+ + +

+
+
+
+
+
+ +
\ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/query-outputs.md b/cartesi-rollups_versioned_docs/version-2.0/development/query-outputs.md index 2c6982d7..94e80252 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/development/query-outputs.md +++ b/cartesi-rollups_versioned_docs/version-2.0/development/query-outputs.md @@ -131,6 +131,87 @@ def handle_advance(data): + + +

+
+```rust
+fn calculate_multiples(num: i64) -> String {
+    let mut multiples = String::new();
+
+    for i in 1..=5 {
+        multiples.push_str(&(num * i).to_string());
+        if i < 5 {
+            multiples.push_str(", ");
+        }
+    }
+
+    multiples
+}
+fn hex_to_string(hex: &str) -> Result> {
+    let hexstr = hex.strip_prefix("0x").unwrap_or(hex);
+    let bytes = hex::decode(hexstr).map_err(|e| Box::new(e) as Box)?;
+    let s = String::from_utf8(bytes).map_err(|e| Box::new(e) as Box)?;
+    Ok(s)
+}
+
+async fn emit_notice( payload: String) -> Option {
+    let hex_string = {
+        let s = payload.strip_prefix("0x").unwrap_or(payload.as_str());
+        hex::encode(s.as_bytes())
+    };
+
+    let server_addr = env::var("ROLLUP_HTTP_SERVER_URL").expect("ROLLUP_HTTP_SERVER_URL not set");
+    let client = hyper::Client::new();
+
+    let response = object! {
+        "payload" => format!("0x{}", hex_string),
+    };
+    let request = hyper::Request::builder()
+    .method(hyper::Method::POST)
+    .header(hyper::header::CONTENT_TYPE, "application/json")
+    .uri(format!("{}/notice", server_addr))
+    .body(hyper::Body::from(response.dump()))
+    .ok()?;
+
+    let response = client.request(request).await;
+
+    match response {
+        Ok(_) => {
+            println!("Notice generation successful");
+            return Some(true);
+        }
+        Err(e) => {
+            println!("Notice request failed {}", e);
+            None
+        }
+    }
+}
+
+pub async fn handle_advance(
+    _client: &hyper::Client,
+    _server_addr: &str,
+    request: JsonValue,
+) -> Result<&'static str, Box> {
+    println!("Received advance request data {}", &request);
+    let payload = request["data"]["payload"]
+        .as_str()
+        .ok_or("Missing payload")?;
+
+    let number: i64 = hex_to_string(payload)?.parse()?;
+
+    let multiples = calculate_multiples(number);
+
+    println!("ADDING NOTICE WITH VALUE {}", multiples);
+
+    emit_notice(multiples).await;
+    Ok("accept")
+}
+```
+
+
+
+ For example, sending an input payload of `“2”` to the application using Cast or `cartesi send generic` will log: @@ -401,6 +482,69 @@ def handle_advance(data): + +

+
+```rust
+pub async fn handle_advance(
+    _client: &hyper::Client,
+    _server_addr: &str,
+    request: JsonValue,
+) -> Result<&'static str, Box> {
+    println!("Received advance request data {}", &request);
+    let payload = request["data"]["payload"]
+        .as_str()
+        .ok_or("Missing payload")?;
+
+    let payload_string = hex_to_string(payload)?;
+
+    emit_report(payload_string).await;
+    Ok("accept")
+}
+
+fn hex_to_string(hex: &str) -> Result> {
+    let hexstr = hex.strip_prefix("0x").unwrap_or(hex);
+    let bytes = hex::decode(hexstr).map_err(|e| Box::new(e) as Box)?;
+    let s = String::from_utf8(bytes).map_err(|e| Box::new(e) as Box)?;
+    Ok(s)
+}
+
+async fn emit_report( payload: String) -> Option {
+    let hex_string = {
+        let s = payload.strip_prefix("0x").unwrap_or(payload.as_str());
+        hex::encode(s.as_bytes())
+    };
+
+    let server_addr = env::var("ROLLUP_HTTP_SERVER_URL").expect("ROLLUP_HTTP_SERVER_URL not set");
+    let client = hyper::Client::new();
+
+    let response = object! {
+        "payload" => format!("0x{}", hex_string),
+    };
+    let request = hyper::Request::builder()
+    .method(hyper::Method::POST)
+    .header(hyper::header::CONTENT_TYPE, "application/json")
+    .uri(format!("{}/report", server_addr))
+    .body(hyper::Body::from(response.dump()))
+    .ok()?;
+
+    let response = client.request(request).await;
+    match response {
+        Ok(_) => {
+            println!("Report generation successful");
+            return Some(true);
+        }
+        Err(e) => {
+            println!("Report request failed {}", e);
+            None
+        }
+    }
+}
+```
+
+
+
+ You can use the exposed GraphQL API to query all reports from your application. diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/send-inputs-and-assets.md b/cartesi-rollups_versioned_docs/version-2.0/development/send-inputs-and-assets.md index f004f685..89db12e6 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/development/send-inputs-and-assets.md +++ b/cartesi-rollups_versioned_docs/version-2.0/development/send-inputs-and-assets.md @@ -119,6 +119,83 @@ while True: + +

+
+```Rust
+use json::{object, JsonValue};
+use std::env;
+
+pub async fn handle_advance(
+    _client: &hyper::Client,
+    _server_addr: &str,
+    request: JsonValue,
+) -> Result<&'static str, Box> {
+    println!("Received advance request data {}", &request);
+    let _payload = request["data"]["payload"]
+        .as_str()
+        .ok_or("Missing payload")?;
+    // TODO: add application logic here
+    Ok("accept")
+}
+
+pub async fn handle_inspect(
+    _client: &hyper::Client,
+    _server_addr: &str,
+    request: JsonValue,
+) -> Result<&'static str, Box> {
+    println!("Received inspect request data {}", &request);
+    let _payload = request["data"]["payload"]
+        .as_str()
+        .ok_or("Missing payload")?;
+    // TODO: add application logic here
+    Ok("accept")
+}
+
+#[tokio::main]
+async fn main() -> Result<(), Box> {
+    let client = hyper::Client::new();
+    let server_addr = env::var("ROLLUP_HTTP_SERVER_URL")?;
+
+    let mut status = "accept";
+    loop {
+        println!("Sending finish");
+        let response = object! {"status" => status};
+        let request = hyper::Request::builder()
+            .method(hyper::Method::POST)
+            .header(hyper::header::CONTENT_TYPE, "application/json")
+            .uri(format!("{}/finish", &server_addr))
+            .body(hyper::Body::from(response.dump()))?;
+        let response = client.request(request).await?;
+        println!("Received finish status {}", response.status());
+
+        if response.status() == hyper::StatusCode::ACCEPTED {
+            println!("No pending rollup request, trying again");
+        } else {
+            let body = hyper::body::to_bytes(response).await?;
+            let utf = std::str::from_utf8(&body)?;
+            let req = json::parse(utf)?;
+
+            let request_type = req["request_type"]
+                .as_str()
+                .ok_or("request_type is not a string")?;
+            status = match request_type {
+                "advance_state" => handle_advance(&client, &server_addr[..], req).await?,
+                "inspect_state" => handle_inspect(&client, &server_addr[..], req).await?,
+                &_ => {
+                    eprintln!("Unknown request type");
+                    "reject"
+                }
+            };
+        }
+    }
+}
+
+```
+
+
+
+ As stated in the [creating an application section](./creating-an-application.md#implementing-your-application-logic), a typical Cartesi backend application has two primary functions, `handle_advance` and `handle_inspect,` these are defined to handle the two different types of requests. diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/snippets/implementing_outputs_js.md b/cartesi-rollups_versioned_docs/version-2.0/development/snippets/implementing_outputs_js.md new file mode 100644 index 00000000..7c03ae1a --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-2.0/development/snippets/implementing_outputs_js.md @@ -0,0 +1,112 @@ +```javascript +import { stringToHex, encodeFunctionData, erc20Abi, hexToString, zeroHash } from "viem"; + +const rollup_server = process.env.ROLLUP_HTTP_SERVER_URL; +console.log("HTTP rollup_server url is " + rollup_server); + +async function handle_advance(data) { + console.log("Received advance request data " + JSON.stringify(data)); + + const sender = data["metadata"]["msg_sender"]; + const payload = hexToString(data.payload); + const erc20Token = "0x784f0c076CC55EAD0a585a9A13e57c467c91Dc3a"; // Sample ERC20 token address + + await emitNotice(payload); + await emitReport(payload); + + const call = encodeFunctionData({ + abi: erc20Abi, + functionName: "transfer", + args: [sender, BigInt(10)], + }); + + let voucher = { + destination: erc20Token, + payload: call, + value: zeroHash, + }; + + await emitVoucher(voucher); + return "accept"; +} + +async function handle_inspect(data) { + console.log("Received inspect request data " + JSON.stringify(data)); + const payload = data.payload; + await emitReport(payload); + return "accept"; +} + +const emitNotice = async (inputPayload) => { + let hexPayload = stringToHex(inputPayload); + try { + await fetch(rollup_server + "/notice", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ payload: hexPayload }), + }); + } catch (error) { + //Do something when there is an error + } +} + +const emitVoucher = async (voucher) => { + try { + await fetch(rollup_server + "/voucher", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(voucher), + }); + } catch (error) { + //Do something when there is an error + } +}; + +const emitReport = async (payload) => { + let hexPayload = stringToHex(payload); + try { + await fetch(rollup_server + "/report", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ payload: hexPayload }), + }); + } catch (error) { + //Do something when there is an error + } +}; + +var handlers = { + advance_state: handle_advance, + inspect_state: handle_inspect, +}; + +var finish = { status: "accept" }; + +(async () => { + while (true) { + const finish_req = await fetch(rollup_server + "/finish", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ status: "accept" }), + }); + + console.log("Received finish status " + finish_req.status); + + if (finish_req.status == 202) { + console.log("No pending rollup request, trying again"); + } else { + const rollup_req = await finish_req.json(); + var handler = handlers[rollup_req["request_type"]]; + finish["status"] = await handler(rollup_req["data"]); + } + } +})(); +``` \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/snippets/implementing_outputs_py.md b/cartesi-rollups_versioned_docs/version-2.0/development/snippets/implementing_outputs_py.md new file mode 100644 index 00000000..70ecb508 --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-2.0/development/snippets/implementing_outputs_py.md @@ -0,0 +1,129 @@ +```python +from os import environ +import logging +import requests +import binascii +import json +from eth_utils import function_signature_to_4byte_selector +from eth_abi import encode + +logging.basicConfig(level="INFO") +logger = logging.getLogger(__name__) + +rollup_server = environ["ROLLUP_HTTP_SERVER_URL"] +logger.info(f"HTTP rollup_server url is {rollup_server}") + + +def handle_advance(data): + logger.info(f"Received advance request data {data}") + sender = data["metadata"]["msg_sender"] + app_contract = data["metadata"]["app_contract"] + payload = data["payload"] + + erc20Token = "0x784f0c076CC55EAD0a585a9A13e57c467c91Dc3a"; # Sample ERC20 token address + + emitNotice(payload); + emitReport(payload); + + voucher = structure_voucher( + "transferFrom(address,uint256)", + erc20Token, + ["address", "uint256"], + [sender, 10], + ) + + emitVoucher(voucher); + return "accept" + +def handle_inspect(data): + logger.info(f"Received inspect request data {data}") + return "accept" + +def structure_voucher(function_signature, destination, types, values, value=0) -> dict: + selector = function_signature_to_4byte_selector(function_signature) + encoded_args = encode(types, values) + payload = "0x" + (selector + encoded_args).hex() + + return { + "destination": destination, + "payload": payload, + "value": hex(value) + } + + +def string_to_hex(s: str) -> str: + return "0x" + binascii.hexlify(s.encode("utf-8")).decode() + +def hex_to_string(hexstr: str) -> str: + if not isinstance(hexstr, str): + return "" + if hexstr.startswith("0x"): + hexstr = hexstr[2:] + if hexstr == "": + return "" + try: + return binascii.unhexlify(hexstr).decode("utf-8") + except UnicodeDecodeError: + return "0x" + hexstr + + +def emitReport(payload: str): + hex_payload = string_to_hex(payload) + try: + response = requests.post( + f"{rollup_server}/report", + json={"payload": hex_payload}, + headers={"Content-Type": "application/json"}, + timeout=5, + ) + logger.info(f"emit_report → status {response.status_code}") + except requests.RequestException as error: + logger.error("Error emitting report: %s", error) + + +def emitVoucher(voucher: dict): + try: + response = requests.post( + f"{rollup_server}/voucher", + json= voucher, + headers={"Content-Type": "application/json"}, + timeout=5, + ) + logger.info(f"emit_voucher → status {response.status_code}") + except requests.RequestException as error: + logger.error("Error emitting voucher: %s", error) + +def emitNotice(payload: str): + hex_payload = string_to_hex(payload) + try: + response = requests.post( + f"{rollup_server}/notice", + json={"payload": hex_payload}, + headers={"Content-Type": "application/json"}, + timeout=5, + ) + logger.info(f"emit_notice → status {response.status_code}") + except requests.RequestException as error: + logger.error("Error emitting notice: %s", error) + + +handlers = { + "advance_state": handle_advance, + "inspect_state": handle_inspect, +} + +finish = {"status": "accept"} + +while True: + logger.info("Sending finish") + response = requests.post(rollup_server + "/finish", json=finish) + logger.info(f"Received finish status {response.status_code}") + if response.status_code == 202: + logger.info("No pending rollup request, trying again") + else: + rollup_request = response.json() + data = rollup_request["data"] + 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-2.0/development/snippets/implementing_outputs_rs.md b/cartesi-rollups_versioned_docs/version-2.0/development/snippets/implementing_outputs_rs.md new file mode 100644 index 00000000..08250e6d --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-2.0/development/snippets/implementing_outputs_rs.md @@ -0,0 +1,211 @@ +```rust +use json::{object, JsonValue}; +use std::env; +use ethers_core::abi::{encode, Token}; +use ethers_core::utils::id; +use hex; +use serde::Serialize; + +fn structure_voucher( + function_signature: &str, + destination: &str, + args: Vec, + value: u128, +) -> JsonValue { + let selector = &id(function_signature)[..4]; + + let encoded_args = encode(&args); + + let mut payload_bytes = Vec::new(); + payload_bytes.extend_from_slice(selector); + payload_bytes.extend_from_slice(&encoded_args); + + let payload = format!("0x{}", hex::encode(payload_bytes)); + + let response = object! { + "destination" => format!("{}", destination), + "payload" => format!("{}", payload), + "value" => format!("0x{}", hex::encode(value.to_be_bytes())), + }; + + return response; +} + +pub async fn handle_advance( + _client: &hyper::Client, + _server_addr: &str, + request: JsonValue, +) -> Result<&'static str, Box> { + println!("Received advance request data {}", &request); + let payload = request["data"]["payload"] + .as_str() + .ok_or("Missing payload")?; + + let sender = request["data"]["metadata"]["msg_sender"] + .as_str() + .ok_or("Missing msg_sender in metadata")? + .to_string(); + + const erc20Token: &str = "0x784f0c076CC55EAD0a585a9A13e57c467c91Dc3a"; // Sample ERC20 token address + + emit_notice(payload.to_string()).await; + emit_report(payload.to_string()).await; + + let amount = 10; + + let args = vec![ + Token::Address(sender.parse().unwrap()), + Token::Uint(amount.into()), + ]; + let function_sig = "transfer(address,uint256)"; + + let voucher = structure_voucher(function_sig, &sender, args, 0); + emit_voucher(voucher).await; + + Ok("accept") +} + +pub async fn handle_inspect( + _client: &hyper::Client, + _server_addr: &str, + request: JsonValue, +) -> Result<&'static str, Box> { + println!("Received inspect request data {}", &request); + let _payload = request["data"]["payload"] + .as_str() + .ok_or("Missing payload")?; + // TODO: add application logic here + Ok("accept") +} + +async fn emit_report( payload: String) -> Option { + let hex_string = { + let s = payload.strip_prefix("0x").unwrap_or(payload.as_str()); + hex::encode(s.as_bytes()) + }; + + let server_addr = env::var("ROLLUP_HTTP_SERVER_URL").expect("ROLLUP_HTTP_SERVER_URL not set"); + let client = hyper::Client::new(); + + let response = object! { + "payload" => format!("0x{}", hex_string), + }; + let request = hyper::Request::builder() + .method(hyper::Method::POST) + .header(hyper::header::CONTENT_TYPE, "application/json") + .uri(format!("{}/report", server_addr)) + .body(hyper::Body::from(response.dump())) + .ok()?; + + let response = client.request(request).await; + match response { + Ok(_) => { + println!("Report generation successful"); + return Some(true); + } + Err(e) => { + println!("Report request failed {}", e); + None + } + } +} + +async fn emit_notice( payload: String) -> Option { + let hex_string = { + let s = payload.strip_prefix("0x").unwrap_or(payload.as_str()); + hex::encode(s.as_bytes()) + }; + + let server_addr = env::var("ROLLUP_HTTP_SERVER_URL").expect("ROLLUP_HTTP_SERVER_URL not set"); + let client = hyper::Client::new(); + + let response = object! { + "payload" => format!("0x{}", hex_string), + }; + let request = hyper::Request::builder() + .method(hyper::Method::POST) + .header(hyper::header::CONTENT_TYPE, "application/json") + .uri(format!("{}/notice", server_addr)) + .body(hyper::Body::from(response.dump())) + .ok()?; + + let response = client.request(request).await; + + match response { + Ok(_) => { + println!("Notice generation successful"); + return Some(true); + } + Err(e) => { + println!("Notice request failed {}", e); + None + } + } +} + +async fn emit_voucher( voucher: JsonValue) -> Option { + let server_addr = env::var("ROLLUP_HTTP_SERVER_URL").expect("ROLLUP_HTTP_SERVER_URL not set"); + let client = hyper::Client::new(); + + let request = hyper::Request::builder() + .method(hyper::Method::POST) + .header(hyper::header::CONTENT_TYPE, "application/json") + .uri(format!("{}/voucher", server_addr)) + .body(hyper::Body::from(voucher.dump())) + .ok()?; + + let response = client.request(request).await; + + match response { + Ok(_) => { + println!("Voucher generation successful"); + return Some(true); + } + Err(e) => { + println!("Voucher request failed {}", e); + None + } + } +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = hyper::Client::new(); + let server_addr = env::var("ROLLUP_HTTP_SERVER_URL")?; + + let mut status = "accept"; + loop { + println!("Sending finish"); + let response = object! {"status" => status}; + let request = hyper::Request::builder() + .method(hyper::Method::POST) + .header(hyper::header::CONTENT_TYPE, "application/json") + .uri(format!("{}/finish", &server_addr)) + .body(hyper::Body::from(response.dump()))?; + let response = client.request(request).await?; + println!("Received finish status {}", response.status()); + + if response.status() == hyper::StatusCode::ACCEPTED { + println!("No pending rollup request, trying again"); + } else { + let body = hyper::body::to_bytes(response).await?; + let utf = std::str::from_utf8(&body)?; + let req = json::parse(utf)?; + + let request_type = req["request_type"] + .as_str() + .ok_or("request_type is not a string")?; + status = match request_type { + "advance_state" => handle_advance(&client, &server_addr[..], req).await?, + "inspect_state" => handle_inspect(&client, &server_addr[..], req).await?, + &_ => { + eprintln!("Unknown request type"); + "reject" + } + }; + } + } +} + + +``` \ No newline at end of file From 923b193751a8666dd00c04f9299fb486af993033 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Tue, 4 Nov 2025 15:59:18 +0100 Subject: [PATCH 04/10] mod(docs): added rust snippets for API Reference section --- .../api-reference/backend/exception.md | 70 ++++++++++++++ .../api-reference/backend/finish.md | 76 ++++++++++++++++ .../api-reference/backend/notices.md | 49 ++++++++++ .../api-reference/backend/reports.md | 44 +++++++++ .../api-reference/backend/vouchers.md | 91 +++++++++++++++++++ .../snippets/implementing_outputs_rs.md | 4 +- 6 files changed, 332 insertions(+), 2 deletions(-) diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/exception.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/exception.md index 90ec4ae1..6d060749 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/exception.md +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/exception.md @@ -81,6 +81,76 @@ def handle_advance(data): + +

+
+```rust
+fn hex_to_string(hex: &str) -> Result> {
+    let hexstr = hex.strip_prefix("0x").unwrap_or(hex);
+    let bytes = hex::decode(hexstr).map_err(|e| Box::new(e) as Box)?;
+    let s = String::from_utf8(bytes).map_err(|e| Box::new(e) as Box)?;
+    Ok(s)
+}
+
+pub async fn handle_advance(
+    _client: &hyper::Client,
+    _server_addr: &str,
+    request: JsonValue,
+) -> Result<&'static str, Box> {
+    println!("Received advance request data {}", &request);
+    let payload = request["data"]["payload"]
+        .as_str()
+        .ok_or("Missing payload")?;
+   
+    let payload_string = hex_to_string(payload);
+
+    match payload_string {
+      Ok(payload_extract) => {
+        // DO SOMETHING HERE!!
+      }
+      Err(e) => {
+        throw_execption(e.to_string()).await;
+      }
+    }
+    Ok("accept")
+}
+
+async fn throw_execption( payload: String) -> Option {
+    let hex_string = {
+        let s = payload.strip_prefix("0x").unwrap_or(payload.as_str());
+        hex::encode(s.as_bytes())
+    };
+
+    let server_addr = env::var("ROLLUP_HTTP_SERVER_URL").expect("ROLLUP_HTTP_SERVER_URL not set");
+    let client = hyper::Client::new();
+
+    let response = object! {
+        "payload" => format!("0x{}", hex_string),
+    };
+    let request = hyper::Request::builder()
+    .method(hyper::Method::POST)
+    .header(hyper::header::CONTENT_TYPE, "application/json")
+    .uri(format!("{}/exception", server_addr))
+    .body(hyper::Body::from(response.dump()))
+    .ok()?;
+
+    let response = client.request(request).await;
+    match response {
+        Ok(_) => {
+            println!("Exception generation successful");
+            return Some(true);
+        }
+        Err(e) => {
+            println!("Exception request failed {}", e);
+            None
+        }
+    }
+}
+```
+
+
+
+ ## Notes diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/finish.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/finish.md index 896a494c..27a3a6a7 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/finish.md +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/finish.md @@ -129,6 +129,82 @@ while True: + +

+
+```rust
+use json::{object, JsonValue};
+use std::env;
+
+pub async fn handle_advance(
+    _client: &hyper::Client,
+    _server_addr: &str,
+    request: JsonValue,
+) -> Result<&'static str, Box> {
+    println!("Received advance request data {}", &request);
+    let _payload = request["data"]["payload"]
+        .as_str()
+        .ok_or("Missing payload")?;
+    // TODO: add application logic here
+    Ok("accept")
+}
+
+pub async fn handle_inspect(
+    _client: &hyper::Client,
+    _server_addr: &str,
+    request: JsonValue,
+) -> Result<&'static str, Box> {
+    println!("Received inspect request data {}", &request);
+    let _payload = request["data"]["payload"]
+        .as_str()
+        .ok_or("Missing payload")?;
+    // TODO: add application logic here
+    Ok("accept")
+}
+
+#[tokio::main]
+async fn main() -> Result<(), Box> {
+    let client = hyper::Client::new();
+    let server_addr = env::var("ROLLUP_HTTP_SERVER_URL")?;
+
+    let mut status = "accept";
+    loop {
+        println!("Sending finish");
+        let response = object! {"status" => status};
+        let request = hyper::Request::builder()
+            .method(hyper::Method::POST)
+            .header(hyper::header::CONTENT_TYPE, "application/json")
+            .uri(format!("{}/finish", &server_addr))
+            .body(hyper::Body::from(response.dump()))?;
+        let response = client.request(request).await?;
+        println!("Received finish status {}", response.status());
+
+        if response.status() == hyper::StatusCode::ACCEPTED {
+            println!("No pending rollup request, trying again");
+        } else {
+            let body = hyper::body::to_bytes(response).await?;
+            let utf = std::str::from_utf8(&body)?;
+            let req = json::parse(utf)?;
+
+            let request_type = req["request_type"]
+                .as_str()
+                .ok_or("request_type is not a string")?;
+            status = match request_type {
+                "advance_state" => handle_advance(&client, &server_addr[..], req).await?,
+                "inspect_state" => handle_inspect(&client, &server_addr[..], req).await?,
+                &_ => {
+                    eprintln!("Unknown request type");
+                    "reject"
+                }
+            };
+        }
+    }
+}
+```
+
+
+
+ ## Notes diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/notices.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/notices.md index 2a6b8ae6..09e9280a 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/notices.md +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/notices.md @@ -77,6 +77,55 @@ def handle_advance(data): + +

+
+```rust
+pub async fn handle_advance(
+    _client: &hyper::Client,
+    _server_addr: &str,
+    request: JsonValue,
+) -> Result<&'static str, Box> {
+    println!("Received advance request data {}", &request);
+    let payload = request["data"]["payload"]
+        .as_str()
+        .ok_or("Missing payload")?;
+
+
+    emit_notice(payload).await;
+    Ok("accept")
+}
+
+async fn emit_notice(payload: String) -> Option {
+    let server_addr = env::var("ROLLUP_HTTP_SERVER_URL").expect("ROLLUP_HTTP_SERVER_URL not set");
+    let client = hyper::Client::new();
+
+    let response = object! {
+        "payload" => payload,
+    };
+    let request = hyper::Request::builder()
+    .method(hyper::Method::POST)
+    .header(hyper::header::CONTENT_TYPE, "application/json")
+    .uri(format!("{}/notice", server_addr))
+    .body(hyper::Body::from(response.dump()))
+    .ok()?;
+
+    let response = client.request(request).await;
+    match response {
+        Ok(_) => {
+            println!("Notice generation successful");
+            return Some(true);
+        }
+        Err(e) => {
+            println!("Notice request failed {}", e);
+            None
+        }
+    }
+}
+```
+
+
+
:::note querying notices diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/reports.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/reports.md index 5429ac8d..7d82f3f3 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/reports.md +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/reports.md @@ -75,6 +75,50 @@ def handle_advance(data): + +

+
+```rust
+pub async fn handle_advance(
+    _client: &hyper::Client,
+    _server_addr: &str,
+    request: JsonValue,
+) -> Result<&'static str, Box> {
+    println!("Received advance request data {}", &request);
+    let payload = request["data"]["payload"]
+        .as_str()
+        .ok_or("Missing payload")?;
+
+    let server_addr = env::var("ROLLUP_HTTP_SERVER_URL").expect("ROLLUP_HTTP_SERVER_URL not set");
+    let client = hyper::Client::new();
+
+    let response = object! {
+        "payload" => payload,
+    };
+    let request = hyper::Request::builder()
+    .method(hyper::Method::POST)
+    .header(hyper::header::CONTENT_TYPE, "application/json")
+    .uri(format!("{}/report", server_addr))
+    .body(hyper::Body::from(response.dump()))
+    .ok()?;
+
+    let response = client.request(request).await;
+    match response {
+        Ok(_) => {
+            println!("Report generation successful");
+            Ok("accept")
+        }
+        Err(e) => {
+            println!("Report request failed {}", e);
+            Err("Report request failed {}")
+        }
+    }
+}
+```
+
+
+
+ :::note querying reports diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/vouchers.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/vouchers.md index 3c7f0639..c49c1301 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/vouchers.md +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/vouchers.md @@ -98,6 +98,97 @@ emit_voucher("mint(address)", "0x784f0c076CC55EAD0a585a9A13e57c467c91Dc3a", ["ad + + +

+
+```rust
+async fn emit_voucher( voucher: JsonValue) -> Option {
+    let server_addr = env::var("ROLLUP_HTTP_SERVER_URL").expect("ROLLUP_HTTP_SERVER_URL not set");
+    let client = hyper::Client::new();
+
+    let request = hyper::Request::builder()
+    .method(hyper::Method::POST)
+    .header(hyper::header::CONTENT_TYPE, "application/json")
+    .uri(format!("{}/voucher", server_addr))
+    .body(hyper::Body::from(voucher.dump()))
+    .ok()?;
+
+    let response = client.request(request).await;
+
+    match response {
+        Ok(_) => {
+            println!("Voucher generation successful");
+            return Some(true);
+        }
+        Err(e) => {
+            println!("Voucher request failed {}", e);
+            None
+        }
+    }
+}
+
+fn structure_voucher(
+    function_signature: &str,
+    destination: &str,
+    args: Vec,
+    value: u128,
+) -> JsonValue {
+    let selector = &id(function_signature)[..4];
+
+    let encoded_args = encode(&args);
+
+    let mut payload_bytes = Vec::new();
+    payload_bytes.extend_from_slice(selector);
+    payload_bytes.extend_from_slice(&encoded_args);
+
+    let payload = format!("0x{}", hex::encode(payload_bytes));
+
+    let response = object! {
+        "destination" => format!("{}", destination),
+        "payload" => format!("{}", payload),
+        "value" => format!("0x{}", hex::encode(value.to_be_bytes())),
+    };
+
+    return response;
+}
+
+pub async fn handle_advance(
+    _client: &hyper::Client,
+    _server_addr: &str,
+    request: JsonValue,
+) -> Result<&'static str, Box> {
+    println!("Received advance request data {}", &request);
+    let payload = request["data"]["payload"]
+        .as_str()
+        .ok_or("Missing payload")?;
+   
+    let sender = request["data"]["metadata"]["msg_sender"]
+        .as_str()
+        .ok_or("Missing msg_sender in metadata")?
+        .to_string();
+
+    let erc_20_token: &str = "0x784f0c076CC55EAD0a585a9A13e57c467c91Dc3a"; // Sample ERC20 token address
+
+    let amount = 10;
+
+    let args = vec![
+        Token::Address(sender.parse().unwrap()),
+        Token::Uint(amount.into()),
+    ];
+    let function_sig = "transfer(address,uint256)";
+
+    let voucher = structure_voucher(function_sig, erc_20_token, args, 0);
+    emit_voucher(voucher).await;
+
+    Ok("accept")
+}
+
+```
+
+
+
+ :::note create a voucher diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/snippets/implementing_outputs_rs.md b/cartesi-rollups_versioned_docs/version-2.0/development/snippets/implementing_outputs_rs.md index 08250e6d..a1a099d1 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/development/snippets/implementing_outputs_rs.md +++ b/cartesi-rollups_versioned_docs/version-2.0/development/snippets/implementing_outputs_rs.md @@ -46,7 +46,7 @@ pub async fn handle_advance( .ok_or("Missing msg_sender in metadata")? .to_string(); - const erc20Token: &str = "0x784f0c076CC55EAD0a585a9A13e57c467c91Dc3a"; // Sample ERC20 token address + const erc_20_token: &str = "0x784f0c076CC55EAD0a585a9A13e57c467c91Dc3a"; // Sample ERC20 token address emit_notice(payload.to_string()).await; emit_report(payload.to_string()).await; @@ -59,7 +59,7 @@ pub async fn handle_advance( ]; let function_sig = "transfer(address,uint256)"; - let voucher = structure_voucher(function_sig, &sender, args, 0); + let voucher = structure_voucher(function_sig, &erc_20_token, args, 0); emit_voucher(voucher).await; Ok("accept") From 8c7f5862e89c2cd213e8cd66949ffd8a1d8be8de Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Mon, 29 Sep 2025 10:09:32 +0100 Subject: [PATCH 05/10] docs(tutorials): added tutorial for utilising test tokens --- ...tilizing-test-tokens-in-dev-environment.md | 1674 +++++++++++++++++ .../version-2.0-sidebars.json | 3 +- 2 files changed, 1676 insertions(+), 1 deletion(-) create mode 100644 cartesi-rollups_versioned_docs/version-2.0/tutorials/Utilizing-test-tokens-in-dev-environment.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/tutorials/Utilizing-test-tokens-in-dev-environment.md b/cartesi-rollups_versioned_docs/version-2.0/tutorials/Utilizing-test-tokens-in-dev-environment.md new file mode 100644 index 00000000..a4be57d0 --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-2.0/tutorials/Utilizing-test-tokens-in-dev-environment.md @@ -0,0 +1,1674 @@ +--- +id: utilizing-the-cli-test-tokens +title: Utilizing test tokens in dev environment +resources: +--- + +## Introduction + +The **Cartesi CLI** is one of the most important tools for developing applications with Cartesi. It provides a wide range of functionalities that simplify and automate the process of setting up, deploying, and interacting with your applications. + +When you run your application using the `cartesi run` command, the CLI automatically spins up a **Local anvil network** in a Docker container. Within this network, all the required contracts are deployed such as the **InputBox contract**, your **application contract**, and **Portals contract**. + +In addition, the CLI also deploys **test token contracts** by default: + +- ERC20 (`TestToken`) +- ERC721 (`TestNFT`) +- ERC1155 (`TestMultiToken`) + +These contracts are owned by the **Anvil's first wallet address**. This ensures developers have immediate access to and ownership over these tokens for testing purposes, without the need for manual deployments. More details about the different addresses provided by anvil can be found on the [Anvil official Docs](https://getfoundry.sh/anvil/overview#getting-started). + +This tutorial will guide you through **using these default tokens** effectively while developing in a local devnet environment. + +## Tokens basic Information + +The following table lists the default test tokens deployed by the CLI: + +| Token | keyword | Value | +| --------------|-------------------------------------------|-----------------------------------------------| +| ERC20 Token | Token Name | TestToken | +| | Token Symbol | TEST | +| | Token Decimal | 18 | +| | Token Address | 0xFBdB734EF6a23aD76863CbA6f10d0C5CBBD8342C | +| | Contract Owner | 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 | +| ERC721 Token | Token Name | TestNFT | +| | Token Symbol | SUNN | +| | Token Address | 0xBa46623aD94AB45850c4ecbA9555D26328917c3B | +| | Contract Owner | 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 | +| ERC1155 Token | Token Name | TestMultiToken | +| | Token Address | 0xDC6d64971B77a47fB3E3c6c409D4A05468C398D2 | +| | Contract Owner | 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 | + +You can also view these token addresses at any time using the CLI command: + +```bash +cartesi address-book +``` + +## ERC20 Test Token + +The ERC20 `TestToken` is based on the OpenZeppelin ERC20 implementation. It is deployed alongside your application contracts on the anvil devnet, with ownership assigned to the default anvil wallet `0xFBdB734EF6a23aD76863CbA6f10d0C5CBBD8342C`, ensuring that developers can readily access these test tokens without needing to manually deploy or transfer new token contracts. However, you can transfer these tokens to other wallets and also to your applications for the duration of which your anvil network is active as these transactions reset every time you restart your anvil devnet, or stop your application using the Cartesi CLI. + +### Minting ERC20 Test Tokens + +Minting new tokens on the `TestToken` is currently disabled, but at deployment `1000000000 * 10 units` of the token was minted to the default anvil wallet `0xFBdB734EF6a23aD76863CbA6f10d0C5CBBD8342C`, this amount should be more than suitable for testing your application. With that covered, we can proceed to transferring and also depositing these tokens to your application. + +### Transferring the ERC20 Test Token to other wallets + +When testing interactions that require multiple addresses (e.g., deposits from different users), you may need to transfer tokens from the owner account to other wallets. +For this example, we’ll use the second Anvil address as the recipient. You can change the recipient to any address of your choice. + +#### Using Cast Command + + + + +

+
+```bash
+cast send  "transfer(address,uint256)"   --rpc-url  --private-key 
+```
+
+
+
+
+ + +

+
+```bash
+cast send 0xFBdB734EF6a23aD76863CbA6f10d0C5CBBD8342C "transfer(address,uint256)" 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 202 --rpc-url http://127.0.0.1:6751/anvil --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
+```
+
+
+
+ +
+ +The above command calls the `transfer(address,uint256)` in the `testToken` contract `0xFBdB7...8342C`. The command passes the following argument to the function: `receivers address= 0x709979...c79C8`and `amount= 202`. While you can always change the amount argument to match what you intend to deposit to your application, it's important to always maintain the same `testToken` contract as well as the private-key `0xac0974b...f2ff80`. + +### Depositing the ERC20 Test Token to your application + +Similar to mainnet, applications undergoing development or testing on devnet can also receive erc20 tokens deposit, it is essential that your application includes the proper logic to receive and also record token deposit. This example will guide you through the process of depositing the test token to your application and also how to properly decode the deposit data sent to your application. + +There are two main approaches: + +#### Using the CLI + +- Ensure you have your Cartesi application running, then from project directory, open a terminal and run the below command: + +``` bash +cartesi deposit +``` + +- Select ERC20 from the list of deposit options. + +- The next menu requests for a token address, but has the `TestToken` address filled but grayed out, hit the enter button to use the `TestToken`. + +- In the next menu, enter the amount to tokens, you intend to deposit then finally hit enter. + +- This would trigger the approval process, then deposit the tokens to your application. + +For a successful deposit process you should show logs similar to this: + +```bash +✔ Select type of asset to deposit erc20 +✔ Token address 0xFBdB734EF6a23aD76863CbA6f10d0C5CBBD8342C +✔ Amount (TEST) 202 +✔ Approved 202 TEST +✔ Deposited 202 TEST to 0xba3347e79665924033beeb7362629ca7992897d9 +``` + +#### Using Cast + +It's also possible to use cast commands to deposit assets to your application, but this would require a more manual process of granting approval, then calling the deposit function in the ERC20Portal contract. Below is a step-by-step process to achieve this: + +- Call the `testToken` contract to approve the `ERC20Portal` an equivalent of the amount of tokens you intend to deposit, using the below command + + + + +

+
+```bash
+cast send  "approve(address,uint256)"   --rpc-url  --private-key 
+```
+
+
+
+ + +

+
+```bash
+cast send 0xFBdB734EF6a23aD76863CbA6f10d0C5CBBD8342C "approve(address,uint256)" 0xc700D6aDd016eECd59d989C028214Eaa0fCC0051 300000000000000000000 --rpc-url http://127.0.0.1:6751/anvil --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
+```
+
+
+
+ +
+ +The snippet above is an example of how to deposit 300 units of the test tokens, `http://127.0.0.1:6751/anvil` is the anvil_rpc to the local devnet where the application contract is deployed, finally the private_key `0xac0974bec39a17...e784d7bf4f2ff80` is the private key of the default anvil address which is also the owner of the erc20 test contract. + +- Call the `depositERC20Tokens` function in the `ERC20Portal` contract, passing in the address of your contract along with the token address and amount of tokens deposited + + + + +

+
+```bash
+cast send  "depositERC20Tokens(address,address,uint256,bytes)"     --rpc-url  --private-key 
+```
+
+
+
+ + +

+
+```bash
+cast send 0xc700D6aDd016eECd59d989C028214Eaa0fCC0051 "depositERC20Tokens(address,address,uint256,bytes)" 0xFBdB734EF6a23aD76863CbA6f10d0C5CBBD8342C 0xba3347e79665924033beeb7362629ca7992897d9 202 0x --rpc-url http://127.0.0.1:6751/anvil --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
+```
+
+
+
+ +
+ +The above command interacts with the ERC20Portal which utilizes the allowance issued in the previous function to call the `transferFrom` function in the `testToken` contract. This operation transfers the amount of tokens listed to the application contract, and calls the input box to send an input to your application. + +The next section covers implementations on your application to handle deposits. + +#### Handling Deposited ERC20 tokens + +One important part of the deposit workflow is the decoding of the inputs. It is an important and delicate process, requiring accurate decoding as errors in this section could lease to unaccounted deposits. + +While the previous example, help deposit tokens to your applications, without proper logic implementation, your application will be unable to record and track tokens deposited by users. The below code snippet is a simple application designed to receive deposit calls, decode, then finally log the data it received. You can test the application through the following steps: + +- Create a new JavaScript application, using the command: + + + +

+```bash
+cartesi create deposit-erc20 --template javascript
+```
+
+
+ + +

+```bash
+cartesi create deposit-erc20 --template rust
+```
+
+
+ + +

+```bash
+cartesi create deposit-erc20 --template python
+```
+
+
+
+ +- CD into the new project then copy and paste the below code snippet into index page. + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + +

+
+```JavaScript
+import { ethers } from "ethers";
+import {
+  getAddress,
+} from "viem";
+
+const rollup_server = process.env.ROLLUP_HTTP_SERVER_URL;
+console.log("HTTP rollup_server url is " + rollup_server);
+
+const PORTAL_ADDRESS = "0xc700D6aDd016eECd59d989C028214Eaa0fCC0051";
+
+
+function parseDepositPayload(payload) {
+    const tokenData = ethers.dataSlice(payload, 0, 20);
+    const senderData = ethers.dataSlice(payload, 20, 40);
+    const amountData = ethers.dataSlice(payload, 40, 72);
+
+    if (!tokenData) {
+      throw new Error("Invalid deposit payload");
+    }
+    return [getAddress(tokenData), getAddress(senderData), BigInt(amountData)];
+  }
+
+async function handle_advance(data) {
+  console.log("Received advance request data " + JSON.stringify(data));
+  let payload = data.payload;
+  const sender = data["metadata"]["msg_sender"];
+
+  if (sender.toLowerCase() === PORTAL_ADDRESS.toLowerCase()) {
+    console.log("Handling portal deposit");
+    const [token, depositor, amount] = parseDepositPayload(payload);
+    console.log(`Token: ${token}, Depositor: ${depositor}, Amount: ${amount}`);
+
+    // Handle deposit logic here
+  } else {
+      // Handle Transaction request like a regular transaction
+  }
+
+  return "accept";
+}
+
+async function handle_inspect(data) {
+  console.log("Received inspect request data " + JSON.stringify(data));
+  return "accept";
+}
+
+var handlers = {
+  advance_state: handle_advance,
+  inspect_state: handle_inspect,
+};
+
+var finish = { status: "accept" };
+
+(async () => {
+  while (true) {
+    const finish_req = await fetch(rollup_server + "/finish", {
+      method: "POST",
+      headers: {
+        "Content-Type": "application/json",
+      },
+      body: JSON.stringify({ status: "accept" }),
+    });
+
+    console.log("Received finish status " + finish_req.status);
+
+    if (finish_req.status == 202) {
+      console.log("No pending rollup request, trying again");
+    } else {
+      const rollup_req = await finish_req.json();
+      var handler = handlers[rollup_req["request_type"]];
+      finish["status"] = await handler(rollup_req["data"]);
+    }
+  }
+})();
+```
+
+
+
+ + +

+
+```Rust
+use json::{object, JsonValue};
+use primitive_types::{H160, U256};
+use std::env;
+use hex::FromHex;
+use std::error::Error;
+use std::io;
+
+pub async fn parse_deposit_payload(
+    payload: &str
+) -> Result<(String, String, U256), Box> {
+
+    let clean = payload.trim().trim_start_matches("0x");
+
+    let bytes: Vec = Vec::from_hex(clean).map_err(|e| -> Box { e.into() })?;
+
+    const TOKEN_ADDR_LEN: usize = 20;
+    const DEPOSITOR_ADDR_LEN: usize = 20;
+    const AMOUNT_LEN: usize = 32;
+    const MIN_LEN: usize = TOKEN_ADDR_LEN + DEPOSITOR_ADDR_LEN + AMOUNT_LEN;
+
+    if bytes.len() < MIN_LEN {
+        return Err(io::Error::new(io::ErrorKind::InvalidData, "payload too short").into());
+    }
+
+    let token_addr_bytes = bytes.get(0..20)
+        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "token address slice"))?;
+    let depositor_addr_bytes = bytes.get(20..40)
+        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "depositor address slice"))?;
+    let token_amount_32 = bytes.get(40..72)
+        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "token id slice"))?;
+
+    let token_address = format!("0x{}", hex::encode(token_addr_bytes));
+    let depositor_address = format!("0x{}", hex::encode(depositor_addr_bytes));
+
+    let token_amount = U256::from_big_endian(token_amount_32);
+
+    Ok((token_address, depositor_address, token_amount))
+}
+
+pub fn format_erc20_18(amount: U256) -> String {
+    let base = U256::from(10).pow(U256::from(18));
+    let int = amount / base;
+    let frac = amount % base;
+    format!("{}.{:018}", int, frac) 
+}
+
+pub async fn handle_advance(
+    _client: &hyper::Client,
+    _server_addr: &str,
+    request: JsonValue,
+) -> Result<&'static str, Box> {
+    println!("Received advance request data {}", &request);
+    let _payload = request["data"]["payload"]
+        .as_str()
+        .ok_or("Missing payload")?;
+
+    let sender = request["data"]["metadata"]["msg_sender"]
+        .as_str()
+        .ok_or("Missing sender")?;
+
+    let erc20_portal_address: &str = "0xc700D6aDd016eECd59d989C028214Eaa0fCC0051";
+
+    if sender.to_lowercase() == erc20_portal_address.to_lowercase() {
+        println!("Received a message from the ERC20 Portal contract");
+        match parse_deposit_payload(_payload).await {
+            Ok((token, depositor, token_amount)) => {
+                let formatted_amount = format_erc20_18(token_amount);
+                println!("Token: {token}, Depositor: {depositor}, token_amount: {formatted_amount}");
+            }
+            Err(e) => {
+                eprintln!("Failed to parse deposit payload: {}", e);
+                return Err("reject".into());
+            }
+        } 
+    }
+
+    // TODO: add application logic here
+    Ok("accept")
+}
+
+pub async fn handle_inspect(
+    _client: &hyper::Client,
+    _server_addr: &str,
+    request: JsonValue,
+) -> Result<&'static str, Box> {
+    println!("Received inspect request data {}", &request);
+    let _payload = request["data"]["payload"]
+        .as_str()
+        .ok_or("Missing payload")?;
+    // TODO: add application logic here
+    Ok("accept")
+}
+
+#[tokio::main]
+async fn main() -> Result<(), Box> {
+    let client = hyper::Client::new();
+    let server_addr = env::var("ROLLUP_HTTP_SERVER_URL")?;
+
+    let mut status = "accept";
+    loop {
+        println!("Sending finish");
+        let response = object! {"status" => status};
+        let request = hyper::Request::builder()
+            .method(hyper::Method::POST)
+            .header(hyper::header::CONTENT_TYPE, "application/json")
+            .uri(format!("{}/finish", &server_addr))
+            .body(hyper::Body::from(response.dump()))?;
+        let response = client.request(request).await?;
+        println!("Received finish status {}", response.status());
+
+        if response.status() == hyper::StatusCode::ACCEPTED {
+            println!("No pending rollup request, trying again");
+        } else {
+            let body = hyper::body::to_bytes(response).await?;
+            let utf = std::str::from_utf8(&body)?;
+            let req = json::parse(utf)?;
+
+            let request_type = req["request_type"]
+                .as_str()
+                .ok_or("request_type is not a string")?;
+            status = match request_type {
+                "advance_state" => handle_advance(&client, &server_addr[..], req).await?,
+                "inspect_state" => handle_inspect(&client, &server_addr[..], req).await?,
+                &_ => {
+                    eprintln!("Unknown request type");
+                    "reject"
+                }
+            };
+        }
+    }
+}
+
+```
+
+
+
+ + +

+
+```python
+from os import environ
+import logging
+import requests
+
+logging.basicConfig(level="INFO")
+logger = logging.getLogger(__name__)
+
+rollup_server = environ["ROLLUP_HTTP_SERVER_URL"]
+logger.info(f"HTTP rollup_server url is {rollup_server}")
+
+def parse_deposit_payload(payload: str):
+    
+    clean = payload.strip().removeprefix("0x")
+
+    bytes_data = bytes.fromhex(clean)
+
+    TOKEN_ADDR_LEN = 20
+    DEPOSITOR_ADDR_LEN = 20
+    TOKEN_ID_LEN = 32
+    EXPECTED_LEN = TOKEN_ADDR_LEN + DEPOSITOR_ADDR_LEN + TOKEN_ID_LEN
+    if len(bytes_data) < EXPECTED_LEN:
+        raise ValueError(f"Invalid payload length: expected {EXPECTED_LEN}, got {len(bytes_data)}")
+
+    token_addr_bytes = bytes_data[0:TOKEN_ADDR_LEN]
+    depositor_addr_bytes = bytes_data[TOKEN_ADDR_LEN:TOKEN_ADDR_LEN + DEPOSITOR_ADDR_LEN]
+    token_amount_32 = bytes_data[TOKEN_ADDR_LEN + DEPOSITOR_ADDR_LEN:EXPECTED_LEN]
+
+    token_address = "0x" + token_addr_bytes.hex()
+    depositor_address = "0x" + depositor_addr_bytes.hex()
+
+    token_amount = int.from_bytes(token_amount_32, byteorder="big")
+
+    return [token_address, depositor_address, token_amount]
+
+def format_erc20_18(amount: int) -> str:
+    base = 10 ** 18
+    integer_part = amount // base
+    fractional_part = amount % base
+    return f"{integer_part}.{fractional_part:018d}"
+
+def handle_advance(data):
+    logger.info(f"Received advance request data {data}")
+    sender = data["metadata"]["msg_sender"]
+    payload = data["payload"]
+    ERC20_PORTAL_ADDRESS = "0xc700D6aDd016eECd59d989C028214Eaa0fCC0051";
+    
+    if sender.lower() == ERC20_PORTAL_ADDRESS.lower():
+        try:
+            [token, depositor, token_amount] = parse_deposit_payload(payload)
+            formated_token = format_erc20_18(token_amount)
+            logger.info(f"Token: {token}, Depositor: {depositor}, token_amount: {formated_token}")
+        except ValueError as e:
+            logger.error(f"Failed to parse deposit payload: {e}")
+
+    return "accept"
+
+
+def handle_inspect(data):
+    logger.info(f"Received inspect request data {data}")
+    return "accept"
+
+
+handlers = {
+    "advance_state": handle_advance,
+    "inspect_state": handle_inspect,
+}
+
+finish = {"status": "accept"}
+
+while True:
+    logger.info("Sending finish")
+    response = requests.post(rollup_server + "/finish", json=finish)
+    logger.info(f"Received finish status {response.status_code}")
+    if response.status_code == 202:
+        logger.info("No pending rollup request, trying again")
+    else:
+        rollup_request = response.json()
+        data = rollup_request["data"]
+        handler = handlers[rollup_request["request_type"]]
+        finish["status"] = handler(rollup_request["data"])
+
+```
+
+
+
+ +
+ +- Install necessary dependencies for rust or javasctipt by running the command: + + + + +

+
+```bash
+npm install viem ethers
+```
+
+
+
+ + +

+
+```bash
+cargo add primitive_types
+cargo add hex
+```
+
+
+
+ +
+ +- Build and run your application using the command: + +```bash +cartesi build + +cartesi run +``` + +- In a new terminal, ensure you're in the directory of your Cartesi application then run deposit command and follow though on the prompts to deposit an ERC20 token. + +```bash +cartesi deposit +``` + +On successful deposit your application should log an object containing the deposited token, depositor and finally the amount deposited. + +Sample Log + +```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 495 "-" "node" 0.032192 +Received finish status 200 +Received advance request data {"metadata":{"chain_id":13370,"app_contract":"0xba3347e79665924033beeb7362629ca7992897d9","msg_sender":"0xc700d6add016eecd59d989c028214eaa0fcc0051","block_number":823,"block_timestamp":1757042397,"prev_randao":"0xf1db9cff537dd607f721ef4aee7d2b516d4adb7fbb0d1c707b72344527e6893e","input_index":0},"payload":"0xfbdb734ef6a23ad76863cba6f10d0c5cbbd8342cf39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000000000000000000000000000c93a592cfb2a00000"} +Handling portal deposit +Token: 0xFBdB734EF6a23aD76863CbA6f10d0C5CBBD8342C, Depositor: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, Amount: 232000000000000000000 +``` + +## ERC721 Test Tokens + +The ERC721 test token is an Openzeppelin implementation of an ERC721 token, similar to other test tokens it's ownership is also set to the default address provided by anvil, therefore interactions with restricted functions on this contract would need to be signed by the default anvil private key `0xf39fd...b92266`. Using the right authorizations you could mint, transfer and also deposit these tokens to other address as well as your application. In the example below, we'll be covering minting, transferring and finally depositing `testNFT` tokens. + +### Minting the ERC721 Test Tokens + +#### Using Cast + + + + +

+
+```bash
+cast send  "safeMint(address,uint256,string)"    --rpc-url  --private-key 
+```
+
+
+
+ + +

+
+```bash
+cast send 0xBa46623aD94AB45850c4ecbA9555D26328917c3B "safeMint(address,uint256,string)" 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 1 "https://example.com/metadata/1.json" --rpc-url http://127.0.0.1:6751/anvil --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
+```
+
+
+
+ +
+ +The above example calls the `safeMint` function in the `testNFT` contract `0xBa4662...917c3B` specifying the receivers address `0xf39Fd6...2266`, the token id `1` and finally the URI for the token `https://example.com/metadata/1.json`. The receivers address, token ID and token URI specified above can all be changed to match your test case but, it's important that the `testNFT` address as well as the private_key be the same. + +### Transferring The ERC721 Test Tokens + +#### Using Cast + + + + +

+
+```bash
+cast send  "transferFrom(address,address,uint256)"   --rpc-url http://127.0.0.1:6751/anvil --private-key 
+```
+
+
+
+ + +

+
+```bash
+cast send 0xBa46623aD94AB45850c4ecbA9555D26328917c3B "transferFrom(address,address,uint256)" 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 2 --rpc-url http://127.0.0.1:6751/anvil --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
+```
+
+
+
+ +
+ +This command utilizes cast to call the `transferFrom` function in the `TestNft` contract `0xBa4662...917c3B`. The function takes the following arguments: senders `address=0xf39Fd6...92266`, `receivers address = 0x709979...dc79C8` and `token_id = 2`. While the function arguments passed can be modified, it's important that the senders address is the address of the wallet which owns the token with the token ID specified, and also that the private key passed is correct private key for the senders' wallet address + +### Depositing the ERC721 Test Tokens + +Depositing the ERC721 test tokens follow a similar process with depositing ERC20 tokens, this can be done either through cast or through the Cartesi CLI. Cast offers a more manual approach while using the CLI automates and simplifies the process. + +#### Using the CLI + +- Ensure you have your Cartesi application running, then from project directory, open a terminal and run the below command: + +``` bash +cartesi deposit +``` + +- Select ERC721 from the list of available transfer options. + +- The next menu requests for token ID, pass the ID of the token you intend to deposit. + +- The next menu requests for a token address, but has the `TestNFT` address filled but grayed out, hit the enter button to use the `TestNFT`, or enter the address of another deployed token. + +- This will trigger the approval process, if successful it'll proceed to trigger the deposit process, then transfer the tokens to your application. + +For a successful deposit process you should get a log similar to this below: + +```bash +✔ Select type of asset to deposit erc721 +✔ Token ID 1 +✔ Token address 0xBa46623aD94AB45850c4ecbA9555D26328917c3B +✔ Approved undefined SUNN +✔ Deposited undefined SUNN to 0xba3347e79665924033beeb7362629ca7992897d9 +``` + +#### Using Cast + +It's also possible to deposit the ERC721 test tokens to your application using Cast, this process, requires a more manual approach and happens in 2 phases, one is granting approval to the ERC721Portal then the second is calling the `depositERC721Token` function in the ERC721Portal contract. Below is a step-by-step process for this: + +- Grant the `ERC721Portal` contract approval + + + + +

+
+```bash
+cast send  "setApprovalForAll(address,bool)"   --rpc-url  --private-key 
+```
+
+
+
+ + +

+
+```bash
+cast send 0xBa46623aD94AB45850c4ecbA9555D26328917c3B "setApprovalForAll(address,bool)" 0xc700d52F5290e978e9CAe7D1E092935263b60051 true --rpc-url http://127.0.0.1:6751/anvil --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
+```
+
+
+
+ +
+ +The above command calls the `testNFT` contract `0xBa46...8917c3B` to approve the `ERC721Portal` `0xc700d5...0051` to withdraw tokens to be deposited to your application, the arguments listed above should be the same if you intend to deposit the `testNFT` token, for other ERC721 tokens, then you could change the token address to match the tokens you intend to transfer. + +- Call the `depositERC721Token` function in the ERC721Portal contract + + + + +

+
+```bash
+cast send  "depositERC721Token(address,address,uint256,bytes,bytes)"        --rpc-url  --private-key 
+```
+
+
+
+ + +

+
+```bash
+cast send 0xc700d52F5290e978e9CAe7D1E092935263b60051 "depositERC721Token(address,address,uint256,bytes,bytes)" 0xBa46623aD94AB45850c4ecbA9555D26328917c3B 0xba3347e79665924033beeb7362629ca7992897d9 8 0x 0x --rpc-url http://127.0.0.1:6751/anvil --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
+```
+
+
+
+ +
+ +From the above command we call the `depositERC721Token` function in the `ERC721Portal` contract `0xc700...60051`, passing the `testNFT` contract `0xBa46...8917c3B`, the `Application contract address` `0x23eff...560fc`, the token_id `4` and finally the optional baseLayer and executionLayer data `0x` and `0x`. For this implementation it's expected that the token_id passed to the function should already be minted and owned by the wallet whose private key is used to sign the transaction. + +#### Handling Deposited ERC721 tokens + +The below example is a process flow to setup and test a simple application designed to receive deposit input, decode, then finally log the data it received. You can run the application through the following steps: + +- Create a new JavaScript application, using the command: + + + +

+```bash
+cartesi create deposit-erc721 --template javascript
+```
+
+
+ + +

+```bash
+cartesi create deposit-erc721 --template rust
+```
+
+
+ + +

+```bash
+cartesi create deposit-erc721 --template python
+```
+
+
+
+ +- CD into the new project then, copy and paste the below code snippet into the index file. + + + +

+
+```javascript
+import { ethers } from "ethers";
+import {
+  getAddress,
+} from "viem";
+
+const rollup_server = process.env.ROLLUP_HTTP_SERVER_URL;
+console.log("HTTP rollup_server url is " + rollup_server);
+
+const ERC721_PORTAL_ADDRESS = "0xc700d52f5290e978e9cae7d1e092935263b60051";
+
+
+function parseDepositPayload(payload) {
+    const tokenData = ethers.dataSlice(payload, 0, 20);
+    const senderData = ethers.dataSlice(payload, 20, 40);
+    const tokenIdData = ethers.dataSlice(payload, 40, 72);
+
+    if (!tokenData) {
+      throw new Error("Invalid deposit payload");
+    }
+    return [getAddress(tokenData), getAddress(senderData), BigInt(tokenIdData)];
+  }
+
+async function handle_advance(data) {
+  console.log("Received advance request data " + JSON.stringify(data));
+  let payload = data.payload;
+  const sender = data["metadata"]["msg_sender"];
+
+  if (sender.toLowerCase() === ERC721_PORTAL_ADDRESS.toLowerCase()) {
+    console.log("Handling portal deposit");
+    const [token, depositor, amount] = parseDepositPayload(payload);
+    console.log(`Token: ${token}, Depositor: ${depositor}, Amount: ${amount}`);
+
+    // Handle deposit logic here
+  } else {
+      // Handle Transaction request like a regular transaction
+  }
+
+  return "accept";
+}
+
+async function handle_inspect(data) {
+  console.log("Received inspect request data " + JSON.stringify(data));
+  return "accept";
+}
+
+var handlers = {
+  advance_state: handle_advance,
+  inspect_state: handle_inspect,
+};
+
+var finish = { status: "accept" };
+
+(async () => {
+  while (true) {
+    const finish_req = await fetch(rollup_server + "/finish", {
+      method: "POST",
+      headers: {
+        "Content-Type": "application/json",
+      },
+      body: JSON.stringify({ status: "accept" }),
+    });
+
+    console.log("Received finish status " + finish_req.status);
+
+    if (finish_req.status == 202) {
+      console.log("No pending rollup request, trying again");
+    } else {
+      const rollup_req = await finish_req.json();
+      var handler = handlers[rollup_req["request_type"]];
+      finish["status"] = await handler(rollup_req["data"]);
+    }
+  }
+})();
+
+```
+
+
+
+ + +

+
+```Rust
+use json::{object, JsonValue};
+use std::env;
+use hex::FromHex;
+use std::error::Error;
+use std::io;
+
+pub async fn parse_deposit_payload(
+    payload: &str
+) -> Result<(String, String, u32), Box> {
+
+    let clean = payload.trim().trim_start_matches("0x");
+
+    let bytes: Vec = Vec::from_hex(clean).map_err(|e| -> Box { e.into() })?;
+
+    const TOKEN_ADDR_LEN: usize = 20;
+    const DEPOSITOR_ADDR_LEN: usize = 20;
+    const TOKEN_ID_LEN: usize = 32;
+    const MIN_LEN: usize = TOKEN_ADDR_LEN + DEPOSITOR_ADDR_LEN + TOKEN_ID_LEN;
+
+    if bytes.len() < MIN_LEN {
+        return Err(io::Error::new(io::ErrorKind::InvalidData, "payload too short").into());
+    }
+
+    let token_addr_bytes = bytes.get(0..20)
+        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "token address slice"))?;
+    let depositor_addr_bytes = bytes.get(20..40)
+        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "depositor address slice"))?;
+    let token_id_32 = bytes.get(40..72)
+        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "token id slice"))?;
+
+    let token_address = format!("0x{}", hex::encode(token_addr_bytes));
+    let depositor_address = format!("0x{}", hex::encode(depositor_addr_bytes));
+
+    let last4: [u8; 4] = token_id_32[28..32].try_into().unwrap();
+    let token_id = u32::from_be_bytes(last4);
+
+    Ok((token_address, depositor_address, token_id))
+}
+
+pub async fn handle_advance(
+    _client: &hyper::Client,
+    _server_addr: &str,
+    request: JsonValue,
+) -> Result<&'static str, Box> {
+    println!("Received advance request data {}", &request);
+    let _payload = request["data"]["payload"]
+        .as_str()
+        .ok_or("Missing payload")?;
+
+    let sender = request["data"]["metadata"]["msg_sender"]
+        .as_str()
+        .ok_or("Missing sender")?;
+
+    let erc721_portal_address: &str = "0xc700d52f5290e978e9cae7d1e092935263b60051";
+
+    if sender.to_lowercase() == erc721_portal_address.to_lowercase() {
+        println!("Received a message from the ERC721 Portal contract");
+        match parse_deposit_payload(_payload).await {
+            Ok((token, depositor, token_id)) => {
+                println!("Token: {token}, Depositor: {depositor}, Token_id: {token_id}");
+            }
+            Err(e) => {
+                eprintln!("Failed to parse deposit payload: {}", e);
+                return Err("reject".into());
+            }
+        } 
+    }
+
+    // TODO: add application logic here
+    Ok("accept")
+}
+
+pub async fn handle_inspect(
+    _client: &hyper::Client,
+    _server_addr: &str,
+    request: JsonValue,
+) -> Result<&'static str, Box> {
+    println!("Received inspect request data {}", &request);
+    let _payload = request["data"]["payload"]
+        .as_str()
+        .ok_or("Missing payload")?;
+    // TODO: add application logic here
+    Ok("accept")
+}
+
+#[tokio::main]
+async fn main() -> Result<(), Box> {
+    let client = hyper::Client::new();
+    let server_addr = env::var("ROLLUP_HTTP_SERVER_URL")?;
+
+    let mut status = "accept";
+    loop {
+        println!("Sending finish");
+        let response = object! {"status" => status};
+        let request = hyper::Request::builder()
+            .method(hyper::Method::POST)
+            .header(hyper::header::CONTENT_TYPE, "application/json")
+            .uri(format!("{}/finish", &server_addr))
+            .body(hyper::Body::from(response.dump()))?;
+        let response = client.request(request).await?;
+        println!("Received finish status {}", response.status());
+
+        if response.status() == hyper::StatusCode::ACCEPTED {
+            println!("No pending rollup request, trying again");
+        } else {
+            let body = hyper::body::to_bytes(response).await?;
+            let utf = std::str::from_utf8(&body)?;
+            let req = json::parse(utf)?;
+
+            let request_type = req["request_type"]
+                .as_str()
+                .ok_or("request_type is not a string")?;
+            status = match request_type {
+                "advance_state" => handle_advance(&client, &server_addr[..], req).await?,
+                "inspect_state" => handle_inspect(&client, &server_addr[..], req).await?,
+                &_ => {
+                    eprintln!("Unknown request type");
+                    "reject"
+                }
+            };
+        }
+    }
+}
+
+```
+
+
+
+ + +

+
+```python
+from os import environ
+import logging
+import requests
+
+logging.basicConfig(level="INFO")
+logger = logging.getLogger(__name__)
+
+rollup_server = environ["ROLLUP_HTTP_SERVER_URL"]
+logger.info(f"HTTP rollup_server url is {rollup_server}")
+
+def parse_deposit_payload(payload: str):
+    
+    clean = payload.strip().removeprefix("0x")
+
+    bytes_data = bytes.fromhex(clean)
+
+    TOKEN_ADDR_LEN = 20
+    DEPOSITOR_ADDR_LEN = 20
+    TOKEN_ID_LEN = 32
+    EXPECTED_LEN = TOKEN_ADDR_LEN + DEPOSITOR_ADDR_LEN + TOKEN_ID_LEN
+    if len(bytes_data) < EXPECTED_LEN:
+        raise ValueError(f"Invalid payload length: expected {EXPECTED_LEN}, got {len(bytes_data)}")
+
+    token_addr_bytes = bytes_data[0:TOKEN_ADDR_LEN]
+    depositor_addr_bytes = bytes_data[TOKEN_ADDR_LEN:TOKEN_ADDR_LEN + DEPOSITOR_ADDR_LEN]
+    token_id_32 = bytes_data[TOKEN_ADDR_LEN + DEPOSITOR_ADDR_LEN:EXPECTED_LEN]
+
+    token_address = "0x" + token_addr_bytes.hex()
+    depositor_address = "0x" + depositor_addr_bytes.hex()
+
+    token_id = int.from_bytes(token_id_32[28:32], byteorder="big")
+
+    return [token_address, depositor_address, token_id]
+
+def handle_advance(data):
+    logger.info(f"Received advance request data {data}")
+    sender = data["metadata"]["msg_sender"]
+    payload = data["payload"]
+    ERC721_PORTAL_ADDRESS = "0xc700d52f5290e978e9cae7d1e092935263b60051";
+    
+    if sender.lower() == ERC721_PORTAL_ADDRESS.lower():
+        try:
+            [token, depositor, token_id] = parse_deposit_payload(payload)
+            logger.info(f"Token: {token}, Depositor: {depositor}, Token_id: {token_id}")
+        except ValueError as e:
+            logger.error(f"Failed to parse deposit payload: {e}")
+
+    return "accept"
+
+
+def handle_inspect(data):
+    logger.info(f"Received inspect request data {data}")
+    return "accept"
+
+
+handlers = {
+    "advance_state": handle_advance,
+    "inspect_state": handle_inspect,
+}
+
+finish = {"status": "accept"}
+
+while True:
+    logger.info("Sending finish")
+    response = requests.post(rollup_server + "/finish", json=finish)
+    logger.info(f"Received finish status {response.status_code}")
+    if response.status_code == 202:
+        logger.info("No pending rollup request, trying again")
+    else:
+        rollup_request = response.json()
+        data = rollup_request["data"]
+        handler = handlers[rollup_request["request_type"]]
+        finish["status"] = handler(rollup_request["data"])
+
+```
+
+
+
+ +
+ +- Install necessary dependencies for rust or javascript by running the command: + + + + +

+
+```bash
+npm install viem ethers
+```
+
+
+
+ + +

+
+```bash
+cargo add hex
+```
+
+
+
+ +
+ +- Build and run your application using the command: + +```bash +cartesi build + +cartesi run +``` + +- In a new terminal, ensure you're in the directory of your Cartesi application then run deposit command and follow though on the prompts to deposit an ERC20 token. + +```bash +cartesi deposit +``` + +On successful deposit your application should log an object containing the deposited token, depositor and finally the amount deposited. + +Sample Log + +```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 753 "-" "node" 0.032320 +Received finish status 200 +Received advance request data {"metadata":{"chain_id":13370,"app_contract":"0xba3347e79665924033beeb7362629ca7992897d9","msg_sender":"0xc700d52f5290e978e9cae7d1e092935263b60051","block_number":24284,"block_timestamp":1757089398,"prev_randao":"0x1b76a6830d8a66d5908c517264984bde0ae7ecb9827d8ab4530066ce2a8f1ad0","input_index":0},"payload":"0xba46623ad94ab45850c4ecba9555d26328917c3bf39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000000000000000000000000000000000000000000b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"} +Handling portal deposit +Token: 0xBa46623aD94AB45850c4ecbA9555D26328917c3B, Depositor: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, Token_id: 7 +``` + +## ERC1155 Tokens + +The ERC155 test token is also an Openzeppelin implementation of an ERC1155 token called `TestMultiToken`, like the other two, it is owned by the default anvil wallet and managed by the Cartesi CLI, below is a guide on, minting, transferring and depositing, this token to other wallets and also your Cartesi application. + +### Minting ERC1155 Test Tokens + +Minting the ERC1155 token issues new tokens to a selected wallet address, this function is restricted to just the owner of the contract, so it's necessary that the transaction be signed by the default anvil private key. + +#### Using Cast + + + + +

+
+```bash
+cast send  "mint(address,uint256,uint256,bytes)"     --rpc-url  --private-key 
+```
+
+
+
+ + +

+
+```bash
+cast send 0xDC6d64971B77a47fB3E3c6c409D4A05468C398D2 "mint(address,uint256,uint256,bytes)" 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 1 1 0x --rpc-url http://127.0.0.1:6751/anvil --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
+```
+
+
+
+ +
+ +The above example we call the `mint` function in the `TestMultiToken` contract `0xDC6d...98D2`, to mint `1` unit of the token with id `1` to the receivers address `0xf39F...92266`. The function arguments passed to this function can be modified, but it's necessary that the private key remain the same as the owner of the `TestMultiToken` contract is set to the default anvil wallet with the private key `0xac097...f2ff80`. + +### Transferring ERC1155 Test Tokens + +#### Using Cast + + + + +

+
+```bash
+cast send  "safeTransferFrom(address,address,uint256,uint256,bytes)"      --rpc-url  --private-key 
+```
+
+
+
+ + +

+
+```bash
+cast send 0xDC6d64971B77a47fB3E3c6c409D4A05468C398D2 "safeTransferFrom(address,address,uint256,uint256,bytes)" 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 1 1 0x --rpc-url http://127.0.0.1:6751/anvil --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
+```
+
+
+
+ +
+ +The above command interacts with the `safeTransferFrom` function of the `TestMultiToken` contract `0xDC6d...98D2`. The command transfers `1` unit of the token with id `1` from the owner address `0xf39F...92266` to the receiver address `0x7099...dc79C8`, this transaction is signed using the private key for the owner address specified. + +### Depositing ERC1155 Tokens + +#### Using The CLI + +- Run your Cartesi application. + +- From the project directory, open a terminal and run: + +```bash +cartesi deposit +``` + +- From the available options, select `erc1155-single` if you intend to deposit a single token, or select `erc1155-batch` if you're depositing multiple tokens at once. + +- Accept the pre-filled TestToken address (or enter another ERC1155 token address). + +- Enter the ID of the token you intend to deposit, if multiple you can add all of their ID's separated by commas. + +- In the next menu enter the amount of tokens you intend to deposit (separate them by comma's if multiple). + +- The CLI would automatically run the approval then the deposit process for the tokens. On successful deposit you should see logs similar to this: + +```bash +✔ Select type of asset to deposit erc1155-single +✔ Token address 0xDC6d64971B77a47fB3E3c6c409D4A05468C398D2 +✔ Token ID 5 +✔ Amount of token ID 5 1 +✔ Approved ERC1155Portal +✔ Deposited 1 units of token id 5 to 0x0c0fe740dcd46f0a6ddb8498d0bfdca93c5910e6 +``` + +#### Using Cast + +- Grant the `ERC1155SinglePortal` contract approval + + + + +

+
+```bash
+cast send  "setApprovalForAll(address,bool)"   ---rpc-url  --private-key 
+```
+
+
+
+ + +

+
+```bash
+cast send 0xDC6d64971B77a47fB3E3c6c409D4A05468C398D2 "setApprovalForAll(address,bool)" 0xc700A261279aFC6F755A3a67D86ae43E2eBD0051 true --rpc-url http://127.0.0.1:6751/anvil --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
+```
+
+
+
+ +
+ +- Call the `depositSingleERC1155Token` function in the ERC1155SinglePortal contract + + + + +

+
+```bash
+cast send  "depositSingleERC1155Token(address,address,uint256,uint256,bytes,bytes)"         ---rpc-url  --private-key 
+```
+
+
+
+ + +

+
+```bash
+cast send 0xc700A261279aFC6F755A3a67D86ae43E2eBD0051 "depositSingleERC1155Token(address,address,uint256,uint256,bytes,bytes)" 0xDC6d64971B77a47fB3E3c6c409D4A05468C398D2 0x0c0fe740dcd46f0a6ddb8498d0bfdca93c5910e6 5 1 0x 0x --rpc-url http://127.0.0.1:6751/anvil --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
+```
+
+
+
+ +
+ +#### Handling Deposited ERC1155 tokens + +This sections provides sample codes and also a step by step guide to design and test the deposit flow for ERC1155 tokens. + +- Create a new JavaScript application, using the command: + + + +

+```bash
+cartesi create deposit-erc1155 --template javascript
+```
+
+
+ + +

+```bash
+cartesi create deposit-erc1155 --template rust
+```
+
+
+ + +

+```bash
+cartesi create deposit-erc1155 --template python
+```
+
+
+
+ +- CD into the new project then, copy and paste the below code snippet into the index file. + + + +

+
+```Javascript
+import { ethers } from "ethers";
+import {
+  getAddress,
+} from "viem";
+
+const rollup_server = process.env.ROLLUP_HTTP_SERVER_URL;
+console.log("HTTP rollup_server url is " + rollup_server);
+
+const ERC1155_SINGLE_PORTAL_ADDRESS = "0xc700A261279aFC6F755A3a67D86ae43E2eBD0051";
+
+
+function parseDepositPayload(payload) {
+    const tokenData = ethers.dataSlice(payload, 0, 20);
+    const senderData = ethers.dataSlice(payload, 20, 40);
+    const tokenIdData = ethers.dataSlice(payload, 40, 72);
+    const amountData = ethers.dataSlice(payload, 72, 104);
+
+    if (!tokenData) {
+      throw new Error("Invalid deposit payload");
+    }
+    return [getAddress(tokenData), getAddress(senderData), BigInt(tokenIdData), BigInt(amountData)];
+  }
+
+async function handle_advance(data) {
+  console.log("Received advance request data " + JSON.stringify(data));
+  let payload = data.payload;
+  const sender = data["metadata"]["msg_sender"];
+
+  if (sender.toLowerCase() === ERC1155_SINGLE_PORTAL_ADDRESS.toLowerCase()) {
+    console.log("Handling portal deposit");
+    const [token, depositor, tokenId, amount] = parseDepositPayload(payload);
+    console.log(`Token: ${token}, Depositor: ${depositor}, Token ID: ${tokenId}, Amount: ${amount}`);
+
+    // Handle deposit logic here
+  } else {
+      // Handle Transaction request like a regular transaction
+  }
+
+  return "accept";
+}
+
+async function handle_inspect(data) {
+  console.log("Received inspect request data " + JSON.stringify(data));
+  return "accept";
+}
+
+var handlers = {
+  advance_state: handle_advance,
+  inspect_state: handle_inspect,
+};
+
+var finish = { status: "accept" };
+
+(async () => {
+  while (true) {
+    const finish_req = await fetch(rollup_server + "/finish", {
+      method: "POST",
+      headers: {
+        "Content-Type": "application/json",
+      },
+      body: JSON.stringify({ status: "accept" }),
+    });
+
+    console.log("Received finish status " + finish_req.status);
+
+    if (finish_req.status == 202) {
+      console.log("No pending rollup request, trying again");
+    } else {
+      const rollup_req = await finish_req.json();
+      var handler = handlers[rollup_req["request_type"]];
+      finish["status"] = await handler(rollup_req["data"]);
+    }
+  }
+})();
+```
+
+
+
+ + +

+
+```Rust
+use json::{object, JsonValue};
+use primitive_types::{H160, U256};
+use std::env;
+use hex::FromHex;
+use std::error::Error;
+use std::io;
+
+pub async fn parse_deposit_payload(
+    payload: &str
+) -> Result<(String, String, U256, U256), Box> {
+
+    let clean = payload.trim().trim_start_matches("0x");
+
+    let bytes: Vec = Vec::from_hex(clean).map_err(|e| -> Box { e.into() })?;
+
+    const TOKEN_ADDR_LEN: usize = 20;
+    const DEPOSITOR_ADDR_LEN: usize = 20;
+    const TOKEN_ID_LEN: usize = 32;
+    const AMOUNT_LEN: usize = 32;
+    const MIN_LEN: usize = TOKEN_ADDR_LEN + DEPOSITOR_ADDR_LEN + AMOUNT_LEN + TOKEN_ID_LEN;
+
+    if bytes.len() < MIN_LEN {
+        return Err(io::Error::new(io::ErrorKind::InvalidData, "payload too short").into());
+    }
+
+    let token_addr_bytes = bytes.get(0..20)
+        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "token address slice"))?;
+    let depositor_addr_bytes = bytes.get(20..40)
+        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "depositor address slice"))?;
+    let token_id_32 = bytes.get(40..72)
+        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "token id slice"))?;
+    let token_amount_32 = bytes.get(72..104)
+        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "token id slice"))?;
+
+    let token_address = format!("0x{}", hex::encode(token_addr_bytes));
+    let depositor_address = format!("0x{}", hex::encode(depositor_addr_bytes));
+    let token_id = U256::from_big_endian(token_id_32);
+    let token_amount = U256::from_big_endian(token_amount_32);
+
+    Ok((token_address, depositor_address, token_id, token_amount))
+}
+
+pub async fn handle_advance(
+    _client: &hyper::Client,
+    _server_addr: &str,
+    request: JsonValue,
+) -> Result<&'static str, Box> {
+    println!("Received advance request data {}", &request);
+    let _payload = request["data"]["payload"]
+        .as_str()
+        .ok_or("Missing payload")?;
+
+    let sender = request["data"]["metadata"]["msg_sender"]
+        .as_str()
+        .ok_or("Missing sender")?;
+
+    let erc1155_portal_address: &str = "0xc700A261279aFC6F755A3a67D86ae43E2eBD0051";
+
+    if sender.to_lowercase() == erc1155_portal_address.to_lowercase() {
+        println!("Received a message from the ERC20 Portal contract");
+        match parse_deposit_payload(_payload).await {
+            Ok((token, depositor, token_id, token_amount)) => {
+                println!("Token: {token}, Depositor: {depositor}, token_id: {token_id}, token_amount: {token_amount}");
+            }
+            Err(e) => {
+                eprintln!("Failed to parse deposit payload: {}", e);
+                return Err("reject".into());
+            }
+        } 
+    }
+    // TODO: add application logic here
+    Ok("accept")
+}
+
+pub async fn handle_inspect(
+    _client: &hyper::Client,
+    _server_addr: &str,
+    request: JsonValue,
+) -> Result<&'static str, Box> {
+    println!("Received inspect request data {}", &request);
+    let _payload = request["data"]["payload"]
+        .as_str()
+        .ok_or("Missing payload")?;
+    // TODO: add application logic here
+    Ok("accept")
+}
+
+#[tokio::main]
+async fn main() -> Result<(), Box> {
+    let client = hyper::Client::new();
+    let server_addr = env::var("ROLLUP_HTTP_SERVER_URL")?;
+
+    let mut status = "accept";
+    loop {
+        println!("Sending finish");
+        let response = object! {"status" => status};
+        let request = hyper::Request::builder()
+            .method(hyper::Method::POST)
+            .header(hyper::header::CONTENT_TYPE, "application/json")
+            .uri(format!("{}/finish", &server_addr))
+            .body(hyper::Body::from(response.dump()))?;
+        let response = client.request(request).await?;
+        println!("Received finish status {}", response.status());
+
+        if response.status() == hyper::StatusCode::ACCEPTED {
+            println!("No pending rollup request, trying again");
+        } else {
+            let body = hyper::body::to_bytes(response).await?;
+            let utf = std::str::from_utf8(&body)?;
+            let req = json::parse(utf)?;
+
+            let request_type = req["request_type"]
+                .as_str()
+                .ok_or("request_type is not a string")?;
+            status = match request_type {
+                "advance_state" => handle_advance(&client, &server_addr[..], req).await?,
+                "inspect_state" => handle_inspect(&client, &server_addr[..], req).await?,
+                &_ => {
+                    eprintln!("Unknown request type");
+                    "reject"
+                }
+            };
+        }
+    }
+}
+```
+
+
+
+ + +

+
+```python
+from os import environ
+import logging
+import requests
+
+logging.basicConfig(level="INFO")
+logger = logging.getLogger(__name__)
+
+rollup_server = environ["ROLLUP_HTTP_SERVER_URL"]
+logger.info(f"HTTP rollup_server url is {rollup_server}")
+
+def parse_deposit_payload(payload: str):
+    
+    clean = payload.strip().removeprefix("0x")
+
+    bytes_data = bytes.fromhex(clean)
+
+    TOKEN_ADDR_LEN = 20
+    DEPOSITOR_ADDR_LEN = 20
+    TOKEN_ID_LEN = 32
+    AMOUNT_LEN = 32
+    EXPECTED_LEN = TOKEN_ADDR_LEN + DEPOSITOR_ADDR_LEN + TOKEN_ID_LEN + AMOUNT_LEN
+    if len(bytes_data) < EXPECTED_LEN:
+        raise ValueError(f"Invalid payload length: expected {EXPECTED_LEN}, got {len(bytes_data)}")
+
+    token_addr_bytes = bytes_data[0:20]
+    depositor_addr_bytes = bytes_data[20:40]
+    token_id_32 = bytes_data[40:72]
+    token_amount_32 = bytes_data[72:104]
+
+    token_address = "0x" + token_addr_bytes.hex()
+    depositor_address = "0x" + depositor_addr_bytes.hex()
+    token_id= int.from_bytes(token_id_32, byteorder="big")
+    token_amount = int.from_bytes(token_amount_32, byteorder="big")
+
+    return [token_address, depositor_address, token_id, token_amount]
+
+def handle_advance(data):
+    logger.info(f"Received advance request data {data}")
+    sender = data["metadata"]["msg_sender"]
+    payload = data["payload"]
+    ERC1155_PORTAL_ADDRESS = "0xc700A261279aFC6F755A3a67D86ae43E2eBD0051";
+    
+    if sender.lower() == ERC1155_PORTAL_ADDRESS.lower():
+        try:
+            [token, depositor, token_id, token_amount] = parse_deposit_payload(payload)
+            logger.info(f"Token: {token}, Depositor: {depositor}, token ID: {token_id} token_amount: {token_amount}")
+        except ValueError as e:
+            logger.error(f"Failed to parse deposit payload: {e}")
+    return "accept"
+
+def handle_inspect(data):
+    logger.info(f"Received inspect request data {data}")
+    return "accept"
+
+handlers = {
+    "advance_state": handle_advance,
+    "inspect_state": handle_inspect,
+}
+
+finish = {"status": "accept"}
+
+while True:
+    logger.info("Sending finish")
+    response = requests.post(rollup_server + "/finish", json=finish)
+    logger.info(f"Received finish status {response.status_code}")
+    if response.status_code == 202:
+        logger.info("No pending rollup request, trying again")
+    else:
+        rollup_request = response.json()
+        data = rollup_request["data"]
+        handler = handlers[rollup_request["request_type"]]
+        finish["status"] = handler(rollup_request["data"])
+```
+
+
+
+ +
+ +- Install necessary dependencies for rust or javasctipt by running the command: + + + + +

+
+```bash
+npm install viem ethers
+```
+
+
+
+ + +

+
+```bash
+cargo add primitive_types
+cargo add hex
+```
+
+
+
+ +
+ +- Build and run your application using the command: + +```bash +cartesi build + +cartesi run +``` + +- In a new terminal, ensure you're in the directory of your Cartesi application then run deposit command and follow though on the prompts to deposit an ERC20 token. + +```bash +cartesi deposit +``` + +On successful deposit your application should log an object containing the deposited token, depositor and finally the amount deposited. + +Sample Log + +```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 814 "-" "python-requests/2.31.0" 0.020416 +INFO:__main__:Received finish status 200 +INFO:__main__:Received advance request data {'metadata': {'chain_id': 13370, 'app_contract': '0x1f6eb23dfa21f9013ee83c4e09a93b494736d7d9', 'msg_sender': '0xc700a261279afc6f755a3a67d86ae43e2ebd0051', 'block_number': 21, 'block_timestamp': 1757624870, 'prev_randao': '0x45c3ecf672b0c35d25c822ccb22f145f633feec5451438436f57fd310366df44', 'input_index': 0}, 'payload': '0xdc6d64971b77a47fb3e3c6c409d4a05468c398d2f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'} +INFO:__main__:Token: 0xdc6d64971b77a47fb3e3c6c409d4a05468c398d2, Depositor: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266, token ID: 1 token_amount: 1 +INFO:__main__:Sending finish +``` diff --git a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json index f55cdb8a..788ddaa0 100644 --- a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json +++ b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json @@ -242,7 +242,8 @@ "tutorials/erc-20-token-wallet", "tutorials/erc-721-token-wallet", "tutorials/react-frontend-application", - "tutorials/cli-account-abstraction-feauture" + "tutorials/cli-account-abstraction-feauture", + "tutorials/utilizing-the-cli-test-tokens" ] }, { From 8dacf3624093783d2928506581e936dfe520f901 Mon Sep 17 00:00:00 2001 From: Shaheen Date: Fri, 21 Nov 2025 21:48:28 +0530 Subject: [PATCH 06/10] updated bonds and reference api --- fraud-proofs/fraud-proof-basics/bonds.md | 36 ++- .../references/archived_deployments.md | 30 ++ fraud-proofs/references/daveconsensus.md | 108 +++++--- fraud-proofs/references/deployments.md | 15 +- fraud-proofs/references/tournament.md | 258 +++++++++++++----- 5 files changed, 328 insertions(+), 119 deletions(-) create mode 100644 fraud-proofs/references/archived_deployments.md diff --git a/fraud-proofs/fraud-proof-basics/bonds.md b/fraud-proofs/fraud-proof-basics/bonds.md index 51a4a797..1fd25c1d 100644 --- a/fraud-proofs/fraud-proof-basics/bonds.md +++ b/fraud-proofs/fraud-proof-basics/bonds.md @@ -1,6 +1,36 @@ # Bonds -:::warning Work in progress -Bonds mechanism is not yet implemented in the Cartesi's fraud proof system. To get the latest updates, get in touch with the core contributors on Cartesi [Discord](https://discord.gg/cartesi). -::: +## What is a bond mechanism? +A bond mechanism in the fraud-proof system is an economic security system that requires participants to post collateral when posting claims in the dispute resolution process. + +The goal is to introduce a modest economic cost to submitting claims, thereby mitigating the impact of Sybil attacks. Bonds provide a means to finance and subsequently reimburse dispute-related expenses, ensuring that honest participants are not financially penalized for engaging in validation. + +The mechanism is not intended to create participation incentives, rather, its design objective is to keep honest, altruistic validation as close to costless as possible, irrespective of the number or behavior of Sybil identities. + +## Why do we need bonds? A Case without Bonds +In a dispute process, each transaction made on Ethereum would incur gas costs for the proposer and the challenger. This cost does not include the bond amount. In this setup, a malicious proposer who can spend a relatively large amount of gas fees has an advantage to repeatedly post claims until the honest challenger exhausts their funds or computational resources. + +This is infamously known as the resource exhaustion attack or [proof-of-whale attack](https://ethresear.ch/t/fraud-proofs-are-broken/19234). It is a type of Sybil attack. To deter such an attack, we introduce a bond system in the fraud-proof system. + +Naively introducing a high bond amount will impact decentralisation. Liveness and security may depend on preventing the adversary from creating too many Sybils. Increasing bonds in such a scenario harms decentralization by restricting the number of players that can afford to participate. + +## Implementation in PRT v2 +Cartesi’s _Permissionless Refereed Tournament (PRT) v2_ features a bond system. Each tournament level requires a bond to be posted in order for a commitment to be submitted. + +A defender gets the gas fees as a refund with an optional incentive to be an active defender of the system. In a single dispute process(a tournament system), the fraud-proof design asks participants to stake a bond at each level of the game. + +In the worst-case scenario, an honest validator will have one bond for each tournament level. Currently, PRT v2 features three levels: top, middle, and bottom, which correspond to three bonds. Bond values differ at each level. + + +When joining a tournament, participants must send the bond value in Wei. The contract enforces this requirement and reverts with an InsufficientBond error if the payment is insufficient. + +The **refund calculation** is capped by three values: the contract balance, a weighted fraction of the bond value, and the actual gas used plus a profit margin. The profit margin incentivizes honest participants to perform necessary operations. + +The winning commitment's submitter receives all bonds after tournament completion, recovering their costs plus rewards from eliminated participants. + +However, in practice, issuing refunds introduces an additional challenge. During dispute actions such as bisect and step, multiple honest validators may attempt the same operation simultaneously. Only the first transaction succeeds, while the rest revert, creating a race condition that leads to unnecessary gas losses and complicates proper refunds. + +To address this, PRT uses a targeted technique based on **MEV-Share’s** `mev_sendBundle` RPC. Although MEV-Share is a general MEV redistribution protocol, we rely on it only for its ability to relay transactions predictably through searcher relays. By submitting dispute actions via `mev_sendBundle`, validators avoid public mempool races and ensure that the intended action is included without incurring failed-transaction costs. + +This approach preserves the goal of making honest validation effectively costless, even when many validators attempt the same operation. diff --git a/fraud-proofs/references/archived_deployments.md b/fraud-proofs/references/archived_deployments.md new file mode 100644 index 00000000..addd19aa --- /dev/null +++ b/fraud-proofs/references/archived_deployments.md @@ -0,0 +1,30 @@ +# Deployments + +This page covers the PRT and Honeypot v2 deployed contracts and their addresses. + +## PRT Deployments + +| Component | Mainnet Address | +|-----------|-----------------| +| **Top Tournament** | [`0x09114973AE4bf3Af3896E4e541082C73f224F8Aa`](https://etherscan.io/address/0x09114973AE4bf3Af3896E4e541082C73f224F8Aa) | +| **Middle Tournament** | [`0xe49E4CB0Ab5c0E5792E762807329B420Cc4FF1AE`](https://etherscan.io/address/0xe49E4CB0Ab5c0E5792E762807329B420Cc4FF1AE) | +| **Bottom Tournament** | [`0x18256941eC7B661F9F46C228b74e775b581e63f8`](https://etherscan.io/address/0x18256941eC7B661F9F46C228b74e775b581e63f8) | +| **Cartesi State Transition** | [`0x772732EFbDE6559B2960327276ed33d707fF057f`](https://etherscan.io/address/0x772732EFbDE6559B2960327276ed33d707fF057f) | +| **MultiLevel Tournament Factory** | [`0xA31C2aCfF3464658866960c0fBD3d798310272D7`](https://etherscan.io/address/0xA31C2aCfF3464658866960c0fBD3d798310272D7) | +| **CanonicalTournamentParametersProvider** | [`0xcC0a49320891Bf35bca834aF1045ab89Ecd44c0c`](https://etherscan.io/address/0xcC0a49320891Bf35bca834aF1045ab89Ecd44c0c) | + +## Honeypot v2 Deployments +:::caution + Honeypot app is being updated. We recently found a bug in the application that has led to a fail-stop state. This means the current deployment is permanently frozen, and the bounty funds within it are no longer recoverable. Get in touch with us on [Discord](https://discord.gg/cWGbyFkQ2W) for more details. +::: + +| Component | Mainnet Address | +|-----------|-----------------| +| **Machine Hash** | `0x615acc9fb8ae058d0e45c0d12fa10e1a6c9e645222c6fd94dfeda194ee427c14` | +| **Application Contract** | [`0x4c1e74ef88a75c24e49eddd9f70d82a94d19251c`](https://etherscan.io/address/0x4c1e74ef88a75c24e49eddd9f70d82a94d19251c) | +| **Dave PRT Consensus Contract** | [`0x6CE590b9F0697327f18c601DF6f0baE4a0801B68`](https://etherscan.io/address/0x6CE590b9F0697327f18c601DF6f0baE4a0801B68) | +| **Input Box Contract** | [`0xc70074BDD26d8cF983Ca6A5b89b8db52D5850051`](https://etherscan.io/address/0xc70074BDD26d8cF983Ca6A5b89b8db52D5850051) | +| **ERC-20 Portal** | [`0xc700D6aDd016eECd59d989C028214Eaa0fCC0051`](https://etherscan.io/address/0xc700D6aDd016eECd59d989C028214Eaa0fCC0051) | +| **ERC-20 Token** | CTSI @ [`0x491604c0FDF08347Dd1fa4Ee062a822A5DD06B5D`](https://etherscan.io/address/0x491604c0FDF08347Dd1fa4Ee062a822A5DD06B5D) | +| **ERC-20 Wallet** | Cartesi Multisig @ [`0x60247492F1538Ed4520e61aE41ca2A8447592Ff5`](https://etherscan.io/address/0x60247492F1538Ed4520e61aE41ca2A8447592Ff5) | + diff --git a/fraud-proofs/references/daveconsensus.md b/fraud-proofs/references/daveconsensus.md index 0883b56f..708f7ebf 100644 --- a/fraud-proofs/references/daveconsensus.md +++ b/fraud-proofs/references/daveconsensus.md @@ -3,6 +3,17 @@ ## DaveConsensus Contract --- +DaveConsensus(also referred to as PRT-Rollups Consensus) is the consensus contract for applications that use Dave tournaments for verification. +This contract is responsible for validating exactly one application contract, and all inputs must originate from the configured InputBox. + +DaveConsensus manages epochs, which are defined as half-open block-number intervals of the form [a, b). +Epochs are numbered sequentially starting from 0. + +Off-chain nodes can track progress by subscribing to the _EpochSealed_ event. This event indicates which epochs have been sealed, which ones are fully settled, and which epoch is currently open for challenges. +Anyone may settle an epoch by calling _settle()_, and an epoch’s eligibility for settlement can be checked via _canSettle()_. Learn more about epochs [here](../../../fraud-proofs/fraud-proof-basics/epochs). + +This contract links input ingestion, epoch progression, and Dave tournament-based verification under a single consensus interface. + ### `canSettle()` ```solidity @@ -15,9 +26,9 @@ Check if the current epoch can be settled by querying the tournament's arbitrati | Name | Type | Description | |------|------|-------------| -| `isFinished` | `bool` | Whether tournament is finished | -| `epochNumber` | `uint256` | Current epoch number | -| `winnerCommitment` | `Tree.Node` | Winner's commitment from tournament | +| `isFinished` | `bool` | Whether the current sealed epoch tournament has finished | +| `epochNumber` | `uint256` | Current sealed epoch number | +| `winnerCommitment` | `Tree.Node` | Winner's commitment in case the tournament has finished | ### `settle()` @@ -25,15 +36,15 @@ Check if the current epoch can be settled by querying the tournament's arbitrati function settle(uint256 epochNumber, bytes32 outputsMerkleRoot, bytes32[] calldata proof) external ``` -Settle an epoch using tournament results and create a new tournament for the next epoch. +Settle the current sealed epoch. On success, it emits an [`EpochSealed`](#epochsealed) event. **Parameters:** | Name | Type | Description | |------|------|-------------| -| `epochNumber` | `uint256` | The epoch number to settle | -| `outputsMerkleRoot` | `bytes32` | Root hash of the outputs Merkle tree | -| `proof` | `bytes32[]` | Merkle proof array for validation | +| `epochNumber` | `uint256` | The current sealed epoch number (used to avoid race conditions) | +| `outputsMerkleRoot` | `bytes32` | The post-epoch outputs Merkle root (used to validate outputs) | +| `proof` | `bytes32[]` | The bottom-up Merkle proof of the outputs Merkle root in the final machine state | ### `getCurrentSealedEpoch()` @@ -50,7 +61,7 @@ Get information about the current sealed epoch including bounds and tournament. | `epochNumber` | `uint256` | Current epoch number | | `inputIndexLowerBound` | `uint256` | Lower bound of input indices (inclusive) | | `inputIndexUpperBound` | `uint256` | Upper bound of input indices (exclusive) | -| `tournament` | `ITournament` | Current tournament contract | +| `tournament` | `ITournament` | The tournament that will decide the post-epoch state | ### `isOutputsMerkleRootValid()` @@ -69,9 +80,9 @@ Validate whether a given outputs Merkle root is valid for the specified applicat **Returns:** -| Name | Type | Description | +| Index | Type | Description | |------|------|-------------| -| `valid` | `bool` | Whether the outputs Merkle root is valid | +| [0] | `bool` | Whether the outputs Merkle root is valid | ### `provideMerkleRootOfInput()` @@ -90,63 +101,76 @@ Get the Merkle root for input data at a specific index within the current epoch. **Returns:** -| Name | Type | Description | +| Index | Type | Description | |------|------|-------------| -| `merkleRoot` | `bytes32` | Merkle root of the input data | - -## DaveConsensusFactory Contract ---- - -### `newDaveConsensus()` +| [0] | `bytes32` | Merkle root of the input data | +### `ConsensusCreation` ```solidity -function newDaveConsensus(address appContract, Machine.Hash initialMachineStateHash) external returns (DaveConsensus) +event ConsensusCreation(IInputBox inputBox, address appContract, ITournamentFactory tournamentFactory) ``` -Deploy a new DaveConsensus contract instance for an application. +An event emitted when a new DaveConsensus contract is deployed. **Parameters:** - | Name | Type | Description | |------|------|-------------| -| `appContract` | `address` | Application contract address | -| `initialMachineStateHash` | `Machine.Hash` | Initial state hash of the Cartesi machine | +| `inputBox` | `IInputBox` | The input box contract | +| `appContract` | `address` | The application contract | +| `tournamentFactory` | `ITournamentFactory` | The tournament factory contract | -**Returns:** +### `EpochSealed` +```solidity +event EpochSealed(uint256 epochNumber, uint256 inputIndexLowerBound, uint256 inputIndexUpperBound, Machine.Hash initialMachineStateHash, bytes32 outputsMerkleRoot, ITournament tournament) +``` +An event emitted when a new epoch is sealed. + +**Parameters:** | Name | Type | Description | |------|------|-------------| -| `daveConsensus` | `DaveConsensus` | Deployed DaveConsensus contract instance | +| `epochNumber` | `uint256` | The epoch number | +| `inputIndexLowerBound` | `uint256` | The lower bound of the input index (inclusive) | +| `inputIndexUpperBound` | `uint256` | The upper bound of the input index (exclusive) | +| `initialMachineStateHash` | `Machine.Hash` | The initial machine state hash | +| `outputsMerkleRoot` | `bytes32` | The Merkle root hash of the outputs tree | +| `tournament` | `ITournament` | The sealed epoch tournament contract| + +## DaveAppFactory Contract +--- + +Dave-Application Factory contract allows anyone to reliably deploy an application validated with its corresponding Consensus contract. -### `newDaveConsensus()` (with salt) +### `newDaveApp()` ```solidity -function newDaveConsensus(address appContract, Machine.Hash initialMachineStateHash, bytes32 salt) external returns (DaveConsensus) +function newDaveApp(bytes32 templateHash, bytes32 salt) external returns (IApplication appContract, IDaveConsensus daveConsensus) ``` -Deploy a new DaveConsensus contract with deterministic address using CREATE2. +Deploy a new Dave-Application pair deterministically. **Parameters:** | Name | Type | Description | |------|------|-------------| -| `appContract` | `address` | Application contract address | -| `initialMachineStateHash` | `Machine.Hash` | Initial state hash of the Cartesi machine | -| `salt` | `bytes32` | Salt for CREATE2 deterministic deployment | +| `templateHash` | `bytes32` | Template hash of the application | +| `salt` | `bytes32` | A 32-byte value used to add entropy to the addresses | **Returns:** | Name | Type | Description | |------|------|-------------| -| `daveConsensus` | `DaveConsensus` | Deployed DaveConsensus contract instance | +| `appContract` | `IApplication` | Deployed application contract | +| `daveConsensus` | `IDaveConsensus` | Deployed DaveConsensus contract | -### `calculateDaveConsensusAddress()` + +### `calculateDaveAppAddress()` ```solidity -function calculateDaveConsensusAddress(address appContract, Machine.Hash initialMachineStateHash, bytes32 salt) external view returns (address) +function calculateDaveAppAddress(bytes32 templateHash, bytes32 salt) external view returns (address appContractAddress, address daveConsensusAddress) ``` -Calculate the deployment address for a DaveConsensus contract before deployment. +Calculate the deployment address for a Dave-App pair before deployment. **Parameters:** @@ -160,4 +184,18 @@ Calculate the deployment address for a DaveConsensus contract before deployment. | Name | Type | Description | |------|------|-------------| -| `address` | `address` | Calculated deployment address | +| `appContractAddress` | `address` | Calculated deployment address for the application contract | +| `daveConsensusAddress` | `address` | Calculated deployment address for the DaveConsensus contract | + +### `DaveAppCreated` +```solidity +event DaveAppCreated(IApplication appContract, IDaveConsensus daveConsensus) +``` + +An event emitted when a new Dave-App pair is deployed. + +**Parameters:** +| Name | Type | Description | +|------|------|-------------| +| `appContract` | `IApplication` | The deployed application contract | +| `daveConsensus` | `IDaveConsensus` | The deployed DaveConsensus contract | \ No newline at end of file diff --git a/fraud-proofs/references/deployments.md b/fraud-proofs/references/deployments.md index addd19aa..c1c106d9 100644 --- a/fraud-proofs/references/deployments.md +++ b/fraud-proofs/references/deployments.md @@ -13,18 +13,15 @@ This page covers the PRT and Honeypot v2 deployed contracts and their addresses. | **MultiLevel Tournament Factory** | [`0xA31C2aCfF3464658866960c0fBD3d798310272D7`](https://etherscan.io/address/0xA31C2aCfF3464658866960c0fBD3d798310272D7) | | **CanonicalTournamentParametersProvider** | [`0xcC0a49320891Bf35bca834aF1045ab89Ecd44c0c`](https://etherscan.io/address/0xcC0a49320891Bf35bca834aF1045ab89Ecd44c0c) | -## Honeypot v2 Deployments -:::caution - Honeypot app is being updated. We recently found a bug in the application that has led to a fail-stop state. This means the current deployment is permanently frozen, and the bounty funds within it are no longer recoverable. Get in touch with us on [Discord](https://discord.gg/cWGbyFkQ2W) for more details. -::: +## Honeypot v3 Deployments | Component | Mainnet Address | |-----------|-----------------| -| **Machine Hash** | `0x615acc9fb8ae058d0e45c0d12fa10e1a6c9e645222c6fd94dfeda194ee427c14` | -| **Application Contract** | [`0x4c1e74ef88a75c24e49eddd9f70d82a94d19251c`](https://etherscan.io/address/0x4c1e74ef88a75c24e49eddd9f70d82a94d19251c) | -| **Dave PRT Consensus Contract** | [`0x6CE590b9F0697327f18c601DF6f0baE4a0801B68`](https://etherscan.io/address/0x6CE590b9F0697327f18c601DF6f0baE4a0801B68) | -| **Input Box Contract** | [`0xc70074BDD26d8cF983Ca6A5b89b8db52D5850051`](https://etherscan.io/address/0xc70074BDD26d8cF983Ca6A5b89b8db52D5850051) | -| **ERC-20 Portal** | [`0xc700D6aDd016eECd59d989C028214Eaa0fCC0051`](https://etherscan.io/address/0xc700D6aDd016eECd59d989C028214Eaa0fCC0051) | +| **Machine Hash** | `0x144d45af1181b35f2b11c4b1150d6cb16934c28093707fb97c911ff16b3fe609` | +| **Application Contract** | [`0xfddf68726a28e418fa0c2a52c3134904a8c3e998`](https://etherscan.io/address/0xfddf68726a28e418fa0c2a52c3134904a8c3e998) | +| **Dave PRT Consensus Contract** | [`0xF0D8374F8446E87e013Ec1435C7245E05f439259`](https://etherscan.io/address/0xF0D8374F8446E87e013Ec1435C7245E05f439259) | +| **Input Box Contract** | [`0x1b51e2992A2755Ba4D6F7094032DF91991a0Cfac`](https://etherscan.io/address/0x1b51e2992A2755Ba4D6F7094032DF91991a0Cfac) | +| **ERC-20 Portal** | [`0xACA6586A0Cf05bD831f2501E7B4aea550dA6562D`](https://etherscan.io/address/0xACA6586A0Cf05bD831f2501E7B4aea550dA6562D) | | **ERC-20 Token** | CTSI @ [`0x491604c0FDF08347Dd1fa4Ee062a822A5DD06B5D`](https://etherscan.io/address/0x491604c0FDF08347Dd1fa4Ee062a822A5DD06B5D) | | **ERC-20 Wallet** | Cartesi Multisig @ [`0x60247492F1538Ed4520e61aE41ca2A8447592Ff5`](https://etherscan.io/address/0x60247492F1538Ed4520e61aE41ca2A8447592Ff5) | diff --git a/fraud-proofs/references/tournament.md b/fraud-proofs/references/tournament.md index b0086876..614082f3 100644 --- a/fraud-proofs/references/tournament.md +++ b/fraud-proofs/references/tournament.md @@ -5,12 +5,17 @@ ### `joinTournament()` ```solidity -function joinTournament(Tree.Node finalState, bytes32[] calldata proof, Tree.Node leftChild, Tree.Node rightChild) external tournamentOpen tournamentNotFinished +function joinTournament( + Machine.Hash finalState, + bytes32[] calldata proof, + Tree.Node leftNode, + Tree.Node rightNode +) external payable ``` Join a tournament by submitting a final computation hash with Merkle proof. Creates a match if another participant is waiting. -**Event Emitted:** `commitmentJoined(Tree.Node root)` when commitment is successfully added +**Event Emitted:** `CommitmentJoined(Tree.Node commitmentRoot, Machine.Hash finalState, address claimer)` when commitment is successfully added **Parameters:** @@ -18,13 +23,19 @@ Join a tournament by submitting a final computation hash with Merkle proof. Crea |------|------|-------------| | `finalState` | `Tree.Node` | Final computational hash | | `proof` | `bytes32[]` | Merkle proof for the final state | -| `leftChild` | `Tree.Node` | Left child of the commitment node | -| `rightChild` | `Tree.Node` | Right child of the commitment node | +| `leftNode` | `Tree.Node` | Left node of the commitment | +| `rightNode` | `Tree.Node` | Right node of the commitment | ### `advanceMatch()` ```solidity -function advanceMatch(Match.Id calldata matchId, Tree.Node leftNode, Tree.Node rightNode, Tree.Node newLeftNode, Tree.Node newRightNode) external tournamentNotFinished +function advanceMatch( + Match.Id calldata matchId, + Tree.Node leftNode, + Tree.Node rightNode, + Tree.Node newLeftNode, + Tree.Node newRightNode + ) external ``` Advance a match by providing new intermediate nodes in the binary search process. @@ -42,7 +53,11 @@ Advance a match by providing new intermediate nodes in the binary search process ### `winMatchByTimeout()` ```solidity -function winMatchByTimeout(Match.Id calldata matchId) external tournamentNotFinished +function winMatchByTimeout( + Match.Id calldata matchId, + Tree.Node leftNode, + Tree.Node rightNode + ) external ``` Win a match when the opponent has run out of time allowance. @@ -56,7 +71,7 @@ Win a match when the opponent has run out of time allowance. ### `eliminateMatchByTimeout()` ```solidity -function eliminateMatchByTimeout(Match.Id calldata matchId) external tournamentNotFinished +function eliminateMatchByTimeout(Match.Id calldata matchId) external ``` Eliminate a match when both participants have run out of time. @@ -70,30 +85,45 @@ Eliminate a match when both participants have run out of time. ### `isFinished()` ```solidity -function isFinished() public view returns (bool) +function isFinished() external view returns (bool) ``` Check if the tournament has finished (has a winner or is eliminated). -**Returns:** +### `isClosed()` -| Name | Type | Description | +```solidity +function isClosed() external view returns (bool) +``` + +Check if the tournament is closed to new participants. + +### `bondValue()` +```solidity +function bondValue() external view returns (uint256) +``` + +Get the amount of Wei necessary to call `joinTournament()`. + +**Return Values:** + +| Index | Type | Description | |------|------|-------------| -| `finished` | `bool` | Whether the tournament is finished | +| [0] | `uint256` | The tournament bond value | -### `isClosed()` +### `tryRecoveringBond()` ```solidity -function isClosed() public view returns (bool) +function tryRecoveringBond() external returns (bool) ``` -Check if the tournament is closed to new participants. +Try recovering the bond of the winner commitment submitter. -**Returns:** +**Return Values:** -| Name | Type | Description | +| Index | Type | Description | |------|------|-------------| -| `closed` | `bool` | Whether the tournament is closed | +| [0] | `bool` | Whether the recovery was successful | ## NonLeafTournament Contract Functions --- @@ -101,12 +131,18 @@ Check if the tournament is closed to new participants. ### `sealInnerMatchAndCreateInnerTournament()` ```solidity -function sealInnerMatchAndCreateInnerTournament(Match.Id calldata matchId, Tree.Node leftLeaf, Tree.Node rightLeaf, Machine.Hash agreeHash, bytes32[] calldata agreeHashProof) external tournamentNotFinished +function sealInnerMatchAndCreateInnerTournament( + Match.Id calldata matchId, + Tree.Node leftLeaf, + Tree.Node rightLeaf, + Machine.Hash agreeHash, + bytes32[] calldata agreeHashProof + ) external ``` Seal an inner match and create a new inner tournament to resolve the dispute at a finer granularity. -**Event Emitted:** `newInnerTournament(Match.IdHash indexed, NonRootTournament)` when inner tournament is created +**Event Emitted:** `NewInnerTournament(Match.IdHash indexed matchIdHash, ITournament indexed childTournament)` when inner tournament is created **Parameters:** @@ -121,7 +157,11 @@ Seal an inner match and create a new inner tournament to resolve the dispute at ### `winInnerTournament()` ```solidity -function winInnerTournament(NonRootTournament innerTournament) external tournamentNotFinished +function winInnerTournament( + ITournament childTournament, + Tree.Node leftNode, + Tree.Node rightNode + ) external ``` Process the result of a finished inner tournament and advance the parent match. @@ -130,12 +170,14 @@ Process the result of a finished inner tournament and advance the parent match. | Name | Type | Description | |------|------|-------------| -| `innerTournament` | `NonRootTournament` | Address of the finished inner tournament | +| `childTournament` | `ITournament` | The inner/child tournament | +| `leftNode` | `Tree.Node` | Left child of the winning commitment | +| `rightNode` | `Tree.Node` | Right child of the winning commitment | ### `eliminateInnerTournament()` ```solidity -function eliminateInnerTournament(NonRootTournament innerTournament) external tournamentNotFinished +function eliminateInnerTournament(ITournament childTournament) external ``` Eliminate an inner tournament that has no winner and advance the parent match. @@ -144,7 +186,7 @@ Eliminate an inner tournament that has no winner and advance the parent match. | Name | Type | Description | |------|------|-------------| -| `innerTournament` | `NonRootTournament` | Address of the inner tournament to eliminate | +| `childTournament` | `ITournament` | The inner/child tournament to eliminate | ## NonRootTournament Contract Functions --- @@ -159,26 +201,26 @@ Get the winner information from a finished inner tournament for parent tournamen **Returns:** -| Name | Type | Description | +| Index | Type | Description | |------|------|-------------| -| `isFinished` | `bool` | Whether the tournament is finished | -| `contestedCommitment` | `Tree.Node` | The contested parent commitment | -| `winnerCommitment` | `Tree.Node` | The winning inner commitment | -| `clock` | `Clock.State` | Paused clock state of the winner | +| [0] | `bool` | Whether the tournament is finished | +| [1] | `Tree.Node` | The contested parent commitment | +| [2] | `Tree.Node` | The winning inner commitment | +| [3] | `Clock.State` | Paused clock state of the winning inner commitment | ### `canBeEliminated()` ```solidity -function canBeEliminated() public view returns (bool) +function canBeEliminated() external view returns (bool) ``` Check if the tournament can be safely eliminated by its parent. **Returns:** -| Name | Type | Description | +| Index | Type | Description | |------|------|-------------| -| `eliminatable` | `bool` | Whether the tournament can be eliminated | +| [0] | `bool` | Whether the tournament can be eliminated | ## RootTournament Contract Functions --- @@ -186,54 +228,57 @@ Check if the tournament can be safely eliminated by its parent. ### `arbitrationResult()` ```solidity -function arbitrationResult() external view returns (bool isFinished, Tree.Node winnerCommitment, Machine.Hash finalMachineStateHash) +function arbitrationResult() external view returns ( + bool finished, + Tree.Node winnerCommitment, + Machine.Hash finalState +) ``` Get the final arbitration result from the root tournament. -**Returns:** +**Return Values:** | Name | Type | Description | |------|------|-------------| -| `isFinished` | `bool` | Whether the tournament is finished | +| `finished` | `bool` | Whether the tournament is finished | | `winnerCommitment` | `Tree.Node` | The winning commitment | -| `finalMachineStateHash` | `Machine.Hash` | Final machine state hash of winner | +| `finalState` | `Machine.Hash` | Final machine state hash of winner | ## Tournament Factory Functions --- -### `instantiate()` (SingleLevelTournamentFactory) +### `instantiate()` ```solidity -function instantiate(Machine.Hash initialHash, IDataProvider provider) external returns (ITournament) +function instantiate(Machine.Hash initialState, IDataProvider provider) external + returns (ITournament) ``` Create a new single-level tournament instance. -**Event Emitted:** `tournamentCreated(ITournament)` when tournament is created +**Event Emitted:** `TournamentCreated(ITournament tournament)` when tournament is created **Parameters:** | Name | Type | Description | |------|------|-------------| -| `initialHash` | `Machine.Hash` | Initial machine state hash | +| `initialState` | `Machine.Hash` | Initial machine state hash | | `provider` | `IDataProvider` | Data provider for input validation | -**Returns:** +**Return Values:** -| Name | Type | Description | +| Index | Type | Description | |------|------|-------------| -| `tournament` | `ITournament` | Created tournament instance | +| [0] | `ITournament` | Created tournament instance | -### `instantiate()` (MultiLevelTournamentFactory) +### `instantiateTop()` (MultiLevelTournamentFactory) ```solidity -function instantiate(Machine.Hash initialHash, IDataProvider provider) external returns (ITournament) +function instantiateTop(Machine.Hash _initialHash, IDataProvider _provider) external returns (ITournament) ``` -Create a new multi-level tournament hierarchy starting with a top tournament. - -**Event Emitted:** `tournamentCreated(ITournament)` when tournament is created +Create a new top-level tournament in the multi-level hierarchy. **Parameters:** @@ -244,38 +289,45 @@ Create a new multi-level tournament hierarchy starting with a top tournament. **Returns:** -| Name | Type | Description | +| Index | Type | Description | |------|------|-------------| -| `tournament` | `ITournament` | Created top tournament instance | +| [0] | `Tournament` | Created top tournament instance | -### `instantiateTop()` +### `instantiateMiddle()` ```solidity -function instantiateTop(Machine.Hash initialHash, IDataProvider provider) external returns (Tournament) +function instantiateMiddle(Machine.Hash initialHash, Tree.Node contestedCommitmentOne, Machine.Hash contestedFinalStateOne, Tree.Node contestedCommitmentTwo, Machine.Hash contestedFinalStateTwo, Time.Duration allowance, uint256 startCycle, uint64 level, IDataProvider provider) external returns (ITournament) ``` -Create a new top-level tournament in the multi-level hierarchy. +Create a new middle-level tournament for dispute resolution. **Parameters:** | Name | Type | Description | |------|------|-------------| | `initialHash` | `Machine.Hash` | Initial machine state hash | +| `contestedCommitmentOne` | `Tree.Node` | First contested commitment | +| `contestedFinalStateOne` | `Machine.Hash` | First contested final state | +| `contestedCommitmentTwo` | `Tree.Node` | Second contested commitment | +| `contestedFinalStateTwo` | `Machine.Hash` | Second contested final state | +| `allowance` | `Time.Duration` | Time allowance for participants | +| `startCycle` | `uint256` | Starting cycle for the tournament | +| `level` | `uint64` | Tournament level in hierarchy | | `provider` | `IDataProvider` | Data provider for input validation | **Returns:** -| Name | Type | Description | +| Index | Type | Description | |------|------|-------------| -| `tournament` | `Tournament` | Created top tournament instance | +| [0] | `ITournament` | Created middle tournament instance | -### `instantiateMiddle()` +### `instantiateBottom()` ```solidity -function instantiateMiddle(Machine.Hash initialHash, Tree.Node contestedCommitmentOne, Machine.Hash contestedFinalStateOne, Tree.Node contestedCommitmentTwo, Machine.Hash contestedFinalStateTwo, Time.Duration allowance, uint256 startCycle, uint64 level, IDataProvider provider) external returns (Tournament) +function instantiateBottom(Machine.Hash initialHash, Tree.Node contestedCommitmentOne, Machine.Hash contestedFinalStateOne, Tree.Node contestedCommitmentTwo, Machine.Hash contestedFinalStateTwo, Time.Duration allowance, uint256 startCycle, uint64 level, IDataProvider provider) external returns (ITournament) ``` -Create a new middle-level tournament for dispute resolution. +Create a new bottom-level tournament for leaf dispute resolution. **Parameters:** @@ -293,34 +345,96 @@ Create a new middle-level tournament for dispute resolution. **Returns:** +| Index | Type | Description | +|------|------|-------------| +| [0] | `ITournament` | Created bottom tournament instance | + +## Other View Functions + +### `getCommitment()` +```solidity +function getCommitment(Tree.Node commitmentRoot) external view returns (Clock.State memory clock, Machine.Hash finalState) external view returns (Clock.State memory clock, Machine.Hash finalState) +``` +Get the clock and final state of a commitment. + +**Parameters:** + | Name | Type | Description | |------|------|-------------| -| `tournament` | `Tournament` | Created middle tournament instance | +| `commitmentRoot` | `Tree.Node` | The root of the commitment | -### `instantiateBottom()` +**Return Values:** +| Index | Type | Description | +|------|------|-------------| +| [0] | `Clock.State` | The clock state of the commitment | +| [1] | `Machine.Hash` | The final state of the commitment | + +### `getMatch()` ```solidity -function instantiateBottom(Machine.Hash initialHash, Tree.Node contestedCommitmentOne, Machine.Hash contestedFinalStateOne, Tree.Node contestedCommitmentTwo, Machine.Hash contestedFinalStateTwo, Time.Duration allowance, uint256 startCycle, uint64 level, IDataProvider provider) external returns (Tournament) +function getMatch(Match.IdHash matchIdHash) external view returns (Match.State memory) ``` -Create a new bottom-level tournament for leaf dispute resolution. +Get the match state for a given match identifier. **Parameters:** | Name | Type | Description | |------|------|-------------| -| `initialHash` | `Machine.Hash` | Initial machine state hash | -| `contestedCommitmentOne` | `Tree.Node` | First contested commitment | -| `contestedFinalStateOne` | `Machine.Hash` | First contested final state | -| `contestedCommitmentTwo` | `Tree.Node` | Second contested commitment | -| `contestedFinalStateTwo` | `Machine.Hash` | Second contested final state | -| `allowance` | `Time.Duration` | Time allowance for participants | -| `startCycle` | `uint256` | Starting cycle for the tournament | -| `level` | `uint64` | Tournament level in hierarchy | -| `provider` | `IDataProvider` | Data provider for input validation | +| `matchIdHash` | `Match.IdHash` | The identifier of the match | -**Returns:** +**Return Values:** + +| Index | Type | Description | +|------|------|-------------| +| [0] | `Match.State` | The state of the match | + + +### `getMatchCycle()` +```solidity +function getMatchCycle(Match.IdHash matchIdHash) + external + view + returns (uint256) +``` + +Get the running machine cycle of a match by its ID hash. + +**Parameters:** + +| Name | Type | Description | +|------|------|-------------| +| `matchIdHash` | `Match.IdHash` | The identifier of the match | + +**Return Values:** + +| Index | Type | Description | +|------|------|-------------| +| [0] | `uint256` | The cycle of the match | + + +### `tournamentLevelConstants()` +```solidity +function tournamentLevelConstants() + external + view + returns (uint64 maxLevel, uint64 level, uint64 log2step, uint64 height) +``` +Get the level constants of a tournament. + +**Return Values:** | Name | Type | Description | |------|------|-------------| -| `tournament` | `Tournament` | Created bottom tournament instance | +| `maxLevel` | `uint64` | The maximum number of levels in the tournament | +| `level` | `uint64` | The current level of the tournament | +| `log2step` | `uint64` | The log2 number of steps between commitment leaves | +| `height` | `uint64` | The height of the commitment tree | + +### todo + +timeFinished +tournamentArguments + +## Data Structures +### TODO \ No newline at end of file From 0a1296498f071c521e1caed7b9b890a0fa3783b4 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Thu, 16 Oct 2025 12:57:29 +0100 Subject: [PATCH 07/10] docs(tutorials): Updated AA tutorial in line with v2.0 --- .../tutorials/utilizing-the-cli-AA-feature.md | 694 +++++++++--------- 1 file changed, 334 insertions(+), 360 deletions(-) diff --git a/cartesi-rollups_versioned_docs/version-2.0/tutorials/utilizing-the-cli-AA-feature.md b/cartesi-rollups_versioned_docs/version-2.0/tutorials/utilizing-the-cli-AA-feature.md index 3c3ad254..ce66ddc5 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/tutorials/utilizing-the-cli-AA-feature.md +++ b/cartesi-rollups_versioned_docs/version-2.0/tutorials/utilizing-the-cli-AA-feature.md @@ -1,36 +1,45 @@ --- id: cli-account-abstraction-feauture -title: Utilizing the account abstraction (AA) feature of the CLI +title: Utilizing the account abstraction feature of the CLI for Sponsored Transactions resources: - - url: https://github.com/tuler/aa-examples - title: AA Examples + - url: https://github.com/Mugen-Builders/docs_examples/tree/main/account-abstraction-frontend-demo + title: Account Abstraction frontend demo + + - url: https://github.com/Mugen-Builders/docs_examples/tree/main/account-abstraction-demo + title: Account Abstraction Script demo --- -This tutorial will guide you through utilizing the account abstraction infrastructure of the CLI to interact with your dApp while testing locally. +This tutorial will guide you through utilizing the account abstraction (AA) feature of the CLI to interact with your application while testing locally. ## Introduction -We currently have various architectures and SDKs supporting account abstraction across multiple chains, but there’s currently no dedicated solution for testing this integration in a local environment during dApp development. The latest update to the Cartesi CLI V0.16.0 fixes this by integrating an account abstraction architecture for testing the AA functionality of your dApp on your local machine. +Better user experience has become a major focus for blockchain protocols, infrastructures teams and applications developers. Over the last couple of years we've had several EIP's targeted at offering better user experience through various methods, of which the most impactful of these EIP's is EIP 4337 (Account Abstraction). + +EIP 4337 introduces a new application-layer architecture that enables features like sponsored transactions, transaction batching, partially funding or even funding gas fees using tokens other than ETH. This unlocks a much smoother onboarding experiences and reduces friction for end users. + +We currently have various architectures and SDKs supporting account abstraction across multiple chains, but there’s currently very few solutions dedicated to testing this integration locally during application development. + +The Cartesi CLI fixes this by integrating an account abstraction infrastructure for testing the AA functionality of your Cartesi application on your local machine. It currently supports sponsored transactions and passkey-based authentication, making it possible to experiment with real-world AA features before deploying. + +This local AA environment relies on external SDKs from popular account abstraction providers such as Alchemy, Biconomy and ZeroDev. Because the CLI uses the same underlying SDKs, the behavior you see locally should mirror what you'd experience on Mainnet provided you’re using the same provider. -This architecture relies on external SDKs from some of the popular account abstraction providers (Alchemy, Biconomy and ZeroDev), so it’s expected that the obtained behaviour on local should mirror mainnet completely as long as you’re using the same provider. For this tutorial, we’ll be using the Alchemy SDK. However, at the end of this tutorial, we’ll also provide demo applications for utilizing other supported SDK’s. +For this tutorial, we’ll focus specifically on sponsored transactions using the ZeroDev SDK, but you can easily adapt the approach for other providers like Alchemy or Biconomy. ## Architecture -The architecture of the CLI implementation for account abstraction is pretty much the same as that of mainnet and testnet applications; the only difference is that the CLI deploys and manages some of the important contracts once you start your local Anvil network by running the `cartesi run` command. What this means is that you don't need to bother yourself with building and deploying these important contracts; you simply utilize any SDK or account abstraction framework to interact with these contracts as you would on mainnet or testnet. +The architecture of the CLI implementation for account abstraction is pretty much the same as that of mainnet and testnet applications; the only difference is that the CLI deploys and manages some important contracts once you start your local Anvil network by running the `cartesi run` command and adding the paymaster and bundler to the service flag. What this means is that you don't need to bother yourself with building and deploying these important contracts; you simply utilize any SDK or account abstraction framework to interact with these contracts as you would on mainnet or testnet. ![img](../../../static/img/v1.5/AA-Architecture.jpg) -From the architecture above, transactions from the frontend are sent to the `bundler URL`, which controls a private key with which it pays for these transactions and sends them to the `entrypoint contract`. This `entrypoint contract` is a universal contract that forwards all transactions to the `paymaster` and also to the `user's smart contract wallet` if the user has one; if the user doesn't, it calls the `account factory` to deploy a smart wallet for the user. The `paymaster` refunds the gas fees for the execution, while the `user’s smart wallet` submits the transactions to the `input box contract`. +From the architecture above, transactions from the frontend are sent to the `bundler URL`, which controls a private key with which it pays for these transactions and sends them to the `entrypoint contract`. This `entrypoint contract` is a universal contract that forwards all transactions to the `paymaster` and also to the `user's smart contract wallet` if the user has one; if the user doesn't, it calls the `account factory` to deploy a smart wallet for the user. The `paymaster` refunds the gas fees for the execution, while the `user’s smart wallet` submits the transactions to the `input box contract`. - -You can view the addresses for some of these components, like the account factory, entrypoint address, paymaster, etc., by running the command `cartesi address-book`. While the bundler and paymaster addresses are displayed once you start your Cartesi dApp using the `cartesi run` command. Note that for mainnet/testnet integration, you’ll have to replace these with the respective URLs provided by the SDK you’re using. +You can view the addresses for some of these components, like the account factory, entrypoint address, paymaster, etc., by running the command `cartesi address-book`. While the bundler and paymaster URL are displayed once you start your Cartesi application using the `cartesi run --services bundler,paymaster` command. Note that for mainnet/testnet integration, you’ll have to replace these with the respective URLs provided by the SDK you’re using. ## Setting up the frontend environment We’ll be using React.js along with Wagmi to build this simple frontend application. We’ll also be relying heavily on some other dependencies that we’ll introduce later on. - -To bootstrap a React project with Wagmi already configured, we’ll use the CLI command +To bootstrap a React project with Wagmi already configured, we’ll use the CLI command ```shell npm create wagmi@latest @@ -38,401 +47,366 @@ To bootstrap a React project with Wagmi already configured, we’ll use the CLI This command would create a new React application with a simple UI and wallet connect by wagmi already integrated. - -Next, we install other dependencies we would be using by running this code in the terminal: +Next, we install other dependencies we would be using by running this code in the terminal: ```shell - npm i permissionless@0.1.43 encoding @alchemy/aa-core @alchemy/aa-accounts @cartesi/rollups + npm i permissionless@0.2.47 viem@2.28.0 @zerodev/sdk@5.4.36 @zerodev/ecdsa-validator@5.4.8 ``` -This command instals five different dependencies this project will be utilizing. Permissionless, and the two alchemy dependencies can be replaced with the packages for any other AA SDK you decide to use. For this tutorial we’ll be using Alchemy, and as such, we’ve installed the Alchemy aa-core and the aa-accounts dependencies. +This command installs four different dependencies this project will be utilizing. Permissionless, and the two zerodev dependencies can be replaced with the packages for any other AA SDK you decide to use. For this tutorial we’ll be using zerodev, and as such, we’ve installed the zerodev sdk and the ecdsa-validator dependencies. ### Configuring wagmi.ts file Congratulations on successfully bootstrapping a new frontend repo; next we’ll need to properly configure a wagmi client to work on localhost. Once you’re ready for mainnet or testnet, then you can update this configuration to work with any chain you intend to support. To do this, we CD into the src folder, then replace the contents of the `wagmi.ts` file with: ```javascript - import { http, cookieStorage, createConfig, createStorage } from "wagmi"; - import { foundry } from "wagmi/chains"; - import { coinbaseWallet, injected } from "wagmi/connectors"; - - export function getConfig() { - return createConfig({ - chains: [foundry], - connectors: [injected(), coinbaseWallet()], - storage: createStorage({ - storage: cookieStorage, - }), - ssr: true, - transports: { - [foundry.id]: http(), - }, - }); - } - - declare module "wagmi" { - interface Register { - config: ReturnType; - } - } +import "dotenv/config" +import { defineChain } from 'viem'; +import { http, cookieStorage, createConfig, createStorage } from 'wagmi' +import { mainnet, sepolia, cannon } from 'wagmi/chains' +import { coinbaseWallet, injected, walletConnect } from 'wagmi/connectors' + +let APPRPC = "http://127.0.0.1:6751/anvil"; + + +export const cartesi = defineChain({ + ...cannon, + rpcUrls: { default: { http: [APPRPC] } }, +}); + +export function getConfig() { + +let projectId = process.env.NEXT_PUBLIC_WC_PROJECT_ID as string; + + + return createConfig({ + chains: [cartesi], + connectors: [ + injected(), + coinbaseWallet(), + walletConnect({ projectId }), + ], + storage: createStorage({ + storage: cookieStorage, + }), + ssr: true, + transports: { + [cartesi.id]: http(APPRPC), + }, + }) +} + +declare module 'wagmi' { + interface Register { + config: ReturnType + } +} ``` -### Set up an Alchemy file +### Set up a zerodev file -Next, we setup an implementation for creating a smart account client for the connected account; this client takes in the paymaster and bundler URL, both of which are provided by the CLI once you start your Cartesi dApp, while the Entrypoint contract that’s used in the file is provided by the permissionless SDK we’re utilizing. We’ll name this file `alchemy.ts` since we’re making use of the Alchemy Smart Account client. This file should be located inside the src folder. +Next, we set up an implementation for creating a smart account, paymaster client, and finally a kernelClient for the connected wallet. The client makes use of the paymaster and bundler URL, both of which are provided by the CLI once you start your Cartesi application. The Entrypoint contract version that’s used in the file is provided by the zerodev SDK but its implementation is deployed and managed by the Cartesi CLI. + +Since we’re making use of the Zerodev Smart Account client, We’ll name this file `zerodev.ts`. This file would serve as the main engine for our implementation, its function is to create a `ecdsa validator` for the wallet connected to our frontend, this validator is bound to the smart wallet that's created, and ensures that only the connected wallet would be able to control the smart wallet. Towards the end of the file we create a kernelAccountClient, this combines the bundler, paymaster and entrypoint contract into a client and abstracts interaction between these three components using already structured implementations by zerodev. +This file should be located inside the src folder and at the same layer as the `wagmi.ts` file we updated previously. ```javascript - import { createLightAccount } from "@alchemy/aa-accounts"; - import { - createSmartAccountClient, - SmartAccountClient, - split, - WalletClientSigner, - } from "@alchemy/aa-core"; - import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "permissionless"; - import { createPimlicoPaymasterClient } from "permissionless/clients/pimlico"; - import { useEffect, useState } from "react"; - import { Chain, concat, Transport, zeroHash } from "viem"; - import { foundry } from "viem/chains"; - import { http, usePublicClient, useWalletClient } from "wagmi"; - - export type SmartAccountClientOptions = { - bundlerUrl: string; - paymasterUrl?: string; - }; +import "dotenv/config" +import { + createKernelAccount, + createKernelAccountClient, +} from "@zerodev/sdk" +import { signerToEcdsaValidator } from "@zerodev/ecdsa-validator" +import { KERNEL_V3_2 } from "@zerodev/sdk/constants" +import { + entryPoint07Address, + EntryPointVersion, +} from "viem/account-abstraction" +import { createPimlicoClient } from "permissionless/clients/pimlico"; +import { createPaymasterClient } from "viem/account-abstraction"; +import { Address, createPublicClient, http } from "viem" +import {cartesi} from "./wagmi" + +let BUNDLER_URL = "http://127.0.0.1:6751/bundler/rpc"; +let PAYMASTER_URL = "http://127.0.0.1:6751/paymaster/"; +let APPRPC = "http://127.0.0.1:6751/anvil"; +let chain = cartesi; + + +export const useSmartAccountClient = async (walletClient: any) => { + + const originalKernelVersion = KERNEL_V3_2 + + const publicClient = createPublicClient({ + chain, + transport: http(APPRPC), + }) + + const entryPoint = { + address: entryPoint07Address as Address, + version: "0.7" as EntryPointVersion, + } - export const useSmartAccountClient = < - TTransport extends Transport = Transport, - TChain extends Chain | undefined = undefined, - >( - options: SmartAccountClientOptions, - ) => { - const { bundlerUrl, paymasterUrl } = options; - const publicClient = usePublicClient(); - const { data: walletClient } = useWalletClient(); - - // create paymaster client - const paymasterClient = paymasterUrl - ? createPimlicoPaymasterClient({ - transport: http(paymasterUrl), - entryPoint: ENTRYPOINT_ADDRESS_V06, - }) - : undefined; - - console.log("printing paymasterClient"); - console.log(paymasterClient); - const bundlerMethods = [ - "eth_sendUserOperation", - "eth_estimateUserOperationGas", - "eth_getUserOperationReceipt", - "eth_getUserOperationByHash", - "eth_supportedEntryPoints", - ]; - - const splitTransport = split({ - overrides: [ - { - methods: bundlerMethods, - transport: http(bundlerUrl), - }, - ], - fallback: http(publicClient.transport.url), - }); + const signer = walletClient; + + if (!signer) { + throw new Error("Wallet client (signer) is undefined"); + } - const [smartAccountClient, setSmartAccountClient] = - useState(); - - const createClient = async () => { - if (walletClient !== undefined) { - const signer = new WalletClientSigner(walletClient, "json-rpc"); - console.log("printing signer..."); - console.log(signer); - const account = await createLightAccount({ - chain: foundry, - signer, - factoryAddress: "0x00004EC70002a32400f8ae005A26081065620D20", // CLI only supports LightAccount 1.1.0 for now - transport: http(publicClient.transport.url), - }); - console.log("printing light account..."); - console.log(account); - - const paymaster = "0x28ec0633192d0cBd9E1156CE05D5FdACAcB93947"; - const paymasterData = - "0x00000000000000000000000000000000000000000000000000000101010101010000000000000000000000000000000000000000000000000000000000000000cd91f19f0f19ce862d7bec7b7d9b95457145afc6f639c28fd0360f488937bfa41e6eedcd3a46054fd95fcd0e3ef6b0bc0a615c4d975eef55c8a3517257904d5b1c"; - const smartAccountClient = createSmartAccountClient({ - account, - chain: foundry, - transport: splitTransport, - paymasterAndData: paymasterClient - ? { - dummyPaymasterAndData: () => - concat([paymaster, paymasterData]), - paymasterAndData: async (userOperation, options) => { - const callData = await userOperation.callData; - const nonce = await userOperation.nonce; - // @ts-ignore - const initCode = await userOperation.initCode; - const { paymasterAndData } = - await paymasterClient.sponsorUserOperation({ - userOperation: { - ...userOperation, - callData, - nonce, - initCode, - }, - }); - return { - ...userOperation, - paymasterAndData, - }; - }, - } - : undefined, - }); - return smartAccountClient; - } - }; - - useEffect(() => { - if (walletClient !== undefined) { - createClient().then(setSmartAccountClient); - } - }, [walletClient, options.bundlerUrl, options.paymasterUrl]); - - return { - smartAccountClient, - }; + const ecdsaValidator = await signerToEcdsaValidator(publicClient, { + signer, + entryPoint, + kernelVersion: originalKernelVersion, + }) + + const account = await createKernelAccount(publicClient, { + plugins: { + sudo: ecdsaValidator, + }, + entryPoint, + kernelVersion: originalKernelVersion, + }) + + + const estimateFeesPerGas: any = async () => { + const pimlicoClient = createPimlicoClient({ + transport: http(BUNDLER_URL), + }); + const gas = await pimlicoClient.getUserOperationGasPrice(); + return gas.standard; }; + + + const paymasterClient = createPaymasterClient({ + transport: http(PAYMASTER_URL), + }) + + + const kernelClient = createKernelAccountClient({ + account, + chain, + bundlerTransport: http(BUNDLER_URL), + userOperation: { estimateFeesPerGas }, + client: publicClient, + paymaster: paymasterClient, + }) + + + return kernelClient; +} ``` -:::note PaymasterData Implementation -The PaymasterData implementation contained in the code block above is a dummy pre-signed data, and the paymaster itself would sponsor every transaction on localhost, while on mainnet or testnet this paymaster can be configured to only sponsor transactions under certain conditions, and the paymasterData would be different from what we’re currently using now. You can always refer to the respective documentation of the account abstraction provider SDK you’re using for more information on PaymasterData +:::note Paymaster and Bundler URL Implementation +The Paymaster and Bundler URL contained in the code block above is the paymaster and bundler URL obtained from the Cartesi CLI, therefore you should replace them with the URL's obtained on your end, while for testnet or mainnet environment, you replace them with the URL's from a provider of your choice. ::: ### Set up the Cartesi Client -The last part of this component we’ll need to set up is the Cartesi client page. This is responsible for integrating the Alchemy file we mentioned in the previous section to relay transactions to the input box; it receives the relayer URL, bunder URL, destination contract address, and the argument as a function argument. Then it uses the smart account client created in the previous section to sponsor these transactions and relay the received arguments to the input box contract. We’ll create this file also in the src folder, then call it `cartesi.ts`. +We’ll need to set up is a Cartesi client page. This is responsible for interacting with the `kernelClient` that was created in the Zerodev file from the previous section. It would utilize the kernel client to structure and relay transactions to the input box; it receives the argument to be sent to the Cartesi Application and the connected walletClient as function arguments, then uses the `kernelClient` to build and sponsor and relay the received arguments to the input box contract. We’ll create this file also in the src folder, then call it `cartesi.ts`. ```javascript - import { Address, encodeFunctionData, Hash, Hex } from "viem"; - import { useSmartAccountClient } from "./alchemy"; - import { useEffect, useState } from "react"; - import { contracts } from "@cartesi/rollups/export/abi/mainnet.json"; - - export type InputBoxAddInputOptions = { - bundlerUrl: string; - paymasterUrl?: string; - args: readonly [Address, Hex]; - }; +import { Address, encodeFunctionData, Hash, Hex, parseAbi } from "viem"; +import { useSmartAccountClient } from "./zerodev"; + + +export const useInputBoxAddInput = async (args: Hex, walletClient: any) => { + let INPUTBOXADDRESS = '0xc70074BDD26d8cF983Ca6A5b89b8db52D5850051' as Address; + let APPADDRESS = '0xa083af219355288722234c47d4c8469ca9af6605' as Address; + + const smartAccountClient = await useSmartAccountClient(walletClient); + console.log("printing useSmartAccountClient"); + console.log(smartAccountClient); + const inputBoxABI = parseAbi([ + "function addInput(address appContract, bytes calldata payload) external returns (bytes32)", + ]); + + const userOpHash = await smartAccountClient.sendUserOperation({ + callData: await smartAccountClient.account.encodeCalls([{ + to: INPUTBOXADDRESS, + value: BigInt(0), + data: encodeFunctionData({ + abi: inputBoxABI, + functionName: "addInput", + args: [APPADDRESS, args], + }), + }]), + }); + const _receipt = await smartAccountClient.waitForUserOperationReceipt({ + hash: userOpHash, + }) + let _hash = _receipt.receipt.transactionHash as Hash; - export const useInputBoxAddInput = (options: InputBoxAddInputOptions) => { - const { smartAccountClient } = useSmartAccountClient(options); - console.log("printing useSmartAccountClient"); - console.log(smartAccountClient); - // console.log(smartAccountClient?.getAddress); - const [hash, setHash] = useState(); - const [write, setWrite] = useState<() => void>(); - - useEffect(() => { - if (smartAccountClient && smartAccountClient.account) { - setWrite(() => async () => { - const uo = await smartAccountClient.sendUserOperation({ - account: smartAccountClient.account!, - uo: { - target: contracts.InputBox.address, - data: encodeFunctionData({ - abi: contracts.InputBox.abi, - functionName: "addInput", - args: options.args, - }), - }, - }); - const hash = - await smartAccountClient.waitForUserOperationTransaction( - uo, - ); - setHash(hash); - return hash; - }); - } - }, [smartAccountClient]); - return { hash, smartAccountClient, write }; - }; + return _hash; + +}; ``` +:::note Inputbox and App Address +The Inputbox and App address contained in the above code snippet are also for a local application instance, therefore if your application address on devnet is different, you should replace the code content as required, while for testnet or mainnet you replace the application address with that of your application and also update the inputbox address with the Cartesi inputbox address for your deployment chain. +::: + ### Set Up the Frontend UI -At this point, we have a complete Smart Account client active, and this is ready to relay transactions to the input box contract. However, one crucial part is missing, which is a user interface for users to be able to pass in any arbitrary payload of their choice to the Cartesi dApp running on the local machine. For this, we’ll CD into the app folder, then replace the contents of `page.tsx` with: +At this point, we have a complete Smart Account client active, and this is ready to relay transactions to the input box contract. However, one crucial part is missing, which is a user interface for users to be able to pass in any arbitrary payload of their choice to the Cartesi application running on the local machine. For this, we’ll CD into the app folder, then replace the contents of `page.tsx` with: ```javascript - "use client"; - - import { useEffect, useState } from "react"; - import { formatUnits, isHex, stringToHex } from "viem"; - import { - useAccount, - useBalance, - useBlockNumber, - useConnect, - useDisconnect, - } from "wagmi"; - import { useInputBoxAddInput } from "@/cartesi"; - import { useQueryClient } from "@tanstack/react-query"; - - function App() { - const account = useAccount(); - const { connectors, connect, status, error } = useConnect(); - const queryClient = useQueryClient(); - const { disconnect } = useDisconnect(); - const { data: blockNumber } = useBlockNumber({ watch: true }); - - const [bundlerUrl, setBundlerUrl] = useState( - "http://localhost:8080/bundler/rpc", - ); - const [paymasterUrl, setPaymasterUrl] = useState( - "http://localhost:8080/paymaster/", - ); - const [usePaymaster, setUsePaymaster] = useState(true); - - const [payload, setPayload] = useState("hello"); - - // transaction through hook - const { hash, smartAccountClient, write } = useInputBoxAddInput({ - bundlerUrl, - paymasterUrl: usePaymaster ? paymasterUrl : undefined, - args: [ - "0xab7528bb862fb57e8a2bcd567a2e929a0be56a5e", - isHex(payload) ? payload : stringToHex(payload), - ], - }); +"use client"; +import { useEffect, useState } from "react"; +import { isHex, stringToHex } from "viem"; +import { + useAccount, + useBlockNumber, + useConnect, + useDisconnect, + useWalletClient, +} from "wagmi"; +import { useInputBoxAddInput } from "@/cartesi"; +import { useSmartAccountClient } from "../zerodev"; + +function App() { + const account = useAccount(); + const { connectors, connect, status, error } = useConnect(); + const { disconnect } = useDisconnect(); + const { data: blockNumber } = useBlockNumber({ watch: true }); + + const [usePaymaster, setUsePaymaster] = useState(true); + const [isSubmitting, setIsSubmitting] = useState(false); + + const [payload, setPayload] = useState("hello"); + + const { data: walletClient } = useWalletClient(); + + const sunmitUserOp = async () => { + setIsSubmitting(true); + let hexPayload = isHex(payload) ? payload : stringToHex(payload); + await useInputBoxAddInput(hexPayload, walletClient); + window.alert( + "Transaction submitted to application. Check your node logs for more details" + ); + setIsSubmitting(false); + }; + + const [smartAccountClient, setSmartAccountClient] = useState(null); + + useEffect(() => { + const fetchSmartAccountClient = async () => { + const client = await useSmartAccountClient(walletClient); + setSmartAccountClient(client); + }; + fetchSmartAccountClient(); + }, [walletClient]); + + return ( + <> +
+

Account (Signer)

+ +
+ status: {account.status} +
+ addresses: {JSON.stringify(account.addresses)} +
+ chainId: {account.chainId} +
+ blockNumber: {blockNumber?.toString()} +
+ + {account.status === "connected" && ( + + )} +
+ +
+

Connect

+ {connectors.map((connector) => ( + + ))} +
{status}
+
{error?.message}
+
+ +
+

Account Abstraction

+
+ Smart Account Address: + {smartAccountClient?.account.address} +
+
+ +
+

Transaction

+
+ payload: + setPayload(e.target.value)} /> +
+ setUsePaymaster(!usePaymaster)} + /> + +
+ +
+ + ); +} + +export default App; - const { data: balance, queryKey } = useBalance({ - address: smartAccountClient?.account.address, - query: { enabled: !!smartAccountClient }, - }); - useEffect(() => { - queryClient.invalidateQueries({ queryKey }); - }, [blockNumber, queryClient]); - - return ( - <> -
-

Account (Signer)

- -
- status: {account.status} -
- addresses: {JSON.stringify(account.addresses)} -
- chainId: {account.chainId} -
- blockNumber: {blockNumber?.toString()} -
- - {account.status === "connected" && ( - - )} -
- -
-

Connect

- {connectors.map((connector) => ( - - ))} -
{status}
-
{error?.message}
-
- -
-

Account Abstraction

-
- Bundler URL: - setBundlerUrl(e.target.value)} - /> -
-
- Paymaster URL: - setPaymasterUrl(e.target.value)} - /> -
-
- Smart Account Address: - {smartAccountClient?.account.address} -
-
- Smart Account Balance: - {balance && ( - - {formatUnits(balance.value, balance.decimals)}{" "} - {balance.symbol} - - )} -
-
- -
-

Transaction

-
- payload: - setPayload(e.target.value)} - /> -
- setUsePaymaster(!usePaymaster)} - /> - -
- - {hash} -
- - ); - } +``` + +We now have a functional frontend and account abstraction infrastructure available to interact with your Cartesi application running on localhost. But to complete this tutorial, we’ll need to set up and run a demo Cartesi application, as this will deploy all the necessary contracts like the entrypoint contract, relayer contract, smart account factory, and the necessary bundler and paymaster servers, without these we won't be able to interact with the account abstraction implementation. + +## Set up and run a Cartesi application backend on localhost - export default App; +This section of the tutorial is focused on setting up a Cartesi application on a local host; this will be the target of all user executions on the frontend. To do this, we simply run the following commands:. + +- Create a new project using the command: + +```bash + cartesi create AA-on-CLI --template javascript ``` -We now have a functional frontend and account abstraction infrastructure available to interact with your Cartesi dApp running on localhost. But to complete this tutorial, we’ll need to set up and run a demo Cartesi dApp, as this will deploy all the necessary contracts like the entrypoint contract, relayer contract, and smart account factory, and without these we won't be able to test out the account abstraction implementation. +- Cd into `AA-on-CLI`, then build the application by running the command + +```bash + cartesi build +``` -## Setting up and running a Cartesi dApp backend on localhost +- Next, run the below command to start a local anvil node and deploy all necessary servers and contracts. + +```bash + cartesi run --services bundler,explorer,graphql,paymaster,passkey +``` -This last section of this article is focused on setting up a Cartesi dApp on a local host; this will be the target of all user executions on the frontend. To do this, we simply run the following commands:. +- Finally, in a new terminal, navigate to our frontend repository and start the frontend by running the command. -- Create a new project using the command `cartesi create AA-on-CLI --template javascript`. -- Cd into `AA-on-CLI` Build the dApp by running the command `cartesi build` -- Next, run the command `cartesi run` to start a local anvil node and deploy all necessary contracts. -- Finally, in a new terminal, navigate to our frontend repository and start the frontend by running the command `npm run dev`. +```bash + npm run dev +``` +## Testing the application -## Testing out the new account abstraction powered dApp -To test out the dApp, we simply visit the page our frontend is running on. We should have a simple UI available with a wallet connect feature and a text box available. First we’ll need to connect our wallet, then after a couple of seconds the frontend displays our smart contract account address; this will be the address that forwards every execution we make to the inputbox contract; therefore, the Dapp on the backend will pick this new address as the sender and not the address we connected initially. Next, we can interact with the dApp by passing in any generic message of our choice into the text box and then hitting the send input button. This should trigger our connected wallet to display a popup asking us to sign a message (which does not require us to pay gas fees) and not the regular transaction verification request. Once we sign this transaction, the message we typed in will be forwarded to our dApp running on localhost. You can check the terminal the dApp is running on to verify that the message got to our dApp and the actual address that sent the message. +To test out the application, we simply visit the page our frontend is running on. We should have a simple UI available with a wallet connect feature and a text box available. First we’ll need to connect our wallet, then after a couple of seconds the frontend displays our smart contract account address; this will be the address that forwards every execution we make to the inputbox contract; therefore, the application on the backend will pick this new address as the sender and not the address we connected initially. Next, we can interact with the application by passing in any generic message of our choice into the text box and then hitting the `send input` button. This should trigger our connected wallet to display a popup asking us to sign a message (which does not require us to pay gas fees) and not the regular transaction verification request. Once we sign this transaction, the message we typed in will be forwarded to our application running on localhost. You can check the terminal where the application is running to verify that the message got to our application and the actual address that sent the message. ## Conclusion -We’ve been able to build our first gasless transaction supporting dApp on Cartesi and that’s great. But it’s important to note that the above infrastructure we’ve currently setup is streamlined to localhost but can easily be configured to work with any network of your choice by simply replacing the bundler URL and also the paymaster URL with that provided by Alchemy for the chain you intend to support. You can check the [Alchemy documentation](https://docs.alchemy.com/reference/bundler-api-quickstart) for more information on how to create an account and also obtain a bundler and paymaster URL. +We’ve been able to build our first sponsored transaction supporting application on Cartesi and that’s great. But it’s important to note that the above infrastructure we’ve setup is streamlined to localhost but can easily be configured to work with any network of your choice by simply replacing the bundler URL and also the paymaster URL with that provided by zerodev for the chain you intend to support. You can check the [zerodev documentation](https://docs.zerodev.app/) for more information on how to create an account and also obtain a bundler and paymaster URL. +Access a complete codebase with the code blocks from the tutorial [here.](https://github.com/Mugen-Builders/docs_examples/tree/main/account-abstraction-frontend-demo) \ No newline at end of file From de86abbc4d1a97852d39fdb6fac85a7aa3a46cdc Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Wed, 19 Nov 2025 14:52:01 +0100 Subject: [PATCH 08/10] docs(graphql) remove mentions and references to graphql server in v2 --- .../api-reference/backend/notices.md | 2 +- .../api-reference/backend/reports.md | 2 +- .../api-reference/graphql/basics.md | 90 --- .../graphql/directives/_category_.yml | 2 - .../graphql/directives/deprecated.mdx | 25 - .../graphql/directives/include.mdx | 25 - .../api-reference/graphql/directives/skip.mdx | 25 - .../graphql/directives/specified-by.mdx | 25 - .../graphql/enums/completion-status.md | 51 -- .../graphql/filters/_category_.yml | 4 - .../graphql/filters/application-filter.md | 139 ----- .../graphql/filters/convenient-filter.md | 189 ------ .../graphql/filters/input-filter.md | 178 ------ .../api-reference/graphql/generated.md | 19 - .../graphql/objects/_category_.yml | 2 - .../graphql/objects/app-connection.md | 49 -- .../api-reference/graphql/objects/app-edge.md | 40 -- .../graphql/objects/application.md | 38 -- .../graphql/objects/completion-status.md | 34 -- .../delegate-call-voucher-connection.md | 52 -- .../objects/delegate-call-voucher-edge.md | 42 -- .../graphql/objects/delegate-call-voucher.md | 66 --- .../graphql/objects/input-connection.md | 52 -- .../graphql/objects/input-edge.md | 42 -- .../api-reference/graphql/objects/input.md | 122 ---- .../graphql/objects/notice-connection.md | 53 -- .../graphql/objects/notice-edge.md | 43 -- .../api-reference/graphql/objects/notice.md | 54 -- .../graphql/objects/output-validity-proof.md | 36 -- .../graphql/objects/page-info.md | 44 -- .../api-reference/graphql/objects/proof.md | 36 -- .../graphql/objects/report-connection.md | 49 -- .../graphql/objects/report-edge.md | 39 -- .../api-reference/graphql/objects/report.md | 47 -- .../graphql/objects/voucher-connection.md | 53 -- .../graphql/objects/voucher-edge.md | 43 -- .../api-reference/graphql/objects/voucher.md | 70 --- .../graphql/queries/_category_.yml | 2 - .../graphql/queries/delegate-call-vouchers.md | 291 --------- .../api-reference/graphql/queries/inputs.md | 447 -------------- .../api-reference/graphql/queries/notices.md | 337 ----------- .../api-reference/graphql/queries/reports.md | 369 ------------ .../api-reference/graphql/queries/vouchers.md | 379 ------------ .../api-reference/graphql/scalars/DateTime.md | 16 - .../graphql/scalars/_category_.yml | 2 - .../api-reference/graphql/scalars/big-int.md | 17 - .../api-reference/graphql/scalars/boolean.md | 16 - .../api-reference/graphql/scalars/int.md | 20 - .../api-reference/graphql/scalars/string.md | 15 - .../api-reference/graphql/sidebar-schema.js | 8 - .../version-2.0/api-reference/index.md | 6 +- .../version-2.0/development/query-outputs.md | 559 ++++++------------ .../development/running-an-application.md | 4 +- .../development/send-inputs-and-assets.md | 2 +- .../getting-started/architecture.md | 5 +- .../version-2.0/resources/migration-guide.md | 2 +- .../version-2.0/tutorials/calculator.md | 59 +- .../version-2.0-sidebars.json | 62 -- 58 files changed, 238 insertions(+), 4262 deletions(-) delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/basics.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/_category_.yml delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/deprecated.mdx delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/include.mdx delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/skip.mdx delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/specified-by.mdx delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/enums/completion-status.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/filters/_category_.yml delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/filters/application-filter.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/filters/convenient-filter.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/filters/input-filter.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/generated.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/_category_.yml delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/app-connection.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/app-edge.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/application.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/completion-status.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/delegate-call-voucher-connection.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/delegate-call-voucher-edge.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/delegate-call-voucher.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/input-connection.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/input-edge.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/input.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/notice-connection.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/notice-edge.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/notice.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/output-validity-proof.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/page-info.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/proof.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/report-connection.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/report-edge.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/report.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/voucher-connection.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/voucher-edge.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/voucher.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/_category_.yml delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/delegate-call-vouchers.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/inputs.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/notices.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/reports.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/vouchers.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/DateTime.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/_category_.yml delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/big-int.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/boolean.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/int.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/string.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/sidebar-schema.js diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/notices.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/notices.md index 09e9280a..cf05b1f1 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/notices.md +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/notices.md @@ -129,5 +129,5 @@ async fn emit_notice(payload: String) -> Option { :::note querying notices -Frontend clients can query notices using a GraphQL API exposed by Cartesi Nodes. [Refer to the documentation here](../../development/query-outputs.md/#query-all-reports) to query notices from the rollup server. +Frontend clients can query notices using a JSON RPC API exposed by Cartesi Nodes. [Refer to the documentation here](../../development/query-outputs.md#query-all-notices) to query notices from the rollup server. ::: diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/reports.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/reports.md index 7d82f3f3..6b8f4ced 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/reports.md +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/reports.md @@ -122,5 +122,5 @@ pub async fn handle_advance( :::note querying reports -Frontend clients can query reports using a GraphQL API exposed by the Cartesi Nodes. [Refer to the documentation to query reports](../../development/query-outputs.md/#query-all-reports) from your dApp. +Frontend clients can query reports using a GraphQL API exposed by the Cartesi Nodes. [Refer to the documentation to query reports](../../development/query-outputs.md#query-all-reports) from your dApp. ::: diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/basics.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/basics.md deleted file mode 100644 index 2af3f0b2..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/basics.md +++ /dev/null @@ -1,90 +0,0 @@ ---- -id: overview -title: Overview ---- - -# GraphQL API Overview - -The Cartesi Rollups GraphQL API provides a powerful interface for querying the state of a Cartesi Rollups instance. This API allows frontend clients to retrieve detailed information about inputs processed by the rollup and the outputs produced by the dApp's backend in response to these inputs. - -The API primarily deals with four main types of entities: - -1. [Inputs](./queries/inputs.md): Requests submitted to the application to advance its state. -2. [Notices](./queries/notices.md): Informational statements that can be validated in the base layer blockchain. -3. [Vouchers](./queries/vouchers.md): Representations of transactions that can be carried out on the base layer blockchain. -4. [Reports](./queries/reports.md): Application logs or diagnostic information. - - -Some key features of the GraphQL API include the ability to: - -- Retrieve detailed information about individual inputs, notices, vouchers, and reports. -- List and paginate through collections of these entities. -- Access proof data for notices and vouchers, allowing for validation on the base layer. -- Filter and sort results based on various criteria. - -## Basic Query Structure - -GraphQL queries in the Cartesi Rollups API typically involve specifying: - -1. The type of entity you're querying (input, notice, voucher, or report). -2. Any necessary arguments (e.g., index values, pagination parameters). -3. The fields you want to retrieve from the entity. - -Here's a basic example of querying an input: - -```graphql -query { - input(index: 1) { - index - status - timestamp - msgSender - payload - } -} -``` - -## Pagination - -The API uses cursor-based pagination for queries that return collections of entities (e.g., listing all notices). This is implemented through connection types (e.g.,[`InputConnection`](./objects/input-connection.md), [`NoticeConnection`](./objects/notice-connection.md)) that include `edges` and `pageInfo` fields. - -Example of a paginated query: - -```graphql -query { - notices(first: 5, after: "cursor_value") { - edges { - node { - index - payload - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } -} -``` - -## Response Format - -API responses are in JSON format and typically have the following structure: - -```json -{ - "data": { - // Requested data here - }, - "errors": [ - // Any errors encountered during query execution - ] -} -``` - -## GraphQL Playground - -You can use the GraphQL Playground, an interactive in-browser IDE, to test and explore the API. For local development, it's typically accessible at `http://localhost:8080/graphql`. - - diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/_category_.yml b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/_category_.yml deleted file mode 100644 index f622453a..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/_category_.yml +++ /dev/null @@ -1,2 +0,0 @@ -label: Directives -link: null diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/deprecated.mdx b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/deprecated.mdx deleted file mode 100644 index b3b7e358..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/deprecated.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -id: deprecated -title: deprecated -hide_table_of_contents: false ---- - - -Marks an element of a GraphQL schema as no longer supported. - -```graphql -directive @deprecated( - reason: String = "No longer supported" -) -``` - - -### Arguments - -#### [`reason`](#) ([`String`](../../scalars/string)) - -Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted using the Markdown syntax, as specified by [CommonMark](https://commonmark.org/). - - - - diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/include.mdx b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/include.mdx deleted file mode 100644 index db56c81d..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/include.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -id: include -title: include -hide_table_of_contents: false ---- - - -Directs the executor to include this field or fragment only when the `if` argument is true. - -```graphql -directive @include( - if: Boolean! -) -``` - - -### Arguments - -#### [`if`](#) ([`Boolean!`](../../scalars/boolean)) - -Included when true. - - - - diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/skip.mdx b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/skip.mdx deleted file mode 100644 index 360f6350..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/skip.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -id: skip -title: skip -hide_table_of_contents: false ---- - - -Directs the executor to skip this field or fragment when the `if` argument is true. - -```graphql -directive @skip( - if: Boolean! -) -``` - - -### Arguments - -#### [`if`](#) ([`Boolean!`](../../scalars/boolean)) - -Skipped when true. - - - - diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/specified-by.mdx b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/specified-by.mdx deleted file mode 100644 index d90a391f..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/specified-by.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -id: specified-by -title: specifiedBy -hide_table_of_contents: false ---- - - -Exposes a URL that specifies the behavior of this scalar. - -```graphql -directive @specifiedBy( - url: String! -) -``` - - -### Arguments - -#### [`url`](#) ([`String!`](../../scalars/string)) - -The URL that specifies the behavior of this scalar. - - - - diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/enums/completion-status.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/enums/completion-status.md deleted file mode 100644 index f8f4b87b..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/enums/completion-status.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -id: completion-status -title: CompletionStatus -hide_table_of_contents: false ---- - -Represents the status of an input's processing. - -```graphql -enum CompletionStatus { - "Input has not been processed yet" - UNPROCESSED - "Input has been processed and accepted" - ACCEPTED - "Input has been processed and rejected" - REJECTED - "Input processing resulted in an exception" - EXCEPTION - "Input processing was halted by the machine" - MACHINE_HALTED - "Input processing exceeded the cycle limit" - CYCLE_LIMIT_EXCEEDED - "Input processing exceeded the time limit" - TIME_LIMIT_EXCEEDED - "Input processing exceeded the payload length limit" - PAYLOAD_LENGTH_LIMIT_EXCEEDED -} -``` - -## Values - -| Value | Description | -| ----- | ----------- | -| `UNPROCESSED` | Input has not been processed yet. | -| `ACCEPTED` | Input has been processed and accepted. | -| `REJECTED` | Input has been processed and rejected. | -| `EXCEPTION` | Input processing resulted in an exception. | -| `MACHINE_HALTED` | Input processing was halted by the machine. | -| `CYCLE_LIMIT_EXCEEDED` | Input processing exceeded the cycle limit. | -| `TIME_LIMIT_EXCEEDED` | Input processing exceeded the time limit. | -| `PAYLOAD_LENGTH_LIMIT_EXCEEDED` | Input processing exceeded the payload length limit. | - -## Example Query - -```graphql -query { - input(id: "1") { - status - } -} -``` \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/filters/_category_.yml b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/filters/_category_.yml deleted file mode 100644 index 977b78ae..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/filters/_category_.yml +++ /dev/null @@ -1,4 +0,0 @@ -label: 'Filters' -position: 6 -collapsible: true -collapsed: false \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/filters/application-filter.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/filters/application-filter.md deleted file mode 100644 index 86dcdad9..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/filters/application-filter.md +++ /dev/null @@ -1,139 +0,0 @@ ---- -title: Application Filter -description: Documentation for filtering applications in Cartesi Rollups GraphQL API -keywords: [graphql, filter, application, query] ---- - -# Application Filter - -The Application Filter enables you to query and filter applications in the Cartesi Rollups GraphQL API. You can filter applications based on their address and other properties. - -## Structure - -The Application Filter provides the following filter options: - -```graphql -input ApplicationFilter { - address: AddressFilterInput -} - -input AddressFilterInput { - eq: String - in: [String!] - not: AddressFilterInput - notIn: [String!] -} -``` - -## Usage Examples - -### Filter by Application Address - -```graphql -query { - applications( - filter: { - address: { - eq: "0x70ac08179605AF2D9e75782b8DEcDD3c22aA4D0C" - } - } - ) { - edges { - node { - address - inputs { - totalCount - } - reports { - totalCount - } - } - } - } -} -``` - -### Filter by Multiple Addresses - -```graphql -query { - applications( - filter: { - address: { - in: [ - "0x70ac08179605AF2D9e75782b8DEcDD3c22aA4D0C", - "0x59b670e9fA9D0A427751Af201D676719a970857b" - ] - } - } - ) { - edges { - node { - address - inputs { - totalCount - } - notices { - totalCount - } - vouchers { - totalCount - } - } - } - } -} -``` - -### Exclude Specific Applications - -```graphql -query { - applications( - filter: { - address: { - notIn: [ - "0x70ac08179605AF2D9e75782b8DEcDD3c22aA4D0C", - "0x59b670e9fA9D0A427751Af201D676719a970857b" - ] - } - } - ) { - edges { - node { - address - } - } - } -} -``` - -## Available Filter Types - -### AddressFilterInput -- `eq`: Equal to a specific address -- `in`: In array of addresses -- `not`: Not equal to a specific address -- `notIn`: Not in array of addresses - -## Best Practices - -1. **Address Format**: - - Always use checksummed Ethereum addresses - - Ensure addresses are properly formatted (0x prefix + 40 hexadecimal characters) - -2. **Query Optimization**: - - Use specific address filters when possible - - Consider using `in` filter for multiple addresses instead of separate queries - -3. **Pagination**: - - Implement pagination when querying multiple applications - - Use appropriate page sizes based on your application's needs - -4. **Error Handling**: - - Validate address format before querying - - Handle cases where no applications match the filter criteria - -5. **Security Considerations**: - - Validate and sanitize address inputs - - Implement appropriate access controls for sensitive application data \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/filters/convenient-filter.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/filters/convenient-filter.md deleted file mode 100644 index 17ee8fd9..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/filters/convenient-filter.md +++ /dev/null @@ -1,189 +0,0 @@ ---- -id: convenient-filter -title: Convenient Filter -hide_table_of_contents: false ---- - -The ConvenientFilter provides a flexible way to filter vouchers and DELEGATECALL vouchers based on various criteria. It supports filtering by destination address, execution status, and complex logical operations. - -## Filter Structure - -```graphql -input ConvenientFilter { - destination: AddressFilterInput - executed: BooleanFilterInput - and: [ConvenientFilter] - or: [ConvenientFilter] -} -``` - -### Address Filter Input - -The `AddressFilterInput` allows filtering by Ethereum addresses: - -```graphql -input AddressFilterInput { - eq: String # Equal to - ne: String # Not equal to - in: [String] # In array of addresses - nin: [String] # Not in array of addresses - and: [ConvenientFilter] - or: [ConvenientFilter] -} -``` - -### Boolean Filter Input - -The `BooleanFilterInput` enables filtering by boolean values: - -```graphql -input BooleanFilterInput { - eq: Boolean # Equal to - ne: Boolean # Not equal to - and: [ConvenientFilter] - or: [ConvenientFilter] -} -``` - -## Usage Examples - -### 1. Filter by Destination Address - -```graphql -query { - vouchers( - filter: [ - { - destination: { - eq: "0x1234567890123456789012345678901234567890" - } - } - ] - ) { - edges { - node { - index - destination - executed - } - } - } -} -``` - -### 2. Filter by Execution Status - -```graphql -query { - delegateCallVouchers( - filter: [ - { - executed: { - eq: false - } - } - ] - ) { - edges { - node { - index - destination - executed - } - } - } -} -``` - -### 3. Complex Filter with Logical Operators - -```graphql -query { - vouchers( - filter: [ - { - and: [ - { - destination: { - eq: "0x1234567890123456789012345678901234567890" - } - }, - { - executed: { - eq: false - } - } - ] - } - ] - ) { - edges { - node { - index - destination - executed - } - } - } -} -``` - -### 4. Filter with Multiple Addresses - -```graphql -query { - delegateCallVouchers( - filter: [ - { - destination: { - in: [ - "0x1234567890123456789012345678901234567890", - "0x0987654321098765432109876543210987654321" - ] - } - } - ] - ) { - edges { - node { - index - destination - executed - } - } - } -} -``` - -## Available Filters by Type - -### Vouchers and DELEGATECALL Vouchers -- `destination`: Filter by the destination address -- `executed`: Filter by execution status - -### Input Filter -```graphql -input InputFilter { - indexLowerThan: Int # Filter inputs with index lower than value - indexGreaterThan: Int # Filter inputs with index greater than value - msgSender: String # Filter by message sender address - type: String # Filter by input type ('inputbox' or 'espresso') -} -``` - -### Application Filter -```graphql -input AppFilter { - indexLowerThan: Int # Filter apps with index lower than value - indexGreaterThan: Int # Filter apps with index greater than value - name: String # Filter by application name - address: String # Filter by application address -} -``` - -## Best Practices - -1. **Performance**: When using filters, try to be as specific as possible to improve query performance. -2. **Logical Operators**: Use `and` and `or` operators judiciously to create precise filter conditions. -3. **Address Format**: Always use the full 20-byte Ethereum address format starting with '0x'. -4. **Pagination**: Consider using filters in combination with pagination for better performance when dealing with large datasets. \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/filters/input-filter.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/filters/input-filter.md deleted file mode 100644 index d68f93d7..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/filters/input-filter.md +++ /dev/null @@ -1,178 +0,0 @@ ---- -title: Input Filter -description: Documentation for filtering inputs in Cartesi Rollups GraphQL API -keywords: [graphql, filter, input, query] ---- - -# Input Filter - -The Input Filter allows you to query and filter inputs in the Cartesi Rollups GraphQL API. You can filter inputs based on various criteria such as input index, metadata, and timestamp. - -## Structure - -The Input Filter provides the following filter options: - -```graphql -input InputFilter { - index: BigIntFilterInput - metadata: MetadataFilterInput - timestamp: DateTimeFilterInput -} - -input MetadataFilterInput { - blockNumber: BigIntFilterInput - blockTimestamp: DateTimeFilterInput - epochIndex: BigIntFilterInput - inputIndex: BigIntFilterInput -} - -input BigIntFilterInput { - eq: String - gt: String - gte: String - in: [String!] - lt: String - lte: String - not: BigIntFilterInput - notIn: [String!] -} - -input DateTimeFilterInput { - eq: DateTime - gt: DateTime - gte: DateTime - in: [DateTime!] - lt: DateTime - lte: DateTime - not: DateTimeFilterInput - notIn: [DateTime!] -} -``` - -## Usage Examples - -### Filter by Input Index - -```graphql -query { - inputs( - filter: { - index: { - eq: "42" - } - } - ) { - edges { - node { - index - metadata { - blockNumber - blockTimestamp - } - } - } - } -} -``` - -### Filter by Block Number Range - -```graphql -query { - inputs( - filter: { - metadata: { - blockNumber: { - gte: "1000000" - lt: "1000100" - } - } - } - ) { - edges { - node { - index - metadata { - blockNumber - blockTimestamp - } - } - } - } -} -``` - -### Filter by Timestamp - -```graphql -query { - inputs( - filter: { - timestamp: { - gte: "2024-01-01T00:00:00Z" - lt: "2024-02-01T00:00:00Z" - } - } - ) { - edges { - node { - index - timestamp - metadata { - blockNumber - blockTimestamp - } - } - } - } -} -``` - -## Available Filter Types - -### BigIntFilterInput -- `eq`: Equal to -- `gt`: Greater than -- `gte`: Greater than or equal to -- `lt`: Less than -- `lte`: Less than or equal to -- `in`: In array of values -- `not`: Not equal to -- `notIn`: Not in array of values - -### DateTimeFilterInput -- `eq`: Equal to -- `gt`: Greater than -- `gte`: Greater than or equal to -- `lt`: Less than -- `lte`: Less than or equal to -- `in`: In array of values -- `not`: Not equal to -- `notIn`: Not in array of values - -### MetadataFilterInput -- `blockNumber`: Filter by block number -- `blockTimestamp`: Filter by block timestamp -- `epochIndex`: Filter by epoch index -- `inputIndex`: Filter by input index - -## Best Practices - -1. **Use Pagination**: When querying large datasets, always use pagination to limit the number of results returned. - -2. **Optimize Query Performance**: - - Use specific filters to narrow down results - - Combine multiple filters when possible - - Avoid overly broad date ranges - -3. **Date Format**: - - Use ISO 8601 format for dates (YYYY-MM-DDTHH:mm:ssZ) - - Consider timezone implications when filtering by timestamp - -4. **Index Usage**: - - Filter by index when possible, as it's the most efficient way to query - - Use index ranges for better performance - -5. **Error Handling**: - - Validate input values before querying - - Handle null or undefined values appropriately \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/generated.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/generated.md deleted file mode 100644 index 66155d1f..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/generated.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -id: schema -slug: /api/graphql -title: Schema Documentation -sidebar_position: 1 -hide_table_of_contents: true -pagination_next: null -pagination_prev: null -sidebar_class_name: navbar__toggle ---- - -This documentation has been automatically generated from the GraphQL schema. - -Use the docs in the sidebar to find out how to use the schema: - -- **Allowed operations**: queries and mutations. -- **Schema-defined types**: scalars, objects, enums, interfaces, unions, and input objects. - -Generated on 4/7/2023, 2:43:56 PM. diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/_category_.yml b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/_category_.yml deleted file mode 100644 index 6f1a07bd..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/_category_.yml +++ /dev/null @@ -1,2 +0,0 @@ -label: Objects -link: null diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/app-connection.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/app-connection.md deleted file mode 100644 index 048cce38..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/app-connection.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -id: app-connection -title: AppConnection -hide_table_of_contents: false ---- - -Represents a paginated list of applications. - -```graphql -type AppConnection { - "Total number of entries that match the query" - totalCount: Int! - "List of edges in the connection" - edges: [AppEdge!]! - "Information about the current page" - pageInfo: PageInfo! -} -``` - -## Fields - -| Name | Type | Description | -| ---- |------| ----------- | -| `totalCount` | [`Int!`](../../scalars/int) | Total number of entries that match the query. | -| `edges` | [`[AppEdge!]!`](../../objects/app-edge) | List of edges in the connection. | -| `pageInfo` | [`PageInfo!`](../../objects/page-info) | Information about the current page. | - -## Example Query - -```graphql -query { - applications(first: 10) { - totalCount - edges { - node { - address - name - } - cursor - } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor - } - } -} -``` \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/app-edge.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/app-edge.md deleted file mode 100644 index 35bf1055..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/app-edge.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -id: app-edge -title: AppEdge -hide_table_of_contents: false ---- - -Represents a single application in a paginated list. - -```graphql -type AppEdge { - "The application at this edge" - node: Application! - "A cursor for use in pagination" - cursor: String! -} -``` - -## Fields - -| Name | Type | Description | -| ---- |------| ----------- | -| `node` | [`Application!`](../../objects/application) | The application at this edge. | -| `cursor` | [`String!`](../../scalars/string) | A cursor for use in pagination. | - -## Example Query - -```graphql -query { - applications(first: 10) { - edges { - node { - id - name - address - } - cursor - } - } -} -``` \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/application.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/application.md deleted file mode 100644 index 4a2dc21b..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/application.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -id: application -title: Application -hide_table_of_contents: false ---- - -Represents a Cartesi Rollups application. - -```graphql -type Application { - "Application ID" - id: String! - "Application name" - name: String! - "Application Address" - address: String! -} -``` - -## Fields - -| Name | Type | Description | -| ---- |------| ----------- | -| `id` | [`String!`](../../scalars/string) | Application ID. | -| `name` | [`String!`](../../scalars/string) | Application name. | -| `address` | [`String!`](../../scalars/string) | Application Address. | - -## Example Query - -```graphql -query { - application(id: "0x123...") { - id - name - address - } -} -``` \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/completion-status.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/completion-status.md deleted file mode 100644 index b9ee93c2..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/completion-status.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -id: completion-status -title: CompletionStatus -hide_table_of_contents: false ---- - -Enum representing the possible statuses of an input's processing. - -```graphql -enum CompletionStatus { - Unprocessed - Accepted - Rejected - Exception - MachineHalted - CycleLimitExceeded - TimeLimitExceeded - PayloadLengthLimitExceeded -} -``` - -## Values - -| Name | Description | -| ---- | ----------- | -| `Unprocessed` | The input has not been processed yet. | -| `Accepted` | The input was accepted and processed successfully. | -| `Rejected` | The input was processed and the results were rejected. | -| `Exception` | An exception occurred during the processing of the input. | -| `MachineHalted` | The machine halted during the processing of the input. | -| `CycleLimitExceeded` | The cycle limit was exceeded during the processing of the input. | -| `TimeLimitExceeded` | The time limit was exceeded during the processing of the input. | -| `PayloadLengthLimitExceeded` | The payload length limit was exceeded during the processing of the input. | - diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/delegate-call-voucher-connection.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/delegate-call-voucher-connection.md deleted file mode 100644 index 384a38d9..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/delegate-call-voucher-connection.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -id: delegate-call-voucher-connection -title: DelegateCallVoucherConnection -hide_table_of_contents: false ---- - -Represents a paginated list of DELEGATECALL vouchers. - -```graphql -type DelegateCallVoucherConnection { - "Total number of entries that match the query" - totalCount: Int! - "List of edges in the connection" - edges: [DelegateCallVoucherEdge!]! - "Information about the current page" - pageInfo: PageInfo! -} -``` - -## Fields - -| Name | Type | Description | -| ---- |------| ----------- | -| `totalCount` | [`Int!`](../../scalars/int) | Total number of entries that match the query. | -| `edges` | [`[DelegateCallVoucherEdge!]!`](../../objects/delegate-call-voucher-edge) | List of edges in the connection. | -| `pageInfo` | [`PageInfo!`](../../objects/page-info) | Information about the current page. | - -## Example Query - -```graphql -query { - delegateCallVouchers(first: 10) { - totalCount - edges { - node { - index - destination - payload - executed - transactionHash - } - cursor - } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor - } - } -} -``` \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/delegate-call-voucher-edge.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/delegate-call-voucher-edge.md deleted file mode 100644 index ea174559..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/delegate-call-voucher-edge.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -id: delegate-call-voucher-edge -title: DelegateCallVoucherEdge -hide_table_of_contents: false ---- - -Represents a single DELEGATECALL voucher in a paginated list. - -```graphql -type DelegateCallVoucherEdge { - "The DELEGATECALL voucher at this edge" - node: DelegateCallVoucher! - "A cursor for use in pagination" - cursor: String! -} -``` - -## Fields - -| Name | Type | Description | -| ---- |------| ----------- | -| `node` | [`DelegateCallVoucher!`](../../objects/delegate-call-voucher) | The DELEGATECALL voucher at this edge. | -| `cursor` | [`String!`](../../scalars/string) | A cursor for use in pagination. | - -## Example Query - -```graphql -query { - delegateCallVouchers(first: 10) { - edges { - node { - index - destination - payload - executed - transactionHash - } - cursor - } - } -} -``` \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/delegate-call-voucher.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/delegate-call-voucher.md deleted file mode 100644 index a9dc9b80..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/delegate-call-voucher.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -id: delegate-call-voucher -title: DelegateCallVoucher -hide_table_of_contents: false ---- - -Represents a DELEGATECALL voucher in a Cartesi Rollups application. - -```graphql -type DelegateCallVoucher { - "Voucher index within the context of the input that produced it" - index: Int! - "Input whose processing produced the voucher" - input: Input! - "Transaction destination address in Ethereum hex binary format (20 bytes), starting with '0x'" - destination: String! - "Transaction payload in Ethereum hex binary format, starting with '0x'" - payload: String! - "Proof object that allows this voucher to be validated and executed on the base layer blockchain" - proof: Proof - "Indicates whether the voucher has been executed on the base layer blockchain" - executed: Boolean - "The hash of executed transaction" - transactionHash: String - "The application that produced the delegateed voucher" - application: Application! -} -``` - -## Fields - -| Name | Type | Description | -| ---- |------| ----------- | -| `index` | [`Int!`](../../scalars/int) | Voucher index within the context of the input that produced it. | -| `input` | [`Input!`](../../objects/input) | Input whose processing produced the voucher. | -| `destination` | [`String!`](../../scalars/string) | Transaction destination address in Ethereum hex binary format (20 bytes), starting with '0x'. | -| `payload` | [`String!`](../../scalars/string) | Transaction payload in Ethereum hex binary format, starting with '0x'. | -| `proof` | [`Proof`](../../objects/proof) | Proof object that allows this voucher to be validated and executed on the base layer blockchain. | -| `executed` | [`Boolean`](../../scalars/boolean) | Indicates whether the voucher has been executed on the base layer blockchain. | -| `transactionHash` | [`String`](../../scalars/string) | The hash of executed transaction. | -| `application` | [`Application!`](../../objects/application) | The application that produced the delegateed voucher. | - -## Example Query - -```graphql -query { - delegateCallVoucher(outputIndex: 1) { - index - input { - index - } - destination - payload - proof { - outputIndex - outputHashesSiblings - } - executed - transactionHash - application { - address - name - } - } -} -``` \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/input-connection.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/input-connection.md deleted file mode 100644 index cabf1f83..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/input-connection.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -id: input-connection -title: InputConnection -hide_table_of_contents: false ---- - -Represents a paginated list of inputs. - -```graphql -type InputConnection { - "Total number of entries that match the query" - totalCount: Int! - "List of edges in the connection" - edges: [InputEdge!]! - "Information about the current page" - pageInfo: PageInfo! -} -``` - -## Fields - -| Name | Type | Description | -| ---- |------| ----------- | -| `totalCount` | [`Int!`](../../scalars/int) | Total number of entries that match the query. | -| `edges` | [`[InputEdge!]!`](../../objects/input-edge) | List of edges in the connection. | -| `pageInfo` | [`PageInfo!`](../../objects/page-info) | Information about the current page. | - -## Example Query - -```graphql -query { - inputs(first: 10) { - totalCount - edges { - node { - id - index - status - msgSender - payload - } - cursor - } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor - } - } -} -``` diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/input-edge.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/input-edge.md deleted file mode 100644 index 2f500b71..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/input-edge.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -id: input-edge -title: InputEdge -hide_table_of_contents: false ---- - -Represents a single input in a paginated list. - -```graphql -type InputEdge { - "The input at this edge" - node: Input! - "A cursor for use in pagination" - cursor: String! -} -``` - -## Fields - -| Name | Type | Description | -| ---- |------| ----------- | -| `node` | [`Input!`](../../objects/input) | The input at this edge. | -| `cursor` | [`String!`](../../scalars/string) | A cursor for use in pagination. | - -## Example Query - -```graphql -query { - inputs(first: 10) { - edges { - node { - id - index - status - msgSender - payload - } - cursor - } - } -} -``` diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/input.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/input.md deleted file mode 100644 index a04ba1cf..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/input.md +++ /dev/null @@ -1,122 +0,0 @@ ---- -id: input -title: Input -hide_table_of_contents: false ---- - -Request submitted to the application to advance its state. - -```graphql -type Input { - "id of the input" - id: String! - "Input index starting from genesis" - index: Int! - "Status of the input" - status: CompletionStatus! - "Address responsible for submitting the input" - msgSender: String! - "Timestamp associated with the input submission, as defined by the base layer's block in which it was recorded" - timestamp: BigInt! @deprecated(reason: "Use `blockTimestamp` instead") - "Number of the base layer block in which the input was recorded" - blockNumber: BigInt! - "Input payload in Ethereum hex binary format, starting with '0x'" - payload: String! - "Get vouchers from this particular input with support for pagination" - vouchers(first: Int, last: Int, after: String, before: String): VoucherConnection! - "Get DELEGATECALL vouchers from this particular input with support for pagination" - delegateCallVouchers(first: Int, last: Int, after: String, before: String): DelegateCallVoucherConnection! - "Get notices from this particular input with support for pagination" - notices(first: Int, last: Int, after: String, before: String): NoticeConnection! - "Get reports from this particular input with support for pagination" - reports(first: Int, last: Int, after: String, before: String): ReportConnection! - "Timestamp associated with the Espresso input submission" - espressoTimestamp: String @deprecated(reason: "Will be removed") - "Number of the Espresso block in which the input was recorded" - espressoBlockNumber: String @deprecated(reason: "Will be removed") - "Input index in the Input Box" - inputBoxIndex: String - "Block timestamp" - blockTimestamp: BigInt - "Previous RANDAO value" - prevRandao: String - "The application that produced the input" - application: Application! -} -``` - -## Fields - -| Name | Type | Description | -| ---- |------| ----------- | -| `id` | [`String!`](../../scalars/string) | ID of the input. | -| `index` | [`Int!`](../../scalars/int) | Input index starting from genesis. | -| `status` | [`CompletionStatus!`](../../enums/completion-status) | Status of the input. | -| `msgSender` | [`String!`](../../scalars/string) | Address responsible for submitting the input. | -| `timestamp` | [`BigInt!`](../../scalars/bigint) | Timestamp associated with the input submission, as defined by the base layer's block in which it was recorded. | -| `blockNumber` | [`BigInt!`](../../scalars/bigint) | Number of the base layer block in which the input was recorded. | -| `payload` | [`String!`](../../scalars/string) | Input payload in Ethereum hex binary format, starting with '0x'. | -| `vouchers` | [`VoucherConnection!`](../../objects/voucher-connection) | Get vouchers from this particular input with support for pagination. | -| `delegateCallVouchers` | [`DelegateCallVoucherConnection!`](../../objects/delegate-call-voucher-connection) | Get DELEGATECALL vouchers from this particular input with support for pagination. | -| `notices` | [`NoticeConnection!`](../../objects/notice-connection) | Get notices from this particular input with support for pagination. | -| `reports` | [`ReportConnection!`](../../objects/report-connection) | Get reports from this particular input with support for pagination. | -| `espressoTimestamp` | [`String`](../../scalars/string) | Timestamp associated with the Espresso input submission. | -| `espressoBlockNumber` | [`String`](../../scalars/string) | Number of the Espresso block in which the input was recorded. | -| `inputBoxIndex` | [`String`](../../scalars/string) | Input index in the Input Box. | -| `blockTimestamp` | [`BigInt`](../../scalars/bigint) | Block timestamp. | -| `prevRandao` | [`String`](../../scalars/string) | Previous RANDAO value. | -| `application` | [`Application!`](../../objects/application) | The application that produced the input. | - -## Example Query - -```graphql -query { - input(outputIndex: 1) { - id - index - status - msgSender - timestamp - blockNumber - payload - vouchers(first: 10) { - edges { - node { - index - destination - payload - } - } - } - delegateCallVouchers(first: 10) { - edges { - node { - index - destination - payload - } - } - } - notices(first: 10) { - edges { - node { - index - payload - } - } - } - reports(first: 10) { - edges { - node { - index - payload - } - } - } - application { - address - name - } - } -} -``` \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/notice-connection.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/notice-connection.md deleted file mode 100644 index 4e6117cd..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/notice-connection.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -id: notice-connection -title: NoticeConnection -hide_table_of_contents: false ---- - -Represents a paginated list of notices. - -```graphql -type NoticeConnection { - "Total number of entries that match the query" - totalCount: Int! - "List of edges in the connection" - edges: [NoticeEdge!]! - "Information about the current page" - pageInfo: PageInfo! -} -``` - -## Fields - -| Name | Type | Description | -| ---- |------| ----------- | -| `totalCount` | [`Int!`](../../scalars/int) | Total number of entries that match the query. | -| `edges` | [`[NoticeEdge!]!`](../../objects/notice-edge) | List of edges in the connection. | -| `pageInfo` | [`PageInfo!`](../../objects/page-info) | Information about the current page. | - -## Example Query - -```graphql -query { - notices(first: 10) { - totalCount - edges { - node { - index - payload - proof { - outputIndex - outputHashesSiblings - } - } - cursor - } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor - } - } -} -``` diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/notice-edge.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/notice-edge.md deleted file mode 100644 index 63188fb6..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/notice-edge.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -id: notice-edge -title: NoticeEdge -hide_table_of_contents: false ---- - -Represents a single notice in a paginated list. - -```graphql -type NoticeEdge { - "The notice at this edge" - node: Notice! - "A cursor for use in pagination" - cursor: String! -} -``` - -## Fields - -| Name | Type | Description | -| ---- |------| ----------- | -| `node` | [`Notice!`](../../objects/notice) | The notice at this edge. | -| `cursor` | [`String!`](../../scalars/string) | A cursor for use in pagination. | - -## Example Query - -```graphql -query { - notices(first: 10) { - edges { - node { - index - payload - proof { - outputIndex - outputHashesSiblings - } - } - cursor - } - } -} -``` diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/notice.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/notice.md deleted file mode 100644 index 165b9566..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/notice.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -id: notice -title: Notice -hide_table_of_contents: false ---- - -Informational statement that can be validated in the base layer blockchain. - -```graphql -type Notice { - "Notice index within the context of the input that produced it" - index: Int! - "Input whose processing produced the notice" - input: Input! - "Notice data as a payload in Ethereum hex binary format, starting with '0x'" - payload: String! - "Proof object that allows this notice to be validated by the base layer blockchain" - proof: Proof - "The application that produced the notice" - application: Application! -} -``` - -## Fields - -| Name | Type | Description | -| ---- |------| ----------- | -| `index` | [`Int!`](../../scalars/int) | Notice index within the context of the input that produced it. | -| `input` | [`Input!`](../../objects/input) | Input whose processing produced the notice. | -| `payload` | [`String!`](../../scalars/string) | Notice data as a payload in Ethereum hex binary format, starting with '0x'. | -| `proof` | [`Proof`](../../objects/proof) | Proof object that allows this notice to be validated by the base layer blockchain. | -| `application` | [`Application!`](../../objects/application) | The application that produced the notice. | - -## Example Query - -```graphql -query { - notice(outputIndex: 1) { - index - input { - index - } - payload - proof { - outputIndex - outputHashesSiblings - } - application { - address - name - } - } -} -``` \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/output-validity-proof.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/output-validity-proof.md deleted file mode 100644 index a5041fde..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/output-validity-proof.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -id: output-validity-proof -title: OutputValidityProof -hide_table_of_contents: false ---- - - -Validity proof for an output. - -```graphql -type OutputValidityProof { - inputIndexWithinEpoch: Int! - outputIndexWithinInput: Int! - outputHashesRootHash: String! - vouchersEpochRootHash: String! - noticesEpochRootHash: String! - machineStateHash: String! - outputHashInOutputHashesSiblings: [String!]! - outputHashesInEpochSiblings: [String!]! -} -``` - - -## Fields - -| Name | Type | Description | -| ---- |------| ----------- | -| `inputIndexWithinEpoch`| [`Int!`](../../scalars/int) | Local input index within the context of the related epoch. | -| `outputIndexWithinInput`| [`Int!`](../../scalars/int) | Output index within the context of the input that produced it. | -| `outputHashesRootHash`| [`String!`](../../scalars/string) | Merkle root of all output hashes of the related input, given in Ethereum hex binary format (32 bytes), starting with '0x'.| -| `vouchersEpochRootHash`| [`String!`](../../scalars/string) | Merkle root of all voucher hashes of the related epoch, given in Ethereum hex binary format (32 bytes), starting with '0x'.| -| `noticesEpochRootHash`| [`String!`](../../scalars/string) | Merkle root of all notice hashes of the related epoch, given in Ethereum hex binary format (32 bytes), starting with '0x'. | -| `machineStateHash`| [`String!`](../../scalars/string) | Hash of the machine state claimed for the related epoch, given in Ethereum hex binary format (32 bytes), starting with '0x'. | -| `outputHashInOutputHashesSiblings`| [`[String!]!`](../../scalars/string) | Proof that this output hash is in the output-hashes merkle tree. This array of siblings is bottom-up ordered (from the leaf to the root). Each hash is given in Ethereum hex binary format (32 bytes), starting with '0x'. | -| `outputHashesInEpochSiblings`| [`[String!]!`](../../scalars/string) | Proof that this output-hashes root hash is in epoch's output merkle tree. This array of siblings is bottom-up ordered (from the leaf to the root). Each hash is given in Ethereum hex binary format (32 bytes), starting with '0x'. | - diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/page-info.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/page-info.md deleted file mode 100644 index 589cc713..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/page-info.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -id: page-info -title: PageInfo -hide_table_of_contents: false ---- - -Page metadata for the cursor-based Connection pagination pattern. - -```graphql -type PageInfo { - "Cursor pointing to the first entry of the page" - startCursor: String - "Cursor pointing to the last entry of the page" - endCursor: String - "Indicates if there are additional entries after the end cursor" - hasNextPage: Boolean! - "Indicates if there are additional entries before the start cursor" - hasPreviousPage: Boolean! -} -``` - -## Fields - -| Name | Type | Description | -| ---- |------| ----------- | -| `startCursor` | [`String`](../../scalars/string) | Cursor pointing to the first entry of the page. | -| `endCursor` | [`String`](../../scalars/string) | Cursor pointing to the last entry of the page. | -| `hasNextPage` | [`Boolean!`](../../scalars/boolean) | Indicates if there are additional entries after the end cursor. | -| `hasPreviousPage` | [`Boolean!`](../../scalars/boolean) | Indicates if there are additional entries before the start cursor. | - -## Example Query - -```graphql -query { - inputs(first: 10) { - pageInfo { - startCursor - endCursor - hasNextPage - hasPreviousPage - } - } -} -``` diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/proof.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/proof.md deleted file mode 100644 index 57f3f671..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/proof.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -id: proof -title: Proof -hide_table_of_contents: false ---- - -Data that can be used as proof to validate notices and execute vouchers on the base layer blockchain. - -```graphql -type Proof { - "Index of the output in the output box" - outputIndex: BigInt! - "Array of sibling hashes in the output box merkle tree" - outputHashesSiblings: [String]! -} -``` - -## Fields - -| Name | Type | Description | -| ---- |------| ----------- | -| `outputIndex` | [`BigInt!`](../../scalars/bigint) | Index of the output in the output box. | -| `outputHashesSiblings` | [`[String]!`](../../scalars/string) | Array of sibling hashes in the output box merkle tree. | - -## Example Query - -```graphql -query { - voucher(outputIndex: 1) { - proof { - outputIndex - outputHashesSiblings - } - } -} -``` diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/report-connection.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/report-connection.md deleted file mode 100644 index c7c90c0c..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/report-connection.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -id: report-connection -title: ReportConnection -hide_table_of_contents: false ---- - -Represents a paginated list of reports. - -```graphql -type ReportConnection { - "Total number of entries that match the query" - totalCount: Int! - "List of edges in the connection" - edges: [ReportEdge!]! - "Information about the current page" - pageInfo: PageInfo! -} -``` - -## Fields - -| Name | Type | Description | -| ---- |------| ----------- | -| `totalCount` | [`Int!`](../../scalars/int) | Total number of entries that match the query. | -| `edges` | [`[ReportEdge!]!`](../../objects/report-edge) | List of edges in the connection. | -| `pageInfo` | [`PageInfo!`](../../objects/page-info) | Information about the current page. | - -## Example Query - -```graphql -query { - reports(first: 10) { - totalCount - edges { - node { - index - payload - } - cursor - } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor - } - } -} -``` diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/report-edge.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/report-edge.md deleted file mode 100644 index 91959d2f..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/report-edge.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -id: report-edge -title: ReportEdge -hide_table_of_contents: false ---- - -Represents a single report in a paginated list. - -```graphql -type ReportEdge { - "The report at this edge" - node: Report! - "A cursor for use in pagination" - cursor: String! -} -``` - -## Fields - -| Name | Type | Description | -| ---- |------| ----------- | -| `node` | [`Report!`](../../objects/report) | The report at this edge. | -| `cursor` | [`String!`](../../scalars/string) | A cursor for use in pagination. | - -## Example Query - -```graphql -query { - reports(first: 10) { - edges { - node { - index - payload - } - cursor - } - } -} -``` diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/report.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/report.md deleted file mode 100644 index 8d8fd8f5..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/report.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -id: report -title: Report -hide_table_of_contents: false ---- - -Application log or diagnostic information. - -```graphql -type Report { - "Report index within the context of the input that produced it" - index: Int! - "Input whose processing produced the report" - input: Input! - "Report data as a payload in Ethereum hex binary format, starting with '0x'" - payload: String! - "The application that produced the report" - application: Application! -} -``` - -## Fields - -| Name | Type | Description | -| ---- |------| ----------- | -| `index` | [`Int!`](../../scalars/int) | Report index within the context of the input that produced it. | -| `input` | [`Input!`](../../objects/input) | Input whose processing produced the report. | -| `payload` | [`String!`](../../scalars/string) | Report data as a payload in Ethereum hex binary format, starting with '0x'. | -| `application` | [`Application!`](../../objects/application) | The application that produced the report. | - -## Example Query - -```graphql -query { - report(reportIndex: 1) { - index - input { - index - } - payload - application { - address - name - } - } -} -``` \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/voucher-connection.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/voucher-connection.md deleted file mode 100644 index 1f7eaec2..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/voucher-connection.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -id: voucher-connection -title: VoucherConnection -hide_table_of_contents: false ---- - -Represents a paginated list of vouchers. - -```graphql -type VoucherConnection { - "Total number of entries that match the query" - totalCount: Int! - "List of edges in the connection" - edges: [VoucherEdge!]! - "Information about the current page" - pageInfo: PageInfo! -} -``` - -## Fields - -| Name | Type | Description | -| ---- |------| ----------- | -| `totalCount` | [`Int!`](../../scalars/int) | Total number of entries that match the query. | -| `edges` | [`[VoucherEdge!]!`](../../objects/voucher-edge) | List of edges in the connection. | -| `pageInfo` | [`PageInfo!`](../../objects/page-info) | Information about the current page. | - -## Example Query - -```graphql -query { - vouchers(first: 10) { - totalCount - edges { - node { - index - destination - payload - value - executed - transactionHash - } - cursor - } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor - } - } -} -``` diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/voucher-edge.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/voucher-edge.md deleted file mode 100644 index 0e5a8e8a..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/voucher-edge.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -id: voucher-edge -title: VoucherEdge -hide_table_of_contents: false ---- - -Represents a single voucher in a paginated list. - -```graphql -type VoucherEdge { - "The voucher at this edge" - node: Voucher! - "A cursor for use in pagination" - cursor: String! -} -``` - -## Fields - -| Name | Type | Description | -| ---- |------| ----------- | -| `node` | [`Voucher!`](../../objects/voucher) | The voucher at this edge. | -| `cursor` | [`String!`](../../scalars/string) | A cursor for use in pagination. | - -## Example Query - -```graphql -query { - vouchers(first: 10) { - edges { - node { - index - destination - payload - value - executed - transactionHash - } - cursor - } - } -} -``` diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/voucher.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/voucher.md deleted file mode 100644 index b2959d38..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/voucher.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -id: voucher -title: Voucher -hide_table_of_contents: false ---- - -Representation of a transaction that can be carried out on the base layer blockchain, such as a transfer of assets. - -```graphql -type Voucher { - "Voucher index within the context of the input that produced it" - index: Int! - "Input whose processing produced the voucher" - input: Input! - "Transaction destination address in Ethereum hex binary format (20 bytes), starting with '0x'" - destination: String! - "Transaction payload in Ethereum hex binary format, starting with '0x'" - payload: String! - "Proof object that allows this voucher to be validated and executed on the base layer blockchain" - proof: Proof - "Value to be sent with the transaction" - value: BigInt - "Indicates whether the voucher has been executed on the base layer blockchain" - executed: Boolean - "The hash of executed transaction" - transactionHash: String - "The application that produced the voucher" - application: Application! -} -``` - -## Fields - -| Name | Type | Description | -| ---- |------| ----------- | -| `index` | [`Int!`](../../scalars/int) | Voucher index within the context of the input that produced it. | -| `input` | [`Input!`](../../objects/input) | Input whose processing produced the voucher. | -| `destination` | [`String!`](../../scalars/string) | Transaction destination address in Ethereum hex binary format (20 bytes), starting with '0x'. | -| `payload` | [`String!`](../../scalars/string) | Transaction payload in Ethereum hex binary format, starting with '0x'. | -| `proof` | [`Proof`](../../objects/proof) | Proof object that allows this voucher to be validated and executed on the base layer blockchain. | -| `value` | [`BigInt`](../../scalars/bigint) | Value to be sent with the transaction. | -| `executed` | [`Boolean`](../../scalars/boolean) | Indicates whether the voucher has been executed on the base layer blockchain. | -| `transactionHash` | [`String`](../../scalars/string) | The hash of executed transaction. | -| `application` | [`Application!`](../../objects/application) | The application that produced the voucher. | - -## Example Query - -```graphql -query { - voucher(address: "0x123...") { - index - input { - index - } - destination - payload - proof { - outputIndex - outputHashesSiblings - } - value - executed - transactionHash - application { - address - name - } - } -} -``` \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/_category_.yml b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/_category_.yml deleted file mode 100644 index e3882b59..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/_category_.yml +++ /dev/null @@ -1,2 +0,0 @@ -label: Queries -link: null diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/delegate-call-vouchers.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/delegate-call-vouchers.md deleted file mode 100644 index af292c50..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/delegate-call-vouchers.md +++ /dev/null @@ -1,291 +0,0 @@ ---- -id: delegate-call-vouchers -title: DELEGATECALL Vouchers -hide_table_of_contents: false ---- - -DELEGATECALL Vouchers are an extension of vouchers that enables DELEGATECALL operations to be executed on the base layer blockchain. While they share similarities with regular vouchers, they are specifically designed for DELEGATECALL operations, allowing for more flexible contract interactions. - -## 1. Get DELEGATECALL Voucher by Index - -Retrieve a specific DELEGATECALL voucher based on its output index. - -```graphql -query delegateCallVoucher($outputIndex: Int!) { - delegateCallVoucher(outputIndex: $outputIndex) { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - destination - payload - proof { - outputIndex - outputHashesSiblings - } - executed - transactionHash - application { - address - name - } - } -} -``` - -For DELEGATECALL vouchers, the API provides access to proof data that can be used for validation on the base layer blockchain. This proof data is accessible through the [`Proof`](../objects/proof.md) field on DELEGATECALL voucher objects. - -### Arguments - -| Name | Type | Description | -| ---- | ---- | ----------- | -| `outputIndex` | [`Int!`](../../scalars/int) | The index of the DELEGATECALL voucher to retrieve. | - -### Response Type - -[`DelegateCallVoucher`](../../objects/delegate-call-voucher) - -## 2. Get DELEGATECALL Vouchers - -Retrieve a list of DELEGATECALL vouchers with support for pagination. - -```graphql -query delegateCallVouchers($first: Int, $after: String, $filter: [ConvenientFilter]) { - delegateCallVouchers(first: $first, after: $after, filter: $filter) { - edges { - node { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - destination - payload - proof { - outputIndex - outputHashesSiblings - } - executed - transactionHash - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } -} -``` - -### Arguments - -| Name | Type | Description | -| ---- | ---- | ----------- | -| `first` | [`Int`](../../scalars/int) | Number of DELEGATECALL vouchers to retrieve (for pagination). | -| `after` | [`String`](../../scalars/string) | Cursor to start retrieving DELEGATECALL vouchers from (for pagination). | -| `filter` | [`[ConvenientFilter]`](../../filters/convenient-filter) | Optional filters to apply to the query. | - -### Response Type - -[`DelegateCallVoucherConnection`](../../objects/delegate-call-voucher-connection) - -## 3. Get DELEGATECALL Vouchers by Input - -Retrieve DELEGATECALL vouchers associated with a specific input. - -```graphql -query delegateCallVouchersByInput($inputIndex: Int!, $first: Int, $after: String) { - input(index: $inputIndex) { - delegateCallVouchers(first: $first, after: $after) { - edges { - node { - index - destination - payload - proof { - outputIndex - outputHashesSiblings - } - executed - transactionHash - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } - } -} -``` - -### Arguments - -| Name | Type | Description | -| ---- | ---- | ----------- | -| `inputIndex` | [`Int!`](../../scalars/int) | Index of the input to retrieve DELEGATECALL vouchers for. | -| `first` | [`Int`](../../scalars/int) | Number of DELEGATECALL vouchers to retrieve (for pagination). | -| `after` | [`String`](../../scalars/string) | Cursor to start retrieving DELEGATECALL vouchers from (for pagination). | - -### Response Type - -[`DelegateCallVoucherConnection`](../../objects/delegate-call-voucher-connection) - -## Examples - -1. Fetching a specific DELEGATECALL voucher: - -```graphql -query { - delegateCallVoucher(outputIndex: 1) { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - destination - payload - proof { - outputIndex - outputHashesSiblings - } - executed - transactionHash - application { - address - name - } - } -} -``` - -2. Listing earlier(first 5) DELEGATECALL vouchers with filter: - -```graphql -query { - delegateCallVouchers( - first: 5 - filter: [ - { - destination: { eq: "0x1234567890123456789012345678901234567890" } - executed: { eq: false } - } - ] - ) { - edges { - node { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - destination - payload - proof { - outputIndex - outputHashesSiblings - } - executed - transactionHash - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } -} -``` - -3. Retrieving DELEGATECALL vouchers for a specific input: - -```graphql -query { - input(index: 10) { - delegateCallVouchers(first: 3) { - edges { - node { - index - destination - payload - proof { - outputIndex - outputHashesSiblings - } - executed - transactionHash - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } - } -} -``` \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/inputs.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/inputs.md deleted file mode 100644 index 01934949..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/inputs.md +++ /dev/null @@ -1,447 +0,0 @@ ---- -id: inputs -title: Inputs -hide_table_of_contents: false ---- - -Inputs represent requests submitted to the application to advance its state. - -## 1. Get Input by Index - -Retrieve a specific input based on its identifier. - -```graphql -query getInput($inputIndex: Int!) { - input(index: $inputIndex) { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } -} -``` - -### Arguments - -| Name | Type | Description | -| ---- | ---- | ----------- | -| `inputIndex` | [`Int`](../../scalars/int) | Index of the input to retrieve. | - -### Response Type - -[`Input`](../../objects/input) - - -## 2. Get Inputs - -Retrieve a list of inputs with support for pagination. - -```graphql -query inputs( - $first: Int - $last: Int - $after: String - $before: String - $where: InputFilter -) { - inputs( - first: $first - last: $last - after: $after - before: $before - where: $where - ) { - edges { - node { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor - } - } -} -``` - -### Arguments - -| Name | Type | Description | -| ---- | ---- | ----------- | -| `first` | [`Int`](../../scalars/int) | Get at most the first `n` entries (forward pagination). | -| `last` | [`Int`](../../scalars/int) | Get at most the last `n` entries (backward pagination). | -| `after` | [`String`](../../scalars/string) | Get entries after the provided cursor (forward pagination). | -| `before` | [`String`](../../scalars/string) | Get entries before the provided cursor (backward pagination). | -| `where` | [`InputFilter`](../../filters/input-filter) | Filter criteria for inputs. | - - -### Response Type - -[`InputConnection`](../../objects/input-connection) - - -:::note pagination and filtering - -- You cannot mix forward pagination (`first`, `after`) with backward pagination (`last`, `before`) in the same query. - -- The `where` argument allows you to filter inputs based the [`InputFilter`](../../filters/input-filter). - -- When using `where` with `after` or `before`, the filter is applied first, and then the pagination is applied to the filtered results. -::: - - - -## 3. Get Input Result - -Retrieve the result of a specific input, including its associated notices, vouchers, and reports. - -```graphql -query getInputResult($inputIndex: Int!) { - input(index: $inputIndex) { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - reports { - edges { - node { - index - payload - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } - notices { - edges { - node { - index - payload - proof { - outputIndex - outputHashesSiblings - } - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } - vouchers { - edges { - node { - index - destination - payload - proof { - outputIndex - outputHashesSiblings - } - value - executed - transactionHash - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } - } -} -``` - -### Arguments - -| Name | Type | Description | -| ---- | ---- | ----------- | -| `inputIndex` | [`Int`](../../scalars/int) | Index of the input to retrieve. | - - -### Response Type - -[`Input`](../../objects/input) with nested connections for reports, notices, and vouchers. - - -## Examples - -1. Fetching a specific input: - - ```graphql - query { - input(index: 5) { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - } - ``` - -2. Listing earlier(first 5) inputs: - - ```graphql - query { - inputs(first: 5) { - edges { - node { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } - } - ``` - -3. Retrieving input results: - - ```graphql - query { - input(index: 10) { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - notices { - edges { - node { - index - payload - proof { - outputIndex - outputHashesSiblings - } - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } - vouchers { - edges { - node { - index - destination - payload - proof { - outputIndex - outputHashesSiblings - } - value - executed - transactionHash - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } - } - } - ``` - -4. Using pagination and filtering: - - ```graphql - query { - inputs(first: 5, where: { indexLowerThan: 1 }) { - edges { - node { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } - } - ``` - -5. Listing inputs with application information: - - ```graphql - query { - inputs(first: 10) { - edges { - node { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - } - } - } - ``` - -6. Retrieving a specific input with application information: - - ```graphql - query { - input(outputIndex: 1) { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - } - ``` - -7. Listing inputs with application information and cursor: - - ```graphql - query { - inputs(first: 10) { - edges { - node { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - } - pageInfo { - hasNextPage - endCursor - } - } - } - ``` \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/notices.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/notices.md deleted file mode 100644 index abc5c2c8..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/notices.md +++ /dev/null @@ -1,337 +0,0 @@ ---- -id: notices -title: Notices -hide_table_of_contents: false ---- - -Notices are informational statements that can be validated in the base layer blockchain. - -## 1. Get Notice by Index - -Retrieve a specific notice based on its index and associated input index. - -```graphql -query notice($outputIndex: Int!) { - notice(outputIndex: $outputIndex) { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - payload - proof { - outputIndex - outputHashesSiblings - } - application { - address - name - } - } -} -``` - -For notices, the API provides access to proof data that can be used for validation on the base layer blockchain. This proof data is accessible through the [`Proof`](../objects/proof.md) field on notice objects. - -### Arguments - -| Name | Type | Description | -| ------------ | --------------------------- | ---------------------------------------------- | -| `outputIndex` | [`Int!`](../../scalars/int) | Index of the notice to retrieve. | - -### Response Type - -[`Notice`](../../objects/notice) - -## 2. Get Notices - -Retrieve a list of notices with support for pagination. - -```graphql -query notices { - notices { - edges { - node { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - payload - proof { - outputIndex - outputHashesSiblings - } - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } -} -``` - -### Arguments - -None - -### Response Type - -[`NoticeConnection`](../../objects/notice-connection) - -## 3. Get Notices by Input - -Retrieve notices associated with a specific input. - -```graphql -query noticesByInput($inputIndex: Int!) { - input(index: $inputIndex) { - notices { - edges { - node { - index - payload - proof { - outputIndex - outputHashesSiblings - } - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } - } -} -``` - -### Arguments - -| Name | Type | Description | -| ------------ | --------------------------- | ------------------------------------------- | -| `inputIndex` | [`Int!`](../../scalars/int) | Index of the input to retrieve notices for. | - -### Response Type - -[`NoticeConnection`](../../objects/notice-connection) - - -## Examples - -1. Fetching a specific notice: - -```graphql -query { - notice(outputIndex: 1) { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - payload - proof { - outputIndex - outputHashesSiblings - } - application { - address - name - } - } -} -``` - -2. Listing earlier(first 5) notices: - -```graphql -query { - notices(first: 5) { - edges { - node { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - payload - proof { - outputIndex - outputHashesSiblings - } - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } -} -``` - -3. Retrieving notices for a specific input: - -```graphql -query { - input(index: 10) { - notices(first: 3) { - edges { - node { - index - payload - proof { - outputIndex - outputHashesSiblings - } - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } - } -} -``` - -4. Listing notices with proof data and application information: - -```graphql -query { - notices(first: 10) { - edges { - node { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - payload - proof { - outputIndex - outputHashesSiblings - } - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } -} -``` - -5. Fetching a specific notice by output index: - -```graphql -query { - notice(outputIndex: 1) { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - payload - proof { - outputIndex - outputHashesSiblings - } - application { - address - name - } - } -} -``` \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/reports.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/reports.md deleted file mode 100644 index bef2f49c..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/reports.md +++ /dev/null @@ -1,369 +0,0 @@ ---- -id: reports -title: Reports -hide_table_of_contents: false ---- - -Reports contain application logs or diagnostic information and cannot be validated on-chain. - -## 1. Get Report by Index - -Retrieve a specific report based on its index and associated input index. - -```graphql -query report($reportIndex: Int!) { - report(reportIndex: $reportIndex) { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - payload - application { - address - name - } - } -} -``` - -### Arguments - -| Name | Type | Description | -| ---- | ---- | ----------- | -| `reportIndex` | [`Int!`](../../scalars/int) | Index of the report to retrieve. | - - -### Response Type - - [`Report`](../../objects/report) - -## 2. Get reports - -Retrieve a list of reports with support for pagination. - -```graphql -query reports { - reports { - edges { - node { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - payload - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } -} -``` - -### Arguments - -None - -### Response Type - -[`ReportConnection`](../../objects/report-connection) - -## 3. Get Reports by Input - -Retrieve reports associated with a specific input. - -```graphql -query reportsByInput($inputIndex: Int!) { - input(index: $inputIndex) { - reports { - edges { - node { - index - payload - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } - } -} -``` - -### Arguments - -| Name | Type | Description | -| ---- | ---- | ----------- | -| `inputIndex` | [`Int!`](../../scalars/int) | Index of the input to retrieve reports for. | - -### Response Type - -[`ReportConnection`](../../objects/report-connection) - -## Examples - -1. Fetching a specific report: - - ```graphql - query { - report(reportIndex: 1) { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - payload - application { - address - name - } - } - } - ``` - -2. Listing earlier(first 5) reports: - - ```graphql - query { - reports(first: 5) { - edges { - node { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - payload - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } - } - ``` - -3. Paginating through reports: - - ```graphql - query { - reports(first: 5, after: "MA==") { - edges { - node { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - payload - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } - } - ``` - -4. Retrieving reports for a specific input: - - ```graphql - query { - input(index: 10) { - reports { - edges { - node { - index - payload - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } - } - } - ``` - -5. Combining pagination with input-specific reports: - - ```graphql - query { - input(index: 0) { - reports(last: 5, before: "MA==") { - edges { - node { - index - payload - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } - } - } - ``` - -6. Listing reports with application information: - - ```graphql - query { - reports(first: 10) { - edges { - node { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - payload - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } - } - ``` - -7. Fetching a specific report with application information: - - ```graphql - query { - report(reportIndex: 1) { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - payload - application { - address - name - } - } - } - ``` - diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/vouchers.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/vouchers.md deleted file mode 100644 index c5bbf344..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/vouchers.md +++ /dev/null @@ -1,379 +0,0 @@ ---- -id: vouchers -title: Vouchers -hide_table_of_contents: false ---- - - -Vouchers represent transactions that can be carried out on the base layer blockchain, such as asset transfers. They are used to effect changes in the base layer based on the application's state. - -## 1. Get Voucher by Index - -Retrieve a specific voucher based on its index and associated input index. - -```graphql -query voucher($outputIndex: Int!) { - voucher(outputIndex: $outputIndex) { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - destination - payload - proof { - outputIndex - outputHashesSiblings - } - value - executed - transactionHash - application { - address - name - } - } -} -``` -For vouchers, the API provides access to proof data that can be used for validation on the base layer blockchain. This proof data is accessible through the [`Proof`](../objects/proof.md) field on voucher objects. - -### Arguments - -| Name | Type | Description | -| ---- | ---- | ----------- | -| `outputIndex` | [`Int!`](../../scalars/int) | The index of the voucher to retrieve. | - - - -### Response Type - -[`Voucher`](../../objects/voucher) - - -## 2. Get Vouchers - -Retrieve a list of vouchers with support for pagination. - -```graphql -query vouchers($first: Int, $after: String) { - vouchers(first: $first, after: $after) { - edges { - node { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - destination - payload - proof { - outputIndex - outputHashesSiblings - } - value - executed - transactionHash - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } -} -``` - -### Arguments - -| Name | Type | Description | -| ---- | ---- | ----------- | -| `first` | [`Int`](../../scalars/int) | Number of vouchers to retrieve (for pagination). | -| `after` | [`String`](../../scalars/string) | Cursor to start retrieving vouchers from (for pagination). | - - -### Response Type - -[`VoucherConnection`](../../objects/voucher-connection) - - -## 3. Get Vouchers by Input - -Retrieve vouchers associated with a specific input. - -```graphql -query vouchersByInput($inputIndex: Int!, $first: Int, $after: String) { - input(index: $inputIndex) { - vouchers(first: $first, after: $after) { - edges { - node { - index - destination - payload - proof { - outputIndex - outputHashesSiblings - } - value - executed - transactionHash - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } - } -} -``` - -### Arguments - -| Name | Type | Description | -| ---- | ---- | ----------- | -| `inputIndex` | [`Int!`](../../scalars/int) | Index of the input to retrieve vouchers for. | -| `first` | [`Int`](../../scalars/int) | Number of vouchers to retrieve (for pagination). | -| `after` | [`String`](../../scalars/string) | Cursor to start retrieving vouchers from (for pagination). | - -### Response Type - -[`VoucherConnection`](../../objects/voucher-connection) - - - -## Examples - -1. Fetching a specific voucher: - - ```graphql - query { - voucher(outputIndex: 1) { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - destination - payload - proof { - outputIndex - outputHashesSiblings - } - value - executed - transactionHash - application { - address - name - } - } - } - ``` - -2. Listing earlier(first 5) vouchers: - - ```graphql - query { - vouchers(first: 5) { - edges { - node { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - destination - payload - proof { - outputIndex - outputHashesSiblings - } - value - executed - transactionHash - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } - } - ``` - -3. Retrieving vouchers associated with a specific input: - - ```graphql - query { - input(index: 10) { - vouchers(first: 3) { - edges { - node { - index - destination - payload - proof { - outputIndex - outputHashesSiblings - } - value - executed - transactionHash - application { - address - name - } - } - cursor - } - pageInfo { - hasNextPage - endCursor - } - } - } - } - ``` - -4. Listing all vouchers with proof data: - - ```graphql - query { - vouchers(first: 10) { - edges { - node { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - destination - payload - proof { - outputIndex - outputHashesSiblings - } - value - executed - transactionHash - application { - address - name - } - } - } - pageInfo { - hasNextPage - endCursor - } - } - } - ``` - -5. Fetching a specific voucher by output index: - - ```graphql - query { - voucher(outputIndex: 1) { - index - input { - id - index - status - msgSender - blockTimestamp - blockNumber - payload - inputBoxIndex - prevRandao - application { - address - name - } - } - destination - payload - proof { - outputIndex - outputHashesSiblings - } - value - executed - transactionHash - application { - address - name - } - } - } - ``` \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/DateTime.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/DateTime.md deleted file mode 100644 index 387b7d1b..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/DateTime.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -id: date-time -title: DateTime -hide_table_of_contents: false ---- - - -The `DateTime` scalar type represents a point in time, expressed as the number of seconds that have elapsed since the Unix epoch (January 1, 1970, at 00:00:00 UTC), not counting leap seconds. This is also known as Unix time, POSIX time, or Epoch time. - -```graphql -scalar DateTime -``` - -Format: Integer representing seconds since the Unix epoch - -Example: `1723746395` (represents August 15, 2024, 00:59:55 UTC) \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/_category_.yml b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/_category_.yml deleted file mode 100644 index 51a038dc..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/_category_.yml +++ /dev/null @@ -1,2 +0,0 @@ -label: Scalars -link: null diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/big-int.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/big-int.md deleted file mode 100644 index d5033f9f..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/big-int.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -id: big-int -title: BigInt -hide_table_of_contents: false ---- - - -The `BigInt` scalar type represents arbitrarily large signed integer values. It can handle numbers beyond the range of a 32-bit integer, making it suitable for representing large numerical values. - - - -```graphql -scalar BigInt -``` - - -Example: `1234567890123456789012345678901234567890`. diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/boolean.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/boolean.md deleted file mode 100644 index ad00a4dc..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/boolean.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -id: boolean -title: Boolean -hide_table_of_contents: false ---- - - -The `Boolean` scalar type represents a logical value of either `true` or `false`. It is used for simple yes/no or on/off flags in queries and mutations. - -```graphql -scalar Boolean -``` - - - - diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/int.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/int.md deleted file mode 100644 index 2169fbcb..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/int.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -id: int -title: Int -hide_table_of_contents: false ---- - - -The Int scalar type represents non-fractional signed whole numeric values. It can represent values between -(2^31) and (2^31 - 1), inclusive. This type is suitable for most counting or indexing needs within the API. - -```graphql -scalar Int -``` - -Range: `-2,147,483,648` to `2,147,483,647`. - -Examples: `42`, `17`, `0`. - - - - diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/string.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/string.md deleted file mode 100644 index 0835583e..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/string.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -id: string -title: String -hide_table_of_contents: false ---- - - -The `String` scalar type represents textual data as UTF-8 character sequences. It is used for free-form human-readable text, identifiers, and other string-based data in the API. - -```graphql -scalar String -``` - - -Examples: `"Hello, World!"`, `"d290f1ee-6c54-4b01-90e6-d701748f0851"`. \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/sidebar-schema.js b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/sidebar-schema.js deleted file mode 100644 index df790f52..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/sidebar-schema.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - "schemaSidebar": [ - { - "type": "autogenerated", - "dirName": "api/graphql" - } - ] -}; \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/index.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/index.md index 21839187..6c06cf7c 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/index.md +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/index.md @@ -10,7 +10,7 @@ resources: title: Smart Contracts for Cartesi Rollups --- -In a Cartesi dApp, the frontend and backend components communicate through the Rollups framework using HTTP and GraphQL APIs. +In a Cartesi dApp, the frontend and backend components communicate through the Rollups framework using HTTP and JSON-RPC APIs. When designing the APIs for this communication framework, we aimed to ensure that developers could create their applications without excessive concern about the low-level components of Cartesi Rollups. @@ -42,8 +42,6 @@ The figure below illustrates the main use cases for these interactions: - [`executeOutput()`](./contracts/application/#executeoutput) — Submits a JSON-RPC blockchain transaction to request the execution of a given voucher or notice by the [`Application`](./contracts/application.md) smart contract on the base layer. Vouchers can only be executed when an epoch is closed. -- Query outputs — You can submit a query to a Cartesi node to retrieve vouchers, notices, and reports as specified by the Cartesi Rollups GraphQL schema. +- Query outputs — You can submit a query to a Cartesi node to retrieve vouchers, notices, and reports as specified by the Cartesi Rollups JSON-RPC schema. - Inspect state — You can make an HTTP call to the Cartesi node to retrieve arbitrary dApp-specific application state. - - diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/query-outputs.md b/cartesi-rollups_versioned_docs/version-2.0/development/query-outputs.md index 94e80252..77cc7fcd 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/development/query-outputs.md +++ b/cartesi-rollups_versioned_docs/version-2.0/development/query-outputs.md @@ -8,6 +8,8 @@ resources: In Cartesi Rollups, outputs are essential in interacting with the blockchain. The direct output types are notices, reports and vouchers. +The JSON-RPC server unifies Notice and Vouchers into a single query, therefore a single request returns all Notices and Vouchers generated by the application. + ## Notices: Off-chain events A notice is a verifiable data declaration that attests to off-chain events or conditions and is accompanied by proof. Unlike vouchers, notices do not trigger direct interactions with other smart contracts. @@ -222,197 +224,152 @@ Received advance request data {"metadata":{"chain_id":31337,"app_contract":"0xef Adding notice with values 2, 4, 6, 8, 10 ``` -The notice can be validated and queried by any interested party. +## Vouchers: On-chain actions -### Query all notices +A voucher in Cartesi Rollups is a mechanism for executing on-chain actions from an application. -Frontend clients can use a GraphQL API exposed by the Cartesi Nodes to query the state of a Cartesi Rollups instance. +Think of it as a digital authorization ticket that enables an application to perform specific actions on the blockchain, such as transferring assets or approving transactions. -You can use the interactive in-browser GraphQL playground hosted on `http://localhost:8080/graphql/{application_address}` for local development. Note that you'll have to replace `{application_address}` with the address of your application. +### How Vouchers Work -In a GraphQL Playground, you typically have a section where you can input your query and variables separately. Here's how you would do it: +- The application backend creates a voucher while executing in the Cartesi Machine. -Input your query in the left pane: +- The voucher specifies the action, such as a token swap, and is sent to the blockchain. -```graphql -query notices { - notices { - totalCount - edges { - node { - index - input { - index - } - application { - id - name - address - } - payload - } - } - } -} -``` +- The [`Application`](../api-reference/contracts/application.md) contract executes the voucher using the [`executeOutput()`](../api-reference/contracts/application.md#executeoutput) function. -Click the "Play" button (a triangular icon). The Playground will send the request to the server, and you'll see the response in the right pane. +- The result is recorded on the base layer through claims submitted by a consensus contract. - + :::note create a voucher + [Refer to the documentation here](./asset-handling.md) for asset handling and creating vouchers in your application. + ::: -Alternatively, you can use a frontend client to query all the notices of an application running in a local environment: +These notices and vouchers can be validated and queried by any interested party. -```javascript -async function fetchNotices() { - const query = '{ "query": "{ notices { edges { node { payload } } } }" }'; +## Query all notices and vouchers + +Frontend clients can use a JSON-RPC API exposed by the Cartesi Nodes to query the state of a Cartesi Rollups instance. + +The below snippet uses a frontend client to request for a list of all vouchers and notices from an application running in a local environment: +```javascript +async function fetchOutputs() { try { - const response = await fetch( - "http://localhost:8080/graphql/0x75135d8ADb7180640d29d822D9AD59E83E8695b2", - { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: query, - } - ); + const response = await fetch("http://127.0.0.1:6751/rpc", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + jsonrpc: "2.0", + method: "cartesi_listOutputs", + params: { + application: "multiples-of-a-number", // APPLICATION NAME + limit: 10, + offset: 0 + }, + id: 1 + }) + }); const result = await response.json(); - for (const edge of result.data.notices.edges) { - const payload = edge.node.payload; - // Do something with the payload - } - } catch (error) { - console.error("Error fetching notices:", error); - } -} + // Extract the array that lives in result.result.data + const outputs = result?.result?.data ?? []; -fetchNotices(); -``` + for (const output of outputs) { + const decoded = output.decoded_data; -You can get notices based on their `inputIndex`: - -Input your query in the left pane. - -```graphql -query noticesByInput($inputIndex: String!) { - input(id: $inputIndex) { - id - index - payload - msgSender - notices { - edges { - node { - index - input { - index - } - application { - id - name - address - } - payload - } + if (!decoded) continue; + + // Handle Notice + if (decoded.type === "Notice") { + const payload = decoded.payload; // hex string + console.log("Notice payload:", payload); + // Do something with the payload + } + + // Handle Voucher + if (decoded.type === "Voucher") { + const { destination, value, payload } = decoded; + console.log("Voucher →", { destination, value, payload }); + // Do something with the payload } } + + } catch (error) { + console.error("Error fetching notices:", error); } } + +fetchOutputs(); ``` -Then, in the bottom-left corner of the Playground, you'll find a section that provides variables. Click on it to expand it, and then you can input your variables like this: +This can also be achieved via the terminal by running the below curl command: -``` -{ - "inputIndex": "0" -} +```bash +curl -X POST http://127.0.0.1:6751/rpc \ + -H "Content-Type: application/json" \ + -d '{ + "jsonrpc": "2.0", + "method": "cartesi_listOutputs", + "params": { + "application": "multiples-of-a-number", + "limit": 10, + "offset": 0 + }, + "id": 1 + }' ``` -Replace `0` with the value you want to pass for `$inputIndex`. +## Query a single notice or voucher - +You can retrieve detailed information about a single notice, including its proof information: -With a JavaScript client, you can construct the GraphQL query and variables separately and send them as a JSON object in the request's body. +Here is the query which takes the variables: `output_index` and `application` then returns the details of a notice. ```javascript -const query = ` -query noticesByInput($inputIndex: String!) { - input(id: $inputIndex) { - id - index - payload - msgSender - notices { - edges { - node { - index - input { - index - } - application { - id - name - address - } - payload - } - } - } +async function getOutput() { + try { + const response = await fetch("http://127.0.0.1:6751/rpc", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + jsonrpc: "2.0", + method: "cartesi_getOutput", + params: { + application: "multiples-of-a-number", // APPLICATION NAME + output_index: "0x0" // INDEX OF THE OUTPUT (VOUCHER OR NOTICE) + }, + id: 1 + }) + }); + + const result = await response.json(); + console.log(result) + // Do something with the JSON result + + + } catch (error) { + console.error("Error getting output:", error); } } -`; - -const variables = { - inputIndex: "0", // Replace 0 with the desired value -}; - -const response = await fetch( - "http://localhost:8080/graphql/0x75135d8ADb7180640d29d822D9AD59E83E8695b2", - { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ query, variables }), - } -); -const result = await response.json(); -for (let edge of result.data.input.notices.edges) { - let payload = edge.node.payload; -} +getOutput(); ``` -### Query a single notice - -You can retrieve detailed information about a notice, including its proof information: - -Here is the query which takes the variable: `outputIndex` and returns the details of a notice. +The curl equivalent of this function would be: -```graphql -query notice($outputIndex: Int!) { - notice(outputIndex: $outputIndex) { - index - input { - index - } - payload - proof { - outputIndex - outputHashesSiblings - } - application { - id - name - address - } - } -} +```bash +curl -X POST http://127.0.0.1:6751/rpc \ + -H "Content-Type: application/json" \ + -d '{ + "jsonrpc": "2.0", + "method": "cartesi_getOutput", + "params": { + "application": "multiples-of-a-number", + "output_index": "0x0" + }, + "id": 1 + }' ``` ## Reports: Stateless logs @@ -547,243 +504,113 @@ async fn emit_report( payload: String) -> Option { -You can use the exposed GraphQL API to query all reports from your application. +You can use the exposed JSON-RPC API to query all reports from an application. ### Query all reports -Frontend clients can use a GraphQL API exposed by the Cartesi Nodes to query the state of a Cartesi Rollups instance. +Frontend clients can also use the JSON-RPC API exposed by the Cartesi Nodes to query the state of a Cartesi Rollups instance as regards to emitted reports. -You can use the interactive in-browser GraphQL playground hosted on `http://localhost:8080/graphql/{application_address}` for local development. +The below command is a function that calls the JSON-RPC API to request for all reports emitted by an application running locally: -In a GraphQL Playground, you typically have a section where you can input your query and variables separately. Here's how you would do it: +```javascript +async function fetchReports() { + try { + const response = await fetch("http://127.0.0.1:6751/rpc", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + "jsonrpc": "2.0", + "method": "cartesi_listReports", + "params": { + "application": "multiples-of-a-number", + "limit": 10, + "offset": 0 + }, + "id": 1 + }) + }); -Input your query in the left pane: + const result = await response.json(); -```graphql -query reports { - reports { - totalCount - edges { - node { - index - input { - index - } - payload - } - } - } -} -``` + // Extract the array that lives in result.result.data + const reports = result?.result?.data ?? []; -Click the "Play" button (a triangular icon). The Playground will send the request to the server, and you'll see the response in the right pane. - -You can retrieve reports based on their `inputIndex`. - -Input your query in the left pane: - -```graphql -query reportsByInput($inputIndex: String!) { - input(id: $inputIndex) { - id - index - payload - msgSender - reports { - totalCount - edges { - node { - index - input { - index - } - application { - id - name - address - } - payload - } - } + for (const report of reports) { + console.log(report); + // Do something with the REPORT } + + } catch (error) { + console.error("Error fetching report:", error); } } -``` -Then, in the bottom-left corner of the Playground, you'll find a section that provides variables. Click on it to expand it, and then you can input your variables like this: - -```graphql -{ - "inputIndex": "0" -} +await fetchReports(); ``` -Replace `0` with the value you want to pass for `$inputIndex`. +This can also be achieved via the terminal by running the below curl command: -### Query a single report - -You can retrieve a report with its `reportIndex`. - -```graphql -query report($reportIndex: Int!) { - report(reportIndex: $reportIndex) { - index - input { - index - } - payload - application { - id - name - address - } - } -} +```bash +curl -X POST http://127.0.0.1:6751/rpc \ + -H "Content-Type: application/json" \ + -d '{ + "jsonrpc": "2.0", + "method": "cartesi_listReports", + "params": { + "application": "multiples-of-a-number", + "limit": 10, + "offset": 0 + }, + "id": 1 + }' ``` -Unlike Vouchers and Notices, reports are stateless and need attached proof. - -## Vouchers: On-chain actions - -A voucher in Cartesi Rollups is a mechanism for executing on-chain actions from an application. - -Think of it as a digital authorization ticket that enables an application to perform specific actions on the blockchain, such as transferring assets or approving transactions. - -### How Vouchers Work: - -- The application backend creates a voucher while executing in the Cartesi Machine. - -- The voucher specifies the action, such as a token swap, and is sent to the blockchain. - -- The [`Application`](../api-reference/contracts/application.md) contract executes the voucher using the [`executeOutput()`](../api-reference/contracts/application.md#executeoutput) function. - -- The result is recorded on the base layer through claims submitted by a consensus contract. - - :::note create a voucher - [Refer to the documentation here](./asset-handling.md) for asset handling and creating vouchers in your application. - ::: +### Query a single report -### Query all Vouchers +Similar to Notice and Vouchers, you can retrieve a single report using its `report_index`. Though unlike Vouchers and Notices, reports are stateless. -Similar to Notices and Reports the Frontend client can use a GraphQL API exposed by the Cartesi Nodes to query for voucher details from a Cartesi Rollups instance +```javascript +async function getOutput() { + try { + const response = await fetch("http://127.0.0.1:6751/rpc", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + jsonrpc: "2.0", + method: "cartesi_getReport", + params: { + application: "multiples-of-a-number", // APPLICATION NAME + report_index: "0x0" // INDEX OF THE REPORT + }, + id: 1 + }) + }); -You can use the interactive in-browser GraphQL playground hosted on `http://localhost:8080/graphql/{application_address}` for local development. + const result = await response.json(); + console.log(result) + // Do something with the JSON result -Input your query in the left pane: -```graphql -query vouchers { - vouchers { - totalCount - edges { - node { - index - input { - index - } - destination - payload - value - proof { - outputIndex - outputHashesSiblings - } - executed - application { - id - name - address - } - } - } - } -} -``` - -Click the "Play" button (a triangular icon). The Playground will send the request to the server, and you'll see the response in the right pane. - -### Query a Voucher via it's input index - -You can retrieve vouchers based on their `inputIndex`, to do that you need to, Input the below query in the left pane of the GraphQL playground: - -```graphql -query voucherByInput($inputIndex: String!) { - input(id: $inputIndex) { - id - index - payload - msgSender - vouchers { - totalCount - edges { - node { - index - input { - index - } - destination - payload - value - proof { - outputIndex - outputHashesSiblings - } - executed - application { - id - name - address - } - } - } - } + } catch (error) { + console.error("Error getting report:", error); } } -``` -Then, in the bottom-left corner of the Playground, you'll find a section that provides variables. Click on it to expand it, and then you can input your variables like this: - -```graphql -{ - "inputIndex": "0" -} +getOutput(); ``` -Replace `0` with the value you want to pass for `$inputIndex`. +The curl equivalent of this function would be: -### Query a single Voucher - -You can retrieve a report with its `reportIndex`. - -```graphql -query voucher($outputIndex: Int!) { - voucher(outputIndex: $outputIndex) { - index - input { - index - } - destination - payload - proof { - outputIndex - outputHashesSiblings - } - value - executed - transactionHash - application { - id - name - address - } - } -} -``` - -As always remember to add the variable `$outputIndex` to the variables tab in the bottom-left corner of the playground, like this: - -```graphql -{ - "outputIndex": 0 -} +```bash +curl -X POST http://127.0.0.1:6751/rpc \ + -H "Content-Type: application/json" \ + -d '{ + "jsonrpc": "2.0", + "method": "cartesi_getReport", + "params": { + "application": "multiples-of-a-number", + "report_index": "0x0" + }, + "id": 1 + }' ``` diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/running-an-application.md b/cartesi-rollups_versioned_docs/version-2.0/development/running-an-application.md index fbfbc6ea..d77a0908 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/development/running-an-application.md +++ b/cartesi-rollups_versioned_docs/version-2.0/development/running-an-application.md @@ -67,15 +67,13 @@ This adjustment should align the node's block reading with the blockchain's curr The `cartesi run` command activates several services essential for node operation: some of these services are not activated by default and can be activated by adding them to the `--service` flag when starting your application. The below command starts your application with all available services: ```shell -cartesi run --services explorer,graphql,bundler,paymaster,passkey +cartesi run --services explorer,bundler,paymaster,passkey ``` Each of these services runs independently and can be activated or deactivated at will by simply including or removing them from the list of services while running your application. Below is a breakdown of these services and their default URL's. - **Anvil Chain**: Runs a local blockchain available at `http://localhost:6751/anvil`. -- **GraphQL Playground**: An interactive IDE at `http://localhost:6751/graphql` for exploring the GraphQL server. - - **Explorer**: Monitors Rollups application activity and manages transactions via `http://localhost:6751/explorer`. - **Inspect**: A diagnostic tool accessible at `http://localhost:6751/inspect/` to inspect the node’s state. diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/send-inputs-and-assets.md b/cartesi-rollups_versioned_docs/version-2.0/development/send-inputs-and-assets.md index 89db12e6..5ae39fc9 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/development/send-inputs-and-assets.md +++ b/cartesi-rollups_versioned_docs/version-2.0/development/send-inputs-and-assets.md @@ -315,7 +315,7 @@ for (let i in result.reports) { ``` :::note Interacting with Sample application? -Replacing the above address `0xb48..3A9` with the application address from deploying the sample application in the [creating an application](./creating-an-application.md#implementing-your-application-logic), then executing the inspect command would emit a report which would immediately be logged to the CLI and can also be quarried using the JSON RPC or GraphQl server. +Replacing the above address `0xb48..3A9` with the application address from deploying the sample application in the [creating an application](./creating-an-application.md#implementing-your-application-logic), then executing the inspect command would emit a report which would immediately be logged to the CLI and can also be quarried using the JSON RPC server. ::: ## Depositing Assets diff --git a/cartesi-rollups_versioned_docs/version-2.0/getting-started/architecture.md b/cartesi-rollups_versioned_docs/version-2.0/getting-started/architecture.md index 78c6d3cf..deb74432 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/getting-started/architecture.md +++ b/cartesi-rollups_versioned_docs/version-2.0/getting-started/architecture.md @@ -94,7 +94,7 @@ The off-chain execution layer is centered around the Cartesi Rollups Node, the c 4. Inspection: The node handles requests to inspect the dApp state, facilitating queries without altering the state. -5. Output management: It operates a GraphQL server that allows clients to query the outputs produced by the dApp. +5. Output management: It operates a JSONRPC server that allows clients to query the outputs produced by the dApp. The Cartesi Rollups Node can operate in two primary modes: @@ -106,7 +106,6 @@ The Cartesi Rollups Node can operate in two primary modes: All Cartesi Nodes function as Validator Nodes, with Reader Node functionality under active development. ::: - ## Data Flow and Processes The Cartesi architecture facilitates several key processes that enable the functioning of dApps. These processes include: @@ -125,7 +124,7 @@ The `advance-state` process changes the application state, and it involves the f - The Cartesi Machine processes the input and generates verifiable outputs ([vouchers](../api-reference/backend/vouchers.md), [notices](../api-reference/backend/notices.md), and [reports](../api-reference/backend/reports.md)). -- The application frontend can query these outputs using the node's [GraphQL API](../api-reference/graphql/basics.md). +- The application frontend can query these outputs using the node's [JSON API](../api-reference/jsonrpc/overview.md). ### Inspect state diff --git a/cartesi-rollups_versioned_docs/version-2.0/resources/migration-guide.md b/cartesi-rollups_versioned_docs/version-2.0/resources/migration-guide.md index 0df74ba7..4c5f2704 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/resources/migration-guide.md +++ b/cartesi-rollups_versioned_docs/version-2.0/resources/migration-guide.md @@ -18,7 +18,7 @@ Rollups node v2.0 introduces some major changes in how the node works internally - listens to voucher execution events. See the [Outputs](#outputs) section. - checks if a voucher was executed. See the [Outputs](#outputs) section. - uses inspect calls. See the [Inspect calls](#inspect-calls) section. -- uses GraphQL queries. See the [GraphQL queries](#graphql-queries) section. +- uses JSON-RPC queries. See the [JSON queries](#jsonrpc-queries) section. :::note If your application uses a high-level framework(ex. Deroll, Rollmelette etc.) for either backend or frontend, check if the framework has already implemented the changes described in this guide. diff --git a/cartesi-rollups_versioned_docs/version-2.0/tutorials/calculator.md b/cartesi-rollups_versioned_docs/version-2.0/tutorials/calculator.md index 52a922cc..78620a3e 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/tutorials/calculator.md +++ b/cartesi-rollups_versioned_docs/version-2.0/tutorials/calculator.md @@ -297,37 +297,50 @@ The `cartesi send generic` sends a notice containing a payload to the Rollup Ser Notice payloads will be returned in hexadecimal format; developers will need to decode these to convert them into plain text. ::: -We can query these notices using the GraphQL playground hosted on `http://localhost:8080/graphql` or with a custom frontend client. - -You can retrieve all notices sent to the rollup server with the query: - -```graphql -query notices { - notices { - edges { - node { - index - input { - index - } - payload - } - } - } -} +We can query these notices using the JSON-RPC server running on `http://127.0.0.1:6751/rpc` or with a custom frontend client. + +You can retrieve all notices sent to the rollup server by making running this request on a terminal: + +```bash +curl -X POST http://127.0.0.1:6751/rpc \ + -H "Content-Type: application/json" \ + -d '{ + "jsonrpc": "2.0", + "method": "cartesi_listOutputs", + "params": { + "application": "calculator", + "limit": 10, + "offset": 0 + }, + "id": 1 + }' ``` Alternatively, you can query this on a frontend client: -```js -const response = await fetch("http://localhost:8080/graphql", { +```javascript +const response = await fetch("http://127.0.0.1:6751/rpc", { method: "POST", headers: { "Content-Type": "application/json" }, - body: '{ "query": "{ notices { edges { node { payload } } } }" }', + body: JSON.stringify({ + jsonrpc: "2.0", + method: "cartesi_listOutputs", + params: { + application: "calculator", + limit: 10, + offset: 0 + }, + id: 1 + }) }); + const result = await response.json(); -for (let edge of result.data.notices.edges) { - let payload = edge.node.payload; + +const outputs = result?.result?.data ?? []; + +for (const output of outputs) { + const decoded = output.decoded_data; + console.log("Output payload:", decoded); } ``` diff --git a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json index 788ddaa0..c1e72f65 100644 --- a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json +++ b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json @@ -84,68 +84,6 @@ "api-reference/backend/finish" ] }, - { - "type": "category", - "label": "GraphQL API", - "collapsed": true, - "items": [ - "api-reference/graphql/overview", - { - "type": "category", - "label": "Queries", - "collapsed": true, - "items": [ - "api-reference/graphql/queries/inputs", - "api-reference/graphql/queries/notices", - "api-reference/graphql/queries/reports", - "api-reference/graphql/queries/vouchers", - "api-reference/graphql/queries/delegate-call-vouchers" - ] - }, - { - "type": "category", - "label": "Objects", - "collapsed": true, - "items": [ - { - "type": "autogenerated", - "dirName": "api-reference/graphql/objects" - } - ] - }, - { - "type": "category", - "label": "Filters", - "collapsed": true, - "items": [ - "api-reference/graphql/filters/input-filter", - "api-reference/graphql/filters/application-filter", - "api-reference/graphql/filters/convenient-filter" - ] - }, - { - "type": "category", - "label": "Enums", - "collapsed": true, - "items": [ - [ - "api-reference/graphql/enums/completion-status" - ] - ] - }, - { - "type": "category", - "label": "Scalars", - "collapsed": true, - "items": [ - { - "type": "autogenerated", - "dirName": "api-reference/graphql/scalars" - } - ] - } - ] - }, { "type": "category", "label": "JSON-RPC API", From fa9f53c73ed40e324769180e08a5f1e6600966f2 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Thu, 29 May 2025 10:44:18 +0100 Subject: [PATCH 09/10] docs: Update old tutorials to node V2 standard --- .../version-2.0/tutorials/calculator.md | 36 ++-- .../tutorials/erc-20-token-wallet.md | 169 ++++++++++++++---- .../tutorials/erc-721-token-wallet.md | 68 ++++--- .../version-2.0/tutorials/ether-wallet.md | 109 ++++++----- 4 files changed, 248 insertions(+), 134 deletions(-) diff --git a/cartesi-rollups_versioned_docs/version-2.0/tutorials/calculator.md b/cartesi-rollups_versioned_docs/version-2.0/tutorials/calculator.md index 78620a3e..23fb6411 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/tutorials/calculator.md +++ b/cartesi-rollups_versioned_docs/version-2.0/tutorials/calculator.md @@ -162,7 +162,7 @@ def handle_advance(data): return status ``` -Here is the final code of our application: +Here is the final code of the application: ```python from os import environ @@ -261,27 +261,21 @@ cartesi run ### Sending inputs with the CLI -We can send inputs to our application with a custom JavaScript frontend, Cast, or Cartesi CLI. +We can send inputs to your application with a custom JavaScript frontend, Cast, or Cartesi CLI. -To send generic inputs to our application quickly, run the following: +To send a string encoded input to your application, run the below command: ```shell -cartesi send generic +cartesi send "1+2" ``` Example: Send `1+2` as an input to the application. ```shell -> cartesi send generic -? Chain Foundry -? RPC URL http://127.0.0.1:8545 -? Wallet Mnemonic -? Mnemonic test test test test test test test test test test test junk -? Account 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 9999.970671818064986684 ETH -? Application address 0xab7528bb862fb57e8a2bcd567a2e929a0be56a5e -? Input String encoding -? Input (as string) 1+2 -✔ Input sent: 0xe2a2ba347659e53c53f3089ff3268255842c03bafbbf185375f94c7a78f3f98a +(base) user@user-MacBook-Pro calculator % cartesi send "1 + 2" +(node:64729) 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) +✔ Input sent: 0x1866ab903f8fa5bd712fc2d322b8c0ba119bb31d30885a06e0fe6005ff079ff2 ```