From 12685b83f626bb7af478c63f54e787ab3a20d096 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Thu, 10 Oct 2024 23:32:00 +0100 Subject: [PATCH 01/99] add: implemented a sidebar file for V2 (cherry picked from commit 0799d128e5a0495646f9366984c67bfb9bc68a4f) --- .../version-2.0-sidebars.json | 214 ++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json diff --git a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json new file mode 100644 index 00000000..288c2b9d --- /dev/null +++ b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json @@ -0,0 +1,214 @@ +{ + "rollups": [ + { + "type": "doc", + "id": "overview", + "label": "Overview" + }, + { + "type": "doc", + "id": "quickstart", + "label": "Quickstart" + }, + { + "type": "category", + "label": "Core Concepts", + "collapsed": true, + "items": [ + "core-concepts/optimistic-rollups", + "core-concepts/architecture", + "core-concepts/mainnet-considerations" + ] + }, + { + "type": "category", + "label": "Rollups APIs", + "collapsed": true, + "items": [ + "rollups-apis/http-api", + { + "type": "category", + "label": "Backend APIs", + "collapsed": true, + "items": [ + "rollups-apis/backend/introduction", + "rollups-apis/backend/notices", + "rollups-apis/backend/vouchers", + "rollups-apis/backend/reports" + ] + }, + { + "type": "category", + "label": "Frontend APIs", + "collapsed": true, + "items": [ + { + "type": "category", + "label": "Smart contracts API", + "collapsed": true, + "items": [ + "rollups-apis/json-rpc/overview", + "rollups-apis/json-rpc/input-box", + "rollups-apis/json-rpc/application", + "rollups-apis/json-rpc/application-factory", + { + "type": "category", + "label": "Portals", + "collapsed": true, + "items": [ + "rollups-apis/json-rpc/portals/ERC20Portal", + "rollups-apis/json-rpc/portals/ERC721Portal", + "rollups-apis/json-rpc/portals/ERC1155SinglePortal", + "rollups-apis/json-rpc/portals/ERC1155BatchPortal", + "rollups-apis/json-rpc/portals/EtherPortal" + ] + }, + { + "type": "category", + "label": "Relayer", + "collapsed": true, + "items": [ + "rollups-apis/json-rpc/relays/relays" + ] + } + ] + }, + { + "type": "category", + "label": "GraphQL API", + "collapsed": true, + "items": [ + "rollups-apis/graphql/overview", + { + "type": "category", + "label": "Queries", + "collapsed": true, + "items": [ + "rollups-apis/graphql/queries/inputs", + "rollups-apis/graphql/queries/notices", + "rollups-apis/graphql/queries/vouchers", + "rollups-apis/graphql/queries/reports" + ] + }, + { + "type": "category", + "label": "Objects", + "collapsed": true, + "items": [ + { + "type": "autogenerated", + "dirName": "rollups-apis/graphql/objects" + } + ] + }, + { + "type": "category", + "label": "Filters", + "collapsed": true, + "items": [ + { + "type": "autogenerated", + "dirName": "rollups-apis/graphql/inputs" + } + ] + }, + { + "type": "category", + "label": "Scalars", + "collapsed": true, + "items": [ + { + "type": "autogenerated", + "dirName": "rollups-apis/graphql/scalars" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "category", + "label": "Development", + "collapsed": true, + "items": [ + "development/installation", + "development/building-a-dapp", + "development/cli-commands", + "development/send-inputs", + "development/query-outputs", + "development/asset-handling", + "development/reference", + "development/community-tools" + ] + }, + { + "type": "category", + "label": "Deployment", + "collapsed": true, + "items": [ + "deployment/introduction", + "deployment/self-hosted" + ] + }, + { + "type": "category", + "label": "Tutorials", + "collapsed": true, + "items": [ + "tutorials/calculator", + "tutorials/ether-wallet", + "tutorials/erc-20-token-wallet", + "tutorials/erc-721-token-wallet", + "tutorials/react-frontend-application", + "tutorials/cli-account-abstraction-feauture" + ] + }, + { + "type": "category", + "label": "References", + "collapsed": true, + "items": [ + { + "type": "category", + "label": "Releases", + "items": [ + { + "type": "link", + "label": "Cartesi CLI", + "href": "https://github.com/cartesi/cli/releases" + }, + { + "type": "link", + "label": "Rollups Node", + "href": "https://github.com/cartesi/rollups-node/releases" + }, + { + "type": "link", + "label": "Rollups Contracts", + "href": "https://github.com/cartesi/rollups-contracts/releases" + } + ] + }, + { + "type": "category", + "label": "Changelog", + "items": [ + { + "type": "link", + "label": "Rollups Node", + "href": "https://github.com/cartesi/rollups-node/blob/main/CHANGELOG.md" + }, + { + "type": "link", + "label": "Rollups Contracts", + "href": "https://github.com/cartesi/rollups-contracts/blob/main/CHANGELOG.md" + } + ] + } + ] + } + ] +} \ No newline at end of file From c22f4bd53adcd4131741df95d24a5f0b41951d13 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Thu, 10 Oct 2024 23:34:01 +0100 Subject: [PATCH 02/99] add: Added the Account abstraction tutorial to V2 (cherry picked from commit 67b2e46236c698945d69d97ed92e63cef84b3e70) --- .../tutorials/utilizing-the-cli-AA-feature.md | 438 ++++++++++++++++++ 1 file changed, 438 insertions(+) create mode 100644 cartesi-rollups_versioned_docs/version-2.0/tutorials/utilizing-the-cli-AA-feature.md 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 new file mode 100644 index 00000000..3c3ad254 --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-2.0/tutorials/utilizing-the-cli-AA-feature.md @@ -0,0 +1,438 @@ +--- +id: cli-account-abstraction-feauture +title: Utilizing the account abstraction (AA) feature of the CLI +resources: + - url: https://github.com/tuler/aa-examples + title: AA Examples +--- + +This tutorial will guide you through utilizing the account abstraction infrastructure of the CLI to interact with your dApp 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. + +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. + +## 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. + +![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`. + + +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. + +## 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 + +```shell + npm create wagmi@latest +``` + +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: + +```shell + npm i permissionless@0.1.43 encoding @alchemy/aa-core @alchemy/aa-accounts @cartesi/rollups +``` + +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. + +### 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; + } + } + +``` + +### Set up an Alchemy 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. + +```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; + }; + + 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 [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, + }; + }; + +``` + +:::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 +::: + +### 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`. + +```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]; + }; + + 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 }; + }; + +``` + +### 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: + +```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), + ], + }); + + 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} +
+ + ); + } + + export default App; +``` + +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. + +## Setting up and running a Cartesi dApp backend on localhost + +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:. + +- 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`. + + +## 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. + +## 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. + From b712b1c117c3b6ab350506f06582b597a7485416 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Thu, 10 Oct 2024 23:37:17 +0100 Subject: [PATCH 03/99] update: Modified the Development folder for V2 (cherry picked from commit 92b1feee91993bda5db1148dced9616e85d78a45) --- .../development/building-a-dapp.md | 163 ++++++++++++++++++ .../development/building-the-application.md | 53 ------ .../version-2.0/development/cli-commands.md | 6 +- .../development/creating-application.md | 35 ---- .../{retrieve-outputs.md => query-outputs.md} | 4 +- .../{migration.md => reference.md} | 4 +- .../development/running-the-application.md | 75 -------- .../{send-requests.md => send-inputs.md} | 4 +- 8 files changed, 174 insertions(+), 170 deletions(-) create mode 100644 cartesi-rollups_versioned_docs/version-2.0/development/building-a-dapp.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/development/building-the-application.md delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/development/creating-application.md rename cartesi-rollups_versioned_docs/version-2.0/development/{retrieve-outputs.md => query-outputs.md} (99%) rename cartesi-rollups_versioned_docs/version-2.0/development/{migration.md => reference.md} (99%) delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/development/running-the-application.md rename cartesi-rollups_versioned_docs/version-2.0/development/{send-requests.md => send-inputs.md} (99%) diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/building-a-dapp.md b/cartesi-rollups_versioned_docs/version-2.0/development/building-a-dapp.md new file mode 100644 index 00000000..226f1523 --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-2.0/development/building-a-dapp.md @@ -0,0 +1,163 @@ +--- +id: building-a-dapp +title: Building a dApp +resources: + - url: https://github.com/Calindra/nonodo + title: NoNodo + - url: https://cartesiscan.io/ + title: CartesiScan +--- + + +## Creating the application +Cartesi CLI simplifies creating dApps on Cartesi. To create a new application, run: + +```shell +cartesi create --template +``` + +For example, create a Python project. + +```shell +cartesi create new-dapp --template python +``` + +This command creates a `new-dapp` directory with essential files for your dApp development. + +- `Dockerfile`: Contains configurations to build a complete Cartesi machine with your app's dependencies. Your backend code will run in this environment. + +- `README.md`: A markdown file with basic information and instructions about your dApp. + +- `dapp.py`: A Python file with template backend code that serves as your application's entry point. + +- `requirements.txt`: Lists the Python dependencies required for your application. + +Cartesi CLI has templates for the following languages – `cpp`, `cpp-low-level`, `go`, `javascript`, `lua`, `python`, `ruby`, `rust`, and `typescript`. + +After creating your application, you can start building your dApp by adding your logic to the `dapp.py` file. + + +:::note Building with Go? +For Go applications on Cartesi, we recommend using [Rollmelette](https://github.com/rollmelette/rollmelette). It’s a high-level Go framework and an alternative template that simplifies development and enhances input management, providing a smoother and more efficient experience. +::: + + +## Building the application + +“Building” in this context compiles your application into RISC-V architecture and consequently builds a Cartesi machine containing your application. This architecture enables computation done by your application to be reproducible and verifiable. + +With the Docker engine running, change the directory to your application and build by running: + +```shell +cartesi build +``` + +The successful execution of this step will log this in your terminal: + +```shell + . + / \ + / \ +\---/---\ /----\ + \ X \ + \----/ \---/---\ + \ / CARTESI + \ / MACHINE + ' + +[INFO rollup_http_server] starting http dispatcher service... +[INFO rollup_http_server::http_service] starting http dispatcher http service! +[INFO actix_server::builder] starting 1 workers +[INFO actix_server::server] Actix runtime found; starting in Actix runtime +[INFO rollup_http_server::dapp_process] starting dapp +INFO:__main__:HTTP rollup_server url is http://127.0.0.1:5004 +INFO:__main__:Sending finish + +Manual yield rx-accepted (0x100000000 data) +Cycles: 2767791744 +2767791744: b740d27cf75b6cb10b1ab18ebd96be445ca8011143d94d8573221342108822f5 +Storing machine: please wait +Successfully copied 288MB to /Users/michaelasiedu/Code/calculator/python/.cartesi/image +``` +### Memory + +To change the default memory size for the Cartesi Machine, you can personalize it by adding a specific label in your Dockerfile. + +The line below lets you define the memory size in megabytes (MB): + +```dockerfile +LABEL io.cartesi.rollups.ram_size=128Mi +``` + +:::note environment variables +You can create a `.cartesi.env` in the project's root and override any variable controlling the rollups-node. +::: + + +## Running the Application + +Running your application starts your backend on port `8080` and local Anvil node on port `8545`. + +In essence, the node also logs all outputs received by your backend. + +Here are the prerequisites to run the node: + +- Docker Engine must be active. +- Cartesi machine snapshot successfully built with `cartesi build`. + +To start the node, run: + +```shell +cartesi run +``` + +This command runs your backend compiled to RISC-V and packages it as a Cartesi machine. + +:::troubleshoot troubleshooting common errors + +#### Error: Depth Too High + +```shell +Attaching to 2bd74695-prompt-1, 2bd74695-validator-1 +2bd74695-validator-1 | Error: DepthTooHigh { depth: 2, latest: 1 } +2bd74695-validator-1 | Error: DepthTooHigh { depth: 2, latest: 1 } +``` + +This indicates that the node is reading blocks too far behind the current blockchain state. + +#### Solution + +Create or modify a `.cartesi.env` file in your project directory and set: + +```shell +TX_DEFAULT_CONFIRMATIONS=1 +``` + +This adjustment should align the node's block reading with the blockchain's current state. + +::: + +### Overview of Node Services + +The `cartesi run` command activates several services essential for node operation: + +- **Anvil Chain**: Runs a local blockchain available at `http://localhost:8545`. + +- **GraphQL Playground**: An interactive IDE at `http://localhost:8080/graphql` for exploring the GraphQL server. + +- **Blockchain Explorer**: Monitors node activity and manages transactions via `http://localhost:8080/explorer/`. + +- **Inspect**: A diagnostic tool accessible at `http://localhost:8080/inspect/` to inspect the node’s state. + + +### CartesiScan + +[CartesiScan](https://cartesiscan.io/) is a valuable tool for developers and users alike, offering a comprehensive overview of Cartesi Rollups applications and their interactions with the blockchain. + +Additionally, it provides expandable data regarding outputs, encompassing notices, vouchers, and reports. + +When you run your application with `cartesi run` , there is a local instance of CartesiScan on `http://localhost:8080/explorer`. + +:::note Testing tools +[NoNodo](https://github.com/Calindra/nonodo) is a Cartesi Rollups testing tool that works with host machine applications, eliminating the need for Docker or RISC-V compilation. +::: diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/building-the-application.md b/cartesi-rollups_versioned_docs/version-2.0/development/building-the-application.md deleted file mode 100644 index fbac5190..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/development/building-the-application.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -id: building-the-application -title: Building the application ---- - -“Building” in this context compiles your application into RISC-V architecture and consequently builds a Cartesi machine containing your application. This architecture enables computation done by your application to be reproducible and verifiable. - -With the Docker engine running, change the directory to your application and build by running: - -```shell -cartesi build -``` - -The successful execution of this step will log this in your terminal: - -```shell - . - / \ - / \ -\---/---\ /----\ - \ X \ - \----/ \---/---\ - \ / CARTESI - \ / MACHINE - ' - -[INFO rollup_http_server] starting http dispatcher service... -[INFO rollup_http_server::http_service] starting http dispatcher http service! -[INFO actix_server::builder] starting 1 workers -[INFO actix_server::server] Actix runtime found; starting in Actix runtime -[INFO rollup_http_server::dapp_process] starting dapp -INFO:__main__:HTTP rollup_server url is http://127.0.0.1:5004 -INFO:__main__:Sending finish - -Manual yield rx-accepted (0x100000000 data) -Cycles: 2767791744 -2767791744: b740d27cf75b6cb10b1ab18ebd96be445ca8011143d94d8573221342108822f5 -Storing machine: please wait -Successfully copied 288MB to /Users/michaelasiedu/Code/calculator/python/.cartesi/image -``` -## Memory - -To change the default memory size for the Cartesi Machine, you can personalize it by adding a specific label in your Dockerfile. - -The line below lets you define the memory size in megabytes (MB): - -```dockerfile -LABEL io.cartesi.rollups.ram_size=128Mi -``` - -:::note environment variables -You can create a `.cartesi.env` in the project's root and override any variable controlling the rollups-node. -::: \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/cli-commands.md b/cartesi-rollups_versioned_docs/version-2.0/development/cli-commands.md index fe3f5a70..6d92fdaa 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/development/cli-commands.md +++ b/cartesi-rollups_versioned_docs/version-2.0/development/cli-commands.md @@ -78,10 +78,14 @@ cartesi run [--block-time ] [--epoch-length ] [--no-backend] [-v] #### Flags: - `--block-time=`: Interval between blocks in seconds (default: 5). -- `--epoch-length=`: length of an epoch in blocks (default: 720). +- `--epoch-length=`: length of an epoch in blocks (default: 720). - `--no-backend`: Run a node without the application code. - `-v`, `--verbose`: Run node with detailed container logs. - `--listen-port=`: Port to listen for incoming connections (default: 8080). +- `--dry-run`: Shows the docker compose configuration. +- `--cpus=`: Define the number of CPUs (eg.: 1) for the rollups-node (default is not limited). +- `--memory=`: Define the amount of memory (eg.: 1024) for the rollups-node in MB (default is not limited). + --- diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/creating-application.md b/cartesi-rollups_versioned_docs/version-2.0/development/creating-application.md deleted file mode 100644 index aa2c4aad..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/development/creating-application.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -id: creating-application -title: Creating an application ---- - -Cartesi CLI simplifies creating dApps on Cartesi. To create a new application, run: - -```shell -cartesi create --template -``` - -For example, create a Python project. - -```shell -cartesi create new-dapp --template python -``` - -This command creates a `new-dapp` directory with essential files for your dApp development. - -- `Dockerfile`: Contains configurations to build a complete Cartesi machine with your app's dependencies. Your backend code will run in this environment. - -- `README.md`: A markdown file with basic information and instructions about your dApp. - -- `dapp.py`: A Python file with template backend code that serves as your application's entry point. - -- `requirements.txt`: Lists the Python dependencies required for your application. - -Cartesi CLI has templates for the following languages – `cpp`, `cpp-low-level`, `go`, `javascript`, `lua`, `python`, `ruby`, `rust`, and `typescript`. - -After creating your application, you can start building your dApp by adding your logic to the `dapp.py` file. - - -:::note Building with Go? -For Go applications on Cartesi, we recommend using [Rollmelette](https://github.com/rollmelette/rollmelette). It’s a high-level Go framework and an alternative template that simplifies development and enhances input management, providing a smoother and more efficient experience. -::: \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/retrieve-outputs.md b/cartesi-rollups_versioned_docs/version-2.0/development/query-outputs.md similarity index 99% rename from cartesi-rollups_versioned_docs/version-2.0/development/retrieve-outputs.md rename to cartesi-rollups_versioned_docs/version-2.0/development/query-outputs.md index 60a90876..28eb2736 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/development/retrieve-outputs.md +++ b/cartesi-rollups_versioned_docs/version-2.0/development/query-outputs.md @@ -1,6 +1,6 @@ --- -id: retrieve-outputs -title: Retrieve outputs +id: query-outputs +title: Query outputs resources: - url: https://www.udemy.com/course/cartesi-masterclass/ title: The Cartesi dApp Developer Free Course diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/migration.md b/cartesi-rollups_versioned_docs/version-2.0/development/reference.md similarity index 99% rename from cartesi-rollups_versioned_docs/version-2.0/development/migration.md rename to cartesi-rollups_versioned_docs/version-2.0/development/reference.md index 9c9e09f4..587555f4 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/development/migration.md +++ b/cartesi-rollups_versioned_docs/version-2.0/development/reference.md @@ -1,6 +1,6 @@ --- -id: migration -title: Migration guide +id: reference +title: Reference --- ## Migrating from Cartesi Rollups Node v1.4 to v1.5.x diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/running-the-application.md b/cartesi-rollups_versioned_docs/version-2.0/development/running-the-application.md deleted file mode 100644 index cef98dd7..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/development/running-the-application.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -id: running-the-application -title: Running the application -resources: - - url: https://github.com/Calindra/nonodo - title: NoNodo - - url: https://cartesiscan.io/ - title: CartesiScan ---- - -Running your application starts your backend on port `8080` and local Anvil node on port `8545`. - -In essence, the node also logs all outputs received by your backend. - -Here are the prerequisites to run the node: - -- Docker Engine must be active. -- Cartesi machine snapshot successfully built with `cartesi build`. - -To start the node, run: - -```shell -cartesi run -``` - -This command runs your backend compiled to RISC-V and packages it as a Cartesi machine. - -:::troubleshoot troubleshooting common errors - -#### Error: Depth Too High - -```shell -Attaching to 2bd74695-prompt-1, 2bd74695-validator-1 -2bd74695-validator-1 | Error: DepthTooHigh { depth: 2, latest: 1 } -2bd74695-validator-1 | Error: DepthTooHigh { depth: 2, latest: 1 } -``` - -This indicates that the node is reading blocks too far behind the current blockchain state. - -#### Solution - -Create or modify a `.cartesi.env` file in your project directory and set: - -```shell -TX_DEFAULT_CONFIRMATIONS=1 -``` - -This adjustment should align the node's block reading with the blockchain's current state. - -::: - -### Overview of Node Services - -The `cartesi run` command activates several services essential for node operation: - -- **Anvil Chain**: Runs a local blockchain available at `http://localhost:8545`. - -- **GraphQL Playground**: An interactive IDE at `http://localhost:8080/graphql` for exploring the GraphQL server. - -- **Blockchain Explorer**: Monitors node activity and manages transactions via `http://localhost:8080/explorer/`. - -- **Inspect**: A diagnostic tool accessible at `http://localhost:8080/inspect/` to inspect the node’s state. - - -## CartesiScan - -[CartesiScan](https://cartesiscan.io/) is a valuable tool for developers and users alike, offering a comprehensive overview of Cartesi Rollups applications and their interactions with the blockchain. - -Additionally, it provides expandable data regarding outputs, encompassing notices, vouchers, and reports. - -When you run your application with `cartesi run` , there is a local instance of CartesiScan on `http://localhost:8080/explorer`. - -:::note Testing tools -[NoNodo](https://github.com/Calindra/nonodo) is a Cartesi Rollups testing tool that works with host machine applications, eliminating the need for Docker or RISC-V compilation. -::: diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/send-requests.md b/cartesi-rollups_versioned_docs/version-2.0/development/send-inputs.md similarity index 99% rename from cartesi-rollups_versioned_docs/version-2.0/development/send-requests.md rename to cartesi-rollups_versioned_docs/version-2.0/development/send-inputs.md index 525b33b9..0ee05c14 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/development/send-requests.md +++ b/cartesi-rollups_versioned_docs/version-2.0/development/send-inputs.md @@ -1,6 +1,6 @@ --- -id: send-requests -title: Send requests +id: send-inputs +title: Send inputs resources: - url: https://github.com/prototyp3-dev/frontend-web-cartesi title: React.js + Typescript template From a97dcbbcd583b1edc3d62266298d406ee3db4b46 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Fri, 11 Oct 2024 17:22:07 +0100 Subject: [PATCH 04/99] fix: Handled error relating to broken links (cherry picked from commit abb0e96d5a0a271a68ffbfc1811be5f4d0941735) --- .../version-2.0/index.md | 15 +-- .../rollups-apis/backend/notices.md | 3 +- .../rollups-apis/backend/reports.md | 8 +- .../version-2.0/tutorials/calculator.md | 5 +- .../tutorials/erc-20-token-wallet.md | 71 +++++++----- .../tutorials/erc-721-token-wallet.md | 108 +++++++++--------- .../version-2.0/tutorials/ether-wallet.md | 72 +++++------- cartesi-rollups_versions.json | 1 + 8 files changed, 136 insertions(+), 147 deletions(-) diff --git a/cartesi-rollups_versioned_docs/version-2.0/index.md b/cartesi-rollups_versioned_docs/version-2.0/index.md index c0623f83..b02aa267 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/index.md +++ b/cartesi-rollups_versioned_docs/version-2.0/index.md @@ -8,24 +8,21 @@ resources: title: Application-Specific Rollups --- - Welcome to Cartesi Rollups, where decentralized application development meets unprecedented flexibility. With a foundation built on the Linux operating system, Cartesi Rollups offers modular stacks that allow developers to tailor consensus, data availability, and settlement layers according to their project requirements. Utilizing the Cartesi Machine for transaction processing, developers can effortlessly implement sophisticated logic using their preferred programming language or tool. Explore the possibilities and streamline your decentralized application development journey with Cartesi Rollups. ![img](../../static/img/v1.3/image.png) - -## Introduction +## Introduction Let's delve into the workings of a Cartesi Rollup at a high level. ![img](../../static/img/v1.3/overview.jpg) +At its core, the Cartesi Rollup executes the Cartesi Machine - a robust RISCV deterministic emulator running Linux OS - fueled by ordered inputs and custom application code. Inputs sourced from the data availability layer are read by the Cartesi Node, inside of which the Cartesi Machine processes them and generates outputs. After the optimistic rollup dispute window passes, these outputs are verifiable and possibly executable on the settlement layer. -At its core, the Cartesi Rollup executes the Cartesi Machine - a robust RISCV deterministic emulator running Linux OS - fueled by ordered inputs and custom application code. Inputs sourced from the data availability layer are read by the Cartesi Node, inside of which the Cartesi Machine processes them and generates outputs. After the optimistic rollup dispute window passes, these outputs are verifiable and possibly executable on the settlement layer. - -The Cartesi Rollup framework is application-specific, assigning each dApp its rollup app chain and CPU while linking its optimistic rollups' consensus directly to the base layer. This structure ensures that validators—whether permissioned or not—can leverage the security features of the base layer, allowing any honest validator to enforce correct outcomes independently. +The Cartesi Rollup framework is application-specific, assigning each dApp its rollup app chain and CPU while linking its optimistic rollups' consensus directly to the base layer. This structure ensures that validators—whether permissioned or not—can leverage the security features of the base layer, allowing any honest validator to enforce correct outcomes independently. In its simplest form, the Cartesi framework integrates tightly with the base layer, which serves as the sole platform for data availability, consensus, and settlement. Transactions are directed to specific smart contracts where DApp code is executed to produce outputs on the base layer after a verification period. @@ -59,10 +56,8 @@ let finish = { status: "accept" }; Cartesi dApps are implemented as infinite loops that manage their transaction cycles through HTTP POST requests to the `/finish` endpoint to ensure flexibility across different programming languages and stacks. You can learn more about this abstraction [here](./rollups-apis/backend/introduction.md). -In the Cartesi Rollup framework, all inputs sent to the base layer trigger an "advance_state" [request](./development/send-requests.md#initiate-an-advance-request), which alters the state of the Cartesi Machine and consequently the Rollup. Since inputs originate on-chain, they are hex-encoded following the EVM message standard. +In the Cartesi Rollup framework, all inputs sent to the base layer trigger an "advance_state" [request](./development/send-inputs.md#initiate-an-advance-request), which alters the state of the Cartesi Machine and consequently the Rollup. Since inputs originate on-chain, they are hex-encoded following the EVM message standard. Notices can be understood as "provable" events; as such, they can be sent to an EVM chain to be verified, so they are also hex-encoded. - -Finally, in this example, no errors are treated; thus, we end the cycle accepting the input (`status: accept`). However, suppose any input causes the application to enter a faulty state or violates business logic. In that case, the application can end the process by calling the endpoint `/finish` with `status: reject` to revert the machine’s (and rollup’s) state before the arrival of the current input. - +Finally, in this example, no errors are treated; thus, we end the cycle accepting the input (`status: accept`). However, suppose any input causes the application to enter a faulty state or violates business logic. In that case, the application can end the process by calling the endpoint `/finish` with `status: reject` to revert the machine’s (and rollup’s) state before the arrival of the current input. diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/backend/notices.md b/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/backend/notices.md index 9bd5eaad..90dffa4b 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/backend/notices.md +++ b/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/backend/notices.md @@ -1,7 +1,6 @@ --- id: notices title: Notices - --- A notice is a verifiable data declaration that attests to off-chain events or conditions and is accompanied by proof. @@ -78,5 +77,5 @@ def handle_advance(data): :::note querying notices -Frontend clients can query notices using a GraphQL API exposed by the Cartesi Nodes. [Refer to the documentation here](../../development/retrieve-outputs.md/#query-all-reports) to query notices from the rollup server. +Frontend clients can query notices using a GraphQL API exposed by the Cartesi Nodes. [Refer to the documentation here](../../development/query-outputs.md/#query-all-reports) to query notices from the rollup server. ::: diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/backend/reports.md b/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/backend/reports.md index 112e43b2..5429ac8d 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/backend/reports.md +++ b/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/backend/reports.md @@ -3,15 +3,14 @@ id: reports title: Reports --- -Reports are stateless logs, offering a means to record read-only information without changing the state. Primarily used for logging and diagnostic purposes, reports provide valuable insights into the operation and performance of a dApp. +Reports are stateless logs, offering a means to record read-only information without changing the state. Primarily used for logging and diagnostic purposes, reports provide valuable insights into the operation and performance of a dApp. -Unlike notices, reports lack any association with proof and are therefore unsuitable for facilitating trustless interactions, such as on-chain processing or convincing independent third parties of dApp outcomes. +Unlike notices, reports lack any association with proof and are therefore unsuitable for facilitating trustless interactions, such as on-chain processing or convincing independent third parties of dApp outcomes. Consider a scenario within a financial dApp where users conduct transactions. In the event of a processing error, such as a failed transaction or insufficient funds, the dApp may generate a report detailing the encountered issue. This report, encapsulating relevant diagnostic data, aids developers in identifying and resolving underlying problems promptly. Here is how you can write your application to send reports to the rollup server: - import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; @@ -78,7 +77,6 @@ def handle_advance(data): - :::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/retrieve-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/tutorials/calculator.md b/cartesi-rollups_versioned_docs/version-2.0/tutorials/calculator.md index 74cd69b5..52a922cc 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/tutorials/calculator.md +++ b/cartesi-rollups_versioned_docs/version-2.0/tutorials/calculator.md @@ -251,7 +251,6 @@ cartesi build “Building” in this context installs the libraries in the `requirements.txt`, compiles your application into RISC-V architecture, and consequently builds a Cartesi machine that contains your backend application. - The anvil node can now run your application. To run your application, enter the command: @@ -332,8 +331,6 @@ for (let edge of result.data.notices.edges) { } ``` - - -You can also [query a notice based on its input index](../development/retrieve-outputs.md/#query-a-single-notice). +You can also [query a notice based on its input index](../development/query-outputs.md/#query-a-single-notice). Congratulations, you have successfully built a dApp on Cartesi Rollups! diff --git a/cartesi-rollups_versioned_docs/version-2.0/tutorials/erc-20-token-wallet.md b/cartesi-rollups_versioned_docs/version-2.0/tutorials/erc-20-token-wallet.md index 44948983..dfac251b 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/tutorials/erc-20-token-wallet.md +++ b/cartesi-rollups_versioned_docs/version-2.0/tutorials/erc-20-token-wallet.md @@ -13,8 +13,8 @@ This tutorial is for educational purposes. For production dApps, we recommend us ::: ## Setting up the project -First, set up your Cartesi project as described in the [Ether wallet tutorial](./ether-wallet.md/#setting-up-the-project). Make sure you have the necessary dependencies installed. +First, set up your Cartesi project as described in the [Ether wallet tutorial](./ether-wallet.md/#setting-up-the-project). Make sure you have the necessary dependencies installed. ## Building the ERC20 wallet @@ -42,30 +42,43 @@ export class Balance { increaseErc20Balance(erc20: Address, amount: bigint): void { if (amount < 0n) { - throw new Error(`Failed to increase balance of ${erc20} for ${this.account}`); + throw new Error( + `Failed to increase balance of ${erc20} for ${this.account}` + ); } try { if (this.erc20Balances.get(erc20) === undefined) { this.erc20Balances.set(erc20, 0n); } - this.erc20Balances.set(erc20, (this.erc20Balances.get(erc20) || 0n) + amount); + this.erc20Balances.set( + erc20, + (this.erc20Balances.get(erc20) || 0n) + amount + ); console.log("ERC20 balance is", this.erc20Balances); } catch (e) { - throw new Error(`Failed to increase balance of ${erc20} for ${this.account}: ${e}`); + throw new Error( + `Failed to increase balance of ${erc20} for ${this.account}: ${e}` + ); } } decreaseErc20Balance(erc20: Address, amount: bigint): void { if (amount < 0n) { - throw new Error(`Failed to decrease balance of ${erc20} for ${this.account}: invalid amount specified`); + throw new Error( + `Failed to decrease balance of ${erc20} for ${this.account}: invalid amount specified` + ); } if (this.erc20Balances.get(erc20) === undefined) { this.erc20Balances.set(erc20, 0n); - throw new Error(`Failed to decrease balance of ${erc20} for ${this.account}: not found with ERC20 balance`); + throw new Error( + `Failed to decrease balance of ${erc20} for ${this.account}: not found with ERC20 balance` + ); } let erc20Balance = this.erc20Balances.get(erc20) || 0n; if (erc20Balance < amount) { - throw new Error(`Failed to decrease balance of ${erc20} for ${this.account}: insufficient ERC20 balance`); + throw new Error( + `Failed to decrease balance of ${erc20} for ${this.account}: insufficient ERC20 balance` + ); } this.erc20Balances.set(erc20, erc20Balance - amount); } @@ -178,7 +191,7 @@ export class Wallet { destination: erc20, payload: call, }); - + return { destination: erc20, payload: call, @@ -217,7 +230,6 @@ export class Wallet { } }; } - ``` ## Using the ERC20 wallet @@ -287,7 +299,7 @@ const handleAdvance: AdvanceRequestHandler = async (data) => { getAddress(erc20 as Address), BigInt(amount) ); - + await createVoucher(voucher); } else { console.log("Unknown operation"); @@ -306,10 +318,12 @@ const handleInspect: InspectRequestHandler = async (data) => { try { const payloadString = hexToString(data.payload); - const [address, erc20] = payloadString.split('/'); - - const balance = wallet.getAccountBalance(address as Address, erc20 as Address); + const [address, erc20] = payloadString.split("/"); + const balance = wallet.getAccountBalance( + address as Address, + erc20 as Address + ); if (balance === undefined) { throw new Error("ERC20 balance is undefined"); @@ -386,10 +400,8 @@ main().catch((e) => { console.log(e); process.exit(1); }); - ``` - Here is a breakdown of the wallet functionality: - We handle deposits when the sender is the `ERC20Portal`. @@ -398,14 +410,13 @@ Here is a breakdown of the wallet functionality: - For `transfers`, we call `wallet.transferErc20` and create a notice with the parsed parameters. -- For `withdrawals`, we call `wallet.withdrawErc20` and create voucher using the dApp dress and the parsed parameters. +- For `withdrawals`, we call `wallet.withdrawErc20` and create voucher using the dApp dress and the parsed parameters. - We created helper functions to `createNotice` for deposits and transfers, `createReport` for balance checks and `createVoucher` for withdrawals. - ## Build and run the application -With Docker running, [build your backend application](../development/building-the-application.md) by running: +With Docker running, [build your backend application](../development/building-a-dapp.md) by running: ```shell cartesi build @@ -420,13 +431,13 @@ cartesi run #### Deposits :::caution token approvals - An approval step is needed for the [**ERC20 token standard**](https://ethereum.org/en/developers/docs/standards/tokens/). This ensures you grant explicit permission for `ERC20Portal` to transfer tokens on your behalf. - - Without this approval, the `ERC20Portal` cannot deposit your tokens to the Cartesi backend. +An approval step is needed for the [**ERC20 token standard**](https://ethereum.org/en/developers/docs/standards/tokens/). This ensures you grant explicit permission for `ERC20Portal` to transfer tokens on your behalf. - You will encounter this error if you don't approve the `ERC20Portal` address before deposits: +Without this approval, the `ERC20Portal` cannot deposit your tokens to the Cartesi backend. - `ContractFunctionExecutionError: The contract function "depositERC20Tokens" reverted with the following reason: ERC20: insufficient allowance` +You will encounter this error if you don't approve the `ERC20Portal` address before deposits: + +`ContractFunctionExecutionError: The contract function "depositERC20Tokens" reverted with the following reason: ERC20: insufficient allowance` ::: To deposit ERC20 tokens, use the `cartesi send erc20` command and follow the prompts. @@ -439,20 +450,18 @@ To inspect the balance, make an HTTP call to: http://localhost:8080/inspect/{address}/{tokenAddress} ``` - #### Transfers and Withdrawals Use the `cartesi send generic` command and follow the prompts. Here are sample payloads: 1. For transfers: - ```js - {"operation":"transfer","erc20":"0xTokenAddress","from":"0xFromAddress","to":"0xToAddress","amount":"1000000000000000000"} - ``` +```js +{"operation":"transfer","erc20":"0xTokenAddress","from":"0xFromAddress","to":"0xToAddress","amount":"1000000000000000000"} +``` 2. For withdrawals: - ```js - {"operation":"withdraw","erc20":"0xTokenAddress","from":"0xFromAddress","amount":"1000000000000000000"} - ``` - +```js +{"operation":"withdraw","erc20":"0xTokenAddress","from":"0xFromAddress","amount":"1000000000000000000"} +``` diff --git a/cartesi-rollups_versioned_docs/version-2.0/tutorials/erc-721-token-wallet.md b/cartesi-rollups_versioned_docs/version-2.0/tutorials/erc-721-token-wallet.md index 817db401..018b69c1 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/tutorials/erc-721-token-wallet.md +++ b/cartesi-rollups_versioned_docs/version-2.0/tutorials/erc-721-token-wallet.md @@ -13,8 +13,8 @@ This tutorial is for educational purposes. For production dApps, we recommend us ::: ## Setting up the project -First, set up your Cartesi project as described in the [Ether wallet tutorial](./ether-wallet.md/#setting-up-the-project). Make sure you have the necessary dependencies installed. +First, set up your Cartesi project as described in the [Ether wallet tutorial](./ether-wallet.md/#setting-up-the-project). Make sure you have the necessary dependencies installed. ## Building the ERC721 wallet @@ -48,17 +48,23 @@ export class Balance { if (tokens) { tokens.add(tokenId); } else { - throw new Error(`Failed to add token ${erc721}, id:${tokenId} for ${this.account}`); + throw new Error( + `Failed to add token ${erc721}, id:${tokenId} for ${this.account}` + ); } } removeErc721Token(erc721: Address, tokenId: number): void { if (!this.erc721Tokens.has(erc721)) { - throw new Error(`Failed to remove token ${erc721}, id:${tokenId} from ${this.account}: Collection not found`); + throw new Error( + `Failed to remove token ${erc721}, id:${tokenId} from ${this.account}: Collection not found` + ); } const tokens = this.erc721Tokens.get(erc721); if (!tokens?.delete(tokenId)) { - throw new Error(`Failed to remove token ${erc721}, id:${tokenId} from ${this.account}: Token not found`); + throw new Error( + `Failed to remove token ${erc721}, id:${tokenId} from ${this.account}: Token not found` + ); } } } @@ -80,32 +86,41 @@ export class Wallet { private accounts: Map = new Map(); private getOrCreateBalance(address: Address): Balance { - let balance = this.accounts.get(address); - if (!balance) { - balance = new Balance(address, new Map()); - this.accounts.set(address, balance); + let balance = this.accounts.get(address); + if (!balance) { + balance = new Balance(address, new Map()); + this.accounts.set(address, balance); + } + return balance; } - return balance; - } - - getBalance(address: Address): Balance { - return this.getOrCreateBalance(address); - } - getErc721Balance(address: Address, erc721: Address): { address: string; erc721: string; tokenIds: number[] } { - const balance = this.getOrCreateBalance(address); - const tokens = balance.getErc721Tokens(erc721) || new Set();; - const tokenIdsArray = Array.from(tokens); - - const result = { - address: address, - erc721: erc721, - tokenIds: tokenIdsArray - }; - - console.info(`ERC721 balance for ${address} and contract ${erc721}: ${JSON.stringify(result, null, 2)}`); - return result; - } + getBalance(address: Address): Balance { + return this.getOrCreateBalance(address); + } + + getErc721Balance( + address: Address, + erc721: Address + ): { address: string; erc721: string; tokenIds: number[] } { + const balance = this.getOrCreateBalance(address); + const tokens = balance.getErc721Tokens(erc721) || new Set(); + const tokenIdsArray = Array.from(tokens); + + const result = { + address: address, + erc721: erc721, + tokenIds: tokenIdsArray, + }; + + console.info( + `ERC721 balance for ${address} and contract ${erc721}: ${JSON.stringify( + result, + null, + 2 + )}` + ); + return result; + } processErc721Deposit(payload: string): string { try { @@ -201,8 +216,8 @@ export class Wallet { } } } - ``` + ## Using the wallet Now, let's create a simple wallet app at the entry point `src/index.ts` to test the wallet’s functionality. @@ -375,7 +390,6 @@ main().catch((e) => { console.log(e); process.exit(1); }); - ``` Here is a breakdown of the wallet functionality: @@ -388,14 +402,13 @@ Here is a breakdown of the wallet functionality: - For `transfers`, we call `wallet.transferErc721` and create a notice with the parsed parameters. -- For `withdrawals`, we call `wallet.withdrawErc721` and create voucher using the dApp dress and the parsed parameters. +- For `withdrawals`, we call `wallet.withdrawErc721` and create voucher using the dApp dress and the parsed parameters. - We created helper functions to `createNotice` for deposits and transfers, `createReport` for balance checks and `createVoucher` for withdrawals. - ## Build and run the application -With Docker running, [build your backend application](../development/building-the-application.md) by running: +With Docker running, [build your backend application](../development/building-a-dapp.md) by running: ```shell cartesi build @@ -407,17 +420,16 @@ To run your application, enter the command: cartesi run ``` - #### Deposits :::caution token approvals - An approval step is needed for the [**ERC721 token standard**](https://ethereum.org/en/developers/docs/standards/tokens/). This ensures you grant explicit permission for `ERC721Portal` to transfer tokens on your behalf. - - Without this approval, the `ERC721Portal` cannot deposit your tokens to the Cartesi backend. +An approval step is needed for the [**ERC721 token standard**](https://ethereum.org/en/developers/docs/standards/tokens/). This ensures you grant explicit permission for `ERC721Portal` to transfer tokens on your behalf. + +Without this approval, the `ERC721Portal` cannot deposit your tokens to the Cartesi backend. - You will encounter this error if you don't approve the `ERC20Portal` address before deposits: +You will encounter this error if you don't approve the `ERC20Portal` address before deposits: - `ContractFunctionExecutionError: The contract function "depositERC721Tokens" reverted with the following reason: ERC721: insufficient allowance` +`ContractFunctionExecutionError: The contract function "depositERC721Tokens" reverted with the following reason: ERC721: insufficient allowance` ::: To deposit ERC721 tokens, use the `cartesi send erc721` command and follow the prompts. @@ -430,24 +442,18 @@ To inspect the balance, make an HTTP call to: http://localhost:8080/inspect/{address}/{tokenAddress} ``` - #### Transfers and Withdrawals Use the `cartesi send generic` command and follow the prompts. Here are sample payloads: 1. For transfers: - ```js - {"operation":"transfer","erc721":"0xTokenAddress","from":"0xFromAddress","to":"0xToAddress","tokenId":"1"} - ``` +```js +{"operation":"transfer","erc721":"0xTokenAddress","from":"0xFromAddress","to":"0xToAddress","tokenId":"1"} +``` 2. For withdrawals: - ```js - {"operation":"withdraw","erc721":"0xTokenAddress","from":"0xFromAddress","tokenId":"1"} - ``` - - - - - +```js +{"operation":"withdraw","erc721":"0xTokenAddress","from":"0xFromAddress","tokenId":"1"} +``` 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 44893fb4..25c2a750 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 @@ -1,7 +1,7 @@ --- id: ether-wallet title: Integrating Ether wallet functionality -resources: +resources: - url: https://github.com/masiedu4/ether-wallet-tutorial title: Source code for Ether wallet tutorial --- @@ -36,8 +36,6 @@ yarn add ethers viem yarn add -D @cartesi/rollups ``` - - ## Define the ABIs Let's write a configuration to generate the ABIs of the Cartesi Rollups Contracts. @@ -68,28 +66,28 @@ generate_abi() { local sol_file="$1" local contract_name="$2" local output_file="$TS_DIR/${contract_name}Abi.ts" - + echo "Compiling $sol_file..." - + # Compile the contract in the temporary directory npx solcjs --abi "$sol_file" --base-path . --include-path node_modules/ --output-dir "$TEMP_DIR" - + # Find the generated ABI file abi_file=$(find "$TEMP_DIR" -name "*_${contract_name}.abi") - + if [ ! -f "$abi_file" ]; then echo "Error: ABI file not found for $contract_name" return 1 fi - + # Read the ABI content abi=$(cat "$abi_file") - + echo "Extracted ABI for $contract_name" - + # Create a TypeScript file with exported ABI echo "export const ${contract_name}Abi = $abi as const;" > "$output_file" - + echo "Generated ABI for $contract_name" echo "----------------------" } @@ -110,12 +108,9 @@ Now, let's make the script executable: chmod +x generate_abis.sh - And run it: - ./generate_abis.sh - ## Building the Ether wallet @@ -264,14 +259,12 @@ export class Wallet { }; } } - - ``` + The `Wallet` class manages multiple accounts and provides methods for everyday wallet operations. Key features include storing balances, centralizing the logic for retrieving or creating a balance, and depositing, withdrawing, and transferring Ether. `parseDepositPayload` and `encodeWithdrawCall` handle the low-level details of working with the base layer data. - ### Voucher creation The `encodeWithdrawCall` method returns a voucher. Creating vouchers is a crucial concept in Cartesi rollups for executing withdrawal operations on the base layer chain. @@ -280,18 +273,16 @@ The voucher creation process occurs during Ether’s withdrawal. Here's how it w 1. The `encodeFunctionData` function creates the calldata for the [`function withdrawEther(address _receiver, uint256 _value) external`](../rollups-apis/json-rpc/application.md/#withdrawether) on the `CartesiDApp` contract. - It returns a Voucher object with two properties: +It returns a Voucher object with two properties: - `destination`: The address of the Cartesi dApp - `payload`: The encoded function calldata - 2. The `withdrawEther` method of the `Wallet` class is called with three parameters: - - `application`: The address of the Cartesi dApp - - `address`: The user's address who wants to withdraw - - `amount`: The amount of Ether to withdraw - + - `application`: The address of the Cartesi dApp + - `address`: The user's address who wants to withdraw + - `amount`: The amount of Ether to withdraw ## Using the Ether wallet @@ -343,7 +334,7 @@ const handleAdvance: AdvanceRequestHandler = async (data) => { if (sender.toLowerCase() === dAppAddressRelay.toLowerCase()) { dAppAddress = data.payload; - return 'accept' + return "accept"; } if (sender.toLowerCase() === EtherPortal.toLowerCase()) { @@ -457,8 +448,8 @@ main().catch((e) => { console.log(e); process.exit(1); }); - ``` + This code sets up a simple application that listens for requests from the Cartesi rollup server. It processes the requests and sends responses back to the server. Here is a breakdown of the wallet functionality: @@ -473,22 +464,19 @@ Here is a breakdown of the wallet functionality: - For `transfers`, we call `wallet.transferEther` and create a notice with the parsed parameters. -For `withdrawals,` we call `wallet.withdrawEther` and create a voucher using the dApp dress and the parsed parameters. +For `withdrawals,` we call `wallet.withdrawEther` and create a voucher using the dApp dress and the parsed parameters. - We created helper functions to `createNotice` for deposits and transfers, `createReport` for balance checks and `createVoucher` for withdrawals. - :::caution important -The dApp address needs to be relayed strictly before withdrawal requests. +The dApp address needs to be relayed strictly before withdrawal requests. To relay the dApp address, run: `cartesi send dapp-address` ::: - - ## Build and run the application -With Docker running, [build your backend application](../development/building-the-application.md) by running: +With Docker running, [build your backend application](../development/building-a-dapp.md) by running: ```shell cartesi build @@ -505,10 +493,9 @@ cartesi run To deposit ether, run the command below and follow the prompts: ``` -cartesi send ether +cartesi send ether ``` - #### Balance checks(used in Inspect requests) To inspect balance, make an HTTP call to: @@ -517,8 +504,6 @@ To inspect balance, make an HTTP call to: http://localhost:8080/inspect/{address} ``` - - #### Transfer and Withdrawals Transfers and withdrawal requests will be sent as generic json strings that will be parsed and processed. @@ -533,23 +518,22 @@ Here are the sample payloads as one-liners, ready to be used in your code: 1. For transfers: - ```js - {"operation":"transfer","from":"0xAddress123","to":"0xAddress345","amount":"1000000000000000000"} - ``` - +```js +{"operation":"transfer","from":"0xAddress123","to":"0xAddress345","amount":"1000000000000000000"} +``` 2. For withdrawals: - ```js - {"operation":"withdraw","from":"0xAddress345","amount":"500000000000000000"} - ``` +```js +{"operation":"withdraw","from":"0xAddress345","amount":"500000000000000000"} +``` ### Using the explorer -For end-to-end functionality, developers will likely build their [custom user-facing web application](../tutorials/react-frontend-application.md). +For end-to-end functionality, developers will likely build their [custom user-facing web application](../tutorials/react-frontend-application.md). [CartesiScan](https://cartesiscan.io/) is a web application that offers a comprehensive overview of your application. It provides expandable data regarding notices, vouchers, and reports. When you run your application with `cartesi run`, there is a local instance of CartesiScan on `http://localhost:8080/explorer`. -You can execute your vouchers via the explorer, which completes the withdrawal process at the end of [an epoch](../rollups-apis/backend/vouchers.md/#epoch-configuration). +You can execute your vouchers via the explorer, which completes the withdrawal process at the end of [an epoch](../rollups-apis/backend/vouchers.md/#epoch-configuration). 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", From 27e1362dd7b595ebc14c9a96ef106c412bb47532 Mon Sep 17 00:00:00 2001 From: Henrique Marlon Date: Tue, 15 Oct 2024 18:15:53 -0300 Subject: [PATCH 05/99] docs: restructuring the api reference (cherry picked from commit 7df70d07b2a41b00399769f7773de2f13552b23a) --- .../backend/introduction.md | 0 .../backend/notices.md | 0 .../backend/reports.md | 0 .../backend/vouchers.md | 0 .../graphql/basics.md | 0 .../graphql/directives/_category_.yml | 0 .../graphql/directives/deprecated.mdx | 0 .../graphql/directives/include.mdx | 0 .../graphql/directives/skip.mdx | 0 .../graphql/directives/specified-by.mdx | 0 .../graphql/generated.md | 0 .../graphql/inputs/_category_.yml | 0 .../graphql/inputs/input-filter.md | 0 .../graphql/objects/_category_.yml | 0 .../graphql/objects/completion-status.md | 0 .../graphql/objects/input-connection.md | 0 .../graphql/objects/input-edge.md | 0 .../graphql/objects/input.md | 0 .../graphql/objects/notice-connection.md | 0 .../graphql/objects/notice-edge.md | 0 .../graphql/objects/notice.md | 0 .../graphql/objects/output-validity-proof.md | 0 .../graphql/objects/page-info.md | 0 .../graphql/objects/proof.md | 0 .../graphql/objects/report-connection.md | 0 .../graphql/objects/report-edge.md | 0 .../graphql/objects/report.md | 0 .../graphql/objects/voucher-connection.md | 0 .../graphql/objects/voucher-edge.md | 0 .../graphql/objects/voucher.md | 0 .../graphql/queries/_category_.yml | 0 .../graphql/queries/inputs.md | 0 .../graphql/queries/notices.md | 0 .../graphql/queries/reports.md | 0 .../graphql/queries/vouchers.md | 0 .../graphql/scalars/DateTime.md | 0 .../graphql/scalars/_category_.yml | 0 .../graphql/scalars/big-int.md | 0 .../graphql/scalars/boolean.md | 0 .../graphql/scalars/int.md | 0 .../graphql/scalars/string.md | 0 .../graphql/sidebar-schema.js | 0 .../{rollups-apis => api-reference}/index.md | 0 .../inspect.yaml | 0 ...tate-http-api-for-cartesi-rollups.info.mdx | 0 .../inspect/inspect.api.mdx | 0 .../json-rpc/application-factory.md | 0 .../json-rpc/application.md | 0 .../json-rpc/input-box.md | 0 .../json-rpc/overview.md | 0 .../json-rpc/portals/ERC1155BatchPortal.md | 0 .../json-rpc/portals/ERC1155SinglePortal.md | 0 .../json-rpc/portals/ERC20Portal.md | 0 .../json-rpc/portals/ERC721Portal.md | 0 .../json-rpc/portals/EtherPortal.md | 0 .../json-rpc/relays/relays.md | 0 .../rollup.yaml | 0 .../rollup/add-notice.api.mdx | 0 .../rollup/add-report.api.mdx | 0 .../rollup/add-voucher.api.mdx | 0 .../rollup/cartesi-rollup-http-api.info.mdx | 0 .../rollup/finish.api.mdx | 0 .../rollup/register-exception.api.mdx | 0 .../rollup/sidebar.js | 0 .../typeDefs.graphql | 0 .../version-2.0/core-concepts/architecture.md | 26 ++-- .../version-2.0/deployment/introduction.md | 2 +- .../version-2.0/deployment/self-hosted.md | 2 +- .../version-2.0/development/asset-handling.md | 14 +- .../version-2.0/development/query-outputs.md | 4 +- .../version-2.0/development/send-inputs.md | 2 +- .../version-2.0/index.md | 4 +- .../version-2.0/tutorials/ether-wallet.md | 8 +- .../tutorials/react-frontend-application.md | 18 +-- .../version-2.0-sidebars.json | 141 +++++++++--------- 75 files changed, 107 insertions(+), 114 deletions(-) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/backend/introduction.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/backend/notices.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/backend/reports.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/backend/vouchers.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/basics.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/directives/_category_.yml (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/directives/deprecated.mdx (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/directives/include.mdx (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/directives/skip.mdx (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/directives/specified-by.mdx (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/generated.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/inputs/_category_.yml (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/inputs/input-filter.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/objects/_category_.yml (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/objects/completion-status.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/objects/input-connection.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/objects/input-edge.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/objects/input.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/objects/notice-connection.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/objects/notice-edge.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/objects/notice.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/objects/output-validity-proof.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/objects/page-info.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/objects/proof.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/objects/report-connection.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/objects/report-edge.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/objects/report.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/objects/voucher-connection.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/objects/voucher-edge.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/objects/voucher.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/queries/_category_.yml (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/queries/inputs.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/queries/notices.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/queries/reports.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/queries/vouchers.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/scalars/DateTime.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/scalars/_category_.yml (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/scalars/big-int.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/scalars/boolean.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/scalars/int.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/scalars/string.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/graphql/sidebar-schema.js (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/index.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/inspect.yaml (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/inspect/inspect-state-http-api-for-cartesi-rollups.info.mdx (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/inspect/inspect.api.mdx (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/json-rpc/application-factory.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/json-rpc/application.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/json-rpc/input-box.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/json-rpc/overview.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/json-rpc/portals/ERC1155BatchPortal.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/json-rpc/portals/ERC1155SinglePortal.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/json-rpc/portals/ERC20Portal.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/json-rpc/portals/ERC721Portal.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/json-rpc/portals/EtherPortal.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/json-rpc/relays/relays.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/rollup.yaml (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/rollup/add-notice.api.mdx (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/rollup/add-report.api.mdx (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/rollup/add-voucher.api.mdx (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/rollup/cartesi-rollup-http-api.info.mdx (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/rollup/finish.api.mdx (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/rollup/register-exception.api.mdx (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/rollup/sidebar.js (100%) rename cartesi-rollups_versioned_docs/version-2.0/{rollups-apis => api-reference}/typeDefs.graphql (100%) diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/backend/introduction.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/introduction.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/backend/introduction.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/introduction.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/backend/notices.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/notices.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/backend/notices.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/notices.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/backend/reports.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/reports.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/backend/reports.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/reports.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/backend/vouchers.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/vouchers.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/backend/vouchers.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/backend/vouchers.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/basics.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/basics.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/basics.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/basics.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/directives/_category_.yml b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/_category_.yml similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/directives/_category_.yml rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/_category_.yml diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/directives/deprecated.mdx b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/deprecated.mdx similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/directives/deprecated.mdx rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/deprecated.mdx diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/directives/include.mdx b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/include.mdx similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/directives/include.mdx rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/include.mdx diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/directives/skip.mdx b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/skip.mdx similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/directives/skip.mdx rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/skip.mdx diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/directives/specified-by.mdx b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/specified-by.mdx similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/directives/specified-by.mdx rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/directives/specified-by.mdx diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/generated.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/generated.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/generated.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/generated.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/inputs/_category_.yml b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/inputs/_category_.yml similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/inputs/_category_.yml rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/inputs/_category_.yml diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/inputs/input-filter.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/inputs/input-filter.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/inputs/input-filter.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/inputs/input-filter.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/_category_.yml b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/_category_.yml similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/_category_.yml rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/_category_.yml diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/completion-status.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/completion-status.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/completion-status.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/completion-status.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/input-connection.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/input-connection.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/input-connection.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/input-connection.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/input-edge.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/input-edge.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/input-edge.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/input-edge.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/input.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/input.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/input.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/input.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/notice-connection.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/notice-connection.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/notice-connection.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/notice-connection.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/notice-edge.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/notice-edge.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/notice-edge.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/notice-edge.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/notice.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/notice.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/notice.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/notice.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/output-validity-proof.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/output-validity-proof.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/output-validity-proof.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/output-validity-proof.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/page-info.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/page-info.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/page-info.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/page-info.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/proof.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/proof.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/proof.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/proof.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/report-connection.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/report-connection.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/report-connection.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/report-connection.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/report-edge.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/report-edge.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/report-edge.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/report-edge.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/report.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/report.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/report.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/report.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/voucher-connection.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/voucher-connection.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/voucher-connection.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/voucher-connection.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/voucher-edge.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/voucher-edge.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/voucher-edge.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/voucher-edge.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/voucher.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/voucher.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/objects/voucher.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/objects/voucher.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/queries/_category_.yml b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/_category_.yml similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/queries/_category_.yml rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/_category_.yml diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/queries/inputs.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/inputs.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/queries/inputs.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/inputs.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/queries/notices.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/notices.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/queries/notices.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/notices.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/queries/reports.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/reports.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/queries/reports.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/reports.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/queries/vouchers.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/vouchers.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/queries/vouchers.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/queries/vouchers.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/scalars/DateTime.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/DateTime.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/scalars/DateTime.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/DateTime.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/scalars/_category_.yml b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/_category_.yml similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/scalars/_category_.yml rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/_category_.yml diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/scalars/big-int.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/big-int.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/scalars/big-int.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/big-int.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/scalars/boolean.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/boolean.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/scalars/boolean.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/boolean.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/scalars/int.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/int.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/scalars/int.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/int.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/scalars/string.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/string.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/scalars/string.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/scalars/string.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/sidebar-schema.js b/cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/sidebar-schema.js similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/graphql/sidebar-schema.js rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/graphql/sidebar-schema.js diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/index.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/index.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/index.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/index.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/inspect.yaml b/cartesi-rollups_versioned_docs/version-2.0/api-reference/inspect.yaml similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/inspect.yaml rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/inspect.yaml diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/inspect/inspect-state-http-api-for-cartesi-rollups.info.mdx b/cartesi-rollups_versioned_docs/version-2.0/api-reference/inspect/inspect-state-http-api-for-cartesi-rollups.info.mdx similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/inspect/inspect-state-http-api-for-cartesi-rollups.info.mdx rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/inspect/inspect-state-http-api-for-cartesi-rollups.info.mdx diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/inspect/inspect.api.mdx b/cartesi-rollups_versioned_docs/version-2.0/api-reference/inspect/inspect.api.mdx similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/inspect/inspect.api.mdx rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/inspect/inspect.api.mdx diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/json-rpc/application-factory.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/application-factory.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/json-rpc/application-factory.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/application-factory.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/json-rpc/application.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/application.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/json-rpc/application.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/application.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/json-rpc/input-box.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/input-box.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/json-rpc/input-box.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/input-box.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/json-rpc/overview.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/overview.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/json-rpc/overview.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/overview.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/json-rpc/portals/ERC1155BatchPortal.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC1155BatchPortal.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/json-rpc/portals/ERC1155BatchPortal.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC1155BatchPortal.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/json-rpc/portals/ERC1155SinglePortal.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC1155SinglePortal.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/json-rpc/portals/ERC1155SinglePortal.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC1155SinglePortal.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/json-rpc/portals/ERC20Portal.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC20Portal.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/json-rpc/portals/ERC20Portal.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC20Portal.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/json-rpc/portals/ERC721Portal.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC721Portal.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/json-rpc/portals/ERC721Portal.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC721Portal.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/json-rpc/portals/EtherPortal.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/EtherPortal.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/json-rpc/portals/EtherPortal.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/EtherPortal.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/json-rpc/relays/relays.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/relays/relays.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/json-rpc/relays/relays.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/relays/relays.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/rollup.yaml b/cartesi-rollups_versioned_docs/version-2.0/api-reference/rollup.yaml similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/rollup.yaml rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/rollup.yaml diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/rollup/add-notice.api.mdx b/cartesi-rollups_versioned_docs/version-2.0/api-reference/rollup/add-notice.api.mdx similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/rollup/add-notice.api.mdx rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/rollup/add-notice.api.mdx diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/rollup/add-report.api.mdx b/cartesi-rollups_versioned_docs/version-2.0/api-reference/rollup/add-report.api.mdx similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/rollup/add-report.api.mdx rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/rollup/add-report.api.mdx diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/rollup/add-voucher.api.mdx b/cartesi-rollups_versioned_docs/version-2.0/api-reference/rollup/add-voucher.api.mdx similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/rollup/add-voucher.api.mdx rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/rollup/add-voucher.api.mdx diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/rollup/cartesi-rollup-http-api.info.mdx b/cartesi-rollups_versioned_docs/version-2.0/api-reference/rollup/cartesi-rollup-http-api.info.mdx similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/rollup/cartesi-rollup-http-api.info.mdx rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/rollup/cartesi-rollup-http-api.info.mdx diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/rollup/finish.api.mdx b/cartesi-rollups_versioned_docs/version-2.0/api-reference/rollup/finish.api.mdx similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/rollup/finish.api.mdx rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/rollup/finish.api.mdx diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/rollup/register-exception.api.mdx b/cartesi-rollups_versioned_docs/version-2.0/api-reference/rollup/register-exception.api.mdx similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/rollup/register-exception.api.mdx rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/rollup/register-exception.api.mdx diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/rollup/sidebar.js b/cartesi-rollups_versioned_docs/version-2.0/api-reference/rollup/sidebar.js similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/rollup/sidebar.js rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/rollup/sidebar.js diff --git a/cartesi-rollups_versioned_docs/version-2.0/rollups-apis/typeDefs.graphql b/cartesi-rollups_versioned_docs/version-2.0/api-reference/typeDefs.graphql similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/rollups-apis/typeDefs.graphql rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/typeDefs.graphql diff --git a/cartesi-rollups_versioned_docs/version-2.0/core-concepts/architecture.md b/cartesi-rollups_versioned_docs/version-2.0/core-concepts/architecture.md index 19918611..de831bfe 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/core-concepts/architecture.md +++ b/cartesi-rollups_versioned_docs/version-2.0/core-concepts/architecture.md @@ -48,28 +48,28 @@ The Cartesi Machine achieves its unique balance of scalability and security by p ## On-chain components -The on-chain part of Cartesi Rollups consists of [several smart contracts](../rollups-apis/json-rpc/overview.md) deployed on the base layer. +The on-chain part of Cartesi Rollups consists of [several smart contracts](../api-reference/json-rpc/overview.md) deployed on the base layer. Here is an overview of the major contracts, with each serving a specific role in the dApp ecosystem: ### InputBox -The [InputBox](../rollups-apis/json-rpc/input-box.md) contract is the entry point for user interactions with the off-chain layer. All inputs destined for a Cartesi dApp are first submitted to this contract, which then emits events that the off-chain components can process. +The [InputBox](../api-reference/json-rpc/input-box.md) contract is the entry point for user interactions with the off-chain layer. All inputs destined for a Cartesi dApp are first submitted to this contract, which then emits events that the off-chain components can process. ### CartesiDApp -Each Cartesi dApp is associated with a unique instance of the [CartesiDApp](../rollups-apis/json-rpc/application.md) contract. This contract acts as the on-chain representation of the dApp and can hold ownership of digital assets on the base layer, including Ether, ERC-20 tokens, and NFTs. +Each Cartesi dApp is associated with a unique instance of the [CartesiDApp](../api-reference/json-rpc/application.md) contract. This contract acts as the on-chain representation of the dApp and can hold ownership of digital assets on the base layer, including Ether, ERC-20 tokens, and NFTs. ### CartesiDAppFactory -The [CartesiDAppFactory](../rollups-apis/json-rpc/application-factory.md) contract simplifies the deployment process for CartesiDApp contracts. It allows developers to deploy new CartesiDApp instances with a single function call, enhancing convenience and security. This factory approach ensures the deployed contract bytecode remains unaltered, assuring users and validators. +The [CartesiDAppFactory](../api-reference/json-rpc/application-factory.md) contract simplifies the deployment process for CartesiDApp contracts. It allows developers to deploy new CartesiDApp instances with a single function call, enhancing convenience and security. This factory approach ensures the deployed contract bytecode remains unaltered, assuring users and validators. ### Portals Portal contracts facilitate the secure transfer of assets between the base layer and the Cartesi execution environment. Currently, Cartesi supports the following types of asset transfers: -- [Ether (ETH)](../rollups-apis/json-rpc/portals/EtherPortal.md) -- [ERC-20 (Fungible tokens)](../rollups-apis/json-rpc/portals/ERC20Portal.md) -- [ERC-721 (Non-fungible tokens)](../rollups-apis/json-rpc/portals/ERC721Portal.md) -- [ERC-1155 Single transfers](../rollups-apis/json-rpc/portals/ERC1155SinglePortal.md) -- [ERC-1155 Batch transfers](../rollups-apis/json-rpc/portals/ERC1155BatchPortal.md) +- [Ether (ETH)](../api-reference/json-rpc/portals/EtherPortal.md) +- [ERC-20 (Fungible tokens)](../api-reference/json-rpc/portals/ERC20Portal.md) +- [ERC-721 (Non-fungible tokens)](../api-reference/json-rpc/portals/ERC721Portal.md) +- [ERC-1155 Single transfers](../api-reference/json-rpc/portals/ERC1155SinglePortal.md) +- [ERC-1155 Batch transfers](../api-reference/json-rpc/portals/ERC1155BatchPortal.md) These Portal contracts implement the logic to "teleport" assets safely between layers, maintaining their integrity and ownership throughout the transfer process. @@ -113,9 +113,9 @@ The `advance-state` process changes the application state, and it involves the f - The node sends the input to the application backend inside the Cartesi Machine. -- The Cartesi Machine processes the input and generates verifiable outputs ([vouchers](../rollups-apis/backend/vouchers.md), [notices](../rollups-apis/backend/notices.md), and [reports](../rollups-apis/backend/reports.md)). +- 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](../rollups-apis/graphql/basics.md). +- The application frontend can query these outputs using the node's [GraphQL API](../api-reference/graphql/basics.md). ### Inspect state @@ -127,9 +127,9 @@ The `inspect-state` process allows for querying the application backend without - The node forwards this input to the Cartesi Machine. -- The Cartesi Machine processes the input and generates a [report](../rollups-apis/backend/reports.md). +- The Cartesi Machine processes the input and generates a [report](../api-reference/backend/reports.md). -- The node returns this report to the frontend via a [REST API](../rollups-apis/backend/introduction.md/#advance-and-inspect). +- The node returns this report to the frontend via a [REST API](../api-reference/backend/introduction.md/#advance-and-inspect). :::note Inspect requests It's important to note that `inspect-state` inputs do not produce vouchers or notices, and the current implementation processes inputs sequentially, which may impact scalability for applications heavily reliant on inspect-state functionality. diff --git a/cartesi-rollups_versioned_docs/version-2.0/deployment/introduction.md b/cartesi-rollups_versioned_docs/version-2.0/deployment/introduction.md index 2e1b63be..f5c469d9 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/deployment/introduction.md +++ b/cartesi-rollups_versioned_docs/version-2.0/deployment/introduction.md @@ -23,7 +23,7 @@ The `cartesi build` command produces the Cartesi genesis machine, which contains After deployment, any changes to the application code will generate a different hash and, hence, require another deployment. -The smart contract that represents the application on the base layer can be deployed using the [`CartesiDAppFactory`](../rollups-apis/json-rpc/application-factory.md) smart contract. +The smart contract that represents the application on the base layer can be deployed using the [`CartesiDAppFactory`](../api-reference/json-rpc/application-factory.md) smart contract. There are two methods to deploy an application: diff --git a/cartesi-rollups_versioned_docs/version-2.0/deployment/self-hosted.md b/cartesi-rollups_versioned_docs/version-2.0/deployment/self-hosted.md index d4ce4914..e6573e49 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/deployment/self-hosted.md +++ b/cartesi-rollups_versioned_docs/version-2.0/deployment/self-hosted.md @@ -93,7 +93,7 @@ Alternatively, you can use a service like [Fly.io](https://fly.io/) to deploy yo The connection string for a PostgreSQL database must be configured at the `CARTESI_POSTGRES_ENDPOINT` variable. - You can use any PostgreSQL database, whether managed by a cloud provider or set up on your local infrastructure. The key configuration required is the connection string, encompassing the database URL, username, password, and name. The node necessitates a PostgreSQL database to store the application state, which is accessible via the [GraphQL API](../rollups-apis/graphql/basics.md). + You can use any PostgreSQL database, whether managed by a cloud provider or set up on your local infrastructure. The key configuration required is the connection string, encompassing the database URL, username, password, and name. The node necessitates a PostgreSQL database to store the application state, which is accessible via the [GraphQL API](../api-reference/graphql/basics.md). 1. With all the config variables set, here is how you can run the node on your local machine: 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 0e6b004e..bd4bc950 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 @@ -12,11 +12,11 @@ As with any execution layer solution, a Cartesi dApp that wants to manipulate as Currently, Cartesi Rollups support the following types of assets: -- [Ether (ETH)](../rollups-apis/json-rpc/portals/) -- [ERC-20](../rollups-apis/json-rpc/portals/ERC20Portal.md) -- [ERC-721](../rollups-apis/json-rpc/portals/ERC721Portal.md) -- [ERC-1155 Single](../rollups-apis/json-rpc/portals/ERC1155SinglePortal.md) -- [ERC-1155 Batch](../rollups-apis/json-rpc/portals/ERC1155BatchPortal.md) +- [Ether (ETH)](../api-reference/json-rpc/portals/EtherPortal.md) +- [ERC-20](../api-reference/json-rpc/portals/ERC20Portal.md) +- [ERC-721](../api-reference/json-rpc/portals/ERC721Portal.md) +- [ERC-1155 Single](../api-reference/json-rpc/portals/ERC1155SinglePortal.md) +- [ERC-1155 Batch](../api-reference/json-rpc/portals/ERC1155BatchPortal.md) ![img](../../../static/img/v1.3/assets.jpg) @@ -50,7 +50,7 @@ Vouchers are crucial in allowing dApps in the execution layer to interact with c The dApp’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 dApp itself. -By calling [`relayDAppAddress()`](../rollups-apis/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()`](../rollups-apis/json-rpc/application.md/#executevoucher) function of the `CartesiDApp` contract. +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. :::note epoch length By default, Cartesi nodes close one epoch every 7200 blocks. You can [manually set the epoch length](./cli-commands.md/#run) to facilitate quicker asset-handling methods. @@ -60,7 +60,7 @@ Here are the function signatures used by vouchers to withdraw the different type | Asset | Destination | Function signature | | :------- | :------------- | :------------------------------------------------------------------------------------------------------------------------------------------ | -| Ether | dApp contract | `withdrawEther(address,uint256)` [:page_facing_up:](../rollups-apis/json-rpc/application.md/#withdrawether) | +| Ether | dApp contract | `withdrawEther(address,uint256)` [:page_facing_up:](../api-reference/json-rpc/application.md/#withdrawether) | | ERC-20 | Token contract | `transfer(address,uint256)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-20#methods) | | ERC-20 | Token contract | `transferFrom(address,address,uint256)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-20#methods) | | ERC-721 | Token contract | `safeTransferFrom(address,address,uint256)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-721#specification) | 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 28eb2736..249d682d 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 @@ -20,7 +20,7 @@ Think of it as a digital authorization ticket that enables a dApp to perform spe - The voucher specifies the action, such as a token swap, and is sent to the blockchain. -- The [`CartesiDApp`](../rollups-apis/json-rpc/application.md) contract executes the voucher using the [`executeVoucher()`](../rollups-apis/json-rpc/application.md/#executevoucher) function. +- The [`CartesiDApp`](../api-reference/json-rpc/application.md) contract executes the voucher using the [`executeVoucher()`](../api-reference/json-rpc/application.md/#executevoucher) function. - The result is recorded on the base layer through claims submitted by a consensus contract. @@ -40,7 +40,7 @@ They serve as a means for dApp to notify the blockchain about particular events. - The notice is submitted to the Rollup Server as evidence of the off-chain event. -- Notices are validated on-chain using the [`validateNotice()`](../rollups-apis/json-rpc/application.md/#validatenotice) function of the [`CartesiDApp`](../rollups-apis/json-rpc/application.md) contract. +- Notices are validated on-chain using the [`validateNotice()`](../api-reference/json-rpc/application.md/#validatenotice) function of the [`CartesiDApp`](../api-reference/json-rpc/application.md) contract. ### Send a notice diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/send-inputs.md b/cartesi-rollups_versioned_docs/version-2.0/development/send-inputs.md index 0ee05c14..93c0efce 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/development/send-inputs.md +++ b/cartesi-rollups_versioned_docs/version-2.0/development/send-inputs.md @@ -137,7 +137,7 @@ Advance requests involve sending input data to the L1 through a JSON-RPC call, a In the dApp architecture, here is how an advance request plays out. -- Step 1: Send an input to the [`addInput(address, bytes)`](../rollups-apis/json-rpc/input-box.md/#addinput) function of the InputBox smart contract. +- Step 1: Send an input to the [`addInput(address, bytes)`](../api-reference/json-rpc/input-box.md/#addinput) function of the InputBox smart contract. - Step 2: The Cartesi Node reads the data and gives it to the Cartesi machine for processing. diff --git a/cartesi-rollups_versioned_docs/version-2.0/index.md b/cartesi-rollups_versioned_docs/version-2.0/index.md index b02aa267..10267c5e 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/index.md +++ b/cartesi-rollups_versioned_docs/version-2.0/index.md @@ -28,7 +28,7 @@ In its simplest form, the Cartesi framework integrates tightly with the base lay ## Example -Below is a simple Node.js example demonstrating how to read an input and reply with a [notice](./rollups-apis/backend/notices.md) (a type of output). +Below is a simple Node.js example demonstrating how to read an input and reply with a [notice](./api-reference/backend/notices.md) (a type of output). ```javascript const { ethers } = require("ethers"); @@ -54,7 +54,7 @@ let finish = { status: "accept" }; ``` -Cartesi dApps are implemented as infinite loops that manage their transaction cycles through HTTP POST requests to the `/finish` endpoint to ensure flexibility across different programming languages and stacks. You can learn more about this abstraction [here](./rollups-apis/backend/introduction.md). +Cartesi dApps are implemented as infinite loops that manage their transaction cycles through HTTP POST requests to the `/finish` endpoint to ensure flexibility across different programming languages and stacks. You can learn more about this abstraction [here](./api-reference/backend/introduction.md). In the Cartesi Rollup framework, all inputs sent to the base layer trigger an "advance_state" [request](./development/send-inputs.md#initiate-an-advance-request), which alters the state of the Cartesi Machine and consequently the Rollup. Since inputs originate on-chain, they are hex-encoded following the EVM message standard. 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 25c2a750..b7199ab5 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 @@ -271,7 +271,7 @@ The `encodeWithdrawCall` method returns a voucher. Creating vouchers is a crucia The voucher creation process occurs during Ether’s withdrawal. Here's how it works in this application: -1. The `encodeFunctionData` function creates the calldata for the [`function withdrawEther(address _receiver, uint256 _value) external`](../rollups-apis/json-rpc/application.md/#withdrawether) on the `CartesiDApp` contract. +1. The `encodeFunctionData` function creates the calldata for the [`function withdrawEther(address _receiver, uint256 _value) external`](../api-reference/json-rpc/application.md/#withdrawether) on the `CartesiDApp` contract. It returns a Voucher object with two properties: @@ -288,9 +288,9 @@ It returns a Voucher object with two properties: Now, let's create a simple application at the entry point, `src/index.ts,` to test the wallet functionality. -The [`EtherPortal`](../rollups-apis/json-rpc/portals/EtherPortal.md) contract allows anyone to perform transfers of Ether to a dApp. All deposits to a dApp are made via the `EtherPortal` contract. +The [`EtherPortal`](../api-reference/json-rpc/portals/EtherPortal.md) contract allows anyone to perform transfers of Ether to a dApp. All deposits to a dApp are made via the `EtherPortal` contract. -The [`DAppAddressRelay`](../rollups-apis/json-rpc/relays/relays.md) contract provides the critical information (the dApp's address) that the voucher creation process needs to function correctly. Without this relay mechanism, the off-chain part of the dApp wouldn't know its on-chain address, making it impossible to create valid vouchers for withdrawals. +The [`DAppAddressRelay`](../api-reference/json-rpc/relays/relays.md) contract provides the critical information (the dApp's address) that the voucher creation process needs to function correctly. Without this relay mechanism, the off-chain part of the dApp wouldn't know its on-chain address, making it impossible to create valid vouchers for withdrawals. :::note Run `cartesi address-book` to get the addresses of the `EtherPortal` and `DAppAddressRelay` contracts. Save these as constants in the `index.ts` file. @@ -536,4 +536,4 @@ For end-to-end functionality, developers will likely build their [custom user-fa When you run your application with `cartesi run`, there is a local instance of CartesiScan on `http://localhost:8080/explorer`. -You can execute your vouchers via the explorer, which completes the withdrawal process at the end of [an epoch](../rollups-apis/backend/vouchers.md/#epoch-configuration). +You can execute your vouchers via the explorer, which completes the withdrawal process at the end of [an epoch](../api-reference/backend/vouchers.md/#epoch-configuration). diff --git a/cartesi-rollups_versioned_docs/version-2.0/tutorials/react-frontend-application.md b/cartesi-rollups_versioned_docs/version-2.0/tutorials/react-frontend-application.md index 4f22d8a8..bd01bd61 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/tutorials/react-frontend-application.md +++ b/cartesi-rollups_versioned_docs/version-2.0/tutorials/react-frontend-application.md @@ -353,7 +353,7 @@ npx wagmi generate ## Sending a generic input -The [`InputBox`](../rollups-apis/json-rpc/input-box.md) contract is a trustless and permissionless contract that receives arbitrary blobs (called "inputs") from anyone. +The [`InputBox`](../api-reference/json-rpc/input-box.md) contract is a trustless and permissionless contract that receives arbitrary blobs (called "inputs") from anyone. The `InputBox` contract is deployed on all supported chains. We will use a React hook to send an input to the backend via the `InputBox` contract. @@ -415,7 +415,7 @@ This hook provides us with state variables and a `writeContractAsync` function t The `submit` function is called when the form is submitted. -It uses the `writeContractAsync` function to send the input to the [`addInput(address _dapp, bytes _input)`](../rollups-apis/json-rpc/input-box.md/#addinput) function of the `InputBox`. +It uses the `writeContractAsync` function to send the input to the [`addInput(address _dapp, bytes _input)`](../api-reference/json-rpc/input-box.md/#addinput) function of the `InputBox`. The `inputValue` will be received by the particular backend address is `dAppAddress`. @@ -535,7 +535,7 @@ export default App; ## Depositing Ether -The [`EtherPortal`](../rollups-apis/json-rpc/portals/EtherPortal.md) contract is a pre-deployed smart contract that allows users to deposit Ether to the Cartesi backend. +The [`EtherPortal`](../api-reference/json-rpc/portals/EtherPortal.md) contract is a pre-deployed smart contract that allows users to deposit Ether to the Cartesi backend. This implementation will be similar to the generic input, but with a few changes to handle Ether transactions. @@ -660,7 +660,7 @@ export default SendEther; ## Depositing ERC20 Tokens -The [`ERC20Portal`](../rollups-apis/json-rpc/portals/ERC20Portal.md) contract is a pre-deployed smart contract that allows users to deposit ERC20 tokens to the Cartesi backend. +The [`ERC20Portal`](../api-reference/json-rpc/portals/ERC20Portal.md) contract is a pre-deployed smart contract that allows users to deposit ERC20 tokens to the Cartesi backend. This implementation will be similar to the [depositing Ether](#depositing-ether), but with a few changes to handle ERC20 token transactions. @@ -869,7 +869,7 @@ export default App; ## Depositing ERC721 Tokens (NFTs) -The [`ERC721Portal`](../rollups-apis/json-rpc/portals/ERC721Portal.md) contract is a pre-deployed smart contract that allows users to deposit ERC721 tokens to the Cartesi backend. +The [`ERC721Portal`](../api-reference/json-rpc/portals/ERC721Portal.md) contract is a pre-deployed smart contract that allows users to deposit ERC721 tokens to the Cartesi backend. This implementation will be similar to the [depositing ERC20 tokens](#depositing-erc20-tokens), but with a few changes to handle ERC721 token transactions. @@ -1086,12 +1086,12 @@ export default App; ## Listing Notices, Reports, and Vouchers -All inputs sent to the Cartesi backend are processed by the Cartesi Machine. The Cartesi Machine produces three types of outputs: [Notices](../rollups-apis/backend/notices.md), [Reports](../rollups-apis/backend/reports.md), and [Vouchers](../rollups-apis/backend/vouchers.md). +All inputs sent to the Cartesi backend are processed by the Cartesi Machine. The Cartesi Machine produces three types of outputs: [Notices](../api-reference/backend/notices.md), [Reports](../api-reference/backend/reports.md), and [Vouchers](../api-reference/backend/vouchers.md). -These outputs can be queried by the frontend using the [GraphQL API](../rollups-apis/graphql/basics.md) on `http://localhost:8080/graphql`. +These outputs can be queried by the frontend using the [GraphQL API](../api-reference/graphql/basics.md) on `http://localhost:8080/graphql`. :::note GraphQL API Reference -Refer to the [GraphQL API documentation](../rollups-apis/graphql/basics.md) for all the queries and mutations available. +Refer to the [GraphQL API documentation](../api-reference/graphql/basics.md) for all the queries and mutations available. ::: Let's move the GraphQL queries to an external file `src/utils/queries.ts` for better organization and reusability. @@ -1440,7 +1440,7 @@ export default Vouchers; Vouchers in Cartesi dApps authorize specific on-chain actions, such as token swaps or asset transfers, by encapsulating the details of these actions. -They are validated and executed on the blockchain using the [`executeVoucher(address _destination, bytes _payload, struct Proof _proof)`](../rollups-apis/json-rpc/application.md#executevoucher) function in the [`CartesiDApp`](../rollups-apis/json-rpc/application.md/) contract, ensuring legitimacy and transparency. +They are validated and executed on the blockchain using the [`executeVoucher(address _destination, bytes _payload, struct Proof _proof)`](../api-reference/json-rpc/application.md#executevoucher) function in the [`CartesiDApp`](../api-reference/json-rpc/application.md/) contract, ensuring legitimacy and transparency. For example, users might generate vouchers to withdraw assets, which are executed on the base later. diff --git a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json index 288c2b9d..0a57a859 100644 --- a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json +++ b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json @@ -22,106 +22,99 @@ }, { "type": "category", - "label": "Rollups APIs", + "label": "API Reference", "collapsed": true, "items": [ - "rollups-apis/http-api", + "api-reference/http-api", { "type": "category", - "label": "Backend APIs", + "label": "Base Layer Contracts", "collapsed": true, "items": [ - "rollups-apis/backend/introduction", - "rollups-apis/backend/notices", - "rollups-apis/backend/vouchers", - "rollups-apis/backend/reports" + "api-reference/json-rpc/overview", + "api-reference/json-rpc/input-box", + "api-reference/json-rpc/application", + "api-reference/json-rpc/application-factory", + { + "type": "category", + "label": "Portals", + "collapsed": true, + "items": [ + "api-reference/json-rpc/portals/ERC20Portal", + "api-reference/json-rpc/portals/ERC721Portal", + "api-reference/json-rpc/portals/ERC1155SinglePortal", + "api-reference/json-rpc/portals/ERC1155BatchPortal", + "api-reference/json-rpc/portals/EtherPortal" + ] + }, + { + "type": "category", + "label": "Relayer", + "collapsed": true, + "items": [ + "api-reference/json-rpc/relays/relays" + ] + } + ] + }, + { + "type": "category", + "label": "Backend API", + "collapsed": true, + "items": [ + "api-reference/backend/introduction", + "api-reference/backend/notices", + "api-reference/backend/vouchers", + "api-reference/backend/reports" ] }, { "type": "category", - "label": "Frontend APIs", + "label": "GraphQL API", "collapsed": true, "items": [ + "api-reference/graphql/overview", { "type": "category", - "label": "Smart contracts API", + "label": "Queries", + "collapsed": true, + "items": [ + "api-reference/graphql/queries/inputs", + "api-reference/graphql/queries/notices", + "api-reference/graphql/queries/vouchers", + "api-reference/graphql/queries/reports" + ] + }, + { + "type": "category", + "label": "Objects", "collapsed": true, "items": [ - "rollups-apis/json-rpc/overview", - "rollups-apis/json-rpc/input-box", - "rollups-apis/json-rpc/application", - "rollups-apis/json-rpc/application-factory", - { - "type": "category", - "label": "Portals", - "collapsed": true, - "items": [ - "rollups-apis/json-rpc/portals/ERC20Portal", - "rollups-apis/json-rpc/portals/ERC721Portal", - "rollups-apis/json-rpc/portals/ERC1155SinglePortal", - "rollups-apis/json-rpc/portals/ERC1155BatchPortal", - "rollups-apis/json-rpc/portals/EtherPortal" - ] - }, { - "type": "category", - "label": "Relayer", - "collapsed": true, - "items": [ - "rollups-apis/json-rpc/relays/relays" - ] + "type": "autogenerated", + "dirName": "api-reference/graphql/objects" } ] }, { "type": "category", - "label": "GraphQL API", + "label": "Filters", "collapsed": true, "items": [ - "rollups-apis/graphql/overview", - { - "type": "category", - "label": "Queries", - "collapsed": true, - "items": [ - "rollups-apis/graphql/queries/inputs", - "rollups-apis/graphql/queries/notices", - "rollups-apis/graphql/queries/vouchers", - "rollups-apis/graphql/queries/reports" - ] - }, { - "type": "category", - "label": "Objects", - "collapsed": true, - "items": [ - { - "type": "autogenerated", - "dirName": "rollups-apis/graphql/objects" - } - ] - }, - { - "type": "category", - "label": "Filters", - "collapsed": true, - "items": [ - { - "type": "autogenerated", - "dirName": "rollups-apis/graphql/inputs" - } - ] - }, + "type": "autogenerated", + "dirName": "api-reference/graphql/inputs" + } + ] + }, + { + "type": "category", + "label": "Scalars", + "collapsed": true, + "items": [ { - "type": "category", - "label": "Scalars", - "collapsed": true, - "items": [ - { - "type": "autogenerated", - "dirName": "rollups-apis/graphql/scalars" - } - ] + "type": "autogenerated", + "dirName": "api-reference/graphql/scalars" } ] } From 46f2b9005469f795d8650909c474c764c317d52b Mon Sep 17 00:00:00 2001 From: Shaheen Date: Mon, 14 Oct 2024 23:32:31 +0530 Subject: [PATCH 06/99] modified: overview & quickstart merged in side-bar (cherry picked from commit cbc95d8434ee818528b68c60ba9a124977ed2c6a) --- .../{ => getting-started}/index.md | 23 +++++++++++-------- .../{ => getting-started}/quickstart.md | 4 ++-- .../version-2.0-sidebars.json | 15 ++++++------ 3 files changed, 23 insertions(+), 19 deletions(-) rename cartesi-rollups_versioned_docs/version-2.0/{ => getting-started}/index.md (86%) rename cartesi-rollups_versioned_docs/version-2.0/{ => getting-started}/quickstart.md (95%) diff --git a/cartesi-rollups_versioned_docs/version-2.0/index.md b/cartesi-rollups_versioned_docs/version-2.0/getting-started/index.md similarity index 86% rename from cartesi-rollups_versioned_docs/version-2.0/index.md rename to cartesi-rollups_versioned_docs/version-2.0/getting-started/index.md index 10267c5e..84c59ee6 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/index.md +++ b/cartesi-rollups_versioned_docs/version-2.0/getting-started/index.md @@ -8,27 +8,30 @@ resources: title: Application-Specific Rollups --- + Welcome to Cartesi Rollups, where decentralized application development meets unprecedented flexibility. With a foundation built on the Linux operating system, Cartesi Rollups offers modular stacks that allow developers to tailor consensus, data availability, and settlement layers according to their project requirements. Utilizing the Cartesi Machine for transaction processing, developers can effortlessly implement sophisticated logic using their preferred programming language or tool. Explore the possibilities and streamline your decentralized application development journey with Cartesi Rollups. -![img](../../static/img/v1.3/image.png) +![img](../../../static/img/v1.3/image.png) + -## Introduction +## Introduction Let's delve into the workings of a Cartesi Rollup at a high level. -![img](../../static/img/v1.3/overview.jpg) +![img](../../../static/img/v1.3/overview.jpg) -At its core, the Cartesi Rollup executes the Cartesi Machine - a robust RISCV deterministic emulator running Linux OS - fueled by ordered inputs and custom application code. Inputs sourced from the data availability layer are read by the Cartesi Node, inside of which the Cartesi Machine processes them and generates outputs. After the optimistic rollup dispute window passes, these outputs are verifiable and possibly executable on the settlement layer. -The Cartesi Rollup framework is application-specific, assigning each dApp its rollup app chain and CPU while linking its optimistic rollups' consensus directly to the base layer. This structure ensures that validators—whether permissioned or not—can leverage the security features of the base layer, allowing any honest validator to enforce correct outcomes independently. +At its core, the Cartesi Rollup executes the Cartesi Machine - a robust RISCV deterministic emulator running Linux OS - fueled by ordered inputs and custom application code. Inputs sourced from the data availability layer are read by the Cartesi Node, inside of which the Cartesi Machine processes them and generates outputs. After the optimistic rollup dispute window passes, these outputs are verifiable and possibly executable on the settlement layer. + +The Cartesi Rollup framework is application-specific, assigning each dApp its rollup app chain and CPU while linking its optimistic rollups' consensus directly to the base layer. This structure ensures that validators—whether permissioned or not—can leverage the security features of the base layer, allowing any honest validator to enforce correct outcomes independently. In its simplest form, the Cartesi framework integrates tightly with the base layer, which serves as the sole platform for data availability, consensus, and settlement. Transactions are directed to specific smart contracts where DApp code is executed to produce outputs on the base layer after a verification period. ## Example -Below is a simple Node.js example demonstrating how to read an input and reply with a [notice](./api-reference/backend/notices.md) (a type of output). +Below is a simple Node.js example demonstrating how to read an input and reply with a [notice](../rollups-apis/backend/notices.md) (a type of output). ```javascript const { ethers } = require("ethers"); @@ -54,10 +57,12 @@ let finish = { status: "accept" }; ``` -Cartesi dApps are implemented as infinite loops that manage their transaction cycles through HTTP POST requests to the `/finish` endpoint to ensure flexibility across different programming languages and stacks. You can learn more about this abstraction [here](./api-reference/backend/introduction.md). +Cartesi dApps are implemented as infinite loops that manage their transaction cycles through HTTP POST requests to the `/finish` endpoint to ensure flexibility across different programming languages and stacks. You can learn more about this abstraction [here](../rollups-apis/backend/introduction.md). -In the Cartesi Rollup framework, all inputs sent to the base layer trigger an "advance_state" [request](./development/send-inputs.md#initiate-an-advance-request), which alters the state of the Cartesi Machine and consequently the Rollup. Since inputs originate on-chain, they are hex-encoded following the EVM message standard. +In the Cartesi Rollup framework, all inputs sent to the base layer trigger an "advance_state" [request](../development/send-inputs.md#initiate-an-advance-request), which alters the state of the Cartesi Machine and consequently the Rollup. Since inputs originate on-chain, they are hex-encoded following the EVM message standard. Notices can be understood as "provable" events; as such, they can be sent to an EVM chain to be verified, so they are also hex-encoded. + +Finally, in this example, no errors are treated; thus, we end the cycle accepting the input (`status: accept`). However, suppose any input causes the application to enter a faulty state or violates business logic. In that case, the application can end the process by calling the endpoint `/finish` with `status: reject` to revert the machine’s (and rollup’s) state before the arrival of the current input. + -Finally, in this example, no errors are treated; thus, we end the cycle accepting the input (`status: accept`). However, suppose any input causes the application to enter a faulty state or violates business logic. In that case, the application can end the process by calling the endpoint `/finish` with `status: reject` to revert the machine’s (and rollup’s) state before the arrival of the current input. diff --git a/cartesi-rollups_versioned_docs/version-2.0/quickstart.md b/cartesi-rollups_versioned_docs/version-2.0/getting-started/quickstart.md similarity index 95% rename from cartesi-rollups_versioned_docs/version-2.0/quickstart.md rename to cartesi-rollups_versioned_docs/version-2.0/getting-started/quickstart.md index ad9da384..5976bafd 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/quickstart.md +++ b/cartesi-rollups_versioned_docs/version-2.0/getting-started/quickstart.md @@ -120,13 +120,13 @@ You can obtain the relevant addresses by running `cartesi address-book`. You can create a custom frontend that interacts with your application. -Follow [the React.js tutorial to build a frontend for your application](./tutorials/react-frontend-application.md). +Follow [the React.js tutorial to build a frontend for your application](../tutorials/react-frontend-application.md). ## Deploy the application There are two methods to deploy an application: -1. [Self-hosted deployment](./deployment/self-hosted.md): Deploy the application node using your infrastructure. +1. [Self-hosted deployment](../deployment/self-hosted.md): Deploy the application node using your infrastructure. 2. Third-party service provider: Outsource running the application node to a service provider. diff --git a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json index 0a57a859..b10bce61 100644 --- a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json +++ b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json @@ -1,14 +1,13 @@ { "rollups": [ { - "type": "doc", - "id": "overview", - "label": "Overview" - }, - { - "type": "doc", - "id": "quickstart", - "label": "Quickstart" + "type": "category", + "label": "Getting Started", + "collapsed": true, + "items": [ + "getting-started/overview", + "getting-started/quickstart" + ] }, { "type": "category", From 5b4fabceaddd7fee5aed9ce843183663e55f25da Mon Sep 17 00:00:00 2001 From: Henrique Marlon Date: Tue, 15 Oct 2024 18:36:13 -0300 Subject: [PATCH 07/99] docs: fix the reference for api-reference inside of getting-started (cherry picked from commit ccaa69a71947d17c9c615f470306bea333427980) --- .../version-2.0/getting-started/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cartesi-rollups_versioned_docs/version-2.0/getting-started/index.md b/cartesi-rollups_versioned_docs/version-2.0/getting-started/index.md index 84c59ee6..84687d81 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/getting-started/index.md +++ b/cartesi-rollups_versioned_docs/version-2.0/getting-started/index.md @@ -31,7 +31,7 @@ In its simplest form, the Cartesi framework integrates tightly with the base lay ## Example -Below is a simple Node.js example demonstrating how to read an input and reply with a [notice](../rollups-apis/backend/notices.md) (a type of output). +Below is a simple Node.js example demonstrating how to read an input and reply with a [notice](../api-reference/backend/notices.md) (a type of output). ```javascript const { ethers } = require("ethers"); @@ -57,7 +57,7 @@ let finish = { status: "accept" }; ``` -Cartesi dApps are implemented as infinite loops that manage their transaction cycles through HTTP POST requests to the `/finish` endpoint to ensure flexibility across different programming languages and stacks. You can learn more about this abstraction [here](../rollups-apis/backend/introduction.md). +Cartesi dApps are implemented as infinite loops that manage their transaction cycles through HTTP POST requests to the `/finish` endpoint to ensure flexibility across different programming languages and stacks. You can learn more about this abstraction [here](../api-reference/backend/introduction.md). In the Cartesi Rollup framework, all inputs sent to the base layer trigger an "advance_state" [request](../development/send-inputs.md#initiate-an-advance-request), which alters the state of the Cartesi Machine and consequently the Rollup. Since inputs originate on-chain, they are hex-encoded following the EVM message standard. From 7a66206e27573efeb4b4e6d183ba40c67de20af3 Mon Sep 17 00:00:00 2001 From: Joao Garcia Date: Thu, 21 Nov 2024 13:00:02 +0700 Subject: [PATCH 08/99] remapped pages (cherry picked from commit 0acdd59f4b94ee3b317e9580d611bc20c18a03b5) --- .../architecture.md | 56 +++++++++++++++ .../version-2.0/api-reference/index.md | 2 +- .../core-concepts/optimistic-rollups.md | 70 ------------------- .../installation.md | 0 .../community-tools.md | 0 .../{core-concepts => resources}/mainnet.md | 0 .../migration-guide.md} | 4 +- .../version-2.0-sidebars.json | 41 +++++------ 8 files changed, 75 insertions(+), 98 deletions(-) rename cartesi-rollups_versioned_docs/version-2.0/{core-concepts => api-reference}/architecture.md (70%) delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/core-concepts/optimistic-rollups.md rename cartesi-rollups_versioned_docs/version-2.0/{development => getting-started}/installation.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{development => resources}/community-tools.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{core-concepts => resources}/mainnet.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{development/reference.md => resources/migration-guide.md} (99%) diff --git a/cartesi-rollups_versioned_docs/version-2.0/core-concepts/architecture.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/architecture.md similarity index 70% rename from cartesi-rollups_versioned_docs/version-2.0/core-concepts/architecture.md rename to cartesi-rollups_versioned_docs/version-2.0/api-reference/architecture.md index de831bfe..8febf875 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/core-concepts/architecture.md +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/architecture.md @@ -26,6 +26,61 @@ A decentralized application (dApp) built on Cartesi incorporates several key ele - Frontend: The application’s user-facing interface, typically implemented as a web application or a command-line interface tool. +## What is a Blockchain Rollup? + +A rollup is a blockchain scalability solution that offloads complex computations "off-chain," meaning they run on a separate computing environment (execution layer) outside the base layer, such as Ethereum. + +When employing rollups, the blockchain receives and logs transactions. In rare instances of an active attack or the involvement of a malicious agent, parties may disagree with a computation’s outcomes, and the blockchain will resolve these disputes. However, it's important to note that disagreements are not expected to occur under normal circumstances. + + +Users interact with a rollup through transactions on the base layer. They send messages (inputs) to the rollup on-chain smart contracts to define a computation to be processed and, as such, advance the state of the computing environment on the execution layer. Interested parties run an off-chain component (a node on the execution layer) that watches the blockchain for inputs, understanding, and executing the state updates. + +Once in a while, the state is checkpointed on the chain; at this point, it is considered finalized and can thus be accepted by any smart contract on the base layer. + +Ensuring this operation is secure is vital, meaning that the execution layer node must somehow prove the new state to the base layer. + +Consider this question: _"How does Ethereum know that the data posted by an off-chain L2 node is valid and was not submitted maliciously?"_ + +The answer depends on the rollup implementation, which falls within one of two categories according to the type of proof used: + +1. **Zero-knowledge Rollups (ZK Rollups)**, which use validity proofs. + +2. **Optimistic Rollups (ORs)**, which use fraud proofs. + +### Zero-knowledge Rollups (ZK Rollups) + +In ZK rollups, which use validity proof schemes, every state update is accompanied by a cryptographic proof created off-chain, attesting to its validity. The update is only taken if the proof successfully passes verification on-chain. Validity proofs(ZK Rollups) bring the enormous benefit of instant finality—as soon as a state update appears on-chain, it can be fully trusted and acted upon. + +The choice, however, also brings less than ideal properties: generating ZK proofs for general-purpose computations is, when possible, immensely expensive, and each on-chain state update must pay the extra gas fee for including and verifying a validity proof. + +### Optimistic Rollups (ORs) + +Optimistic Rollups, which use fraud-proof schemes, work by a different paradigm. State updates come unaccompanied by proofs; they’re proposed and, if not challenged, confirmed on-chain. Challenging a state update proposal using fraud proofs has two categories: **non-interactive** and **interactive**. + +Non-interactive refers to the fact that the challengers can prove that a state update is invalid in one step. With interactive fraud proofs, the claimer and challenger must, mediated by the blockchain, partake in something similar to a verification game. + +The assumption that state updates will likely be honest often gives solutions like this the name of Optimistic Rollups. + +This optimism is reinforced by financial incentives that reward honest behavior. Furthermore, any proposed false state will only be accepted if it remains undisputed for a prolonged period. + + +The main advantage of Optimistic Rollups is that they are much cheaper than ZK Rollups. Posting a state update on-chain is minimal, and challenging a state update is also low. + +The main disadvantage is that state updates take time to finalize and are not entirely accepted immediately. During this period, they are considered "optimistic" and can be challenged. + + +## Cartesi Rollups + +Cartesi's Optimistic Rollups adopt interactive fraud proofs to handle disputes. + +The base layer isn't burdened with executing all computations, allowing for more extensive computational tasks. + +Transactions and computations occur off-chain, leading to more intricate logic within transactions; hence, applications leverage powerful virtual machines (VMs) on the execution layer for complex computations. + +Cartesi's architecture specializes in app-specific rollups(appchains). Each dApp has its dedicated rollup for off-chain computation, enhancing scalability and performance. + + + ![img](../../../static/img/v1.5/architecture-overview.jpg) @@ -160,3 +215,4 @@ Dave introduces an approach where the resources required to defend against dispu With Dave, a single honest participant can effectively defend their claims on-chain, ensuring the integrity of transactions without relying on trust in validators. Based on the [Permissionless Refereed Tournaments algorithm](https://arxiv.org/abs/2212.12439), this protocol empowers anyone to validate rollups and uphold correct states on-chain, enhancing transaction security and reliability. Similar to how a consensus algorithm is crucial for achieving agreement on a single state of the blockchain among all nodes in a base-layer chain, Dave plays a fundamental role in ensuring the integrity and trustworthiness of state transitions within Cartesi Rollups. + 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 236643eb..f4f4a67d 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 @@ -31,7 +31,7 @@ You can send two requests to an application depending on whether you want to cha - **Inspect**: This involves making an external HTTP API call to the Cartesi Node to read the dApp state without changing it. -## Frontend APIs +## Base Layer Contracts The frontend component of the dApp needs to access the Cartesi Rollups framework to submit user requests and retrieve the corresponding outputs produced by the backend. diff --git a/cartesi-rollups_versioned_docs/version-2.0/core-concepts/optimistic-rollups.md b/cartesi-rollups_versioned_docs/version-2.0/core-concepts/optimistic-rollups.md deleted file mode 100644 index a83a7034..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/core-concepts/optimistic-rollups.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -id: optimistic-rollups -title: Optimistic Rollups -resources: - - url: https://ethereum.org/en/developers/docs/scaling/optimistic-rollups/ - title: Optimistic Rollups - - url: https://www.paradigm.xyz/2021/01/almost-everything-you-need-to-know-about-optimistic-rollup - title: Everything you need to know about Optimistic Rollups - - url: https://cartesi.io/blog/grokking-dave/ - title: Fraud-proof protocols | Grokking Dave ---- - -Cartesi implements a rollup design known as Optimistic Rollups. - -The combination of an Optimistic Rollups framework and the Cartesi Machine Emulator enables the development of dApps using any package or library available for Linux. - -## What is a Blockchain Rollup? - -A rollup is a blockchain scalability solution that offloads complex computations "off-chain," meaning they run on a separate computing environment (execution layer) outside the base layer, such as Ethereum. - -When employing rollups, the blockchain receives and logs transactions. In rare instances of an active attack or the involvement of a malicious agent, parties may disagree with a computation’s outcomes, and the blockchain will resolve these disputes. However, it's important to note that disagreements are not expected to occur under normal circumstances. - - -Users interact with a rollup through transactions on the base layer. They send messages (inputs) to the rollup on-chain smart contracts to define a computation to be processed and, as such, advance the state of the computing environment on the execution layer. Interested parties run an off-chain component (a node on the execution layer) that watches the blockchain for inputs, understanding, and executing the state updates. - -Once in a while, the state is checkpointed on the chain; at this point, it is considered finalized and can thus be accepted by any smart contract on the base layer. - -Ensuring this operation is secure is vital, meaning that the execution layer node must somehow prove the new state to the base layer. - -Consider this question: _"How does Ethereum know that the data posted by an off-chain L2 node is valid and was not submitted maliciously?"_ - -The answer depends on the rollup implementation, which falls within one of two categories according to the type of proof used: - -1. **Zero-knowledge Rollups (ZK Rollups)**, which use validity proofs. - -2. **Optimistic Rollups (ORs)**, which use fraud proofs. - -### Zero-knowledge Rollups (ZK Rollups) - -In ZK rollups, which use validity proof schemes, every state update is accompanied by a cryptographic proof created off-chain, attesting to its validity. The update is only taken if the proof successfully passes verification on-chain. Validity proofs(ZK Rollups) bring the enormous benefit of instant finality—as soon as a state update appears on-chain, it can be fully trusted and acted upon. - -The choice, however, also brings less than ideal properties: generating ZK proofs for general-purpose computations is, when possible, immensely expensive, and each on-chain state update must pay the extra gas fee for including and verifying a validity proof. - -### Optimistic Rollups (ORs) - -Optimistic Rollups, which use fraud-proof schemes, work by a different paradigm. State updates come unaccompanied by proofs; they’re proposed and, if not challenged, confirmed on-chain. Challenging a state update proposal using fraud proofs has two categories: **non-interactive** and **interactive**. - -Non-interactive refers to the fact that the challengers can prove that a state update is invalid in one step. With interactive fraud proofs, the claimer and challenger must, mediated by the blockchain, partake in something similar to a verification game. - -The assumption that state updates will likely be honest often gives solutions like this the name of Optimistic Rollups. - -This optimism is reinforced by financial incentives that reward honest behavior. Furthermore, any proposed false state will only be accepted if it remains undisputed for a prolonged period. - - -The main advantage of Optimistic Rollups is that they are much cheaper than ZK Rollups. Posting a state update on-chain is minimal, and challenging a state update is also low. - -The main disadvantage is that state updates take time to finalize and are not entirely accepted immediately. During this period, they are considered "optimistic" and can be challenged. - - -## Cartesi Rollups - -Cartesi's Optimistic Rollups adopt interactive fraud proofs to handle disputes. - -The base layer isn't burdened with executing all computations, allowing for more extensive computational tasks. - -Transactions and computations occur off-chain, leading to more intricate logic within transactions; hence, applications leverage powerful virtual machines (VMs) on the execution layer for complex computations. - -Cartesi's architecture specializes in app-specific rollups(appchains). Each dApp has its dedicated rollup for off-chain computation, enhancing scalability and performance. - - diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/installation.md b/cartesi-rollups_versioned_docs/version-2.0/getting-started/installation.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/development/installation.md rename to cartesi-rollups_versioned_docs/version-2.0/getting-started/installation.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/community-tools.md b/cartesi-rollups_versioned_docs/version-2.0/resources/community-tools.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/development/community-tools.md rename to cartesi-rollups_versioned_docs/version-2.0/resources/community-tools.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/core-concepts/mainnet.md b/cartesi-rollups_versioned_docs/version-2.0/resources/mainnet.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/core-concepts/mainnet.md rename to cartesi-rollups_versioned_docs/version-2.0/resources/mainnet.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/reference.md b/cartesi-rollups_versioned_docs/version-2.0/resources/migration-guide.md similarity index 99% rename from cartesi-rollups_versioned_docs/version-2.0/development/reference.md rename to cartesi-rollups_versioned_docs/version-2.0/resources/migration-guide.md index 587555f4..c99c63b0 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/development/reference.md +++ b/cartesi-rollups_versioned_docs/version-2.0/resources/migration-guide.md @@ -1,6 +1,6 @@ --- -id: reference -title: Reference +id: migration-guide +title: Migration Guide --- ## Migrating from Cartesi Rollups Node v1.4 to v1.5.x diff --git a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json index b10bce61..81758f7b 100644 --- a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json +++ b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json @@ -6,17 +6,8 @@ "collapsed": true, "items": [ "getting-started/overview", - "getting-started/quickstart" - ] - }, - { - "type": "category", - "label": "Core Concepts", - "collapsed": true, - "items": [ - "core-concepts/optimistic-rollups", - "core-concepts/architecture", - "core-concepts/mainnet-considerations" + "getting-started/quickstart", + "getting-started/installation" ] }, { @@ -25,6 +16,7 @@ "collapsed": true, "items": [ "api-reference/http-api", + "api-reference/architecture", { "type": "category", "label": "Base Layer Contracts", @@ -39,19 +31,11 @@ "label": "Portals", "collapsed": true, "items": [ + "api-reference/json-rpc/portals/EtherPortal", "api-reference/json-rpc/portals/ERC20Portal", "api-reference/json-rpc/portals/ERC721Portal", "api-reference/json-rpc/portals/ERC1155SinglePortal", - "api-reference/json-rpc/portals/ERC1155BatchPortal", - "api-reference/json-rpc/portals/EtherPortal" - ] - }, - { - "type": "category", - "label": "Relayer", - "collapsed": true, - "items": [ - "api-reference/json-rpc/relays/relays" + "api-reference/json-rpc/portals/ERC1155BatchPortal" ] } ] @@ -126,14 +110,11 @@ "label": "Development", "collapsed": true, "items": [ - "development/installation", "development/building-a-dapp", - "development/cli-commands", "development/send-inputs", "development/query-outputs", "development/asset-handling", - "development/reference", - "development/community-tools" + "development/cli-commands" ] }, { @@ -158,6 +139,16 @@ "tutorials/cli-account-abstraction-feauture" ] }, + { + "type": "category", + "label": "Resources", + "collapsed": true, + "items": [ + "resources/community-tools", + "resources/mainnet-considerations", + "resources/migration-guide" + ] + }, { "type": "category", "label": "References", From e43d14f8fc95b042bfb31dd65205bfd9d22ac508 Mon Sep 17 00:00:00 2001 From: Joao Garcia Date: Thu, 21 Nov 2024 14:00:19 +0700 Subject: [PATCH 09/99] setting redirects (cherry picked from commit 2644c4783afec32dd400a9c02bca06520aa2044d) --- .../version-2.0/tutorials/calculator.md | 2 +- .../version-2.0/tutorials/ether-wallet.md | 2 +- docusaurus.config.js | 56 +++++++++++++++++++ 3 files changed, 58 insertions(+), 2 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 52a922cc..204c52ce 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/tutorials/calculator.md +++ b/cartesi-rollups_versioned_docs/version-2.0/tutorials/calculator.md @@ -18,7 +18,7 @@ The backend will be written using Python. For added flexibility, feel free to ex Install these to set up your environment for quick building: -- Cartesi CLI: A simple tool for building applications on Cartesi. [Install Cartesi CLI for your OS of choice](../development/installation.md). +- Cartesi CLI: A simple tool for building applications on Cartesi. [Install Cartesi CLI for your OS of choice](../getting-started/installation.md). - Docker Desktop 4.x: The tool you need to run the Cartesi Machine and its dependencies. [Install Docker for your OS of choice](https://www.docker.com/products/docker-desktop/). 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 b7199ab5..a1e42d9d 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 @@ -16,7 +16,7 @@ This tutorial is for educational purposes. For production dApps, we recommend us ## Setting up the project -First, create a new TypeScript project using the [Cartesi CLI](../development/installation.md/#cartesi-cli). +First, create a new TypeScript project using the [Cartesi CLI](../getting-started/installation.md/#cartesi-cli). ```bash cartesi create ether-wallet-dapp --template typescript diff --git a/docusaurus.config.js b/docusaurus.config.js index 57c4f471..21775972 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -363,6 +363,10 @@ const config = { label: "1.5", path: "1.5", }, + "2.0": { + label: "2.0", + path: "2.0", + } }, showLastUpdateTime: true, }, @@ -440,6 +444,58 @@ const config = { to: "/cartesi-machine/blockchain/vg/", from: ["/machine/blockchain/vg/"], }, + { + to: "/cartesi-rollups/2.0/getting-started/quickstart/", + from: "/cartesi-rollups/2.0/quickstart/", + }, + { + to: "/cartesi-rollups/2.0/getting-started/installation/", + from: "/cartesi-rollups/2.0/development/installation/", + }, + { + to: "/cartesi-rollups/2.0/api-reference/architecture/", + from: "/cartesi-rollups/2.0/core-concepts/optimistic-rollups/", + }, + { + to: "/cartesi-rollups/2.0/api-reference/architecture/", + from: "/cartesi-rollups/2.0/core-concepts/architecture/", + }, + { + to: "/cartesi-rollups/2.0/resources/mainnet-considerations/", + from: "/cartesi-rollups/2.0/core-concepts/mainnet-considerations/", + }, + { + to: "/cartesi-rollups/2.0/getting-started/installation/", + from: "/cartesi-rollups/2.0/development/installation/", + }, + { + to: "/cartesi-rollups/2.0/development/building-a-dapp/", + from: "/cartesi-rollups/2.0/development/creating-application/", + }, + { + to: "/cartesi-rollups/2.0/development/building-a-dapp/", + from: "/cartesi-rollups/2.0/development/building-the-application/", + }, + { + to: "/cartesi-rollups/2.0/development/building-a-dapp/", + from: "/cartesi-rollups/2.0/development/running-the-application/", + }, + { + to: "/cartesi-rollups/2.0/development/send-inputs/", + from: "/cartesi-rollups/2.0/development/send-requests/", + }, + { + to: "/cartesi-rollups/2.0/development/query-outputs/", + from: "/cartesi-rollups/2.0/development/retrieve-outputs/", + }, + { + to: "/cartesi-rollups/2.0/resources/migration-guide/", + from: "/cartesi-rollups/2.0/development/migration/", + }, + { + to: "/cartesi-rollups/2.0/resources/community-tools/", + from: "/cartesi-rollups/2.0/development/community-tools/", + } ], createRedirects(existingPath) { if (existingPath.includes("/cartesi-rollups/1.0/")) { From c97a5ad6eb8b5820a0426d345e506a092027c451 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Thu, 10 Oct 2024 23:32:00 +0100 Subject: [PATCH 10/99] add: implemented a sidebar file for V2 (cherry picked from commit 588a9db6de7750a84c8cfcc8558a63642c6b8208) --- .../version-2.0-sidebars.json | 167 ++++++++++-------- 1 file changed, 92 insertions(+), 75 deletions(-) diff --git a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json index 81758f7b..288c2b9d 100644 --- a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json +++ b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json @@ -1,103 +1,127 @@ { "rollups": [ + { + "type": "doc", + "id": "overview", + "label": "Overview" + }, + { + "type": "doc", + "id": "quickstart", + "label": "Quickstart" + }, { "type": "category", - "label": "Getting Started", + "label": "Core Concepts", "collapsed": true, "items": [ - "getting-started/overview", - "getting-started/quickstart", - "getting-started/installation" + "core-concepts/optimistic-rollups", + "core-concepts/architecture", + "core-concepts/mainnet-considerations" ] }, { "type": "category", - "label": "API Reference", + "label": "Rollups APIs", "collapsed": true, "items": [ - "api-reference/http-api", - "api-reference/architecture", + "rollups-apis/http-api", { "type": "category", - "label": "Base Layer Contracts", + "label": "Backend APIs", "collapsed": true, "items": [ - "api-reference/json-rpc/overview", - "api-reference/json-rpc/input-box", - "api-reference/json-rpc/application", - "api-reference/json-rpc/application-factory", - { - "type": "category", - "label": "Portals", - "collapsed": true, - "items": [ - "api-reference/json-rpc/portals/EtherPortal", - "api-reference/json-rpc/portals/ERC20Portal", - "api-reference/json-rpc/portals/ERC721Portal", - "api-reference/json-rpc/portals/ERC1155SinglePortal", - "api-reference/json-rpc/portals/ERC1155BatchPortal" - ] - } + "rollups-apis/backend/introduction", + "rollups-apis/backend/notices", + "rollups-apis/backend/vouchers", + "rollups-apis/backend/reports" ] }, { "type": "category", - "label": "Backend API", + "label": "Frontend APIs", "collapsed": true, "items": [ - "api-reference/backend/introduction", - "api-reference/backend/notices", - "api-reference/backend/vouchers", - "api-reference/backend/reports" - ] - }, - { - "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/vouchers", - "api-reference/graphql/queries/reports" - ] - }, { "type": "category", - "label": "Objects", + "label": "Smart contracts API", "collapsed": true, "items": [ + "rollups-apis/json-rpc/overview", + "rollups-apis/json-rpc/input-box", + "rollups-apis/json-rpc/application", + "rollups-apis/json-rpc/application-factory", { - "type": "autogenerated", - "dirName": "api-reference/graphql/objects" - } - ] - }, - { - "type": "category", - "label": "Filters", - "collapsed": true, - "items": [ + "type": "category", + "label": "Portals", + "collapsed": true, + "items": [ + "rollups-apis/json-rpc/portals/ERC20Portal", + "rollups-apis/json-rpc/portals/ERC721Portal", + "rollups-apis/json-rpc/portals/ERC1155SinglePortal", + "rollups-apis/json-rpc/portals/ERC1155BatchPortal", + "rollups-apis/json-rpc/portals/EtherPortal" + ] + }, { - "type": "autogenerated", - "dirName": "api-reference/graphql/inputs" + "type": "category", + "label": "Relayer", + "collapsed": true, + "items": [ + "rollups-apis/json-rpc/relays/relays" + ] } ] }, { "type": "category", - "label": "Scalars", + "label": "GraphQL API", "collapsed": true, "items": [ + "rollups-apis/graphql/overview", { - "type": "autogenerated", - "dirName": "api-reference/graphql/scalars" + "type": "category", + "label": "Queries", + "collapsed": true, + "items": [ + "rollups-apis/graphql/queries/inputs", + "rollups-apis/graphql/queries/notices", + "rollups-apis/graphql/queries/vouchers", + "rollups-apis/graphql/queries/reports" + ] + }, + { + "type": "category", + "label": "Objects", + "collapsed": true, + "items": [ + { + "type": "autogenerated", + "dirName": "rollups-apis/graphql/objects" + } + ] + }, + { + "type": "category", + "label": "Filters", + "collapsed": true, + "items": [ + { + "type": "autogenerated", + "dirName": "rollups-apis/graphql/inputs" + } + ] + }, + { + "type": "category", + "label": "Scalars", + "collapsed": true, + "items": [ + { + "type": "autogenerated", + "dirName": "rollups-apis/graphql/scalars" + } + ] } ] } @@ -110,11 +134,14 @@ "label": "Development", "collapsed": true, "items": [ + "development/installation", "development/building-a-dapp", + "development/cli-commands", "development/send-inputs", "development/query-outputs", "development/asset-handling", - "development/cli-commands" + "development/reference", + "development/community-tools" ] }, { @@ -139,16 +166,6 @@ "tutorials/cli-account-abstraction-feauture" ] }, - { - "type": "category", - "label": "Resources", - "collapsed": true, - "items": [ - "resources/community-tools", - "resources/mainnet-considerations", - "resources/migration-guide" - ] - }, { "type": "category", "label": "References", From 1a2fb67b6c3491193068668be21a4ece2f461b92 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Thu, 10 Oct 2024 23:37:17 +0100 Subject: [PATCH 11/99] update: Modified the Development folder for V2 (cherry picked from commit 594b7a7859d7651819d3964db8406324267bb26c) --- .../version-2.0/development/reference.md | 114 ++++++++++++++++++ .../version-2.0/resources/migration-guide.md | 4 +- 2 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 cartesi-rollups_versioned_docs/version-2.0/development/reference.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/reference.md b/cartesi-rollups_versioned_docs/version-2.0/development/reference.md new file mode 100644 index 00000000..587555f4 --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-2.0/development/reference.md @@ -0,0 +1,114 @@ +--- +id: reference +title: Reference +--- + +## Migrating from Cartesi Rollups Node v1.4 to v1.5.x + +Release `v1.5.0` introduces a critical change in how epochs are closed in the Cartesi Rollups Node, transitioning from a **timestamp-based system to a block number-based system**. + +Epoch closure is now determined by the `CARTESI_EPOCH_LENGTH` environment variable (in blocks) instead of `CARTESI_EPOCH_DURATION` (in seconds). + +```plaintext +CARTESI_EPOCH_LENGTH = CARTESI_EPOCH_DURATION / BLOCK_TIME +``` + +Where `BLOCK_TIME` is the time to generate a block in the target network. + +**Example**: If a block is generated every 12 seconds and `CARTESI_EPOCH_DURATION` is set to 86400 seconds (24 hours), `CARTESI_EPOCH_LENGTH = 86400 / 12 = 7200` + +### Option 1: Redeploy all contracts + +Redeploy all contracts and your application with the new configuration. + +Refer to the [self-hosted deployment guide](../deployment/self-hosted.md) for detailed deployment instructions. + +:::caution +Redeploying creates a new application instance. All previous inputs, outputs, claims, and funds locked in the application contract will remain associated with the old application address. +::: + +### Option 2: Replace Application's History + +This process allows inputs, outputs, and locked funds to remain unchanged but is more involved. + +:::note +A new _History_ will have no claims. Upon restarting the node with a new _History_, previous claims will be resubmitted, incurring additional costs for the _Authority_ owner. +::: + +:::caution +Instances of the [_History_](https://github.com/cartesi/rollups-contracts/blob/v1.2.0/onchain/rollups/contracts/history/History.sol) contract from [rollups-contracts v1.2.0](https://github.com/cartesi/rollups-contracts/releases/tag/v1.2.0) may be used simultaneously by several applications through their associated _Authority_ instance. +Application owners must consider that and exercise care when performing the steps listed below. +::: + +It's recommended to use the deterministic deployment functions available in the Rollups contracts. + +#### 1. Define the environment variables + +- `SALT`: A random 32-byte value for the deterministic deployment functions. +- `RPC_URL`: The RPC endpoint to be used. +- `MNEMONIC`: The mnemonic phrase for the Authority owner's wallet (other wallet options may be used). +- `HISTORY_FACTORY_ADDRESS`: The address of a valid HistoryFactory instance. +- `AUTHORITY_ADDRESS`: The address of the Authority instance used by the application. + + :::note environment variables + A `HistoryFactory` is deployed at `0x1f158b5320BBf677FdA89F9a438df99BbE560A26` for all supported networks, including Ethereum, Optimism, Arbitrum, Base, and their respective Sepolia-based testnets. + ::: + +#### 2. Instantiate a New _History_ + +This is a two-step process. First calculate the address of the new History. After that, the new instance of History may be created. + +- To calculate the address of a new _History_ contract call the `calculateHistoryAddress(address,bytes32)(address)` function with the help of Foundry's Cast: + + ```shell + cast call \ + --trace --verbose \ + $HISTORY_FACTORY_ADDRESS \ + "calculateHistoryAddress(address,bytes32)(address)" \ + $AUTHORITY_ADDRESS \ + $SALT \ + --rpc-url "$RPC_URL" + ``` + + If the command executes successfully, it will display the address of the new History contract. Store this address in the environment variable `NEW_HISTORY_ADDRESS` for later use. + +- Create a new instance of _History_ may be created by calling function `newHistory(address,bytes32)`: + + ```shell + cast send \ + --json \ + --mnemonic "$MNEMONIC" \ + $HISTORY_FACTORY_ADDRESS \ + "newHistory(address,bytes32)(History)" \ + $AUTHORITY_ADDRESS \ + $SALT \ + --rpc-url "$RPC_URL" + ``` + + The `cast send` command will fail if Cast does not recognize the _History_ type during execution. In such cases, replace _History_ with `address` as the return type for `newHistory()` and execute the command again. + + The `cast send` command may also fail due to gas estimation issues. To circumvent this, provide gas constraints with the `--gas-limit` parameter (e.g., `--gas-limit 7000000`). + +#### 3. Replace the _History_ + +Ensure the environment variables from the previous step are set, including `NEW_HISTORY_ADDRESS`, which should have the address of the new History. + +To replace the _History_ used by the _Authority_, run this command: + +```shell +cast send \ + --json \ + --mnemonic "$MNEMONIC" \ + "$AUTHORITY_ADDRESS" \ + "setHistory(address)" \ + "$NEW_HISTORY_ADDRESS" \ + --rpc-url "$RPC_URL" +``` + +After replacing the _History_, update the `CARTESI_CONTRACTS_HISTORY_ADDRESS` in the application configuration with the new _History_ address. Then, upgrade the Cartesi Rollups Node as usual. + +When the Cartesi Rollups Node restarts, it processes all existing inputs, recalculates the epochs, and sends the claims to the new _History_ based on the updated configuration. + + + + 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 c99c63b0..587555f4 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 @@ -1,6 +1,6 @@ --- -id: migration-guide -title: Migration Guide +id: reference +title: Reference --- ## Migrating from Cartesi Rollups Node v1.4 to v1.5.x From ddd1d83e66020f46bbf1d1c78cbc0c6d8b296152 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Fri, 11 Oct 2024 17:22:07 +0100 Subject: [PATCH 12/99] fix: Handled error relating to broken links (cherry picked from commit 2478b6c5683c63034039907227d201f331e4a1de) --- .../version-2.0/getting-started/index.md | 15 +++++---------- .../version-2.0/tutorials/ether-wallet.md | 2 +- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/cartesi-rollups_versioned_docs/version-2.0/getting-started/index.md b/cartesi-rollups_versioned_docs/version-2.0/getting-started/index.md index 84687d81..44c44318 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/getting-started/index.md +++ b/cartesi-rollups_versioned_docs/version-2.0/getting-started/index.md @@ -8,24 +8,21 @@ resources: title: Application-Specific Rollups --- - Welcome to Cartesi Rollups, where decentralized application development meets unprecedented flexibility. With a foundation built on the Linux operating system, Cartesi Rollups offers modular stacks that allow developers to tailor consensus, data availability, and settlement layers according to their project requirements. Utilizing the Cartesi Machine for transaction processing, developers can effortlessly implement sophisticated logic using their preferred programming language or tool. Explore the possibilities and streamline your decentralized application development journey with Cartesi Rollups. ![img](../../../static/img/v1.3/image.png) - -## Introduction +## Introduction Let's delve into the workings of a Cartesi Rollup at a high level. ![img](../../../static/img/v1.3/overview.jpg) +At its core, the Cartesi Rollup executes the Cartesi Machine - a robust RISCV deterministic emulator running Linux OS - fueled by ordered inputs and custom application code. Inputs sourced from the data availability layer are read by the Cartesi Node, inside of which the Cartesi Machine processes them and generates outputs. After the optimistic rollup dispute window passes, these outputs are verifiable and possibly executable on the settlement layer. -At its core, the Cartesi Rollup executes the Cartesi Machine - a robust RISCV deterministic emulator running Linux OS - fueled by ordered inputs and custom application code. Inputs sourced from the data availability layer are read by the Cartesi Node, inside of which the Cartesi Machine processes them and generates outputs. After the optimistic rollup dispute window passes, these outputs are verifiable and possibly executable on the settlement layer. - -The Cartesi Rollup framework is application-specific, assigning each dApp its rollup app chain and CPU while linking its optimistic rollups' consensus directly to the base layer. This structure ensures that validators—whether permissioned or not—can leverage the security features of the base layer, allowing any honest validator to enforce correct outcomes independently. +The Cartesi Rollup framework is application-specific, assigning each dApp its rollup app chain and CPU while linking its optimistic rollups' consensus directly to the base layer. This structure ensures that validators—whether permissioned or not—can leverage the security features of the base layer, allowing any honest validator to enforce correct outcomes independently. In its simplest form, the Cartesi framework integrates tightly with the base layer, which serves as the sole platform for data availability, consensus, and settlement. Transactions are directed to specific smart contracts where DApp code is executed to produce outputs on the base layer after a verification period. @@ -59,10 +56,8 @@ let finish = { status: "accept" }; Cartesi dApps are implemented as infinite loops that manage their transaction cycles through HTTP POST requests to the `/finish` endpoint to ensure flexibility across different programming languages and stacks. You can learn more about this abstraction [here](../api-reference/backend/introduction.md). -In the Cartesi Rollup framework, all inputs sent to the base layer trigger an "advance_state" [request](../development/send-inputs.md#initiate-an-advance-request), which alters the state of the Cartesi Machine and consequently the Rollup. Since inputs originate on-chain, they are hex-encoded following the EVM message standard. +In the Cartesi Rollup framework, all inputs sent to the base layer trigger an "advance_state" [request](./development/send-inputs.md#initiate-an-advance-request), which alters the state of the Cartesi Machine and consequently the Rollup. Since inputs originate on-chain, they are hex-encoded following the EVM message standard. Notices can be understood as "provable" events; as such, they can be sent to an EVM chain to be verified, so they are also hex-encoded. - -Finally, in this example, no errors are treated; thus, we end the cycle accepting the input (`status: accept`). However, suppose any input causes the application to enter a faulty state or violates business logic. In that case, the application can end the process by calling the endpoint `/finish` with `status: reject` to revert the machine’s (and rollup’s) state before the arrival of the current input. - +Finally, in this example, no errors are treated; thus, we end the cycle accepting the input (`status: accept`). However, suppose any input causes the application to enter a faulty state or violates business logic. In that case, the application can end the process by calling the endpoint `/finish` with `status: reject` to revert the machine’s (and rollup’s) state before the arrival of the current input. 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 a1e42d9d..a216dcdf 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 @@ -536,4 +536,4 @@ For end-to-end functionality, developers will likely build their [custom user-fa When you run your application with `cartesi run`, there is a local instance of CartesiScan on `http://localhost:8080/explorer`. -You can execute your vouchers via the explorer, which completes the withdrawal process at the end of [an epoch](../api-reference/backend/vouchers.md/#epoch-configuration). +You can execute your vouchers via the explorer, which completes the withdrawal process at the end of [an epoch](../rollups-apis/backend/vouchers.md/#epoch-configuration). From 3ed551cf590977b45d04b04811bfe2ef5d511bdc Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Mon, 18 Nov 2024 11:53:44 +0100 Subject: [PATCH 13/99] update: modified application factory contract in line with V2.0 (cherry picked from commit 2f2d7c8ac7f72759937f120cc8856d5c2335763b) --- .../json-rpc/application-factory.md | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/application-factory.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/application-factory.md index 2e0bf3b0..63bd1a53 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/application-factory.md +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/application-factory.md @@ -1,22 +1,21 @@ --- id: application-factory -title: CartesiDAppFactory +title: ApplicationFactory resources: - - url: https://github.com/cartesi/rollups-contracts/blob/v1.4.0/onchain/rollups/contracts/dapp/CartesiDAppFactory.sol - title: CartesiDAppFactory contract + - url: https://github.com/cartesi/rollups-contracts/blob/prerelease/2.0.0/contracts/dapp/ApplicationFactory.sol + title: Application Factory contract --- -The **CartesiDAppFactory** contract is a tool for reliably deploying new instances of the [`CartesiDApp`](../json-rpc/application.md) contract with or without a specified salt value for address derivation. +The **ApplicationFactory** contract is a tool for reliably deploying new instances of the [`CartesiDApp`](../json-rpc/application.md) contract with or without a specified salt value for address derivation. Additionally, it provides a function to calculate the address of a potential new `CartesiDApp` contract based on input parameters. This contract ensures efficient and secure deployment of `CartesiDApp` contracts within the Cartesi Rollups framework. - -## `newApplication()` +## `newApplication()` ```solidity -function newApplication( IConsensus consensus, IInputBox inputBox, IPortal[] memory portals, address appOwner, bytes32 templateHash ) external returns (Application) +function newApplication( IConsensus consensus, address appOwner, bytes32 templateHash) external override returns (IApplication) ``` Deploys a new Application contract without specifying a salt value for address derivation. @@ -28,15 +27,13 @@ Emits an `ApplicationCreated` event upon successful deployment. | Name | Type | Description | | ------------ | ---------- | ---------------------------------------- | | consensus | IConsensus | Instance of the consensus interface | -| inputBox | IInputBox | Instance of the input box interface | -| portals | IPortal[] | Array of portal instances | | appOwner | address | Address of the owner of the application | | templateHash | bytes32 | Hash of the template for the application | ## `newApplication()`(with salt) ```solidity -function newApplication(IConsensus consensus, IInputBox inputBox, IPortal[] memory portals, address appOwner, bytes32 templateHash, bytes32 salt) external returns (Application) +function newApplication( IConsensus consensus, address appOwner, bytes32 templateHash, bytes32 salt ) external override returns (IApplication) ``` Deploys a new `Application` contract with a specified salt value for address derivation. @@ -48,8 +45,6 @@ Emits an `ApplicationCreated` event upon successful deployment. | Name | Type | Description | | ------------ | ---------- | ---------------------------------------- | | consensus | IConsensus | Instance of the consensus interface | -| inputBox | IInputBox | Instance of the input box interface | -| portals | IPortal[] | Array of portal instances | | appOwner | address | Address of the owner of the application | | templateHash | bytes32 | Hash of the template for the application | | salt | bytes32 | Salt value for address derivation | @@ -57,7 +52,7 @@ Emits an `ApplicationCreated` event upon successful deployment. ### `calculateApplicationAddress()` ```solidity -function calculateApplicationAddress(IConsensus consensus,IInputBox inputBox,IPortal[] memory portals,address appOwner,bytes32 templateHash,bytes32 salt ) external view returns (address) +function calculateApplicationAddress( IConsensus consensus, address appOwner, bytes32 templateHash, bytes32 salt ) external view override returns (address) ``` Calculates the address of a potential new Application contract based on input parameters. @@ -67,8 +62,6 @@ Calculates the address of a potential new Application contract based on input pa | Name | Type | Description | | ------------ | ---------- | ---------------------------------------- | | consensus | IConsensus | Instance of the consensus interface | -| inputBox | IInputBox | Instance of the input box interface | -| portals | IPortal[] | Array of portal instances | | appOwner | address | Address of the owner of the application | | templateHash | bytes32 | Hash of the template for the application | | salt | bytes32 | Salt value for address derivation | From 9d90901f661d84f74d82055a2dc75e5ae01879e4 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Mon, 18 Nov 2024 12:34:55 +0100 Subject: [PATCH 14/99] update: modified the different portals page in line with docs V2 (cherry picked from commit aa4e7d28d52aa51ef1e302283546d61b2cdfe025) --- .../json-rpc/portals/ERC1155BatchPortal.md | 20 +++++++++---------- .../json-rpc/portals/ERC1155SinglePortal.md | 19 +++++++++--------- .../json-rpc/portals/ERC20Portal.md | 14 ++++++------- .../json-rpc/portals/ERC721Portal.md | 16 +++++++-------- .../json-rpc/portals/EtherPortal.md | 10 +++++----- 5 files changed, 40 insertions(+), 39 deletions(-) diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC1155BatchPortal.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC1155BatchPortal.md index eb278e6c..e91d657d 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC1155BatchPortal.md +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC1155BatchPortal.md @@ -10,7 +10,7 @@ ERC-1155 tokens to a dApp while informing the off-chain machine. ## `depositBatchERC1155Token()` ```solidity -function depositBatchERC1155Token(contract IERC1155 _token, address _dapp, uint256[] _tokenIds, uint256[] _values, bytes _baseLayerData, bytes _execLayerData) external +function depositBatchERC1155Token( IERC1155 token, address appContract, uint256[] calldata tokenIds, uint256[] calldata values, bytes calldata baseLayerData, bytes calldata execLayerData) external; ``` Transfer a batch of ERC-1155 tokens to a dApp and add an input to @@ -19,15 +19,15 @@ the dApp's input box to signal such operation. The caller must enable approval for the portal to manage all of their tokens beforehand, by calling the `setApprovalForAll` function in the token contract. -_Please make sure `_tokenIds` and `_values` have the same length._ +_Please make sure `tokenIds` and `values` have the same length._ #### Parameters -| Name | Type | Description | -| --------------- | ----------------- | -------------------------------------------------------- | -| \_token | contract IERC1155 | The ERC-1155 token contract | -| \_dapp | address | The address of the dApp | -| \_tokenIds | uint256[] | The identifiers of the tokens being transferred | -| \_values | uint256[] | Transfer amounts per token type | -| \_baseLayerData | bytes | Additional data to be interpreted by the base layer | -| \_execLayerData | bytes | Additional data to be interpreted by the execution layer | +| Name | Type | Description | +| ------------- | --------- | -------------------------------------------------------- | +| token | IERC1155 | The ERC-1155 token contract | +| appContract | address | The address of the dApp | +| tokenIds | uint256[] | The identifiers of the tokens being transferred | +| values | uint256[] | Transfer amounts per token type | +| baseLayerData | bytes | Additional data to be interpreted by the base layer | +| execLayerData | bytes | Additional data to be interpreted by the execution layer | diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC1155SinglePortal.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC1155SinglePortal.md index 1ced9b32..ad41581a 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC1155SinglePortal.md +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC1155SinglePortal.md @@ -9,7 +9,8 @@ The **ERC1155SinglePortal** allows anyone to perform single transfers of ERC-115 ### `depositSingleERC1155Token()` ```solidity -function depositSingleERC1155Token(contract IERC1155 _token, address _dapp, uint256 _tokenId, uint256 _value, bytes _baseLayerData, bytes _execLayerData) external +function depositSingleERC1155Token( IERC1155 token, address appContract, uint256 tokenId, uint256 value, bytes calldata baseLayerData, bytes calldata execLayerData) external; + ``` Transfer an ERC-1155 token to a dApp and add an input to @@ -20,11 +21,11 @@ beforehand, by calling the `setApprovalForAll` function in the token contract. #### Parameters -| Name | Type | Description | -| --------------- | ----------------- | -------------------------------------------------------- | -| \_token | contract IERC1155 | The ERC-1155 token contract | -| \_dapp | address | The address of the dApp | -| \_tokenId | uint256 | The identifier of the token being transferred | -| \_value | uint256 | Transfer amount | -| \_baseLayerData | bytes | Additional data to be interpreted by the base layer | -| \_execLayerData | bytes | Additional data to be interpreted by the execution layer | +| Name | Type | Description | +| ------------- | -------- | -------------------------------------------------------- | +| token | IERC1155 | The ERC-1155 token contract | +| appContract | address | The address of the dApp | +| tokenId | uint256 | The identifier of the token being transferred | +| value | uint256 | Transfer amount | +| baseLayerData | bytes | Additional data to be interpreted by the base layer | +| execLayerData | bytes | Additional data to be interpreted by the execution layer | diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC20Portal.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC20Portal.md index cc175d79..ded704b1 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC20Portal.md +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC20Portal.md @@ -10,7 +10,7 @@ ERC-20 tokens to a dApp while informing the off-chain machine. ## `depositERC20Tokens()` ```solidity -function depositERC20Tokens(contract IERC20 _token, address _dapp, uint256 _amount, bytes _execLayerData) external +function depositERC20Tokens(IERC20 token, address appContract, uint256 value, bytes calldata execLayerData) external; ``` Transfer ERC-20 tokens to a dApp and add an input to @@ -22,9 +22,9 @@ token contract. #### Parameters -| Name | Type | Description | -| --------------- | --------------- | -------------------------------------------------------- | -| \_token | contract IERC20 | The ERC-20 token contract | -| \_dapp | address | The address of the dApp | -| \_amount | uint256 | The amount of tokens to be transferred | -| \_execLayerData | bytes | Additional data to be interpreted by the execution layer | +| Name | Type | Description | +| ------------- | ------- | -------------------------------------------------------- | +| token | IERC20 | The ERC-20 token contract address | +| appContract | address | The address of the dApp | +| value | uint256 | The amount of tokens to be transferred | +| execLayerData | bytes | Additional data to be interpreted by the execution layer | diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC721Portal.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC721Portal.md index b005fb92..443f7cd5 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC721Portal.md +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/ERC721Portal.md @@ -10,7 +10,7 @@ ERC-721 tokens to a dApp while informing the off-chain machine. ## `depositERC721Token()` ```solidity -function depositERC721Token(contract IERC721 _token, address _dapp, uint256 _tokenId, bytes _baseLayerData, bytes _execLayerData) external +function depositERC721Token( IERC721 token, address appContract, uint256 tokenId, bytes baseLayerData, bytes execLayerData) external ``` Transfer an ERC-721 token to a dApp and add an input to @@ -22,10 +22,10 @@ token contract. #### Parameters -| Name | Type | Description | -| --------------- | ---------------- | -------------------------------------------------------- | -| \_token | contract IERC721 | The ERC-721 token contract | -| \_dapp | address | The address of the dApp | -| \_tokenId | uint256 | The identifier of the token being transferred | -| \_baseLayerData | bytes | Additional data to be interpreted by the base layer | -| \_execLayerData | bytes | Additional data to be interpreted by the execution layer | +| Name | Type | Description | +| ------------- | ------- | -------------------------------------------------------- | +| token | IERC721 | The ERC-721 token contract address | +| appContract | address | The address of the dApp | +| tokenId | uint256 | The identifier of the token being transferred | +| baseLayerData | bytes | Additional data to be interpreted by the base layer | +| execLayerData | bytes | Additional data to be interpreted by the execution layer | diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/EtherPortal.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/EtherPortal.md index 61df3f00..a39cb8c4 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/EtherPortal.md +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/portals/EtherPortal.md @@ -10,7 +10,7 @@ Ether to a dApp while informing the off-chain machine. ## `depositEther()` ```solidity -function depositEther(address _dapp, bytes _execLayerData) external payable +function depositEther(address appContract, bytes execLayerData) external payable ``` Transfer Ether to a dApp and add an input to @@ -20,7 +20,7 @@ All the value sent through this function is forwarded to the dApp. #### Parameters -| Name | Type | Description | -| --------------- | ------- | -------------------------------------------------------- | -| \_dapp | address | The address of the dApp | -| \_execLayerData | bytes | Additional data to be interpreted by the execution layer | +| Name | Type | Description | +| ------------- | ------- | -------------------------------------------------------- | +| appContract | address | The address of the dApp | +| execLayerData | bytes | Additional data to be interpreted by the execution layer | From 7138e8ef946d8be59e05bcef239fccc89527ca0b Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Mon, 18 Nov 2024 12:38:39 +0100 Subject: [PATCH 15/99] mod: relayer page and also reference in sidebar (cherry picked from commit e5653fd4679a9008ff1d1180710a94465896f943) --- .../api-reference/json-rpc/relays/relays.md | 24 ------------------- .../version-2.0-sidebars.json | 8 ------- 2 files changed, 32 deletions(-) delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/relays/relays.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/relays/relays.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/relays/relays.md deleted file mode 100644 index 56548533..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/relays/relays.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -id: relays -title: DAppAddressRelay -resources: - - url: https://github.com/cartesi/rollups-contracts/blob/v1.4.0/onchain/rollups/contracts/relays/DAppAddressRelay.sol - title: DAppAddressRelay contract ---- - -The **DAppAddressRelay** contract allows anyone to inform the off-chain machine -of the address of the dApp contract in a trustless and permissionless way. - -## `relayDAppAddress()` - -```solidity -function relayDAppAddress(address _dapp) external -``` - -Add an input to a dApp's input box with its address. - -### Parameters - -| Name | Type | Description | -| ------ | ------- | ----------------------- | -| \_dapp | address | The address of the dApp | diff --git a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json index 288c2b9d..aabe7963 100644 --- a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json +++ b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json @@ -62,14 +62,6 @@ "rollups-apis/json-rpc/portals/ERC1155BatchPortal", "rollups-apis/json-rpc/portals/EtherPortal" ] - }, - { - "type": "category", - "label": "Relayer", - "collapsed": true, - "items": [ - "rollups-apis/json-rpc/relays/relays" - ] } ] }, From 0f35ddd722cacadc31783c70c7935584cc03372f Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Mon, 25 Nov 2024 12:30:03 +0100 Subject: [PATCH 16/99] mod: updated querry outputs inline with V2 (cherry picked from commit aacc049bfe6ebf850d85ebd346535b8ed555d15b) --- .../version-2.0/development/asset-handling.md | 12 +++---- .../version-2.0/development/query-outputs.md | 34 +++++++++--------- static/img/v2.0/deposit-payload.jpg | Bin 0 -> 246973 bytes static/img/v2.0/onchain-contracts.jpg | Bin 0 -> 170593 bytes 4 files changed, 23 insertions(+), 23 deletions(-) create mode 100644 static/img/v2.0/deposit-payload.jpg create mode 100644 static/img/v2.0/onchain-contracts.jpg 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 bd4bc950..def9db67 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 @@ -18,7 +18,7 @@ Currently, Cartesi Rollups support the following types of assets: - [ERC-1155 Single](../api-reference/json-rpc/portals/ERC1155SinglePortal.md) - [ERC-1155 Batch](../api-reference/json-rpc/portals/ERC1155BatchPortal.md) -![img](../../../static/img/v1.3/assets.jpg) +![img](../../..//static/img/v2.0/onchain-contracts.jpg) ## Deposits @@ -28,20 +28,18 @@ When an asset is deposited, it is on the base layer but gains a representation i Deposit input payloads are always specified as packed ABI-encoded parameters, as detailed below. -![img](../../..//static/img/v1.3/abi.jpg) +![img](../../..//static/img/v2.0/deposit-payload.jpg) ### ABI encoding for deposits - | Asset | Packed ABI-encoded payload fields | Standard ABI-encoded payload fields | | :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------- | | Ether |
  • `address sender`,
  • `uint256 value`,
  • `bytes execLayerData`
| none | -| ERC-20 |
  • `bool success`,
  • `address token`,
  • `address sender`,
  • `uint256 amount`,
  • `bytes execLayerData`
| none | +| ERC-20 |
  • `address token`,
  • `address sender`,
  • `uint256 amount`,
  • `bytes execLayerData`
| none | | ERC-721 |
  • `address token`,
  • `address sender`,
  • `uint256 tokenId`,
  • standard ABI-encoded fields...
|
  • `bytes baseLayerData`,
  • `bytes execLayerData`
| | ERC-1155 (single) |
  • `address token`,
  • `address sender`,
  • `uint256 tokenId`,
  • `uint256 value`,
  • standard ABI-encoded fields...
|
  • `bytes baseLayerData`,
  • `bytes execLayerData`
| | ERC-1155 (batch) |
  • `address token`,
  • `address sender`,
  • standard ABI-encoded fields...
|
  • `uint256[] tokenIds`,
  • `uint256[] values`,
  • `bytes baseLayerData`,
  • `bytes execLayerData`
| - ## Withdrawing assets Users can deposit assets to a Cartesi dApp, but only the dApp can initiate withdrawals. When a withdrawal request is made, it’s processed and interpreted off-chain by the Cartesi Machine running the dApp’s code. Subsequently, the Cartesi Machine creates a voucher containing the necessary instructions for withdrawal, which is executable when an epoch has settled. @@ -50,7 +48,7 @@ Vouchers are crucial in allowing dApps in the execution layer to interact with c The dApp’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 dApp 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 [`executeVoucher()`](../rollups-apis/json-rpc/application.md/#executevoucher) function of the `CartesiDApp` 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. :::note epoch length By default, Cartesi nodes close one epoch every 7200 blocks. You can [manually set the epoch length](./cli-commands.md/#run) to facilitate quicker asset-handling methods. @@ -60,7 +58,7 @@ Here are the function signatures used by vouchers to withdraw the different type | Asset | Destination | Function signature | | :------- | :------------- | :------------------------------------------------------------------------------------------------------------------------------------------ | -| Ether | dApp contract | `withdrawEther(address,uint256)` [:page_facing_up:](../api-reference/json-rpc/application.md/#withdrawether) | +| Ether | dApp contract | `withdrawEther(address,uint256)` [:page_facing_up:](../rollups-apis/json-rpc/application.md/#withdrawether) | | ERC-20 | Token contract | `transfer(address,uint256)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-20#methods) | | ERC-20 | Token contract | `transferFrom(address,address,uint256)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-20#methods) | | ERC-721 | Token contract | `safeTransferFrom(address,address,uint256)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-721#specification) | 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 249d682d..24e254ca 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 @@ -42,7 +42,6 @@ They serve as a means for dApp to notify the blockchain about particular events. - Notices are validated on-chain using the [`validateNotice()`](../api-reference/json-rpc/application.md/#validatenotice) function of the [`CartesiDApp`](../api-reference/json-rpc/application.md) contract. - ### Send a notice Let's examine how a Cartesi dApp has its Advance request **calculating and returning the first five multiples of a given number**. @@ -168,7 +167,7 @@ The notice can be validated and queried by any interested party. Frontend clients can use a GraphQL API exposed by the Cartesi Nodes to query the state of a Cartesi Rollups instance. -You can use the interactive in-browser GraphQL playground hosted on `http://localhost:8080/graphql` for local development. +You can use the interactive in-browser GraphQL playground hosted on `http://localhost:8080/graphql/{dapp_address}` for local development. Note that you'll have to replace '{dapp_address}' with the address of your application. 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: @@ -204,11 +203,14 @@ async function fetchNotices() { const query = '{ "query": "{ notices { edges { node { payload } } } }" }'; try { - const response = await fetch("http://localhost:8080/graphql", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: query, - }); + const response = await fetch( + "http://localhost:8080/graphql/0x75135d8ADb7180640d29d822D9AD59E83E8695b2", + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: query, + } + ); const result = await response.json(); @@ -256,7 +258,6 @@ Then, in the bottom-left corner of the Playground, you'll find a section that pr Replace `123` with the value you want to pass for `$inputIndex`. - ^hCW+fNZG~CK4Nk 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 literal 0 HcmV?d00001 diff --git a/static/img/v2.0/onchain-contracts.jpg b/static/img/v2.0/onchain-contracts.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d53ba4687b324352f74b300ccca6693039407806 GIT binary patch literal 170593 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

^!BH$nc2nc!6@+UN16{X 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%0D?hB8L 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�=Gun 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�k8T1`! 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 f3;%tx&mU& Date: Mon, 25 Nov 2024 16:25:58 +0100 Subject: [PATCH 17/99] merge: merged Joao's restructure with V2 updates (cherry picked from commit e60583214c1db8fd7636766c127ef10cc93ff407) --- .../api-reference/json-rpc/relays/relays.md | 24 +++ .../version-2.0/development/reference.md | 6 +- .../version-2.0/getting-started/index.md | 2 +- .../version-2.0/resources/migration-guide.md | 10 +- .../version-2.0/tutorials/ether-wallet.md | 2 +- .../version-2.0-sidebars.json | 159 +++++++++--------- 6 files changed, 105 insertions(+), 98 deletions(-) create mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/relays/relays.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/relays/relays.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/relays/relays.md new file mode 100644 index 00000000..56548533 --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/relays/relays.md @@ -0,0 +1,24 @@ +--- +id: relays +title: DAppAddressRelay +resources: + - url: https://github.com/cartesi/rollups-contracts/blob/v1.4.0/onchain/rollups/contracts/relays/DAppAddressRelay.sol + title: DAppAddressRelay contract +--- + +The **DAppAddressRelay** contract allows anyone to inform the off-chain machine +of the address of the dApp contract in a trustless and permissionless way. + +## `relayDAppAddress()` + +```solidity +function relayDAppAddress(address _dapp) external +``` + +Add an input to a dApp's input box with its address. + +### Parameters + +| Name | Type | Description | +| ------ | ------- | ----------------------- | +| \_dapp | address | The address of the dApp | diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/reference.md b/cartesi-rollups_versioned_docs/version-2.0/development/reference.md index 587555f4..6c84fa33 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/development/reference.md +++ b/cartesi-rollups_versioned_docs/version-2.0/development/reference.md @@ -87,7 +87,7 @@ This is a two-step process. First calculate the address of the new History. Afte The `cast send` command will fail if Cast does not recognize the _History_ type during execution. In such cases, replace _History_ with `address` as the return type for `newHistory()` and execute the command again. - The `cast send` command may also fail due to gas estimation issues. To circumvent this, provide gas constraints with the `--gas-limit` parameter (e.g., `--gas-limit 7000000`). +The `cast send` command may also fail due to gas estimation issues. To circumvent this, provide gas constraints with the `--gas-limit` parameter (e.g., `--gas-limit 7000000`). #### 3. Replace the _History_ @@ -108,7 +108,3 @@ cast send \ After replacing the _History_, update the `CARTESI_CONTRACTS_HISTORY_ADDRESS` in the application configuration with the new _History_ address. Then, upgrade the Cartesi Rollups Node as usual. When the Cartesi Rollups Node restarts, it processes all existing inputs, recalculates the epochs, and sends the claims to the new _History_ based on the updated configuration. - - - - diff --git a/cartesi-rollups_versioned_docs/version-2.0/getting-started/index.md b/cartesi-rollups_versioned_docs/version-2.0/getting-started/index.md index 44c44318..6be846ab 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/getting-started/index.md +++ b/cartesi-rollups_versioned_docs/version-2.0/getting-started/index.md @@ -56,7 +56,7 @@ let finish = { status: "accept" }; Cartesi dApps are implemented as infinite loops that manage their transaction cycles through HTTP POST requests to the `/finish` endpoint to ensure flexibility across different programming languages and stacks. You can learn more about this abstraction [here](../api-reference/backend/introduction.md). -In the Cartesi Rollup framework, all inputs sent to the base layer trigger an "advance_state" [request](./development/send-inputs.md#initiate-an-advance-request), which alters the state of the Cartesi Machine and consequently the Rollup. Since inputs originate on-chain, they are hex-encoded following the EVM message standard. +In the Cartesi Rollup framework, all inputs sent to the base layer trigger an "advance_state" [request](../development/send-inputs.md#initiate-an-advance-request), which alters the state of the Cartesi Machine and consequently the Rollup. Since inputs originate on-chain, they are hex-encoded following the EVM message standard. Notices can be understood as "provable" events; as such, they can be sent to an EVM chain to be verified, so they are also hex-encoded. 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 587555f4..96a7d8ee 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 @@ -1,6 +1,6 @@ --- -id: reference -title: Reference +id: migration-guide +title: Migration Guide --- ## Migrating from Cartesi Rollups Node v1.4 to v1.5.x @@ -87,7 +87,7 @@ This is a two-step process. First calculate the address of the new History. Afte The `cast send` command will fail if Cast does not recognize the _History_ type during execution. In such cases, replace _History_ with `address` as the return type for `newHistory()` and execute the command again. - The `cast send` command may also fail due to gas estimation issues. To circumvent this, provide gas constraints with the `--gas-limit` parameter (e.g., `--gas-limit 7000000`). +The `cast send` command may also fail due to gas estimation issues. To circumvent this, provide gas constraints with the `--gas-limit` parameter (e.g., `--gas-limit 7000000`). #### 3. Replace the _History_ @@ -108,7 +108,3 @@ cast send \ After replacing the _History_, update the `CARTESI_CONTRACTS_HISTORY_ADDRESS` in the application configuration with the new _History_ address. Then, upgrade the Cartesi Rollups Node as usual. When the Cartesi Rollups Node restarts, it processes all existing inputs, recalculates the epochs, and sends the claims to the new _History_ based on the updated configuration. - - - - 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 a216dcdf..a1e42d9d 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 @@ -536,4 +536,4 @@ For end-to-end functionality, developers will likely build their [custom user-fa When you run your application with `cartesi run`, there is a local instance of CartesiScan on `http://localhost:8080/explorer`. -You can execute your vouchers via the explorer, which completes the withdrawal process at the end of [an epoch](../rollups-apis/backend/vouchers.md/#epoch-configuration). +You can execute your vouchers via the explorer, which completes the withdrawal process at the end of [an epoch](../api-reference/backend/vouchers.md/#epoch-configuration). diff --git a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json index aabe7963..81758f7b 100644 --- a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json +++ b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json @@ -1,119 +1,103 @@ { "rollups": [ - { - "type": "doc", - "id": "overview", - "label": "Overview" - }, - { - "type": "doc", - "id": "quickstart", - "label": "Quickstart" - }, { "type": "category", - "label": "Core Concepts", + "label": "Getting Started", "collapsed": true, "items": [ - "core-concepts/optimistic-rollups", - "core-concepts/architecture", - "core-concepts/mainnet-considerations" + "getting-started/overview", + "getting-started/quickstart", + "getting-started/installation" ] }, { "type": "category", - "label": "Rollups APIs", + "label": "API Reference", "collapsed": true, "items": [ - "rollups-apis/http-api", + "api-reference/http-api", + "api-reference/architecture", { "type": "category", - "label": "Backend APIs", + "label": "Base Layer Contracts", "collapsed": true, "items": [ - "rollups-apis/backend/introduction", - "rollups-apis/backend/notices", - "rollups-apis/backend/vouchers", - "rollups-apis/backend/reports" + "api-reference/json-rpc/overview", + "api-reference/json-rpc/input-box", + "api-reference/json-rpc/application", + "api-reference/json-rpc/application-factory", + { + "type": "category", + "label": "Portals", + "collapsed": true, + "items": [ + "api-reference/json-rpc/portals/EtherPortal", + "api-reference/json-rpc/portals/ERC20Portal", + "api-reference/json-rpc/portals/ERC721Portal", + "api-reference/json-rpc/portals/ERC1155SinglePortal", + "api-reference/json-rpc/portals/ERC1155BatchPortal" + ] + } ] }, { "type": "category", - "label": "Frontend APIs", + "label": "Backend API", "collapsed": true, "items": [ + "api-reference/backend/introduction", + "api-reference/backend/notices", + "api-reference/backend/vouchers", + "api-reference/backend/reports" + ] + }, + { + "type": "category", + "label": "GraphQL API", + "collapsed": true, + "items": [ + "api-reference/graphql/overview", { "type": "category", - "label": "Smart contracts API", + "label": "Queries", + "collapsed": true, + "items": [ + "api-reference/graphql/queries/inputs", + "api-reference/graphql/queries/notices", + "api-reference/graphql/queries/vouchers", + "api-reference/graphql/queries/reports" + ] + }, + { + "type": "category", + "label": "Objects", "collapsed": true, "items": [ - "rollups-apis/json-rpc/overview", - "rollups-apis/json-rpc/input-box", - "rollups-apis/json-rpc/application", - "rollups-apis/json-rpc/application-factory", { - "type": "category", - "label": "Portals", - "collapsed": true, - "items": [ - "rollups-apis/json-rpc/portals/ERC20Portal", - "rollups-apis/json-rpc/portals/ERC721Portal", - "rollups-apis/json-rpc/portals/ERC1155SinglePortal", - "rollups-apis/json-rpc/portals/ERC1155BatchPortal", - "rollups-apis/json-rpc/portals/EtherPortal" - ] + "type": "autogenerated", + "dirName": "api-reference/graphql/objects" } ] }, { "type": "category", - "label": "GraphQL API", + "label": "Filters", "collapsed": true, "items": [ - "rollups-apis/graphql/overview", - { - "type": "category", - "label": "Queries", - "collapsed": true, - "items": [ - "rollups-apis/graphql/queries/inputs", - "rollups-apis/graphql/queries/notices", - "rollups-apis/graphql/queries/vouchers", - "rollups-apis/graphql/queries/reports" - ] - }, - { - "type": "category", - "label": "Objects", - "collapsed": true, - "items": [ - { - "type": "autogenerated", - "dirName": "rollups-apis/graphql/objects" - } - ] - }, { - "type": "category", - "label": "Filters", - "collapsed": true, - "items": [ - { - "type": "autogenerated", - "dirName": "rollups-apis/graphql/inputs" - } - ] - }, + "type": "autogenerated", + "dirName": "api-reference/graphql/inputs" + } + ] + }, + { + "type": "category", + "label": "Scalars", + "collapsed": true, + "items": [ { - "type": "category", - "label": "Scalars", - "collapsed": true, - "items": [ - { - "type": "autogenerated", - "dirName": "rollups-apis/graphql/scalars" - } - ] + "type": "autogenerated", + "dirName": "api-reference/graphql/scalars" } ] } @@ -126,14 +110,11 @@ "label": "Development", "collapsed": true, "items": [ - "development/installation", "development/building-a-dapp", - "development/cli-commands", "development/send-inputs", "development/query-outputs", "development/asset-handling", - "development/reference", - "development/community-tools" + "development/cli-commands" ] }, { @@ -158,6 +139,16 @@ "tutorials/cli-account-abstraction-feauture" ] }, + { + "type": "category", + "label": "Resources", + "collapsed": true, + "items": [ + "resources/community-tools", + "resources/mainnet-considerations", + "resources/migration-guide" + ] + }, { "type": "category", "label": "References", From a583a9930dbc7c4be1f9c85c464ba29e7b2c6bc7 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Mon, 25 Nov 2024 17:06:50 +0100 Subject: [PATCH 18/99] update: modified asssets-handling inline with V2 (cherry picked from commit 5195283a58647d8adccc23ea37f2e3eb6ed39b47) --- .../version-2.0/api-reference/json-rpc/application.md | 1 - .../version-2.0/development/asset-handling.md | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/application.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/application.md index ffe89278..5efa4570 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/application.md +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/json-rpc/application.md @@ -14,7 +14,6 @@ Every dApp is subscribed to a consensus contract and governed by a single addres The dApp developer can choose whichever ownership and consensus models it wants. - Examples of dApp ownership models include: - no owner (address zero) 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 def9db67..50631099 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 @@ -48,7 +48,7 @@ Vouchers are crucial in allowing dApps in the execution layer to interact with c The dApp’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 dApp itself. -Next, the off-chain machine uses the address of the application on the base layer to generate a voucher for execution at the [`executeVoucher()`](../rollups-apis/json-rpc/application.md/#executevoucher) function of the `CartesiDApp` 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. +Next, the off-chain machine uses the address of the application on the base layer to generate a voucher for execution at the [`executeVoucher()`](../api-reference/json-rpc/application.md/#executevoucher) function of the `CartesiDApp` 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. :::note epoch length By default, Cartesi nodes close one epoch every 7200 blocks. You can [manually set the epoch length](./cli-commands.md/#run) to facilitate quicker asset-handling methods. @@ -58,7 +58,7 @@ Here are the function signatures used by vouchers to withdraw the different type | Asset | Destination | Function signature | | :------- | :------------- | :------------------------------------------------------------------------------------------------------------------------------------------ | -| Ether | dApp contract | `withdrawEther(address,uint256)` [:page_facing_up:](../rollups-apis/json-rpc/application.md/#withdrawether) | +| Ether | dApp contract | `withdrawEther(address,uint256)` [:page_facing_up:](../api-reference/json-rpc/application.md/#withdrawether) | | ERC-20 | Token contract | `transfer(address,uint256)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-20#methods) | | ERC-20 | Token contract | `transferFrom(address,address,uint256)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-20#methods) | | ERC-721 | Token contract | `safeTransferFrom(address,address,uint256)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-721#specification) | From a1d3d51635d0bba84352ea914a02ab756742a966 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Thu, 7 Nov 2024 23:24:41 +0100 Subject: [PATCH 19/99] add: Added an external resource section (cherry picked from commit 8f64ce759965c7ce546fefe9d0b498ff7c6f466e) --- .../development/community-tools.md | 161 ---------------- .../external-resources/community-tools.md | 173 ++++++++++++++++++ .../external-resources/integration-guides.md | 14 ++ .../version-1.5-sidebars.json | 13 +- 4 files changed, 198 insertions(+), 163 deletions(-) delete mode 100644 cartesi-rollups_versioned_docs/version-1.5/development/community-tools.md create mode 100644 cartesi-rollups_versioned_docs/version-1.5/external-resources/community-tools.md create mode 100644 cartesi-rollups_versioned_docs/version-1.5/external-resources/integration-guides.md diff --git a/cartesi-rollups_versioned_docs/version-1.5/development/community-tools.md b/cartesi-rollups_versioned_docs/version-1.5/development/community-tools.md deleted file mode 100644 index b51b18c8..00000000 --- a/cartesi-rollups_versioned_docs/version-1.5/development/community-tools.md +++ /dev/null @@ -1,161 +0,0 @@ ---- -id: community-tools -title: Community tools ---- - -Several tools created and maintained by the community streamline the dApp creation process on Cartesi Rollups. - -## Deroll - -Introducing Deroll, a powerful TypeScript framework designed to simplify the development of dApps on Cartesi. - -- **Features**: - - Simplifies dApp development with intuitive methods. - - Handles advance and inspect requests easily. - - Comprehensive wallet functionality for ERC20, ERC721 and ERC-1155 token standards. - - Integrated router for complex routing logic. - -- **Getting Started**: - - Create a new Deroll project by running: - ```bash - npm init @deroll/app - ``` - -- **Resources**: - - [Deroll Documentation](https://deroll.dev) - - [Deroll GitHub Repository](https://github.com/tuler/deroll) - ---- - -## NoNodo - -NoNodo is a cutting-edge development tool for Cartesi Rollups that allows applications to run directly on the host machine, bypassing Docker or RISC-V compilation. - -- **Features**: - - Run applications directly on the host machine for faster performance. - - No Docker or RISC-V Required. - -- **Getting Started**: - - Install NoNodo by running: - ```bash - npm i -g nonodo - ``` -- **Resources**: - - [NoNodo GitHub Repository](https://github.com/Calindra/nonodo) - ---- - -## Cartesify - -Cartesify is a robust Web3 client designed for seamless interaction with the Cartesi Machine. - -- **Features**: - - Send transactions to the Cartesi Machine. - - Query data efficiently. - - Engage with backend systems using a REST-like interface. - -- **Resources**: - - [Cartesify GitHub Repository](https://github.com/Calindra/cartesify) - ---- - -## Tikua - -Tikua is a versatile JS Cartesi package designed for seamless integration with any visual library, whether in browser or terminal environments. - -- **Features**: - - Integrates smoothly with any visual library on both Browser and Terminal. - - Supports any provider or network with extensive configurability. - - Handles multi-chain applications. - - Provides warnings for unsupported provider chains. - - Retrieve machine results. - -- **Resources**: - - - [Tikua GitHub Repository](https://github.com/doiim/tikua) - - ---- - -## Rollmelette - -Rollmelette is a high-level framework that simplifies building Cartesi applications using the Go programming language. - -- **Features**: - - Simplifies the development of Cartesi applications. - - Provides a high-level API for interacting with the Cartesi Machine. - - Simplifies sending inputs, retrieving outputs and asset handling - - Supports the Go programming language. - -- **Resources**: - - [Rollmelette GitHub Repository](https://github.com/rollmelette/rollmelette) - ---- - -## Python-Cartesi - -Python-Cartesi is a high-level framework that simplifies the development of Cartesi applications using Python. - -- **Features**: - - Simplifies the development of Cartesi applications. - - Prioritizes testing, equipping developers with tools to write tests for DApps within a local Python environment. - - Allows full control over inputs and outputs for scenarios where high-level tools may be insufficient - - Supports the Python programming language. - -- **Getting Started**: - - Install Python-Cartesi by running: - ```bash - pip install python-cartesi - ``` -- **Resources**: - - [Python-Cartesi GitHub Repository](https://github.com/prototyp3-dev/python-cartesi) - ---- - -## TypeScript-SQLite template - -A backend application built with TypeScript and SQLite, designed to complement a corresponding frontend project. - -- **Features**: - - TypeScript and SQLite for backend development. - - Integration with React for the frontend. - - Ethers.js for seamless blockchain interaction. - - Template designed for easy project initiation. - -- **Resources**: - - [TypeScript-SQLite GitHub Repository](https://github.com/doiim/cartesi-ts-sqlite) - - [Pre-deployed demo available on the Sepolia Network](https://doiim.github.io/cartesi-ts-react-sqlite/). - ---- - -## Python-Wallet - -A Python-based wallet implementation for Cartesi dApps designed to handle various types of assets. - -- **Features**: - - Simplifies asset handling for Cartesi dApps. - - Deposit assets into the dApp. - - Transfer assets within the dApp. - - Withdraw assets from the dApp. - -- **Resources**: - - [Python-Wallet GitHub Repository](https://github.com/jplgarcia/python-wallet/tree/main) - - [Full example](https://github.com/jplgarcia/python-wallet/blob/main/dapp.py) ---- -## CartDevKit - -CartDevKit is an all-in-one package for building on Cartesi. - -- **Features**: - - CLI tool for easy project setup. - - Templates for backend, frontend and Cartesify. - -- **Getting Started**: - - Create a new project: - ```bash - npx cartdevkit@latest create mydapp - ``` - -- **Resources**: - - [CartDevKit GitHub Repository](https://github.com/gconnect/cartdev-kit) - - [CartDevKit Documentation](https://africlab.gitbook.io/cartdevkit) \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-1.5/external-resources/community-tools.md b/cartesi-rollups_versioned_docs/version-1.5/external-resources/community-tools.md new file mode 100644 index 00000000..88f93d11 --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-1.5/external-resources/community-tools.md @@ -0,0 +1,173 @@ +--- +id: community-tools +title: Community tools +--- + +Several tools created and maintained by the community streamline the dApp creation process on Cartesi Rollups. + +## Deroll + +Introducing Deroll, a powerful TypeScript framework designed to simplify the development of dApps on Cartesi. + +- **Features**: + + - Simplifies dApp development with intuitive methods. + - Handles advance and inspect requests easily. + - Comprehensive wallet functionality for ERC20, ERC721 and ERC-1155 token standards. + - Integrated router for complex routing logic. + +- **Getting Started**: + + - Create a new Deroll project by running: + ```bash + npm init @deroll/app + ``` + +- **Resources**: + - [Deroll Documentation](https://deroll.dev) + - [Deroll GitHub Repository](https://github.com/tuler/deroll) + +--- + +## NoNodo + +NoNodo is a cutting-edge development tool for Cartesi Rollups that allows applications to run directly on the host machine, bypassing Docker or RISC-V compilation. + +- **Features**: + + - Run applications directly on the host machine for faster performance. + - No Docker or RISC-V Required. + +- **Getting Started**: + - Install NoNodo by running: + ```bash + npm i -g nonodo + ``` +- **Resources**: + - [NoNodo GitHub Repository](https://github.com/Calindra/nonodo) + +--- + +## Cartesify + +Cartesify is a robust Web3 client designed for seamless interaction with the Cartesi Machine. + +- **Features**: + + - Send transactions to the Cartesi Machine. + - Query data efficiently. + - Engage with backend systems using a REST-like interface. + +- **Resources**: + - [Cartesify GitHub Repository](https://github.com/Calindra/cartesify) + +--- + +## Tikua + +Tikua is a versatile JS Cartesi package designed for seamless integration with any visual library, whether in browser or terminal environments. + +- **Features**: + + - Integrates smoothly with any visual library on both Browser and Terminal. + - Supports any provider or network with extensive configurability. + - Handles multi-chain applications. + - Provides warnings for unsupported provider chains. + - Retrieve machine results. + +- **Resources**: + + - [Tikua GitHub Repository](https://github.com/doiim/tikua) + +--- + +## Rollmelette + +Rollmelette is a high-level framework that simplifies building Cartesi applications using the Go programming language. + +- **Features**: + + - Simplifies the development of Cartesi applications. + - Provides a high-level API for interacting with the Cartesi Machine. + - Simplifies sending inputs, retrieving outputs and asset handling + - Supports the Go programming language. + +- **Resources**: + - [Rollmelette GitHub Repository](https://github.com/rollmelette/rollmelette) + +--- + +## Python-Cartesi + +Python-Cartesi is a high-level framework that simplifies the development of Cartesi applications using Python. + +- **Features**: + + - Simplifies the development of Cartesi applications. + - Prioritizes testing, equipping developers with tools to write tests for DApps within a local Python environment. + - Allows full control over inputs and outputs for scenarios where high-level tools may be insufficient + - Supports the Python programming language. + +- **Getting Started**: + - Install Python-Cartesi by running: + ```bash + pip install python-cartesi + ``` +- **Resources**: + - [Python-Cartesi GitHub Repository](https://github.com/prototyp3-dev/python-cartesi) + +--- + +## TypeScript-SQLite template + +A backend application built with TypeScript and SQLite, designed to complement a corresponding frontend project. + +- **Features**: + + - TypeScript and SQLite for backend development. + - Integration with React for the frontend. + - Ethers.js for seamless blockchain interaction. + - Template designed for easy project initiation. + +- **Resources**: + - [TypeScript-SQLite GitHub Repository](https://github.com/doiim/cartesi-ts-sqlite) + - [Pre-deployed demo available on the Sepolia Network](https://doiim.github.io/cartesi-ts-react-sqlite/). + +--- + +## Python-Wallet + +A Python-based wallet implementation for Cartesi dApps designed to handle various types of assets. + +- **Features**: + + - Simplifies asset handling for Cartesi dApps. + - Deposit assets into the dApp. + - Transfer assets within the dApp. + - Withdraw assets from the dApp. + +- **Resources**: + - [Python-Wallet GitHub Repository](https://github.com/jplgarcia/python-wallet/tree/main) + - [Full example](https://github.com/jplgarcia/python-wallet/blob/main/dapp.py) + +--- + +## CartDevKit + +CartDevKit is an all-in-one package for building on Cartesi. + +- **Features**: + + - CLI tool for easy project setup. + - Templates for backend, frontend and Cartesify. + +- **Getting Started**: + + - Create a new project: + ```bash + npx cartdevkit@latest create mydapp + ``` + +- **Resources**: + - [CartDevKit GitHub Repository](https://github.com/gconnect/cartdev-kit) + - [CartDevKit Documentation](https://africlab.gitbook.io/cartdevkit) diff --git a/cartesi-rollups_versioned_docs/version-1.5/external-resources/integration-guides.md b/cartesi-rollups_versioned_docs/version-1.5/external-resources/integration-guides.md new file mode 100644 index 00000000..266a7b9d --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-1.5/external-resources/integration-guides.md @@ -0,0 +1,14 @@ +--- +id: integration-guides +title: Integrations guide +--- + +Guides for integrating external protocols into your Cartesi application. Each integration offers a unique functionality to your appication. + +## Avail Integration + +This guide will walk you through setting up a Cartesi dApp using Avail on your local machine. You will learn how to send transactions either directly (through Cartesi Rollups Smart Contracts deployed on an L1) or through Avail DA using EIP-712 signed messages. Also how to inspect the dApp state and outputs through the APIs provided by the Cartesi Rollups Framework. + +## Espresso Integration + +This guide covers the Cartesi+Espresso integrationand how to upgrade Cartesi application such that inputs can be submitted via Espresso instead of the regular base layer. diff --git a/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json b/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json index 32c6c3fa..01db8014 100644 --- a/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json +++ b/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json @@ -142,8 +142,7 @@ "development/send-requests", "development/retrieve-outputs", "development/asset-handling", - "development/migration", - "development/community-tools" + "development/migration" ] }, { @@ -211,6 +210,16 @@ ] } ] + }, + { + "type": "category", + "label": "External Resources", + "collapsed": true, + "items": [ + "external-resources/community-tools", + "external-resources/integration-guides" + + ] } ] } \ No newline at end of file From bcfda382390b8c10fb11ba8fe815c0ada8d05238 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Thu, 7 Nov 2024 23:26:45 +0100 Subject: [PATCH 20/99] update: restructured external resource position (cherry picked from commit 0f793365f21d4f174d81bc4c6bd41e5f766748ea) --- .../version-1.5-sidebars.json | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json b/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json index 01db8014..48c55283 100644 --- a/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json +++ b/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json @@ -167,6 +167,17 @@ "tutorials/cli-account-abstraction-feauture" ] }, + + { + "type": "category", + "label": "External Resources", + "collapsed": true, + "items": [ + "external-resources/community-tools", + "external-resources/integration-guides" + + ] + }, { "type": "category", "label": "References", @@ -210,16 +221,6 @@ ] } ] - }, - { - "type": "category", - "label": "External Resources", - "collapsed": true, - "items": [ - "external-resources/community-tools", - "external-resources/integration-guides" - - ] } ] } \ No newline at end of file From 52d51e5e15eb1f8d0b1c5d225ff9b32743b00f46 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Thu, 7 Nov 2024 23:24:41 +0100 Subject: [PATCH 21/99] add: Added an external resource section (cherry picked from commit 7f49cdce224863254c35144c762fc2d8c284d1bb) --- .../version-1.5-sidebars.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json b/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json index 48c55283..eb5b5648 100644 --- a/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json +++ b/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json @@ -221,6 +221,16 @@ ] } ] + }, + { + "type": "category", + "label": "External Resources", + "collapsed": true, + "items": [ + "external-resources/community-tools", + "external-resources/integration-guides" + + ] } ] } \ No newline at end of file From dbd3d24a35c74865efd1cece86103a1778756930 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Thu, 7 Nov 2024 23:26:45 +0100 Subject: [PATCH 22/99] update: restructured external resource position (cherry picked from commit fcfc2bb879a38e15da374ca43dbfecdb154ddd04) --- .../version-1.5-sidebars.json | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json b/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json index eb5b5648..48c55283 100644 --- a/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json +++ b/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json @@ -221,16 +221,6 @@ ] } ] - }, - { - "type": "category", - "label": "External Resources", - "collapsed": true, - "items": [ - "external-resources/community-tools", - "external-resources/integration-guides" - - ] } ] } \ No newline at end of file From e5719a12b9ee63e25f25ff70c26c9a0126e2528b Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Mon, 18 Nov 2024 10:48:31 +0100 Subject: [PATCH 23/99] update: added shour descriptions for different integratins (cherry picked from commit 82478efa2bf76ec5f10c731a28295ce6dfa6057e) --- .vscode/ltex.dictionary.en-US.txt | 8 +++ .vscode/ltex.hiddenFalsePositives.en-US.txt | 3 ++ .../external-resources/community-tools.md | 4 ++ .../external-resources/integration-guides.md | 54 +++++++++++++++++-- 4 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 .vscode/ltex.dictionary.en-US.txt create mode 100644 .vscode/ltex.hiddenFalsePositives.en-US.txt diff --git a/.vscode/ltex.dictionary.en-US.txt b/.vscode/ltex.dictionary.en-US.txt new file mode 100644 index 00000000..f10f69fb --- /dev/null +++ b/.vscode/ltex.dictionary.en-US.txt @@ -0,0 +1,8 @@ +Cartesi +dApp +Rollups +Prevado +XMTP +Farcaster +onchain +Mugen diff --git a/.vscode/ltex.hiddenFalsePositives.en-US.txt b/.vscode/ltex.hiddenFalsePositives.en-US.txt new file mode 100644 index 00000000..84dc8691 --- /dev/null +++ b/.vscode/ltex.hiddenFalsePositives.en-US.txt @@ -0,0 +1,3 @@ +{"rule":"BASE_BASIS","sentence":"^\\QThis guide covers the Cartesi+Espresso integration and how to upgrade Cartesi application such that inputs can be submitted via Espresso instead of the regular base layer.\\E$"} +{"rule":"MORFOLOGIK_RULE_EN_US","sentence":"^\\QIntegrating Cartesi and Chronicle offers Cartesi applications access to onchain and offcahin data like, price feed without developers having to set up additional systems or intermediaries.\\E$"} +{"rule":"MORFOLOGIK_RULE_EN_US","sentence":"^\\QPrevado Id:\\E$"} diff --git a/cartesi-rollups_versioned_docs/version-1.5/external-resources/community-tools.md b/cartesi-rollups_versioned_docs/version-1.5/external-resources/community-tools.md index 88f93d11..b27fd071 100644 --- a/cartesi-rollups_versioned_docs/version-1.5/external-resources/community-tools.md +++ b/cartesi-rollups_versioned_docs/version-1.5/external-resources/community-tools.md @@ -33,6 +33,10 @@ Introducing Deroll, a powerful TypeScript framework designed to simplify the dev NoNodo is a cutting-edge development tool for Cartesi Rollups that allows applications to run directly on the host machine, bypassing Docker or RISC-V compilation. +:::caution Disclaimer: +This tool runs in a non-reproducible execution mode, which is not provable on-chain. As such cannot be used by deployed applications, instead is used for providing a faster iterative development emperience. +::: + - **Features**: - Run applications directly on the host machine for faster performance. diff --git a/cartesi-rollups_versioned_docs/version-1.5/external-resources/integration-guides.md b/cartesi-rollups_versioned_docs/version-1.5/external-resources/integration-guides.md index 266a7b9d..1caede82 100644 --- a/cartesi-rollups_versioned_docs/version-1.5/external-resources/integration-guides.md +++ b/cartesi-rollups_versioned_docs/version-1.5/external-resources/integration-guides.md @@ -3,12 +3,60 @@ id: integration-guides title: Integrations guide --- -Guides for integrating external protocols into your Cartesi application. Each integration offers a unique functionality to your appication. +Guides for integrating external protocols into your Cartesi application. Each integration offers a unique functionality to your application. ## Avail Integration -This guide will walk you through setting up a Cartesi dApp using Avail on your local machine. You will learn how to send transactions either directly (through Cartesi Rollups Smart Contracts deployed on an L1) or through Avail DA using EIP-712 signed messages. Also how to inspect the dApp state and outputs through the APIs provided by the Cartesi Rollups Framework. +Avail is a Web3 infrastructure layer that allows modular execution layers to scale and interoperate in a trust minimized way. It stands out as one of the few data availability layers that combines validity proofs with data availability sampling. This guide will walk you through setting up a Cartesi dApp using Avail on your local machine. You will learn how to send transactions either directly (through Cartesi Rollups Smart Contracts deployed on an L1) or through Avail DA using EIP-712 signed messages. Also, how to inspect the dApp state and outputs through the APIs provided by the Cartesi Rollups Framework. + +- **Resources**: + - Integration Guide: [Mugen Docs](https://docs.mugen.builders/cartesi-avail-tutorial/introduction) + +--- ## Espresso Integration -This guide covers the Cartesi+Espresso integrationand how to upgrade Cartesi application such that inputs can be submitted via Espresso instead of the regular base layer. +Espresso is the protocol for coordinated block building: enabling & incentivizing chains to work together as one unified system. It offers low-cost data availability and also decentralized sequencing. This guide covers the Cartesi+Espresso integration and how to upgrade Cartesi application such that inputs can be submitted via Espresso instead of the regular base layer. + +- **Resources**: + - Integration Guide: [Docs](https://docs.google.com/document/d/1jKtuEPLPK7FUgAhkX7tMRjkIsNgAJ3OYrKtppjUYUrk/edit?tab=t.0#heading=h.fih7t9k0olyg) + +--- + +## Push Integration + +Push protocol is a web3 native communication network, enabling cross-chain notifications, messaging, and more for apps, wallets, and services. This integration is an application level integration that requires developers to run a specialized server for picking up and routing messages and notifications to the push network. + +- **Resources**: + - Integration Guide: [Github](https://github.com/Mugen-Builders/push-cartesi) + - Demo Video: [Youtube](https://www.youtube.com/watch?v=SO-xhHT85Bk) + +--- + +## Chronicle Integration + +Chronicle is an oracle solution, making tremendous strides in redefining access to cost-efficient and verifiable data. Integrating Cartesi and Chronicle offers Cartesi applications access to onchain and offcahin data like, price feed without developers having to set up additional systems or intermediaries. + +- **Resources**: + - Integration Guide: [Github](https://github.com/Mugen-Builders/cartesi-chronicle-integration/tree/main) + - Demo Video: [x](https://x.com/MichaelAsiedu_/status/1826200037786878012) + +--- + +## Prevado ID + +Prevado ID is a set of tools that enable secure identity verification they also facilitate trusted and secured relationship between apps and users. Integrating Cartesi and Prevado provides developers with the ability to verify users identity onchain using zero knowledge proofs, without having to concern themselves with the underlining architecture and setups. + +- **Resources**: + - Article Guide: [Medium](https://medium.com/@jathinjagannath/building-secure-scalable-and-private-dapps-with-decentralized-identity-management-f755991f012b) + +--- + +## XMTP + +XMTP is an open protocol, network, and standards for secure, private web3 messaging. Integrating Cartesi and XMTP will allow Cartesi applications to access this network for decentralized and private messaging, thereby allowing Cartesi applications communicate and also send messages/ notifications to users. At the moment this is an application level integration and would require the developer to set up and run a server dedicated to this integration. + +- **Resources**: + - Integration Guide: [Github](https://github.com/Mugen-Builders/xmtp-cartesi) + - Demo Video: [x](https://x.com/shanemac/status/1828174660133101661?s=46) + - Article Guide: [Medium](https://medium.com/@idogwuchi/integrating-cartesi-dapps-with-xmtp-bridging-decentralised-computation-and-secured-communication-360fa7bdb1d1) From ee84feddd65432c0a9ccef5939c1ee9936f062d3 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Mon, 25 Nov 2024 17:19:05 +0100 Subject: [PATCH 24/99] add: added the integrations guide section (cherry picked from commit 61c08ee1ba15ded2949227fc16c58ca9e2a1d70d) --- .../resources/integration-guides.md | 63 +++++++++++++++++++ .../version-2.0-sidebars.json | 3 +- 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 cartesi-rollups_versioned_docs/version-2.0/resources/integration-guides.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/resources/integration-guides.md b/cartesi-rollups_versioned_docs/version-2.0/resources/integration-guides.md new file mode 100644 index 00000000..0229081b --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-2.0/resources/integration-guides.md @@ -0,0 +1,63 @@ +--- +id: integration-guides +title: Integrations guide +--- + +Guides for integrating external protocols into your Cartesi application. Each integration offers a unique functionality to your application. + +## Avail Integration + +Avail is a Web3 infrastructure layer that allows modular execution layers to scale and interoperate in a trust minimized way. It stands out as one of the few data availability layers that combines validity proofs with data availability sampling. This guide will walk you through setting up a Cartesi dApp using Avail on your local machine. You will learn how to send transactions either directly (through Cartesi Rollups Smart Contracts deployed on an L1) or through Avail DA using EIP-712 signed messages. Also, how to inspect the dApp state and outputs through the APIs provided by the Cartesi Rollups Framework. + +- **Resources**: + - Integration Guide: [Mugen Docs](https://docs.mugen.builders/cartesi-avail-tutorial/introduction) + +--- + +## Espresso Integration + +Espresso is the protocol for coordinated block building: enabling & incentivizing chains to work together as one unified system. It offers low-cost data availability and also decentralized sequencing. This guide covers the Cartesi+Espresso integration and how to upgrade Cartesi application such that inputs can be submitted via Espresso instead of the regular base layer. + +- **Resources**: + - Integration Guide: [Docs](https://docs.google.com/document/d/1jKtuEPLPK7FUgAhkX7tMRjkIsNgAJ3OYrKtppjUYUrk/edit?tab=t.0#heading=h.fih7t9k0olyg) + +--- + +## Push Integration + +Push protocol is a web3 native communication network, enabling cross-chain notifications, messaging, and more for apps, wallets, and services. This integration is an application level integration that requires developers to run a specialized server for picking up and routing messages and notifications to the push network. + +- **Resources**: + - Integration Guide: [Github](https://github.com/Mugen-Builders/push-cartesi) + - Demo Video: [Youtube](https://www.youtube.com/watch?v=SO-xhHT85Bk) + +--- + +## Chronicle Integration + +Chronicle is an oracle solution, making tremendous strides in redefining access to cost-efficient and verifiable data. Integrating Cartesi and Chronicle offers Cartesi applications access to onchain and offcahin data like, price feed without developers having to set up additional systems or intermediaries. + +- **Resources**: + - Integration Guide: [Github](https://github.com/Mugen-Builders/cartesi-chronicle-integration/tree/main) + - Demo Video: [x](https://x.com/MichaelAsiedu_/status/1826200037786878012) + +--- + +## Prevado ID + +Prevado ID is a set of tools that enable secure identity verification they also facilitate trusted and secured relationship between apps and users. Integrating Cartesi and Prevado provides developers with the ability to verify users identity onchain using zero knowledge proofs, without having to concern themselves with the underlining architecture and setups. + +- **Resources**: + - Article Guide: [Medium](https://medium.com/@jathinjagannath/building-secure-scalable-and-private-dapps-with-decentralized-identity-management-f755991f012b) + +--- + +## XMTP + +XMTP is an open protocol, network, and standards for secure, private web3 messaging. Integrating Cartesi and XMTP will allow Cartesi applications to access this network for decentralized and private messaging, thereby allowing Cartesi applications communicate and also send messages/ notifications to users. At the moment this is an application level integration and would require the developer to set up and run a server dedicated to this integration. + +- **Resources**: + - Integration Guide: [Github](https://github.com/Mugen-Builders/xmtp-cartesi) + - Demo Video: [x](https://x.com/shanemac/status/1828174660133101661?s=46) + - Article Guide: [Medium](https://medium.com/@idogwuchi/integrating-cartesi-dapps-with-xmtp-bridging-decentralised-computation-and-secured-communication-360fa7bdb1d1) + diff --git a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json index 81758f7b..f975a52f 100644 --- a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json +++ b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json @@ -146,7 +146,8 @@ "items": [ "resources/community-tools", "resources/mainnet-considerations", - "resources/migration-guide" + "resources/migration-guide", + "resources/integration-guides" ] }, { From 86b0d9e9b445a73c2c39e02006f3e3eecb6f8925 Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Mon, 25 Nov 2024 17:41:15 +0100 Subject: [PATCH 25/99] add: added the integrations guide section (cherry picked from commit 53fe9978273072eb195fa9bf784c429167a13190) --- .../version-2.0/development/reference.md | 110 ------------------ 1 file changed, 110 deletions(-) delete mode 100644 cartesi-rollups_versioned_docs/version-2.0/development/reference.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/development/reference.md b/cartesi-rollups_versioned_docs/version-2.0/development/reference.md deleted file mode 100644 index 6c84fa33..00000000 --- a/cartesi-rollups_versioned_docs/version-2.0/development/reference.md +++ /dev/null @@ -1,110 +0,0 @@ ---- -id: reference -title: Reference ---- - -## Migrating from Cartesi Rollups Node v1.4 to v1.5.x - -Release `v1.5.0` introduces a critical change in how epochs are closed in the Cartesi Rollups Node, transitioning from a **timestamp-based system to a block number-based system**. - -Epoch closure is now determined by the `CARTESI_EPOCH_LENGTH` environment variable (in blocks) instead of `CARTESI_EPOCH_DURATION` (in seconds). - -```plaintext -CARTESI_EPOCH_LENGTH = CARTESI_EPOCH_DURATION / BLOCK_TIME -``` - -Where `BLOCK_TIME` is the time to generate a block in the target network. - -**Example**: If a block is generated every 12 seconds and `CARTESI_EPOCH_DURATION` is set to 86400 seconds (24 hours), `CARTESI_EPOCH_LENGTH = 86400 / 12 = 7200` - -### Option 1: Redeploy all contracts - -Redeploy all contracts and your application with the new configuration. - -Refer to the [self-hosted deployment guide](../deployment/self-hosted.md) for detailed deployment instructions. - -:::caution -Redeploying creates a new application instance. All previous inputs, outputs, claims, and funds locked in the application contract will remain associated with the old application address. -::: - -### Option 2: Replace Application's History - -This process allows inputs, outputs, and locked funds to remain unchanged but is more involved. - -:::note -A new _History_ will have no claims. Upon restarting the node with a new _History_, previous claims will be resubmitted, incurring additional costs for the _Authority_ owner. -::: - -:::caution -Instances of the [_History_](https://github.com/cartesi/rollups-contracts/blob/v1.2.0/onchain/rollups/contracts/history/History.sol) contract from [rollups-contracts v1.2.0](https://github.com/cartesi/rollups-contracts/releases/tag/v1.2.0) may be used simultaneously by several applications through their associated _Authority_ instance. -Application owners must consider that and exercise care when performing the steps listed below. -::: - -It's recommended to use the deterministic deployment functions available in the Rollups contracts. - -#### 1. Define the environment variables - -- `SALT`: A random 32-byte value for the deterministic deployment functions. -- `RPC_URL`: The RPC endpoint to be used. -- `MNEMONIC`: The mnemonic phrase for the Authority owner's wallet (other wallet options may be used). -- `HISTORY_FACTORY_ADDRESS`: The address of a valid HistoryFactory instance. -- `AUTHORITY_ADDRESS`: The address of the Authority instance used by the application. - - :::note environment variables - A `HistoryFactory` is deployed at `0x1f158b5320BBf677FdA89F9a438df99BbE560A26` for all supported networks, including Ethereum, Optimism, Arbitrum, Base, and their respective Sepolia-based testnets. - ::: - -#### 2. Instantiate a New _History_ - -This is a two-step process. First calculate the address of the new History. After that, the new instance of History may be created. - -- To calculate the address of a new _History_ contract call the `calculateHistoryAddress(address,bytes32)(address)` function with the help of Foundry's Cast: - - ```shell - cast call \ - --trace --verbose \ - $HISTORY_FACTORY_ADDRESS \ - "calculateHistoryAddress(address,bytes32)(address)" \ - $AUTHORITY_ADDRESS \ - $SALT \ - --rpc-url "$RPC_URL" - ``` - - If the command executes successfully, it will display the address of the new History contract. Store this address in the environment variable `NEW_HISTORY_ADDRESS` for later use. - -- Create a new instance of _History_ may be created by calling function `newHistory(address,bytes32)`: - - ```shell - cast send \ - --json \ - --mnemonic "$MNEMONIC" \ - $HISTORY_FACTORY_ADDRESS \ - "newHistory(address,bytes32)(History)" \ - $AUTHORITY_ADDRESS \ - $SALT \ - --rpc-url "$RPC_URL" - ``` - - The `cast send` command will fail if Cast does not recognize the _History_ type during execution. In such cases, replace _History_ with `address` as the return type for `newHistory()` and execute the command again. - -The `cast send` command may also fail due to gas estimation issues. To circumvent this, provide gas constraints with the `--gas-limit` parameter (e.g., `--gas-limit 7000000`). - -#### 3. Replace the _History_ - -Ensure the environment variables from the previous step are set, including `NEW_HISTORY_ADDRESS`, which should have the address of the new History. - -To replace the _History_ used by the _Authority_, run this command: - -```shell -cast send \ - --json \ - --mnemonic "$MNEMONIC" \ - "$AUTHORITY_ADDRESS" \ - "setHistory(address)" \ - "$NEW_HISTORY_ADDRESS" \ - --rpc-url "$RPC_URL" -``` - -After replacing the _History_, update the `CARTESI_CONTRACTS_HISTORY_ADDRESS` in the application configuration with the new _History_ address. Then, upgrade the Cartesi Rollups Node as usual. - -When the Cartesi Rollups Node restarts, it processes all existing inputs, recalculates the epochs, and sends the claims to the new _History_ based on the updated configuration. From 6c3b9ef944785c700220f045a36f16b95ca98a8a Mon Sep 17 00:00:00 2001 From: Joao Garcia Date: Mon, 9 Dec 2024 19:15:15 +0000 Subject: [PATCH 26/99] update on structure, spliting architecture concepts and installation back on development (cherry picked from commit 2b2afa5e76a95a4a668fd39c46793b9981d8ad4b) --- .../installation.md | 0 .../architecture.md | 56 ---------------- .../version-2.0/getting-started/concepts.md | 64 +++++++++++++++++++ .../version-2.0-sidebars.json | 7 +- docusaurus.config.js | 8 --- 5 files changed, 68 insertions(+), 67 deletions(-) rename cartesi-rollups_versioned_docs/version-2.0/{getting-started => development}/installation.md (100%) rename cartesi-rollups_versioned_docs/version-2.0/{api-reference => getting-started}/architecture.md (64%) create mode 100644 cartesi-rollups_versioned_docs/version-2.0/getting-started/concepts.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/getting-started/installation.md b/cartesi-rollups_versioned_docs/version-2.0/development/installation.md similarity index 100% rename from cartesi-rollups_versioned_docs/version-2.0/getting-started/installation.md rename to cartesi-rollups_versioned_docs/version-2.0/development/installation.md diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/architecture.md b/cartesi-rollups_versioned_docs/version-2.0/getting-started/architecture.md similarity index 64% rename from cartesi-rollups_versioned_docs/version-2.0/api-reference/architecture.md rename to cartesi-rollups_versioned_docs/version-2.0/getting-started/architecture.md index 8febf875..de919281 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/api-reference/architecture.md +++ b/cartesi-rollups_versioned_docs/version-2.0/getting-started/architecture.md @@ -26,49 +26,6 @@ A decentralized application (dApp) built on Cartesi incorporates several key ele - Frontend: The application’s user-facing interface, typically implemented as a web application or a command-line interface tool. -## What is a Blockchain Rollup? - -A rollup is a blockchain scalability solution that offloads complex computations "off-chain," meaning they run on a separate computing environment (execution layer) outside the base layer, such as Ethereum. - -When employing rollups, the blockchain receives and logs transactions. In rare instances of an active attack or the involvement of a malicious agent, parties may disagree with a computation’s outcomes, and the blockchain will resolve these disputes. However, it's important to note that disagreements are not expected to occur under normal circumstances. - - -Users interact with a rollup through transactions on the base layer. They send messages (inputs) to the rollup on-chain smart contracts to define a computation to be processed and, as such, advance the state of the computing environment on the execution layer. Interested parties run an off-chain component (a node on the execution layer) that watches the blockchain for inputs, understanding, and executing the state updates. - -Once in a while, the state is checkpointed on the chain; at this point, it is considered finalized and can thus be accepted by any smart contract on the base layer. - -Ensuring this operation is secure is vital, meaning that the execution layer node must somehow prove the new state to the base layer. - -Consider this question: _"How does Ethereum know that the data posted by an off-chain L2 node is valid and was not submitted maliciously?"_ - -The answer depends on the rollup implementation, which falls within one of two categories according to the type of proof used: - -1. **Zero-knowledge Rollups (ZK Rollups)**, which use validity proofs. - -2. **Optimistic Rollups (ORs)**, which use fraud proofs. - -### Zero-knowledge Rollups (ZK Rollups) - -In ZK rollups, which use validity proof schemes, every state update is accompanied by a cryptographic proof created off-chain, attesting to its validity. The update is only taken if the proof successfully passes verification on-chain. Validity proofs(ZK Rollups) bring the enormous benefit of instant finality—as soon as a state update appears on-chain, it can be fully trusted and acted upon. - -The choice, however, also brings less than ideal properties: generating ZK proofs for general-purpose computations is, when possible, immensely expensive, and each on-chain state update must pay the extra gas fee for including and verifying a validity proof. - -### Optimistic Rollups (ORs) - -Optimistic Rollups, which use fraud-proof schemes, work by a different paradigm. State updates come unaccompanied by proofs; they’re proposed and, if not challenged, confirmed on-chain. Challenging a state update proposal using fraud proofs has two categories: **non-interactive** and **interactive**. - -Non-interactive refers to the fact that the challengers can prove that a state update is invalid in one step. With interactive fraud proofs, the claimer and challenger must, mediated by the blockchain, partake in something similar to a verification game. - -The assumption that state updates will likely be honest often gives solutions like this the name of Optimistic Rollups. - -This optimism is reinforced by financial incentives that reward honest behavior. Furthermore, any proposed false state will only be accepted if it remains undisputed for a prolonged period. - - -The main advantage of Optimistic Rollups is that they are much cheaper than ZK Rollups. Posting a state update on-chain is minimal, and challenging a state update is also low. - -The main disadvantage is that state updates take time to finalize and are not entirely accepted immediately. During this period, they are considered "optimistic" and can be challenged. - - ## Cartesi Rollups Cartesi's Optimistic Rollups adopt interactive fraud proofs to handle disputes. @@ -80,8 +37,6 @@ Transactions and computations occur off-chain, leading to more intricate logic w Cartesi's architecture specializes in app-specific rollups(appchains). Each dApp has its dedicated rollup for off-chain computation, enhancing scalability and performance. - - ![img](../../../static/img/v1.5/architecture-overview.jpg) ## Cartesi Machine @@ -205,14 +160,3 @@ The validation process ensures the integrity of the off-chain computations: - The application frontend can fetch proofs for specific outputs within a closed epoch. - These proofs can validate outputs on-chain, such as validating notices or executing vouchers. - -## Introducing Dave — an interactive fraud-proof system - -[Dave](https://github.com/cartesi/dave) is Cartesi's dispute resolution algorithm designed to address shortcomings in existing fraud-proof protocols. Traditional fraud-proof systems often face challenges such as delay attacks and vulnerability to Sybil attacks, where malicious nodes can disrupt operations by continuously challenging transactions or overwhelming honest validators. - -Dave introduces an approach where the resources required to defend against disputes grow logarithmically with the number of opponents. This means that defending against challenges remains affordable for a single honest node, even in the face of multiple attackers. - -With Dave, a single honest participant can effectively defend their claims on-chain, ensuring the integrity of transactions without relying on trust in validators. Based on the [Permissionless Refereed Tournaments algorithm](https://arxiv.org/abs/2212.12439), this protocol empowers anyone to validate rollups and uphold correct states on-chain, enhancing transaction security and reliability. - -Similar to how a consensus algorithm is crucial for achieving agreement on a single state of the blockchain among all nodes in a base-layer chain, Dave plays a fundamental role in ensuring the integrity and trustworthiness of state transitions within Cartesi Rollups. - diff --git a/cartesi-rollups_versioned_docs/version-2.0/getting-started/concepts.md b/cartesi-rollups_versioned_docs/version-2.0/getting-started/concepts.md new file mode 100644 index 00000000..cefcf22f --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-2.0/getting-started/concepts.md @@ -0,0 +1,64 @@ +--- +id: concepts +title: Concepts +resources: + - url: https://cartesi.io/blog/understanding-cartesi-rollups-pt2/ + title: Understanding Cartesi Rollups + - url: https://github.com/cartesi/dave + title: Dave +--- + +Before diving deep into the architecture and mechanisms of rollups, it's important to understand the fundamental concepts behind these scalability solutions. Rollups play a crucial role in enhancing blockchain performance by moving computation off-chain while ensuring security through on-chain verification. This section covers the core principles of rollups, the different approaches to validating state updates, and how dispute resolution mechanisms function. These foundational ideas will help you better grasp the architecture and innovations introduced by Cartesi's rollup solutions, including the **Dave** fraud-proof system. + +## What is a Blockchain Rollup? + +A rollup is a blockchain scalability solution that offloads complex computations "off-chain," meaning they run on a separate computing environment (execution layer) outside the base layer, such as Ethereum. + +When employing rollups, the blockchain receives and logs transactions. In rare instances of an active attack or the involvement of a malicious agent, parties may disagree with a computation’s outcomes, and the blockchain will resolve these disputes. However, it's important to note that disagreements are not expected to occur under normal circumstances. + + +Users interact with a rollup through transactions on the base layer. They send messages (inputs) to the rollup on-chain smart contracts to define a computation to be processed and, as such, advance the state of the computing environment on the execution layer. Interested parties run an off-chain component (a node on the execution layer) that watches the blockchain for inputs, understanding, and executing the state updates. + +Once in a while, the state is checkpointed on the chain; at this point, it is considered finalized and can thus be accepted by any smart contract on the base layer. + +Ensuring this operation is secure is vital, meaning that the execution layer node must somehow prove the new state to the base layer. + +Consider this question: _"How does Ethereum know that the data posted by an off-chain L2 node is valid and was not submitted maliciously?"_ + +The answer depends on the rollup implementation, which falls within one of two categories according to the type of proof used: + +1. **Zero-knowledge Rollups (ZK Rollups)**, which use validity proofs. + +2. **Optimistic Rollups (ORs)**, which use fraud proofs. + +### Zero-knowledge Rollups (ZK Rollups) + +In ZK rollups, which use validity proof schemes, every state update is accompanied by a cryptographic proof created off-chain, attesting to its validity. The update is only taken if the proof successfully passes verification on-chain. Validity proofs(ZK Rollups) bring the enormous benefit of instant finality—as soon as a state update appears on-chain, it can be fully trusted and acted upon. + +The choice, however, also brings less than ideal properties: generating ZK proofs for general-purpose computations is, when possible, immensely expensive, and each on-chain state update must pay the extra gas fee for including and verifying a validity proof. + +### Optimistic Rollups (ORs) + +Optimistic Rollups, which use fraud-proof schemes, work by a different paradigm. State updates come unaccompanied by proofs; they’re proposed and, if not challenged, confirmed on-chain. Challenging a state update proposal using fraud proofs has two categories: **non-interactive** and **interactive**. + +Non-interactive refers to the fact that the challengers can prove that a state update is invalid in one step. With interactive fraud proofs, the claimer and challenger must, mediated by the blockchain, partake in something similar to a verification game. + +The assumption that state updates will likely be honest often gives solutions like this the name of Optimistic Rollups. + +This optimism is reinforced by financial incentives that reward honest behavior. Furthermore, any proposed false state will only be accepted if it remains undisputed for a prolonged period. + + +The main advantage of Optimistic Rollups is that they are much cheaper than ZK Rollups. Posting a state update on-chain is minimal, and challenging a state update is also low. + +The main disadvantage is that state updates take time to finalize and are not entirely accepted immediately. During this period, they are considered "optimistic" and can be challenged. + +## Introducing Dave — an interactive fraud-proof system + +[Dave](https://github.com/cartesi/dave) is Cartesi's dispute resolution algorithm designed to address shortcomings in existing fraud-proof protocols. Traditional fraud-proof systems often face challenges such as delay attacks and vulnerability to Sybil attacks, where malicious nodes can disrupt operations by continuously challenging transactions or overwhelming honest validators. + +Dave introduces an approach where the resources required to defend against disputes grow logarithmically with the number of opponents. This means that defending against challenges remains affordable for a single honest node, even in the face of multiple attackers. + +With Dave, a single honest participant can effectively defend their claims on-chain, ensuring the integrity of transactions without relying on trust in validators. Based on the [Permissionless Refereed Tournaments algorithm](https://arxiv.org/abs/2212.12439), this protocol empowers anyone to validate rollups and uphold correct states on-chain, enhancing transaction security and reliability. + +Similar to how a consensus algorithm is crucial for achieving agreement on a single state of the blockchain among all nodes in a base-layer chain, Dave plays a fundamental role in ensuring the integrity and trustworthiness of state transitions within Cartesi Rollups. + diff --git a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json index f975a52f..0204ed88 100644 --- a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json +++ b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json @@ -6,8 +6,9 @@ "collapsed": true, "items": [ "getting-started/overview", - "getting-started/quickstart", - "getting-started/installation" + "getting-started/concepts", + "getting-started/architecture", + "getting-started/quickstart" ] }, { @@ -16,7 +17,6 @@ "collapsed": true, "items": [ "api-reference/http-api", - "api-reference/architecture", { "type": "category", "label": "Base Layer Contracts", @@ -110,6 +110,7 @@ "label": "Development", "collapsed": true, "items": [ + "development/installation", "development/building-a-dapp", "development/send-inputs", "development/query-outputs", diff --git a/docusaurus.config.js b/docusaurus.config.js index 21775972..e5fdcdd5 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -448,10 +448,6 @@ const config = { to: "/cartesi-rollups/2.0/getting-started/quickstart/", from: "/cartesi-rollups/2.0/quickstart/", }, - { - to: "/cartesi-rollups/2.0/getting-started/installation/", - from: "/cartesi-rollups/2.0/development/installation/", - }, { to: "/cartesi-rollups/2.0/api-reference/architecture/", from: "/cartesi-rollups/2.0/core-concepts/optimistic-rollups/", @@ -464,10 +460,6 @@ const config = { to: "/cartesi-rollups/2.0/resources/mainnet-considerations/", from: "/cartesi-rollups/2.0/core-concepts/mainnet-considerations/", }, - { - to: "/cartesi-rollups/2.0/getting-started/installation/", - from: "/cartesi-rollups/2.0/development/installation/", - }, { to: "/cartesi-rollups/2.0/development/building-a-dapp/", from: "/cartesi-rollups/2.0/development/creating-application/", From 4585074ef5857819c4fff76ac1fb041fb4cb8207 Mon Sep 17 00:00:00 2001 From: Joao Garcia Date: Mon, 23 Dec 2024 13:03:38 -0300 Subject: [PATCH 27/99] updated tutorials for v2 (cherry picked from commit 0efa578695c59ff46657c0899cc5979f175201cb) --- .../version-2.0/tutorials/calculator.md | 2 +- .../tutorials/erc-20-token-wallet.md | 14 +++++--------- .../tutorials/erc-721-token-wallet.md | 14 ++------------ .../version-2.0/tutorials/ether-wallet.md | 17 +++-------------- 4 files changed, 11 insertions(+), 36 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 204c52ce..52a922cc 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/tutorials/calculator.md +++ b/cartesi-rollups_versioned_docs/version-2.0/tutorials/calculator.md @@ -18,7 +18,7 @@ The backend will be written using Python. For added flexibility, feel free to ex Install these to set up your environment for quick building: -- Cartesi CLI: A simple tool for building applications on Cartesi. [Install Cartesi CLI for your OS of choice](../getting-started/installation.md). +- Cartesi CLI: A simple tool for building applications on Cartesi. [Install Cartesi CLI for your OS of choice](../development/installation.md). - Docker Desktop 4.x: The tool you need to run the Cartesi Machine and its dependencies. [Install Docker for your OS of choice](https://www.docker.com/products/docker-desktop/). diff --git a/cartesi-rollups_versioned_docs/version-2.0/tutorials/erc-20-token-wallet.md b/cartesi-rollups_versioned_docs/version-2.0/tutorials/erc-20-token-wallet.md index dfac251b..083ed66b 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/tutorials/erc-20-token-wallet.md +++ b/cartesi-rollups_versioned_docs/version-2.0/tutorials/erc-20-token-wallet.md @@ -137,18 +137,14 @@ export class Wallet { private parseErc20Deposit = (payload: string): [Address, Address, bigint] => { try { let inputData = []; - inputData[0] = ethers.dataSlice(payload, 0, 1); - inputData[1] = ethers.dataSlice(payload, 1, 21); - inputData[2] = ethers.dataSlice(payload, 21, 41); - inputData[3] = ethers.dataSlice(payload, 41, 73); + inputData[0] = ethers.dataSlice(payload, 0, 20); + inputData[1] = ethers.dataSlice(payload, 20, 40); + inputData[2] = ethers.dataSlice(payload, 40, 72); - if (!inputData[0]) { - throw new Error("ERC20 deposit unsuccessful"); - } return [ + getAddress(inputData[0]), getAddress(inputData[1]), - getAddress(inputData[2]), - BigInt(inputData[3]), + BigInt(inputData[2]), ]; } catch (e) { throw new Error(`Error parsing ERC20 deposit: ${e}`); diff --git a/cartesi-rollups_versioned_docs/version-2.0/tutorials/erc-721-token-wallet.md b/cartesi-rollups_versioned_docs/version-2.0/tutorials/erc-721-token-wallet.md index 018b69c1..e577b57a 100644 --- a/cartesi-rollups_versioned_docs/version-2.0/tutorials/erc-721-token-wallet.md +++ b/cartesi-rollups_versioned_docs/version-2.0/tutorials/erc-721-token-wallet.md @@ -223,7 +223,7 @@ export class Wallet { Now, let's create a simple wallet app at the entry point `src/index.ts` to test the wallet’s functionality. :::note -Run `cartesi address-book` to get the addresses of the `ERC721Portal` and `DAppAddressRelay` contracts. Save these as constants in the `index.ts` file. +Run `cartesi address-book` to get the addresses of the `ERC721Portal` contract. Save these as constants in the `index.ts` file. ::: ```typescript @@ -249,9 +249,6 @@ type AdvanceRequestHandler = ( const wallet = new Wallet(); const ERC721Portal = `0x237F8DD094C0e47f4236f12b4Fa01d6Dae89fb87`; -const dAppAddresRelay = `0xF5DE34d6BbC0446E2a45719E718efEbaaE179daE`; - -let dAppAddress: Address; const rollupServer = process.env.ROLLUP_HTTP_SERVER_URL; console.log("HTTP rollup_server url is " + rollupServer); @@ -259,15 +256,10 @@ console.log("HTTP rollup_server url is " + rollupServer); const handleAdvance: AdvanceRequestHandler = async (data) => { console.log("Received advance request data " + JSON.stringify(data)); + const dAppAddress = data["metadata"]["app_contract"]; const sender = data["metadata"]["msg_sender"]; const payload = data.payload; - if (sender.toLowerCase() === dAppAddresRelay.toLowerCase()) { - dAppAddress = data.payload; - - return "accept"; - } - if (sender.toLowerCase() === ERC721Portal.toLowerCase()) { // Handle deposit const deposit = wallet.processErc721Deposit(payload); @@ -396,8 +388,6 @@ Here is a breakdown of the wallet functionality: - We handle deposits when the sender is the `ERC721Portal`. -- We relay the dApp address when the sender is `DAppAddressRelay`. - - We parse the payload for other senders to determine the operation (`transfer` or `withdraw`). - For `transfers`, we call `wallet.transferErc721` and create a notice with the parsed parameters. 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 a1e42d9d..325d86f9 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 @@ -16,7 +16,7 @@ This tutorial is for educational purposes. For production dApps, we recommend us ## Setting up the project -First, create a new TypeScript project using the [Cartesi CLI](../getting-started/installation.md/#cartesi-cli). +First, create a new TypeScript project using the [Cartesi CLI](../development/installation.md/#cartesi-cli). ```bash cartesi create ether-wallet-dapp --template typescript @@ -290,10 +290,8 @@ Now, let's create a simple application at the entry point, `src/index.ts,` to te The [`EtherPortal`](../api-reference/json-rpc/portals/EtherPortal.md) contract allows anyone to perform transfers of Ether to a dApp. All deposits to a dApp are made via the `EtherPortal` contract. -The [`DAppAddressRelay`](../api-reference/json-rpc/relays/relays.md) contract provides the critical information (the dApp's address) that the voucher creation process needs to function correctly. Without this relay mechanism, the off-chain part of the dApp wouldn't know its on-chain address, making it impossible to create valid vouchers for withdrawals. - :::note -Run `cartesi address-book` to get the addresses of the `EtherPortal` and `DAppAddressRelay` contracts. Save these as constants in the `index.ts` file. +Run `cartesi address-book` to get the addresses of the `EtherPortal` contract. Save these as constants in the `index.ts` file. ::: ```typescript @@ -319,9 +317,6 @@ type AdvanceRequestHandler = ( const wallet = new Wallet(); const EtherPortal = `0xFfdbe43d4c855BF7e0f105c400A50857f53AB044`; -const dAppAddressRelay = `0xF5DE34d6BbC0446E2a45719E718efEbaaE179daE`; - -let dAppAddress: Address; const rollupServer = process.env.ROLLUP_HTTP_SERVER_URL; console.log("HTTP rollup_server url is " + rollupServer); @@ -329,14 +324,10 @@ console.log("HTTP rollup_server url is " + rollupServer); const handleAdvance: AdvanceRequestHandler = async (data) => { console.log("Received advance request data " + JSON.stringify(data)); + const dAppAddress = data["metadata"]["app_contract"]; const sender = data["metadata"]["msg_sender"]; const payload = data.payload; - if (sender.toLowerCase() === dAppAddressRelay.toLowerCase()) { - dAppAddress = data.payload; - return "accept"; - } - if (sender.toLowerCase() === EtherPortal.toLowerCase()) { // Handle deposit const deposit = wallet.depositEther(payload); @@ -456,8 +447,6 @@ Here is a breakdown of the wallet functionality: - The `handle_advance` handles three main scenarios: dApp address relay, Ether deposits, and user operations (transfers/withdrawals). -- We relay the address when the sender is `DAppAddressRelay`. - - We handle deposits and create a notice when the sender is the `EtherPortal`. - We parse the payload for other senders to determine the operation (`transfer` or `withdraw`). From 673f3b5d5aba52bf1636c5774523f4f4f764b68b Mon Sep 17 00:00:00 2001 From: Joro Date: Mon, 2 Dec 2024 16:32:51 +0200 Subject: [PATCH 28/99] admonition (cherry picked from commit 6b9e3f70ab9d05a566beb36498723451e4b473ce) --- src/css/custom.css | 37 ++ src/theme/Admonition/Icon/Danger.js | 21 ++ src/theme/Admonition/Icon/Goal.js | 25 ++ src/theme/Admonition/Icon/Info.js | 23 ++ src/theme/Admonition/Icon/Note.js | 23 ++ src/theme/Admonition/Icon/Tip.js | 22 ++ src/theme/Admonition/Icon/Troubleshoot.js | 19 ++ src/theme/Admonition/Icon/Warning.js | 32 ++ src/theme/Admonition/Layout/index.js | 39 +++ src/theme/Admonition/Layout/styles.module.css | 67 ++++ src/theme/Admonition/Type/Caution.js | 28 ++ src/theme/Admonition/Type/Danger.js | 26 ++ src/theme/Admonition/Type/Goal.js | 28 ++ src/theme/Admonition/Type/Info.js | 26 ++ src/theme/Admonition/Type/Note.js | 28 ++ src/theme/Admonition/Type/Tip.js | 26 ++ src/theme/Admonition/Type/Troubleshoot.js | 30 ++ src/theme/Admonition/Type/Warning.js | 26 ++ src/theme/Admonition/Types.js | 34 ++ src/theme/Admonition/index.js | 315 +----------------- src/theme/Admonition/styles.module.css | 53 --- 21 files changed, 573 insertions(+), 355 deletions(-) create mode 100644 src/theme/Admonition/Icon/Danger.js create mode 100644 src/theme/Admonition/Icon/Goal.js create mode 100644 src/theme/Admonition/Icon/Info.js create mode 100644 src/theme/Admonition/Icon/Note.js create mode 100644 src/theme/Admonition/Icon/Tip.js create mode 100644 src/theme/Admonition/Icon/Troubleshoot.js create mode 100644 src/theme/Admonition/Icon/Warning.js create mode 100644 src/theme/Admonition/Layout/index.js create mode 100644 src/theme/Admonition/Layout/styles.module.css create mode 100644 src/theme/Admonition/Type/Caution.js create mode 100644 src/theme/Admonition/Type/Danger.js create mode 100644 src/theme/Admonition/Type/Goal.js create mode 100644 src/theme/Admonition/Type/Info.js create mode 100644 src/theme/Admonition/Type/Note.js create mode 100644 src/theme/Admonition/Type/Tip.js create mode 100644 src/theme/Admonition/Type/Troubleshoot.js create mode 100644 src/theme/Admonition/Type/Warning.js create mode 100644 src/theme/Admonition/Types.js delete mode 100644 src/theme/Admonition/styles.module.css diff --git a/src/css/custom.css b/src/css/custom.css index 8d019167..29378e02 100755 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -537,4 +537,41 @@ html[data-theme="dark"] .dropdown-version .navbar__item--version:before { margin-left: 0.5rem; } +.alert--goal { + padding: 0; + background: var(--ifm-color-warning-contrast-background); + overflow: hidden; +} + +.alert--goal div:first-child { + padding: 0.75rem var(--ifm-alert-padding-horizontal); + background: var(--ifm-color-warning-dark); + line-height: 1; + color: var(--ifm-alert-foreground-color); +} + +.alert--goal div:last-child { + padding: var(--ifm-alert-padding-vertical) var(--ifm-alert-padding-horizontal); +} + +.alert--goal svg { + stroke: currentColor !important; +} + +html[data-theme="dark"] .alert--goal div:first-child { + color: var(--ifm-background-color); +} + +.alert--troubleshoot { + --ifm-alert-background-color: var(--ifm-color-info-contrast-background); + --ifm-alert-background-color-highlight: rgba(84, 199, 236, 0.15); + --ifm-alert-foreground-color: var(--ifm-color-info-contrast-foreground); + --ifm-alert-border-color: var(--ifm-color-info-dark); +} + +.alert--troubleshoot svg { + fill: var(--ifm-alert-foreground-color); + stroke: none !important; +} + @tailwind utilities; diff --git a/src/theme/Admonition/Icon/Danger.js b/src/theme/Admonition/Icon/Danger.js new file mode 100644 index 00000000..ca249370 --- /dev/null +++ b/src/theme/Admonition/Icon/Danger.js @@ -0,0 +1,21 @@ +import React from "react"; +export default function AdmonitionIconDanger(props) { + return ( + + + + + ); +} diff --git a/src/theme/Admonition/Icon/Goal.js b/src/theme/Admonition/Icon/Goal.js new file mode 100644 index 00000000..048c2c7b --- /dev/null +++ b/src/theme/Admonition/Icon/Goal.js @@ -0,0 +1,25 @@ +import React from "react"; +export default function AdmonitionIconGoal(props) { + return ( + + + + + + + + + ); +} diff --git a/src/theme/Admonition/Icon/Info.js b/src/theme/Admonition/Icon/Info.js new file mode 100644 index 00000000..c4c5522c --- /dev/null +++ b/src/theme/Admonition/Icon/Info.js @@ -0,0 +1,23 @@ +import React from "react"; +export default function AdmonitionIconInfo(props) { + return ( + + + + + + + ); +} diff --git a/src/theme/Admonition/Icon/Note.js b/src/theme/Admonition/Icon/Note.js new file mode 100644 index 00000000..44108ba7 --- /dev/null +++ b/src/theme/Admonition/Icon/Note.js @@ -0,0 +1,23 @@ +import React from "react"; +export default function AdmonitionIconNote(props) { + return ( + + + + + ); +} diff --git a/src/theme/Admonition/Icon/Tip.js b/src/theme/Admonition/Icon/Tip.js new file mode 100644 index 00000000..c22586f0 --- /dev/null +++ b/src/theme/Admonition/Icon/Tip.js @@ -0,0 +1,22 @@ +import React from "react"; +export default function AdmonitionIconTip(props) { + return ( + + + + + + ); +} diff --git a/src/theme/Admonition/Icon/Troubleshoot.js b/src/theme/Admonition/Icon/Troubleshoot.js new file mode 100644 index 00000000..edcb4540 --- /dev/null +++ b/src/theme/Admonition/Icon/Troubleshoot.js @@ -0,0 +1,19 @@ +import React from "react"; +export default function AdmonitionIconTroubleshoot(props) { + return ( + + + + ); +} diff --git a/src/theme/Admonition/Icon/Warning.js b/src/theme/Admonition/Icon/Warning.js new file mode 100644 index 00000000..a4264abd --- /dev/null +++ b/src/theme/Admonition/Icon/Warning.js @@ -0,0 +1,32 @@ +import React from "react"; +export default function AdmonitionIconCaution(props) { + return ( + + + + + + ); +} diff --git a/src/theme/Admonition/Layout/index.js b/src/theme/Admonition/Layout/index.js new file mode 100644 index 00000000..074a1c54 --- /dev/null +++ b/src/theme/Admonition/Layout/index.js @@ -0,0 +1,39 @@ +import React from 'react'; +import clsx from 'clsx'; +import {ThemeClassNames} from '@docusaurus/theme-common'; +import styles from './styles.module.css'; +function AdmonitionContainer({type, className, children}) { + return ( +

+ {children} +
+ ); +} +function AdmonitionHeading({icon, title}) { + return ( +
+ {icon} + {title} +
+ ); +} +function AdmonitionContent({children}) { + return children ? ( +
{children}
+ ) : null; +} +export default function AdmonitionLayout(props) { + const {type, icon, title, children, className} = props; + return ( + + {title || icon ? : null} + {children} + + ); +} diff --git a/src/theme/Admonition/Layout/styles.module.css b/src/theme/Admonition/Layout/styles.module.css new file mode 100644 index 00000000..2d61d4f0 --- /dev/null +++ b/src/theme/Admonition/Layout/styles.module.css @@ -0,0 +1,67 @@ +.admonition { + margin-bottom: 1em; +} + +.admonitionHeading { + font: var(--ifm-heading-font-weight) var(--ifm-h5-font-size) / + var(--ifm-heading-line-height) var(--ifm-heading-font-family); + text-transform: uppercase; +} + +/* Heading alone without content (does not handle fragment content) */ +.admonitionHeading:not(:last-child) { + margin-bottom: 0.3rem; +} + +.admonitionHeading code { + text-transform: none; +} + +.admonitionIcon { + display: inline-block; + vertical-align: middle; + margin-right: 0.4em; +} + +.admonitionIcon svg { + display: inline-block; + height: 1.6em; + width: 1.6em; + stroke: var(--ifm-alert-foreground-color); +} + +.admonitionContent > :last-child { + margin-bottom: 0; +} + +.admonition { + margin-bottom: 2em; +} + +.admonitionHeading { + font: var(--ifm-heading-font-weight) var(--ifm-h5-font-size) / + var(--ifm-heading-line-height) var(--ifm-heading-font-family); + text-transform: uppercase; + margin-bottom: 0.3rem; +} + +.admonitionHeading code { + text-transform: none; +} + +.admonitionIcon { + display: inline-block; + vertical-align: middle; + margin-right: 0.4em; +} + +.admonitionIcon svg { + display: inline-block; + height: 1.25rem; + width: 1.25rem; + stroke-color: var(--ifm-alert-foreground-color); +} + +.admonitionContent > :last-child { + margin-bottom: 0; +} diff --git a/src/theme/Admonition/Type/Caution.js b/src/theme/Admonition/Type/Caution.js new file mode 100644 index 00000000..7890ef7c --- /dev/null +++ b/src/theme/Admonition/Type/Caution.js @@ -0,0 +1,28 @@ +import React from 'react'; +import clsx from 'clsx'; +import Translate from '@docusaurus/Translate'; +import AdmonitionLayout from '@theme/Admonition/Layout'; +import IconWarning from '@theme/Admonition/Icon/Warning'; +const infimaClassName = 'alert alert--warning'; +const defaultProps = { + icon: , + title: ( + + caution + + ), +}; +// TODO remove before v4: Caution replaced by Warning +// see https://github.com/facebook/docusaurus/issues/7558 +export default function AdmonitionTypeCaution(props) { + return ( + + {props.children} + + ); +} diff --git a/src/theme/Admonition/Type/Danger.js b/src/theme/Admonition/Type/Danger.js new file mode 100644 index 00000000..7a8eb840 --- /dev/null +++ b/src/theme/Admonition/Type/Danger.js @@ -0,0 +1,26 @@ +import React from 'react'; +import clsx from 'clsx'; +import Translate from '@docusaurus/Translate'; +import AdmonitionLayout from '@theme/Admonition/Layout'; +import IconDanger from '@theme/Admonition/Icon/Danger'; +const infimaClassName = 'alert alert--danger'; +const defaultProps = { + icon: , + title: ( + + danger + + ), +}; +export default function AdmonitionTypeDanger(props) { + return ( + + {props.children} + + ); +} diff --git a/src/theme/Admonition/Type/Goal.js b/src/theme/Admonition/Type/Goal.js new file mode 100644 index 00000000..79268beb --- /dev/null +++ b/src/theme/Admonition/Type/Goal.js @@ -0,0 +1,28 @@ +import React from "react"; +import clsx from "clsx"; +import Translate from "@docusaurus/Translate"; +import AdmonitionLayout from "@theme/Admonition/Layout"; +import IconGoal from "@theme/Admonition/Icon/Goal"; +const infimaClassName = "alert alert--goal"; +const defaultProps = { + icon: , + title: ( + + goal + + ), +}; +export default function AdmonitionTypeGoal(props) { + return ( + + {props.children} + + ); +} diff --git a/src/theme/Admonition/Type/Info.js b/src/theme/Admonition/Type/Info.js new file mode 100644 index 00000000..f0e9d2b6 --- /dev/null +++ b/src/theme/Admonition/Type/Info.js @@ -0,0 +1,26 @@ +import React from 'react'; +import clsx from 'clsx'; +import Translate from '@docusaurus/Translate'; +import AdmonitionLayout from '@theme/Admonition/Layout'; +import IconInfo from '@theme/Admonition/Icon/Info'; +const infimaClassName = 'alert alert--info'; +const defaultProps = { + icon: , + title: ( + + info + + ), +}; +export default function AdmonitionTypeInfo(props) { + return ( + + {props.children} + + ); +} diff --git a/src/theme/Admonition/Type/Note.js b/src/theme/Admonition/Type/Note.js new file mode 100644 index 00000000..6fb240dd --- /dev/null +++ b/src/theme/Admonition/Type/Note.js @@ -0,0 +1,28 @@ +import React from "react"; +import clsx from "clsx"; +import Translate from "@docusaurus/Translate"; +import AdmonitionLayout from "@theme/Admonition/Layout"; +import IconNote from "@theme/Admonition/Icon/Note"; +const infimaClassName = "alert alert--info"; +const defaultProps = { + icon: , + title: ( + + note + + ), +}; +export default function AdmonitionTypeNote(props) { + return ( + + {props.children} + + ); +} diff --git a/src/theme/Admonition/Type/Tip.js b/src/theme/Admonition/Type/Tip.js new file mode 100644 index 00000000..f31e4208 --- /dev/null +++ b/src/theme/Admonition/Type/Tip.js @@ -0,0 +1,26 @@ +import React from 'react'; +import clsx from 'clsx'; +import Translate from '@docusaurus/Translate'; +import AdmonitionLayout from '@theme/Admonition/Layout'; +import IconTip from '@theme/Admonition/Icon/Tip'; +const infimaClassName = 'alert alert--success'; +const defaultProps = { + icon: , + title: ( + + tip + + ), +}; +export default function AdmonitionTypeTip(props) { + return ( + + {props.children} + + ); +} diff --git a/src/theme/Admonition/Type/Troubleshoot.js b/src/theme/Admonition/Type/Troubleshoot.js new file mode 100644 index 00000000..4dfc3fce --- /dev/null +++ b/src/theme/Admonition/Type/Troubleshoot.js @@ -0,0 +1,30 @@ +import React from "react"; +import clsx from "clsx"; +import Translate from "@docusaurus/Translate"; +import AdmonitionLayout from "@theme/Admonition/Layout"; +import IconTroubleshoot from "@theme/Admonition/Icon/Troubleshoot"; +const infimaClassName = "alert alert--troubleshoot"; +const defaultProps = { + icon: , + title: ( + + troubleshoot + + ), +}; +// TODO remove before v4: Caution replaced by Warning +// see https://github.com/facebook/docusaurus/issues/7558 +export default function AdmonitionTypeTroubleshoot(props) { + return ( + + {props.children} + + ); +} diff --git a/src/theme/Admonition/Type/Warning.js b/src/theme/Admonition/Type/Warning.js new file mode 100644 index 00000000..b507d8d9 --- /dev/null +++ b/src/theme/Admonition/Type/Warning.js @@ -0,0 +1,26 @@ +import React from 'react'; +import clsx from 'clsx'; +import Translate from '@docusaurus/Translate'; +import AdmonitionLayout from '@theme/Admonition/Layout'; +import IconWarning from '@theme/Admonition/Icon/Warning'; +const infimaClassName = 'alert alert--warning'; +const defaultProps = { + icon: , + title: ( + + warning + + ), +}; +export default function AdmonitionTypeWarning(props) { + return ( + + {props.children} + + ); +} diff --git a/src/theme/Admonition/Types.js b/src/theme/Admonition/Types.js new file mode 100644 index 00000000..f5618dbb --- /dev/null +++ b/src/theme/Admonition/Types.js @@ -0,0 +1,34 @@ +import React from "react"; +import AdmonitionTypeNote from "@theme/Admonition/Type/Note"; +import AdmonitionTypeTip from "@theme/Admonition/Type/Tip"; +import AdmonitionTypeInfo from "@theme/Admonition/Type/Info"; +import AdmonitionTypeWarning from "@theme/Admonition/Type/Warning"; +import AdmonitionTypeDanger from "@theme/Admonition/Type/Danger"; +import AdmonitionTypeCaution from "@theme/Admonition/Type/Caution"; +import AdmonitionTypeTroubleshoot from "@theme/Admonition/Type/Troubleshoot"; +import AdmonitionTypeGoal from "@theme/Admonition/Type/Goal"; +const admonitionTypes = { + note: AdmonitionTypeNote, + tip: AdmonitionTypeTip, + info: AdmonitionTypeInfo, + warning: AdmonitionTypeWarning, + danger: AdmonitionTypeDanger, + troubleshoot: AdmonitionTypeCaution, + goal: AdmonitionTypeGoal, +}; +// Undocumented legacy admonition type aliases +// Provide hardcoded/untranslated retrocompatible label +// See also https://github.com/facebook/docusaurus/issues/7767 +const admonitionAliases = { + secondary: (props) => , + important: (props) => , + success: (props) => , + caution: AdmonitionTypeCaution, + troubleshoot: (props) => ( + + ), +}; +export default { + ...admonitionTypes, + ...admonitionAliases, +}; diff --git a/src/theme/Admonition/index.js b/src/theme/Admonition/index.js index b3c89ea4..a6ecab74 100644 --- a/src/theme/Admonition/index.js +++ b/src/theme/Admonition/index.js @@ -1,307 +1,18 @@ -import React from "react"; -import clsx from "clsx"; -import { ThemeClassNames } from "@docusaurus/theme-common"; -import Translate from "@docusaurus/Translate"; -import styles from "./styles.module.css"; -function NoteIcon() { - return ( - - - - - ); -} -function TroubleshootIcon() { - return ( - - - - ); -} -function TipIcon() { - return ( - - - - - - ); -} -function DangerIcon() { - return ( - - - - - ); -} -function InfoIcon() { - return ( - - - - - - - ); -} -function CautionIcon() { - return ( - - - - - - - ); -} -function GoalIcon() { - return ( - - - - - - - - - ); -} -// eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style -const AdmonitionConfigs = { - note: { - infimaClassName: "info", - iconComponent: NoteIcon, - label: ( - - note - - ), - }, - troubleshoot: { - infimaClassName: "info", - iconComponent: TroubleshootIcon, - label: ( - - troubleshoot - - ), - }, - tip: { - infimaClassName: "success", - iconComponent: TipIcon, - label: ( - - tip - - ), - }, - danger: { - infimaClassName: "danger", - iconComponent: DangerIcon, - label: ( - - danger - - ), - }, - info: { - infimaClassName: "info", - iconComponent: InfoIcon, - label: ( - - info - - ), - }, - caution: { - infimaClassName: "warning", - iconComponent: CautionIcon, - label: ( - - caution - - ), - }, - goal: { - infimaClassName: "goal", - iconComponent: GoalIcon, - label: ( - - Section Goal - - ), - }, -}; -// Legacy aliases, undocumented but kept for retro-compatibility -const aliases = { - secondary: "note", - important: "info", - success: "tip", - warning: "danger", -}; -function getAdmonitionConfig(unsafeType) { - const type = aliases[unsafeType] ?? unsafeType; - const config = AdmonitionConfigs[type]; - if (config) { - return config; +import React from 'react'; +import {processAdmonitionProps} from '@docusaurus/theme-common'; +import AdmonitionTypes from '@theme/Admonition/Types'; +function getAdmonitionTypeComponent(type) { + const component = AdmonitionTypes[type]; + if (component) { + return component; } console.warn( - `No admonition config found for admonition type "${type}". Using Info as fallback.` + `No admonition component found for admonition type "${type}". Using Info as fallback.`, ); - return AdmonitionConfigs.info; + return AdmonitionTypes.info; } -// Workaround because it's difficult in MDX v1 to provide a MDX title as props -// See https://github.com/facebook/docusaurus/pull/7152#issuecomment-1145779682 -function extractMDXAdmonitionTitle(children) { - const items = React.Children.toArray(children); - const mdxAdmonitionTitle = items.find( - (item) => - React.isValidElement(item) && item.props?.mdxType === "mdxAdmonitionTitle" - ); - const rest = <>{items.filter((item) => item !== mdxAdmonitionTitle)}; - return { - mdxAdmonitionTitle, - rest, - }; -} -function processAdmonitionProps(props) { - const { mdxAdmonitionTitle, rest } = extractMDXAdmonitionTitle( - props.children - ); - return { - ...props, - title: props.title ?? mdxAdmonitionTitle, - children: rest, - }; -} -export default function Admonition(props) { - const { - children, - type, - title, - icon: iconProp, - } = processAdmonitionProps(props); - const typeConfig = getAdmonitionConfig(type); - const titleLabel = title ?? typeConfig.label; - const { iconComponent: IconComponent } = typeConfig; - const icon = iconProp ?? ; - - return ( -
-
- {icon} - {titleLabel} -
-
{children}
-
- ); +export default function Admonition(unprocessedProps) { + const props = processAdmonitionProps(unprocessedProps); + const AdmonitionTypeComponent = getAdmonitionTypeComponent(props.type); + return ; } diff --git a/src/theme/Admonition/styles.module.css b/src/theme/Admonition/styles.module.css deleted file mode 100644 index 126a0488..00000000 --- a/src/theme/Admonition/styles.module.css +++ /dev/null @@ -1,53 +0,0 @@ -.admonition { - margin-bottom: 2em; -} - -.admonitionHeading { - font: var(--ifm-heading-font-weight) var(--ifm-h5-font-size) / - var(--ifm-heading-line-height) var(--ifm-heading-font-family); - text-transform: uppercase; - margin-bottom: 0.3rem; -} - -.admonitionHeading code { - text-transform: none; -} - -.admonitionIcon { - display: inline-block; - vertical-align: middle; - margin-right: 0.4em; -} - -.admonitionIcon svg { - display: inline-block; - height: 1.25rem; - width: 1.25rem; - stroke-color: var(--ifm-alert-foreground-color); -} - -.admonitionContent > :last-child { - margin-bottom: 0; -} - -/* Goal */ -.admonition-goal { - padding: 0; - background: var(--ifm-color-warning-contrast-background); - overflow: hidden; -} - -.admonition-goal .admonitionHeading { - padding: 0.75rem var(--ifm-alert-padding-horizontal); - background: var(--ifm-color-warning-dark); - line-height: 1; - color: var(--ifm-alert-foreground-color); -} - -html[data-theme="dark"] .admonition-goal .admonitionHeading { - color: var(--ifm-background-color); -} - -.admonition-goal .admonitionContent { - padding: var(--ifm-alert-padding-vertical) var(--ifm-alert-padding-horizontal); -} From a86109ffb301b7023b04c9cac40ef1e62303e919 Mon Sep 17 00:00:00 2001 From: Joro Date: Mon, 2 Dec 2024 16:44:38 +0200 Subject: [PATCH 29/99] announcement bar (cherry picked from commit 5d7ce31485c7a7cb4fb38cc4e47fb95ed493da0e) --- .../CloseButton/styles.module.css | 1 + src/theme/AnnouncementBar/Content/index.js | 41 ++++++----- src/theme/AnnouncementBar/index.js | 68 ++++++------------- 3 files changed, 41 insertions(+), 69 deletions(-) diff --git a/src/theme/AnnouncementBar/CloseButton/styles.module.css b/src/theme/AnnouncementBar/CloseButton/styles.module.css index 0494ec0d..02d15534 100644 --- a/src/theme/AnnouncementBar/CloseButton/styles.module.css +++ b/src/theme/AnnouncementBar/CloseButton/styles.module.css @@ -1,4 +1,5 @@ .closeButton { padding: 0; line-height: 0; + color: currentColor; } diff --git a/src/theme/AnnouncementBar/Content/index.js b/src/theme/AnnouncementBar/Content/index.js index 5babebbc..a8c7df0b 100644 --- a/src/theme/AnnouncementBar/Content/index.js +++ b/src/theme/AnnouncementBar/Content/index.js @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo, useState } from "react"; +import React, { useEffect, useState, useMemo } from "react"; import clsx from "clsx"; import { useThemeConfig } from "@docusaurus/theme-common"; import styles from "./styles.module.css"; @@ -18,6 +18,22 @@ const client = createPublicClient({ transport, }); +function formatNumber(num) { + const absNum = Math.abs(num); + + if (absNum >= 1000000000000) { + return (num / 1000000000000).toFixed(1) + "T"; + } else if (absNum >= 1000000000) { + return (num / 1000000000).toFixed(1) + "B"; + } else if (absNum >= 1000000) { + return (num / 1000000).toFixed(1) + "M"; + } else if (absNum >= 1000) { + return (num / 1000).toFixed(1) + "K"; + } else { + return num.toString(); + } +} + export default function AnnouncementBarContent(props) { const { announcementBar } = useThemeConfig(); const { content } = announcementBar; @@ -26,22 +42,6 @@ export default function AnnouncementBarContent(props) { const [dynamicContent, setDynamicContent] = useState(content); const [balanceLoaded, setBalanceLoaded] = useState(false); - function formatNumber(num) { - const absNum = Math.abs(num); - - if (absNum >= 1000000000000) { - return (num / 1000000000000).toFixed(1) + "T"; - } else if (absNum >= 1000000000) { - return (num / 1000000000).toFixed(1) + "B"; - } else if (absNum >= 1000000) { - return (num / 1000000).toFixed(1) + "M"; - } else if (absNum >= 1000) { - return (num / 1000).toFixed(1) + "K"; - } else { - return num.toString(); - } - } - // Fetch data for variables in the content const getBalance = async () => { if (typeof window === "undefined") { @@ -94,17 +94,16 @@ export default function AnnouncementBarContent(props) { } updateContent(); - }, [contentVars, balanceLoaded]); + console.log("Content Vars: ", contentVars); + }, [contentVars, balanceLoaded]); return (
); } diff --git a/src/theme/AnnouncementBar/index.js b/src/theme/AnnouncementBar/index.js index d6249de0..1463d896 100644 --- a/src/theme/AnnouncementBar/index.js +++ b/src/theme/AnnouncementBar/index.js @@ -1,63 +1,35 @@ -import React, { useEffect, useMemo } from "react"; +import React from "react"; import { useThemeConfig } from "@docusaurus/theme-common"; import { useAnnouncementBar } from "@docusaurus/theme-common/internal"; import AnnouncementBarCloseButton from "@theme/AnnouncementBar/CloseButton"; import AnnouncementBarContent from "@theme/AnnouncementBar/Content"; -import { CookiesProvider, useCookies } from "react-cookie"; - import styles from "./styles.module.css"; + export default function AnnouncementBar() { const { announcementBar } = useThemeConfig(); if (!announcementBar) return null; const { isActive, close } = useAnnouncementBar(); - - const COOKIE_EXPIRY = 1000 * 60 * 60 * 24 * 2; - // const COOKIE_EXPIRY = 1000 * 10; - const COOKIE_NAME = "docusaurus.announcement.dismissExpiry"; - - const [cookies, setCookie] = useCookies([COOKIE_NAME]); - - const handleClose = () => { - setCookie(COOKIE_NAME, true, { - expires: new Date(Date.now() + COOKIE_EXPIRY), - }); - close(); - }; - - const cookieExpired = useMemo(() => { - return !cookies[COOKIE_NAME]; - }, [cookies]); - - const showAnnouncementBar = useMemo(() => { - return isActive ? isActive : cookieExpired; - }, [isActive, cookieExpired]); - - useEffect(() => { - if (!showAnnouncementBar) return; - document.documentElement.dataset.announcementBarInitiallyDismissed = false; - }, [showAnnouncementBar]); - + if (!isActive) { + return null; + } const { backgroundColor, textColor, isCloseable } = announcementBar; + return ( - - {showAnnouncementBar ? ( -
- {isCloseable &&
} - - {isCloseable && ( - - )} -
- ) : null} - +
+ {isCloseable &&
} + + {isCloseable && ( + + )} +
); } From 3c68bbb8b67258daa465563370c3fb9cc52f2425 Mon Sep 17 00:00:00 2001 From: Joro Date: Mon, 2 Dec 2024 16:53:42 +0200 Subject: [PATCH 30/99] api item (cherry picked from commit 6cc0bc0efe6d0874930610adff47e7c11c4db2cd) --- src/theme/ApiItem/Layout/index.js | 150 +++++++++++++------- src/theme/ApiItem/hooks.js | 7 + src/theme/ApiItem/index.js | 223 ++++++++++++++++++++++++++++++ src/theme/ApiItem/store.js | 47 +++++++ 4 files changed, 380 insertions(+), 47 deletions(-) create mode 100644 src/theme/ApiItem/hooks.js create mode 100644 src/theme/ApiItem/index.js create mode 100644 src/theme/ApiItem/store.js diff --git a/src/theme/ApiItem/Layout/index.js b/src/theme/ApiItem/Layout/index.js index d8d73baa..80c77d65 100644 --- a/src/theme/ApiItem/Layout/index.js +++ b/src/theme/ApiItem/Layout/index.js @@ -1,63 +1,119 @@ -import React from "react"; -import { useWindowSize } from "@docusaurus/theme-common"; -import { useDoc } from "@docusaurus/theme-common/internal"; -import DocBreadcrumbs from "@theme/DocBreadcrumbs"; -import DocItemContent from "@theme/DocItem/Content"; -import DocItemFooter from "@theme/DocItem/Footer"; -import DocItemPaginator from "@theme/DocItem/Paginator"; -import DocItemTOCDesktop from "@theme/DocItem/TOC/Desktop"; -import DocItemTOCMobile from "@theme/DocItem/TOC/Mobile"; -import DocVersionBanner from "@theme/DocVersionBanner"; -import clsx from "clsx"; -import styles from "./styles.module.css"; - +"use strict"; +var __importDefault = + (this && this.__importDefault) || + function (mod) { + return mod && mod.__esModule ? mod : { default: mod }; + }; +Object.defineProperty(exports, "__esModule", { value: true }); +const react_1 = __importDefault(require("react")); +const client_1 = require("@docusaurus/plugin-content-docs/client"); +const theme_common_1 = require("@docusaurus/theme-common"); +const ContentVisibility_1 = __importDefault( + require("@theme/ContentVisibility") +); +const DocBreadcrumbs_1 = __importDefault(require("@theme/DocBreadcrumbs")); +const Content_1 = __importDefault(require("@theme/DocItem/Content")); +const Footer_1 = __importDefault(require("@theme/DocItem/Footer")); +const Paginator_1 = __importDefault(require("@theme/DocItem/Paginator")); +const Desktop_1 = __importDefault(require("@theme/DocItem/TOC/Desktop")); +const Mobile_1 = __importDefault(require("@theme/DocItem/TOC/Mobile")); +const DocVersionBadge_1 = __importDefault(require("@theme/DocVersionBadge")); +const DocVersionBanner_1 = __importDefault(require("@theme/DocVersionBanner")); +const clsx_1 = __importDefault(require("clsx")); +const styles_module_css_1 = __importDefault(require("./styles.module.css")); /** * Decide if the toc should be rendered, on mobile or desktop viewports */ function useDocTOC() { - const { frontMatter, toc } = useDoc(); - const windowSize = useWindowSize(); + const { frontMatter, toc } = (0, client_1.useDoc)(); + const windowSize = (0, theme_common_1.useWindowSize)(); const hidden = frontMatter.hide_table_of_contents; const canRender = !hidden && toc.length > 0; - const mobile = canRender ? : undefined; + const mobile = canRender + ? react_1.default.createElement(Mobile_1.default, null) + : undefined; const desktop = - canRender && (windowSize === "desktop" || windowSize === "ssr") ? ( - - ) : undefined; + canRender && (windowSize === "desktop" || windowSize === "ssr") + ? react_1.default.createElement(Desktop_1.default, null) + : undefined; return { hidden, mobile, desktop, }; } -export default function DocItemLayout({ children }) { +function DocItemLayout({ children }) { const docTOC = useDocTOC(); - const { - frontMatter: { api }, - } = useDoc(); - return ( -
-
- -
-
- - {docTOC.mobile} - {children} -
- -
-
-
- -
-
-
- {docTOC.desktop && ( -
-
{docTOC.desktop}
-
- )} -
+ const { metadata } = (0, client_1.useDoc)(); + const { frontMatter } = (0, client_1.useDoc)(); + const api = frontMatter.api; + const schema = frontMatter.schema; + return react_1.default.createElement( + "div", + { className: "row" }, + react_1.default.createElement( + "div", + { + className: (0, clsx_1.default)( + "col", + !docTOC.hidden && styles_module_css_1.default.docItemCol + ), + }, + react_1.default.createElement(ContentVisibility_1.default, { + metadata: metadata, + }), + react_1.default.createElement(DocVersionBanner_1.default, null), + react_1.default.createElement( + "div", + { className: styles_module_css_1.default.docItemContainer }, + react_1.default.createElement( + "article", + null, + react_1.default.createElement(DocBreadcrumbs_1.default, null), + react_1.default.createElement(DocVersionBadge_1.default, null), + docTOC.mobile, + react_1.default.createElement(Content_1.default, null, children), + react_1.default.createElement( + "div", + { className: "row" }, + react_1.default.createElement( + "div", + { + className: (0, clsx_1.default)( + "col", + api || schema ? "col--7" : "col--12" + ), + }, + react_1.default.createElement(Footer_1.default, null) + ) + ) + ), + react_1.default.createElement( + "div", + { className: "row" }, + react_1.default.createElement( + "div", + { + className: (0, clsx_1.default)( + "col", + api || schema ? "col--7" : "col--12" + ), + }, + react_1.default.createElement(Paginator_1.default, null) + ) + ) + ) + ), + docTOC.desktop && + react_1.default.createElement( + "div", + { className: "col col--3" }, + react_1.default.createElement( + "div", + { className: "toc-column" }, + docTOC.desktop + ) + ) ); } +exports.default = DocItemLayout; diff --git a/src/theme/ApiItem/hooks.js b/src/theme/ApiItem/hooks.js new file mode 100644 index 00000000..0d97c9cc --- /dev/null +++ b/src/theme/ApiItem/hooks.js @@ -0,0 +1,7 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.useTypedSelector = exports.useTypedDispatch = void 0; +const react_redux_1 = require("react-redux"); +const useTypedDispatch = () => (0, react_redux_1.useDispatch)(); +exports.useTypedDispatch = useTypedDispatch; +exports.useTypedSelector = react_redux_1.useSelector; diff --git a/src/theme/ApiItem/index.js b/src/theme/ApiItem/index.js new file mode 100644 index 00000000..12049852 --- /dev/null +++ b/src/theme/ApiItem/index.js @@ -0,0 +1,223 @@ +"use strict"; +var __importDefault = + (this && this.__importDefault) || + function (mod) { + return mod && mod.__esModule ? mod : { default: mod }; + }; +Object.defineProperty(exports, "__esModule", { value: true }); +const zlib_1 = __importDefault(require("zlib")); +const react_1 = __importDefault(require("react")); +const BrowserOnly_1 = __importDefault(require("@docusaurus/BrowserOnly")); +const ExecutionEnvironment_1 = __importDefault( + require("@docusaurus/ExecutionEnvironment") +); +const client_1 = require("@docusaurus/plugin-content-docs/client"); +const theme_common_1 = require("@docusaurus/theme-common"); +const useDocusaurusContext_1 = __importDefault( + require("@docusaurus/useDocusaurusContext") +); +const useIsBrowser_1 = __importDefault(require("@docusaurus/useIsBrowser")); +const slice_1 = require("@theme/ApiExplorer/Authorization/slice"); +const persistanceMiddleware_1 = require("@theme/ApiExplorer/persistanceMiddleware"); +const Layout_1 = __importDefault(require("@theme/ApiItem/Layout")); +const CodeBlock_1 = __importDefault(require("@theme/CodeBlock")); +const Metadata_1 = __importDefault(require("@theme/DocItem/Metadata")); +const SkeletonLoader_1 = __importDefault(require("@theme/SkeletonLoader")); +const clsx_1 = __importDefault(require("clsx")); +const react_redux_1 = require("react-redux"); +const store_1 = require("./store"); +let ApiExplorer = (_) => react_1.default.createElement("div", null); +if (ExecutionEnvironment_1.default.canUseDOM) { + ApiExplorer = require("@theme/ApiExplorer").default; +} +// @ts-ignore +function ApiItem(props) { + const docHtmlClassName = `docs-doc-id-${props.content.metadata.id}`; + const MDXComponent = props.content; + const { frontMatter } = MDXComponent; + const { info_path: infoPath } = frontMatter; + let { api } = frontMatter; + const { schema } = frontMatter; + const { sample } = frontMatter; + // decompress and parse + if (api) { + try { + api = JSON.parse( + zlib_1.default.inflateSync(Buffer.from(api, "base64")).toString() + ); + } catch {} + } + const { siteConfig } = (0, useDocusaurusContext_1.default)(); + const themeConfig = siteConfig.themeConfig; + const options = themeConfig.api; + const isBrowser = (0, useIsBrowser_1.default)(); + // Regex for 2XX status + const statusRegex = new RegExp("(20[0-9]|2[1-9][0-9])"); + // Define store2 + let store2 = {}; + const persistanceMiddleware = (0, + persistanceMiddleware_1.createPersistanceMiddleware)(options); + // Init store for SSR + if (!isBrowser) { + store2 = (0, store_1.createStoreWithoutState)({}, [persistanceMiddleware]); + } + // Init store for CSR to hydrate components + if (isBrowser) { + // Create list of only 2XX response content types to create request samples from + let acceptArray = []; + for (const [code, content] of Object.entries(api?.responses ?? [])) { + if (statusRegex.test(code)) { + acceptArray.push(Object.keys(content.content ?? {})); + } + } + acceptArray = acceptArray.flat(); + const content = api?.requestBody?.content ?? {}; + const contentTypeArray = Object.keys(content); + const servers = api?.servers ?? []; + const params = { + path: [], + query: [], + header: [], + cookie: [], + }; + api?.parameters?.forEach((param) => { + const paramType = param.in; + const paramsArray = params[paramType]; + paramsArray.push(param); + }); + const auth = (0, slice_1.createAuth)({ + security: api?.security, + securitySchemes: api?.securitySchemes, + options, + }); + // TODO: determine way to rehydrate without flashing + // const acceptValue = window?.sessionStorage.getItem("accept"); + // const contentTypeValue = window?.sessionStorage.getItem("contentType"); + const server = window?.sessionStorage.getItem("server"); + const serverObject = JSON.parse(server) ?? {}; + store2 = (0, store_1.createStoreWithState)( + { + accept: { + value: acceptArray[0], + options: acceptArray, + }, + contentType: { + value: contentTypeArray[0], + options: contentTypeArray, + }, + server: { + value: serverObject.url ? serverObject : undefined, + options: servers, + }, + response: { value: undefined }, + body: { type: "empty" }, + params, + auth, + }, + [persistanceMiddleware] + ); + } + if (api) { + return react_1.default.createElement( + client_1.DocProvider, + { content: props.content }, + react_1.default.createElement( + theme_common_1.HtmlClassNameProvider, + { className: docHtmlClassName }, + react_1.default.createElement(Metadata_1.default, null), + react_1.default.createElement( + Layout_1.default, + null, + react_1.default.createElement( + react_redux_1.Provider, + { store: store2 }, + react_1.default.createElement( + "div", + { className: (0, clsx_1.default)("row", "theme-api-markdown") }, + react_1.default.createElement( + "div", + { className: "col col--7 openapi-left-panel__container" }, + react_1.default.createElement(MDXComponent, null) + ), + react_1.default.createElement( + "div", + { className: "col col--5 openapi-right-panel__container" }, + react_1.default.createElement( + BrowserOnly_1.default, + { + fallback: react_1.default.createElement( + SkeletonLoader_1.default, + { size: "lg" } + ), + }, + () => { + return react_1.default.createElement(ApiExplorer, { + item: api, + infoPath: infoPath, + }); + } + ) + ) + ) + ) + ) + ) + ); + } else if (schema) { + return react_1.default.createElement( + client_1.DocProvider, + { content: props.content }, + react_1.default.createElement( + theme_common_1.HtmlClassNameProvider, + { className: docHtmlClassName }, + react_1.default.createElement(Metadata_1.default, null), + react_1.default.createElement( + Layout_1.default, + null, + react_1.default.createElement( + "div", + { className: (0, clsx_1.default)("row", "theme-api-markdown") }, + react_1.default.createElement( + "div", + { className: "col col--7 openapi-left-panel__container schema" }, + react_1.default.createElement(MDXComponent, null) + ), + react_1.default.createElement( + "div", + { className: "col col--5 openapi-right-panel__container" }, + react_1.default.createElement( + CodeBlock_1.default, + { language: "json", title: `${frontMatter.title}` }, + JSON.stringify(sample, null, 2) + ) + ) + ) + ) + ) + ); + } + // Non-API docs + return react_1.default.createElement( + client_1.DocProvider, + { content: props.content }, + react_1.default.createElement( + theme_common_1.HtmlClassNameProvider, + { className: docHtmlClassName }, + react_1.default.createElement(Metadata_1.default, null), + react_1.default.createElement( + Layout_1.default, + null, + react_1.default.createElement( + "div", + { className: "row" }, + react_1.default.createElement( + "div", + { className: "col col--12 markdown" }, + react_1.default.createElement(MDXComponent, null) + ) + ) + ) + ) + ); +} +exports.default = ApiItem; diff --git a/src/theme/ApiItem/store.js b/src/theme/ApiItem/store.js new file mode 100644 index 00000000..747b2fdb --- /dev/null +++ b/src/theme/ApiItem/store.js @@ -0,0 +1,47 @@ +"use strict"; +var __importDefault = + (this && this.__importDefault) || + function (mod) { + return mod && mod.__esModule ? mod : { default: mod }; + }; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createStoreWithoutState = exports.createStoreWithState = void 0; +const toolkit_1 = require("@reduxjs/toolkit"); +const slice_1 = __importDefault(require("@theme/ApiExplorer/Accept/slice")); +const slice_2 = __importDefault( + require("@theme/ApiExplorer/Authorization/slice") +); +const slice_3 = __importDefault(require("@theme/ApiExplorer/Body/slice")); +const slice_4 = __importDefault( + require("@theme/ApiExplorer/ContentType/slice") +); +const slice_5 = __importDefault( + require("@theme/ApiExplorer/ParamOptions/slice") +); +const slice_6 = __importDefault(require("@theme/ApiExplorer/Response/slice")); +const slice_7 = __importDefault(require("@theme/ApiExplorer/Server/slice")); +const rootReducer = (0, toolkit_1.combineReducers)({ + accept: slice_1.default, + contentType: slice_4.default, + response: slice_6.default, + server: slice_7.default, + body: slice_3.default, + params: slice_5.default, + auth: slice_2.default, +}); +const createStoreWithState = (preloadedState, middlewares) => + (0, toolkit_1.configureStore)({ + reducer: rootReducer, + preloadedState, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware().concat(...middlewares), + }); +exports.createStoreWithState = createStoreWithState; +const createStoreWithoutState = (preloadedState, middlewares) => + (0, toolkit_1.configureStore)({ + reducer: rootReducer, + preloadedState, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware().concat(...middlewares), + }); +exports.createStoreWithoutState = createStoreWithoutState; From b4fda6f4e78e7174d37e1e5317b9baabfefef0ce Mon Sep 17 00:00:00 2001 From: Joro Date: Mon, 2 Dec 2024 16:54:58 +0200 Subject: [PATCH 31/99] api tabs (cherry picked from commit b4c39ebe6d61e17b51e77cd146c6dcb07137a713) --- src/theme/ApiTabs/_ApiTabs.scss | 136 +++++++++++++++++ src/theme/ApiTabs/index.js | 250 ++++++++++++++++++++++++++++++++ 2 files changed, 386 insertions(+) create mode 100644 src/theme/ApiTabs/_ApiTabs.scss create mode 100644 src/theme/ApiTabs/index.js diff --git a/src/theme/ApiTabs/_ApiTabs.scss b/src/theme/ApiTabs/_ApiTabs.scss new file mode 100644 index 00000000..801c2fe7 --- /dev/null +++ b/src/theme/ApiTabs/_ApiTabs.scss @@ -0,0 +1,136 @@ +.openapi-tabs__container { + margin-left: -1px; +} + +.openapi-tabs__response-header { + &.openapi-tabs__heading { + margin-bottom: 0; + } +} + +.openapi-tabs__response-code-item { + border: 1px solid transparent; + margin-top: 0 !important; + margin-right: 0.5rem; + padding: 0.35rem 0.85rem; + border-radius: var(--ifm-global-radius); + font-weight: var(--ifm-font-weight-bold); + font-size: 12px; + transition: 300ms; + color: var(--ifm-font-color-secondary); + + &.success.active { + background-color: var(--ifm-color-success); + color: var(--ifm-color-white); + } + + &.danger.active { + background-color: var(--ifm-color-danger); + color: var(--ifm-color-white); + } + + &.info.active { + background-color: var(--ifm-color-info); + color: var(--ifm-color-white); + } + + &.active, + &:hover { + opacity: 1; + } + + &:hover:not(.active) { + background-color: transparent; + border: 1px solid var(--ifm-toc-border-color); + } +} + +.openapi-tabs__response-code-item:not(.active) { + opacity: 0.65; +} + +.openapi-tabs__response-code-item:hover { + opacity: 1; +} + +.openapi-tabs__response-code-item:last-child { + margin-right: 0 !important; +} + +/* Open API Response Code Tabs */ +.openapi-tabs__response-header-section { + border-top: 1px solid var(--ifm-toc-border-color); + margin-top: 2rem; + padding-top: 2rem; + display: flex; + justify-content: space-between; + align-items: center; +} + +.openapi-tabs__response-container { + display: flex; + align-items: center; + max-width: 390px; + padding-left: 1rem; + overflow: hidden; +} + +.openapi-tabs__response-list-container { + padding: 0 0.25rem; + overflow-y: hidden; + overflow-x: scroll; + scroll-behavior: smooth; + scrollbar-width: none; +} + +.openapi-tabs__response-list-container::-webkit-scrollbar { + display: none; +} + +/* Response Code Tabs - Colored Dots */ +.openapi-tabs__response-dot { + width: 12.5px; + height: 12.5px; + margin-right: 5px; + border-radius: 50%; +} + +.openapi-tabs__response-schema-container { + max-width: 600px; +} + +/* Tab Arrows */ +.openapi-tabs__arrow { + content: ""; + height: 1.25rem; + width: 1.25rem; + border: none; + min-width: 1.25rem; + background: var(--ifm-menu-link-sublist-icon) 50% / 2rem 2rem; + filter: var(--ifm-menu-link-sublist-icon-filter); + + &:hover { + cursor: pointer; + } + + &.left { + transform: rotate(270deg); + } + + &.right { + transform: rotate(90deg); + } +} + +@media screen and (max-width: 500px) { + .openapi-tabs__response-header-section { + flex-direction: column; + align-items: flex-start; + } + + .openapi-tabs__response-container { + width: 100%; + margin-top: var(--ifm-spacing-vertical); + padding: 0; + } +} diff --git a/src/theme/ApiTabs/index.js b/src/theme/ApiTabs/index.js new file mode 100644 index 00000000..71297c9d --- /dev/null +++ b/src/theme/ApiTabs/index.js @@ -0,0 +1,250 @@ +"use strict"; +var __createBinding = + (this && this.__createBinding) || + (Object.create + ? function (o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if ( + !desc || + ("get" in desc ? !m.__esModule : desc.writable || desc.configurable) + ) { + desc = { + enumerable: true, + get: function () { + return m[k]; + }, + }; + } + Object.defineProperty(o, k2, desc); + } + : function (o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; + }); +var __setModuleDefault = + (this && this.__setModuleDefault) || + (Object.create + ? function (o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); + } + : function (o, v) { + o["default"] = v; + }); +var __importStar = + (this && this.__importStar) || + function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) + for (var k in mod) + if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) + __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; + }; +var __importDefault = + (this && this.__importDefault) || + function (mod) { + return mod && mod.__esModule ? mod : { default: mod }; + }; +Object.defineProperty(exports, "__esModule", { value: true }); +const react_1 = __importStar(require("react")); +const internal_1 = require("@docusaurus/theme-common/internal"); +const useIsBrowser_1 = __importDefault(require("@docusaurus/useIsBrowser")); +const Heading_1 = __importDefault(require("@theme/Heading")); +const clsx_1 = __importDefault(require("clsx")); +function TabList({ + className, + block, + selectedValue, + selectValue, + tabValues, + label = "Responses", + id = "responses", +}) { + const tabRefs = []; + const { blockElementScrollPositionUntilNextRender } = (0, + internal_1.useScrollPositionBlocker)(); + const handleTabChange = (event) => { + const newTab = event.currentTarget; + const newTabIndex = tabRefs.indexOf(newTab); + const newTabValue = tabValues[newTabIndex].value; + if (newTabValue !== selectedValue) { + blockElementScrollPositionUntilNextRender(newTab); + selectValue(newTabValue); + } + }; + const handleKeydown = (event) => { + let focusElement = null; + switch (event.key) { + case "Enter": { + handleTabChange(event); + break; + } + case "ArrowRight": { + const nextTab = tabRefs.indexOf(event.currentTarget) + 1; + focusElement = tabRefs[nextTab] ?? tabRefs[0]; + break; + } + case "ArrowLeft": { + const prevTab = tabRefs.indexOf(event.currentTarget) - 1; + focusElement = tabRefs[prevTab] ?? tabRefs[tabRefs.length - 1]; + break; + } + default: + break; + } + focusElement?.focus(); + }; + const tabItemListContainerRef = (0, react_1.useRef)(null); + const [showTabArrows, setShowTabArrows] = (0, react_1.useState)(false); + (0, react_1.useEffect)(() => { + const resizeObserver = new ResizeObserver((entries) => { + for (let entry of entries) { + requestAnimationFrame(() => { + if (entry.target.clientWidth < entry.target.scrollWidth) { + setShowTabArrows(true); + } else { + setShowTabArrows(false); + } + }); + } + }); + resizeObserver.observe(tabItemListContainerRef.current); + return () => { + resizeObserver.disconnect(); + }; + }, []); + const handleRightClick = () => { + tabItemListContainerRef.current.scrollLeft += 90; + }; + const handleLeftClick = () => { + tabItemListContainerRef.current.scrollLeft -= 90; + }; + return react_1.default.createElement( + "div", + { className: "openapi-tabs__response-header-section" }, + react_1.default.createElement( + Heading_1.default, + { + as: "h2", + id: id, + className: "openapi-tabs__heading openapi-tabs__response-header", + }, + label + ), + react_1.default.createElement( + "div", + { className: "openapi-tabs__response-container" }, + showTabArrows && + react_1.default.createElement("button", { + className: "openapi-tabs__arrow left", + onClick: handleLeftClick, + }), + react_1.default.createElement( + "ul", + { + ref: tabItemListContainerRef, + role: "tablist", + "aria-orientation": "horizontal", + className: (0, clsx_1.default)( + "openapi-tabs__response-list-container", + "tabs", + { + "tabs--block": block, + }, + className + ), + }, + tabValues.map(({ value, label, attributes }) => + react_1.default.createElement( + "li", + { + // TODO extract TabListItem + role: "tab", + tabIndex: selectedValue === value ? 0 : -1, + "aria-selected": selectedValue === value, + key: value, + ref: (tabControl) => tabRefs.push(tabControl), + onKeyDown: handleKeydown, + onClick: handleTabChange, + ...attributes, + className: (0, clsx_1.default)( + "tabs__item", + "openapi-tabs__response-code-item", + attributes?.className, + parseInt(value) >= 400 + ? "danger" + : parseInt(value) >= 200 && parseInt(value) < 300 + ? "success" + : "info", + { + active: selectedValue === value, + } + ), + }, + label ?? value + ) + ) + ), + showTabArrows && + react_1.default.createElement("button", { + className: "openapi-tabs__arrow right", + onClick: handleRightClick, + }) + ) + ); +} +function TabContent({ lazy, children, selectedValue }) { + const childTabs = (Array.isArray(children) ? children : [children]).filter( + Boolean + ); + if (lazy) { + const selectedTabItem = childTabs.find( + (tabItem) => tabItem.props.value === selectedValue + ); + if (!selectedTabItem) { + // fail-safe or fail-fast? not sure what's best here + return null; + } + return (0, react_1.cloneElement)(selectedTabItem, { + className: "margin-top--md", + }); + } + return react_1.default.createElement( + "div", + { className: "margin-top--md" }, + childTabs.map((tabItem, i) => + (0, react_1.cloneElement)(tabItem, { + key: i, + hidden: tabItem.props.value !== selectedValue, + }) + ) + ); +} +function TabsComponent(props) { + const tabs = (0, internal_1.useTabs)(props); + return react_1.default.createElement( + "div", + { className: "openapi-tabs__container" }, + react_1.default.createElement(TabList, { ...props, ...tabs }), + react_1.default.createElement(TabContent, { ...props, ...tabs }) + ); +} +function ApiTabs(props) { + const isBrowser = (0, useIsBrowser_1.default)(); + return react_1.default.createElement( + TabsComponent, + // Remount tabs after hydration + // Temporary fix for https://github.com/facebook/docusaurus/issues/5653 + { + // Remount tabs after hydration + // Temporary fix for https://github.com/facebook/docusaurus/issues/5653 + key: String(isBrowser), + ...props, + }, + (0, internal_1.sanitizeTabsChildren)(props.children) + ); +} +exports.default = ApiTabs; From 2c35b086d9b81e9522670c29198e35825f56f3a9 Mon Sep 17 00:00:00 2001 From: Joro Date: Mon, 2 Dec 2024 16:56:17 +0200 Subject: [PATCH 32/99] code block (cherry picked from commit 9e9e9fcb5f6d79d9c545469cd66557429b6e0656) --- src/theme/CodeBlock/Content/String.js | 24 +++++++++++-------- src/theme/CodeBlock/Content/styles.module.css | 2 +- src/theme/CodeBlock/CopyButton/index.js | 1 - src/theme/CodeBlock/Line/index.js | 2 +- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/theme/CodeBlock/Content/String.js b/src/theme/CodeBlock/Content/String.js index b54aa192..57b1c510 100644 --- a/src/theme/CodeBlock/Content/String.js +++ b/src/theme/CodeBlock/Content/String.js @@ -8,12 +8,18 @@ import { containsLineNumbers, useCodeWordWrap, } from '@docusaurus/theme-common/internal'; -import Highlight, {defaultProps} from 'prism-react-renderer'; +import {Highlight} from 'prism-react-renderer'; import Line from '@theme/CodeBlock/Line'; import CopyButton from '@theme/CodeBlock/CopyButton'; import WordWrapButton from '@theme/CodeBlock/WordWrapButton'; import Container from '@theme/CodeBlock/Container'; import styles from './styles.module.css'; +// Prism languages are always lowercase +// We want to fail-safe and allow both "php" and "PHP" +// See https://github.com/facebook/docusaurus/issues/9012 +function normalizeLanguage(language) { + return language?.toLowerCase(); +} export default function CodeBlockString({ children, className: blockClassName = '', @@ -25,8 +31,9 @@ export default function CodeBlockString({ const { prism: {defaultLanguage, magicComments}, } = useThemeConfig(); - const language = - languageProp ?? parseLanguage(blockClassName) ?? defaultLanguage; + const language = normalizeLanguage( + languageProp ?? parseLanguage(blockClassName) ?? defaultLanguage, + ); const prismTheme = usePrismTheme(); const wordWrap = useCodeWordWrap(); // We still parse the metastring in case we want to support more syntax in the @@ -51,17 +58,14 @@ export default function CodeBlockString({ )}> {title &&
{title}
}
- - {({className, tokens, getLineProps, getTokenProps}) => ( + + {({className, style, tokens, getLineProps, getTokenProps}) => (
+              className={clsx(className, styles.codeBlock, 'thin-scrollbar')}
+              style={style}>
                (
-    
+    
   ));
   return (
     

From 7888b1c17ad3d24d2c6b2744ecf86a91637fc7a7 Mon Sep 17 00:00:00 2001
From: Joro 
Date: Mon, 2 Dec 2024 17:03:48 +0200
Subject: [PATCH 33/99] doc breadcrumbs

(cherry picked from commit 9b68e59f251e5290a58e6c6373f6b2669e6a0a75)
---
 src/theme/DocBreadcrumbs/index.js | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/src/theme/DocBreadcrumbs/index.js b/src/theme/DocBreadcrumbs/index.js
index 0f5cfd6c..10d5c114 100644
--- a/src/theme/DocBreadcrumbs/index.js
+++ b/src/theme/DocBreadcrumbs/index.js
@@ -1,10 +1,8 @@
 import React from "react";
 import clsx from "clsx";
 import { ThemeClassNames } from "@docusaurus/theme-common";
-import {
-  useSidebarBreadcrumbs,
-  useHomePageRoute,
-} from "@docusaurus/theme-common/internal";
+import { useSidebarBreadcrumbs } from "@docusaurus/plugin-content-docs/client";
+import { useHomePageRoute } from "@docusaurus/theme-common/internal";
 import Link from "@docusaurus/Link";
 import { translate } from "@docusaurus/Translate";
 import HomeBreadcrumbItem from "@theme/DocBreadcrumbs/Items/Home";
@@ -52,8 +50,8 @@ function BreadcrumbsItem({ children, active, index, addMicrodata }) {
 }
 export default function DocBreadcrumbs() {
   const breadcrumbs = useSidebarBreadcrumbs();
-  // const homePageRoute = useHomePageRoute();
-  if (!breadcrumbs || breadcrumbs.length === 1) {
+  const homePageRoute = useHomePageRoute();
+  if (!breadcrumbs) {
     return null;
   }
   return (
@@ -76,14 +74,18 @@ export default function DocBreadcrumbs() {
         {/* {homePageRoute && } */}
         {breadcrumbs.map((item, idx) => {
           const isLast = idx === breadcrumbs.length - 1;
+          const href =
+            item.type === "category" && item.linkUnlisted
+              ? undefined
+              : item.href;
           return (
             
-              
+              
                 {item.label}
               
             

From b9caf9856e086e8587954a69fddf8e3474fa1ddd Mon Sep 17 00:00:00 2001
From: Joro 
Date: Mon, 2 Dec 2024 17:08:55 +0200
Subject: [PATCH 34/99] doc card

(cherry picked from commit f4ca1d28a302628c60c7feb432b9d2918f36e6d6)
---
 src/theme/DocCard/index.js          | 51 ++++++++++++++++++++---------
 src/theme/DocCard/styles.module.css |  1 -
 2 files changed, 35 insertions(+), 17 deletions(-)

diff --git a/src/theme/DocCard/index.js b/src/theme/DocCard/index.js
index 7a67e6f2..86d8b5b2 100644
--- a/src/theme/DocCard/index.js
+++ b/src/theme/DocCard/index.js
@@ -2,12 +2,30 @@ import React from "react";
 import clsx from "clsx";
 import Link from "@docusaurus/Link";
 import {
-  findFirstCategoryLink,
   useDocById,
-} from "@docusaurus/theme-common/internal";
+  findFirstSidebarItemLink,
+} from "@docusaurus/plugin-content-docs/client";
+import { usePluralForm } from "@docusaurus/theme-common";
 import isInternalUrl from "@docusaurus/isInternalUrl";
 import { translate } from "@docusaurus/Translate";
+import Heading from "@theme/Heading";
 import styles from "./styles.module.css";
+function useCategoryItemsPlural() {
+  const { selectMessage } = usePluralForm();
+  return (count) =>
+    selectMessage(
+      count,
+      translate(
+        {
+          message: "1 item|{count} items",
+          id: "theme.docs.DocCard.categoryDescription.plurals",
+          description:
+            "The default description for a category card in the generated index about how many items this category includes",
+        },
+        { count }
+      )
+    );
+}
 function CardContainer({ href, children }) {
   return (
     
   );
 }
-function CardLayout({ href, title, description }) {
+function CardLayout({ href, icon, title, description }) {
   return (
     
-      

+ + {/* {icon} */} {title} -

+ {description && (

); } function CardLink({ item }) { + const icon = isInternalUrl(item.href) ? "📄️" : "🔗"; const doc = useDocById(item.docId ?? undefined); return ( ); } diff --git a/src/theme/DocCard/styles.module.css b/src/theme/DocCard/styles.module.css index 94e8f513..4f7ad27f 100644 --- a/src/theme/DocCard/styles.module.css +++ b/src/theme/DocCard/styles.module.css @@ -20,7 +20,6 @@ .cardTitle { font-size: 1.2rem; - color: var(--ifm-color-emphasis-900); } .cardDescription { From a58c4208cc5874d8b622f9be90f65baf6230f435 Mon Sep 17 00:00:00 2001 From: Joro Date: Mon, 2 Dec 2024 17:12:15 +0200 Subject: [PATCH 35/99] doc generated index (cherry picked from commit 5096f1e1b2186698a12c67ee67c7cfdc13dd0e29) --- src/theme/DocCategoryGeneratedIndexPage/index.js | 11 +++++------ .../DocCategoryGeneratedIndexPage/styles.module.css | 1 + 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/theme/DocCategoryGeneratedIndexPage/index.js b/src/theme/DocCategoryGeneratedIndexPage/index.js index 442fa541..47a64a35 100644 --- a/src/theme/DocCategoryGeneratedIndexPage/index.js +++ b/src/theme/DocCategoryGeneratedIndexPage/index.js @@ -1,13 +1,11 @@ import React from "react"; -import clsx from "clsx"; -import { - PageMetadata, - useCurrentSidebarCategory, -} from "@docusaurus/theme-common"; +import { PageMetadata } from "@docusaurus/theme-common"; +import { useCurrentSidebarCategory } from "@docusaurus/plugin-content-docs/client"; import useBaseUrl from "@docusaurus/useBaseUrl"; import DocCardList from "@theme/DocCardList"; import DocPaginator from "@theme/DocPaginator"; import DocVersionBanner from "@theme/DocVersionBanner"; +import DocVersionBadge from "@theme/DocVersionBadge"; import DocBreadcrumbs from "@theme/DocBreadcrumbs"; import Heading from "@theme/Heading"; import styles from "./styles.module.css"; @@ -28,6 +26,7 @@ function DocCategoryGeneratedIndexPageContent({ categoryGeneratedIndex }) {

+ {/* */}
{categoryGeneratedIndex.title} @@ -36,7 +35,7 @@ function DocCategoryGeneratedIndexPageContent({ categoryGeneratedIndex }) {

{categoryGeneratedIndex.description}

)}
-
+