@sei-js/create-sei 1.0.1 → 2.0.1
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/LICENSE +21 -0
- package/README.md +5 -4
- package/dist/extensions/precompiles/src/components/default/index.tsx +44 -51
- package/dist/main.js +112 -34
- package/dist/templates/next-template/.env.example +1 -1
- package/dist/templates/next-template/README.md +57 -27
- package/dist/templates/next-template/biome.json +3 -4
- package/dist/templates/next-template/gitignore +41 -0
- package/dist/templates/next-template/next.config.mjs +12 -6
- package/dist/templates/next-template/package.json +36 -27
- package/dist/templates/next-template/public/brand/powered-by-sei-light.png +0 -0
- package/dist/templates/next-template/public/brand/sei-lockup-light.svg +7 -0
- package/dist/templates/next-template/public/brand/sei-mark.png +0 -0
- package/dist/templates/next-template/src/app/development/page.tsx +29 -30
- package/dist/templates/next-template/src/app/favicon.ico/route.ts +3 -0
- package/dist/templates/next-template/src/app/globals.css +40 -3
- package/dist/templates/next-template/src/app/layout.tsx +14 -13
- package/dist/templates/next-template/src/app/page.tsx +3 -3
- package/dist/templates/next-template/src/app/resources/page.tsx +34 -51
- package/dist/templates/next-template/src/app/resources/resources.ts +19 -32
- package/dist/templates/next-template/src/components/default/examples.ts +4 -7
- package/dist/templates/next-template/src/components/default/index.tsx +46 -46
- package/dist/templates/next-template/src/components/index.ts +3 -3
- package/dist/templates/next-template/src/components/landing/index.tsx +19 -97
- package/dist/templates/next-template/src/components/providers/index.ts +1 -1
- package/dist/templates/next-template/src/components/providers/providers.tsx +19 -25
- package/dist/templates/next-template/src/components/shell/index.ts +1 -1
- package/dist/templates/next-template/src/components/shell/shell.tsx +14 -7
- package/dist/templates/next-template/src/theme.ts +1 -0
- package/package.json +20 -9
- package/.rsyncignore +0 -11
- package/CHANGELOG.md +0 -131
- package/biome.json +0 -8
- package/dist/extensions/precompiles/package.json +0 -44
- package/dist/main.test.js +0 -34
- package/dist/templates/next-template/public/favicon.ico +0 -0
- package/extensions/precompiles/package.json +0 -44
- package/extensions/precompiles/src/components/default/index.tsx +0 -201
- package/jest.config.js +0 -14
- package/src/main.test.ts +0 -38
- package/src/main.ts +0 -159
- package/templates/next-template/.env.example +0 -8
- package/templates/next-template/.eslintrc.json +0 -1
- package/templates/next-template/.nvmrc +0 -1
- package/templates/next-template/README.md +0 -54
- package/templates/next-template/biome.json +0 -56
- package/templates/next-template/next-env.d.ts +0 -5
- package/templates/next-template/next.config.mjs +0 -17
- package/templates/next-template/package.json +0 -46
- package/templates/next-template/postcss.config.mjs +0 -6
- package/templates/next-template/public/favicon.ico +0 -0
- package/templates/next-template/src/app/development/page.tsx +0 -260
- package/templates/next-template/src/app/globals.css +0 -16
- package/templates/next-template/src/app/layout.tsx +0 -30
- package/templates/next-template/src/app/page.tsx +0 -10
- package/templates/next-template/src/app/resources/page.tsx +0 -175
- package/templates/next-template/src/app/resources/resources.ts +0 -56
- package/templates/next-template/src/components/default/examples.ts +0 -63
- package/templates/next-template/src/components/default/index.tsx +0 -402
- package/templates/next-template/src/components/index.ts +0 -3
- package/templates/next-template/src/components/landing/index.tsx +0 -155
- package/templates/next-template/src/components/providers/index.ts +0 -1
- package/templates/next-template/src/components/providers/providers.tsx +0 -108
- package/templates/next-template/src/components/shell/index.ts +0 -1
- package/templates/next-template/src/components/shell/shell.tsx +0 -88
- package/templates/next-template/tailwind.config.js +0 -5
- package/templates/next-template/tsconfig.json +0 -27
- package/tsconfig.json +0 -19
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026 Sei Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
**Scaffold new Sei applications with pre-configured templates and tooling**
|
|
11
11
|
|
|
12
|
-
[
|
|
12
|
+
[GitHub](https://github.com/sei-protocol/sei-js) • [NPM](https://www.npmjs.com/package/@sei-js/create-sei) • [Telegram](https://t.me/+LPW_1djQwRQwMzlk)
|
|
13
13
|
|
|
14
14
|
</div>
|
|
15
15
|
|
|
@@ -19,9 +19,10 @@
|
|
|
19
19
|
npx @sei-js/create-sei app -n my-sei-app
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
Generated applications require Bun 1.3.14 or newer. Their dependency overrides and smoke coverage are Bun-specific; using npm, Yarn, or pnpm can resolve a different, unverified security graph.
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
**[sei-js.docs.sei.io/create-sei](https://sei-js.docs.sei.io/create-sei/welcome)**
|
|
24
|
+
## Release verification
|
|
26
25
|
|
|
26
|
+
Pull requests run `auto` mode in the dedicated create-sei smoke workflow. It uses a local candidate when pending Changesets computes the template's exact `@sei-js/precompiles` pin, or when a Version Packages PR has already set the local manifest to that exact version. Otherwise it validates the exact published npm version. A mismatched source tree is never relabeled as an older package.
|
|
27
27
|
|
|
28
|
+
After `@sei-js/precompiles` has been staged to npm, dispatch the same workflow in `registry` mode. Registry mode always leaves the generated manifest untouched and verifies npm resolves the exact template pin. Audit findings are report-only in registry mode; local candidate mode blocks only on high or critical findings while reporting lower severities.
|
|
@@ -1,27 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { type ReadContractParameters, formatEther } from 'viem';
|
|
11
|
-
import { useAccount, usePublicClient } from 'wagmi';
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { CodeHighlight } from "@mantine/code-highlight";
|
|
4
|
+
import { ActionIcon, Badge, Button, Card, Collapse, Container, Flex, Group, Paper, Stack, Text, ThemeIcon, Title } from "@mantine/core";
|
|
5
|
+
import { notifications } from "@mantine/notifications";
|
|
6
|
+
import { BANK_PRECOMPILE_ABI, BANK_PRECOMPILE_ADDRESS } from "@sei-js/precompiles";
|
|
7
|
+
import { IconBulb, IconChevronDown, IconChevronUp, IconCopy, IconDatabase } from "@tabler/icons-react";
|
|
8
|
+
import { useState } from "react";
|
|
9
|
+
import { usePublicClient } from "wagmi";
|
|
12
10
|
|
|
13
11
|
function Examples() {
|
|
14
|
-
const [supply, setSupply] = useState(
|
|
12
|
+
const [supply, setSupply] = useState("");
|
|
15
13
|
const [showCode, setShowCode] = useState(false);
|
|
16
14
|
|
|
17
15
|
const publicClient = usePublicClient();
|
|
18
|
-
const { address } = useAccount();
|
|
19
16
|
|
|
20
17
|
const formatLargeSeiNumber = (num: string, decimals: number): string => {
|
|
21
18
|
if (num.length > 10) {
|
|
22
19
|
return `${(Number(num) / 1000000 / 1000000000).toLocaleString(navigator.language, {
|
|
23
20
|
minimumFractionDigits: decimals,
|
|
24
|
-
maximumFractionDigits: decimals
|
|
21
|
+
maximumFractionDigits: decimals,
|
|
25
22
|
})} B`;
|
|
26
23
|
}
|
|
27
24
|
return (Number(num) / 1000000).toLocaleString(navigator.language);
|
|
@@ -31,17 +28,17 @@ function Examples() {
|
|
|
31
28
|
try {
|
|
32
29
|
navigator.clipboard.writeText(text).then(() => {
|
|
33
30
|
notifications.show({
|
|
34
|
-
title:
|
|
35
|
-
message:
|
|
36
|
-
color:
|
|
31
|
+
title: "Copied!",
|
|
32
|
+
message: "Code copied to clipboard",
|
|
33
|
+
color: "green",
|
|
37
34
|
});
|
|
38
35
|
});
|
|
39
36
|
} catch (err) {
|
|
40
|
-
console.error(
|
|
37
|
+
console.error("Failed to copy text: ", err);
|
|
41
38
|
notifications.show({
|
|
42
|
-
title:
|
|
43
|
-
message:
|
|
44
|
-
color:
|
|
39
|
+
title: "Error",
|
|
40
|
+
message: "Failed to copy to clipboard",
|
|
41
|
+
color: "red",
|
|
45
42
|
});
|
|
46
43
|
}
|
|
47
44
|
};
|
|
@@ -51,42 +48,38 @@ function Examples() {
|
|
|
51
48
|
return undefined;
|
|
52
49
|
}
|
|
53
50
|
|
|
54
|
-
const readContractParams: ReadContractParameters = {
|
|
55
|
-
abi: VIEM_BANK_PRECOMPILE_ABI,
|
|
56
|
-
address: BANK_PRECOMPILE_ADDRESS,
|
|
57
|
-
functionName: 'supply',
|
|
58
|
-
args: ['usei']
|
|
59
|
-
};
|
|
60
|
-
|
|
61
51
|
try {
|
|
62
|
-
const result = await publicClient.readContract(
|
|
63
|
-
|
|
52
|
+
const result = await publicClient.readContract({
|
|
53
|
+
abi: BANK_PRECOMPILE_ABI,
|
|
54
|
+
address: BANK_PRECOMPILE_ADDRESS,
|
|
55
|
+
functionName: "supply",
|
|
56
|
+
args: ["usei"],
|
|
57
|
+
});
|
|
58
|
+
setSupply(result.toString());
|
|
64
59
|
} catch (error) {
|
|
65
|
-
console.error(
|
|
60
|
+
console.error("Error fetching supply:", error);
|
|
66
61
|
notifications.show({
|
|
67
|
-
title:
|
|
68
|
-
message:
|
|
69
|
-
color:
|
|
62
|
+
title: "Error",
|
|
63
|
+
message: "Failed to fetch SEI supply",
|
|
64
|
+
color: "red",
|
|
70
65
|
});
|
|
71
66
|
}
|
|
72
67
|
};
|
|
73
68
|
|
|
74
69
|
const codeExample = `// Sei Precompile Example - Bank Precompile
|
|
75
|
-
import {
|
|
70
|
+
import { BANK_PRECOMPILE_ABI, BANK_PRECOMPILE_ADDRESS } from '@sei-js/precompiles';
|
|
76
71
|
import { usePublicClient } from 'wagmi';
|
|
77
72
|
|
|
78
73
|
const publicClient = usePublicClient();
|
|
79
74
|
|
|
80
75
|
const getSeiSupply = async () => {
|
|
81
|
-
const
|
|
82
|
-
abi:
|
|
76
|
+
const result = await publicClient?.readContract({
|
|
77
|
+
abi: BANK_PRECOMPILE_ABI,
|
|
83
78
|
address: BANK_PRECOMPILE_ADDRESS,
|
|
84
79
|
functionName: 'supply',
|
|
85
80
|
args: ['usei'],
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
const result = await publicClient?.readContract(readContractParams);
|
|
89
|
-
return (result as bigint).toString();
|
|
81
|
+
});
|
|
82
|
+
return result?.toString();
|
|
90
83
|
};`;
|
|
91
84
|
|
|
92
85
|
return (
|
|
@@ -95,11 +88,11 @@ const getSeiSupply = async () => {
|
|
|
95
88
|
{/* Header */}
|
|
96
89
|
<div>
|
|
97
90
|
<Group gap="md" align="center" mb="lg">
|
|
98
|
-
<ThemeIcon size={48} radius="md"
|
|
91
|
+
<ThemeIcon size={48} radius="md" variant="outline" color="seiMaroon">
|
|
99
92
|
<IconBulb size={24} />
|
|
100
93
|
</ThemeIcon>
|
|
101
94
|
<div>
|
|
102
|
-
<Title order={1} fw={700} c="
|
|
95
|
+
<Title order={1} fw={700} c="seiMaroon.9" className="sei-heading">
|
|
103
96
|
Sei Precompiles
|
|
104
97
|
</Title>
|
|
105
98
|
<Text size="lg" c="gray.6" mt={4}>
|
|
@@ -115,7 +108,7 @@ const getSeiSupply = async () => {
|
|
|
115
108
|
<Flex justify="space-between" align="flex-start">
|
|
116
109
|
<Stack gap="xs">
|
|
117
110
|
<Group gap="md">
|
|
118
|
-
<ThemeIcon size={40} radius="md"
|
|
111
|
+
<ThemeIcon size={40} radius="md" variant="outline" color="seiMaroon">
|
|
119
112
|
<IconDatabase size={20} />
|
|
120
113
|
</ThemeIcon>
|
|
121
114
|
<Title order={3} fw={600} c="gray.9">
|
|
@@ -123,13 +116,13 @@ const getSeiSupply = async () => {
|
|
|
123
116
|
</Title>
|
|
124
117
|
</Group>
|
|
125
118
|
<Text size="sm" c="gray.6">
|
|
126
|
-
Query total SEI supply using Sei's native{
|
|
127
|
-
<Text component="a" href="https://
|
|
119
|
+
Query total SEI supply using Sei's native{" "}
|
|
120
|
+
<Text component="a" href="https://docs.sei.io/evm/precompiles/cosmwasm-precompiles/bank" target="_blank" c="seiMaroon.9" td="underline">
|
|
128
121
|
Bank precompile
|
|
129
122
|
</Text>
|
|
130
123
|
</Text>
|
|
131
124
|
</Stack>
|
|
132
|
-
<Badge
|
|
125
|
+
<Badge variant="outline" color="gray" size="md" radius="md">
|
|
133
126
|
precompiles
|
|
134
127
|
</Badge>
|
|
135
128
|
</Flex>
|
|
@@ -142,7 +135,7 @@ const getSeiSupply = async () => {
|
|
|
142
135
|
<Flex align="baseline" gap="xs">
|
|
143
136
|
{supply ? (
|
|
144
137
|
<>
|
|
145
|
-
<Text fw={700} size="xl" c="
|
|
138
|
+
<Text fw={700} size="xl" c="seiGold.9" ff="var(--sei-font-mono)">
|
|
146
139
|
{formatLargeSeiNumber(supply, 2)}
|
|
147
140
|
</Text>
|
|
148
141
|
<Text size="sm" c="gray.6" fw={500}>
|
|
@@ -162,8 +155,8 @@ const getSeiSupply = async () => {
|
|
|
162
155
|
</Paper>
|
|
163
156
|
)}
|
|
164
157
|
</Flex>
|
|
165
|
-
<Button onClick={getSeiSupply} size="sm" radius="md" variant="
|
|
166
|
-
{supply ?
|
|
158
|
+
<Button onClick={getSeiSupply} size="sm" radius="md" variant="filled" color="seiMaroon" leftSection={<IconDatabase size={14} />}>
|
|
159
|
+
{supply ? "Refresh" : "Query Supply"}
|
|
167
160
|
</Button>
|
|
168
161
|
</Flex>
|
|
169
162
|
</Paper>
|
|
@@ -177,7 +170,7 @@ const getSeiSupply = async () => {
|
|
|
177
170
|
leftSection={showCode ? <IconChevronUp size={14} /> : <IconChevronDown size={14} />}
|
|
178
171
|
onClick={() => setShowCode(!showCode)}
|
|
179
172
|
>
|
|
180
|
-
{showCode ?
|
|
173
|
+
{showCode ? "Hide" : "View"} Implementation
|
|
181
174
|
</Button>
|
|
182
175
|
{showCode && (
|
|
183
176
|
<ActionIcon variant="subtle" color="gray" size="sm" radius="md" onClick={() => copyToClipboard(codeExample)}>
|
package/dist/main.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import boxen from 'boxen';
|
|
3
|
-
import inquirer from 'inquirer';
|
|
4
2
|
import fs from 'node:fs';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import { dirname } from 'node:path';
|
|
3
|
+
import path, { dirname } from 'node:path';
|
|
7
4
|
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import boxen from 'boxen';
|
|
8
6
|
import { Command } from 'commander';
|
|
7
|
+
import inquirer from 'inquirer';
|
|
9
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
9
|
const __dirname = dirname(__filename);
|
|
11
10
|
const program = new Command();
|
|
@@ -15,19 +14,21 @@ const printWelcomeMessage = () => {
|
|
|
15
14
|
padding: 1,
|
|
16
15
|
margin: 1,
|
|
17
16
|
borderStyle: 'double',
|
|
18
|
-
borderColor: '#
|
|
17
|
+
borderColor: '#600014'
|
|
19
18
|
}));
|
|
20
19
|
};
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (typeof dirName !== 'string' || dirName.length === 0) {
|
|
20
|
+
const MAX_NPM_PACKAGE_NAME_LENGTH = 214;
|
|
21
|
+
const validNpmProjectNameRe = /^[a-z0-9][a-z0-9._-]*$/;
|
|
22
|
+
const windowsReservedNameRe = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i;
|
|
23
|
+
function isValidProjectName(projectName) {
|
|
24
|
+
if (typeof projectName !== 'string' || projectName.length === 0 || projectName.length > MAX_NPM_PACKAGE_NAME_LENGTH) {
|
|
27
25
|
return false;
|
|
28
26
|
}
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
if (projectName === 'node_modules' ||
|
|
28
|
+
projectName === 'favicon.ico' ||
|
|
29
|
+
projectName.endsWith('.') ||
|
|
30
|
+
windowsReservedNameRe.test(projectName) ||
|
|
31
|
+
!validNpmProjectNameRe.test(projectName)) {
|
|
31
32
|
return false;
|
|
32
33
|
}
|
|
33
34
|
return true;
|
|
@@ -35,18 +36,28 @@ function isValidDirectoryName(dirName) {
|
|
|
35
36
|
const validateOptions = (options) => {
|
|
36
37
|
let valid = true;
|
|
37
38
|
if (options.name) {
|
|
38
|
-
if (!
|
|
39
|
-
console.
|
|
39
|
+
if (!isValidProjectName(options.name)) {
|
|
40
|
+
console.error('Invalid package name. Please use a valid npm package name.');
|
|
40
41
|
valid = false;
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
44
|
return valid;
|
|
44
45
|
};
|
|
46
|
+
const extensionsPath = path.join(__dirname, 'extensions');
|
|
47
|
+
function printCliError(error) {
|
|
48
|
+
if (process.env.DEBUG && error instanceof Error) {
|
|
49
|
+
console.error(error.stack ?? error);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
console.error('An error occurred:', error instanceof Error ? error.message : error);
|
|
53
|
+
}
|
|
54
|
+
async function discoverExtensions() {
|
|
55
|
+
const extensions = await fs.promises.readdir(extensionsPath, { withFileTypes: true });
|
|
56
|
+
return extensions.filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
57
|
+
}
|
|
45
58
|
async function listExtensions() {
|
|
46
|
-
const extensionsPath = path.join(__dirname, 'extensions');
|
|
47
59
|
try {
|
|
48
|
-
const
|
|
49
|
-
const extensionDirs = extensions.filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
60
|
+
const extensionDirs = await discoverExtensions();
|
|
50
61
|
if (extensionDirs.length === 0) {
|
|
51
62
|
console.log('No extensions available.');
|
|
52
63
|
return;
|
|
@@ -56,15 +67,67 @@ async function listExtensions() {
|
|
|
56
67
|
console.log(` - ${ext}`);
|
|
57
68
|
}
|
|
58
69
|
}
|
|
59
|
-
catch (
|
|
70
|
+
catch (_error) {
|
|
60
71
|
console.log('No extensions directory found.');
|
|
61
72
|
}
|
|
62
73
|
}
|
|
74
|
+
function isDirectChildName(name) {
|
|
75
|
+
return name.length > 0 && name !== '.' && name !== '..' && !name.includes('/') && !name.includes('\\') && !name.includes('\0');
|
|
76
|
+
}
|
|
77
|
+
async function resolveExtension(extension) {
|
|
78
|
+
if (!extension) {
|
|
79
|
+
return {};
|
|
80
|
+
}
|
|
81
|
+
if (!isDirectChildName(extension)) {
|
|
82
|
+
throw new Error(`Invalid extension '${extension}'. Extensions must be selected by name from list-extensions.`);
|
|
83
|
+
}
|
|
84
|
+
let extensionDirs;
|
|
85
|
+
try {
|
|
86
|
+
extensionDirs = await discoverExtensions();
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
if (error.code !== 'ENOENT') {
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
extensionDirs = [];
|
|
93
|
+
}
|
|
94
|
+
if (!extensionDirs.includes(extension)) {
|
|
95
|
+
return { warning: `Warning: Extension '${extension}' not found. Continuing with base template.` };
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
extension: {
|
|
99
|
+
name: extension,
|
|
100
|
+
source: path.join(extensionsPath, extension)
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
async function copyDirectoryContents(source, destination, overwrite) {
|
|
105
|
+
const entries = await fs.promises.readdir(source, { withFileTypes: true });
|
|
106
|
+
for (const entry of entries) {
|
|
107
|
+
await fs.promises.cp(path.join(source, entry.name), path.join(destination, entry.name), {
|
|
108
|
+
recursive: true,
|
|
109
|
+
force: overwrite,
|
|
110
|
+
errorOnExist: !overwrite
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async function writeProjectName(destination, projectName) {
|
|
115
|
+
const manifestPath = path.join(destination, 'package.json');
|
|
116
|
+
const manifest = JSON.parse(await fs.promises.readFile(manifestPath, 'utf8'));
|
|
117
|
+
manifest.name = projectName;
|
|
118
|
+
await fs.promises.writeFile(manifestPath, `${JSON.stringify(manifest, null, '\t')}\n`);
|
|
119
|
+
}
|
|
63
120
|
async function runWizard(options) {
|
|
64
121
|
if (!validateOptions(options)) {
|
|
122
|
+
process.exitCode = 1;
|
|
65
123
|
return;
|
|
66
124
|
}
|
|
125
|
+
const extensionResolution = await resolveExtension(options.extension);
|
|
67
126
|
printWelcomeMessage();
|
|
127
|
+
if (extensionResolution.warning) {
|
|
128
|
+
console.warn(extensionResolution.warning);
|
|
129
|
+
}
|
|
130
|
+
const extension = extensionResolution.extension;
|
|
68
131
|
let dAppName = '';
|
|
69
132
|
if (options.name) {
|
|
70
133
|
dAppName = options.name;
|
|
@@ -76,7 +139,7 @@ async function runWizard(options) {
|
|
|
76
139
|
name: 'dAppName',
|
|
77
140
|
message: 'What is your dApp (project) name?',
|
|
78
141
|
validate: (input) => {
|
|
79
|
-
return
|
|
142
|
+
return isValidProjectName(input) || 'Invalid package name. Please use a valid npm package name.';
|
|
80
143
|
}
|
|
81
144
|
}
|
|
82
145
|
]);
|
|
@@ -86,34 +149,48 @@ async function runWizard(options) {
|
|
|
86
149
|
const templateName = 'next-template';
|
|
87
150
|
const templatePath = path.join(__dirname, 'templates', templateName);
|
|
88
151
|
const dst = path.join(process.cwd(), dAppName);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (options.extension) {
|
|
92
|
-
const extensionPath = path.join(__dirname, 'extensions', options.extension);
|
|
152
|
+
let destinationCreated = false;
|
|
153
|
+
try {
|
|
93
154
|
try {
|
|
94
|
-
await fs.promises.
|
|
95
|
-
|
|
96
|
-
console.log(`Applied extension: ${options.extension}`);
|
|
155
|
+
await fs.promises.mkdir(dst);
|
|
156
|
+
destinationCreated = true;
|
|
97
157
|
}
|
|
98
158
|
catch (error) {
|
|
99
|
-
|
|
159
|
+
if (error.code === 'EEXIST') {
|
|
160
|
+
throw new Error(`Destination '${dAppName}' already exists. Choose a different project name.`);
|
|
161
|
+
}
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
await copyDirectoryContents(templatePath, dst, false);
|
|
165
|
+
if (extension) {
|
|
166
|
+
await copyDirectoryContents(extension.source, dst, true);
|
|
167
|
+
console.log(`Applied extension: ${extension.name}`);
|
|
168
|
+
}
|
|
169
|
+
await fs.promises.rename(path.join(dst, 'gitignore'), path.join(dst, '.gitignore'));
|
|
170
|
+
await writeProjectName(dst, dAppName);
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
if (destinationCreated) {
|
|
174
|
+
await fs.promises.rm(dst, { recursive: true, force: true });
|
|
100
175
|
}
|
|
176
|
+
throw error;
|
|
101
177
|
}
|
|
102
|
-
const extensionText =
|
|
178
|
+
const extensionText = extension ? ` with ${extension.name} extension` : '';
|
|
103
179
|
console.log(`Project setup complete! Using template ${templateName}${extensionText}\n`);
|
|
104
|
-
console.log(`To start your app, run: \n > cd
|
|
180
|
+
console.log(`To start your app, run: \n > cd "./${dAppName}" \n > bun install \n > bun run dev\n`);
|
|
105
181
|
}
|
|
106
182
|
program
|
|
107
183
|
.command('app')
|
|
108
184
|
.description('Create a new SEI dApp')
|
|
109
|
-
.option('--name <name>', 'Specify the name of your dApp. Name must be a valid package name.')
|
|
185
|
+
.option('-n, --name <name>', 'Specify the name of your dApp. Name must be a valid package name.')
|
|
110
186
|
.option('--extension <extension>', 'Specify an extension to apply to the base template')
|
|
111
187
|
.action(async (options) => {
|
|
112
188
|
try {
|
|
113
189
|
await runWizard(options);
|
|
114
190
|
}
|
|
115
191
|
catch (error) {
|
|
116
|
-
|
|
192
|
+
printCliError(error);
|
|
193
|
+
process.exitCode = 1;
|
|
117
194
|
}
|
|
118
195
|
});
|
|
119
196
|
program
|
|
@@ -124,7 +201,8 @@ program
|
|
|
124
201
|
await listExtensions();
|
|
125
202
|
}
|
|
126
203
|
catch (error) {
|
|
127
|
-
|
|
204
|
+
printCliError(error);
|
|
205
|
+
process.exitCode = 1;
|
|
128
206
|
}
|
|
129
207
|
});
|
|
130
|
-
program.
|
|
208
|
+
await program.parseAsync(process.argv);
|
|
@@ -1,54 +1,84 @@
|
|
|
1
|
-
|
|
1
|
+
# Sei dApp starter
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This app was generated by `@sei-js/create-sei`. It uses Next.js, Wagmi, Viem, RainbowKit, Mantine, and Tailwind CSS.
|
|
4
|
+
|
|
5
|
+
## Package manager
|
|
6
|
+
|
|
7
|
+
Bun 1.3.14 or newer is required for this starter. Its security overrides and release smoke are validated with Bun; npm, Yarn, and pnpm do not share one override contract and may silently resolve a different dependency graph.
|
|
8
|
+
|
|
9
|
+
The generated project intentionally does not ship a lockfile. Exact direct pins reduce drift, but transitive dependencies are resolved when you run `bun install`; the dedicated smoke validates the current graph rather than claiming every future install is byte-for-byte reproducible. Commit the resulting `bun.lock` after your first reviewed install.
|
|
10
|
+
|
|
11
|
+
## Run locally
|
|
12
|
+
|
|
13
|
+
Install the dependencies and start the development server:
|
|
4
14
|
|
|
5
|
-
Install dependencies by running
|
|
6
15
|
```bash
|
|
7
16
|
bun install
|
|
17
|
+
bun run dev
|
|
8
18
|
```
|
|
9
19
|
|
|
10
|
-
|
|
20
|
+
Open [http://localhost:3000](http://localhost:3000).
|
|
21
|
+
|
|
22
|
+
## Check and build
|
|
23
|
+
|
|
24
|
+
Run `bun run check` before building. If it reports fixable issues, run `bun run check:fix`. The build's prebuild hook runs the same check.
|
|
11
25
|
|
|
12
26
|
```bash
|
|
13
|
-
bun run
|
|
27
|
+
bun run check
|
|
28
|
+
bun run build
|
|
14
29
|
```
|
|
15
30
|
|
|
16
|
-
|
|
31
|
+
## Image handling
|
|
32
|
+
|
|
33
|
+
Next.js image optimization is disabled so the generated app needs no native Sharp build. Turning it on is a template choice rather than a security tradeoff.
|
|
17
34
|
|
|
18
|
-
|
|
35
|
+
The manifest's `sharp` override is a separate matter and has to stay. The pinned Next 15 line declares Sharp `^0.34.3 || ^0.35.4`, and the `0.34.x` half of that range is still inside [GHSA-rgj7-g3m4-5g8c](https://github.com/advisories/GHSA-rgj7-g3m4-5g8c), so `0.35.4` is the advisory floor. Loosening or removing the override reintroduces a high audit finding whether or not optimization is enabled.
|
|
19
36
|
|
|
20
|
-
|
|
37
|
+
## Development bundler
|
|
21
38
|
|
|
22
|
-
|
|
23
|
-
This repository includes basic features that allow you to connect to a wallet and use the wallet to interact with the chain.
|
|
39
|
+
`bun run dev` uses the Next 15 webpack default. Do not add `--turbopack`: the pinned Next 15 line cannot express the required `false` alias for MetaMask SDK's mobile-only storage import in Turbopack without adding a synthetic replacement module. The production build and smoke use the validated webpack alias in `next.config.mjs`.
|
|
24
40
|
|
|
25
|
-
|
|
41
|
+
## Sei brand
|
|
26
42
|
|
|
27
|
-
|
|
43
|
+
The assets in `public/brand` come unchanged from the [official Sei brand kit](https://brand.sei.io). The primary lockup is used in the header, the horizontal “Powered by Sei” asset appears in the footer, and the standalone mark is reserved for app-icon metadata. Preserve their proportions and clearspace; do not redraw or recolor them.
|
|
28
44
|
|
|
29
|
-
|
|
30
|
-
This component is a Sei specific wrapper around a Wagmi Provider that should wrap the application. Wrapping the application in this provider will enable it to access hooks in Wagmi. These hooks are enable connection to a wallet and interaction with the chain.
|
|
45
|
+
Black and white are the base palette. `--sei-maroon` (`#600014`) is reserved for primary actions and headlines, while `--sei-gold` (`#966F22`) is reserved for large or bold data and non-text highlights, not normal-size text on light surfaces. Reusable palette and typography tokens live in `src/app/globals.css`.
|
|
31
46
|
|
|
32
|
-
|
|
47
|
+
The Mantine theme maps `gray` to the official neutral ramp; ordinary and dimmed text resolves to `#666666` or darker for accessible contrast on white and `#F5F5F7`.
|
|
33
48
|
|
|
34
|
-
|
|
49
|
+
The public brand archive does not include the proprietary Lateral or ABC Repro Mono font files, so this starter uses license-safe system fallback stacks. Teams with appropriately licensed fonts can map Lateral and ABC Repro Mono to the documented CSS font tokens.
|
|
35
50
|
|
|
36
|
-
|
|
37
|
-
This application connects to the arctic-1 devnet by default, using chain configurations uploaded to viem.
|
|
51
|
+
## Project structure
|
|
38
52
|
|
|
39
|
-
|
|
53
|
+
- `src/app/layout.tsx` wraps every route with the wallet and UI providers.
|
|
54
|
+
- `src/app/favicon.ico/route.ts` redirects conventional favicon requests to the unchanged official app mark.
|
|
55
|
+
- `src/app/page.tsx` shows the landing page until a wallet connects, then shows the interactive examples.
|
|
56
|
+
- `src/components/providers/providers.tsx` configures Wagmi, RainbowKit, and the selected Sei network. It registers the injected browser-wallet connector by default; add other wallets to its `wallets` list.
|
|
57
|
+
- `src/components/shell/shell.tsx` provides the shared app shell around the landing and connected views.
|
|
58
|
+
- `src/components/landing/index.tsx` contains the disconnected landing page.
|
|
59
|
+
- `src/components/default/index.tsx` contains the connected wallet, balance, and transaction examples.
|
|
60
|
+
- `src/app/development/page.tsx` and `src/app/resources/page.tsx` contain development notes and links.
|
|
40
61
|
|
|
41
|
-
##
|
|
62
|
+
## Network configuration
|
|
42
63
|
|
|
43
|
-
|
|
64
|
+
The app connects to Sei mainnet when `NEXT_PUBLIC_CHAIN` is unset. The included `.env.example` sets it to `testnet`, so copying that file to `.env.local` selects Sei testnet. To select testnet explicitly, add this to `.env.local`:
|
|
44
65
|
|
|
45
|
-
|
|
46
|
-
|
|
66
|
+
```dotenv
|
|
67
|
+
NEXT_PUBLIC_CHAIN=testnet
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
WalletConnect-based wallets require a real project ID from [WalletConnect Cloud](https://cloud.walletconnect.com). Set it in `.env.local`:
|
|
47
71
|
|
|
48
|
-
|
|
72
|
+
```dotenv
|
|
73
|
+
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your-project-id
|
|
74
|
+
```
|
|
49
75
|
|
|
50
|
-
|
|
76
|
+
The fallback value in the template is only a placeholder, so WalletConnect-based wallets will not connect until you replace it. The bundled injected connector works without a WalletConnect project ID for installed browser wallets such as MetaMask.
|
|
51
77
|
|
|
52
|
-
|
|
78
|
+
## Learn more
|
|
53
79
|
|
|
54
|
-
|
|
80
|
+
- [Sei documentation](https://docs.sei.io/)
|
|
81
|
+
- [Next.js documentation](https://nextjs.org/docs)
|
|
82
|
+
- [Wagmi documentation](https://wagmi.sh/react/getting-started)
|
|
83
|
+
- [Viem documentation](https://viem.sh/)
|
|
84
|
+
- [RainbowKit documentation](https://www.rainbowkit.com/docs/introduction)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$schema": "https://biomejs.dev/schemas/2.
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/2.5.8/schema.json",
|
|
3
3
|
"root": true,
|
|
4
4
|
"assist": {
|
|
5
5
|
"actions": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"linter": {
|
|
12
12
|
"enabled": true,
|
|
13
13
|
"rules": {
|
|
14
|
-
"
|
|
14
|
+
"preset": "recommended",
|
|
15
15
|
"style": {
|
|
16
16
|
"noNonNullAssertion": "off"
|
|
17
17
|
},
|
|
@@ -50,7 +50,6 @@
|
|
|
50
50
|
}
|
|
51
51
|
},
|
|
52
52
|
"files": {
|
|
53
|
-
"includes": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.css", "*.json", "*.md"]
|
|
54
|
-
"ignore": ["node_modules/**", "dist/**", ".next/**", ".yarn/**"]
|
|
53
|
+
"includes": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.css", "*.json", "*.md", "!**/node_modules", "!**/dist", "!**/.next", "!**/.yarn"]
|
|
55
54
|
}
|
|
56
55
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
/node_modules
|
|
3
|
+
/.pnp
|
|
4
|
+
.pnp.*
|
|
5
|
+
.yarn/*
|
|
6
|
+
!.yarn/patches
|
|
7
|
+
!.yarn/plugins
|
|
8
|
+
!.yarn/releases
|
|
9
|
+
!.yarn/versions
|
|
10
|
+
|
|
11
|
+
# Tests
|
|
12
|
+
/coverage
|
|
13
|
+
|
|
14
|
+
# Next.js
|
|
15
|
+
/.next/
|
|
16
|
+
/out/
|
|
17
|
+
|
|
18
|
+
# Production
|
|
19
|
+
/build
|
|
20
|
+
|
|
21
|
+
# Local files
|
|
22
|
+
.DS_Store
|
|
23
|
+
*.pem
|
|
24
|
+
|
|
25
|
+
# Debug logs
|
|
26
|
+
npm-debug.log*
|
|
27
|
+
yarn-debug.log*
|
|
28
|
+
yarn-error.log*
|
|
29
|
+
pnpm-debug.log*
|
|
30
|
+
bun-debug.log*
|
|
31
|
+
|
|
32
|
+
# Environment files (keep the example)
|
|
33
|
+
.env*
|
|
34
|
+
!.env.example
|
|
35
|
+
|
|
36
|
+
# Vercel
|
|
37
|
+
.vercel
|
|
38
|
+
|
|
39
|
+
# TypeScript
|
|
40
|
+
*.tsbuildinfo
|
|
41
|
+
next-env.d.ts
|