From 8338e36104638220a0edd0143dbba504d2cf09bd Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Thu, 18 Dec 2025 17:09:48 +0100 Subject: [PATCH 1/2] feat(CMA): Updated development section to include CMA guide --- .dockerignore | 1 + .gitignore | 1 + .vscode/ltex.hiddenFalsePositives.en-US.txt | 2 + .../version-1.5/development/asset-handling.md | 15 + .../ledger-submoule.md | 204 ++++++ .../parser-submodule.md | 585 ++++++++++++++++++ .../snippets/cpp-parser-result-types.md | 133 ++++ .../snippets/rust-parser-result-types.md | 181 ++++++ .../version-1.5-sidebars.json | 11 +- src/css/custom.css | 12 +- 10 files changed, 1142 insertions(+), 3 deletions(-) create mode 100644 cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/ledger-submoule.md create mode 100644 cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/parser-submodule.md create mode 100644 cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/snippets/cpp-parser-result-types.md create mode 100644 cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/snippets/rust-parser-result-types.md diff --git a/.dockerignore b/.dockerignore index 27d2dae2b..844ca197f 100755 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,3 @@ */node_modules +/TEMP *.log diff --git a/.gitignore b/.gitignore index a13d11fbf..ff43b52a8 100755 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ # production /build +/TEMP # generated files .docusaurus diff --git a/.vscode/ltex.hiddenFalsePositives.en-US.txt b/.vscode/ltex.hiddenFalsePositives.en-US.txt index 5c4c1e912..b14449ef5 100644 --- a/.vscode/ltex.hiddenFalsePositives.en-US.txt +++ b/.vscode/ltex.hiddenFalsePositives.en-US.txt @@ -2,3 +2,5 @@ {"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$"} {"rule":"MORFOLOGIK_RULE_EN_US","sentence":"^\\QThe Devnet environment functions similarly to a mainnet.\\E$"} +{"rule":"MORFOLOGIK_RULE_EN_US","sentence":"^\\QDeposit input payloads arrives applications as an abi-encoded hex, in-order to obtain the deposit parameters this hex payload is decoded\\E$"} +{"rule":"PLURAL_VERB_AFTER_THIS","sentence":"^\\QThis decode process could either be handled manually by the developers or could be automated using the assets manager library\\E$"} diff --git a/cartesi-rollups_versioned_docs/version-1.5/development/asset-handling.md b/cartesi-rollups_versioned_docs/version-1.5/development/asset-handling.md index cf54a313d..2eb606a4f 100644 --- a/cartesi-rollups_versioned_docs/version-1.5/development/asset-handling.md +++ b/cartesi-rollups_versioned_docs/version-1.5/development/asset-handling.md @@ -41,6 +41,19 @@ Deposit input payloads are always specified as packed ABI-encoded parameters, as | ERC-1155 (single) | | | | ERC-1155 (batch) | | | +## Decoding Deposits + +Deposit input payloads arrives applications as an abi-encoded hex, this hex input would need to be decoded to obtain the contained deposit parameters. This decode process could either be handled manually by the developers or could be automated using the assets' manager library. + +The **parser submodule** of the asset manager library simplifies the decode process for deposit payloads by abstracting an already implemented logic to decode the deposit payload for all major assets (ERC721, ERC20, ERC721), it exposes a method called decode input, which the application logic transfers the deposit payload to then receives a struct containing all the parameters contained in the deposit payload. A more detailed guide on the parser library can be found in this [Asset management library section.](../rollups-apis/asset-management-library/parser-submodule.md) + +## Managing and Recording Deposits + +Once a deposit occurs on the base layer, the application receives all necessary details pertaining to the deposit. It's important that the application maintain a record of an address to a token and finally the balance of the address for that token, without a proper record, the application is unable to tell the exact token and also the amount of token that each address owns on the dApp. + +This record can either be implemented manually by the developer or the application could simply import the Asset manager library then leverage the **ledger submodule** to manage and keep this record. + +The ledger submodule can be likened to a plug in wallet for applications, it contains a storage struct which tracks the assets balance for each address as well as the total supply for each token deposited to the wallet, finally it exposes methods to handle deposit, transfer and withdrawal of these assets in the record. For a detailed explanation of the ledger library, check this [Asset management library section.](../rollups-apis/asset-management-library/ledger-submoule.md) ## Withdrawing assets @@ -67,3 +80,5 @@ Here are the function signatures used by vouchers to withdraw the different type | ERC-721 | Token contract | `safeTransferFrom(address,address,uint256,bytes)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-721#specification) | | ERC-1155 | Token contract | `safeTransferFrom(address,address,uint256,uint256,data)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-1155#specification) | | ERC-1155 | Token contract | `safeBatchTransferFrom(address,address,uint256[],uint256[],data)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-1155#specification) | + +Structuring a voucher to these signatures can either be done manually or by using the **Parser submodule** of the asset management library. \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/ledger-submoule.md b/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/ledger-submoule.md new file mode 100644 index 000000000..911cdb6c6 --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/ledger-submoule.md @@ -0,0 +1,204 @@ +--- +id: ledger-submodule +title: Ledger Submodule +--- + +# Integrating the Ledger Library + +The **Asset Manager Library Ledger submodule** is one of the two submodules of the Asset management library, it's designed for managing assets, accounts, balances, and transfers inside Cartesi applications. + +You can think of the Ledger as a **plug-and-play wallet engine**. Once integrated, it allows your application to safely track and modify asset state without re-implementing common accounting logic such as deposits, withdrawals, and transfers. +The CMA tools are split into **two complementary libraries**: + +- **Ledger**: Manages assets, accounts, balances, and state changes +- **Parser**: Parses user input and structures data for the ledger to consume + +Together, they provide a smooth development experience: + +1. The parser receives raw user input and converts it into structured data. + +2. The ledger consumes this structured input and applies the corresponding asset and account updates. + +If you are not yet familiar with the parser, see [Integrating the CMA Parser Library](./parser-submodule.md). + +By integrating the ledger, your application gains a secure, deterministic, and type-safe way to manage digital assets such as Ether, ERC20, ERC721, and ERC1155, while remaining fully compatible with Cartesi rollup workflows. + +## Why Use the Ledger? + +The CMA Ledger solves common asset-management problems in Cartesi applications: + +- **Unified Asset Management**: Manage multiple asset standards and account models from a single interface. + +- **Deterministic & Auditable**: Ledger behavior is identical across environments (tests, simulations, and production), making state changes predictable and auditable. + +- **Strong Safety Guarantees**: All operations return typed errors, eliminating silent failures and ambiguous states. + +- **Cartesi-Native Design**: Built specifically for Cartesi rollups, while remaining flexible and non-intrusive to application logic. + +## Key Capabilities + +- **Asset Models**: Support for Ether, ERC20, ERC721, and ERC1155-style assets. + +- **Account Models**: Support for Ethereum wallet addresses mapped to generic identifiers' ID. + +- **Asset Lifecycle Operations**: Create, retrieve, deposit, withdraw, and transfer assets. + +- **Balance & Supply Queries**: Read per-account balances and total asset supply at any time. + +- **Strong Typing**: Prevents asset/account mismatches at compile time. + +## Defined Data Types + +| Type | Description | +|-----|------------| +| `LedgerAssetId` | Internal 64-bit asset identifier | +| `LedgerAccountId` | Internal 64-bit account identifier | +| `AssetType` | ID-based, address-based, or address + ID | +| `AccountType` | Wallet address or generic account ID | +| `RetrieveOperation` | `Find`, `Create`, or `FindOrCreate` | + +## Commonly Used Methods + +### Asset and Account Retrieval + +The below table details all available methods for handling assets and account retrievals. + +| Method | Description | Asset/Account type | Args | Returns | +|---|---|---|---|---| +| `retrieve_asset()` | Low-level method for retrieving or creating any asset | Generic | `asset_id`, `token_address`, `token_id`, `asset_type`, `operation` | `Result` | +| `retrieve_erc20_asset_via_address()` | Retrieves or creates an ERC20 asset using its contract address | Erc20 | `token_address` | `Result` | +| `retrieve_erc721_assets_via_address()` | Retrieves or creates an ERC721 asset using contract address and token ID | Erc721 | `token_address`, `token_id` | `Result` | +| `retrieve_ether_assets()` | Retrieves or creates the Ether asset | Ether | none | `Result` | +| `retrieve_account()` | Low level method for retrieving or creating an account | Generic | `account_id`, `account_type`, `addr_or_id`, `operation` | `Result` | +| `retrieve_account_via_address()` | Retrieves or creates an account mapped to an Ethereum address | Wallet | wallet_address | `Result` | + +### State Mutations + +State mutations cover methods that alter the assets record of the ledger library, this could be either to deposit, transfer or withdraw assets, the below table offers better description for each of this methods. + +| Method | Description | Args | +|---|---|---| +| `deposit()` | Adds assets to an account's balance | `asset_id`, `to_account_id`, `amount` | +| `withdraw()` | Removes assets from an account's balance | `asset_id`, `from_account_id`, `amount` | +| `transfer()` | Moves assets between two accounts | `asset_id`, `from_account_id`, `to_account_id`, `amount` | + +### Queries + +The Ledger also exposes read only methods for inspecting its internal state. These methods do not mutate state and can be safely called at any time. + +| Method | Description | Args | Returns | +|---|---|---|---| +| `get_balance()` | Returns the balance of an asset for an account | `asset_id`, `account_id` | `Result` | +| `get_total_supply()` | Returns the total supply of an asset | `asset_id` | `Result` | + +### Lifecycle + +Before using the ledger lib, it's necessary to initialize a new instance via the `new()` method. This returns a ledger struct that serves as the single source of truth for all asset and account records in your application. The below table provides more details about functions that alter the lifecycle of the ledger. + +| Method | Description | Returns | +|---|---|---| +| `Ledger::new()` | Creates a new ledger instance | `Result` | +| `ledger.reset()` | Resets all records in the ledger | `Result<(), LedgerError>` | + +## Error Handling + +All ledger operations return a `LedgerError`, including: + +- Asset or account not found +- Insufficient balance +- Type mismatches +- Internal ledger failures + +This makes error handling explicit and predictable. + +## Installation + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + +

+
+```bash
+cargo add cma-rust-parser
+```
+
+
+
+ + +## Usage Example + + + + +

+
+```rust
+use cma_rust_parser::{
+    Ledger, AssetType, RetrieveOperation, AccountType, U256, Address
+};
+
+let address_one = "0x0000000000000000000000000000000000000001";
+let address_two = "0x0000000000000000000000000000000000000002";
+let erc_20_token = "0x92C6bcA388E99d6B304f1Af3c3Cd749Ff0b591e2";
+let erc_721_token = "0xc6582A9b48F211Fa8c2B5b16CB615eC39bcA653B";
+
+// Initialize the ledger
+let mut ledger = Ledger::new()?;
+
+// Retrieve or create an asset
+let token_address = Address::from_str_hex(erc_721_token).unwrap();
+let token_id = U256::from_u64(1); // Used for NFTs
+
+let asset_id = ledger.retrieve_asset(
+    None,
+    Some(token_address),
+    Some(token_id),
+    AssetType::TokenAddressId,
+    RetrieveOperation::FindOrCreate,
+)?;
+
+// Retrieve or create an account
+let wallet_address = Address::from_str_hex(address_one).unwrap();
+
+let account_id = ledger.retrieve_account(
+    None,
+    AccountType::WalletAddress,
+    RetrieveOperation::FindOrCreate,
+    Some(wallet_address.as_bytes())
+)?;
+
+// Deposit tokens
+ledger.deposit(asset_id, account_id, U256::from_u64(1000)).unwrap();
+
+// Transfer tokens
+let recipient_address = Address::from_str_hex(address_two).unwrap();
+let receipient_id = ledger.retrieve_account_via_address(recipient_address).unwrap();
+
+let transfer_result = ledger.transfer(asset_id, account_id, recipient_id, U256::from_u64(1)).unwrap();
+
+// Query balances and supply
+let balance = ledger.get_balance(asset_id, account_id).unwrap();
+let supply = ledger.get_total_supply(asset_id).unwrap();
+
+// Retrieve ERC20 asset ID
+let erc20_address = Address::from_str_hex(erc_20_token).unwrap();
+let erc20_token_id = ledger.retrieve_erc20_asset_via_address(erc20_address).unwrap();
+
+// Withdraw Token
+let withdraw_result = ledger.withdraw(erc20_token_id, account_id, U256::from_u64(100000000000000000000))
+
+// Retrieve Ether ID
+let ether_id = ledger.retrieve_ether_assets(); // Ether has no contract address hence there's no need to pass an address
+```
+
+
+
+ +## Further Reading + +- [C++ API & language bindings](https://github.com/Mugen-Builders/machine-asset-tools) +- [Integrating the CMA Library – Parser Functions](./parser-submodule.md) diff --git a/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/parser-submodule.md b/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/parser-submodule.md new file mode 100644 index 000000000..5241fc794 --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/parser-submodule.md @@ -0,0 +1,585 @@ +--- +id: parser-submodule +title: Parser Submodule +--- + +# Integrating the Parser Library + +The **Asset Manager Library Parser submodule** is a submodule of the Asset management library designed to handle the encoding and decoding of Cartesi application inputs and outputs in a type-safe, user-friendly, and extensible way. The parser is implemented natively in each language (Rust, Python, C++, etc.) to maximize flexibility and allow seamless integration into various application development environments. + +## Functions of the Parser Lib + +- **Parsing Rollup Inputs and Inspects**: Decode raw payloads from Cartesi rollup advance/inspect requests into rich, typed objects for validation/business logic. + +- **Voucher Encoding**: Construct valid voucher payloads targeting Ether, ERC20, ERC721, and ERC1155 standards to move these assets back to L1. + +## Benefits of the Parser Lib + +- **Full Coverage of Token Standards**: Supports parsing of inspects requests, token deposit and voucher generation for major assets (Ether, ERC20, ERC721, ERC1155). + +- **Cartesi-Native Design**: Built specifically for Cartesi rollups, while remaining flexible and non-intrusive to application logic. + +- **Deterministic & Auditable**: Ledger behavior is identical across languages and environments (tests, simulations, and production), making state changes predictable and auditable. + +- **Data Integrity & Safety**: Strictly typed output models and errors boost security while eliminating common integration bugs. + +## Defined Data Types + +| Type | Description | +|--------------------|--------------------------------------------| +| `CmaParserInputType` | An enum of all supported operation types (requests) that can be decoded (deposit, withdrawal, transfer, inspect, etc.)| +| `CmaParserInputData` | An enum containing a definition of all possible decoded input return object | +| `CmaParserInput` | The fully parsed request object containing the request type and the decoded input object (CmaParserInputData) | +| `CmaVoucherFieldType`| Enum, fields for all voucher types (Ether, ERC20, etc.)| +| `CmaVoucher` | Output-ready voucher ABI fields | +| `TxHexCodes` | An enum containing function selector definitions for all decodable methods | + +Result objects and error types are strictly enforced, ensuring safe contract logic in every supported language. + +## Commonly Used Methods + +### **cma_decode_advance():** +This function receives a request type identifier as well as the application advance input, then decodes the input and returns a well structured output object based on the specified request type. + +#### **Function declaration:** +Below is a function declaration of the cma_decode_advance() function across multiple languages, it contains the function arguments and return types. + + + +

+
+```rust
+fn cma_decode_advance(req_type:  CmaParserInputType, input: JsonValue) -> Result;
+```
+
+
+ + +

+
+```cpp
+cma_parser_error_t cma_decode_advance(cma_parser_input_type_t type, const cmt_rollup_advance_t *input, cma_parser_input_t *parser_input);
+```
+
+
+
+ +#### **Arguments:** +The cma_decode_advance function takes two arguments `req_type` and `input` : + +- **req_type**: This an enum, it represents the type of request being passed to the parser and helps the parser decide how to decode the `input`. Below is a sample definition of the req_type in different languages. + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + +```bash +Click on Rust or Cpp tab to expand +``` + + + + +

+
+```rust
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum CmaParserInputType {
+    CmaParserInputTypeNone,
+    CmaParserInputTypeAuto,
+    CmaParserInputTypeUnidentified,
+    CmaParserInputTypeEtherDeposit,
+    CmaParserInputTypeErc20Deposit,
+    CmaParserInputTypeErc721Deposit,
+    CmaParserInputTypeErc1155SingleDeposit,
+    CmaParserInputTypeErc1155BatchDeposit,
+    CmaParserInputTypeEtherWithdrawal,
+    CmaParserInputTypeErc20Withdrawal,
+    CmaParserInputTypeErc721Withdrawal,
+    CmaParserInputTypeErc1155SingleWithdrawal,
+    CmaParserInputTypeErc1155BatchWithdrawal,
+    CmaParserInputTypeEtherTransfer,
+    CmaParserInputTypeErc20Transfer,
+    CmaParserInputTypeErc721Transfer,
+    CmaParserInputTypeErc1155SingleTransfer,
+    CmaParserInputTypeErc1155BatchTransfer,
+    CmaParserInputTypeBalance,
+    CmaParserInputTypeSupply,
+}
+```
+
+
+ + +

+
+```cpp
+enum cma_parser_input_type_t {
+    CMA_PARSER_INPUT_TYPE_NONE,
+    CMA_PARSER_INPUT_TYPE_AUTO,
+    CMA_PARSER_INPUT_TYPE_ETHER_DEPOSIT,
+    CMA_PARSER_INPUT_TYPE_ERC20_DEPOSIT,
+    CMA_PARSER_INPUT_TYPE_ERC721_DEPOSIT,
+    CMA_PARSER_INPUT_TYPE_ERC1155_SINGLE_DEPOSIT,
+    CMA_PARSER_INPUT_TYPE_ERC1155_BATCH_DEPOSIT,
+    CMA_PARSER_INPUT_TYPE_ETHER_WITHDRAWAL,
+    CMA_PARSER_INPUT_TYPE_ERC20_WITHDRAWAL,
+    CMA_PARSER_INPUT_TYPE_ERC721_WITHDRAWAL,
+    CMA_PARSER_INPUT_TYPE_ERC1155_SINGLE_WITHDRAWAL,
+    CMA_PARSER_INPUT_TYPE_ERC1155_BATCH_WITHDRAWAL,
+    CMA_PARSER_INPUT_TYPE_ETHER_TRANSFER,
+    CMA_PARSER_INPUT_TYPE_ERC20_TRANSFER,
+    CMA_PARSER_INPUT_TYPE_ERC721_TRANSFER,
+    CMA_PARSER_INPUT_TYPE_ERC1155_SINGLE_TRANSFER,
+    CMA_PARSER_INPUT_TYPE_ERC1155_BATCH_TRANSFER,
+    CMA_PARSER_INPUT_TYPE_BALANCE,
+    CMA_PARSER_INPUT_TYPE_SUPPLY,
+};
+```
+
+
+
+ +- **input**: This is a Json object type, and is expected to be the exact JSON object that was passed to the application from the rollup server. + + +#### **Return / Output:** +The `cma_decode_advance()` function returns either an error or a struct, in certain languages, it returns both. The struct returned contains the `req_type` and another struct called `input` which contains well labelled arguments extracted from the JSON input received from the rollups server. Below is a sample declaration of the return values of the cma_decode_advance function. + +import RustOutput from './snippets/rust-parser-result-types.md'; +import CppOutput from './snippets/cpp-parser-result-types.md'; + + + + +```bash +Click on Rust or Cpp tab to expand +``` + + + + +

+    
+
+
+ + +

