@haneullabs/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 +1159 -0
  2. package/README.md +33 -0
  3. package/bin/index.js +4 -0
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.js +102 -0
  6. package/dist/index.js.map +1 -0
  7. package/package.json +52 -0
  8. package/src/index.ts +130 -0
  9. package/templates/react-client-dapp/README.md +35 -0
  10. package/templates/react-client-dapp/index.html +59 -0
  11. package/templates/react-client-dapp/package.json +35 -0
  12. package/templates/react-client-dapp/prettier.config.cjs +4 -0
  13. package/templates/react-client-dapp/src/App.tsx +39 -0
  14. package/templates/react-client-dapp/src/OwnedObjects.tsx +42 -0
  15. package/templates/react-client-dapp/src/WalletStatus.tsx +23 -0
  16. package/templates/react-client-dapp/src/main.tsx +26 -0
  17. package/templates/react-client-dapp/src/networkConfig.ts +17 -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.mts +7 -0
  22. package/templates/react-e2e-counter/README.md +92 -0
  23. package/templates/react-e2e-counter/index.html +59 -0
  24. package/templates/react-e2e-counter/move/counter/Move.toml +10 -0
  25. package/templates/react-e2e-counter/move/counter/sources/counter.move +36 -0
  26. package/templates/react-e2e-counter/package.json +36 -0
  27. package/templates/react-e2e-counter/prettier.config.cjs +4 -0
  28. package/templates/react-e2e-counter/src/App.tsx +61 -0
  29. package/templates/react-e2e-counter/src/Counter.tsx +106 -0
  30. package/templates/react-e2e-counter/src/CreateCounter.tsx +60 -0
  31. package/templates/react-e2e-counter/src/constants.ts +3 -0
  32. package/templates/react-e2e-counter/src/main.tsx +26 -0
  33. package/templates/react-e2e-counter/src/networkConfig.ts +31 -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.mts +7 -0
