@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.
- package/CHANGELOG.md +1159 -0
- package/README.md +33 -0
- package/bin/index.js +4 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +102 -0
- package/dist/index.js.map +1 -0
- package/package.json +52 -0
- package/src/index.ts +130 -0
- package/templates/react-client-dapp/README.md +35 -0
- package/templates/react-client-dapp/index.html +59 -0
- package/templates/react-client-dapp/package.json +35 -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.mts +7 -0
- package/templates/react-e2e-counter/README.md +92 -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 +36 -0
- package/templates/react-e2e-counter/package.json +36 -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 +106 -0
- package/templates/react-e2e-counter/src/CreateCounter.tsx +60 -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.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
package/dist/index.d.ts
ADDED
|
@@ -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,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
|
+
}
|