+    
+
+
+
+ +### **cma_decode_inspect():** +This receives the application inspect value, then decodes and returns a structured object based on the inspect request type. + +#### **Function declaration:** +Below is a function declaration of the cma_decode_inspect() function across multiple languages, it also contains the function arguments and return types. + + + +

+
+```rust
+fn cma_decode_inspect(input: JsonValue) -> Result;
+```
+
+
+ + +

+
+```cpp
+int cma_parser_decode_inspect(cma_parser_input_type_t type, const cmt_rollup_inspect_t *input, cma_parser_input_t *parser_input);
+```
+
+
+
+ +#### **Arguments:** +Depending on the implementation language, the cma_decode_inspect function takes a single or two arguments (just `input` or `type` and `input`) then returns the destructured `CmaParserInput`. + +- **input**: This is a JSON object type, and is expected to be the exact JSON object that was passed to the application from the rollup server. + +- **type**: This an enum, it represents the type of request being passed to the parser and helps the parser decide how to decode the `input`. It is of the same type as the `req_input` from the cma_decode_advance() function, but the cma_parser_decode_inspect() function only accepts the `CMA_PARSER_INPUT_TYPE_BALANCE`, `CMA_PARSER_INPUT_TYPE_SUPPLY` components of the enum. + +#### **Return / Output:** +The `cma_parser_decode_inspect()` function returns either an error or a struct, in certain languages like C++, it returns both. The struct returned is the same as the `req_type` that's returned by the cma_parser_decode_advance function. + +### **cma_encode_voucher():** +This function requires the request type as well as a well defined struct containing all necessary details to construct the specified voucher, then proceeds to build and return the constructed voucher struct. + +#### **Function declaration:** +Below is a function declaration of the cma_encode_voucher() function across multiple languages: + + + +

+
+```rust
+fn cma_encode_voucher( req_type: CmaParserVoucherType, voucher_request: CmaVoucherFieldType) -> Result 
+```
+
+
+ + +

+
+```cpp
+cma_parser_error_t cma_encode_voucher(cma_parser_voucher_type_t type, cma_abi_address_t *app_address, const cma_parser_voucher_data_t *voucher_request, cma_voucher_t *voucher);
+```
+
+
+
+ +#### **Arguments:** +For Rust applicaitons the cma_decode_advance function takes two arguments, `req_type` and `voucher_request`, while for C++ it takes 4 arguments; `type`, `app_address`, `voucher_request`, `voucher`. + +- **req_type / type**: This is an enum, it specifies the exact voucher type which the application wants the parser lib to encode. + + + +

+
+```rust
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum CmaParserVoucherType {
+    CmaParserVoucherTypeNone,
+    CmaParserVoucherTypeEther,
+    CmaParserVoucherTypeErc20,
+    CmaParserVoucherTypeErc721,
+    CmaParserVoucherTypeErc1155Single,
+    CmaParserVoucherTypeErc1155Batch,
+}
+```
+
+
+ + +

+
+```cpp
+enum cma_parser_voucher_type_t {
+    CMA_PARSER_VOUCHER_TYPE_NONE,
+    CMA_PARSER_VOUCHER_TYPE_ETHER,
+    CMA_PARSER_VOUCHER_TYPE_ERC20,
+    CMA_PARSER_VOUCHER_TYPE_ERC721,
+    CMA_PARSER_VOUCHER_TYPE_ERC1155_SINGLE,
+    CMA_PARSER_VOUCHER_TYPE_ERC1155_BATCH,
+};
+```
+
+
+
+ +- **app_address**: This is the address of the application on the base layer. + +- **voucher_request**: This is an enum containing a group of structs, with each struct containing the different arguments needed to create a specific type of voucher, the parser library receives this struct then creates a voucher based on the contents of that struct. Below is a structure of the voucher_request enum along with all supported child structs. + + + + + +```bash +Click on Rust or Cpp tab to expand +``` + + + + + +

+
+```rust
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum CmaVoucherFieldType {
+    EtherVoucherFields(CmaParserEtherVoucherFields),
+    Erc20VoucherFields(CmaParserErc20VoucherFields),
+    Erc721VoucherFields(CmaParserErc721VoucherFields),
+    Erc1155SingleVoucherFields(CmaParserErc1155SingleVoucherFields),
+    Erc1155BatchVoucherFields(CmaParserErc1155BatchVoucherFields),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct CmaParserEtherVoucherFields {
+    pub amount: U256,
+    pub receiver: Address,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct CmaParserErc20VoucherFields {
+    pub token: Address,
+    pub receiver: Address,
+    pub amount: U256,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct CmaParserErc721VoucherFields {
+    pub token: Address,
+    pub token_id: U256,
+    pub receiver: Address,
+    pub application_address: Address,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct CmaParserErc1155SingleVoucherFields {
+    pub token: Address,
+    pub token_id: U256,
+    pub receiver: Address,
+    pub value: U256,
+    pub amount: U256,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct CmaParserErc1155BatchVoucherFields {
+    pub token: Address,
+    pub receiver: Address,
+    pub count: usize,
+    pub token_ids: Vec,
+    pub value: U256,
+    pub amounts: Vec,
+}
+```
+
+
+ + +

+
+```cpp
+typedef struct cma_parser_voucher_data {
+    cma_abi_address_t *receiver;
+    union {
+        struct cma_parser_ether_voucher_fields_t ether_voucher_fields;
+        struct cma_parser_erc20_voucher_fields_t erc20_voucher_fields;
+        struct cma_parser_erc721_voucher_fields_t erc721_voucher_fields;
+        struct cma_parser_erc1155_single_voucher_fields_t erc1155_single_voucher_fields;
+        struct cma_parser_erc1155_batch_voucher_fields_t erc1155_batch_voucher_fields;
+    } u;
+} cma_parser_voucher_data_t;
+
+struct cma_parser_ether_voucher_fields_t {
+    cma_amount_t amount;
+};
+struct cma_parser_erc20_voucher_fields_t {
+    cma_token_address_t token;
+    cma_amount_t amount;
+};
+struct cma_parser_erc721_voucher_fields_t {
+    cma_token_address_t token;
+    cma_token_id_t token_id;
+    cma_abi_bytes_t exec_layer_data;
+};
+struct cma_parser_erc1155_single_voucher_fields_t {
+    cma_token_address_t token;
+    cma_token_id_t token_id;
+    cma_amount_t amount;
+};
+struct cma_parser_erc1155_batch_voucher_fields_t {
+    cma_token_address_t token;
+    size_t count;
+    cma_token_id_t *token_ids;
+    cma_amount_t *amounts;
+};
+```
+
+
+
+ +#### **Return / Output:** +The `cma_encode_voucher()` function returns either an error or a struct for the rust implementation, but for C++ it returns an error as well as populate an area in memory with the voucher it generated based on the application request. Below is a definition and structure of the possible return struct containing the voucher. + + + +

+
+```rust
+fn cma_encode_voucher( req_type: CmaParserVoucherType, voucher_request: CmaVoucherFieldType) -> Result 
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct CmaVoucher {
+    pub destination: String,
+    pub payload: String,
+}
+```
+
+
+ + +

+
+```cpp
+cma_parser_error_t cma_encode_voucher(cma_parser_voucher_type_t type, cma_abi_address_t *app_address, const cma_parser_voucher_data_t *voucher_request, cma_voucher_t *voucher);
+
+typedef struct cma_voucher {
+    cmt_abi_address_t address;
+    cmt_abi_bytes_t data;
+} cma_voucher_t;
+```
+
+
+
+ + +## Error Handling + +All parser operations return a possible `CmaParserError`, Error, which may include: + +- Incompatible input error +- Malformed Input Error +- Unknown Error +- User specified message string Error + +## Supported Advance Operations + +### **Deposits:** + +The parser library automatically decodes deposits sent through the **Cartesi asset portals** (Ether, ERC20, ERC721, and ERC1155). When the application receives a deposit request from any of the portals. The application logic compares and identifies the caller to be one of the portal, then calls the **cma_decode_advance()** function in the parser lib, passing it the input type (based on the exact portal), and the exact input received. The parser lib: + +1. Extracts relevant details such as: + - Deposited asset + - Depositor address + - Amount or token ID(s) + - Execution layer data (if any) + +2. Builds a strongly typed struct specific to the deposited asset. + +3. Returns the decoded struct to the application. + +The returned struct can then be consumed directly by **The Application-defined logic**, or **The CMA Ledger library** for state updates. This process is automatic and does **not** require application-defined function selectors. + +### **Application defined calls (Withdrawals, Transfers, Etc.)**: + +Unlike deposits, **application-defined calls** must follow a predefined ABI structure for the parser to decode them correctly. To properly decode an application input, the parser must: +- Know the expected function selector +- Know the argument order and types + +The parser achieves this by maintaining a **predefined collection of supported function selectors**. When an input is received: +1. The parser extracts the first 4 bytes (function selector) +2. It compares the selector against its supported collection +3. If a match is found, the input is decoded according to the expected ABI +4. The decoded data is returned as a structured type + +It is therefore important that **if you intend to use the parser lib, you should structure your input to match one of the supported function selector signatures then also send the input to the applicaton as an abi encoded hex string**. In a situation where the input does not match any of the predefined function selectors, the parser simply returns the Input to the application as decode response. + +The table below lists all **application-defined function selectors** supported by the parser library. Inputs **must** be ABI-encoded and match one of these signatures to be decoded correctly. + +| Function Declaration | Function Selector | Function Arguments | +|----------------------|-------------------|--------------------| +| `WithdrawEther(uint256, bytes)` | `0x8cf70f0b` | • `uint256` – Amount of Ether to withdraw
• `bytes` – Execution-layer data | +| `WithdrawErc20(address, uint256, bytes)` | `0x4f94d342` | • `address` – ERC20 token contract address
• `uint256` – Amount to withdraw
• `bytes` – Execution-layer data | +| `WithdrawErc721(address, uint256, bytes)` | `0x33acf293` | • `address` – ERC721 token contract address
• `uint256` – Token ID to withdraw
• `bytes` – Execution-layer data | +| `WithdrawErc1155Single(address, uint256, uint256, bytes)` | `0x8bb0a811` | • `address` – ERC1155 token contract address
• `uint256` – Token ID
• `uint256` – Amount to withdraw
• `bytes` – Execution-layer data | +| `WithdrawErc1155Batch(address, uint256[], uint256[], bytes)` | `0x50c80019` | • `address` – ERC1155 token contract address
• `uint256[]` – Token IDs
• `uint256[]` – Amounts (must match token IDs)
• `bytes` – Execution-layer data | +| `TransferEther(uint256, bytes32, bytes)` | `0x428c9c4d` | • `uint256` – Amount of Ether to transfer
• `bytes32` – Recipient account identifier
• `bytes` – Execution-layer data | +| `TransferErc20(address, bytes32, uint256, bytes)` | `0x03d61dcd` | • `address` – ERC20 token contract address
• `bytes32` – Recipient account identifier
• `uint256` – Amount to transfer
• `bytes` – Execution-layer data | +| `TransferErc721(address, bytes32, uint256, bytes)` | `0xaf615a5a` | • `address` – ERC721 token contract address
• `bytes32` – Recipient account identifier
• `uint256` – Token ID to transfer
• `bytes` – Execution-layer data | +| `TransferErc1155Single(address, bytes32, uint256, uint256, bytes)` | `0xe1c913ed` | • `address` – ERC1155 token contract address
• `bytes32` – Recipient account identifier
• `uint256` – Token ID
• `uint256` – Amount to transfer
• `bytes` – Execution-layer data | +| `TransferErc1155Batch(address, bytes32, uint256[], uint256[], bytes)` | `0x638ac6f9` | • `address` – ERC1155 token contract address
• `bytes32` – Recipient account identifier
• `uint256[]` – Token IDs
• `uint256[]` – Amounts
• `bytes` – Execution-layer data | + +## Installation + + + +

+
+```bash
+cargo add cma-rust-parser
+```
+
+
+
+ + +## Usage Example + + + +

