@iota/create-dapp 0.0.0-dev-20251015065834
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.
- package/CHANGELOG.md +207 -0
- package/LICENSE +201 -0
- package/README.md +35 -0
- package/bin/index.js +7 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +103 -0
- package/dist/index.js.map +1 -0
- package/package.json +45 -0
- package/src/index.ts +131 -0
- package/templates/react-client-dapp/README.md +35 -0
- package/templates/react-client-dapp/eslint.config.js +28 -0
- package/templates/react-client-dapp/index.html +59 -0
- package/templates/react-client-dapp/package.json +37 -0
- package/templates/react-client-dapp/prettier.config.cjs +4 -0
- package/templates/react-client-dapp/src/App.tsx +39 -0
- package/templates/react-client-dapp/src/OwnedObjects.tsx +42 -0
- package/templates/react-client-dapp/src/WalletStatus.tsx +23 -0
- package/templates/react-client-dapp/src/main.tsx +26 -0
- package/templates/react-client-dapp/src/networkConfig.ts +17 -0
- package/templates/react-client-dapp/src/vite-env.d.ts +1 -0
- package/templates/react-client-dapp/tsconfig.json +25 -0
- package/templates/react-client-dapp/tsconfig.node.json +10 -0
- package/templates/react-client-dapp/vite.config.ts +7 -0
- package/templates/react-e2e-counter/README.md +100 -0
- package/templates/react-e2e-counter/eslint.config.js +28 -0
- package/templates/react-e2e-counter/index.html +59 -0
- package/templates/react-e2e-counter/move/counter/Move.toml +10 -0
- package/templates/react-e2e-counter/move/counter/sources/counter.move +37 -0
- package/templates/react-e2e-counter/package.json +38 -0
- package/templates/react-e2e-counter/prettier.config.cjs +4 -0
- package/templates/react-e2e-counter/src/App.tsx +61 -0
- package/templates/react-e2e-counter/src/Counter.tsx +108 -0
- package/templates/react-e2e-counter/src/CreateCounter.tsx +65 -0
- package/templates/react-e2e-counter/src/constants.ts +3 -0
- package/templates/react-e2e-counter/src/main.tsx +26 -0
- package/templates/react-e2e-counter/src/networkConfig.ts +31 -0
- package/templates/react-e2e-counter/src/vite-env.d.ts +1 -0
- package/templates/react-e2e-counter/tsconfig.json +25 -0
- package/templates/react-e2e-counter/tsconfig.node.json +10 -0
- package/templates/react-e2e-counter/vite.config.ts +7 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// Copyright (c) Mysten Labs, Inc.
|
|
2
|
+
// Modifications Copyright (c) 2024 IOTA Stiftung
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
/* eslint-disable @typescript-eslint/ban-types */
|
|
5
|
+
/* eslint-disable no-restricted-globals */
|
|
6
|
+
|
|
7
|
+
import { existsSync, statSync } from 'fs';
|
|
8
|
+
import { mkdir, readdir, readFile, writeFile } from 'fs/promises';
|
|
9
|
+
import { relative, resolve } from 'path';
|
|
10
|
+
import { parseArgs } from 'util';
|
|
11
|
+
import { prompt } from 'enquirer';
|
|
12
|
+
|
|
13
|
+
const { values: args } = parseArgs({
|
|
14
|
+
options: {
|
|
15
|
+
template: {
|
|
16
|
+
type: 'string',
|
|
17
|
+
default: '',
|
|
18
|
+
short: 't',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
async function main() {
|
|
24
|
+
const results = await prompt<{
|
|
25
|
+
template: string;
|
|
26
|
+
dAppName: string;
|
|
27
|
+
}>(
|
|
28
|
+
[
|
|
29
|
+
{
|
|
30
|
+
type: 'select',
|
|
31
|
+
name: 'template',
|
|
32
|
+
message: 'Which starter template would you like to use?',
|
|
33
|
+
choices: [
|
|
34
|
+
{
|
|
35
|
+
name: 'react-client-dapp',
|
|
36
|
+
hint: 'React Client dApp that reads data from wallet and the blockchain',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'react-e2e-counter',
|
|
40
|
+
hint: 'React dApp with a move smart contract that implements a distributed counter',
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
type: 'input',
|
|
46
|
+
|
|
47
|
+
name: 'dAppName',
|
|
48
|
+
message: 'What is the name of your dApp? (this will be used as the directory name)',
|
|
49
|
+
initial: 'my-first-iota-dapp',
|
|
50
|
+
},
|
|
51
|
+
].filter((question) => !args[question.name as 'template']),
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const outDir = resolve(process.cwd(), results.dAppName);
|
|
55
|
+
|
|
56
|
+
if (existsSync(outDir)) {
|
|
57
|
+
throw new Error(`Directory ${outDir} already exists`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const files = await collectFiles(results.template ?? args.template, results.dAppName);
|
|
61
|
+
await writeFiles(files, outDir);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
main();
|
|
65
|
+
|
|
66
|
+
async function collectFiles(template: string, dAppName: string) {
|
|
67
|
+
const dependencies = await getDependencyVersions();
|
|
68
|
+
const templateDir = resolve(__dirname, '../templates', template);
|
|
69
|
+
const files = new Array<{
|
|
70
|
+
path: string;
|
|
71
|
+
content: Buffer;
|
|
72
|
+
}>();
|
|
73
|
+
|
|
74
|
+
if (!statSync(templateDir).isDirectory()) {
|
|
75
|
+
throw new Error(`Template ${templateDir} could not be found`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
await addDir(templateDir);
|
|
79
|
+
|
|
80
|
+
return files;
|
|
81
|
+
|
|
82
|
+
async function addDir(dir: string) {
|
|
83
|
+
const entries = await readdir(dir);
|
|
84
|
+
|
|
85
|
+
for (const entry of entries) {
|
|
86
|
+
if (entry === 'node_modules') {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const entryPath = resolve(dir, entry);
|
|
90
|
+
const stat = statSync(entryPath);
|
|
91
|
+
|
|
92
|
+
if (stat.isDirectory()) {
|
|
93
|
+
await addDir(entryPath);
|
|
94
|
+
} else {
|
|
95
|
+
let content = await readFile(entryPath);
|
|
96
|
+
|
|
97
|
+
if (entry === 'package.json') {
|
|
98
|
+
const json = JSON.parse(content.toString());
|
|
99
|
+
json.name = dAppName;
|
|
100
|
+
json.dependencies['@iota/iota-sdk'] = dependencies['@iota/iota-sdk'];
|
|
101
|
+
json.dependencies['@iota/dapp-kit'] = dependencies['@iota/dapp-kit'];
|
|
102
|
+
|
|
103
|
+
content = Buffer.from(JSON.stringify(json, null, 2));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
files.push({ path: relative(templateDir, entryPath), content });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function writeFiles(files: Array<{ path: string; content: Buffer }>, outDir: string) {
|
|
113
|
+
for (const file of files) {
|
|
114
|
+
const filePath = resolve(outDir, file.path);
|
|
115
|
+
const dirPath = resolve(filePath, '..');
|
|
116
|
+
if (!existsSync(dirPath)) {
|
|
117
|
+
await mkdir(dirPath, { recursive: true });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
await writeFile(filePath, file.content);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function getDependencyVersions() {
|
|
125
|
+
const packagePath = resolve(__dirname, '../package.json');
|
|
126
|
+
const content = JSON.parse(await readFile(packagePath, 'utf-8')) as {
|
|
127
|
+
dependencies: Record<string, string>;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
return content.dependencies;
|
|
131
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
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/)
|
|
11
|
+
- [`@iota/dapp-kit`](https://docs.iota.org/developer/ts-sdk/dapp-kit) for
|
|
12
|
+
connecting to wallets and loading data
|
|
13
|
+
- [pnpm](https://pnpm.io/) for package management
|
|
14
|
+
|
|
15
|
+
## Starting your dApp
|
|
16
|
+
|
|
17
|
+
To install dependencies you can run
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm install
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
To start your dApp in development mode run
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm dev
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Building
|
|
30
|
+
|
|
31
|
+
To build your app for deployment you can run
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pnpm build
|
|
35
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import eslint from "@eslint/js";
|
|
2
|
+
import tseslint from "typescript-eslint";
|
|
3
|
+
import react from "eslint-plugin-react";
|
|
4
|
+
import reactHooks from "eslint-plugin-react-hooks";
|
|
5
|
+
import reactRefresh from "eslint-plugin-react-refresh";
|
|
6
|
+
|
|
7
|
+
export default tseslint.config(
|
|
8
|
+
{
|
|
9
|
+
files: ["**/*.{js,jsx,mjs,cjs,ts,tsx}"],
|
|
10
|
+
plugins: {
|
|
11
|
+
react,
|
|
12
|
+
reactHooks,
|
|
13
|
+
reactRefresh,
|
|
14
|
+
},
|
|
15
|
+
settings: {
|
|
16
|
+
version: "18",
|
|
17
|
+
},
|
|
18
|
+
languageOptions: {
|
|
19
|
+
parserOptions: {
|
|
20
|
+
ecmaFeatures: {
|
|
21
|
+
jsx: true,
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
eslint.configs.recommended,
|
|
27
|
+
tseslint.configs.recommended,
|
|
28
|
+
);
|
|
@@ -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,37 @@
|
|
|
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 .",
|
|
10
|
+
"preview": "vite preview",
|
|
11
|
+
"format": "prettier -w ."
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@iota/dapp-kit": "workspace:*",
|
|
15
|
+
"@iota/iota-sdk": "workspace:*",
|
|
16
|
+
"@radix-ui/themes": "^3.1.1",
|
|
17
|
+
"@tanstack/react-query": "^5.50.1",
|
|
18
|
+
"react": "^18.3.1",
|
|
19
|
+
"react-dom": "^18.3.1"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@eslint/js": "^9.17.0",
|
|
23
|
+
"@types/react": "^18.3.3",
|
|
24
|
+
"@types/react-dom": "^18.3.0",
|
|
25
|
+
"@typescript-eslint/eslint-plugin": "^8.18.0",
|
|
26
|
+
"@typescript-eslint/parser": "^8.18.0",
|
|
27
|
+
"@vitejs/plugin-react-swc": "^3.7.0",
|
|
28
|
+
"eslint": "^9.17.0",
|
|
29
|
+
"eslint-plugin-react-hooks": "^5.1.0",
|
|
30
|
+
"eslint-plugin-react-refresh": "^0.4.7",
|
|
31
|
+
"eslint-plugin-react": "^7.37.2",
|
|
32
|
+
"typescript-eslint": "^8.18.0",
|
|
33
|
+
"prettier": "^3.3.2",
|
|
34
|
+
"typescript": "^5.5.3",
|
|
35
|
+
"vite": "^5.4.12"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -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,26 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import ReactDOM from "react-dom/client";
|
|
3
|
+
import "@iota/dapp-kit/dist/index.css";
|
|
4
|
+
import "@radix-ui/themes/styles.css";
|
|
5
|
+
|
|
6
|
+
import { IotaClientProvider, WalletProvider } from "@iota/dapp-kit";
|
|
7
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
8
|
+
import { Theme } from "@radix-ui/themes";
|
|
9
|
+
import App from "./App.tsx";
|
|
10
|
+
import { networkConfig } from "./networkConfig.ts";
|
|
11
|
+
|
|
12
|
+
const queryClient = new QueryClient();
|
|
13
|
+
|
|
14
|
+
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
15
|
+
<React.StrictMode>
|
|
16
|
+
<Theme appearance="dark">
|
|
17
|
+
<QueryClientProvider client={queryClient}>
|
|
18
|
+
<IotaClientProvider networks={networkConfig} defaultNetwork="testnet">
|
|
19
|
+
<WalletProvider autoConnect>
|
|
20
|
+
<App />
|
|
21
|
+
</WalletProvider>
|
|
22
|
+
</IotaClientProvider>
|
|
23
|
+
</QueryClientProvider>
|
|
24
|
+
</Theme>
|
|
25
|
+
</React.StrictMode>,
|
|
26
|
+
);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getFullnodeUrl } from "@iota/iota-sdk/client";
|
|
2
|
+
import { createNetworkConfig } from "@iota/dapp-kit";
|
|
3
|
+
|
|
4
|
+
const { networkConfig, useNetworkVariable, useNetworkVariables } =
|
|
5
|
+
createNetworkConfig({
|
|
6
|
+
devnet: {
|
|
7
|
+
url: getFullnodeUrl("devnet"),
|
|
8
|
+
},
|
|
9
|
+
testnet: {
|
|
10
|
+
url: getFullnodeUrl("testnet"),
|
|
11
|
+
},
|
|
12
|
+
mainnet: {
|
|
13
|
+
url: getFullnodeUrl("mainnet"),
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export { useNetworkVariable, useNetworkVariables, networkConfig };
|
|
@@ -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,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`](https://docs.iota.org/developer/ts-sdk/dapp-kit) for
|
|
12
|
+
connecting to 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.org/developer/getting-started/install-iota) to get
|
|
22
|
+
everything set up.
|
|
23
|
+
|
|
24
|
+
This template uses `testnet` by default, so we'll need to set up a testnet
|
|
25
|
+
environment in the CLI:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
iota client new-env --alias testnet --rpc https://fullnode.testnet.iota.org:443
|
|
29
|
+
iota client switch --env testnet
|
|
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 --key-scheme 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.testnet.iota.org/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 TESTNET_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,28 @@
|
|
|
1
|
+
import eslint from "@eslint/js";
|
|
2
|
+
import tseslint from "typescript-eslint";
|
|
3
|
+
import react from "eslint-plugin-react";
|
|
4
|
+
import reactHooks from "eslint-plugin-react-hooks";
|
|
5
|
+
import reactRefresh from "eslint-plugin-react-refresh";
|
|
6
|
+
|
|
7
|
+
export default tseslint.config(
|
|
8
|
+
{
|
|
9
|
+
files: ["**/*.{js,jsx,mjs,cjs,ts,tsx}"],
|
|
10
|
+
plugins: {
|
|
11
|
+
react,
|
|
12
|
+
reactHooks,
|
|
13
|
+
reactRefresh,
|
|
14
|
+
},
|
|
15
|
+
settings: {
|
|
16
|
+
version: "18",
|
|
17
|
+
},
|
|
18
|
+
languageOptions: {
|
|
19
|
+
parserOptions: {
|
|
20
|
+
ecmaFeatures: {
|
|
21
|
+
jsx: true,
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
eslint.configs.recommended,
|
|
27
|
+
tseslint.configs.recommended,
|
|
28
|
+
);
|
|
@@ -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>
|