@iota/create-dapp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/LICENSE +201 -0
  3. package/README.md +33 -0
  4. package/bin/index.js +7 -0
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.js +103 -0
  7. package/dist/index.js.map +1 -0
  8. package/package.json +44 -0
  9. package/src/index.ts +131 -0
  10. package/templates/react-client-dapp/README.md +35 -0
  11. package/templates/react-client-dapp/index.html +59 -0
  12. package/templates/react-client-dapp/package.json +35 -0
  13. package/templates/react-client-dapp/prettier.config.cjs +4 -0
  14. package/templates/react-client-dapp/src/App.tsx +39 -0
  15. package/templates/react-client-dapp/src/OwnedObjects.tsx +42 -0
  16. package/templates/react-client-dapp/src/WalletStatus.tsx +23 -0
  17. package/templates/react-client-dapp/src/main.tsx +40 -0
  18. package/templates/react-client-dapp/src/vite-env.d.ts +1 -0
  19. package/templates/react-client-dapp/tsconfig.json +25 -0
  20. package/templates/react-client-dapp/tsconfig.node.json +10 -0
  21. package/templates/react-client-dapp/vite.config.ts +7 -0
  22. package/templates/react-e2e-counter/README.md +100 -0
  23. package/templates/react-e2e-counter/index.html +59 -0
  24. package/templates/react-e2e-counter/move/counter/Move.toml +9 -0
  25. package/templates/react-e2e-counter/move/counter/sources/counter.move +54 -0
  26. package/templates/react-e2e-counter/package.json +35 -0
  27. package/templates/react-e2e-counter/prettier.config.cjs +4 -0
  28. package/templates/react-e2e-counter/src/App.tsx +64 -0
  29. package/templates/react-e2e-counter/src/Counter.tsx +94 -0
  30. package/templates/react-e2e-counter/src/CreateCounter.tsx +67 -0
  31. package/templates/react-e2e-counter/src/constants.ts +2 -0
  32. package/templates/react-e2e-counter/src/main.tsx +26 -0
  33. package/templates/react-e2e-counter/src/networkConfig.ts +27 -0
  34. package/templates/react-e2e-counter/src/vite-env.d.ts +1 -0
  35. package/templates/react-e2e-counter/tsconfig.json +25 -0
  36. package/templates/react-e2e-counter/tsconfig.node.json +10 -0
  37. package/templates/react-e2e-counter/vite.config.ts +7 -0
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@iota/template-react-client-app",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc && vite build",
9
+ "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10
+ "preview": "vite preview"
11
+ },
12
+ "dependencies": {
13
+ "@iota/dapp-kit": "workspace:*",
14
+ "@iota/iota-sdk": "workspace:*",
15
+ "@radix-ui/colors": "^3.0.0",
16
+ "@radix-ui/react-icons": "^1.3.0",
17
+ "@radix-ui/themes": "^2.0.0",
18
+ "@tanstack/react-query": "^5.0.0",
19
+ "react": "^18.3.1",
20
+ "react-dom": "^18.3.1"
21
+ },
22
+ "devDependencies": {
23
+ "@types/react": "^18.3.3",
24
+ "@types/react-dom": "^18.2.7",
25
+ "@typescript-eslint/eslint-plugin": "^6.1.0",
26
+ "@typescript-eslint/parser": "^6.1.0",
27
+ "@vitejs/plugin-react-swc": "^3.3.2",
28
+ "eslint": "^8.45.0",
29
+ "eslint-plugin-react-hooks": "^4.6.0",
30
+ "eslint-plugin-react-refresh": "^0.4.3",
31
+ "prettier": "^3.0.0",
32
+ "typescript": "^5.3.3",
33
+ "vite": "^4.4.4"
34
+ }
35
+ }
@@ -0,0 +1,4 @@
1
+ // eslint-disable-next-line no-undef
2
+ module.exports = {
3
+ proseWrap: "always",
4
+ };
@@ -0,0 +1,39 @@
1
+ import { ConnectButton } from "@iota/dapp-kit";
2
+ import { Box, Container, Flex, Heading } from "@radix-ui/themes";
3
+ import { WalletStatus } from "./WalletStatus";
4
+
5
+ function App() {
6
+ return (
7
+ <>
8
+ <Flex
9
+ position="sticky"
10
+ px="4"
11
+ py="2"
12
+ justify="between"
13
+ style={{
14
+ borderBottom: "1px solid var(--gray-a2)",
15
+ }}
16
+ >
17
+ <Box>
18
+ <Heading>dApp Starter Template</Heading>
19
+ </Box>
20
+
21
+ <Box>
22
+ <ConnectButton />
23
+ </Box>
24
+ </Flex>
25
+ <Container>
26
+ <Container
27
+ mt="5"
28
+ pt="2"
29
+ px="4"
30
+ style={{ background: "var(--gray-a2)", minHeight: 500 }}
31
+ >
32
+ <WalletStatus />
33
+ </Container>
34
+ </Container>
35
+ </>
36
+ );
37
+ }
38
+
39
+ export default App;
@@ -0,0 +1,42 @@
1
+ import { useCurrentAccount, useIotaClientQuery } from "@iota/dapp-kit";
2
+ import { Flex, Heading, Text } from "@radix-ui/themes";
3
+
4
+ export function OwnedObjects() {
5
+ const account = useCurrentAccount();
6
+ const { data, isPending, error } = useIotaClientQuery(
7
+ "getOwnedObjects",
8
+ {
9
+ owner: account?.address as string,
10
+ },
11
+ {
12
+ enabled: !!account,
13
+ },
14
+ );
15
+
16
+ if (!account) {
17
+ return;
18
+ }
19
+
20
+ if (error) {
21
+ return <Flex>Error: {error.message}</Flex>;
22
+ }
23
+
24
+ if (isPending || !data) {
25
+ return <Flex>Loading...</Flex>;
26
+ }
27
+
28
+ return (
29
+ <Flex direction="column" my="2">
30
+ {data.data.length === 0 ? (
31
+ <Text>No objects owned by the connected wallet</Text>
32
+ ) : (
33
+ <Heading size="4">Objects owned by the connected wallet</Heading>
34
+ )}
35
+ {data.data.map((object) => (
36
+ <Flex key={object.data?.objectId}>
37
+ <Text>Object ID: {object.data?.objectId}</Text>
38
+ </Flex>
39
+ ))}
40
+ </Flex>
41
+ );
42
+ }
@@ -0,0 +1,23 @@
1
+ import { useCurrentAccount } from "@iota/dapp-kit";
2
+ import { Container, Flex, Heading, Text } from "@radix-ui/themes";
3
+ import { OwnedObjects } from "./OwnedObjects";
4
+
5
+ export function WalletStatus() {
6
+ const account = useCurrentAccount();
7
+
8
+ return (
9
+ <Container my="2">
10
+ <Heading mb="2">Wallet Status</Heading>
11
+
12
+ {account ? (
13
+ <Flex direction="column">
14
+ <Text>Wallet connected</Text>
15
+ <Text>Address: {account.address}</Text>
16
+ </Flex>
17
+ ) : (
18
+ <Text>Wallet not connected</Text>
19
+ )}
20
+ <OwnedObjects />
21
+ </Container>
22
+ );
23
+ }
@@ -0,0 +1,40 @@
1
+ // Copyright (c) 2024 IOTA Stiftung
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import React from "react";
5
+ import ReactDOM from "react-dom/client";
6
+ import "@iota/dapp-kit/dist/index.css";
7
+ import "@radix-ui/themes/styles.css";
8
+
9
+ import { getFullnodeUrl } from "@iota/iota-sdk/client";
10
+ import {
11
+ IotaClientProvider,
12
+ WalletProvider,
13
+ createNetworkConfig,
14
+ } from "@iota/dapp-kit";
15
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
16
+ import { Theme } from "@radix-ui/themes";
17
+ import App from "./App.tsx";
18
+
19
+ const queryClient = new QueryClient();
20
+
21
+ const { networkConfig } = createNetworkConfig({
22
+ localnet: { url: getFullnodeUrl("localnet") },
23
+ devnet: { url: getFullnodeUrl("devnet") },
24
+ testnet: { url: getFullnodeUrl("testnet") },
25
+ mainnet: { url: getFullnodeUrl("mainnet") },
26
+ });
27
+
28
+ ReactDOM.createRoot(document.getElementById("root")!).render(
29
+ <React.StrictMode>
30
+ <Theme appearance="dark">
31
+ <QueryClientProvider client={queryClient}>
32
+ <IotaClientProvider networks={networkConfig} defaultNetwork="devnet">
33
+ <WalletProvider autoConnect>
34
+ <App />
35
+ </WalletProvider>
36
+ </IotaClientProvider>
37
+ </QueryClientProvider>
38
+ </Theme>
39
+ </React.StrictMode>,
40
+ );
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+
9
+ /* Bundler mode */
10
+ "moduleResolution": "bundler",
11
+ "allowImportingTsExtensions": true,
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "noEmit": true,
15
+ "jsx": "react-jsx",
16
+
17
+ /* Linting */
18
+ "strict": true,
19
+ "noUnusedLocals": true,
20
+ "noUnusedParameters": true,
21
+ "noFallthroughCasesInSwitch": true
22
+ },
23
+ "include": ["src"],
24
+ "references": [{ "path": "./tsconfig.node.json" }]
25
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "skipLibCheck": true,
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "allowSyntheticDefaultImports": true
8
+ },
9
+ "include": ["vite.config.ts"]
10
+ }
@@ -0,0 +1,7 @@
1
+ import { defineConfig } from "vite";
2
+ import react from "@vitejs/plugin-react-swc";
3
+
4
+ // https://vitejs.dev/config/
5
+ export default defineConfig({
6
+ plugins: [react()],
7
+ });
@@ -0,0 +1,100 @@
1
+ # Iota dApp Starter Template
2
+
3
+ This dApp was created using `@iota/create-dapp` that sets up a basic React
4
+ Client dApp using the following tools:
5
+
6
+ - [React](https://react.dev/) as the UI framework
7
+ - [TypeScript](https://www.typescriptlang.org/) for type checking
8
+ - [Vite](https://vitejs.dev/) for build tooling
9
+ - [Radix UI](https://www.radix-ui.com/) for pre-built UI components
10
+ - [ESLint](https://eslint.org/) for linting
11
+ - [`@iota/dapp-kit`]() for connecting to
12
+ wallets and loading data
13
+ - [pnpm](https://pnpm.io/) for package management
14
+
15
+ ## Deploying your Move code
16
+
17
+ ### Install Iota cli
18
+
19
+ Before deploying your move code, ensure that you have installed the Iota CLI.
20
+ You can follow the
21
+ [Iota installation instruction](https://docs.iota.io/build/install) to get
22
+ everything set up.
23
+
24
+ This template uses `devnet` by default, so we'll need to set up a devnet
25
+ environment in the CLI:
26
+
27
+ ```bash
28
+ iota client new-env --alias devnet --rpc https://fullnode.devnet.iota.io:443
29
+ iota client switch --env devnet
30
+ ```
31
+
32
+ If you haven't set up an address in the iota client yet, you can use the
33
+ following command to get a new address:
34
+
35
+ ```bash
36
+ iota client new-address secp256k1
37
+ ```
38
+
39
+ This well generate a new address and recover phrase for you. You can mark a
40
+ newly created address as you active address by running the following command
41
+ with your new address:
42
+
43
+ ```bash
44
+ iota client switch --address 0xYOUR_ADDRESS...
45
+ ```
46
+
47
+ We can ensure we have some Iota in our new wallet by requesting Iota from the
48
+ faucet (make sure to replace the address with your address):
49
+
50
+ ```bash
51
+ curl --location --request POST 'https://faucet.devnet.iota.io/gas' \
52
+ --header 'Content-Type: application/json' \
53
+ --data-raw '{
54
+ "FixedAmountRequest": {
55
+ "recipient": "<YOUR_ADDRESS>"
56
+ }
57
+ }'
58
+ ```
59
+
60
+ ### Publishing the move package
61
+
62
+ The move code for this template is located in the `move` directory. To publish
63
+ it, you can enter the `move` directory, and publish it with the Iota CLI:
64
+
65
+ ```bash
66
+ cd move
67
+ iota client publish --gas-budget 100000000 counter
68
+ ```
69
+
70
+ In the output there will be an object with a `"packageId"` property. You'll want
71
+ to save that package ID to the `src/constants.ts` file as `PACKAGE_ID`:
72
+
73
+ ```ts
74
+ export const DEVNET_COUNTER_PACKAGE_ID = "<YOUR_PACKAGE_ID>";
75
+ ```
76
+
77
+ Now that we have published the move code, and update the package ID, we can
78
+ start the app.
79
+
80
+ ## Starting your dApp
81
+
82
+ To install dependencies you can run
83
+
84
+ ```bash
85
+ pnpm install
86
+ ```
87
+
88
+ To start your dApp in development mode run
89
+
90
+ ```bash
91
+ pnpm dev
92
+ ```
93
+
94
+ ## Building
95
+
96
+ To build your app for deployment you can run
97
+
98
+ ```bash
99
+ pnpm build
100
+ ```
@@ -0,0 +1,59 @@
1
+ <!doctype html>
2
+ <html lang="en" class="dark-theme" style="color-scheme: dark">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Iota dApp Starter</title>
8
+
9
+ <style>
10
+ /*
11
+ Josh's Custom CSS Reset
12
+ https://www.joshwcomeau.com/css/custom-css-reset/
13
+ */
14
+ *,
15
+ *::before,
16
+ *::after {
17
+ box-sizing: border-box;
18
+ }
19
+ * {
20
+ margin: 0;
21
+ }
22
+ body {
23
+ line-height: 1.5;
24
+ -webkit-font-smoothing: antialiased;
25
+ }
26
+ img,
27
+ picture,
28
+ video,
29
+ canvas,
30
+ svg {
31
+ display: block;
32
+ max-width: 100%;
33
+ }
34
+ input,
35
+ button,
36
+ textarea,
37
+ select {
38
+ font: inherit;
39
+ }
40
+ p,
41
+ h1,
42
+ h2,
43
+ h3,
44
+ h4,
45
+ h5,
46
+ h6 {
47
+ overflow-wrap: break-word;
48
+ }
49
+ #root,
50
+ #__next {
51
+ isolation: isolate;
52
+ }
53
+ </style>
54
+ </head>
55
+ <body>
56
+ <div id="root"></div>
57
+ <script type="module" src="/src/main.tsx"></script>
58
+ </body>
59
+ </html>
@@ -0,0 +1,9 @@
1
+ [package]
2
+ name = "counter"
3
+ version = "0.0.1"
4
+
5
+ [dependencies]
6
+ Iota = { git = "https://github.com/iotaledger/iota.git", subdir = "crates/iota-framework/packages/iota-framework", rev = "framework/devnet" }
7
+
8
+ [addresses]
9
+ counter = "0x0"
@@ -0,0 +1,54 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // Modifications Copyright (c) 2024 IOTA Stiftung
3
+ // SPDX-License-Identifier: Apache-2.0
4
+
5
+ /// This example demonstrates a basic use of a shared object.
6
+ /// Rules:
7
+ /// - anyone can create and share a counter
8
+ /// - everyone can increment a counter by 1
9
+ /// - the owner of the counter can reset it to any value
10
+ module counter::counter {
11
+ use iota::transfer;
12
+ use iota::object::{Self, UID};
13
+ use iota::tx_context::{Self, TxContext};
14
+
15
+ /// A shared counter.
16
+ struct Counter has key {
17
+ id: UID,
18
+ owner: address,
19
+ value: u64
20
+ }
21
+
22
+ public fun owner(counter: &Counter): address {
23
+ counter.owner
24
+ }
25
+
26
+ public fun value(counter: &Counter): u64 {
27
+ counter.value
28
+ }
29
+
30
+ /// Create and share a Counter object.
31
+ public fun create(ctx: &mut TxContext) {
32
+ transfer::share_object(Counter {
33
+ id: object::new(ctx),
34
+ owner: tx_context::sender(ctx),
35
+ value: 0
36
+ })
37
+ }
38
+
39
+ /// Increment a counter by 1.
40
+ public fun increment(counter: &mut Counter) {
41
+ counter.value = counter.value + 1;
42
+ }
43
+
44
+ /// Set value (only runnable by the Counter owner)
45
+ public fun set_value(counter: &mut Counter, value: u64, ctx: &TxContext) {
46
+ assert!(counter.owner == tx_context::sender(ctx), 0);
47
+ counter.value = value;
48
+ }
49
+
50
+ /// Assert a value for the counter.
51
+ public fun assert_value(counter: &Counter, value: u64) {
52
+ assert!(counter.value == value, 0)
53
+ }
54
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@iota/template-react-e2e-coinflip",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc && vite build",
9
+ "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10
+ "preview": "vite preview"
11
+ },
12
+ "dependencies": {
13
+ "@iota/dapp-kit": "workspace:*",
14
+ "@iota/iota-sdk": "workspace:*",
15
+ "@radix-ui/colors": "^3.0.0",
16
+ "@radix-ui/react-icons": "^1.3.0",
17
+ "@radix-ui/themes": "^2.0.0",
18
+ "@tanstack/react-query": "^5.0.0",
19
+ "react": "^18.3.1",
20
+ "react-dom": "^18.3.1"
21
+ },
22
+ "devDependencies": {
23
+ "@types/react": "^18.3.3",
24
+ "@types/react-dom": "^18.2.7",
25
+ "@typescript-eslint/eslint-plugin": "^6.1.0",
26
+ "@typescript-eslint/parser": "^6.1.0",
27
+ "@vitejs/plugin-react-swc": "^3.3.2",
28
+ "eslint": "^8.45.0",
29
+ "eslint-plugin-react-hooks": "^4.6.0",
30
+ "eslint-plugin-react-refresh": "^0.4.3",
31
+ "prettier": "^3.0.0",
32
+ "typescript": "^5.3.3",
33
+ "vite": "^4.4.4"
34
+ }
35
+ }
@@ -0,0 +1,4 @@
1
+ // eslint-disable-next-line no-undef
2
+ module.exports = {
3
+ proseWrap: "always",
4
+ };
@@ -0,0 +1,64 @@
1
+ // Copyright (c) 2024 IOTA Stiftung
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { ConnectButton, useCurrentAccount } from "@iota/dapp-kit";
5
+ import { isValidIotaObjectId } from "@iota/iota-sdk/utils";
6
+ import { Box, Container, Flex, Heading } from "@radix-ui/themes";
7
+ import { useState } from "react";
8
+ import { Counter } from "./Counter";
9
+ import { CreateCounter } from "./CreateCounter";
10
+
11
+ function App() {
12
+ const currentAccount = useCurrentAccount();
13
+ const [counterId, setCounter] = useState(() => {
14
+ const hash = window.location.hash.slice(1);
15
+ return isValidIotaObjectId(hash) ? hash : null;
16
+ });
17
+
18
+ return (
19
+ <>
20
+ <Flex
21
+ position="sticky"
22
+ px="4"
23
+ py="2"
24
+ justify="between"
25
+ style={{
26
+ borderBottom: "1px solid var(--gray-a2)",
27
+ }}
28
+ >
29
+ <Box>
30
+ <Heading>dApp Starter Template</Heading>
31
+ </Box>
32
+
33
+ <Box>
34
+ <ConnectButton />
35
+ </Box>
36
+ </Flex>
37
+ <Container>
38
+ <Container
39
+ mt="5"
40
+ pt="2"
41
+ px="4"
42
+ style={{ background: "var(--gray-a2)", minHeight: 500 }}
43
+ >
44
+ {currentAccount ? (
45
+ counterId ? (
46
+ <Counter id={counterId} />
47
+ ) : (
48
+ <CreateCounter
49
+ onCreated={(id) => {
50
+ window.location.hash = id;
51
+ setCounter(id);
52
+ }}
53
+ />
54
+ )
55
+ ) : (
56
+ <Heading>Please connect your wallet</Heading>
57
+ )}
58
+ </Container>
59
+ </Container>
60
+ </>
61
+ );
62
+ }
63
+
64
+ export default App;
@@ -0,0 +1,94 @@
1
+ // Copyright (c) 2024 IOTA Stiftung
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import {
5
+ useCurrentAccount,
6
+ useSignAndExecuteTransactionBlock,
7
+ useIotaClient,
8
+ useIotaClientQuery,
9
+ } from "@iota/dapp-kit";
10
+ import type { IotaObjectData } from "@iota/iota-sdk/client";
11
+ import { TransactionBlock } from "@iota/iota-sdk/transactions";
12
+ import { Button, Flex, Heading, Text } from "@radix-ui/themes";
13
+ import { useNetworkVariable } from "./networkConfig";
14
+
15
+ export function Counter({ id }: { id: string }) {
16
+ const client = useIotaClient();
17
+ const currentAccount = useCurrentAccount();
18
+ const counterPackageId = useNetworkVariable("counterPackageId");
19
+ const { mutate: signAndExecute } = useSignAndExecuteTransactionBlock();
20
+ const { data, isPending, error, refetch } = useIotaClientQuery("getObject", {
21
+ id,
22
+ options: {
23
+ showContent: true,
24
+ showOwner: true,
25
+ },
26
+ });
27
+
28
+ const executeMoveCall = (method: "increment" | "reset") => {
29
+ const txb = new TransactionBlock();
30
+
31
+ if (method === "reset") {
32
+ txb.moveCall({
33
+ arguments: [txb.object(id), txb.pure.u64(0)],
34
+ target: `${counterPackageId}::counter::set_value`,
35
+ });
36
+ } else {
37
+ txb.moveCall({
38
+ arguments: [txb.object(id)],
39
+ target: `${counterPackageId}::counter::increment`,
40
+ });
41
+ }
42
+
43
+ signAndExecute(
44
+ {
45
+ transactionBlock: txb,
46
+ options: {
47
+ showEffects: true,
48
+ showObjectChanges: true,
49
+ },
50
+ },
51
+ {
52
+ onSuccess: (tx) => {
53
+ client.waitForTransactionBlock({ digest: tx.digest }).then(() => {
54
+ refetch();
55
+ });
56
+ },
57
+ },
58
+ );
59
+ };
60
+
61
+ if (isPending) return <Text>Loading...</Text>;
62
+
63
+ if (error) return <Text>Error: {error.message}</Text>;
64
+
65
+ if (!data.data) return <Text>Not found</Text>;
66
+
67
+ const ownedByCurrentAccount =
68
+ getCounterFields(data.data)?.owner === currentAccount?.address;
69
+
70
+ return (
71
+ <>
72
+ <Heading size="3">Counter {id}</Heading>
73
+
74
+ <Flex direction="column" gap="2">
75
+ <Text>Count: {getCounterFields(data.data)?.value}</Text>
76
+ <Flex direction="row" gap="2">
77
+ <Button onClick={() => executeMoveCall("increment")}>
78
+ Increment
79
+ </Button>
80
+ {ownedByCurrentAccount ? (
81
+ <Button onClick={() => executeMoveCall("reset")}>Reset</Button>
82
+ ) : null}
83
+ </Flex>
84
+ </Flex>
85
+ </>
86
+ );
87
+ }
88
+ function getCounterFields(data: IotaObjectData) {
89
+ if (data.content?.dataType !== "moveObject") {
90
+ return null;
91
+ }
92
+
93
+ return data.content.fields as { value: number; owner: string };
94
+ }