+
+```rust
+use cma_rust_parser::{
+    CmaParserInputType, cma_decode_advance, cma_encode_voucher, CmaParserVoucherType, CmaVoucherFieldType, CmaParserEtherVoucherFields, CmaParserInputData, CmaParserErc721VoucherFields
+};
+use cma_rust_parser::helpers::{ToAddress, ToJson};
+
+pub struct Storage {
+    pub erc721_portal_address: String,
+    pub dapp_address_relay: String,
+    pub erc721_token: String,
+    pub app_address: String,
+}
+
+pub async fn handle_advance( _client: &hyper::Client, _server_addr: &str, request: JsonValue, storage: &mut Storage
+) -> Result<&'static str, Box> {
+    let zero_address = "0x0000000000000000000000000000000000000000".to_string();
+    let msg_sender = request["data"]["metadata"]["msg_sender"].as_str().ok_or("Invalid msg_sender address")?;
+    let mut decoded_req = Err(CmaParserError::Unknown);
+
+    match msg_sender {
+        s if s.to_lowercase() == storage.erc721_portal_address.to_lowercase() => {
+            let req_type = CmaParserInputType::CmaParserInputTypeErc721Deposit;
+            // CALL THE PARSER LIBRARY TO DECODE THE ERC721 TOKEN DEPOSIT
+            decoded_req = cma_decode_advance(req_type, request.clone()); 
+        },
+        s if s.to_lowercase() == storage.dapp_address_relay.to_lowercase() => {
+            if storage.app_address == zero_address {
+                let _payload = request["data"]["payload"].as_str().ok_or("Missing payload")?;
+                storage.app_address = _payload.to_string();
+            }
+        },
+        _ => { // IF THE MSG_SENDER IS NOT ANY OF THE PORTALS OR THE ADDRESS RELAYER, IT'S SAFE TO ASSUME IT'S A REGULAR USER INTERACTION
+            let req_type: CmaParserInputType = CmaParserInputType::CmaParserInputTypeAuto;
+            // CALL THE PARSER LIBRARY TO DECODE A USER INTERACTION (PARSER LIB MATCHES FUNCTION SELECTOR)
+            decoded_req = cma_decode_advance(req_type, request.clone()); 
+        }
+    }
+
+    match decoded_req { // MATCH ON THE RESPONSE FROM THE PARSER LIB SO AS TO HANDLE ERRORS OR CONSUME RESPONSE OBJECT
+        Ok(decoded) => {
+            match decoded.req_type { // MATCH ON A SUCCESFUL RESPONSE TO IDENTIFY WHICH OBJECT WAS RETURNED (ERC721)
+                CmaParserInputType::CmaParserInputTypeErc721Deposit => {
+                    if let CmaParserInputData::Erc721Deposit(data) = input {                        
+                        // BUILD A STRUCT WHICH CONTAINS ALL NECESSARY DATA TO BUILD A VOUCHER
+                        let voucher_request = CmaParserErc721VoucherFields{
+                            token: format!("{:?}", data.token).to_address().unwrap(),
+                            token_id: data.token_id.into(),
+                            receiver: format!("{:?}", data.sender).to_address().unwrap(),
+                            application_address: storage.app_address.to_address().unwrap()
+                        };
+                        // CALL THE PARSER LIB TO BUILD AN ERC721 VOUCHER
+                        if let Ok(voucher) = cma_encode_voucher(CmaParserVoucherType::CmaParserVoucherTypeErc721, CmaVoucherFieldType::Erc721VoucherFields(voucher_request)) {
+                            emit_voucher(voucher.to_json()).await;
+                        }
+                    }
+                },
+                CmaParserInputType::CmaParserInputTypeUnidentified => {
+                    // HANDLE THE RESULT STRUCT FOR USER DEFINED INPUTS
+                }
+                _ => {
+                    // HANDLE ANY OTHER POSSIBLE RESPONSE STRUCTURE THE APPLICAITON EXPECTS
+                }
+            }
+        },
+        Err(e) => {
+            emit_report(format!("Could'nt decoding advance request: {:?}", e)).await;
+        }
+    }
+    Ok("accept")
+}
+```
+
+
+
+ +## Best Practices +- Use type-safe enums and objects in every language to avoid logic bugs +- Always check error results, especially when parsing raw input or encoding vouchers + +## See Also +- [Using the CMA library - Ledger component](./ledger-submoule.md) +- [Original C++ implementation & FFI headers](https://github.com/Mugen-Builders/machine-asset-tools) +- [Rust parser reference (crate)](https://github.com/Mugen-Builders/machine-asset-tools/tree/main/cma-rust-parser) diff --git a/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/snippets/cpp-parser-result-types.md b/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/snippets/cpp-parser-result-types.md new file mode 100644 index 000000000..3fce687a3 --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/snippets/cpp-parser-result-types.md @@ -0,0 +1,133 @@ +```cpp +cma_parser_error_t cma_decode_advance(cma_parser_input_type_t type, const cmt_rollup_advance_t *input, cma_parser_input_t *parser_input); + +typedef struct cma_parser_input { + enum cma_parser_input_type_t type; + union { + struct cma_parser_ether_deposit_t ether_deposit; + struct cma_parser_erc20_deposit_t erc20_deposit; + struct cma_parser_erc721_deposit_t erc721_deposit; + struct cma_parser_erc1155_single_deposit_t erc1155_single_deposit; + struct cma_parser_erc1155_batch_deposit_t erc1155_batch_deposit; + struct cma_parser_ether_withdrawal_t ether_withdrawal; + struct cma_parser_erc20_withdrawal_t erc20_withdrawal; + struct cma_parser_erc721_withdrawal_t erc721_withdrawal; + struct cma_parser_erc1155_single_withdrawal_t erc1155_single_withdrawal; + struct cma_parser_erc1155_batch_withdrawal_t erc1155_batch_withdrawal; + struct cma_parser_ether_transfer_t ether_transfer; + struct cma_parser_erc20_transfer_t erc20_transfer; + struct cma_parser_erc721_transfer_t erc721_transfer; + struct cma_parser_erc1155_single_transfer_t erc1155_single_transfer; + struct cma_parser_erc1155_batch_transfer_t erc1155_batch_transfer; + struct cma_parser_balance_t balance; + struct cma_parser_supply_t supply; + } u; +} cma_parser_input_t; + +struct cma_parser_ether_deposit_t { + cma_abi_address_t sender; + cma_amount_t amount; + cma_abi_bytes_t exec_layer_data; +}; +struct cma_parser_erc20_deposit_t { + cma_abi_address_t sender; + cma_token_address_t token; + cma_amount_t amount; + cma_abi_bytes_t exec_layer_data; +}; +struct cma_parser_erc721_deposit_t { + cma_abi_address_t sender; + cma_token_address_t token; + cma_token_id_t token_id; + cma_abi_bytes_t exec_layer_data; +}; +struct cma_parser_erc1155_single_deposit_t { + cma_abi_address_t sender; + cma_token_address_t token; + cma_token_id_t token_id; + cma_amount_t amount; + cma_abi_bytes_t exec_layer_data; +}; +struct cma_parser_erc1155_batch_deposit_t { + cma_abi_address_t sender; + cma_token_address_t token; + size_t count; + cma_token_id_t *token_ids; + cma_amount_t *amounts; + cma_abi_bytes_t base_layer_data; + cma_abi_bytes_t exec_layer_data; +}; + +struct cma_parser_ether_withdrawal_t { + cma_amount_t amount; + cma_abi_bytes_t exec_layer_data; +}; +struct cma_parser_erc20_withdrawal_t { + cma_token_address_t token; + cma_amount_t amount; + cma_abi_bytes_t exec_layer_data; +}; +struct cma_parser_erc721_withdrawal_t { + cma_token_address_t token; + cma_token_id_t token_id; + cma_abi_bytes_t exec_layer_data; +}; +struct cma_parser_erc1155_single_withdrawal_t { + cma_token_address_t token; + cma_token_id_t token_id; + cma_amount_t amount; + cma_abi_bytes_t exec_layer_data; +}; +struct cma_parser_erc1155_batch_withdrawal_t { + cma_token_address_t token; + size_t count; + cma_token_id_t *token_ids; + cma_amount_t *amounts; + cma_abi_bytes_t exec_layer_data; +}; + +struct cma_parser_ether_transfer_t { + cma_account_id_t receiver; + cma_amount_t amount; + cma_abi_bytes_t exec_layer_data; +}; +struct cma_parser_erc20_transfer_t { + cma_account_id_t receiver; + cma_token_address_t token; + cma_amount_t amount; + cma_abi_bytes_t exec_layer_data; +}; +struct cma_parser_erc721_transfer_t { + cma_account_id_t receiver; + cma_token_address_t token; + cma_token_id_t token_id; + cma_abi_bytes_t exec_layer_data; +}; +struct cma_parser_erc1155_single_transfer_t { + cma_account_id_t receiver; + cma_token_address_t token; + cma_token_id_t token_id; + cma_amount_t amount; + cma_abi_bytes_t exec_layer_data; +}; +struct cma_parser_erc1155_batch_transfer_t { + cma_account_id_t receiver; + cma_token_address_t token; + size_t count; + cma_token_id_t *token_ids; + cma_amount_t *amounts; + cma_abi_bytes_t exec_layer_data; +}; + +struct cma_parser_balance_t { + cma_account_id_t account; + cma_token_address_t token; + cma_token_id_t token_id; + cma_abi_bytes_t exec_layer_data; +}; +struct cma_parser_supply_t { + cma_token_address_t token; + cma_token_id_t token_id; + cma_abi_bytes_t exec_layer_data; +}; +``` \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/snippets/rust-parser-result-types.md b/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/snippets/rust-parser-result-types.md new file mode 100644 index 000000000..4e0cf9306 --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/snippets/rust-parser-result-types.md @@ -0,0 +1,181 @@ +```rust +fn cma_decode_advance(req_type: CmaParserInputType, input: JsonValue) -> Result; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CmaParserInput { + pub req_type: CmaParserInputType, + pub input: CmaParserInputData, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum CmaParserInputData { + EtherDeposit(CmaParserEtherDeposit), + Erc20Deposit(CmaParserErc20Deposit), + Erc721Deposit(CmaParserErc721Deposit), + Erc1155SingleDeposit(CmaParserErc1155SingleDeposit), + Erc1155BatchDeposit(CmaParserErc1155BatchDeposit), + EtherWithdrawal(CmaParserEtherWithdrawal), + Erc20Withdrawal(CmaParserErc20Withdrawal), + Erc721Withdrawal(CmaParserErc721Withdrawal), + Erc1155SingleWithdrawal(CmaParserErc1155SingleWithdrawal), + Erc1155BatchWithdrawal(CmaParserErc1155BatchWithdrawal), + EtherTransfer(CmaParserEtherTransfer), + Erc20Transfer(CmaParserErc20Transfer), + Erc721Transfer(CmaParserErc721Transfer), + Erc1155SingleTransfer(CmaParserErc1155SingleTransfer), + Erc1155BatchTransfer(CmaParserErc1155BatchTransfer), + Balance(CmaParserBalance), + Supply(CmaParserSupply), + Unidentified(CmaParserUnidentifiedInput), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CmaParserEtherDeposit { + pub sender: Address, + pub amount: U256, + pub exec_layer_data: Bytes, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CmaParserErc20Deposit { + pub sender: Address, + pub token: Address, + pub amount: U256, + pub exec_layer_data: Bytes, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CmaParserErc721Deposit { + pub sender: Address, + pub token: Address, + pub token_id: U256, + pub exec_layer_data: Bytes, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CmaParserErc1155SingleDeposit { + pub sender: Address, + pub token: Address, + pub token_id: U256, + pub amount: U256, + pub exec_layer_data: Bytes, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CmaParserErc1155BatchDeposit { + pub sender: Address, + pub token: Address, + pub count: usize, + pub token_ids: Vec, + pub amounts: Vec, + pub base_layer_data: Bytes, + pub exec_layer_data: Bytes, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CmaParserEtherWithdrawal { + pub receiver: Address, + pub amount: U256, + pub exec_layer_data: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CmaParserErc20Withdrawal { + pub receiver: Address, + pub token: Address, + pub amount: U256, + pub exec_layer_data: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CmaParserErc721Withdrawal { + pub receiver: Address, + pub token: Address, + pub token_id: U256, + pub exec_layer_data: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CmaParserErc1155SingleWithdrawal { + pub receiver: Address, + pub token: Address, + pub token_id: U256, + pub amount: U256, + pub exec_layer_data: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CmaParserErc1155BatchWithdrawal { + pub receiver: Address, + pub token: Address, + pub count: usize, + pub token_ids: Vec, + pub amounts: Vec, + pub base_layer_data: String, + pub exec_layer_data: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CmaParserEtherTransfer { + pub receiver: U256, + pub amount: U256, + pub exec_layer_data: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CmaParserErc20Transfer { + pub receiver: U256, + pub token: Address, + pub amount: U256, + pub exec_layer_data: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CmaParserErc721Transfer { + pub receiver: U256, + pub token: Address, + pub token_id: U256, + pub exec_layer_data: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CmaParserErc1155SingleTransfer { + pub sender: Address, + pub receiver: Address, + pub token: Address, + pub token_id: U256, + pub amount: U256, + pub exec_layer_data: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CmaParserErc1155BatchTransfer { + pub sender: Address, + pub receiver: Address, + pub token: Address, + pub count: usize, + pub token_ids: Vec, + pub amounts: Vec, + pub base_layer_data: String, + pub exec_layer_data: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CmaParserBalance { + pub account: Address, + pub token: Address, + pub token_ids: Option>, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CmaParserSupply { + pub token: Address, + pub token_ids: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CmaParserUnidentifiedInput { + pub abi_encoded_bytes: Vec, + pub msg_sender: Address, +} +``` \ No newline at end of file diff --git a/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json b/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json index 4ecb9d494..ecbd2dc6c 100644 --- a/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json +++ b/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json @@ -126,7 +126,16 @@ ] } ] - } + }, + { + "type": "category", + "label": "Asset Management Library", + "collapsed": true, + "items": [ + "rollups-apis/asset-management-library/parser-submodule", + "rollups-apis/asset-management-library/ledger-submodule" + ] + } ] }, { diff --git a/src/css/custom.css b/src/css/custom.css index 393ad8e22..f6498da07 100755 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -405,6 +405,14 @@ html[data-theme="dark"] { /* Details */ +details { + background-color: #F6F7F8 !important; + border: 1px solid rgb(246, 247, 248); + padding: 1rem; + border-radius: 8px; + border-color: rgb(246, 247, 248) !important; +} + .details { border: 0 !important; border-bottom: 1px solid rgba(0, 0, 0, 0.1) !important; @@ -412,8 +420,8 @@ html[data-theme="dark"] { margin: 0 !important; border-radius: 0 !important; box-shadow: none !important; - background-color: transparent !important; - color: inherit; + background-color: rgba(0, 0, 0, 0.1) !important; + color: rgba(0, 0, 0, 0.1) !important; } [data-theme="dark"] .details { From 97d611ed5c2c90858e7664ec3e6fc0b1f8b9617e Mon Sep 17 00:00:00 2001 From: Idogwu Chinonso Date: Mon, 15 Jun 2026 06:24:19 +0100 Subject: [PATCH 2/2] Mod: Update Assets management guide --- .dockerignore | 1 - .gitignore | 1 - .vscode/ltex.hiddenFalsePositives.en-US.txt | 2 - .../version-1.5/development/asset-handling.md | 15 - .../ledger-submoule.md | 204 ------ .../parser-submodule.md | 585 ------------------ .../snippets/cpp-parser-result-types.md | 133 ---- .../snippets/rust-parser-result-types.md | 181 ------ .../asset-management/getting-started.md | 199 ++++++ .../asset-management/ledger-reference.md | 152 +++++ .../asset-management/managing-balances.md | 251 ++++++++ .../asset-management/overview.md | 62 ++ .../asset-management/parser-reference.md | 183 ++++++ .../asset-management/parsing-inputs.md | 268 ++++++++ .../asset-management/types-and-selectors.md | 227 +++++++ .../asset-management/vouchers.md | 132 ++++ .../version-2.0/development/asset-handling.md | 16 +- .../version-1.5-sidebars.json | 11 +- .../version-2.0-sidebars.json | 15 + src/css/custom.css | 12 +- static/llms.txt | 11 + 21 files changed, 1518 insertions(+), 1143 deletions(-) delete mode 100644 cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/ledger-submoule.md delete mode 100644 cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/parser-submodule.md delete mode 100644 cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/snippets/cpp-parser-result-types.md delete mode 100644 cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/snippets/rust-parser-result-types.md create mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/getting-started.md create mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/ledger-reference.md create mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/managing-balances.md create mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/overview.md create mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/parser-reference.md create mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/parsing-inputs.md create mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/types-and-selectors.md create mode 100644 cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/vouchers.md diff --git a/.dockerignore b/.dockerignore index 844ca197f..27d2dae2b 100755 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,2 @@ */node_modules -/TEMP *.log diff --git a/.gitignore b/.gitignore index ff43b52a8..a13d11fbf 100755 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ # production /build -/TEMP # generated files .docusaurus diff --git a/.vscode/ltex.hiddenFalsePositives.en-US.txt b/.vscode/ltex.hiddenFalsePositives.en-US.txt index b14449ef5..5c4c1e912 100644 --- a/.vscode/ltex.hiddenFalsePositives.en-US.txt +++ b/.vscode/ltex.hiddenFalsePositives.en-US.txt @@ -2,5 +2,3 @@ {"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$"} {"rule":"MORFOLOGIK_RULE_EN_US","sentence":"^\\QThe Devnet environment functions similarly to a mainnet.\\E$"} -{"rule":"MORFOLOGIK_RULE_EN_US","sentence":"^\\QDeposit input payloads arrives applications as an abi-encoded hex, in-order to obtain the deposit parameters this hex payload is decoded\\E$"} -{"rule":"PLURAL_VERB_AFTER_THIS","sentence":"^\\QThis decode process could either be handled manually by the developers or could be automated using the assets manager library\\E$"} diff --git a/cartesi-rollups_versioned_docs/version-1.5/development/asset-handling.md b/cartesi-rollups_versioned_docs/version-1.5/development/asset-handling.md index 2eb606a4f..cf54a313d 100644 --- a/cartesi-rollups_versioned_docs/version-1.5/development/asset-handling.md +++ b/cartesi-rollups_versioned_docs/version-1.5/development/asset-handling.md @@ -41,19 +41,6 @@ Deposit input payloads are always specified as packed ABI-encoded parameters, as | 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`
| -## Decoding Deposits - -Deposit input payloads arrives applications as an abi-encoded hex, this hex input would need to be decoded to obtain the contained deposit parameters. This decode process could either be handled manually by the developers or could be automated using the assets' manager library. - -The **parser submodule** of the asset manager library simplifies the decode process for deposit payloads by abstracting an already implemented logic to decode the deposit payload for all major assets (ERC721, ERC20, ERC721), it exposes a method called decode input, which the application logic transfers the deposit payload to then receives a struct containing all the parameters contained in the deposit payload. A more detailed guide on the parser library can be found in this [Asset management library section.](../rollups-apis/asset-management-library/parser-submodule.md) - -## Managing and Recording Deposits - -Once a deposit occurs on the base layer, the application receives all necessary details pertaining to the deposit. It's important that the application maintain a record of an address to a token and finally the balance of the address for that token, without a proper record, the application is unable to tell the exact token and also the amount of token that each address owns on the dApp. - -This record can either be implemented manually by the developer or the application could simply import the Asset manager library then leverage the **ledger submodule** to manage and keep this record. - -The ledger submodule can be likened to a plug in wallet for applications, it contains a storage struct which tracks the assets balance for each address as well as the total supply for each token deposited to the wallet, finally it exposes methods to handle deposit, transfer and withdrawal of these assets in the record. For a detailed explanation of the ledger library, check this [Asset management library section.](../rollups-apis/asset-management-library/ledger-submoule.md) ## Withdrawing assets @@ -80,5 +67,3 @@ Here are the function signatures used by vouchers to withdraw the different type | ERC-721 | Token contract | `safeTransferFrom(address,address,uint256,bytes)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-721#specification) | | ERC-1155 | Token contract | `safeTransferFrom(address,address,uint256,uint256,data)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-1155#specification) | | ERC-1155 | Token contract | `safeBatchTransferFrom(address,address,uint256[],uint256[],data)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-1155#specification) | - -Structuring a voucher to these signatures can either be done manually or by using the **Parser submodule** of the asset management library. \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/ledger-submoule.md b/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/ledger-submoule.md deleted file mode 100644 index 911cdb6c6..000000000 --- a/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/ledger-submoule.md +++ /dev/null @@ -1,204 +0,0 @@ ---- -id: ledger-submodule -title: Ledger Submodule ---- - -# Integrating the Ledger Library - -The **Asset Manager Library Ledger submodule** is one of the two submodules of the Asset management library, it's designed for managing assets, accounts, balances, and transfers inside Cartesi applications. - -You can think of the Ledger as a **plug-and-play wallet engine**. Once integrated, it allows your application to safely track and modify asset state without re-implementing common accounting logic such as deposits, withdrawals, and transfers. -The CMA tools are split into **two complementary libraries**: - -- **Ledger**: Manages assets, accounts, balances, and state changes -- **Parser**: Parses user input and structures data for the ledger to consume - -Together, they provide a smooth development experience: - -1. The parser receives raw user input and converts it into structured data. - -2. The ledger consumes this structured input and applies the corresponding asset and account updates. - -If you are not yet familiar with the parser, see [Integrating the CMA Parser Library](./parser-submodule.md). - -By integrating the ledger, your application gains a secure, deterministic, and type-safe way to manage digital assets such as Ether, ERC20, ERC721, and ERC1155, while remaining fully compatible with Cartesi rollup workflows. - -## Why Use the Ledger? - -The CMA Ledger solves common asset-management problems in Cartesi applications: - -- **Unified Asset Management**: Manage multiple asset standards and account models from a single interface. - -- **Deterministic & Auditable**: Ledger behavior is identical across environments (tests, simulations, and production), making state changes predictable and auditable. - -- **Strong Safety Guarantees**: All operations return typed errors, eliminating silent failures and ambiguous states. - -- **Cartesi-Native Design**: Built specifically for Cartesi rollups, while remaining flexible and non-intrusive to application logic. - -## Key Capabilities - -- **Asset Models**: Support for Ether, ERC20, ERC721, and ERC1155-style assets. - -- **Account Models**: Support for Ethereum wallet addresses mapped to generic identifiers' ID. - -- **Asset Lifecycle Operations**: Create, retrieve, deposit, withdraw, and transfer assets. - -- **Balance & Supply Queries**: Read per-account balances and total asset supply at any time. - -- **Strong Typing**: Prevents asset/account mismatches at compile time. - -## Defined Data Types - -| Type | Description | -|-----|------------| -| `LedgerAssetId` | Internal 64-bit asset identifier | -| `LedgerAccountId` | Internal 64-bit account identifier | -| `AssetType` | ID-based, address-based, or address + ID | -| `AccountType` | Wallet address or generic account ID | -| `RetrieveOperation` | `Find`, `Create`, or `FindOrCreate` | - -## Commonly Used Methods - -### Asset and Account Retrieval - -The below table details all available methods for handling assets and account retrievals. - -| Method | Description | Asset/Account type | Args | Returns | -|---|---|---|---|---| -| `retrieve_asset()` | Low-level method for retrieving or creating any asset | Generic | `asset_id`, `token_address`, `token_id`, `asset_type`, `operation` | `Result` | -| `retrieve_erc20_asset_via_address()` | Retrieves or creates an ERC20 asset using its contract address | Erc20 | `token_address` | `Result` | -| `retrieve_erc721_assets_via_address()` | Retrieves or creates an ERC721 asset using contract address and token ID | Erc721 | `token_address`, `token_id` | `Result` | -| `retrieve_ether_assets()` | Retrieves or creates the Ether asset | Ether | none | `Result` | -| `retrieve_account()` | Low level method for retrieving or creating an account | Generic | `account_id`, `account_type`, `addr_or_id`, `operation` | `Result` | -| `retrieve_account_via_address()` | Retrieves or creates an account mapped to an Ethereum address | Wallet | wallet_address | `Result` | - -### State Mutations - -State mutations cover methods that alter the assets record of the ledger library, this could be either to deposit, transfer or withdraw assets, the below table offers better description for each of this methods. - -| Method | Description | Args | -|---|---|---| -| `deposit()` | Adds assets to an account's balance | `asset_id`, `to_account_id`, `amount` | -| `withdraw()` | Removes assets from an account's balance | `asset_id`, `from_account_id`, `amount` | -| `transfer()` | Moves assets between two accounts | `asset_id`, `from_account_id`, `to_account_id`, `amount` | - -### Queries - -The Ledger also exposes read only methods for inspecting its internal state. These methods do not mutate state and can be safely called at any time. - -| Method | Description | Args | Returns | -|---|---|---|---| -| `get_balance()` | Returns the balance of an asset for an account | `asset_id`, `account_id` | `Result` | -| `get_total_supply()` | Returns the total supply of an asset | `asset_id` | `Result` | - -### Lifecycle - -Before using the ledger lib, it's necessary to initialize a new instance via the `new()` method. This returns a ledger struct that serves as the single source of truth for all asset and account records in your application. The below table provides more details about functions that alter the lifecycle of the ledger. - -| Method | Description | Returns | -|---|---|---| -| `Ledger::new()` | Creates a new ledger instance | `Result` | -| `ledger.reset()` | Resets all records in the ledger | `Result<(), LedgerError>` | - -## Error Handling - -All ledger operations return a `LedgerError`, including: - -- Asset or account not found -- Insufficient balance -- Type mismatches -- Internal ledger failures - -This makes error handling explicit and predictable. - -## Installation - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - - - - -

-
-```bash
-cargo add cma-rust-parser
-```
-
-
-
- - -## Usage Example - - - - -

