From fa298610d0e1be121c4561d50c9f6bd403a0a9e5 Mon Sep 17 00:00:00 2001 From: Bruno Menezes Date: Fri, 30 Jan 2026 10:10:54 +0000 Subject: [PATCH 01/10] chore(dave): Wrap BigInt prototype definition on try/catch to avoid errors on compilation. --- apps/dave/.storybook/preview.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/dave/.storybook/preview.tsx b/apps/dave/.storybook/preview.tsx index 83586414..663e954b 100644 --- a/apps/dave/.storybook/preview.tsx +++ b/apps/dave/.storybook/preview.tsx @@ -7,10 +7,15 @@ import { Providers } from '../src/providers/Providers'; import theme from "../src/providers/theme"; import './global.css'; -// @ts-expect-error JSON.stringify will try to call toJSON on bigints. ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#use_within_json -BigInt.prototype.toJSON = function () { - return this.toString(); -}; +try { + // @ts-expect-error JSON.stringify will try to call toJSON on bigints. ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#use_within_json + BigInt.prototype.toJSON = function () { + return this.toString(); + }; + +} catch (error: unknown) { + console.info((error as Error).message) +} const withLayout = (StoryFn: StoryFn, context: StoryContext) => { const { title } = context; From 26f884cbe1390fdd1e4e0d9d109ee0dbea3917eb Mon Sep 17 00:00:00 2001 From: Bruno Menezes Date: Mon, 2 Feb 2026 19:06:23 +0000 Subject: [PATCH 02/10] chore(dave): Upgrade cartesi wagm and view libs and add pretty-ms. --- apps/dave/package.json | 5 +-- pnpm-lock.yaml | 79 ++++++++++++++++++++++++++++++------------ 2 files changed, 59 insertions(+), 25 deletions(-) diff --git a/apps/dave/package.json b/apps/dave/package.json index 1f7ae38f..5571904f 100644 --- a/apps/dave/package.json +++ b/apps/dave/package.json @@ -17,8 +17,8 @@ "format": "prettier --write \"**/*.{ts,tsx,md}\"" }, "dependencies": { - "@cartesi/viem": "2.0.0-alpha.24", - "@cartesi/wagmi": "2.0.0-alpha.27", + "@cartesi/viem": "2.0.0-alpha.26", + "@cartesi/wagmi": "2.0.0-alpha.30", "@mantine/core": "^8.3.13", "@mantine/form": "^8.3.13", "@mantine/hooks": "^8.3.13", @@ -31,6 +31,7 @@ "date-fns": "^4.1.0", "humanize-duration": "^3.33.2", "next": "^16.1.5", + "pretty-ms": "^8", "ramda": "^0.32.0", "ramda-adjunct": "^5.1.0", "react": "catalog:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 257cc546..f14bfb41 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -66,11 +66,11 @@ importers: apps/dave: dependencies: '@cartesi/viem': - specifier: 2.0.0-alpha.24 - version: 2.0.0-alpha.24(typescript@5.9.3)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(zod@4.1.12) + specifier: 2.0.0-alpha.26 + version: 2.0.0-alpha.26(typescript@5.9.3)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(zod@4.1.12) '@cartesi/wagmi': - specifier: 2.0.0-alpha.27 - version: 2.0.0-alpha.27(@tanstack/react-query@5.90.12(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(wagmi@2.18.2(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.2.7)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(zod@4.1.12))(zod@4.1.12) + specifier: 2.0.0-alpha.30 + version: 2.0.0-alpha.30(@tanstack/react-query@5.90.12(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(wagmi@2.18.2(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.2.7)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(zod@4.1.12))(zod@4.1.12) '@mantine/core': specifier: ^8.3.13 version: 8.3.13(@mantine/hooks@8.3.13(react@19.2.3))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -107,6 +107,9 @@ importers: next: specifier: ^16.1.5 version: 16.1.5(@babel/core@7.28.4)(@playwright/test@1.57.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + pretty-ms: + specifier: ^8 + version: 8.0.0 ramda: specifier: ^0.32.0 version: 0.32.0 @@ -597,7 +600,7 @@ importers: version: link:../tsconfig '@sunodo/wagmi-plugin-hardhat-deploy': specifier: ^0.4.0 - version: 0.4.0(@wagmi/core@2.22.1(@tanstack/query-core@5.90.12)(@types/react@19.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(abitype@1.2.2(typescript@5.9.3)(zod@3.25.76))(typescript@5.9.3)(wagmi@2.18.2(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.1.13)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)) + version: 0.4.0(@wagmi/core@2.22.1(@tanstack/query-core@5.90.12)(@types/react@19.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(abitype@1.2.3(typescript@5.9.3)(zod@3.25.76))(typescript@5.9.3)(wagmi@2.18.2(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.1.13)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)) '@types/node': specifier: ^20 version: 20.19.4 @@ -1660,24 +1663,24 @@ packages: '@cartesi/rollups@2.1.1': resolution: {integrity: sha512-1k+8gEp6VL6pGr0hlUTqK1hEIvBmRKUBodU3259e7rpR7ECaTLUV08C+P4efnXrz5Zj2SYWF8QZuFUVMxsMBVA==} - '@cartesi/rpc@2.0.0-alpha.17': - resolution: {integrity: sha512-hD+ROXq0YMBZwNp6FlR5gYpzU6Am7Ug3lhIyD7OrP6/SVzmJC5u6Q7PY5kSPf6xrGGoE9U0tVAWl0tQWWYMv5w==} + '@cartesi/rpc@2.0.0-alpha.19': + resolution: {integrity: sha512-KOA3dzeq0B7iQe9RCYvNPC/VaKDFoNHZHqjSfjhXUSJA6VsJzJgKjAnkZm1bKpSIiMnq/eKAhPW/+Urr194phA==} '@cartesi/util@6.3.0': resolution: {integrity: sha512-UgsyTklI4mf3ZbnPHQTuYJcMyldM3y9Xm+ib9xBgDzDq6TxMO81xkrPv/gwA23seHL2/bkJ5flXJEhCE8poW8Q==} - '@cartesi/viem@2.0.0-alpha.24': - resolution: {integrity: sha512-rKFVELnilpJf3vqqV0qwbddoTpXg7i4GocIP3bRkFJpGhXFA4j5b7aEzUtsUPstbmf5rq7xu074ttp6FPnpsrQ==} + '@cartesi/viem@2.0.0-alpha.26': + resolution: {integrity: sha512-TbdYh44cga4YVROKllZDd1w2dpVBkjhkkFiCtqNE+ASvRpAhDrGzBycYlkX92O0z3ZaLCDRUfpNUJwh52gv/qQ==} peerDependencies: viem: ^2.0.0 - '@cartesi/wagmi@2.0.0-alpha.27': - resolution: {integrity: sha512-WBqKhLD69aYmginxvFBpFT43NELbrFQIK3wE1eoT49c7bfWEMCWWEcC73AW3wv13vfJczLHlXZW8pYiyIVVHoQ==} + '@cartesi/wagmi@2.0.0-alpha.30': + resolution: {integrity: sha512-bbbLBhGK3E9nKxy+Pan87wkc1B+FdKtffyfAfp2U35Q3NrIL9NwMLMHez/by0g0r3OJIUD0WjXwkECi7eGFAPw==} peerDependencies: '@tanstack/react-query': ^5.0.0 react: ^19.0.0 viem: ^2.0.0 - wagmi: ^2.0.0 + wagmi: ^3.0.0 '@changesets/apply-release-plan@7.0.14': resolution: {integrity: sha512-ddBvf9PHdy2YY0OUiEl3TV78mH9sckndJR14QAt87KLEbIov81XO0q0QAmvooBxXlqRRP8I9B7XOzZwQG7JkWA==} @@ -4418,6 +4421,17 @@ packages: zod: optional: true + abitype@1.2.3: + resolution: {integrity: sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -9946,6 +9960,9 @@ packages: wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + web-solc@0.5.1: + resolution: {integrity: sha512-Z/hBplZq1+4i4bYeIeD9N3vP1BLUBXpSDa4h0Ipm2Z2cHv7x7DtZ2zFb0E1L1VZo4BF+OJGVtGlT+nTUXGgncQ==} + web-streams-polyfill@3.2.1: resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==} engines: {node: '>= 8'} @@ -11523,25 +11540,25 @@ snapshots: '@cartesi/rollups@2.1.1': {} - '@cartesi/rpc@2.0.0-alpha.17': + '@cartesi/rpc@2.0.0-alpha.19': dependencies: json-rpc-2.0: 1.7.1 '@cartesi/util@6.3.0': {} - '@cartesi/viem@2.0.0-alpha.24(typescript@5.9.3)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(zod@4.1.12)': + '@cartesi/viem@2.0.0-alpha.26(typescript@5.9.3)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(zod@4.1.12)': dependencies: - '@cartesi/rpc': 2.0.0-alpha.17 - abitype: 1.2.2(typescript@5.9.3)(zod@4.1.12) + '@cartesi/rpc': 2.0.0-alpha.19 + abitype: 1.2.3(typescript@5.9.3)(zod@4.1.12) p-retry: 7.1.1 viem: 2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) transitivePeerDependencies: - typescript - zod - '@cartesi/wagmi@2.0.0-alpha.27(@tanstack/react-query@5.90.12(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(wagmi@2.18.2(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.2.7)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(zod@4.1.12))(zod@4.1.12)': + '@cartesi/wagmi@2.0.0-alpha.30(@tanstack/react-query@5.90.12(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(wagmi@2.18.2(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.2.7)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(zod@4.1.12))(zod@4.1.12)': dependencies: - '@cartesi/viem': 2.0.0-alpha.24(typescript@5.9.3)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(zod@4.1.12) + '@cartesi/viem': 2.0.0-alpha.26(typescript@5.9.3)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(zod@4.1.12) '@tanstack/react-query': 5.90.12(react@19.2.3) react: 19.2.3 viem: 2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) @@ -14540,9 +14557,9 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@sunodo/wagmi-plugin-hardhat-deploy@0.4.0(@wagmi/core@2.22.1(@tanstack/query-core@5.90.12)(@types/react@19.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(abitype@1.2.2(typescript@5.9.3)(zod@3.25.76))(typescript@5.9.3)(wagmi@2.18.2(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.1.13)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))': + '@sunodo/wagmi-plugin-hardhat-deploy@0.4.0(@wagmi/core@2.22.1(@tanstack/query-core@5.90.12)(@types/react@19.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(abitype@1.2.3(typescript@5.9.3)(zod@3.25.76))(typescript@5.9.3)(wagmi@2.18.2(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(@types/react@19.1.13)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))': dependencies: - abitype: 1.2.2(typescript@5.9.3)(zod@3.25.76) + abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) optionalDependencies: '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.12)(@types/react@19.1.13)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(viem@2.41.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) typescript: 5.9.3 @@ -14847,7 +14864,7 @@ snapshots: '@usecannon/web-solc': 0.5.1 acorn: 8.15.0 axios: 1.10.0(debug@4.4.3) - axios-retry: 4.5.0(axios@1.10.0) + axios-retry: 4.5.0(axios@1.10.0(debug@4.4.3)) buffer: 6.0.3 chalk: 4.1.2 debug: 4.4.3 @@ -14905,7 +14922,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@usecannon/web-solc@0.5.1': {} + '@usecannon/web-solc@0.5.1': + dependencies: + web-solc: 0.5.1 '@vanilla-extract/css@1.17.3': dependencies: @@ -16185,6 +16204,16 @@ snapshots: typescript: 5.9.3 zod: 4.1.12 + abitype@1.2.3(typescript@5.9.3)(zod@3.25.76): + optionalDependencies: + typescript: 5.9.3 + zod: 3.25.76 + + abitype@1.2.3(typescript@5.9.3)(zod@4.1.12): + optionalDependencies: + typescript: 5.9.3 + zod: 4.1.12 + abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 @@ -16425,7 +16454,7 @@ snapshots: axe-core@4.11.0: {} - axios-retry@4.5.0(axios@1.10.0): + axios-retry@4.5.0(axios@1.10.0(debug@4.4.3)): dependencies: axios: 1.10.0(debug@4.4.3) is-retry-allowed: 2.2.0 @@ -22612,6 +22641,10 @@ snapshots: dependencies: defaults: 1.0.4 + web-solc@0.5.1: + dependencies: + semver: 7.7.3 + web-streams-polyfill@3.2.1: {} webcrypto-core@1.7.7: From 45fa8a56041a741a4c3d6923aebbd6ae88463a60 Mon Sep 17 00:00:00 2001 From: Bruno Menezes Date: Mon, 2 Feb 2026 19:09:31 +0000 Subject: [PATCH 03/10] feat(dave): Add decoders and rollup-contracts name resolver. --- apps/dave/src/lib/decoders.ts | 41 ++++++++++++ apps/dave/src/lib/rollupContractResolver.ts | 72 +++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 apps/dave/src/lib/decoders.ts create mode 100644 apps/dave/src/lib/rollupContractResolver.ts diff --git a/apps/dave/src/lib/decoders.ts b/apps/dave/src/lib/decoders.ts new file mode 100644 index 00000000..f75d282c --- /dev/null +++ b/apps/dave/src/lib/decoders.ts @@ -0,0 +1,41 @@ +import { cond, identity, T } from "ramda"; +import { hexToString, isHex } from "viem"; +import type { DecoderType } from "../components/types"; + +/** + * Check if the content is a Hex value and tries to parse it to a readable string + * in case of failure it will return the original string content. + * @param content + * @returns + */ +export const asText = (content: string): string => { + try { + if (isHex(content)) return hexToString(content); + return content; + } catch (error) { + console.error((error as Error).message); + return content; + } +}; + +/** + * Check if the content is a Hex value (0x...) parsing to string value and then + * parsing the JSON and applying spacing. In case of failure the original string content is returned + * @param content + * @returns + */ +export const asJson = (content: string): string => { + try { + const text = asText(content); + return JSON.stringify(JSON.parse(text), null, 2); + } catch (error) { + console.error((error as Error).message); + return content; + } +}; + +export const getDecoder = cond([ + [(decoderType: DecoderType) => decoderType === "text", () => asText], + [(decoderType: DecoderType) => decoderType === "json", () => asJson], + [T, () => identity], +]); diff --git a/apps/dave/src/lib/rollupContractResolver.ts b/apps/dave/src/lib/rollupContractResolver.ts new file mode 100644 index 00000000..7569b58e --- /dev/null +++ b/apps/dave/src/lib/rollupContractResolver.ts @@ -0,0 +1,72 @@ +import { + erc1155BatchPortalAddress, + erc1155SinglePortalAddress, + erc20PortalAddress, + erc721PortalAddress, + etherPortalAddress, + inputBoxAddress, +} from "@cartesi/wagmi"; +import { T, cond } from "ramda"; +import { type Address, getAddress, isAddress } from "viem"; + +export type Resolver = ( + contractAddress: Address, +) => { name: string; method: string } | undefined; + +const etherPortalAddresses: Address[] = [etherPortalAddress]; +const erc20PortalAddresses: Address[] = [erc20PortalAddress]; +const erc721PortalAddresses: Address[] = [erc721PortalAddress]; +const erc1155BatchPortalAddresses: Address[] = [erc1155BatchPortalAddress]; +const erc1155SinglePortalAddresses: Address[] = [erc1155SinglePortalAddress]; +const inputBoxAddresses: Address[] = [inputBoxAddress]; + +const isOneOf = (portalAddresses: Address[]) => (address: Address) => + portalAddresses.includes(address); + +const resolver: Resolver = cond([ + [ + isOneOf(etherPortalAddresses), + () => ({ name: "EtherPortal", method: "depositEther" }), + ], + [ + isOneOf(erc20PortalAddresses), + () => ({ name: "ERC20Portal", method: "depositERC20Tokens" }), + ], + [ + isOneOf(erc721PortalAddresses), + () => ({ name: "ERC721Portal", method: "depositERC721Tokens" }), + ], + [ + isOneOf(erc1155SinglePortalAddresses), + () => ({ + name: "ERC1155SinglePortal", + method: "depositERC1155SingleTokens", + }), + ], + [ + isOneOf(erc1155BatchPortalAddresses), + () => ({ + name: "ERC1155BatchPortal", + method: "depositERC1155BatchToken", + }), + ], + [ + isOneOf(inputBoxAddresses), + () => ({ name: "InputBox", method: "addInput" }), + ], + [T, () => undefined], +]); + +export default class RollupContractResolver { + static resolveName(contractAddress: Address) { + if (!isAddress(contractAddress)) return undefined; + const result = resolver(getAddress(contractAddress)); + return result?.name; + } + + static resolveMethod(contractAddress: Address) { + if (!isAddress(contractAddress)) return undefined; + const result = resolver(getAddress(contractAddress)); + return result?.method; + } +} From bbe0b4488e193cdcc9704d0fee8cb1d977a0ee78 Mon Sep 17 00:00:00 2001 From: Bruno Menezes Date: Mon, 2 Feb 2026 19:10:25 +0000 Subject: [PATCH 04/10] chore(dave): Wrap defineProperty to avoid errors to bubble. --- apps/dave/src/providers/Providers.tsx | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/apps/dave/src/providers/Providers.tsx b/apps/dave/src/providers/Providers.tsx index b1d77872..83dd2674 100644 --- a/apps/dave/src/providers/Providers.tsx +++ b/apps/dave/src/providers/Providers.tsx @@ -21,14 +21,18 @@ import WalletProvider from "./WalletProvider"; * a PR is currently open here {@link https://github.com/facebook/react/pull/35013} */ if (process.env.NODE_ENV === "development") { - Object.defineProperty(BigInt.prototype, "toJSON", { - writable: false, - enumerable: true, - configurable: false, - value() { - return this.toString(); - }, - }); + try { + Object.defineProperty(BigInt.prototype, "toJSON", { + writable: false, + enumerable: true, + configurable: false, + value() { + return this.toString(); + }, + }); + } catch (error: unknown) { + console.info((error as Error).message); + } } type ProviderProps = { children: ReactNode }; From d4c25379dca1948c37fa36a114d926146e47709c Mon Sep 17 00:00:00 2001 From: Bruno Menezes Date: Mon, 2 Feb 2026 19:13:59 +0000 Subject: [PATCH 05/10] feat(dave): Add new components to support input information display. --- apps/dave/src/components/Address.tsx | 80 ++++++++++++++++++++ apps/dave/src/components/CopyButton.tsx | 47 ++++++++++++ apps/dave/src/components/PrettyTime.tsx | 49 ++++++++++++ apps/dave/src/components/TransactionHash.tsx | 33 ++++++++ 4 files changed, 209 insertions(+) create mode 100644 apps/dave/src/components/Address.tsx create mode 100644 apps/dave/src/components/CopyButton.tsx create mode 100644 apps/dave/src/components/PrettyTime.tsx create mode 100644 apps/dave/src/components/TransactionHash.tsx diff --git a/apps/dave/src/components/Address.tsx b/apps/dave/src/components/Address.tsx new file mode 100644 index 00000000..749ca52b --- /dev/null +++ b/apps/dave/src/components/Address.tsx @@ -0,0 +1,80 @@ +"use client"; + +import { + Anchor, + Group, + type GroupProps, + type MantineStyleProp, + Text, + Tooltip, +} from "@mantine/core"; +import Link from "next/link"; +import { type FC } from "react"; +import { jsNumberForAddress } from "react-jazzicon"; +import Jazzicon from "react-jazzicon/dist/Jazzicon"; +import { type Address as AddressType, getAddress } from "viem"; +import CopyButton from "./CopyButton"; + +import RollupContractResolver from "../lib/rollupContractResolver"; +import { shortenHash } from "../lib/textUtils"; + +export interface AddressProps extends GroupProps { + value: AddressType; + href?: string; + hrefTarget?: "_self" | "_blank" | "_top" | "_parent"; + icon?: boolean; + iconSize?: number; + shorten?: boolean; + canCopy?: boolean; +} + +const Address: FC = ({ + href, + value, + icon, + iconSize, + shorten, + hrefTarget = "_self", + canCopy = true, + ...restProps +}) => { + value = getAddress(value); + const name = RollupContractResolver.resolveName(value); + const text = shorten ? shortenHash(value) : value; + const textStyle: MantineStyleProp = { wordBreak: "break-all" }; + + const label = name ? ( + + {name} + + ) : shorten ? ( + + {text} + + ) : ( + {text} + ); + return ( + + + {icon && ( + + )} + + {href ? ( + + {label} + + ) : ( + label + )} + + {canCopy && } + + ); +}; + +export default Address; diff --git a/apps/dave/src/components/CopyButton.tsx b/apps/dave/src/components/CopyButton.tsx new file mode 100644 index 00000000..d6c55122 --- /dev/null +++ b/apps/dave/src/components/CopyButton.tsx @@ -0,0 +1,47 @@ +import { + ActionIcon, + CopyButton as MantineCopyButton, + rem, + Tooltip, +} from "@mantine/core"; +import { type FC } from "react"; +import { TbCheck, TbCopy } from "react-icons/tb"; + +interface CopyButtonProps { + value: string; +} + +const CopyButton: FC = ({ value }) => { + return ( + + {({ copied, copy }) => ( + + + {copied ? ( + + ) : ( + + )} + + + )} + + ); +}; + +export default CopyButton; diff --git a/apps/dave/src/components/PrettyTime.tsx b/apps/dave/src/components/PrettyTime.tsx new file mode 100644 index 00000000..a9db24b7 --- /dev/null +++ b/apps/dave/src/components/PrettyTime.tsx @@ -0,0 +1,49 @@ +import { Button, Text, type TextProps } from "@mantine/core"; +import { useDisclosure } from "@mantine/hooks"; +import prettyMillis, { type Options } from "pretty-ms"; +import { Activity, type FC } from "react"; + +interface PrettyTimeProps { + milliseconds: number; + options?: Options; + displayTimestampUTC?: boolean; + size?: TextProps["size"]; +} + +const defaultOpts = { + unitCount: 2, + secondsDecimalDigits: 0, + verbose: true, +}; + +export const PrettyTime: FC = ({ + milliseconds, + options, + displayTimestampUTC = false, + size, +}) => { + const opts: Options = Object.assign({ ...defaultOpts }, options); + const [asTimestamp, handlers] = useDisclosure(false); + const text = asTimestamp + ? new Date(milliseconds).toISOString() + : `${prettyMillis(Date.now() - milliseconds, opts)} ago`; + + return ( + <> + + + + + {text} + + + ); +}; diff --git a/apps/dave/src/components/TransactionHash.tsx b/apps/dave/src/components/TransactionHash.tsx new file mode 100644 index 00000000..11518546 --- /dev/null +++ b/apps/dave/src/components/TransactionHash.tsx @@ -0,0 +1,33 @@ +import { Flex, Text } from "@mantine/core"; +import { type FC } from "react"; +import { useConfig } from "wagmi"; +import { shortenHash } from "../lib/textUtils"; +import { BlockExplorerLink } from "./BlockExplorerLink"; +import CopyButton from "./CopyButton"; + +interface TransactionHashProps { + transactionHash: string; +} + +const TransactionHash: FC = ({ transactionHash }) => { + const config = useConfig(); + + const Link = BlockExplorerLink({ + value: transactionHash, + type: "tx", + chain: config.chains[0], + }); + + return ( + + {Link === null ? ( + {shortenHash(transactionHash)} + ) : ( + <>{Link} + )}{" "} + + + ); +}; + +export default TransactionHash; From bfdf87b98a9304b096dc794bbe5217c7a32de092 Mon Sep 17 00:00:00 2001 From: Bruno Menezes Date: Mon, 2 Feb 2026 19:15:24 +0000 Subject: [PATCH 06/10] feat(dave): Add OutputList and Output component with pagination and initial decoding capability --- .../src/components/output/Output.stories.tsx | 218 ++++++++++++++++++ apps/dave/src/components/output/Output.tsx | 105 +++++++++ .../dave/src/components/output/OutputList.tsx | 101 ++++++++ 3 files changed, 424 insertions(+) create mode 100644 apps/dave/src/components/output/Output.stories.tsx create mode 100644 apps/dave/src/components/output/Output.tsx create mode 100644 apps/dave/src/components/output/OutputList.tsx diff --git a/apps/dave/src/components/output/Output.stories.tsx b/apps/dave/src/components/output/Output.stories.tsx new file mode 100644 index 00000000..888ecced --- /dev/null +++ b/apps/dave/src/components/output/Output.stories.tsx @@ -0,0 +1,218 @@ +import type { GetOutputReturnType } from "@cartesi/viem"; +import { SegmentedControl, Stack } from "@mantine/core"; +import type { Meta, StoryObj } from "@storybook/nextjs"; +import { useState } from "react"; +import { type DecoderType } from "../types"; +import { Output } from "./Output"; + +const meta = { + title: "Components/Output", + component: Output, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const validVoucher: GetOutputReturnType = { + epochIndex: 1n, + inputIndex: 1n, + index: 2n, + rawData: + "0x237a816f000000000000000000000000c3e53f4d16ae77db1c982e75a937b9f60fe63690000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000a074683b5be015f053b5dceb064c41fc9d11b6e50000000000000000000000000000000000000000000000bdbc41e0348b30000000000000000000000000000000000000000000000000000000000000", + hash: "0x568f6025a01afa779e1db16825bd4f6ca0ffe62285373be4c08ef79e31711a6d", + outputHashesSiblings: [ + "0xf1de49ce16cbc7791e2fb8703279b4cb768bb0e54d84e5034b55bb25abba0772", + "0x1cbac0658afbae291460e6a75767cdd2804044939db31e48d5a8d8d85b1ec880", + "0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", + "0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", + "0xe58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344", + "0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d", + "0x887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968", + "0xffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f83", + "0x9867cc5f7f196b93bae1e27e6320742445d290f2263827498b54fec539f756af", + "0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0", + "0xf9dc3e7fe016e050eff260334f18a5d4fe391d82092319f5964f2e2eb7c1c3a5", + "0xf8b13a49e282f609c317a833fb8d976d11517c571d1221a265d25af778ecf892", + "0x3490c6ceeb450aecdc82e28293031d10c7d73bf85e57bf041a97360aa2c5d99c", + "0xc1df82d9c4b87413eae2ef048f94b4d3554cea73d92b0f7af96e0271c691e2bb", + "0x5c67add7c6caf302256adedf7ab114da0acfe870d449a3a489f781d659e8becc", + "0xda7bce9f4e8618b6bd2f4132ce798cdc7a60e7e1460a7299e3c6342a579626d2", + "0x2733e50f526ec2fa19a22b31e8ed50f23cd1fdf94c9154ed3a7609a2f1ff981f", + "0xe1d3b5c807b281e4683cc6d6315cf95b9ade8641defcb32372f1c126e398ef7a", + "0x5a2dce0a8a7f68bb74560f8f71837c2c2ebbcbf7fffb42ae1896f13f7c7479a0", + "0xb46a28b6f55540f89444f63de0378e3d121be09e06cc9ded1c20e65876d36aa0", + "0xc65e9645644786b620e2dd2ad648ddfcbf4a7e5b1a3a4ecfe7f64667a3f0b7e2", + "0xf4418588ed35a2458cffeb39b93d26f18d2ab13bdce6aee58e7b99359ec2dfd9", + "0x5a9c16dc00d6ef18b7933a6f8dc65ccb55667138776f7dea101070dc8796e377", + "0x4df84f40ae0c8229d0d6069e5c8f39a7c299677a09d367fc7b05e3bc380ee652", + "0xcdc72595f74c7b1043d0e1ffbab734648c838dfb0527d971b602bc216c9619ef", + "0x0abf5ac974a1ed57f4050aa510dd9c74f508277b39d7973bb2dfccc5eeb0618d", + "0xb8cd74046ff337f0a7bf2c8e03e10f642c1886798d71806ab1e888d9e5ee87d0", + "0x838c5655cb21c6cb83313b5a631175dff4963772cce9108188b34ac87c81c41e", + "0x662ee4dd2dd7b2bc707961b1e646c4047669dcb6584f0d8d770daf5d7e7deb2e", + "0x388ab20e2573d171a88108e79d820e98f26c0b84aa8b2f4aa4968dbb818ea322", + "0x93237c50ba75ee485f4c22adf2f741400bdf8d6a9cc7df7ecae576221665d735", + "0x8448818bb4ae4562849e949e17ac16e0be16688e156b5cf15e098c627c0056a9", + "0x27ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d757", + "0xbf558bebd2ceec7f3c5dce04a4782f88c2c6036ae78ee206d0bc5289d20461a2", + "0xe21908c2968c0699040a6fd866a577a99a9d2ec88745c815fd4a472c789244da", + "0xae824d72ddc272aab68a8c3022e36f10454437c1886f3ff9927b64f232df414f", + "0x27e429a4bef3083bc31a671d046ea5c1f5b8c3094d72868d9dfdc12c7334ac5f", + "0x743cc5c365a9a6a15c1f240ac25880c7a9d1de290696cb766074a1d83d927816", + "0x4adcf616c3bfabf63999a01966c998b7bb572774035a63ead49da73b5987f347", + "0x75786645d0c5dd7c04a2f8a75dcae085213652f5bce3ea8b9b9bedd1cab3c5e9", + "0xb88b152c9b8a7b79637d35911848b0c41e7cc7cca2ab4fe9a15f9c38bb4bb939", + "0x0c4e2d8ce834ffd7a6cd85d7113d4521abb857774845c4291e6f6d010d97e318", + "0x5bc799d83e3bb31501b3da786680df30fbc18eb41cbce611e8c0e9c72f69571c", + "0xa10d3ef857d04d9c03ead7c6317d797a090fa1271ad9c7addfbcb412e9643d4f", + "0xb33b1809c42623f474055fa9400a2027a7a885c8dfa4efe20666b4ee27d7529c", + "0x134d7f28d53f175f6bf4b62faa2110d5b76f0f770c15e628181c1fcc18f970a9", + "0xc34d24b2fc8c50ca9c07a7156ef4e5ff4bdf002eda0b11c1d359d0b59a546807", + "0x04dbb9db631457879b27e0dfdbe50158fd9cf9b4cf77605c4ac4c95bd65fc9f6", + "0xf9295a686647cb999090819cda700820c282c613cedcd218540bbc6f37b01c65", + "0x67c4a1ea624f092a3a5cca2d6f0f0db231972fce627f0ecca0dee60f17551c5f", + "0x8fdaeb5ab560b2ceb781cdb339361a0fbee1b9dffad59115138c8d6a70dda9cc", + "0xc1bf0bbdd7fee15764845db875f6432559ff8dbc9055324431bc34e5b93d15da", + "0x307317849eccd90c0c7b98870b9317c15a5959dcfb84c76dcc908c4fe6ba9212", + "0x6339bf06e458f6646df5e83ba7c3d35bc263b3222c8e9040068847749ca8e8f9", + "0x5045e4342aeb521eb3a5587ec268ed3aa6faf32b62b0bc41a9d549521f406fc3", + "0x08601d83cdd34b5f7b8df63e7b9a16519d35473d0b89c317beed3d3d9424b253", + "0x84e35c5d92171376cae5c86300822d729cd3a8479583bef09527027dba5f1126", + "0x3c5cbbeb3834b7a5c1cba9aa5fee0c95ec3f17a33ec3d8047fff799187f5ae20", + "0x40bbe913c226c34c9fbe4389dd728984257a816892b3cae3e43191dd291f0eb5", + "0x14af5385bcbb1e4738bbae8106046e6e2fca42875aa5c000c582587742bcc748", + "0x72f29656803c2f4be177b1b8dd2a5137892b080b022100fde4e96d93ef8c96ff", + "0xd06f27061c734d7825b46865d00aa900e5cc3a3672080e527171e1171aa5038a", + "0x28203985b5f2d87709171678169739f957d2745f4bfa5cc91e2b4bd9bf483b40", + ], + executionTransactionHash: null, + createdAt: new Date(), + updatedAt: new Date(), + decodedData: { + type: "Voucher", + destination: "0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690", + value: 0n, + payload: + "0xa9059cbb000000000000000000000000a074683b5be015f053b5dceb064c41fc9d11b6e50000000000000000000000000000000000000000000000bdbc41e0348b300000", + }, +}; +const validNotice: GetOutputReturnType = { + epochIndex: 1n, + inputIndex: 1n, + index: 3n, + rawData: + "0xc258d6e5000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000847b22616374696f6e223a2265726332305f6465706f736974222c2264617461223a7b226d7367223a22576974686472617720766f7563686572206f662033353030204552432d323020746f203078613037343638334235424530313546303533623544636562303634433431664339443131423645352067656e6572617465642e227d7d00000000000000000000000000000000000000000000000000000000", + hash: "0xf1de49ce16cbc7791e2fb8703279b4cb768bb0e54d84e5034b55bb25abba0772", + outputHashesSiblings: [ + "0x568f6025a01afa779e1db16825bd4f6ca0ffe62285373be4c08ef79e31711a6d", + "0x1cbac0658afbae291460e6a75767cdd2804044939db31e48d5a8d8d85b1ec880", + "0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", + "0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", + "0xe58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344", + "0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d", + "0x887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968", + "0xffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f83", + "0x9867cc5f7f196b93bae1e27e6320742445d290f2263827498b54fec539f756af", + "0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0", + "0xf9dc3e7fe016e050eff260334f18a5d4fe391d82092319f5964f2e2eb7c1c3a5", + "0xf8b13a49e282f609c317a833fb8d976d11517c571d1221a265d25af778ecf892", + "0x3490c6ceeb450aecdc82e28293031d10c7d73bf85e57bf041a97360aa2c5d99c", + "0xc1df82d9c4b87413eae2ef048f94b4d3554cea73d92b0f7af96e0271c691e2bb", + "0x5c67add7c6caf302256adedf7ab114da0acfe870d449a3a489f781d659e8becc", + "0xda7bce9f4e8618b6bd2f4132ce798cdc7a60e7e1460a7299e3c6342a579626d2", + "0x2733e50f526ec2fa19a22b31e8ed50f23cd1fdf94c9154ed3a7609a2f1ff981f", + "0xe1d3b5c807b281e4683cc6d6315cf95b9ade8641defcb32372f1c126e398ef7a", + "0x5a2dce0a8a7f68bb74560f8f71837c2c2ebbcbf7fffb42ae1896f13f7c7479a0", + "0xb46a28b6f55540f89444f63de0378e3d121be09e06cc9ded1c20e65876d36aa0", + "0xc65e9645644786b620e2dd2ad648ddfcbf4a7e5b1a3a4ecfe7f64667a3f0b7e2", + "0xf4418588ed35a2458cffeb39b93d26f18d2ab13bdce6aee58e7b99359ec2dfd9", + "0x5a9c16dc00d6ef18b7933a6f8dc65ccb55667138776f7dea101070dc8796e377", + "0x4df84f40ae0c8229d0d6069e5c8f39a7c299677a09d367fc7b05e3bc380ee652", + "0xcdc72595f74c7b1043d0e1ffbab734648c838dfb0527d971b602bc216c9619ef", + "0x0abf5ac974a1ed57f4050aa510dd9c74f508277b39d7973bb2dfccc5eeb0618d", + "0xb8cd74046ff337f0a7bf2c8e03e10f642c1886798d71806ab1e888d9e5ee87d0", + "0x838c5655cb21c6cb83313b5a631175dff4963772cce9108188b34ac87c81c41e", + "0x662ee4dd2dd7b2bc707961b1e646c4047669dcb6584f0d8d770daf5d7e7deb2e", + "0x388ab20e2573d171a88108e79d820e98f26c0b84aa8b2f4aa4968dbb818ea322", + "0x93237c50ba75ee485f4c22adf2f741400bdf8d6a9cc7df7ecae576221665d735", + "0x8448818bb4ae4562849e949e17ac16e0be16688e156b5cf15e098c627c0056a9", + "0x27ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d757", + "0xbf558bebd2ceec7f3c5dce04a4782f88c2c6036ae78ee206d0bc5289d20461a2", + "0xe21908c2968c0699040a6fd866a577a99a9d2ec88745c815fd4a472c789244da", + "0xae824d72ddc272aab68a8c3022e36f10454437c1886f3ff9927b64f232df414f", + "0x27e429a4bef3083bc31a671d046ea5c1f5b8c3094d72868d9dfdc12c7334ac5f", + "0x743cc5c365a9a6a15c1f240ac25880c7a9d1de290696cb766074a1d83d927816", + "0x4adcf616c3bfabf63999a01966c998b7bb572774035a63ead49da73b5987f347", + "0x75786645d0c5dd7c04a2f8a75dcae085213652f5bce3ea8b9b9bedd1cab3c5e9", + "0xb88b152c9b8a7b79637d35911848b0c41e7cc7cca2ab4fe9a15f9c38bb4bb939", + "0x0c4e2d8ce834ffd7a6cd85d7113d4521abb857774845c4291e6f6d010d97e318", + "0x5bc799d83e3bb31501b3da786680df30fbc18eb41cbce611e8c0e9c72f69571c", + "0xa10d3ef857d04d9c03ead7c6317d797a090fa1271ad9c7addfbcb412e9643d4f", + "0xb33b1809c42623f474055fa9400a2027a7a885c8dfa4efe20666b4ee27d7529c", + "0x134d7f28d53f175f6bf4b62faa2110d5b76f0f770c15e628181c1fcc18f970a9", + "0xc34d24b2fc8c50ca9c07a7156ef4e5ff4bdf002eda0b11c1d359d0b59a546807", + "0x04dbb9db631457879b27e0dfdbe50158fd9cf9b4cf77605c4ac4c95bd65fc9f6", + "0xf9295a686647cb999090819cda700820c282c613cedcd218540bbc6f37b01c65", + "0x67c4a1ea624f092a3a5cca2d6f0f0db231972fce627f0ecca0dee60f17551c5f", + "0x8fdaeb5ab560b2ceb781cdb339361a0fbee1b9dffad59115138c8d6a70dda9cc", + "0xc1bf0bbdd7fee15764845db875f6432559ff8dbc9055324431bc34e5b93d15da", + "0x307317849eccd90c0c7b98870b9317c15a5959dcfb84c76dcc908c4fe6ba9212", + "0x6339bf06e458f6646df5e83ba7c3d35bc263b3222c8e9040068847749ca8e8f9", + "0x5045e4342aeb521eb3a5587ec268ed3aa6faf32b62b0bc41a9d549521f406fc3", + "0x08601d83cdd34b5f7b8df63e7b9a16519d35473d0b89c317beed3d3d9424b253", + "0x84e35c5d92171376cae5c86300822d729cd3a8479583bef09527027dba5f1126", + "0x3c5cbbeb3834b7a5c1cba9aa5fee0c95ec3f17a33ec3d8047fff799187f5ae20", + "0x40bbe913c226c34c9fbe4389dd728984257a816892b3cae3e43191dd291f0eb5", + "0x14af5385bcbb1e4738bbae8106046e6e2fca42875aa5c000c582587742bcc748", + "0x72f29656803c2f4be177b1b8dd2a5137892b080b022100fde4e96d93ef8c96ff", + "0xd06f27061c734d7825b46865d00aa900e5cc3a3672080e527171e1171aa5038a", + "0x28203985b5f2d87709171678169739f957d2745f4bfa5cc91e2b4bd9bf483b40", + ], + executionTransactionHash: null, + createdAt: new Date(), + updatedAt: new Date(), + decodedData: { + type: "Notice", + payload: + "0x7b22616374696f6e223a2265726332305f6465706f736974222c2264617461223a7b226d7367223a22576974686472617720766f7563686572206f662033353030204552432d323020746f203078613037343638334235424530313546303533623544636562303634433431664339443131423645352067656e6572617465642e227d7d", + }, +}; + +type Props = Parameters[0]; + +const WithDecoding = (props: Props) => { + const options = [ + { label: "Raw", value: "raw" }, + { label: "as Text", value: "text" }, + { label: "as JSON", value: "json" }, + ]; + const [displayOpt, setDisplayOpt] = useState("raw"); + + return ( + + setDisplayOpt(value as DecoderType)} + /> + + + ); +}; + +export const Notice: Story = { + args: { + displayAs: "raw", + output: validNotice, + }, + render: WithDecoding, +}; + +export const Voucher: Story = { + args: { + displayAs: "raw", + output: validVoucher, + }, + render: WithDecoding, +}; diff --git a/apps/dave/src/components/output/Output.tsx b/apps/dave/src/components/output/Output.tsx new file mode 100644 index 00000000..fa08b045 --- /dev/null +++ b/apps/dave/src/components/output/Output.tsx @@ -0,0 +1,105 @@ +import type { + DelegateCallVoucher, + GetOutputReturnType, + Notice, + Voucher, +} from "@cartesi/viem"; +import { Divider, Fieldset, Group, Spoiler, Text } from "@mantine/core"; +import type { FC } from "react"; +import { formatUnits, isHex } from "viem"; +import { getDecoder } from "../../lib/decoders"; +import Address from "../Address"; +import type { DecoderType } from "../types"; + +interface OutputProps { + displayAs?: DecoderType; + output: GetOutputReturnType; +} + +type NoticeProps = { decodedData: Notice; decoderType: DecoderType }; + +const NoticeContent: FC = ({ decodedData, decoderType }) => { + const decoderFn = getDecoder(decoderType); + + return ( +
+ + + {decoderFn(decodedData.payload)} + + +
+ ); +}; + +type VoucherProps = { + decodedData: Voucher | DelegateCallVoucher; + decoderType: DecoderType; + title?: string; +}; + +const VoucherContent: FC = ({ + decodedData, + decoderType, + title = "Voucher", +}) => { + const decoderFn = getDecoder(decoderType); + const hasPayload = + isHex(decodedData.payload) && decodedData.payload !== "0x"; + const amount = decodedData.type === "Voucher" ? decodedData.value : 0n; + const hasAmount = amount > 0n; + + return ( +
+ + Destination +
+ + {hasAmount && ( + + Amount + {formatUnits(amount, 18)} + + )} + {hasPayload && ( + <> + + + + {decoderFn(decodedData.payload)} + + + + )} +
+ ); +}; + +export const Output: FC = ({ displayAs = "raw", output }) => { + const outputType = output.decodedData.type; + return ( + <> + {outputType === "Notice" ? ( + + ) : outputType === "Voucher" ? ( + + ) : outputType === "DelegateCallVoucher" ? ( + + ) : null} + + ); +}; diff --git a/apps/dave/src/components/output/OutputList.tsx b/apps/dave/src/components/output/OutputList.tsx new file mode 100644 index 00000000..c8e64ab2 --- /dev/null +++ b/apps/dave/src/components/output/OutputList.tsx @@ -0,0 +1,101 @@ +import type { ListOutputsParams } from "@cartesi/viem"; +import { useOutputs } from "@cartesi/wagmi"; +import { Card, Center, Group, Pagination, Stack, Text } from "@mantine/core"; +import { Activity, useState, type FC } from "react"; +import type { DecoderType } from "../types"; +import { Output } from "./Output"; + +interface OutputListProps extends ListOutputsParams { + decoderType?: DecoderType; +} + +const NoOutputs = () => ( +
+ + No outputs generated + +
+); + +export const OutputList: FC = ({ + application, + descending = true, + inputIndex, + epochIndex, + limit = 50, + outputType, + offset = 0, + voucherAddress, + decoderType = "raw", +}) => { + const [newOffSet, setNewOffset] = useState(offset); + const { + data: result, + isLoading, + error, + isError, + } = useOutputs({ + application, + epochIndex, + inputIndex, + outputType, + voucherAddress, + limit, + offset: newOffSet, + descending, + }); + + if (isLoading) { + return ( + +
+ Checking for outputs... +
+
+ ); + } + + if (isError) { + console.error(error.message); + return ( + +
+ Could not fetch the outputs +
+
+ ); + } + + if (!result || result.data.length === 0) { + return ; + } + + const hasMoreThanOne = result.pagination.totalCount; + + return ( + + + + { + if (value !== newOffSet + 1) { + setNewOffset(value - 1); + } else { + console.log(`Clicked same number ${value}`); + } + }} + /> + + + {result.data.map((output) => ( + + ))} + + ); +}; From 2492d5792cb70ee99d11a5abead390a3cffdf868 Mon Sep 17 00:00:00 2001 From: Bruno Menezes Date: Mon, 2 Feb 2026 19:16:06 +0000 Subject: [PATCH 07/10] feat(dave): Add initial report-list content display with decoding. --- .../src/components/report/Report.stories.tsx | 54 +++++++++++++ apps/dave/src/components/report/Report.tsx | 29 +++++++ .../dave/src/components/report/ReportList.tsx | 79 +++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 apps/dave/src/components/report/Report.stories.tsx create mode 100644 apps/dave/src/components/report/Report.tsx create mode 100644 apps/dave/src/components/report/ReportList.tsx diff --git a/apps/dave/src/components/report/Report.stories.tsx b/apps/dave/src/components/report/Report.stories.tsx new file mode 100644 index 00000000..518ca8e4 --- /dev/null +++ b/apps/dave/src/components/report/Report.stories.tsx @@ -0,0 +1,54 @@ +import type { GetReportReturnType } from "@cartesi/viem"; +import { SegmentedControl, Stack } from "@mantine/core"; +import type { Meta, StoryObj } from "@storybook/nextjs"; +import { useState } from "react"; +import { type DecoderType } from "../types"; +import { Report } from "./Report"; + +const meta = { + title: "Components/Report", + component: Report, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const validReport: GetReportReturnType = { + createdAt: new Date(), + updatedAt: new Date(), + index: 1n, + inputIndex: 1n, + rawData: + "0x7b22616374696f6e223a2265726332305f6465706f736974222c2264617461223a7b226d7367223a22576974686472617720766f7563686572206f662033353030204552432d323020746f203078613037343638334235424530313546303533623544636562303634433431664339443131423645352067656e6572617465642e227d7d", +}; + +type Props = Parameters[0]; + +const WithDecoding = (props: Props) => { + const options = [ + { label: "Raw", value: "raw" }, + { label: "as Text", value: "text" }, + { label: "as JSON", value: "json" }, + ]; + const [displayOpt, setDisplayOpt] = useState("raw"); + + return ( + + setDisplayOpt(value as DecoderType)} + /> + + + ); +}; + +export const Default: Story = { + args: { + displayAs: "raw", + report: validReport, + }, + render: WithDecoding, +}; diff --git a/apps/dave/src/components/report/Report.tsx b/apps/dave/src/components/report/Report.tsx new file mode 100644 index 00000000..8007523b --- /dev/null +++ b/apps/dave/src/components/report/Report.tsx @@ -0,0 +1,29 @@ +import type { GetReportReturnType } from "@cartesi/viem"; +import { Spoiler, Text } from "@mantine/core"; +import { useEffect, useRef, type FC } from "react"; +import { getDecoder } from "../../lib/decoders"; +import type { DecoderType } from "../types"; + +interface ReportProps { + displayAs?: DecoderType; + report: GetReportReturnType; +} + +export const Report: FC = ({ displayAs = "raw", report }) => { + const decoderFn = getDecoder(displayAs); + const ref = useRef(null); + + useEffect(() => { + if (ref.current !== null) { + ref.current.blur(); + } + }); + + return ( + + + {decoderFn(report.rawData)} + + + ); +}; diff --git a/apps/dave/src/components/report/ReportList.tsx b/apps/dave/src/components/report/ReportList.tsx new file mode 100644 index 00000000..bd62829f --- /dev/null +++ b/apps/dave/src/components/report/ReportList.tsx @@ -0,0 +1,79 @@ +import type { ListReportsParams } from "@cartesi/viem"; +import { useReports } from "@cartesi/wagmi"; +import { Card, Center, Stack, Text } from "@mantine/core"; +import { type FC } from "react"; +import type { DecoderType } from "../types"; +import { Report } from "./Report"; + +interface ReportListProps extends ListReportsParams { + decoderType?: DecoderType; +} + +const NoReports = () => ( +
+ + No reports generated + +
+); + +export const ReportList: FC = ({ + application, + descending = true, + epochIndex, + inputIndex, + limit, + offset, + decoderType = "raw", +}) => { + const { + data: result, + isLoading, + error, + isError, + } = useReports({ + application, + epochIndex, + inputIndex, + descending, + limit, + offset, + }); + + if (isLoading) { + return ( + +
+ Checking for reports... +
+
+ ); + } + + if (isError) { + console.error(error.message); + return ( + +
+ Could not fetch the reports +
+
+ ); + } + + if (!result || result.data.length === 0) { + return ; + } + + return ( + + {result.data.map((report) => ( + + ))} + + ); +}; From 112adc62f7a2912cdb2d7c81deeaae1dbe46027b Mon Sep 17 00:00:00 2001 From: Bruno Menezes Date: Mon, 2 Feb 2026 19:43:35 +0000 Subject: [PATCH 08/10] feat(dave): Add output, report and initial decoding capability to inputs. --- apps/dave/src/components/input/InputCard.tsx | 157 ++++++++++++++----- apps/dave/src/components/input/InputList.tsx | 2 +- apps/dave/src/components/layout/Layout.tsx | 12 +- apps/dave/src/components/types.ts | 17 ++ 4 files changed, 148 insertions(+), 40 deletions(-) diff --git a/apps/dave/src/components/input/InputCard.tsx b/apps/dave/src/components/input/InputCard.tsx index 1cddd6c8..4110b0ec 100644 --- a/apps/dave/src/components/input/InputCard.tsx +++ b/apps/dave/src/components/input/InputCard.tsx @@ -1,21 +1,27 @@ import type { Input, InputStatus } from "@cartesi/viem"; import { Badge, - Button, Card, - Collapse, Group, + ScrollArea, + SegmentedControl, + Select, + Spoiler, Stack, Text, - Textarea, + Tooltip, type MantineColor, } from "@mantine/core"; -import { useDisclosure } from "@mantine/hooks"; -import { type FC } from "react"; -import { TbEyeMinus, TbEyePlus } from "react-icons/tb"; +import { Activity, useMemo, useState, type FC } from "react"; +import { TbReceipt } from "react-icons/tb"; import useRightColorShade from "../../hooks/useRightColorShade"; -import theme from "../../providers/theme"; -import { LongText } from "../LongText"; +import { getDecoder } from "../../lib/decoders"; +import Address from "../Address"; +import { PrettyTime } from "../PrettyTime"; +import TransactionHash from "../TransactionHash"; +import { OutputList } from "../output/OutputList"; +import { ReportList } from "../report/ReportList"; +import { contentDisplayOptions, type DecoderType } from "../types"; interface Props { input: Input; @@ -32,45 +38,126 @@ const getStatusColor = (status: InputStatus): MantineColor => { } }; +type ViewControl = "payload" | "output" | "report"; + +const maxHeight = 450; +const iconSize = 21; // TODO: Define what else will be inside like payload (decoding etc) export const InputCard: FC = ({ input }) => { - const [displayMeta, { toggle: toggleDisplayMeta }] = useDisclosure(false); const statusColor = useRightColorShade(getStatusColor(input.status)); + const [viewControl, setViewControl] = useState("payload"); + const [decoderType, setDecoderType] = useState("raw"); + const decoderFn = useMemo(() => getDecoder(decoderType), [decoderType]); + const millis = Number(input.decodedData.blockTimestamp * 1000n); return ( - + - # {input.index} - {input.status !== "ACCEPTED" && ( - {input.status} - )} +
+ + # {input.index} + + {input.status} + + - + + - + data={[ + { value: "payload", label: "Payload" }, + { value: "output", label: "Output" }, + { value: "report", label: "Report" }, + ]} + /> +