package/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # @haneullabs/create-dapp
2
+
3
+ `@haneullabs/create-dapp` is a CLI tool that helps you to create a new dApp project.
4
+
5
+ You can get started quickly by running the following command:
6
+
7
+ ```bash
8
+ pnpm create @haneullabs/dapp
9
+ ```
10
+
11
+ This will prompt you through creating a new dApp project. It will ask you for the name/directory and
12
+ ask you to select from one of the provided templates.
13
+
14
+ ## Templates
15
+
16
+ The following templates are available:
17
+
18
+ - `react-client-dapp`: A basic React dApp that fetches a list of objects owned by the connected
19
+ wallet
20
+ - `react-e2e-counter`: An end to end Example with move code and UI for a simple counter app
21
+
22
+ The examples are based off the Vite TypeScript starter project, and pre-configure a few things for
23
+ you including:
24
+
25
+ - [React](https://react.dev/)
26
+ - [TypeScript](https://www.typescriptlang.org/)
27
+ - [Vite](https://vitejs.dev/)
28
+ - [Radix UI](https://www.radix-ui.com/)
29
+ - [ESLint](https://eslint.org/)
30
+ - [`@haneullabs/dapp-kit`](https://sdk.haneullabs.com/dapp-kit)
31
+
32
+ These templates are still new, and would love to get feedback and suggestions for improvements or
33
+ future templates. Please open an issue on GitHub if you have any feedback.
package/bin/index.js ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ // Copyright (c) Mysten Labs, Inc.
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ require('../dist/index.js');
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ // Copyright (c) Mysten Labs, Inc.
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ /* eslint-disable @typescript-eslint/ban-types */
5
+ /* eslint-disable no-restricted-globals */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const fs_1 = require("fs");
8
+ const promises_1 = require("fs/promises");
9
+ const path_1 = require("path");
10
+ const util_1 = require("util");
11
+ const enquirer_1 = require("enquirer");
12
+ const { values: args } = (0, util_1.parseArgs)({
13
+ options: {
14
+ template: {
15
+ type: 'string',
16
+ default: '',
17
+ short: 't',
18
+ },
19
+ },
20
+ });
21
+ async function main() {
22
+ const results = await (0, enquirer_1.prompt)([
23
+ {
24
+ type: 'select',
25
+ name: 'template',
26
+ message: 'Which starter template would you like to use?',
27
+ choices: [
28
+ {
29
+ name: 'react-client-dapp',
30
+ hint: 'React Client dApp that reads data from wallet and the blockchain',
31
+ },
32
+ {
33
+ name: 'react-e2e-counter',
34
+ hint: 'React dApp with a move smart contract that implements a distributed counter',
35
+ },
36
+ ],
37
+ },
38
+ {
39
+ type: 'input',
40
+ name: 'dAppName',
41
+ message: 'What is the name of your dApp? (this will be used as the directory name)',
42
+ initial: 'my-first-haneul-dapp',
43
+ },
44
+ ].filter((question) => !args[question.name]));
45
+ const outDir = (0, path_1.resolve)(process.cwd(), results.dAppName);
46
+ if ((0, fs_1.existsSync)(outDir)) {
47
+ throw new Error(`Directory ${outDir} already exists`);
48
+ }
49
+ const files = await collectFiles(results.template ?? args.template, results.dAppName);
50
+ await writeFiles(files, outDir);
51
+ }
52
+ main();
53
+ async function collectFiles(template, dAppName) {
54
+ const dependencies = await getDependencyVersions();
55
+ const templateDir = (0, path_1.resolve)(__dirname, '../templates', template);
56
+ const files = new Array();
57
+ if (!(0, fs_1.statSync)(templateDir).isDirectory()) {
58
+ throw new Error(`Template ${templateDir} could not be found`);
59
+ }
60
+ await addDir(templateDir);
61
+ return files;
62
+ async function addDir(dir) {
63
+ const entries = await (0, promises_1.readdir)(dir);
64
+ for (const entry of entries) {
65
+ if (entry === 'node_modules') {
66
+ continue;
67
+ }
68
+ const entryPath = (0, path_1.resolve)(dir, entry);
69
+ const stat = (0, fs_1.statSync)(entryPath);
70
+ if (stat.isDirectory()) {
71
+ await addDir(entryPath);
72
+ }
73
+ else {
74
+ let content = await (0, promises_1.readFile)(entryPath);
75
+ if (entry === 'package.json') {
76
+ const json = JSON.parse(content.toString());
77
+ json.name = dAppName;
78
+ json.dependencies['@haneullabs/haneul'] = dependencies['@haneullabs/haneul'];
79
+ json.dependencies['@haneullabs/dapp-kit'] = dependencies['@haneullabs/dapp-kit'];
80
+ content = Buffer.from(JSON.stringify(json, null, 2));
81
+ }
82
+ files.push({ path: (0, path_1.relative)(templateDir, entryPath), content });
83
+ }
84
+ }
85
+ }
86
+ }
87
+ async function writeFiles(files, outDir) {
88
+ for (const file of files) {
89
+ const filePath = (0, path_1.resolve)(outDir, file.path);
90
+ const dirPath = (0, path_1.resolve)(filePath, '..');
91
+ if (!(0, fs_1.existsSync)(dirPath)) {
92
+ await (0, promises_1.mkdir)(dirPath, { recursive: true });
93
+ }
94
+ await (0, promises_1.writeFile)(filePath, file.content);
95
+ }
96
+ }
97
+ async function getDependencyVersions() {
98
+ const packagePath = (0, path_1.resolve)(__dirname, '../package.json');
99
+ const content = JSON.parse(await (0, promises_1.readFile)(packagePath, 'utf-8'));
100
+ return content.dependencies;
101
+ }
102
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@haneullabs/create-dapp",
3
+ "author": "Haneul Labs <build@haneullabs.com>",
4
+ "description": "A CLI for creating new Haneul dApps",
5
+ "homepage": "https://sdk.haneullabs.com",
6
+ "version": "0.1.0",
7
+ "license": "Apache-2.0",
8
+ "files": [
9
+ "CHANGELOG.md",
10
+ "LICENSE",
11
+ "README.md",
12
+ "dist",
13
+ "src",
14
+ "templates"
15
+ ],
16
+ "type": "commonjs",
17
+ "main": "./dist/cjs/index.js",
18
+ "module": "./dist/esm/index.js",
19
+ "types": "./dist/cjs/index.d.ts",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/GeunhwaJeong/haneul-ts-sdks.git"
23
+ },
24
+ "bugs": {
25
+ "url": "https://github.com/GeunhwaJeong/haneul-ts-sdks/issues/new"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "devDependencies": {
31
+ "typescript": "^5.9.3",
32
+ "@haneullabs/build-scripts": "0.1.0"
33
+ },
34
+ "dependencies": {
35
+ "@types/node": "^24.10.1",
36
+ "enquirer": "^2.4.1",
37
+ "@haneullabs/dapp-kit": "0.1.0",
38
+ "@haneullabs/haneul": "0.1.0"
39
+ },
40
+ "sideEffects": false,
41
+ "bin": "./bin/index.js",
42
+ "scripts": {
43
+ "clean": "rm -rf tsconfig.tsbuildinfo ./dist",
44
+ "build": "tsc --build",
45
+ "prettier:check": "prettier -c --ignore-unknown .",
46
+ "prettier:fix": "prettier -w --ignore-unknown .",
47
+ "eslint:check": "eslint --max-warnings=0 .",
48
+ "eslint:fix": "pnpm run eslint:check --fix",
49
+ "lint": "pnpm run eslint:check && pnpm run prettier:check",
50
+ "lint:fix": "pnpm run eslint:fix && pnpm run prettier:fix"
51
+ }
52
+ }
package/src/index.ts ADDED
@@ -0,0 +1,130 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /* eslint-disable @typescript-eslint/ban-types */
4
+ /* eslint-disable no-restricted-globals */
5
+
6
+ import { existsSync, statSync } from 'fs';
7
+ import { mkdir, readdir, readFile, writeFile } from 'fs/promises';
8
+ import { relative, resolve } from 'path';
9
+ import { parseArgs } from 'util';
10
+ import { prompt } from 'enquirer';
11
+
12
+ const { values: args } = parseArgs({
13
+ options: {
14
+ template: {
15
+ type: 'string',
16
+ default: '',
17
+ short: 't',
18
+ },
19
+ },
20
+ });
21
+
22
+ async function main() {
23
+ const results = await prompt<{
24
+ template: string;
25
+ dAppName: string;
26
+ }>(
27
+ [
28
+ {
29
+ type: 'select',
30
+ name: 'template',
31
+ message: 'Which starter template would you like to use?',
32
+ choices: [
33
+ {
34
+ name: 'react-client-dapp',
35
+ hint: 'React Client dApp that reads data from wallet and the blockchain',
36
+ },
37
+ {
38
+ name: 'react-e2e-counter',
39
+ hint: 'React dApp with a move smart contract that implements a distributed counter',
40
+ },
41
+ ],
42
+ },
43
+ {
44
+ type: 'input',
45
+
46
+ name: 'dAppName',
47
+ message: 'What is the name of your dApp? (this will be used as the directory name)',
48
+ initial: 'my-first-haneul-dapp',
49
+ },
50
+ ].filter((question) => !args[question.name as 'template']),
51
+ );
52
+
53
+ const outDir = resolve(process.cwd(), results.dAppName);
54
+
55
+ if (existsSync(outDir)) {
56
+ throw new Error(`Directory ${outDir} already exists`);
57
+ }
58
+
59
+ const files = await collectFiles(results.template ?? args.template, results.dAppName);
60
+ await writeFiles(files, outDir);
61
+ }
62
+
63
+ main();
64
+
65
+ async function collectFiles(template: string, dAppName: string) {
66
+ const dependencies = await getDependencyVersions();
67
+ const templateDir = resolve(__dirname, '../templates', template);
68
+ const files = new Array<{
69
+ path: string;
70
+ content: Buffer;
71
+ }>();
72
+
73
+ if (!statSync(templateDir).isDirectory()) {
74
+ throw new Error(`Template ${templateDir} could not be found`);
75
+ }
76
+
77
+ await addDir(templateDir);
78
+
79
+ return files;
80
+
81
+ async function addDir(dir: string) {
82
+ const entries = await readdir(dir);
83
+
84
+ for (const entry of entries) {
85
+ if (entry === 'node_modules') {
86
+ continue;
87
+ }
88
+ const entryPath = resolve(dir, entry);
89
+ const stat = statSync(entryPath);
90
+
91
+ if (stat.isDirectory()) {
92
+ await addDir(entryPath);
93
+ } else {
94
+ let content = await readFile(entryPath);
95
+
96
+ if (entry === 'package.json') {
97
+ const json = JSON.parse(content.toString());
98
+ json.name = dAppName;
99
+ json.dependencies['@haneullabs/haneul'] = dependencies['@haneullabs/haneul'];
100
+ json.dependencies['@haneullabs/dapp-kit'] = dependencies['@haneullabs/dapp-kit'];
101
+
102
+ content = Buffer.from(JSON.stringify(json, null, 2));
103
+ }
104
+
105
+ files.push({ path: relative(templateDir, entryPath), content });
106
+ }
107
+ }
108
+ }
109
+ }
110
+
111
+ async function writeFiles(files: Array<{ path: string; content: Buffer }>, outDir: string) {
112
+ for (const file of files) {
113
+ const filePath = resolve(outDir, file.path);
114
+ const dirPath = resolve(filePath, '..');
115
+ if (!existsSync(dirPath)) {
116
+ await mkdir(dirPath, { recursive: true });
117
+ }
118
+
119
+ await writeFile(filePath, file.content);
120
+ }
121
+ }
122
+
123
+ async function getDependencyVersions() {
124
+ const packagePath = resolve(__dirname, '../package.json');
125
+ const content = JSON.parse(await readFile(packagePath, 'utf-8')) as {
126
+ dependencies: Record<string, string>;
127
+ };
128
+
129
+ return content.dependencies;
130
+ }
@@ -0,0 +1,35 @@
1
+ # Sui dApp Starter Template
2
+
3
+ This dApp was created using `@haneullabs/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
+ - [`@haneullabs/dapp-kit`](https://sdk.haneullabs.com/dapp-kit) for connecting to
12
+ 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,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>Sui 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,35 @@
1
+ {
2
+ "name": "@haneullabs/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
+ "@haneullabs/dapp-kit": "workspace:*",
14
+ "@haneullabs/haneul": "workspace:*",
15
+ "@radix-ui/colors": "^3.0.0",
16
+ "@radix-ui/react-icons": "^1.3.0",
17
+ "@radix-ui/themes": "^3.2.1",
18
+ "@tanstack/react-query": "^5.90.11",
19
+ "react": "^19.2.1",
20
+ "react-dom": "^19.2.1"
21
+ },
22
+ "devDependencies": {
23
+ "@types/react": "^19.2.7",
24
+ "@types/react-dom": "^19.2.3",
25
+ "@typescript-eslint/eslint-plugin": "^8.48.1",
26
+ "@typescript-eslint/parser": "^8.48.1",
27
+ "@vitejs/plugin-react-swc": "^4.2.2",
28
+ "eslint": "^9.17.0",
29
+ "eslint-plugin-react-hooks": "^5.2.0",
30
+ "eslint-plugin-react-refresh": "^0.4.20",
31
+ "prettier": "^3.7.3",
32
+ "typescript": "^5.9.3",
33
+ "vite": "^7.2.6"
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 "@haneullabs/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, useHaneulClientQuery } from "@haneullabs/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 } = useHaneulClientQuery(
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 "@haneullabs/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 "@haneullabs/dapp-kit/dist/index.css";
4
+ import "@radix-ui/themes/styles.css";
5
+
6
+ import { HaneulClientProvider, WalletProvider } from "@haneullabs/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
+ <HaneulClientProvider networks={networkConfig} defaultNetwork="testnet">
19
+ <WalletProvider autoConnect>
20
+ <App />
21
+ </WalletProvider>
22
+ </HaneulClientProvider>
23
+ </QueryClientProvider>
24
+ </Theme>
25
+ </React.StrictMode>,
26
+ );
@@ -0,0 +1,17 @@
1
+ import { getFullnodeUrl } from "@haneullabs/haneul/client";
2
+ import { createNetworkConfig } from "@haneullabs/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,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "skipLibCheck": true,
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "allowSyntheticDefaultImports": true
8
+ },
9
+ "include": ["vite.config.mts"]
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
+ });