-
-```rust
-use cma_rust_parser::{
-    Ledger, AssetType, RetrieveOperation, AccountType, U256, Address
-};
-
-let address_one = "0x0000000000000000000000000000000000000001";
-let address_two = "0x0000000000000000000000000000000000000002";
-let erc_20_token = "0x92C6bcA388E99d6B304f1Af3c3Cd749Ff0b591e2";
-let erc_721_token = "0xc6582A9b48F211Fa8c2B5b16CB615eC39bcA653B";
-
-// Initialize the ledger
-let mut ledger = Ledger::new()?;
-
-// Retrieve or create an asset
-let token_address = Address::from_str_hex(erc_721_token).unwrap();
-let token_id = U256::from_u64(1); // Used for NFTs
-
-let asset_id = ledger.retrieve_asset(
-    None,
-    Some(token_address),
-    Some(token_id),
-    AssetType::TokenAddressId,
-    RetrieveOperation::FindOrCreate,
-)?;
-
-// Retrieve or create an account
-let wallet_address = Address::from_str_hex(address_one).unwrap();
-
-let account_id = ledger.retrieve_account(
-    None,
-    AccountType::WalletAddress,
-    RetrieveOperation::FindOrCreate,
-    Some(wallet_address.as_bytes())
-)?;
-
-// Deposit tokens
-ledger.deposit(asset_id, account_id, U256::from_u64(1000)).unwrap();
-
-// Transfer tokens
-let recipient_address = Address::from_str_hex(address_two).unwrap();
-let receipient_id = ledger.retrieve_account_via_address(recipient_address).unwrap();
-
-let transfer_result = ledger.transfer(asset_id, account_id, recipient_id, U256::from_u64(1)).unwrap();
-
-// Query balances and supply
-let balance = ledger.get_balance(asset_id, account_id).unwrap();
-let supply = ledger.get_total_supply(asset_id).unwrap();
-
-// Retrieve ERC20 asset ID
-let erc20_address = Address::from_str_hex(erc_20_token).unwrap();
-let erc20_token_id = ledger.retrieve_erc20_asset_via_address(erc20_address).unwrap();
-
-// Withdraw Token
-let withdraw_result = ledger.withdraw(erc20_token_id, account_id, U256::from_u64(100000000000000000000))
-
-// Retrieve Ether ID
-let ether_id = ledger.retrieve_ether_assets(); // Ether has no contract address hence there's no need to pass an address
-```
-
-
-
- -## Further Reading - -- [C++ API & language bindings](https://github.com/Mugen-Builders/machine-asset-tools) -- [Integrating the CMA Library – Parser Functions](./parser-submodule.md) diff --git a/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/parser-submodule.md b/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/parser-submodule.md deleted file mode 100644 index 5241fc794..000000000 --- a/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/parser-submodule.md +++ /dev/null @@ -1,585 +0,0 @@ ---- -id: parser-submodule -title: Parser Submodule ---- - -# Integrating the Parser Library - -The **Asset Manager Library Parser submodule** is a submodule of the Asset management library designed to handle the encoding and decoding of Cartesi application inputs and outputs in a type-safe, user-friendly, and extensible way. The parser is implemented natively in each language (Rust, Python, C++, etc.) to maximize flexibility and allow seamless integration into various application development environments. - -## Functions of the Parser Lib - -- **Parsing Rollup Inputs and Inspects**: Decode raw payloads from Cartesi rollup advance/inspect requests into rich, typed objects for validation/business logic. - -- **Voucher Encoding**: Construct valid voucher payloads targeting Ether, ERC20, ERC721, and ERC1155 standards to move these assets back to L1. - -## Benefits of the Parser Lib - -- **Full Coverage of Token Standards**: Supports parsing of inspects requests, token deposit and voucher generation for major assets (Ether, ERC20, ERC721, ERC1155). - -- **Cartesi-Native Design**: Built specifically for Cartesi rollups, while remaining flexible and non-intrusive to application logic. - -- **Deterministic & Auditable**: Ledger behavior is identical across languages and environments (tests, simulations, and production), making state changes predictable and auditable. - -- **Data Integrity & Safety**: Strictly typed output models and errors boost security while eliminating common integration bugs. - -## Defined Data Types - -| Type | Description | -|--------------------|--------------------------------------------| -| `CmaParserInputType` | An enum of all supported operation types (requests) that can be decoded (deposit, withdrawal, transfer, inspect, etc.)| -| `CmaParserInputData` | An enum containing a definition of all possible decoded input return object | -| `CmaParserInput` | The fully parsed request object containing the request type and the decoded input object (CmaParserInputData) | -| `CmaVoucherFieldType`| Enum, fields for all voucher types (Ether, ERC20, etc.)| -| `CmaVoucher` | Output-ready voucher ABI fields | -| `TxHexCodes` | An enum containing function selector definitions for all decodable methods | - -Result objects and error types are strictly enforced, ensuring safe contract logic in every supported language. - -## Commonly Used Methods - -### **cma_decode_advance():** -This function receives a request type identifier as well as the application advance input, then decodes the input and returns a well structured output object based on the specified request type. - -#### **Function declaration:** -Below is a function declaration of the cma_decode_advance() function across multiple languages, it contains the function arguments and return types. - - - -

-
-```rust
-fn cma_decode_advance(req_type:  CmaParserInputType, input: JsonValue) -> Result;
-```
-
-
- - -

-
-```cpp
-cma_parser_error_t cma_decode_advance(cma_parser_input_type_t type, const cmt_rollup_advance_t *input, cma_parser_input_t *parser_input);
-```
-
-
-
- -#### **Arguments:** -The cma_decode_advance function takes two arguments `req_type` and `input` : - -- **req_type**: This an enum, it represents the type of request being passed to the parser and helps the parser decide how to decode the `input`. Below is a sample definition of the req_type in different languages. - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - - - - -```bash -Click on Rust or Cpp tab to expand -``` - - - - -

-
-```rust
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum CmaParserInputType {
-    CmaParserInputTypeNone,
-    CmaParserInputTypeAuto,
-    CmaParserInputTypeUnidentified,
-    CmaParserInputTypeEtherDeposit,
-    CmaParserInputTypeErc20Deposit,
-    CmaParserInputTypeErc721Deposit,
-    CmaParserInputTypeErc1155SingleDeposit,
-    CmaParserInputTypeErc1155BatchDeposit,
-    CmaParserInputTypeEtherWithdrawal,
-    CmaParserInputTypeErc20Withdrawal,
-    CmaParserInputTypeErc721Withdrawal,
-    CmaParserInputTypeErc1155SingleWithdrawal,
-    CmaParserInputTypeErc1155BatchWithdrawal,
-    CmaParserInputTypeEtherTransfer,
-    CmaParserInputTypeErc20Transfer,
-    CmaParserInputTypeErc721Transfer,
-    CmaParserInputTypeErc1155SingleTransfer,
-    CmaParserInputTypeErc1155BatchTransfer,
-    CmaParserInputTypeBalance,
-    CmaParserInputTypeSupply,
-}
-```
-
-
- - -

-
-```cpp
-enum cma_parser_input_type_t {
-    CMA_PARSER_INPUT_TYPE_NONE,
-    CMA_PARSER_INPUT_TYPE_AUTO,
-    CMA_PARSER_INPUT_TYPE_ETHER_DEPOSIT,
-    CMA_PARSER_INPUT_TYPE_ERC20_DEPOSIT,
-    CMA_PARSER_INPUT_TYPE_ERC721_DEPOSIT,
-    CMA_PARSER_INPUT_TYPE_ERC1155_SINGLE_DEPOSIT,
-    CMA_PARSER_INPUT_TYPE_ERC1155_BATCH_DEPOSIT,
-    CMA_PARSER_INPUT_TYPE_ETHER_WITHDRAWAL,
-    CMA_PARSER_INPUT_TYPE_ERC20_WITHDRAWAL,
-    CMA_PARSER_INPUT_TYPE_ERC721_WITHDRAWAL,
-    CMA_PARSER_INPUT_TYPE_ERC1155_SINGLE_WITHDRAWAL,
-    CMA_PARSER_INPUT_TYPE_ERC1155_BATCH_WITHDRAWAL,
-    CMA_PARSER_INPUT_TYPE_ETHER_TRANSFER,
-    CMA_PARSER_INPUT_TYPE_ERC20_TRANSFER,
-    CMA_PARSER_INPUT_TYPE_ERC721_TRANSFER,
-    CMA_PARSER_INPUT_TYPE_ERC1155_SINGLE_TRANSFER,
-    CMA_PARSER_INPUT_TYPE_ERC1155_BATCH_TRANSFER,
-    CMA_PARSER_INPUT_TYPE_BALANCE,
-    CMA_PARSER_INPUT_TYPE_SUPPLY,
-};
-```
-
-
-
- -- **input**: This is a Json object type, and is expected to be the exact JSON object that was passed to the application from the rollup server. - - -#### **Return / Output:** -The `cma_decode_advance()` function returns either an error or a struct, in certain languages, it returns both. The struct returned contains the `req_type` and another struct called `input` which contains well labelled arguments extracted from the JSON input received from the rollups server. Below is a sample declaration of the return values of the cma_decode_advance function. - -import RustOutput from './snippets/rust-parser-result-types.md'; -import CppOutput from './snippets/cpp-parser-result-types.md'; - - - - -```bash -Click on Rust or Cpp tab to expand -``` - - - - -

-    
-
-
- - -

-    
-
-
-
- -### **cma_decode_inspect():** -This receives the application inspect value, then decodes and returns a structured object based on the inspect request type. - -#### **Function declaration:** -Below is a function declaration of the cma_decode_inspect() function across multiple languages, it also contains the function arguments and return types. - - - -

-
-```rust
-fn cma_decode_inspect(input: JsonValue) -> Result;
-```
-
-
- - -

-
-```cpp
-int cma_parser_decode_inspect(cma_parser_input_type_t type, const cmt_rollup_inspect_t *input, cma_parser_input_t *parser_input);
-```
-
-
-
- -#### **Arguments:** -Depending on the implementation language, the cma_decode_inspect function takes a single or two arguments (just `input` or `type` and `input`) then returns the destructured `CmaParserInput`. - -- **input**: This is a JSON object type, and is expected to be the exact JSON object that was passed to the application from the rollup server. - -- **type**: This an enum, it represents the type of request being passed to the parser and helps the parser decide how to decode the `input`. It is of the same type as the `req_input` from the cma_decode_advance() function, but the cma_parser_decode_inspect() function only accepts the `CMA_PARSER_INPUT_TYPE_BALANCE`, `CMA_PARSER_INPUT_TYPE_SUPPLY` components of the enum. - -#### **Return / Output:** -The `cma_parser_decode_inspect()` function returns either an error or a struct, in certain languages like C++, it returns both. The struct returned is the same as the `req_type` that's returned by the cma_parser_decode_advance function. - -### **cma_encode_voucher():** -This function requires the request type as well as a well defined struct containing all necessary details to construct the specified voucher, then proceeds to build and return the constructed voucher struct. - -#### **Function declaration:** -Below is a function declaration of the cma_encode_voucher() function across multiple languages: - - - -

-
-```rust
-fn cma_encode_voucher( req_type: CmaParserVoucherType, voucher_request: CmaVoucherFieldType) -> Result 
-```
-
-
- - -

-
-```cpp
-cma_parser_error_t cma_encode_voucher(cma_parser_voucher_type_t type, cma_abi_address_t *app_address, const cma_parser_voucher_data_t *voucher_request, cma_voucher_t *voucher);
-```
-
-
-
- -#### **Arguments:** -For Rust applicaitons the cma_decode_advance function takes two arguments, `req_type` and `voucher_request`, while for C++ it takes 4 arguments; `type`, `app_address`, `voucher_request`, `voucher`. - -- **req_type / type**: This is an enum, it specifies the exact voucher type which the application wants the parser lib to encode. - - - -

-
-```rust
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum CmaParserVoucherType {
-    CmaParserVoucherTypeNone,
-    CmaParserVoucherTypeEther,
-    CmaParserVoucherTypeErc20,
-    CmaParserVoucherTypeErc721,
-    CmaParserVoucherTypeErc1155Single,
-    CmaParserVoucherTypeErc1155Batch,
-}
-```
-
-
- - -

-
-```cpp
-enum cma_parser_voucher_type_t {
-    CMA_PARSER_VOUCHER_TYPE_NONE,
-    CMA_PARSER_VOUCHER_TYPE_ETHER,
-    CMA_PARSER_VOUCHER_TYPE_ERC20,
-    CMA_PARSER_VOUCHER_TYPE_ERC721,
-    CMA_PARSER_VOUCHER_TYPE_ERC1155_SINGLE,
-    CMA_PARSER_VOUCHER_TYPE_ERC1155_BATCH,
-};
-```
-
-
-
- -- **app_address**: This is the address of the application on the base layer. - -- **voucher_request**: This is an enum containing a group of structs, with each struct containing the different arguments needed to create a specific type of voucher, the parser library receives this struct then creates a voucher based on the contents of that struct. Below is a structure of the voucher_request enum along with all supported child structs. - - - - - -```bash -Click on Rust or Cpp tab to expand -``` - - - - - -

-
-```rust
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum CmaVoucherFieldType {
-    EtherVoucherFields(CmaParserEtherVoucherFields),
-    Erc20VoucherFields(CmaParserErc20VoucherFields),
-    Erc721VoucherFields(CmaParserErc721VoucherFields),
-    Erc1155SingleVoucherFields(CmaParserErc1155SingleVoucherFields),
-    Erc1155BatchVoucherFields(CmaParserErc1155BatchVoucherFields),
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct CmaParserEtherVoucherFields {
-    pub amount: U256,
-    pub receiver: Address,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct CmaParserErc20VoucherFields {
-    pub token: Address,
-    pub receiver: Address,
-    pub amount: U256,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct CmaParserErc721VoucherFields {
-    pub token: Address,
-    pub token_id: U256,
-    pub receiver: Address,
-    pub application_address: Address,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct CmaParserErc1155SingleVoucherFields {
-    pub token: Address,
-    pub token_id: U256,
-    pub receiver: Address,
-    pub value: U256,
-    pub amount: U256,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct CmaParserErc1155BatchVoucherFields {
-    pub token: Address,
-    pub receiver: Address,
-    pub count: usize,
-    pub token_ids: Vec,
-    pub value: U256,
-    pub amounts: Vec,
-}
-```
-
-
- - -

-
-```cpp
-typedef struct cma_parser_voucher_data {
-    cma_abi_address_t *receiver;
-    union {
-        struct cma_parser_ether_voucher_fields_t ether_voucher_fields;
-        struct cma_parser_erc20_voucher_fields_t erc20_voucher_fields;
-        struct cma_parser_erc721_voucher_fields_t erc721_voucher_fields;
-        struct cma_parser_erc1155_single_voucher_fields_t erc1155_single_voucher_fields;
-        struct cma_parser_erc1155_batch_voucher_fields_t erc1155_batch_voucher_fields;
-    } u;
-} cma_parser_voucher_data_t;
-
-struct cma_parser_ether_voucher_fields_t {
-    cma_amount_t amount;
-};
-struct cma_parser_erc20_voucher_fields_t {
-    cma_token_address_t token;
-    cma_amount_t amount;
-};
-struct cma_parser_erc721_voucher_fields_t {
-    cma_token_address_t token;
-    cma_token_id_t token_id;
-    cma_abi_bytes_t exec_layer_data;
-};
-struct cma_parser_erc1155_single_voucher_fields_t {
-    cma_token_address_t token;
-    cma_token_id_t token_id;
-    cma_amount_t amount;
-};
-struct cma_parser_erc1155_batch_voucher_fields_t {
-    cma_token_address_t token;
-    size_t count;
-    cma_token_id_t *token_ids;
-    cma_amount_t *amounts;
-};
-```
-
-
-
- -#### **Return / Output:** -The `cma_encode_voucher()` function returns either an error or a struct for the rust implementation, but for C++ it returns an error as well as populate an area in memory with the voucher it generated based on the application request. Below is a definition and structure of the possible return struct containing the voucher. - - - -

-
-```rust
-fn cma_encode_voucher( req_type: CmaParserVoucherType, voucher_request: CmaVoucherFieldType) -> Result 
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct CmaVoucher {
-    pub destination: String,
-    pub payload: String,
-}
-```
-
-
- - -

-
-```cpp
-cma_parser_error_t cma_encode_voucher(cma_parser_voucher_type_t type, cma_abi_address_t *app_address, const cma_parser_voucher_data_t *voucher_request, cma_voucher_t *voucher);
-
-typedef struct cma_voucher {
-    cmt_abi_address_t address;
-    cmt_abi_bytes_t data;
-} cma_voucher_t;
-```
-
-
-
- - -## Error Handling - -All parser operations return a possible `CmaParserError`, Error, which may include: - -- Incompatible input error -- Malformed Input Error -- Unknown Error -- User specified message string Error - -## Supported Advance Operations - -### **Deposits:** - -The parser library automatically decodes deposits sent through the **Cartesi asset portals** (Ether, ERC20, ERC721, and ERC1155). When the application receives a deposit request from any of the portals. The application logic compares and identifies the caller to be one of the portal, then calls the **cma_decode_advance()** function in the parser lib, passing it the input type (based on the exact portal), and the exact input received. The parser lib: - -1. Extracts relevant details such as: - - Deposited asset - - Depositor address - - Amount or token ID(s) - - Execution layer data (if any) - -2. Builds a strongly typed struct specific to the deposited asset. - -3. Returns the decoded struct to the application. - -The returned struct can then be consumed directly by **The Application-defined logic**, or **The CMA Ledger library** for state updates. This process is automatic and does **not** require application-defined function selectors. - -### **Application defined calls (Withdrawals, Transfers, Etc.)**: - -Unlike deposits, **application-defined calls** must follow a predefined ABI structure for the parser to decode them correctly. To properly decode an application input, the parser must: -- Know the expected function selector -- Know the argument order and types - -The parser achieves this by maintaining a **predefined collection of supported function selectors**. When an input is received: -1. The parser extracts the first 4 bytes (function selector) -2. It compares the selector against its supported collection -3. If a match is found, the input is decoded according to the expected ABI -4. The decoded data is returned as a structured type - -It is therefore important that **if you intend to use the parser lib, you should structure your input to match one of the supported function selector signatures then also send the input to the applicaton as an abi encoded hex string**. In a situation where the input does not match any of the predefined function selectors, the parser simply returns the Input to the application as decode response. - -The table below lists all **application-defined function selectors** supported by the parser library. Inputs **must** be ABI-encoded and match one of these signatures to be decoded correctly. - -| Function Declaration | Function Selector | Function Arguments | -|----------------------|-------------------|--------------------| -| `WithdrawEther(uint256, bytes)` | `0x8cf70f0b` | • `uint256` – Amount of Ether to withdraw
• `bytes` – Execution-layer data | -| `WithdrawErc20(address, uint256, bytes)` | `0x4f94d342` | • `address` – ERC20 token contract address
• `uint256` – Amount to withdraw
• `bytes` – Execution-layer data | -| `WithdrawErc721(address, uint256, bytes)` | `0x33acf293` | • `address` – ERC721 token contract address
• `uint256` – Token ID to withdraw
• `bytes` – Execution-layer data | -| `WithdrawErc1155Single(address, uint256, uint256, bytes)` | `0x8bb0a811` | • `address` – ERC1155 token contract address
• `uint256` – Token ID
• `uint256` – Amount to withdraw
• `bytes` – Execution-layer data | -| `WithdrawErc1155Batch(address, uint256[], uint256[], bytes)` | `0x50c80019` | • `address` – ERC1155 token contract address
• `uint256[]` – Token IDs
• `uint256[]` – Amounts (must match token IDs)
• `bytes` – Execution-layer data | -| `TransferEther(uint256, bytes32, bytes)` | `0x428c9c4d` | • `uint256` – Amount of Ether to transfer
• `bytes32` – Recipient account identifier
• `bytes` – Execution-layer data | -| `TransferErc20(address, bytes32, uint256, bytes)` | `0x03d61dcd` | • `address` – ERC20 token contract address
• `bytes32` – Recipient account identifier
• `uint256` – Amount to transfer
• `bytes` – Execution-layer data | -| `TransferErc721(address, bytes32, uint256, bytes)` | `0xaf615a5a` | • `address` – ERC721 token contract address
• `bytes32` – Recipient account identifier
• `uint256` – Token ID to transfer
• `bytes` – Execution-layer data | -| `TransferErc1155Single(address, bytes32, uint256, uint256, bytes)` | `0xe1c913ed` | • `address` – ERC1155 token contract address
• `bytes32` – Recipient account identifier
• `uint256` – Token ID
• `uint256` – Amount to transfer
• `bytes` – Execution-layer data | -| `TransferErc1155Batch(address, bytes32, uint256[], uint256[], bytes)` | `0x638ac6f9` | • `address` – ERC1155 token contract address
• `bytes32` – Recipient account identifier
• `uint256[]` – Token IDs
• `uint256[]` – Amounts
• `bytes` – Execution-layer data | - -## Installation - - - -

