create-stylus 0.1.11 → 0.1.14
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/package.json +1 -1
- package/templates/base/.lintstagedrc.js +2 -2
- package/templates/base/package.json +1 -1
- package/templates/base/packages/nextjs/components/Header.tsx +1 -2
- package/templates/base/packages/nextjs/components/ThemeProvider.tsx +1 -2
- package/templates/base/packages/nextjs/components/scaffold-eth/Input/EtherInput.tsx +1 -0
- package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressInfoDropdown.tsx +8 -7
- package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/WrongNetworkDropdown.tsx +8 -2
- package/templates/base/packages/nextjs/eslint.config.mjs +8 -18
- package/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldWriteContract.ts +48 -17
- package/templates/base/packages/nextjs/next-env.d.ts +1 -1
- package/templates/base/packages/nextjs/next.config.js +0 -8
- package/templates/base/packages/nextjs/package.json +29 -30
- package/templates/base/packages/nextjs/postcss.config.js +1 -2
- package/templates/base/packages/nextjs/scaffold.config.ts +10 -0
- package/templates/base/packages/nextjs/styles/globals.css +128 -4
- package/templates/base/packages/nextjs/tsconfig.json +3 -3
- package/templates/base/packages/stylus/scripts/utils/command.ts +38 -0
- package/templates/base/readme.md +2 -2
- package/templates/base/yarn.lock +6035 -3448
- package/templates/base/packages/nextjs/.eslintignore +0 -11
- package/templates/base/packages/nextjs/.eslintrc.json +0 -15
- package/templates/base/packages/nextjs/tailwind.config.js +0 -102
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const path = require("path");
|
|
2
2
|
|
|
3
3
|
const buildNextEslintCommand = (filenames) =>
|
|
4
|
-
`yarn
|
|
4
|
+
`yarn workspace @ss/nextjs eslint --fix ${filenames
|
|
5
5
|
.map((f) => path.relative(path.join("packages", "nextjs"), f))
|
|
6
|
-
.join("
|
|
6
|
+
.join(" ")}`;
|
|
7
7
|
|
|
8
8
|
const checkTypesNextCommand = () => "yarn next:check-types";
|
|
9
9
|
|
|
@@ -105,9 +105,8 @@ export const Header = () => {
|
|
|
105
105
|
}}
|
|
106
106
|
>
|
|
107
107
|
<div className="navbar-start w-auto lg:w-1/2">
|
|
108
|
-
<div className=
|
|
108
|
+
<div className={`lg:hidden dropdown ${isDrawerOpen ? "dropdown-open" : ""}`} ref={burgerMenuRef}>
|
|
109
109
|
<label
|
|
110
|
-
tabIndex={0}
|
|
111
110
|
className={`ml-1 btn btn-ghost ${isDrawerOpen ? "hover:bg-secondary" : "hover:bg-transparent"}`}
|
|
112
111
|
onClick={() => {
|
|
113
112
|
setIsDrawerOpen(prevIsOpenState => !prevIsOpenState);
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import { ThemeProvider as NextThemesProvider } from "next-themes";
|
|
5
|
-
import { type ThemeProviderProps } from "next-themes/dist/types";
|
|
4
|
+
import { ThemeProvider as NextThemesProvider, type ThemeProviderProps } from "next-themes";
|
|
6
5
|
|
|
7
6
|
export const ThemeProvider = ({ children, ...props }: ThemeProviderProps) => {
|
|
8
7
|
const [mounted, setMounted] = React.useState(false);
|
|
@@ -66,6 +66,7 @@ export const EtherInput = ({
|
|
|
66
66
|
return transitoryDisplayValue;
|
|
67
67
|
}
|
|
68
68
|
// Clear any transitory display values that might be set
|
|
69
|
+
// eslint-disable-next-line react-hooks/set-state-in-render
|
|
69
70
|
setTransitoryDisplayValue(undefined);
|
|
70
71
|
return newDisplayValue;
|
|
71
72
|
}, [nativeCurrencyPrice, transitoryDisplayValue, displayUsdMode, value]);
|
|
@@ -40,21 +40,22 @@ export const AddressInfoDropdown = ({ address, ensAvatar, displayName, onSwitchA
|
|
|
40
40
|
const { copyToClipboard: copyAddressToClipboard, isCopiedToClipboard: isAddressCopiedToClipboard } =
|
|
41
41
|
useCopyToClipboard();
|
|
42
42
|
const [selectingNetwork, setSelectingNetwork] = useState(false);
|
|
43
|
-
const
|
|
43
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
44
|
+
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
44
45
|
|
|
45
46
|
const closeDropdown = () => {
|
|
46
47
|
setSelectingNetwork(false);
|
|
47
|
-
|
|
48
|
+
setIsOpen(false);
|
|
48
49
|
};
|
|
49
50
|
|
|
50
51
|
useOutsideClick(dropdownRef, closeDropdown);
|
|
51
52
|
|
|
52
53
|
return (
|
|
53
54
|
<>
|
|
54
|
-
<
|
|
55
|
-
<
|
|
56
|
-
tabIndex={0}
|
|
55
|
+
<div ref={dropdownRef} className={`dropdown dropdown-end leading-3 ${isOpen ? "dropdown-open" : ""}`}>
|
|
56
|
+
<label
|
|
57
57
|
className="dropdown-toggle gap-0 !h-auto"
|
|
58
|
+
onClick={() => setIsOpen(prev => !prev)}
|
|
58
59
|
style={{
|
|
59
60
|
position: "relative",
|
|
60
61
|
width: "220px",
|
|
@@ -126,7 +127,7 @@ export const AddressInfoDropdown = ({ address, ensAvatar, displayName, onSwitchA
|
|
|
126
127
|
}}
|
|
127
128
|
/>
|
|
128
129
|
</div>
|
|
129
|
-
</
|
|
130
|
+
</label>
|
|
130
131
|
<ul
|
|
131
132
|
tabIndex={0}
|
|
132
133
|
className="dropdown-content menu z-[2] p-2 mt-4 gap-1"
|
|
@@ -229,7 +230,7 @@ export const AddressInfoDropdown = ({ address, ensAvatar, displayName, onSwitchA
|
|
|
229
230
|
</button>
|
|
230
231
|
</li>
|
|
231
232
|
</ul>
|
|
232
|
-
</
|
|
233
|
+
</div>
|
|
233
234
|
</>
|
|
234
235
|
);
|
|
235
236
|
};
|
|
@@ -1,13 +1,19 @@
|
|
|
1
|
+
import { useRef, useState } from "react";
|
|
1
2
|
import { NetworkOptions } from "./NetworkOptions";
|
|
2
3
|
import { useDisconnect } from "wagmi";
|
|
3
4
|
import { ArrowLeftEndOnRectangleIcon, ChevronDownIcon } from "@heroicons/react/24/outline";
|
|
5
|
+
import { useOutsideClick } from "~~/hooks/scaffold-eth";
|
|
4
6
|
|
|
5
7
|
export const WrongNetworkDropdown = () => {
|
|
6
8
|
const { disconnect } = useDisconnect();
|
|
9
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
10
|
+
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
11
|
+
|
|
12
|
+
useOutsideClick(dropdownRef, () => setIsOpen(false));
|
|
7
13
|
|
|
8
14
|
return (
|
|
9
|
-
<div className=
|
|
10
|
-
<label
|
|
15
|
+
<div ref={dropdownRef} className={`dropdown dropdown-end mr-2 ${isOpen ? "dropdown-open" : ""}`}>
|
|
16
|
+
<label className="btn btn-error btn-sm dropdown-toggle gap-1" onClick={() => setIsOpen(prev => !prev)}>
|
|
11
17
|
<span>Wrong network</span>
|
|
12
18
|
<ChevronDownIcon className="h-6 w-4 ml-2 sm:ml-0" />
|
|
13
19
|
</label>
|
|
@@ -1,26 +1,21 @@
|
|
|
1
|
-
import
|
|
1
|
+
import nextCoreWebVitals from "eslint-config-next/core-web-vitals";
|
|
2
|
+
import nextTypescript from "eslint-config-next/typescript";
|
|
3
|
+
import prettierConfig from "eslint-config-prettier";
|
|
2
4
|
import prettierPlugin from "eslint-plugin-prettier";
|
|
3
5
|
import { defineConfig } from "eslint/config";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
|
|
7
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
-
const __dirname = path.dirname(__filename);
|
|
9
|
-
const compat = new FlatCompat({
|
|
10
|
-
baseDirectory: __dirname,
|
|
11
|
-
});
|
|
12
6
|
|
|
13
7
|
export default defineConfig([
|
|
8
|
+
...nextCoreWebVitals,
|
|
9
|
+
...nextTypescript,
|
|
10
|
+
prettierConfig,
|
|
14
11
|
{
|
|
15
12
|
plugins: {
|
|
16
13
|
prettier: prettierPlugin,
|
|
17
14
|
},
|
|
18
|
-
extends: compat.extends("next/core-web-vitals", "next/typescript", "prettier"),
|
|
19
|
-
|
|
20
15
|
rules: {
|
|
21
16
|
"@typescript-eslint/no-explicit-any": "off",
|
|
22
17
|
"@typescript-eslint/ban-ts-comment": "off",
|
|
23
|
-
|
|
18
|
+
"react-hooks/set-state-in-effect": "off",
|
|
24
19
|
"prettier/prettier": [
|
|
25
20
|
"warn",
|
|
26
21
|
{
|
|
@@ -28,11 +23,6 @@ export default defineConfig([
|
|
|
28
23
|
},
|
|
29
24
|
],
|
|
30
25
|
},
|
|
31
|
-
|
|
32
|
-
{
|
|
33
|
-
files: ["next-env.d.ts"],
|
|
34
|
-
rules: {
|
|
35
|
-
"@typescript-eslint/triple-slash-reference": "off",
|
|
36
|
-
},
|
|
26
|
+
ignores: [".next", "next-env.d.ts"],
|
|
37
27
|
},
|
|
38
28
|
]);
|
|
@@ -2,12 +2,14 @@ import { useEffect, useState } from "react";
|
|
|
2
2
|
import { MutateOptions } from "@tanstack/react-query";
|
|
3
3
|
import { Abi, ExtractAbiFunctionNames } from "abitype";
|
|
4
4
|
import { Config, UseWriteContractParameters, useAccount, useConfig, useWriteContract } from "wagmi";
|
|
5
|
-
import { WriteContractErrorType, WriteContractReturnType } from "wagmi/actions";
|
|
5
|
+
import { estimateFeesPerGas, WriteContractErrorType, WriteContractReturnType } from "wagmi/actions";
|
|
6
6
|
import { WriteContractVariables } from "wagmi/query";
|
|
7
7
|
import { useSelectedNetwork } from "~~/hooks/scaffold-eth";
|
|
8
8
|
import { useDeployedContractInfo, useTransactor } from "~~/hooks/scaffold-eth";
|
|
9
9
|
import { notification } from "~~/utils/scaffold-eth";
|
|
10
10
|
import { AllowedChainIds } from "~~/utils/scaffold-stylus";
|
|
11
|
+
import scaffoldConfig from "~~/scaffold.config";
|
|
12
|
+
import { wagmiConfig } from "~~/services/web3/wagmiConfig";
|
|
11
13
|
import {
|
|
12
14
|
ContractAbi,
|
|
13
15
|
ContractName,
|
|
@@ -34,6 +36,30 @@ type ScaffoldWriteContractReturnType<TContractName extends ContractName> = Omit<
|
|
|
34
36
|
) => void;
|
|
35
37
|
};
|
|
36
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Optionally bump maxFeePerGas on the write args to protect against base fee spikes.
|
|
41
|
+
* Since maxFeePerGas is a ceiling (not the actual fee), a generous multiplier is safe.
|
|
42
|
+
*/
|
|
43
|
+
async function applyGasFeeMultiplier(
|
|
44
|
+
args: WriteContractVariables<Abi, string, any[], Config, number>,
|
|
45
|
+
chainId: number,
|
|
46
|
+
): Promise<WriteContractVariables<Abi, string, any[], Config, number>> {
|
|
47
|
+
const multiplier = scaffoldConfig.gasFeeMultiplier;
|
|
48
|
+
if (!multiplier || multiplier <= 1) return args;
|
|
49
|
+
try {
|
|
50
|
+
const fees = await estimateFeesPerGas(wagmiConfig, { chainId: chainId as any });
|
|
51
|
+
const scaledMultiplier = BigInt(Math.round(multiplier * 100));
|
|
52
|
+
return {
|
|
53
|
+
...args,
|
|
54
|
+
maxFeePerGas: (fees.maxFeePerGas * scaledMultiplier) / 100n,
|
|
55
|
+
maxPriorityFeePerGas: fees.maxPriorityFeePerGas,
|
|
56
|
+
} as WriteContractVariables<Abi, string, any[], Config, number>;
|
|
57
|
+
} catch (e) {
|
|
58
|
+
console.warn("⚡️ ~ useScaffoldWriteContract ~ estimateFeesPerGas failed:", e);
|
|
59
|
+
return args;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
37
63
|
export function useScaffoldWriteContract<TContractName extends ContractName>(
|
|
38
64
|
config: UseScaffoldWriteConfig<TContractName>,
|
|
39
65
|
): ScaffoldWriteContractReturnType<TContractName>;
|
|
@@ -110,12 +136,14 @@ export function useScaffoldWriteContract<TContractName extends ContractName>(
|
|
|
110
136
|
setIsMining(true);
|
|
111
137
|
const { blockConfirmations, onBlockConfirmation, ...mutateOptions } = options || {};
|
|
112
138
|
|
|
113
|
-
|
|
139
|
+
let writeContractObject = {
|
|
114
140
|
abi: deployedContractData.abi as Abi,
|
|
115
141
|
address: deployedContractData.address,
|
|
116
142
|
...variables,
|
|
117
143
|
} as WriteContractVariables<Abi, string, any[], Config, number>;
|
|
118
144
|
|
|
145
|
+
writeContractObject = await applyGasFeeMultiplier(writeContractObject, selectedNetwork.id as AllowedChainIds);
|
|
146
|
+
|
|
119
147
|
if (!finalConfig?.disableSimulate) {
|
|
120
148
|
await simulateContractWriteAndNotifyError({
|
|
121
149
|
wagmiConfig,
|
|
@@ -167,21 +195,24 @@ export function useScaffoldWriteContract<TContractName extends ContractName>(
|
|
|
167
195
|
return;
|
|
168
196
|
}
|
|
169
197
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
198
|
+
const writeContractObject = {
|
|
199
|
+
abi: deployedContractData.abi as Abi,
|
|
200
|
+
address: deployedContractData.address,
|
|
201
|
+
...variables,
|
|
202
|
+
} as WriteContractVariables<Abi, string, any[], Config, number>;
|
|
203
|
+
applyGasFeeMultiplier(writeContractObject, selectedNetwork.id as AllowedChainIds).then(buffered => {
|
|
204
|
+
wagmiContractWrite.writeContract(
|
|
205
|
+
buffered,
|
|
206
|
+
options as
|
|
207
|
+
| MutateOptions<
|
|
208
|
+
WriteContractReturnType,
|
|
209
|
+
WriteContractErrorType,
|
|
210
|
+
WriteContractVariables<Abi, string, any[], Config, number>,
|
|
211
|
+
unknown
|
|
212
|
+
>
|
|
213
|
+
| undefined,
|
|
214
|
+
);
|
|
215
|
+
});
|
|
185
216
|
};
|
|
186
217
|
|
|
187
218
|
return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="next" />
|
|
2
2
|
/// <reference types="next/image-types/global" />
|
|
3
|
-
|
|
3
|
+
import "./.next/types/routes.d.ts";
|
|
4
4
|
|
|
5
5
|
// NOTE: This file should not be edited
|
|
6
6
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
|
@@ -6,14 +6,6 @@ const nextConfig = {
|
|
|
6
6
|
typescript: {
|
|
7
7
|
ignoreBuildErrors: process.env.NEXT_PUBLIC_IGNORE_BUILD_ERROR === "true",
|
|
8
8
|
},
|
|
9
|
-
eslint: {
|
|
10
|
-
ignoreDuringBuilds: process.env.NEXT_PUBLIC_IGNORE_BUILD_ERROR === "true",
|
|
11
|
-
},
|
|
12
|
-
webpack: config => {
|
|
13
|
-
config.resolve.fallback = { fs: false, net: false, tls: false };
|
|
14
|
-
config.externals.push("pino-pretty", "lokijs", "encoding");
|
|
15
|
-
return config;
|
|
16
|
-
},
|
|
17
9
|
};
|
|
18
10
|
|
|
19
11
|
module.exports = nextConfig;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"start": "next dev",
|
|
8
8
|
"build": "next build",
|
|
9
9
|
"serve": "next start",
|
|
10
|
-
"lint": "
|
|
10
|
+
"lint": "eslint .",
|
|
11
11
|
"format": "prettier --write . '!(node_modules|.next|contracts)/**/*'",
|
|
12
12
|
"check-types": "tsc --noEmit --incremental",
|
|
13
13
|
"vercel": "vercel",
|
|
@@ -23,44 +23,43 @@
|
|
|
23
23
|
"has-ansi": "5.0.1"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@heroicons/react": "^2.
|
|
27
|
-
"@rainbow-me/rainbowkit": "2.2.
|
|
26
|
+
"@heroicons/react": "^2.2.0",
|
|
27
|
+
"@rainbow-me/rainbowkit": "2.2.9",
|
|
28
28
|
"@tanstack/react-query": "^5.59.15",
|
|
29
29
|
"@uniswap/sdk-core": "^5.8.2",
|
|
30
30
|
"@uniswap/v2-sdk": "^4.6.1",
|
|
31
|
-
"blo": "^
|
|
32
|
-
"burner-connector": "0.0.
|
|
33
|
-
"daisyui": "
|
|
34
|
-
"kubo-rpc-client": "^
|
|
35
|
-
"next": "^
|
|
36
|
-
"next-nprogress-bar": "^2.
|
|
37
|
-
"next-themes": "^0.
|
|
38
|
-
"qrcode.react": "^4.0
|
|
39
|
-
"react": "^19.
|
|
40
|
-
"react-dom": "^19.
|
|
41
|
-
"react-hot-toast": "^2.
|
|
42
|
-
"usehooks-ts": "^3.1.
|
|
43
|
-
"viem": "2.
|
|
44
|
-
"wagmi": "2.
|
|
45
|
-
"zustand": "^5.0.
|
|
31
|
+
"blo": "^2.0.0",
|
|
32
|
+
"burner-connector": "0.0.20",
|
|
33
|
+
"daisyui": "^5.5.19",
|
|
34
|
+
"kubo-rpc-client": "^6.1.0",
|
|
35
|
+
"next": "^16.2.4",
|
|
36
|
+
"next-nprogress-bar": "^2.4.7",
|
|
37
|
+
"next-themes": "^0.4.6",
|
|
38
|
+
"qrcode.react": "^4.2.0",
|
|
39
|
+
"react": "^19.2.5",
|
|
40
|
+
"react-dom": "^19.2.5",
|
|
41
|
+
"react-hot-toast": "^2.6.0",
|
|
42
|
+
"usehooks-ts": "^3.1.1",
|
|
43
|
+
"viem": "2.39.0",
|
|
44
|
+
"wagmi": "2.19.5",
|
|
45
|
+
"zustand": "^5.0.12"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@tailwindcss/postcss": "
|
|
48
|
+
"@tailwindcss/postcss": "^4.2.4",
|
|
49
49
|
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
|
50
|
-
"@types/node": "^18.19.
|
|
51
|
-
"@types/react": "^19.
|
|
50
|
+
"@types/node": "^18.19.130",
|
|
51
|
+
"@types/react": "^19.2.14",
|
|
52
52
|
"abitype": "1.0.6",
|
|
53
|
-
"autoprefixer": "^10.4.12",
|
|
54
53
|
"bgipfs": "^0.0.12",
|
|
55
|
-
"eslint": "^9.
|
|
56
|
-
"eslint-config-next": "^
|
|
57
|
-
"eslint-config-prettier": "^10.1.
|
|
58
|
-
"eslint-plugin-prettier": "^5.
|
|
59
|
-
"postcss": "^8.
|
|
60
|
-
"prettier": "^3.
|
|
61
|
-
"tailwindcss": "^
|
|
54
|
+
"eslint": "^9.39.0",
|
|
55
|
+
"eslint-config-next": "^16.2.4",
|
|
56
|
+
"eslint-config-prettier": "^10.1.8",
|
|
57
|
+
"eslint-plugin-prettier": "^5.5.5",
|
|
58
|
+
"postcss": "^8.5.10",
|
|
59
|
+
"prettier": "^3.8.3",
|
|
60
|
+
"tailwindcss": "^4.2.4",
|
|
62
61
|
"type-fest": "^4.26.1",
|
|
63
62
|
"typescript": "^5.8.2",
|
|
64
|
-
"vercel": "
|
|
63
|
+
"vercel": "~52.2.1"
|
|
65
64
|
}
|
|
66
65
|
}
|
|
@@ -11,6 +11,13 @@ export type ScaffoldConfig = {
|
|
|
11
11
|
walletConnectProjectId: string;
|
|
12
12
|
onlyLocalBurnerWallet: boolean;
|
|
13
13
|
walletAutoConnect: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Multiplier applied to maxFeePerGas for contract writes.
|
|
16
|
+
* Since maxFeePerGas is a CEILING (actual fee = baseFee + priorityFee capped at this),
|
|
17
|
+
* values >1 protect against block-to-block base fee spikes without increasing real cost.
|
|
18
|
+
* Set to 1 or omit to disable.
|
|
19
|
+
*/
|
|
20
|
+
gasFeeMultiplier?: number;
|
|
14
21
|
};
|
|
15
22
|
|
|
16
23
|
export const DEFAULT_ALCHEMY_API_KEY = "oKxs-03sij-U_N0iOlrSsZFr29-IqbuF";
|
|
@@ -51,6 +58,9 @@ const scaffoldConfig = {
|
|
|
51
58
|
* 2. If user is not connected to any wallet: On reload, connect to burner wallet if burnerWallet.enabled is true && burnerWallet.onlyLocal is false
|
|
52
59
|
*/
|
|
53
60
|
walletAutoConnect: true,
|
|
61
|
+
|
|
62
|
+
// Gas fee multiplier for contract writes (ceiling, not actual cost — safe to be generous)
|
|
63
|
+
gasFeeMultiplier: 2,
|
|
54
64
|
} as const satisfies ScaffoldConfig;
|
|
55
65
|
|
|
56
66
|
export default scaffoldConfig;
|
|
@@ -1,10 +1,134 @@
|
|
|
1
|
-
@import "tailwindcss
|
|
2
|
-
|
|
3
|
-
@
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
|
|
3
|
+
@source "../app";
|
|
4
|
+
@source "../components";
|
|
5
|
+
@source "../utils";
|
|
6
|
+
|
|
7
|
+
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
|
|
8
|
+
|
|
9
|
+
@theme {
|
|
10
|
+
--font-sans: var(--font-inter), system-ui, sans-serif;
|
|
11
|
+
--shadow-center: 0 0 12px -2px rgb(0 0 0 / 0.05);
|
|
12
|
+
--animate-pulse-fast: pulse 1s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/* Orbitron utility — defined as a standalone @utility (not @theme) to avoid a
|
|
16
|
+
self-referential --font-orbitron, since next/font already emits that var. */
|
|
17
|
+
@utility font-orbitron {
|
|
18
|
+
font-family: var(--font-orbitron), sans-serif;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@plugin "daisyui" {
|
|
22
|
+
themes:
|
|
23
|
+
light,
|
|
24
|
+
dark --prefersdark;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@plugin "daisyui/theme" {
|
|
28
|
+
name: "light";
|
|
29
|
+
|
|
30
|
+
--color-primary: #93bbfb;
|
|
31
|
+
--color-primary-content: #212638;
|
|
32
|
+
--color-secondary: #dae8ff;
|
|
33
|
+
--color-secondary-content: #212638;
|
|
34
|
+
--color-accent: #93bbfb;
|
|
35
|
+
--color-accent-content: #212638;
|
|
36
|
+
--color-neutral: #212638;
|
|
37
|
+
--color-neutral-content: #ffffff;
|
|
38
|
+
--color-base-100: #ffffff;
|
|
39
|
+
--color-base-200: #f4f8ff;
|
|
40
|
+
--color-base-300: #dae8ff;
|
|
41
|
+
--color-base-content: #212638;
|
|
42
|
+
--color-info: #93bbfb;
|
|
43
|
+
--color-success: #34eeb6;
|
|
44
|
+
--color-warning: #ffcf72;
|
|
45
|
+
--color-error: #ff8863;
|
|
46
|
+
|
|
47
|
+
/* ported from --rounded-btn: 9999rem (daisyui v4) -> v5 radius family */
|
|
48
|
+
--radius-field: 9999rem;
|
|
49
|
+
--radius-box: 1rem;
|
|
50
|
+
|
|
51
|
+
/* ported from .tooltip { --tooltip-tail: 6px } */
|
|
52
|
+
--tt-tailw: 6px;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@plugin "daisyui/theme" {
|
|
56
|
+
name: "dark";
|
|
57
|
+
|
|
58
|
+
--color-primary: #212638;
|
|
59
|
+
--color-primary-content: #f9fbff;
|
|
60
|
+
--color-secondary: #323f61;
|
|
61
|
+
--color-secondary-content: #f9fbff;
|
|
62
|
+
--color-accent: #4969a6;
|
|
63
|
+
--color-accent-content: #f9fbff;
|
|
64
|
+
--color-neutral: #f9fbff;
|
|
65
|
+
--color-neutral-content: #385183;
|
|
66
|
+
--color-base-100: #000000;
|
|
67
|
+
--color-base-200: #2a3655;
|
|
68
|
+
--color-base-300: #212638;
|
|
69
|
+
--color-base-content: #f9fbff;
|
|
70
|
+
--color-info: #385183;
|
|
71
|
+
--color-success: #34eeb6;
|
|
72
|
+
--color-warning: #ffcf72;
|
|
73
|
+
--color-error: #ff8863;
|
|
74
|
+
|
|
75
|
+
/* ported from --rounded-btn: 9999rem (daisyui v4) -> v5 radius family */
|
|
76
|
+
--radius-field: 9999rem;
|
|
77
|
+
--radius-box: 1rem;
|
|
78
|
+
|
|
79
|
+
/* ported from .tooltip { --tooltip-tail: 6px; --tooltip-color: oklch(var(--p)) } */
|
|
80
|
+
--tt-tailw: 6px;
|
|
81
|
+
--tt-bg: var(--color-primary);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Per-theme custom variables ported verbatim from tailwind.config.js daisyui themes.
|
|
85
|
+
Defined with explicit [data-theme] selectors (not inside the daisyui theme blocks,
|
|
86
|
+
which only accept daisyui's own tokens). */
|
|
87
|
+
[data-theme="light"] {
|
|
88
|
+
--surface: rgba(0, 0, 0, 0.04);
|
|
89
|
+
--bg-border: #ffffff;
|
|
90
|
+
--round-color: #000;
|
|
91
|
+
--gradient-start: #3283eb;
|
|
92
|
+
--gradient-end: #e3066e;
|
|
93
|
+
--border-color: rgba(0, 0, 0, 0.07);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
[data-theme="dark"] {
|
|
97
|
+
--surface: rgba(255, 255, 255, 0.04);
|
|
98
|
+
--bg-border: #000000;
|
|
99
|
+
--round-color: #e3066e;
|
|
100
|
+
--gradient-start: #3283eb;
|
|
101
|
+
--gradient-end: #e3066e;
|
|
102
|
+
--border-color: rgba(255, 255, 255, 0.2);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/*
|
|
106
|
+
Tailwind CSS v4 changed the default border color to `currentColor`.
|
|
107
|
+
Restore the v3 default (gray-200) so existing borders look identical.
|
|
108
|
+
*/
|
|
109
|
+
@layer base {
|
|
110
|
+
*,
|
|
111
|
+
::after,
|
|
112
|
+
::before,
|
|
113
|
+
::backdrop,
|
|
114
|
+
::file-selector-button {
|
|
115
|
+
border-color: var(--color-gray-200, currentColor);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
4
118
|
|
|
5
119
|
:root,
|
|
6
120
|
[data-theme] {
|
|
7
|
-
background:
|
|
121
|
+
background: var(--color-base-100);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* ported from .link / .link:hover (defined per-theme in tailwind.config.js;
|
|
125
|
+
identical across light & dark, so defined once globally) */
|
|
126
|
+
.link {
|
|
127
|
+
text-underline-offset: 2px;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.link:hover {
|
|
131
|
+
opacity: 80%;
|
|
8
132
|
}
|
|
9
133
|
|
|
10
134
|
body {
|
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
"noEmit": true,
|
|
10
10
|
"esModuleInterop": true,
|
|
11
11
|
"module": "esnext",
|
|
12
|
-
"moduleResolution": "
|
|
12
|
+
"moduleResolution": "bundler",
|
|
13
13
|
"resolveJsonModule": true,
|
|
14
14
|
"isolatedModules": true,
|
|
15
|
-
"jsx": "
|
|
15
|
+
"jsx": "react-jsx",
|
|
16
16
|
"incremental": true,
|
|
17
17
|
"paths": {
|
|
18
18
|
"~~/*": ["./*"]
|
|
@@ -23,6 +23,6 @@
|
|
|
23
23
|
}
|
|
24
24
|
]
|
|
25
25
|
},
|
|
26
|
-
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
26
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", ".next/dev/types/**/*.ts"],
|
|
27
27
|
"exclude": ["node_modules"]
|
|
28
28
|
}
|
|
@@ -5,6 +5,38 @@ import {
|
|
|
5
5
|
isContractHasConstructor,
|
|
6
6
|
} from "./contract";
|
|
7
7
|
import { getRpcUrlFromChain } from "./network";
|
|
8
|
+
import { createPublicClient, http, formatUnits } from "viem";
|
|
9
|
+
|
|
10
|
+
const DEFAULT_GAS_FEE_MULTIPLIER = 3;
|
|
11
|
+
const MIN_FEE_GWEI = 0.1;
|
|
12
|
+
|
|
13
|
+
function getGasFeeMultiplier(): number {
|
|
14
|
+
const envVal = process.env["DEPLOY_GAS_FEE_MULTIPLIER"];
|
|
15
|
+
if (envVal) {
|
|
16
|
+
const parsed = parseFloat(envVal);
|
|
17
|
+
if (!isNaN(parsed) && parsed > 0) {
|
|
18
|
+
return parsed;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return DEFAULT_GAS_FEE_MULTIPLIER;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function getBufferedMaxFeeGwei(rpcUrl: string): Promise<number> {
|
|
25
|
+
try {
|
|
26
|
+
const publicClient = createPublicClient({
|
|
27
|
+
transport: http(rpcUrl),
|
|
28
|
+
});
|
|
29
|
+
const block = await publicClient.getBlock({ blockTag: "latest" });
|
|
30
|
+
if (block.baseFeePerGas === null) {
|
|
31
|
+
return MIN_FEE_GWEI;
|
|
32
|
+
}
|
|
33
|
+
const baseFeeGwei = Number(formatUnits(block.baseFeePerGas, 9));
|
|
34
|
+
const buffered = baseFeeGwei * getGasFeeMultiplier();
|
|
35
|
+
return Math.max(buffered, MIN_FEE_GWEI);
|
|
36
|
+
} catch {
|
|
37
|
+
return MIN_FEE_GWEI;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
8
40
|
|
|
9
41
|
export async function buildDeployCommand(
|
|
10
42
|
config: DeploymentConfig,
|
|
@@ -18,6 +50,12 @@ export async function buildDeployCommand(
|
|
|
18
50
|
|
|
19
51
|
if (deployOptions.maxFee) {
|
|
20
52
|
baseCommand += ` --max-fee-per-gas-gwei=${deployOptions.maxFee}`;
|
|
53
|
+
} else {
|
|
54
|
+
// maxFeePerGas is a CEILING (actual charge = base fee), so over-provisioning is safe and free.
|
|
55
|
+
// Cargo stylus without this flag uses a tight estimate that the base fee can creep past.
|
|
56
|
+
const rpcUrl = getRpcUrlFromChain(config.chain);
|
|
57
|
+
const maxFeeGwei = await getBufferedMaxFeeGwei(rpcUrl);
|
|
58
|
+
baseCommand += ` --max-fee-per-gas-gwei=${maxFeeGwei}`;
|
|
21
59
|
}
|
|
22
60
|
|
|
23
61
|
if (!deployOptions.verify) {
|
package/templates/base/readme.md
CHANGED
|
@@ -67,12 +67,12 @@ Check the [Rust installation guide](https://www.rust-lang.org/tools/install) for
|
|
|
67
67
|
Then install the Stylus CLI tools:
|
|
68
68
|
|
|
69
69
|
```bash
|
|
70
|
-
cargo install --force
|
|
70
|
+
cargo install --force --locked cargo-stylus@0.6.3
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
**Prerequisite:**
|
|
74
74
|
|
|
75
|
-
- `cargo-stylus` version
|
|
75
|
+
- `cargo-stylus` version `0.6.3`
|
|
76
76
|
- `rustc` version match with `packages/stylus/your-contract/rust-toolchain.toml`
|
|
77
77
|
|
|
78
78
|
Set default `toolchain` match `rust-toolchain.toml` and add the `wasm32-unknown-unknown` build target to your Rust compiler:
|