-
-```bash
-cargo add cma-rust-parser
-```
-
-
-
- - -## Usage Example - - - -

-
-```rust
-use cma_rust_parser::{
-    CmaParserInputType, cma_decode_advance, cma_encode_voucher, CmaParserVoucherType, CmaVoucherFieldType, CmaParserEtherVoucherFields, CmaParserInputData, CmaParserErc721VoucherFields
-};
-use cma_rust_parser::helpers::{ToAddress, ToJson};
-
-pub struct Storage {
-    pub erc721_portal_address: String,
-    pub dapp_address_relay: String,
-    pub erc721_token: String,
-    pub app_address: String,
-}
-
-pub async fn handle_advance( _client: &hyper::Client, _server_addr: &str, request: JsonValue, storage: &mut Storage
-) -> Result<&'static str, Box> {
-    let zero_address = "0x0000000000000000000000000000000000000000".to_string();
-    let msg_sender = request["data"]["metadata"]["msg_sender"].as_str().ok_or("Invalid msg_sender address")?;
-    let mut decoded_req = Err(CmaParserError::Unknown);
-
-    match msg_sender {
-        s if s.to_lowercase() == storage.erc721_portal_address.to_lowercase() => {
-            let req_type = CmaParserInputType::CmaParserInputTypeErc721Deposit;
-            // CALL THE PARSER LIBRARY TO DECODE THE ERC721 TOKEN DEPOSIT
-            decoded_req = cma_decode_advance(req_type, request.clone()); 
-        },
-        s if s.to_lowercase() == storage.dapp_address_relay.to_lowercase() => {
-            if storage.app_address == zero_address {
-                let _payload = request["data"]["payload"].as_str().ok_or("Missing payload")?;
-                storage.app_address = _payload.to_string();
-            }
-        },
-        _ => { // IF THE MSG_SENDER IS NOT ANY OF THE PORTALS OR THE ADDRESS RELAYER, IT'S SAFE TO ASSUME IT'S A REGULAR USER INTERACTION
-            let req_type: CmaParserInputType = CmaParserInputType::CmaParserInputTypeAuto;
-            // CALL THE PARSER LIBRARY TO DECODE A USER INTERACTION (PARSER LIB MATCHES FUNCTION SELECTOR)
-            decoded_req = cma_decode_advance(req_type, request.clone()); 
-        }
-    }
-
-    match decoded_req { // MATCH ON THE RESPONSE FROM THE PARSER LIB SO AS TO HANDLE ERRORS OR CONSUME RESPONSE OBJECT
-        Ok(decoded) => {
-            match decoded.req_type { // MATCH ON A SUCCESFUL RESPONSE TO IDENTIFY WHICH OBJECT WAS RETURNED (ERC721)
-                CmaParserInputType::CmaParserInputTypeErc721Deposit => {
-                    if let CmaParserInputData::Erc721Deposit(data) = input {                        
-                        // BUILD A STRUCT WHICH CONTAINS ALL NECESSARY DATA TO BUILD A VOUCHER
-                        let voucher_request = CmaParserErc721VoucherFields{
-                            token: format!("{:?}", data.token).to_address().unwrap(),
-                            token_id: data.token_id.into(),
-                            receiver: format!("{:?}", data.sender).to_address().unwrap(),
-                            application_address: storage.app_address.to_address().unwrap()
-                        };
-                        // CALL THE PARSER LIB TO BUILD AN ERC721 VOUCHER
-                        if let Ok(voucher) = cma_encode_voucher(CmaParserVoucherType::CmaParserVoucherTypeErc721, CmaVoucherFieldType::Erc721VoucherFields(voucher_request)) {
-                            emit_voucher(voucher.to_json()).await;
-                        }
-                    }
-                },
-                CmaParserInputType::CmaParserInputTypeUnidentified => {
-                    // HANDLE THE RESULT STRUCT FOR USER DEFINED INPUTS
-                }
-                _ => {
-                    // HANDLE ANY OTHER POSSIBLE RESPONSE STRUCTURE THE APPLICAITON EXPECTS
-                }
-            }
-        },
-        Err(e) => {
-            emit_report(format!("Could'nt decoding advance request: {:?}", e)).await;
-        }
-    }
-    Ok("accept")
-}
-```
-
-
-
- -## Best Practices -- Use type-safe enums and objects in every language to avoid logic bugs -- Always check error results, especially when parsing raw input or encoding vouchers - -## See Also -- [Using the CMA library - Ledger component](./ledger-submoule.md) -- [Original C++ implementation & FFI headers](https://github.com/Mugen-Builders/machine-asset-tools) -- [Rust parser reference (crate)](https://github.com/Mugen-Builders/machine-asset-tools/tree/main/cma-rust-parser) diff --git a/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/snippets/cpp-parser-result-types.md b/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/snippets/cpp-parser-result-types.md deleted file mode 100644 index 3fce687a3..000000000 --- a/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/snippets/cpp-parser-result-types.md +++ /dev/null @@ -1,133 +0,0 @@ -```cpp -cma_parser_error_t cma_decode_advance(cma_parser_input_type_t type, const cmt_rollup_advance_t *input, cma_parser_input_t *parser_input); - -typedef struct cma_parser_input { - enum cma_parser_input_type_t type; - union { - struct cma_parser_ether_deposit_t ether_deposit; - struct cma_parser_erc20_deposit_t erc20_deposit; - struct cma_parser_erc721_deposit_t erc721_deposit; - struct cma_parser_erc1155_single_deposit_t erc1155_single_deposit; - struct cma_parser_erc1155_batch_deposit_t erc1155_batch_deposit; - struct cma_parser_ether_withdrawal_t ether_withdrawal; - struct cma_parser_erc20_withdrawal_t erc20_withdrawal; - struct cma_parser_erc721_withdrawal_t erc721_withdrawal; - struct cma_parser_erc1155_single_withdrawal_t erc1155_single_withdrawal; - struct cma_parser_erc1155_batch_withdrawal_t erc1155_batch_withdrawal; - struct cma_parser_ether_transfer_t ether_transfer; - struct cma_parser_erc20_transfer_t erc20_transfer; - struct cma_parser_erc721_transfer_t erc721_transfer; - struct cma_parser_erc1155_single_transfer_t erc1155_single_transfer; - struct cma_parser_erc1155_batch_transfer_t erc1155_batch_transfer; - struct cma_parser_balance_t balance; - struct cma_parser_supply_t supply; - } u; -} cma_parser_input_t; - -struct cma_parser_ether_deposit_t { - cma_abi_address_t sender; - cma_amount_t amount; - cma_abi_bytes_t exec_layer_data; -}; -struct cma_parser_erc20_deposit_t { - cma_abi_address_t sender; - cma_token_address_t token; - cma_amount_t amount; - cma_abi_bytes_t exec_layer_data; -}; -struct cma_parser_erc721_deposit_t { - cma_abi_address_t sender; - cma_token_address_t token; - cma_token_id_t token_id; - cma_abi_bytes_t exec_layer_data; -}; -struct cma_parser_erc1155_single_deposit_t { - cma_abi_address_t sender; - cma_token_address_t token; - cma_token_id_t token_id; - cma_amount_t amount; - cma_abi_bytes_t exec_layer_data; -}; -struct cma_parser_erc1155_batch_deposit_t { - cma_abi_address_t sender; - cma_token_address_t token; - size_t count; - cma_token_id_t *token_ids; - cma_amount_t *amounts; - cma_abi_bytes_t base_layer_data; - cma_abi_bytes_t exec_layer_data; -}; - -struct cma_parser_ether_withdrawal_t { - cma_amount_t amount; - cma_abi_bytes_t exec_layer_data; -}; -struct cma_parser_erc20_withdrawal_t { - cma_token_address_t token; - cma_amount_t amount; - cma_abi_bytes_t exec_layer_data; -}; -struct cma_parser_erc721_withdrawal_t { - cma_token_address_t token; - cma_token_id_t token_id; - cma_abi_bytes_t exec_layer_data; -}; -struct cma_parser_erc1155_single_withdrawal_t { - cma_token_address_t token; - cma_token_id_t token_id; - cma_amount_t amount; - cma_abi_bytes_t exec_layer_data; -}; -struct cma_parser_erc1155_batch_withdrawal_t { - cma_token_address_t token; - size_t count; - cma_token_id_t *token_ids; - cma_amount_t *amounts; - cma_abi_bytes_t exec_layer_data; -}; - -struct cma_parser_ether_transfer_t { - cma_account_id_t receiver; - cma_amount_t amount; - cma_abi_bytes_t exec_layer_data; -}; -struct cma_parser_erc20_transfer_t { - cma_account_id_t receiver; - cma_token_address_t token; - cma_amount_t amount; - cma_abi_bytes_t exec_layer_data; -}; -struct cma_parser_erc721_transfer_t { - cma_account_id_t receiver; - cma_token_address_t token; - cma_token_id_t token_id; - cma_abi_bytes_t exec_layer_data; -}; -struct cma_parser_erc1155_single_transfer_t { - cma_account_id_t receiver; - cma_token_address_t token; - cma_token_id_t token_id; - cma_amount_t amount; - cma_abi_bytes_t exec_layer_data; -}; -struct cma_parser_erc1155_batch_transfer_t { - cma_account_id_t receiver; - cma_token_address_t token; - size_t count; - cma_token_id_t *token_ids; - cma_amount_t *amounts; - cma_abi_bytes_t exec_layer_data; -}; - -struct cma_parser_balance_t { - cma_account_id_t account; - cma_token_address_t token; - cma_token_id_t token_id; - cma_abi_bytes_t exec_layer_data; -}; -struct cma_parser_supply_t { - cma_token_address_t token; - cma_token_id_t token_id; - cma_abi_bytes_t exec_layer_data; -}; -``` \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/snippets/rust-parser-result-types.md b/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/snippets/rust-parser-result-types.md deleted file mode 100644 index 4e0cf9306..000000000 --- a/cartesi-rollups_versioned_docs/version-1.5/rollups-apis/asset-management-library/snippets/rust-parser-result-types.md +++ /dev/null @@ -1,181 +0,0 @@ -```rust -fn cma_decode_advance(req_type: CmaParserInputType, input: JsonValue) -> Result; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CmaParserInput { - pub req_type: CmaParserInputType, - pub input: CmaParserInputData, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum CmaParserInputData { - EtherDeposit(CmaParserEtherDeposit), - Erc20Deposit(CmaParserErc20Deposit), - Erc721Deposit(CmaParserErc721Deposit), - Erc1155SingleDeposit(CmaParserErc1155SingleDeposit), - Erc1155BatchDeposit(CmaParserErc1155BatchDeposit), - EtherWithdrawal(CmaParserEtherWithdrawal), - Erc20Withdrawal(CmaParserErc20Withdrawal), - Erc721Withdrawal(CmaParserErc721Withdrawal), - Erc1155SingleWithdrawal(CmaParserErc1155SingleWithdrawal), - Erc1155BatchWithdrawal(CmaParserErc1155BatchWithdrawal), - EtherTransfer(CmaParserEtherTransfer), - Erc20Transfer(CmaParserErc20Transfer), - Erc721Transfer(CmaParserErc721Transfer), - Erc1155SingleTransfer(CmaParserErc1155SingleTransfer), - Erc1155BatchTransfer(CmaParserErc1155BatchTransfer), - Balance(CmaParserBalance), - Supply(CmaParserSupply), - Unidentified(CmaParserUnidentifiedInput), -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CmaParserEtherDeposit { - pub sender: Address, - pub amount: U256, - pub exec_layer_data: Bytes, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CmaParserErc20Deposit { - pub sender: Address, - pub token: Address, - pub amount: U256, - pub exec_layer_data: Bytes, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CmaParserErc721Deposit { - pub sender: Address, - pub token: Address, - pub token_id: U256, - pub exec_layer_data: Bytes, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CmaParserErc1155SingleDeposit { - pub sender: Address, - pub token: Address, - pub token_id: U256, - pub amount: U256, - pub exec_layer_data: Bytes, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CmaParserErc1155BatchDeposit { - pub sender: Address, - pub token: Address, - pub count: usize, - pub token_ids: Vec, - pub amounts: Vec, - pub base_layer_data: Bytes, - pub exec_layer_data: Bytes, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CmaParserEtherWithdrawal { - pub receiver: Address, - pub amount: U256, - pub exec_layer_data: String, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CmaParserErc20Withdrawal { - pub receiver: Address, - pub token: Address, - pub amount: U256, - pub exec_layer_data: String, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CmaParserErc721Withdrawal { - pub receiver: Address, - pub token: Address, - pub token_id: U256, - pub exec_layer_data: String, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CmaParserErc1155SingleWithdrawal { - pub receiver: Address, - pub token: Address, - pub token_id: U256, - pub amount: U256, - pub exec_layer_data: String, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CmaParserErc1155BatchWithdrawal { - pub receiver: Address, - pub token: Address, - pub count: usize, - pub token_ids: Vec, - pub amounts: Vec, - pub base_layer_data: String, - pub exec_layer_data: String, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CmaParserEtherTransfer { - pub receiver: U256, - pub amount: U256, - pub exec_layer_data: String, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CmaParserErc20Transfer { - pub receiver: U256, - pub token: Address, - pub amount: U256, - pub exec_layer_data: String, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CmaParserErc721Transfer { - pub receiver: U256, - pub token: Address, - pub token_id: U256, - pub exec_layer_data: String, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CmaParserErc1155SingleTransfer { - pub sender: Address, - pub receiver: Address, - pub token: Address, - pub token_id: U256, - pub amount: U256, - pub exec_layer_data: String, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CmaParserErc1155BatchTransfer { - pub sender: Address, - pub receiver: Address, - pub token: Address, - pub count: usize, - pub token_ids: Vec, - pub amounts: Vec, - pub base_layer_data: String, - pub exec_layer_data: String, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CmaParserBalance { - pub account: Address, - pub token: Address, - pub token_ids: Option>, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CmaParserSupply { - pub token: Address, - pub token_ids: Vec, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CmaParserUnidentifiedInput { - pub abi_encoded_bytes: Vec, - pub msg_sender: Address, -} -``` \ No newline at end of file diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/getting-started.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/getting-started.md new file mode 100644 index 000000000..a14dd2939 --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/getting-started.md @@ -0,0 +1,199 @@ +--- +id: getting-started +title: Getting started +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +This page shows you how to add the CMA library to a Cartesi application and build a small wallet that accepts Ether deposits and answers balance queries. + +## Start from a template + +The fastest way to start is with the official [application templates](https://github.com/Mugen-Builders/libcma-app-templates). The repository contains ready to build projects for Python, Rust and C++. Each one already wires the library, the build files and the portal addresses together. + +```shell +git clone https://github.com/Mugen-Builders/libcma-app-templates +``` + +Copy the folder for your language into your workspace and use it as the base of your application. + +## Add the library to an existing application + +You can also add CMA to an application you already have. + + + + +Install `pycma` from the prebuilt RISC-V wheels: + +```shell +pip3 install pycma --find-links https://prototyp3-dev.github.io/pip-wheels-riscv/wheels/ +``` + +Or build it from the repository: + +```shell +pip3 install pycma@git+https://github.com/Mugen-Builders/libcma-binding-python +``` + +The package is compiled for the RISC-V target, so run the install step inside the Dockerfile that builds your application's machine image. + + + + +Add the binding as a git dependency in your `Cargo.toml`: + +```toml +[dependencies] +libcma_binding_rust = { git = "https://github.com/Mugen-Builders/libcma_binding_rust", branch = "main" } +``` + + + + +Download a prebuilt release from the [releases page](https://github.com/Mugen-Builders/machine-asset-tools/releases). Releases contain runtime and development artifacts for musl and glibc systems. In a Dockerfile: + +```dockerfile +ARG MACHINE_ASSET_TOOLS_VERSION +ADD https://github.com/Mugen-Builders/machine-asset-tools/releases/download/v${MACHINE_ASSET_TOOLS_VERSION}/machine-asset-tools_glibc_riscv64_v${MACHINE_ASSET_TOOLS_VERSION}.tar.gz /tmp/ +RUN tar -xzf /tmp/machine-asset-tools_glibc_riscv64_v${MACHINE_ASSET_TOOLS_VERSION}.tar.gz -C / \ + && rm /tmp/machine-asset-tools_glibc_riscv64_v${MACHINE_ASSET_TOOLS_VERSION}.tar.gz +``` + +This installs the shared library. Use the `_dev` artifact when you also need the headers and the static library. + + + + +## Your first wallet + +The example below accepts Ether deposits, records them in the ledger and answers balance queries. It is a trimmed version of the [wallet sample application](https://github.com/Mugen-Builders/libcma-binding-python/tree/main/sample_apps/wallet_app) that ships with the Python binding. + + + + +```python +from pycma import RollupCma, Ledger, decode_ether_deposit, decode_inspect + +ETHER_PORTAL_ADDRESS = "0xA632c5c05812c6a6149B7af5C56117d1D2603828"[2:].lower() + +rollup = RollupCma() +ledger = Ledger() + +# Create the asset entry for Ether once, at startup +ether = ledger.retrieve_asset(base_token=True) +ETHER_ID = ether["asset_id"] + +def handle_advance(rollup, ledger): + advance = rollup.read_advance_state() + msg_sender = advance["msg_sender"].hex().lower() + + if msg_sender == ETHER_PORTAL_ADDRESS: + deposit = decode_ether_deposit(advance) + account = ledger.retrieve_account(account=deposit["sender"]) + ledger.deposit(ETHER_ID, account["account_id"], deposit["amount"]) + return True + + return False + +def handle_inspect(rollup, ledger): + inspect = rollup.read_inspect_state() + query = decode_inspect(inspect) + + if query["type"] == "BALANCE": + account = ledger.retrieve_account(account=query["account"]) + balance = ledger.balance(ETHER_ID, account["account_id"]) + rollup.emit_report(balance.to_bytes(32, "big")) + return True + + return False + +handlers = {"advance": handle_advance, "inspect": handle_inspect} + +accept = True +while True: + next_request_type = rollup.finish(accept) + accept = handlers[next_request_type](rollup, ledger) +``` + + + + +The Rust template uses [libcmt-binding-rust](https://github.com/Mugen-Builders/libcmt-binding-rust) to read inputs from the machine and the CMA parser to decode them: + +```rust +use libcma_binding_rust::parser::{cma_decode_advance, CmaParserInputType, CmaParserInputData}; +use libcma_binding_rust::Ledger; +use json::object; + +const ETHER_PORTAL: &str = "0xA632c5c05812c6a6149B7af5C56117d1D2603828"; + +fn handle_ether_deposit( + ledger: &mut Ledger, + msg_sender: &str, + payload_hex: &str, +) -> Result<(), Box> { + // The parser reads the payload from data.payload + // and the sender from data.metadata.msg_sender + let request = object! { + data: { + metadata: { msg_sender: msg_sender }, + payload: payload_hex + } + }; + + let decoded = cma_decode_advance( + CmaParserInputType::CmaParserInputTypeEtherDeposit, + request, + )?; + + if let CmaParserInputData::EtherDeposit(deposit) = decoded.input { + let ether_id = ledger.retrieve_ether_assets()?; + let account_id = ledger.retrieve_account_via_address(deposit.sender)?; + ledger.deposit(ether_id, account_id, deposit.amount)?; + } + Ok(()) +} +``` + +Compare the sender of each advance request against the portal addresses to pick the right input type, as shown in the [Rust template](https://github.com/Mugen-Builders/libcma-app-templates/blob/main/rust/src/main.rs). + + + + +In C++ you read inputs with libcmt and pass them straight to the parser: + +```cpp +extern "C" { +#include +#include +#include +} + +// input is a cmt_rollup_advance_t filled by cmt_rollup_read_advance_state(rollup, &input) +cma_parser_input_t parser_input; + +const int err = cma_parser_decode_advance(CMA_PARSER_INPUT_TYPE_ETHER_DEPOSIT, &input, &parser_input); +if (err < 0) { + printf("unable to decode deposit: %d - %s\n", -err, cma_parser_get_last_error_message()); +} + +// parser_input.ether_deposit.sender -> who deposited +// parser_input.ether_deposit.amount -> how much +``` + +The [C++ template](https://github.com/Mugen-Builders/libcma-app-templates/blob/main/cpp/app.cpp) shows the complete request loop. + + + + +## Build and run it + +Applications using the CMA library build and run like any other Cartesi application. Follow [Building an application](../../development/building-an-application.md) to produce the machine image, then [Running an application](../../development/running-an-application.md) to start a local node. Use [Sending inputs and assets](../../development/send-inputs-and-assets.md) to make a deposit and watch your wallet handle it. + +## Next steps + +- [Parsing inputs](./parsing-inputs.md) covers every input the parser can decode. +- [Managing balances](./managing-balances.md) covers the full ledger API. +- [Vouchers and withdrawals](./vouchers.md) shows how users take their assets back to the base layer. diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/ledger-reference.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/ledger-reference.md new file mode 100644 index 000000000..8fb0b15c9 --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/ledger-reference.md @@ -0,0 +1,152 @@ +--- +id: ledger-reference +title: Ledger reference +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +This page lists every ledger operation with its signature per language. For a guided walk through, see [Managing balances](./managing-balances.md). + +## Lifecycle + +Create, persist, reset and release a ledger. + + + + +| Method | Description | +| :--- | :--- | +| `Ledger()` | Creates an in memory ledger | +| `Ledger(memory_filename, offset, mem_length, n_accounts, n_assets, n_balances, initialize_memory)` | Creates or opens a file backed ledger. Pass `initialize_memory=True` only the first time, to create and size the file | +| `reset()` | Clears all accounts, assets and balances | + + + + +| Method | Returns | Description | +| :--- | :--- | :--- | +| `Ledger::new()` | `Result` | Creates an in memory ledger | +| `Ledger::init_from_file(config)` | `Result` | Opens or creates a file backed ledger from a `LedgerFileConfig` | +| `Ledger::init_from_buffer(config)` | `Result` | Creates a ledger backed by a caller provided buffer from a `LedgerBufferConfig` | +| `reset()` | `Result<(), LedgerError>` | Clears all records | +| `get_last_error_message()` | `Result` | Returns the text of the last error | + + + + +| Function | Description | +| :--- | :--- | +| `cma_ledger_init(ledger)` | Creates an in memory ledger | +| `cma_ledger_init_file(ledger, memory_file_name, mode, offset, mem_length, n_accounts, n_assets, n_balances)` | Opens (`CMA_LEDGER_OPEN_ONLY`) or creates (`CMA_LEDGER_CREATE_ONLY`) a file backed ledger | +| `cma_ledger_init_buffer(ledger, buffer, mem_length, n_accounts, n_assets, n_balances)` | Creates a ledger backed by a caller provided buffer | +| `cma_ledger_reset(ledger)` | Clears all records | +| `cma_ledger_fini(ledger)` | Releases the ledger | +| `cma_ledger_get_last_error_message()` | Returns the text of the last error | + + + + +## Retrieving accounts and assets + +These calls find an entry and return its internal ID. With the find or create operation they also create missing entries, which is the common case when handling deposits. + + + + +| Method | Description | +| :--- | :--- | +| `retrieve_account(account_id=None, account=None)` | Finds or creates an account. `account` is a hex string: a 20 byte wallet address or a 32 byte account ID. Returns a dict with `account_id` and `account` | +| `retrieve_asset(asset_id=None, token=None, token_id=None, token_id_with_amount=None, base_token=None, force_find=None)` | Finds or creates an asset. Use `base_token=True` for Ether, `token` for ERC20, `token` plus `token_id` for ERC721, and add `token_id_with_amount=True` for ERC1155. `force_find=True` fails instead of creating. Returns a dict with `asset_id`, `token`, `token_id` and `total_supply` | + + + + +| Method | Returns | Description | +| :--- | :--- | :--- | +| `retrieve_account_via_address(address)` | `Result` | Finds or creates an account from a wallet address | +| `retrieve_account(account_id, account_type, operation, addr_or_id)` | `Result` | Generic account retrieval with explicit `AccountType` and `RetrieveOperation` | +| `retrieve_ether_assets()` | `Result` | Finds or creates the Ether asset | +| `retrieve_erc20_asset_via_address(token_address)` | `Result` | Finds or creates an ERC20 asset from its contract address | +| `retrieve_erc721_assets_via_address(token_address, token_id)` | `Result` | Finds or creates an ERC721 asset from its contract address and token ID | +| `retrieve_asset(asset_id, token_address, token_id, asset_type, operation)` | `Result` | Generic asset retrieval with explicit `AssetType` and `RetrieveOperation` | + + + + +| Function | Description | +| :--- | :--- | +| `cma_ledger_retrieve_account(ledger, account_id, account, addr_accid, n_balances, account_type, operation)` | Finds or creates an account. When `account_id` is set the call fills the account details, otherwise it fills the ID | +| `cma_ledger_retrieve_asset(ledger, asset_id, token_address, token_id, out_total_supply, asset_type, operation)` | Finds or creates an asset. Also returns the asset's total supply through `out_total_supply` | + + + + +## State changes + +Each operation either completes fully or fails with an error, for example when funds are insufficient. + + + + +| Method | Description | +| :--- | :--- | +| `deposit(asset_id, account_id, amount)` | Adds `amount` of the asset to the account and raises the total supply | +| `withdraw(asset_id, account_id, amount)` | Removes `amount` from the account and lowers the total supply | +| `transfer(asset_id, from_account_id, to_account_id, amount)` | Moves `amount` between two accounts | + + + + +| Method | Returns | Description | +| :--- | :--- | :--- | +| `deposit(asset_id, to_account_id, amount)` | `Result<(), LedgerError>` | Adds `amount` (a `U256`) to the account | +| `withdraw(asset_id, from_account_id, amount)` | `Result<(), LedgerError>` | Removes `amount` from the account | +| `transfer(asset_id, from_account_id, to_account_id, amount)` | `Result<(), LedgerError>` | Moves `amount` between two accounts | + + + + +| Function | Description | +| :--- | :--- | +| `cma_ledger_deposit(ledger, asset_id, to_account_id, amount)` | Adds the amount to the account | +| `cma_ledger_withdraw(ledger, asset_id, from_account_id, amount)` | Removes the amount from the account | +| `cma_ledger_transfer(ledger, asset_id, from_account_id, to_account_id, amount)` | Moves the amount between two accounts | + + + + +## Queries + +Read only calls. They never change the ledger, so they are safe to use in inspect handlers. + + + + +| Method | Description | +| :--- | :--- | +| `balance(asset_id, account_id)` | Returns the account's balance of the asset as an int | +| `supply(asset_id)` | Returns the total supply of the asset as an int | + + + + +| Method | Returns | Description | +| :--- | :--- | :--- | +| `get_balance(asset_id, account_id)` | `Result` | Returns the account's balance of the asset | +| `get_total_supply(asset_id)` | `Result` | Returns the total supply of the asset | + + + + +| Function | Description | +| :--- | :--- | +| `cma_ledger_get_balance(ledger, asset_id, account_id, out_balance, account_balance_info)` | Fills `out_balance` with the account's balance of the asset | +| `cma_ledger_retrieve_asset(...)` | Returns the asset's total supply through its `out_total_supply` parameter | + + + + +## Errors + +Every operation reports failures with a typed error: an exception in Python, a `LedgerError` in Rust, and a negative code in C and C++. The full list, including insufficient funds, missing accounts and storage limits, is in [Types and selectors](./types-and-selectors.md#error-codes). diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/managing-balances.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/managing-balances.md new file mode 100644 index 000000000..fb3e8eff2 --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/managing-balances.md @@ -0,0 +1,251 @@ +--- +id: managing-balances +title: Managing balances +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +The CMA ledger records which account owns which asset, and how much of it. Think of it as a plug in wallet for your application: instead of writing your own balance bookkeeping, you call deposit, withdraw and transfer, and the ledger keeps the state consistent. + +## Core ideas + +The ledger works with three things: + +- **Accounts**. An account usually maps to a wallet address, but it can also map to a generic 32 byte ID. Internally each account gets a small numeric `account_id`. +- **Assets**. An asset is anything you track: Ether, an ERC20 token (identified by its address), an ERC721 or ERC1155 token (identified by address plus token ID). Internally each asset gets a numeric `asset_id`. +- **Balances**. The amount of an asset that an account holds. The ledger also tracks the total supply of each asset inside your application. + +Most ledger calls follow a retrieve pattern: you describe the account or asset, and the ledger finds it or creates it and hands back its ID. After that, you work with IDs only. + +## Creating a ledger + + + + +Create an in memory ledger with no arguments, or a file backed ledger that survives restarts by passing a memory file and size limits: + +```python +from pycma import Ledger + +# In memory +ledger = Ledger() + +# File backed, sized for your application +ledger = Ledger( + memory_filename="/dev/pmem2", + offset=0, + mem_length=64 * 1024 * 1024, + n_accounts=16 * 1024, + n_assets=8, + n_balances=8 * 16 * 1024, + initialize_memory=True, # True only on first creation +) +``` + + + + +```rust +use libcma_binding_rust::Ledger; + +// In memory +let mut ledger = Ledger::new()?; +``` + +The binding also exposes `Ledger::init_from_file` and `Ledger::init_from_buffer` for persistent ledgers, configured through `LedgerFileConfig` and `LedgerBufferConfig`. + + + + +```cpp +cma_ledger_t ledger; +if (cma_ledger_init(&ledger) < 0) { + printf(cma_ledger_get_last_error_message()); +} + +// ... use the ledger ... + +cma_ledger_fini(&ledger); +``` + +`cma_ledger_init_file` and `cma_ledger_init_buffer` create persistent ledgers backed by a memory file or a buffer you provide. + + + + +## Accounts and assets + + + + +`retrieve_account` and `retrieve_asset` find an entry or create it when it does not exist yet. Both return a dictionary that includes the internal ID: + +```python +# Account from a wallet address +account = ledger.retrieve_account(account="0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266") +account_id = account["account_id"] + +# The Ether asset (the base token has no contract address) +ether = ledger.retrieve_asset(base_token=True) + +# An ERC20 asset, identified by its contract address +erc20 = ledger.retrieve_asset(token="0x491604c0fdf08347dd1fa4ee062a822a5dd06b5d") + +# An ERC721 asset, identified by address plus token ID +nft = ledger.retrieve_asset(token="0x...", token_id=1) + +# An ERC1155 asset, where each token ID has its own amount +sft = ledger.retrieve_asset(token="0x...", token_id=1, token_id_with_amount=True) + +# Find only, do not create +existing = ledger.retrieve_asset(base_token=True, force_find=True) +``` + + + + +The binding has one generic method and shortcuts for the common cases: + +```rust +use libcma_binding_rust::types::{AssetType, AccountType, RetrieveOperation, AddressCBindingsExt}; +use libcma_binding_rust::types::{Address, U256}; + +// Shortcuts +let ether_id = ledger.retrieve_ether_assets()?; +let erc20_id = ledger.retrieve_erc20_asset_via_address(token_address)?; +let erc721_id = ledger.retrieve_erc721_assets_via_address(token_address, token_id)?; +let account_id = ledger.retrieve_account_via_address(wallet_address)?; + +// Generic forms +let asset_id = ledger.retrieve_asset( + None, // asset_id, when you already know it + Some(token_address), + Some(token_id), + AssetType::TokenAddressId, + RetrieveOperation::FindOrCreate, +)?; + +let account_id = ledger.retrieve_account( + None, + AccountType::WalletAddress, + RetrieveOperation::FindOrCreate, + Some(wallet_address.as_bytes()), +)?; +``` + + + + +```cpp +// Find or create an account from a wallet address +cma_ledger_account_t account = {.address = {.data = { + 0xf3, 0x9f, 0xd6, 0xe5, 0x1a, 0xad, 0x88, 0xf6, 0xf4, 0xce, + 0x6a, 0xb8, 0x82, 0x72, 0x79, 0xcf, 0xff, 0xb9, 0x22, 0x66, +}}}; +cma_ledger_account_id_t account_id; +cma_ledger_account_type_t account_type = CMA_LEDGER_ACCOUNT_TYPE_WALLET_ADDRESS; +cma_ledger_retrieve_account(&ledger, &account_id, &account, NULL, NULL, &account_type, CMA_LEDGER_OP_FIND_OR_CREATE); + +// Find or create an asset from a token address +cma_token_address_t token_address = { /* 20 bytes */ }; +cma_ledger_asset_id_t asset_id; +cma_amount_t total_supply; +cma_ledger_asset_type_t asset_type = CMA_LEDGER_ASSET_TYPE_TOKEN_ADDRESS; +cma_ledger_retrieve_asset(&ledger, &asset_id, &token_address, NULL, &total_supply, &asset_type, CMA_LEDGER_OP_FIND_OR_CREATE); +``` + + + + +## Moving assets + +Three operations change balances. Each one fails with a clear error instead of leaving partial state, for example when an account tries to spend more than it holds. + + + + +```python +# Credit an account, for example after a decoded deposit +ledger.deposit(asset_id, account_id, amount) + +# Move assets between two accounts inside the application +ledger.transfer(asset_id, from_account_id, to_account_id, amount) + +# Debit an account, for example before emitting a withdrawal voucher +ledger.withdraw(asset_id, account_id, amount) +``` + + + + +```rust +ledger.deposit(asset_id, to_account_id, amount)?; +ledger.transfer(asset_id, from_account_id, to_account_id, amount)?; +ledger.withdraw(asset_id, from_account_id, amount)?; +``` + + + + +```cpp +cma_ledger_deposit(&ledger, asset_id, to_account_id, &amount); +cma_ledger_transfer(&ledger, asset_id, from_account_id, to_account_id, &amount); +cma_ledger_withdraw(&ledger, asset_id, from_account_id, &amount); +``` + + + + +## Reading balances + +Queries never change state, so you can call them from inspect handlers. + + + + +```python +balance = ledger.balance(asset_id, account_id) # int +supply = ledger.supply(asset_id) # int, total supply of the asset +``` + + + + +```rust +let balance = ledger.get_balance(asset_id, account_id)?; // U256 +let supply = ledger.get_total_supply(asset_id)?; // U256 +``` + + + + +```cpp +cma_amount_t balance; +cma_ledger_get_balance(&ledger, asset_id, account_id, &balance, NULL); + +// The total supply is returned by cma_ledger_retrieve_asset +// through its out_total_supply parameter. +``` + + + + +## A complete deposit flow + +This is how the parser and the ledger work together when an ERC20 deposit arrives. The snippet comes from the [Python wallet sample](https://github.com/Mugen-Builders/libcma-binding-python/blob/main/sample_apps/wallet_app/app.py): + +```python +if msg_sender == ERC20_PORTAL_ADDRESS: + deposit = decode_erc20_deposit(advance) + + asset = ledger.retrieve_asset(token=deposit["token"]) + account = ledger.retrieve_account(account=deposit["sender"]) + + ledger.deposit(asset["asset_id"], account["account_id"], deposit["amount"]) + return True +``` + +## Error handling + +Ledger operations report failures such as insufficient funds, unknown accounts or full storage. The full code list is in [Types and selectors](./types-and-selectors.md#error-codes). In Python a failed call raises an exception, in Rust it returns a `LedgerError`, and in C and C++ it returns a negative code with details available from `cma_ledger_get_last_error_message()`. diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/overview.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/overview.md new file mode 100644 index 000000000..c9ce5e0f6 --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/overview.md @@ -0,0 +1,62 @@ +--- +id: overview +title: Overview +resources: + - url: https://github.com/Mugen-Builders/machine-asset-tools + title: CMA core library (C and C++) + - url: https://github.com/Mugen-Builders/libcma-binding-python + title: Python binding (pycma) + - url: https://github.com/Mugen-Builders/libcma_binding_rust + title: Rust binding + - url: https://github.com/Mugen-Builders/libcma-app-templates + title: Application templates +--- + +The Cartesi Machine Assets library (CMA), assists Cartesi applications manage assets such as Ether, ERC20, ERC721 and ERC1155 tokens. It handles the two most repetitive jobs in asset management: + +1. **Decoding inputs**. Deposits arrive from the [portal contracts](../contracts/overview.md) as ABI encoded bytes. Withdrawal and transfer requests from users also arrive as encoded bytes. CMA turns these raw bytes into typed values your code can read directly. + +2. **Recording balances**. Once assets are deposited into your application, the application must record who owns what. CMA library offers a ready made ledger that stores accounts, assets and balances, and updates them on every deposit, transfer and withdrawal. + +CMA is written in C and C++ in the [machine-asset-tools](https://github.com/Mugen-Builders/machine-asset-tools) repository, with bindings for Python and Rust. It does not depend on the Rollup HTTP server hence it communicates with the Cartesi machine through [libcmt](https://github.com/cartesi/machine-guest-tools). + +## The two components + +CMA is split into two parts that work together. You can use both, or only the one you need. + +### Parser + +The parser is the translation layer between your application and the base layer. It serializes and deserializes ABI encoded payloads, mapping thes raw bytes to values your application logic understands and operates on: + +- It decodes Ether, ERC20, ERC721 and ERC1155 deposits sent through the Cartesi portals. +- It decodes withdrawal and transfer requests that users send as ABI encoded inputs. +- It decodes balance and supply queries sent as inspect requests. +- It encodes vouchers so users can move their assets back to the base layer. + +See [Parsing inputs](./parsing-inputs.md) for the guide and the [Parser reference](./parser-reference.md) for every function. + +### Ledger + +The ledger is a small in machine database for managing assets. It stores: + +- **Accounts**, identified by a wallet address or a generic ID. +- **Assets**, identified by a token address, a token address plus token ID, or a plain ID. +- **Balances**, the amount of each asset held by each account, plus the total supply of each asset. + +It exposes deposit, withdraw and transfer operations, and read only balance and supply queries. Every operation either succeeds or returns a clear error code, so your application state stays consistent. See [Managing balances](./managing-balances.md) for an advance guide. + +## Language support + +| Language | Package | Where it comes from | +| :-------- | :-------------------- | :----------------------------------------------------------------------------------------------------------------------------------- | +| Python | `pycma` | [libcma-binding-python](https://github.com/Mugen-Builders/libcma-binding-python), prebuilt RISC-V wheels available | +| Rust | `libcma_binding_rust` | [libcma_binding_rust](https://github.com/Mugen-Builders/libcma_binding_rust), added as a git dependency and also published on cargo. | +| C and C++ | `libcma` | [machine-asset-tools](https://github.com/Mugen-Builders/machine-asset-tools), prebuilt RISC-V binaries on the releases page | + +## Where to go next + +- [Getting started](./getting-started.md): install the library and run your first wallet application. +- [Parsing inputs](./parsing-inputs.md): decode deposits, user requests and inspect queries. +- [Managing balances](./managing-balances.md): record and update asset ownership with the ledger. +- [Vouchers and withdrawals](./vouchers.md): send assets back to the base layer. +- [Types and selectors](./types-and-selectors.md): every enum, struct, function selector and error code. diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/parser-reference.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/parser-reference.md new file mode 100644 index 000000000..306bdd692 --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/parser-reference.md @@ -0,0 +1,183 @@ +--- +id: parser-reference +title: Parser reference +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +This page lists every parser function with its exact signature per language. The enums, structs and error codes they use are defined in [Types and selectors](./types-and-selectors.md). + +## Decode advance + +Decodes an advance input into a typed result. Pass the deposit type when the sender is a portal, or the automatic type to detect withdrawals and transfers from the payload. + +### Signature + + + + +```python +# One function per deposit type. Each takes the dict returned by +# read_advance_state() and returns a dict of decoded fields. +decode_ether_deposit(input: dict) -> dict +decode_erc20_deposit(input: dict) -> dict +decode_erc721_deposit(input: dict) -> dict +decode_erc1155_single_deposit(input: dict) -> dict +decode_erc1155_batch_deposit(input: dict) -> dict + +# Automatic detection for withdrawals and transfers. +# Returns a dict with a "type" key naming the operation. +decode_advance(input: dict) -> dict +``` + + + + +```rust +pub fn cma_decode_advance( + req_type: CmaParserInputType, + input: JsonValue, +) -> Result +``` + + + + +```cpp +int cma_parser_decode_advance( + cma_parser_input_type_t type, + const cmt_rollup_advance_t *input, + cma_parser_input_t *parser_input); +``` + + + + +### Parameters + +- **type / req_type**: the input type to decode. Use a specific deposit type when you matched the sender to a portal. Use the automatic type (`CMA_PARSER_INPUT_TYPE_AUTO` in C and C++, `CmaParserInputTypeAuto` in Rust) for everything else. The Python deposit functions carry the type in their name, and `decode_advance` always runs in automatic mode. +- **input**: the advance request. + - Python: the dictionary returned by `read_advance_state()`, containing `msg_sender` and `payload`. + - Rust: a JSON object. The parser reads the payload from `data.payload` as a hex string and, in automatic mode, the sender from `data.metadata.msg_sender`. + - C and C++: the `cmt_rollup_advance_t` struct filled by `cmt_rollup_read_advance_state`. +- **parser_input** (C and C++ only): output struct the parser fills. + +### Returns + +- Python: a dictionary of decoded fields. `decode_advance` adds a `type` key, for example `ERC20_WITHDRAWAL`. +- Rust: a `CmaParserInput` with `req_type` (the detected type) and `input` (a `CmaParserInputData` variant holding the fields). +- C and C++: `0` on success, a negative error code on failure. The decoded data lands in `parser_input`, with `parser_input.type` naming the variant. + +### Errors + +Fails when the payload is shorter than the expected layout, the hex is invalid, or the input matches no supported operation. See [error codes](./types-and-selectors.md#error-codes). + +## Decode inspect + +Decodes a balance or total supply query from an inspect request. The payload is a JSON document with `method` and `params` fields, described in [Parsing inputs](./parsing-inputs.md#decoding-inspect-queries). + +### Signature + + + + +```python +decode_inspect(input: dict) -> dict +``` + + + + +```rust +pub fn cma_decode_inspect( + input: JsonValue, +) -> Result +``` + + + + +```cpp +int cma_parser_decode_inspect( + cma_parser_input_type_t type, + const cmt_rollup_inspect_t *input, + cma_parser_input_t *parser_input); +``` + + + + +### Parameters + +- **input**: the inspect request. Python takes the dict from `read_inspect_state()`. Rust takes a JSON object and reads the hex payload from `data.payload`. C and C++ take the `cmt_rollup_inspect_t` struct. +- **type** (C and C++ only): pass `CMA_PARSER_INPUT_TYPE_AUTO` to accept both query methods, or a specific balance or supply type to accept only one. + +### Returns + +- Python: a dict with `type` set to `BALANCE` or `SUPPLY`, plus `account`, `token` and `token_id` fields. Fields not present in the query are `None`. +- Rust: a `CmaParserInput` whose `req_type` is the balance or supply type and whose data variant holds the query fields. +- C and C++: `0` on success with the result in `parser_input`, or a negative error code. + +### Errors + +Fails when the payload is not valid JSON, the `method` key is missing or unknown, or a parameter has the wrong format. + +## Encode voucher + +Builds the destination and payload of a voucher that returns an asset to the base layer. + +### Signature + + + + +```python +# Methods of RollupCma. Each encodes the voucher and emits it +# to the machine in one step. +emit_ether_voucher(receiver: str, amount: int) +emit_erc20_voucher(token: str, receiver: str, amount: int) +emit_erc721_voucher(token: str, receiver: str, token_id: int) +emit_erc1155_single_voucher(token: str, receiver: str, token_id: int, amount: int) +emit_erc1155_batch_voucher(token: str, receiver: str, token_ids: list, amounts: list) +``` + + + + +```rust +pub fn cma_encode_voucher( + req_type: CmaParserVoucherType, + voucher_request: CmaVoucherFieldType, +) -> Result +``` + + + + +```cpp +int cma_parser_encode_voucher( + cma_parser_voucher_type_t type, + const cma_abi_address_t *app_address, + const cma_parser_voucher_data_t *voucher_request, + cma_voucher_t *voucher); +``` + + + + +### Parameters + +- **type / req_type**: the voucher type, one entry per asset standard. See [voucher types](./types-and-selectors.md#voucher-fields). +- **voucher_request**: the fields for that voucher type, such as token address, receiver and amount. +- **app_address** (C and C++ only): your application's address on the base layer, available in the advance request metadata. The Python binding captures it for you from `read_advance_state()`. The Rust binding takes it as a field of the ERC721 voucher request. + +### Returns + +- Python: the emit methods send the voucher directly and return the emit result. +- Rust: a `CmaVoucher` with `destination` and `payload` strings, ready to emit through your rollup I/O layer. +- C and C++: `0` on success with the result in `voucher`, holding the destination address and the call data. + +### Errors + +Fails when the request fields do not match the voucher type. In the Rust binding, ERC1155 single and batch voucher types are not implemented yet and return an error. diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/parsing-inputs.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/parsing-inputs.md new file mode 100644 index 000000000..5a76c6d24 --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/parsing-inputs.md @@ -0,0 +1,268 @@ +--- +id: parsing-inputs +title: Parsing inputs +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Every input reaches your application as raw bytes. The CMA parser understands and processes three kinds of inputs and turns each one into typed values: + +1. **Deposits** sent by the Cartesi portal contracts. +2. **Application calls** such as withdrawals and transfers, sent by users as ABI encoded payloads. +3. **Inspect queries** for balances and total supply, sent as JSON. + +## Know who sent the input + +The parser does not guess where an input came from. Your application checks the `msg_sender` of each advance request first: + +- If the sender is a portal contract, the input is a deposit. Pass the matching deposit type to the parser. +- If the sender is anything else, treat it as an application call and let the parser detect the operation from the payload itself. + +:::caution +Portal addresses change between Rollups versions. Always confirm the addresses for the version you deploy against. +::: + +## Decoding deposits + +Each portal encodes its deposits in a fixed ABI layout. The parser knows these layouts, so one call gives you all the deposit fields. + + + + +`pycma` has one decode function per deposit type. Each takes the advance request returned by `read_advance_state()` and returns a plain dictionary: + +```python +from pycma import ( + decode_ether_deposit, + decode_erc20_deposit, + decode_erc721_deposit, + decode_erc1155_single_deposit, + decode_erc1155_batch_deposit, +) + +advance = rollup.read_advance_state() +msg_sender = advance["msg_sender"].hex().lower() + +if msg_sender == ERC20_PORTAL_ADDRESS: + deposit = decode_erc20_deposit(advance) + # deposit["sender"] -> depositor address + # deposit["token"] -> token contract address + # deposit["amount"] -> amount as an int +``` + + + + +`cma_decode_advance` takes the input type and a JSON object. The parser reads the payload from `data.payload` and, for application calls, the sender from `data.metadata.msg_sender`: + +```rust +use libcma_binding_rust::parser::{cma_decode_advance, CmaParserInputType, CmaParserInputData}; +use json::object; + +let request = object! { + data: { + metadata: { msg_sender: msg_sender }, + payload: payload_hex + } +}; + +let decoded = cma_decode_advance( + CmaParserInputType::CmaParserInputTypeErc20Deposit, + request, +)?; + +if let CmaParserInputData::Erc20Deposit(deposit) = decoded.input { + // deposit.sender, deposit.token, deposit.amount, deposit.exec_layer_data +} +``` + + + + +`cma_parser_decode_advance` takes the input type, the advance struct from libcmt and an output struct: + +```cpp +// input is a cmt_rollup_advance_t filled by cmt_rollup_read_advance_state(rollup, &input) +cma_parser_input_t parser_input; + +const int err = cma_parser_decode_advance(CMA_PARSER_INPUT_TYPE_ERC20_DEPOSIT, &input, &parser_input); +if (err < 0) { + printf("unable to decode erc20 deposit: %d - %s\n", -err, cma_parser_get_last_error_message()); +} + +parser_input.erc20_deposit.sender; // sender of the deposit +parser_input.erc20_deposit.token; // token address +parser_input.erc20_deposit.amount; // amount +parser_input.erc20_deposit.exec_layer_data; // extra data sent by the depositor +``` + + + + +The full list of deposit result fields per asset is in [Types and selectors](./types-and-selectors.md#parser-results). + +## Decoding application calls + +Withdrawals and transfers are actions application users call directly. For the parser to decode them, the input must arrive as an ABI encoded call that matches one of the function selectors the parser knows. The first four bytes of the payload identify the operation, and the rest carries the arguments. The complete selector table is in [Types and selectors](./types-and-selectors.md#application-call-selectors). + +When the sender is not a portal, the parser is tasked with detecting the operation automatically: + + + + +`decode_advance` detects the operation from the payload and returns a dictionary with a `type` key: + +```python +from pycma import decode_advance + +decoded = decode_advance(advance) + +if decoded["type"] == "ERC20_WITHDRAWAL": + # decoded["token"], decoded["amount"], decoded["exec_layer_data"] + ... +elif decoded["type"] == "ERC20_TRANSFER": + # decoded["receiver"], decoded["token"], decoded["amount"] + ... +``` + +The Python binding decodes all ten operations: Ether, ERC20, ERC721, ERC1155 single and ERC1155 batch, each as withdrawal and as transfer. It raises an exception when the payload does not match any known selector. + + + + +Pass `CmaParserInputTypeAuto` and match on the result type: + +```rust +let decoded = cma_decode_advance(CmaParserInputType::CmaParserInputTypeAuto, request)?; + +match decoded.req_type { + CmaParserInputType::CmaParserInputTypeErc20Withdrawal => { + if let CmaParserInputData::Erc20Withdrawal(w) = decoded.input { + // w.receiver, w.token, w.amount + } + } + CmaParserInputType::CmaParserInputTypeUnidentified => { + // Not a CMA operation. The raw bytes and sender are returned + // so your own logic can handle the input. + } + _ => {} +} +``` + +:::note current coverage in the Rust binding +In automatic mode the Rust binding currently decodes Ether, ERC20, ERC721 and ERC1155 withdrawals and transfers. Inputs that match no selector come back as `Unidentified` with the raw bytes, so your application can process its own custom input formats. +::: + + + + +Pass `CMA_PARSER_INPUT_TYPE_AUTO` and check the resulting type: + +```cpp +cma_parser_input_t parser_input; + +const int err = cma_parser_decode_advance(CMA_PARSER_INPUT_TYPE_AUTO, &input, &parser_input); +if (err < 0) { + printf("unable to decode input: %d - %s\n", -err, cma_parser_get_last_error_message()); +} + +switch (parser_input.type) { +case CMA_PARSER_INPUT_TYPE_ERC20_WITHDRAWAL: + // parser_input.erc20_withdrawal.token, .amount, .exec_layer_data + break; +case CMA_PARSER_INPUT_TYPE_ERC20_TRANSFER: + // parser_input.erc20_transfer.receiver, .token, .amount + break; +default: + break; +} +``` + + + + +## Decoding inspect queries + +Balance and supply queries arrive as inspect requests. The payload is a small JSON document with a `method` name and a `params` array. + + + + +The C core and the Python binding accept the methods `ledger_getBalance` and `ledger_getTotalSupply`: + +```json +{ + "method": "ledger_getBalance", + "params": ["0x0000000000000000000000000000000000000001"] +} +``` + +`params[0]` is the account. You can add a token address as `params[1]` and a token ID as `params[2]` to query a specific asset: + +```python +from pycma import decode_inspect + +inspect = rollup.read_inspect_state() +query = decode_inspect(inspect) + +if query["type"] == "BALANCE": + # query["account"], query["token"], query["token_id"] + ... +elif query["type"] == "SUPPLY": + # query["token"], query["token_id"] + ... +``` + + + + +The Rust binding accepts the methods `ledgerGetBalance` and `ledgerGetTotalSupply`. The inspect payload is the hex encoding of the JSON document: + +```rust +use libcma_binding_rust::parser::{cma_decode_inspect, CmaParserInputType, CmaParserInputData}; + +let decoded = cma_decode_inspect(request)?; + +match decoded.req_type { + CmaParserInputType::CmaParserInputTypeBalance => { + if let CmaParserInputData::Balance(q) = decoded.input { + // q.account, q.token, q.token_ids + } + } + CmaParserInputType::CmaParserInputTypeSupply => { + if let CmaParserInputData::Supply(q) = decoded.input { + // q.token, q.token_ids + } + } + _ => {} +} +``` + + + + +`cma_parser_decode_inspect` works like the advance variant. Use `CMA_PARSER_INPUT_TYPE_AUTO` to accept both methods: + +```cpp +cma_parser_input_t parser_input; + +const int err = cma_parser_decode_inspect(CMA_PARSER_INPUT_TYPE_AUTO, &input, &parser_input); +if (err < 0) { + printf("unable to decode inspect: %d - %s\n", -err, cma_parser_get_last_error_message()); +} +// parser_input.type tells you whether it is a balance or a supply query +``` + + + + +## Handling parser errors + +Every decode call can fail, for example when a payload is shorter than expected. Always check the result before you touch the ledger: + +- Python raises an exception with the error code and message. +- Rust returns a `Result` with a `CmaParserError`. +- C and C++ return a negative error code, and `cma_parser_get_last_error_message()` gives the matching text. + +The complete error lists are in [Types and selectors](./types-and-selectors.md#error-codes). diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/types-and-selectors.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/types-and-selectors.md new file mode 100644 index 000000000..7606ab665 --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/types-and-selectors.md @@ -0,0 +1,227 @@ +--- +id: types-and-selectors +title: Types and selectors +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +This page is the lookup table for the CMA library: input types, decoded fields, function selectors, voucher fields, portal addresses and error codes. The values below come from the library sources: [machine-asset-tools](https://github.com/Mugen-Builders/machine-asset-tools) for C and C++ and Python, and [libcma_binding_rust](https://github.com/Mugen-Builders/libcma_binding_rust) for Rust. + +## Input types + +The parser identifies every input with one of these types. + + + + +```cpp +typedef enum { + CMA_PARSER_INPUT_TYPE_NONE, + CMA_PARSER_INPUT_TYPE_AUTO, + CMA_PARSER_INPUT_TYPE_ETHER_DEPOSIT, + CMA_PARSER_INPUT_TYPE_ERC20_DEPOSIT, + CMA_PARSER_INPUT_TYPE_ERC721_DEPOSIT, + CMA_PARSER_INPUT_TYPE_ERC1155_SINGLE_DEPOSIT, + CMA_PARSER_INPUT_TYPE_ERC1155_BATCH_DEPOSIT, + CMA_PARSER_INPUT_TYPE_ETHER_WITHDRAWAL, + CMA_PARSER_INPUT_TYPE_ERC20_WITHDRAWAL, + CMA_PARSER_INPUT_TYPE_ERC721_WITHDRAWAL, + CMA_PARSER_INPUT_TYPE_ERC1155_SINGLE_WITHDRAWAL, + CMA_PARSER_INPUT_TYPE_ERC1155_BATCH_WITHDRAWAL, + CMA_PARSER_INPUT_TYPE_ETHER_TRANSFER, + CMA_PARSER_INPUT_TYPE_ERC20_TRANSFER, + CMA_PARSER_INPUT_TYPE_ERC721_TRANSFER, + CMA_PARSER_INPUT_TYPE_ERC1155_SINGLE_TRANSFER, + CMA_PARSER_INPUT_TYPE_ERC1155_BATCH_TRANSFER, + CMA_PARSER_INPUT_TYPE_BALANCE, + CMA_PARSER_INPUT_TYPE_BALANCE_ACCOUNT, + CMA_PARSER_INPUT_TYPE_BALANCE_ACCOUNT_TOKEN_ADDRESS, + CMA_PARSER_INPUT_TYPE_BALANCE_ACCOUNT_TOKEN_ADDRESS_ID, + CMA_PARSER_INPUT_TYPE_SUPPLY, + CMA_PARSER_INPUT_TYPE_SUPPLY_TOKEN_ADDRESS, + CMA_PARSER_INPUT_TYPE_SUPPLY_TOKEN_ADDRESS_ID, +} cma_parser_input_type_t; +``` + +The Python binding uses these same values internally and translates them to the `type` strings returned by `decode_advance` and `decode_inspect`, such as `ERC20_WITHDRAWAL` and `BALANCE`. + + + + +```rust +pub enum CmaParserInputType { + CmaParserInputTypeNone, + CmaParserInputTypeAuto, + CmaParserInputTypeUnidentified, + CmaParserInputTypeEtherDeposit, + CmaParserInputTypeErc20Deposit, + CmaParserInputTypeErc721Deposit, + CmaParserInputTypeErc1155SingleDeposit, + CmaParserInputTypeErc1155BatchDeposit, + CmaParserInputTypeEtherWithdrawal, + CmaParserInputTypeErc20Withdrawal, + CmaParserInputTypeErc721Withdrawal, + CmaParserInputTypeErc1155SingleWithdrawal, + CmaParserInputTypeErc1155BatchWithdrawal, + CmaParserInputTypeEtherTransfer, + CmaParserInputTypeErc20Transfer, + CmaParserInputTypeErc721Transfer, + CmaParserInputTypeErc1155SingleTransfer, + CmaParserInputTypeErc1155BatchTransfer, + CmaParserInputTypeBalance, + CmaParserInputTypeSupply, +} +``` + + + + +## Parser results + +The fields the parser returns for each operation. Names match the Rust struct fields and the Python dictionary keys. + +### Deposits + +| Operation | Fields | +| :--------------------- | :------------------------------------------------------------------------------ | +| Ether deposit | `sender`, `amount`, `exec_layer_data` | +| ERC20 deposit | `sender`, `token`, `amount`, `exec_layer_data` | +| ERC721 deposit | `sender`, `token`, `token_id`, `exec_layer_data` | +| ERC1155 single deposit | `sender`, `token`, `token_id`, `amount`, `exec_layer_data` | +| ERC1155 batch deposit | `sender`, `token`, `token_ids`, `amounts`, `base_layer_data`, `exec_layer_data` | + +### Withdrawals + +The account that withdraws is the `msg_sender` of the input. + +| Operation | Fields | +| :------------------------ | :------------------------------------------------- | +| Ether withdrawal | `amount`, `exec_layer_data` | +| ERC20 withdrawal | `token`, `amount`, `exec_layer_data` | +| ERC721 withdrawal | `token`, `token_id`, `exec_layer_data` | +| ERC1155 single withdrawal | `token`, `token_id`, `amount`, `exec_layer_data` | +| ERC1155 batch withdrawal | `token`, `token_ids`, `amounts`, `exec_layer_data` | + +### Transfers + +Transfers move assets between accounts inside the application. The `receiver` is a 32 byte account identifier. + +| Operation | Fields | +| :---------------------- | :------------------------------------------------------------- | +| Ether transfer | `receiver`, `amount`, `exec_layer_data` | +| ERC20 transfer | `receiver`, `token`, `amount`, `exec_layer_data` | +| ERC721 transfer | `receiver`, `token`, `token_id`, `exec_layer_data` | +| ERC1155 single transfer | `receiver`, `token`, `token_id`, `amount`, `exec_layer_data` | +| ERC1155 batch transfer | `receiver`, `token`, `token_ids`, `amounts`, `exec_layer_data` | + +### Inspect queries + +| Query | Fields | +| :------ | :---------------------------------------------- | +| Balance | `account`, plus optional `token` and `token_id` | +| Supply | optional `token` and `token_id` | + +## Application call selectors + +To request a withdrawal or transfer, the user sends an ABI encoded call as the input payload. The first four bytes select the operation. These are the selectors the C core and the Python binding decode: + +| Function | Selector | Arguments | +| :---------------------------------------------------------------- | :----------- | :--------------------------------------------------- | +| `WithdrawEther(uint256,bytes)` | `0x8cf70f0b` | amount, exec layer data | +| `WithdrawErc20(address,uint256,bytes)` | `0x4f94d342` | token, amount, exec layer data | +| `WithdrawErc721(address,uint256,bytes)` | `0x33acf293` | token, token ID, exec layer data | +| `WithdrawErc1155Single(address,uint256,uint256,bytes)` | `0x8bb0a811` | token, token ID, amount, exec layer data | +| `WithdrawErc1155Batch(address,uint256[],uint256[],bytes)` | `0x50c80019` | token, token IDs, amounts, exec layer data | +| `TransferEther(bytes32,uint256,bytes)` | `0xff67c903` | receiver, amount, exec layer data | +| `TransferErc20(address,bytes32,uint256,bytes)` | `0x03d61dcd` | token, receiver, amount, exec layer data | +| `TransferErc721(address,bytes32,uint256,bytes)` | `0xaf615a5a` | token, receiver, token ID, exec layer data | +| `TransferErc1155Single(address,bytes32,uint256,uint256,bytes)` | `0xe1c913ed` | token, receiver, token ID, amount, exec layer data | +| `TransferErc1155Batch(address,bytes32,uint256[],uint256[],bytes)` | `0x638ac6f9` | token, receiver, token IDs, amounts, exec layer data | + +## Inspect methods + +Inspect payloads are JSON documents with a `method` and a `params` array. + + + + +Methods: `ledger_getBalance` and `ledger_getTotalSupply`. + +```json +{ + "method": "ledger_getBalance", + "params": ["0x0000000000000000000000000000000000000001"] +} +``` + +For balance queries, `params` holds the account, then an optional token address and an optional token ID. For supply queries, `params` holds an optional token address and an optional token ID. + + + + +Methods: `ledgerGetBalance` and `ledgerGetTotalSupply`. The payload is the hex encoding of the JSON document, and `params` holds the account, the token address and an optional array of token IDs. + + + + +## Voucher fields + +Each voucher type takes these fields. In C and C++ the receiver lives in the outer `cma_parser_voucher_data_t` struct, and in Rust it is a field of each struct. + +| Voucher type | Fields | Voucher destination | +| :------------- | :----------------------------------------------------------- | :------------------- | +| Ether | `receiver`, `amount` | The receiver address | +| ERC20 | `token`, `receiver`, `amount` | The token contract | +| ERC721 | `token`, `token_id`, `receiver`, and the application address | The token contract | +| ERC1155 single | `token`, `token_id`, `receiver`, `amount` | The token contract | +| ERC1155 batch | `token`, `receiver`, `token_ids`, `amounts` | The token contract | + +## Ledger types + +| Concept | C and C++ | Rust | +| :----------------- | :------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------- | +| Retrieve operation | `CMA_LEDGER_OP_FIND`, `CMA_LEDGER_OP_CREATE`, `CMA_LEDGER_OP_FIND_OR_CREATE`, `CMA_LEDGER_OP_FIND_AND_REMOVE` | `RetrieveOperation::Find`, `Create`, `FindOrCreate` | +| Asset type | `CMA_LEDGER_ASSET_TYPE_ID`, `BASE`, `TOKEN_ADDRESS`, `TOKEN_ADDRESS_ID`, `TOKEN_ADDRESS_ID_AMOUNT` | `AssetType::Id`, `TokenAddress`, `TokenAddressId` | +| Account type | `CMA_LEDGER_ACCOUNT_TYPE_ID`, `WALLET_ADDRESS`, `ACCOUNT_ID` | `AccountType::Id`, `WalletAddress`, `AccountId` | +| Asset ID | `cma_ledger_asset_id_t` | `LedgerAssetId(u64)` | +| Account ID | `cma_ledger_account_id_t` | `LedgerAccountId(u64)` | + +The Python `Ledger` methods select these types for you from the arguments you pass, as described in the [Ledger reference](./ledger-reference.md). + +## Error codes + +### Parser errors + +| C and C++ | Code | Rust | +| :------------------------------------ | :------ | :---------------------------------- | +| `CMA_PARSER_SUCCESS` | `0` | `Ok(...)` | +| `CMA_PARSER_ERROR_UNKNOWN` | `-2001` | `CmaParserError::Unknown` | +| `CMA_PARSER_ERROR_EXCEPTION` | `-2002` | | +| `CMA_PARSER_ERROR_INCOMPATIBLE_INPUT` | `-2003` | `CmaParserError::IncompatibleInput` | +| `CMA_PARSER_ERROR_MALFORMED_INPUT` | `-2004` | `CmaParserError::MalformedInput` | +| `CMA_PARSER_ERROR_INVALID_AMOUNT` | `-2005` | | + +The Rust binding also returns `CmaParserError::Message(String)` with a description for input validation failures. Python raises an exception that carries the code and the message from `cma_parser_get_last_error_message()`. + +### Ledger errors + +| C and C++ | Code | Rust | +| :-------------------------------------- | :------ | :------------------------------- | +| `CMA_LEDGER_SUCCESS` | `0` | `Ok(...)` | +| `CMA_LEDGER_ERROR_UNKNOWN` | `-1001` | `LedgerError::Unknown` | +| `CMA_LEDGER_ERROR_EXCEPTION` | `-1002` | `LedgerError::Exception` | +| `CMA_LEDGER_ERROR_INSUFFICIENT_FUNDS` | `-1003` | `LedgerError::InsufficientFunds` | +| `CMA_LEDGER_ERROR_ACCOUNT_NOT_FOUND` | `-1004` | `LedgerError::AccountNotFound` | +| `CMA_LEDGER_ERROR_ASSET_NOT_FOUND` | `-1005` | `LedgerError::AssetNotFound` | +| `CMA_LEDGER_ERROR_BALANCE_NOT_FOUND` | `-1006` | `LedgerError::Other(code)` | +| `CMA_LEDGER_ERROR_SUPPLY_OVERFLOW` | `-1007` | `LedgerError::SupplyOverflow` | +| `CMA_LEDGER_ERROR_BALANCE_OVERFLOW` | `-1008` | `LedgerError::BalanceOverflow` | +| `CMA_LEDGER_ERROR_INVALID_ACCOUNT` | `-1009` | `LedgerError::InvalidAccount` | +| `CMA_LEDGER_ERROR_INSERTION_ERROR` | `-1010` | `LedgerError::InsertionError` | +| `CMA_LEDGER_ERROR_MAX_ASSETS_REACHED` | `-1011` | `LedgerError::Other(code)` | +| `CMA_LEDGER_ERROR_MAX_ACCOUNTS_REACHED` | `-1012` | `LedgerError::Other(code)` | +| `CMA_LEDGER_ERROR_MAX_BALANCES_REACHED` | `-1013` | `LedgerError::Other(code)` | +| `CMA_LEDGER_ERROR_ASSET_SUPPLY` | `-1014` | `LedgerError::Other(code)` | +| `CMA_LEDGER_ERROR_ACCOUNT_BALANCE` | `-1015` | `LedgerError::Other(code)` | +| `CMA_LEDGER_ERROR_REMOVE` | `-1016` | `LedgerError::Other(code)` | diff --git a/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/vouchers.md b/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/vouchers.md new file mode 100644 index 000000000..e0d3d1ab1 --- /dev/null +++ b/cartesi-rollups_versioned_docs/version-2.0/api-reference/asset-management/vouchers.md @@ -0,0 +1,132 @@ +--- +id: vouchers +title: Vouchers and withdrawals +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Users deposit assets into your application, but only your application can send them back. It does this by emitting a [voucher](../backend/vouchers.md), a message that executes a call on the base layer once the epoch settles. CMA builds these vouchers for you with the correct destination and payload for each asset type. + +## The withdrawal flow + +A complete withdrawal has three steps, and you have already seen the first two in the previous guides: + +1. **Decode the request**. The user sends an ABI encoded withdrawal input. The parser identifies it and returns the typed fields. See [Parsing inputs](./parsing-inputs.md). +2. **Debit the ledger**. Call `withdraw` so the user's balance drops inside your application. If the user lacks funds, this call fails and you stop here. See [Managing balances](./managing-balances.md). +3. **Emit the voucher**. Build and emit the voucher that releases the asset on the base layer. + +Running the ledger debit before emitting the voucher matters. It guarantees a user can never withdraw more than they own. + +## Emitting vouchers + + + + +`RollupCma` has one emit method per asset type. Each builds the voucher and sends it to the machine in a single call. The application address needed for the voucher is captured automatically when you call `read_advance_state()`: + +```python +# Ether +rollup.emit_ether_voucher(receiver, amount) + +# ERC20 +rollup.emit_erc20_voucher(token, receiver, amount) + +# ERC721 +rollup.emit_erc721_voucher(token, receiver, token_id) + +# ERC1155 +rollup.emit_erc1155_single_voucher(token, receiver, token_id, amount) +rollup.emit_erc1155_batch_voucher(token, receiver, token_ids, amounts) +``` + +A full withdrawal handler, from the [wallet sample](https://github.com/Mugen-Builders/libcma-binding-python/blob/main/sample_apps/wallet_app/app.py): + +```python +decoded = decode_advance(advance) + +if decoded["type"] == "ERC20_WITHDRAWAL": + asset = ledger.retrieve_asset(token=decoded["token"]) + account = ledger.retrieve_account(account=msg_sender) + + ledger.withdraw(asset["asset_id"], account["account_id"], decoded["amount"]) + + rollup.emit_erc20_voucher(asset["token"], msg_sender, decoded["amount"]) +``` + + + + +`cma_encode_voucher` builds the voucher fields. You then emit the result with your rollup I/O layer, for example [libcmt-binding-rust](https://github.com/Mugen-Builders/libcmt-binding-rust): + +```rust +use libcma_binding_rust::parser::{ + cma_encode_voucher, CmaParserVoucherType, CmaVoucherFieldType, CmaParserErc20VoucherFields, +}; + +let voucher = cma_encode_voucher( + CmaParserVoucherType::CmaParserVoucherTypeErc20, + CmaVoucherFieldType::Erc20VoucherFields(CmaParserErc20VoucherFields { + token, + receiver, + amount, + }), +)?; + +// voucher.destination -> address the voucher calls +// voucher.payload -> hex encoded call data +``` + +ERC721 vouchers also need your application address, because the token contract transfers the token out of the application's custody: + +```rust +use libcma_binding_rust::parser::CmaParserErc721VoucherFields; + +let voucher = cma_encode_voucher( + CmaParserVoucherType::CmaParserVoucherTypeErc721, + CmaVoucherFieldType::Erc721VoucherFields(CmaParserErc721VoucherFields { + token, + token_id, + receiver, + application_address, + }), +)?; +``` + + + + +`cma_parser_encode_voucher` fills a voucher struct that you pass to `cmt_rollup_emit_voucher`: + +```cpp +cma_parser_voucher_data_t voucher_request; +// fill voucher_request.receiver and the union member +// that matches the asset type, for example +// voucher_request.u.erc20_voucher_fields + +cma_voucher_t voucher; +const int err = cma_parser_encode_voucher(CMA_PARSER_VOUCHER_TYPE_ERC20, &app_address, &voucher_request, &voucher); +if (err < 0) { + printf("unable to encode voucher: %d - %s\n", -err, cma_parser_get_last_error_message()); +} +``` + +The `app_address` comes from the advance request metadata of any input your application has received. + + + + +## What each voucher does on the base layer + +| Asset | Voucher destination | Effect when executed | +| :------ | :------------------- | :--------------------------------------------------------------------- | +| Ether | The receiver address | Sends the Ether amount to the receiver | +| ERC20 | The token contract | Calls the token's transfer function to pay the receiver | +| ERC721 | The token contract | Transfers the token ID from the application to the receiver | +| ERC1155 | The token contract | Transfers the token ID and amount, or batches of them, to the receiver | + +After the epoch that contains the input settles, anyone can execute the voucher on the base layer through the application contract. See [Asset handling](../../development/asset-handling.md) for the full lifecycle. + +## Voucher field structs + +The exact fields each voucher type needs are listed in [Types and selectors](./types-and-selectors.md#voucher-fields). 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 4bbf9ff24..b7992bcf6 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 @@ -101,6 +101,20 @@ import AssetWithdrawEtherCPP from './snippets/asset_withdraw_ether_cpp.md'; For a full guide, see the Tutorials: [ERC-20 Token Wallet](../tutorials/erc-20-token-wallet.md), [ERC-1155 Token Wallet](../tutorials/erc-1155-token-wallet.md), and [Utilizing test tokens in dev environment](../tutorials/utilizing-the-cli-test-tokens.md). +## Decoding Deposits + +Deposit input payloads arrives applications as an abi-encoded hex, this hex input would need to be decoded to obtain the contained deposit parameters. This decode process could either be handled manually by the developers or could be automated using the assets' manager library. + +The **parser submodule** of the asset manager library simplifies the decode process for deposit payloads by abstracting an already implemented logic to decode the deposit payload for all major assets (ERC721, ERC20, ERC721), it exposes a method called decode input, which the application logic transfers the deposit payload to then receives a struct containing all the parameters contained in the deposit payload. A more detailed guide on the parser library can be found in this [Asset Management Library (CMA)](../api-reference/asset-management/overview.md) + +## Managing and Recording Deposits + +Once a deposit occurs on the base layer, the application receives all necessary details pertaining to the deposit. It's important that the application maintain a record of an address to a token and finally the balance of the address for that token, without a proper record, the application is unable to tell the exact token and also the amount of token that each address owns on the dApp. + +This record can either be implemented manually by the developer or the application could simply import the Asset manager library then leverage the **ledger submodule** to manage and keep this record. + +The ledger submodule can be likened to a plug in wallet for applications, it contains a storage struct which tracks the assets balance for each address as well as the total supply for each token deposited to the wallet, finally it exposes methods to handle deposit, transfer and withdrawal of these assets in the record. For a detailed explanation of the ledger library, check this [Asset Management Library (CMA)](../api-reference/asset-management/overview.md) + ## Withdrawing assets Users can deposit assets to a Cartesi Application, but only the Application can initiate withdrawals. When a withdrawal request is made, it’s processed and interpreted off-chain by the Cartesi Machine running the application’s code. Subsequently, the Cartesi Machine creates a voucher containing the necessary instructions for withdrawal, which is executable when an epoch has settled. @@ -206,7 +220,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:](../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_sidebars/version-1.5-sidebars.json b/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json index ecbd2dc6c..4ecb9d494 100644 --- a/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json +++ b/cartesi-rollups_versioned_sidebars/version-1.5-sidebars.json @@ -126,16 +126,7 @@ ] } ] - }, - { - "type": "category", - "label": "Asset Management Library", - "collapsed": true, - "items": [ - "rollups-apis/asset-management-library/parser-submodule", - "rollups-apis/asset-management-library/ledger-submodule" - ] - } + } ] }, { diff --git a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json index e0da3dff9..a5ccd35a7 100644 --- a/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json +++ b/cartesi-rollups_versioned_sidebars/version-2.0-sidebars.json @@ -84,6 +84,21 @@ "api-reference/backend/finish" ] }, + { + "type": "category", + "label": "Asset Management Library", + "collapsed": true, + "items": [ + "api-reference/asset-management/overview", + "api-reference/asset-management/getting-started", + "api-reference/asset-management/parsing-inputs", + "api-reference/asset-management/managing-balances", + "api-reference/asset-management/vouchers", + "api-reference/asset-management/parser-reference", + "api-reference/asset-management/ledger-reference", + "api-reference/asset-management/types-and-selectors" + ] + }, { "type": "category", "label": "JSON-RPC API", diff --git a/src/css/custom.css b/src/css/custom.css index f6498da07..393ad8e22 100755 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -405,14 +405,6 @@ html[data-theme="dark"] { /* Details */ -details { - background-color: #F6F7F8 !important; - border: 1px solid rgb(246, 247, 248); - padding: 1rem; - border-radius: 8px; - border-color: rgb(246, 247, 248) !important; -} - .details { border: 0 !important; border-bottom: 1px solid rgba(0, 0, 0, 0.1) !important; @@ -420,8 +412,8 @@ details { margin: 0 !important; border-radius: 0 !important; box-shadow: none !important; - background-color: rgba(0, 0, 0, 0.1) !important; - color: rgba(0, 0, 0, 0.1) !important; + background-color: transparent !important; + color: inherit; } [data-theme="dark"] .details { diff --git a/static/llms.txt b/static/llms.txt index 1342fd355..fa325d7ee 100644 --- a/static/llms.txt +++ b/static/llms.txt @@ -145,6 +145,17 @@ If a user's code or question references the old GraphQL API, `sunodo`, or v1.x C - [Exception](https://docs.cartesi.io/cartesi-rollups/2.0/api-reference/backend/exception.md): Register exceptions - [Finish](https://docs.cartesi.io/cartesi-rollups/2.0/api-reference/backend/finish.md): Complete request processing +## Cartesi Rollups v2.0 — Asset Management Library (CMA) + +- [Overview](https://docs.cartesi.io/cartesi-rollups/2.0/api-reference/asset-management/overview.md): What the CMA library is and how its parser and ledger work together +- [Getting started](https://docs.cartesi.io/cartesi-rollups/2.0/api-reference/asset-management/getting-started.md): Install CMA in Python, Rust or C++ and build a first wallet +- [Parsing inputs](https://docs.cartesi.io/cartesi-rollups/2.0/api-reference/asset-management/parsing-inputs.md): Decode deposits, withdrawals, transfers and inspect queries +- [Managing balances](https://docs.cartesi.io/cartesi-rollups/2.0/api-reference/asset-management/managing-balances.md): Track accounts, assets and balances with the ledger +- [Vouchers and withdrawals](https://docs.cartesi.io/cartesi-rollups/2.0/api-reference/asset-management/vouchers.md): Build vouchers that return assets to the base layer +- [Parser reference](https://docs.cartesi.io/cartesi-rollups/2.0/api-reference/asset-management/parser-reference.md): Signatures for decode advance, decode inspect and encode voucher +- [Ledger reference](https://docs.cartesi.io/cartesi-rollups/2.0/api-reference/asset-management/ledger-reference.md): Signatures for ledger lifecycle, retrieval, state changes and queries +- [Types and selectors](https://docs.cartesi.io/cartesi-rollups/2.0/api-reference/asset-management/types-and-selectors.md): Input types, function selectors, portal addresses and error codes + ## Cartesi Rollups v2.0 — Contracts API - [Contracts Overview](https://docs.cartesi.io/cartesi-rollups/2.0/api-reference/contracts/overview.md): All smart contracts in the framework