create-stylus 1.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 (222) hide show
  1. package/.github/issue_template.md +7 -0
  2. package/.github/pull_request_template.md +11 -0
  3. package/.github/workflows/release-alpha.yml +32 -0
  4. package/.yarnrc.yml +1 -0
  5. package/CONTRIBUTING.md +42 -0
  6. package/README.md +66 -0
  7. package/bin/create-dapp-ss.js +4 -0
  8. package/dist/cli.js +656 -0
  9. package/dist/cli.js.map +1 -0
  10. package/package.json +46 -0
  11. package/rollup.config.js +22 -0
  12. package/src/cli.ts +14 -0
  13. package/src/extensions.json +14 -0
  14. package/src/main.ts +70 -0
  15. package/src/tasks/copy-extension-file.ts +227 -0
  16. package/src/tasks/copy-template-files.ts +252 -0
  17. package/src/tasks/create-first-git-commit.ts +35 -0
  18. package/src/tasks/create-project-directory.ts +34 -0
  19. package/src/tasks/index.ts +5 -0
  20. package/src/tasks/install-packages.ts +15 -0
  21. package/src/tasks/prettier-format.ts +17 -0
  22. package/src/types.ts +31 -0
  23. package/src/utils/consts.ts +1 -0
  24. package/src/utils/find-files-recursively.ts +19 -0
  25. package/src/utils/link.ts +44 -0
  26. package/src/utils/load-extensions.ts +10 -0
  27. package/src/utils/merge-package-json.ts +33 -0
  28. package/src/utils/parse-arguments-into-options.ts +38 -0
  29. package/src/utils/prompt-for-missing-options.ts +53 -0
  30. package/src/utils/render-intro-message.ts +11 -0
  31. package/src/utils/render-outro-message.ts +34 -0
  32. package/templates/base/.github/ISSUE_TEMPLATE/bug_report.yml +58 -0
  33. package/templates/base/.github/ISSUE_TEMPLATE/config.yml +8 -0
  34. package/templates/base/.github/pull_request_template.md +16 -0
  35. package/templates/base/.github/workflows/lint.yaml +300 -0
  36. package/templates/base/.gitignore.template.mjs +19 -0
  37. package/templates/base/.gitmodules +0 -0
  38. package/templates/base/.husky/pre-commit +4 -0
  39. package/templates/base/.lintstagedrc.js +21 -0
  40. package/templates/base/.vscode/settings.json +7 -0
  41. package/templates/base/.yarn/plugins/@yarnpkg/plugin-typescript.cjs +9 -0
  42. package/templates/base/.yarn/releases/yarn-3.2.3.cjs +783 -0
  43. package/templates/base/.yarnrc.yml +11 -0
  44. package/templates/base/CONTRIBUTING.md +86 -0
  45. package/templates/base/LICENCE +21 -0
  46. package/templates/base/nitro-devnode/LICENSE +201 -0
  47. package/templates/base/nitro-devnode/README.md +70 -0
  48. package/templates/base/nitro-devnode/run-dev-node.sh +132 -0
  49. package/templates/base/nitro-devnode/start-chain-with-cors.sh +150 -0
  50. package/templates/base/nitro-devnode/stylus-deployer-bytecode.txt +1 -0
  51. package/templates/base/nitro-devnode/stylus-dev/Dockerfile +10 -0
  52. package/templates/base/package.json +43 -0
  53. package/templates/base/packages/nextjs/.env.example +13 -0
  54. package/templates/base/packages/nextjs/.eslintignore +11 -0
  55. package/templates/base/packages/nextjs/.eslintrc.json +15 -0
  56. package/templates/base/packages/nextjs/.gitignore.template.mjs +42 -0
  57. package/templates/base/packages/nextjs/.prettierrc.js +9 -0
  58. package/templates/base/packages/nextjs/.prettierrc.json +8 -0
  59. package/templates/base/packages/nextjs/app/blockexplorer/_components/AddressCodeTab.tsx +27 -0
  60. package/templates/base/packages/nextjs/app/blockexplorer/_components/AddressComponent.tsx +36 -0
  61. package/templates/base/packages/nextjs/app/blockexplorer/_components/AddressLogsTab.tsx +21 -0
  62. package/templates/base/packages/nextjs/app/blockexplorer/_components/AddressStorageTab.tsx +61 -0
  63. package/templates/base/packages/nextjs/app/blockexplorer/_components/BackButton.tsx +12 -0
  64. package/templates/base/packages/nextjs/app/blockexplorer/_components/ContractTabs.tsx +102 -0
  65. package/templates/base/packages/nextjs/app/blockexplorer/_components/PaginationButton.tsx +39 -0
  66. package/templates/base/packages/nextjs/app/blockexplorer/_components/SearchBar.tsx +49 -0
  67. package/templates/base/packages/nextjs/app/blockexplorer/_components/TransactionHash.tsx +28 -0
  68. package/templates/base/packages/nextjs/app/blockexplorer/_components/TransactionsTable.tsx +71 -0
  69. package/templates/base/packages/nextjs/app/blockexplorer/_components/index.tsx +7 -0
  70. package/templates/base/packages/nextjs/app/blockexplorer/address/[address]/page.tsx +101 -0
  71. package/templates/base/packages/nextjs/app/blockexplorer/layout.tsx +12 -0
  72. package/templates/base/packages/nextjs/app/blockexplorer/page.tsx +83 -0
  73. package/templates/base/packages/nextjs/app/blockexplorer/transaction/[txHash]/page.tsx +23 -0
  74. package/templates/base/packages/nextjs/app/blockexplorer/transaction/_components/TransactionComp.tsx +152 -0
  75. package/templates/base/packages/nextjs/app/debug/_components/DebugContracts.tsx +73 -0
  76. package/templates/base/packages/nextjs/app/debug/_components/contract/ContractInput.tsx +84 -0
  77. package/templates/base/packages/nextjs/app/debug/_components/contract/ContractReadMethods.tsx +43 -0
  78. package/templates/base/packages/nextjs/app/debug/_components/contract/ContractUI.tsx +164 -0
  79. package/templates/base/packages/nextjs/app/debug/_components/contract/ContractVariables.tsx +50 -0
  80. package/templates/base/packages/nextjs/app/debug/_components/contract/ContractWriteMethods.tsx +49 -0
  81. package/templates/base/packages/nextjs/app/debug/_components/contract/DisplayVariable.tsx +85 -0
  82. package/templates/base/packages/nextjs/app/debug/_components/contract/InheritanceTooltip.tsx +14 -0
  83. package/templates/base/packages/nextjs/app/debug/_components/contract/ReadOnlyFunctionForm.tsx +102 -0
  84. package/templates/base/packages/nextjs/app/debug/_components/contract/Tuple.tsx +44 -0
  85. package/templates/base/packages/nextjs/app/debug/_components/contract/TupleArray.tsx +142 -0
  86. package/templates/base/packages/nextjs/app/debug/_components/contract/TxReceipt.tsx +42 -0
  87. package/templates/base/packages/nextjs/app/debug/_components/contract/WriteOnlyFunctionForm.tsx +144 -0
  88. package/templates/base/packages/nextjs/app/debug/_components/contract/index.tsx +8 -0
  89. package/templates/base/packages/nextjs/app/debug/_components/contract/utilsContract.tsx +166 -0
  90. package/templates/base/packages/nextjs/app/debug/_components/contract/utilsDisplay.tsx +114 -0
  91. package/templates/base/packages/nextjs/app/debug/page.tsx +14 -0
  92. package/templates/base/packages/nextjs/app/layout.tsx +67 -0
  93. package/templates/base/packages/nextjs/app/not-found.tsx +16 -0
  94. package/templates/base/packages/nextjs/app/page.tsx +94 -0
  95. package/templates/base/packages/nextjs/components/Background.tsx +37 -0
  96. package/templates/base/packages/nextjs/components/Card.tsx +40 -0
  97. package/templates/base/packages/nextjs/components/Footer.tsx +93 -0
  98. package/templates/base/packages/nextjs/components/Header.tsx +114 -0
  99. package/templates/base/packages/nextjs/components/ScaffoldEthAppWithProviders.tsx +77 -0
  100. package/templates/base/packages/nextjs/components/SwitchTheme.tsx +41 -0
  101. package/templates/base/packages/nextjs/components/ThemeProvider.tsx +13 -0
  102. package/templates/base/packages/nextjs/components/assets/BuidlGuidlLogo.tsx +18 -0
  103. package/templates/base/packages/nextjs/components/scaffold-eth/Address/Address.tsx +187 -0
  104. package/templates/base/packages/nextjs/components/scaffold-eth/Address/AddressCopyIcon.tsx +23 -0
  105. package/templates/base/packages/nextjs/components/scaffold-eth/Address/AddressLinkWrapper.tsx +29 -0
  106. package/templates/base/packages/nextjs/components/scaffold-eth/Balance.tsx +75 -0
  107. package/templates/base/packages/nextjs/components/scaffold-eth/BlockieAvatar.tsx +17 -0
  108. package/templates/base/packages/nextjs/components/scaffold-eth/Faucet.tsx +131 -0
  109. package/templates/base/packages/nextjs/components/scaffold-eth/FaucetButton.tsx +75 -0
  110. package/templates/base/packages/nextjs/components/scaffold-eth/Input/AddressInput.tsx +120 -0
  111. package/templates/base/packages/nextjs/components/scaffold-eth/Input/Bytes32Input.tsx +31 -0
  112. package/templates/base/packages/nextjs/components/scaffold-eth/Input/BytesInput.tsx +28 -0
  113. package/templates/base/packages/nextjs/components/scaffold-eth/Input/EtherInput.tsx +128 -0
  114. package/templates/base/packages/nextjs/components/scaffold-eth/Input/InputBase.tsx +66 -0
  115. package/templates/base/packages/nextjs/components/scaffold-eth/Input/IntegerInput.tsx +63 -0
  116. package/templates/base/packages/nextjs/components/scaffold-eth/Input/index.ts +9 -0
  117. package/templates/base/packages/nextjs/components/scaffold-eth/Input/utils.ts +109 -0
  118. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressInfoDropdown.tsx +121 -0
  119. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressQRCodeModal.tsx +33 -0
  120. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/BurnerWalletModal.tsx +63 -0
  121. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/NetworkOptions.tsx +48 -0
  122. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/WrongNetworkDropdown.tsx +32 -0
  123. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/index.tsx +89 -0
  124. package/templates/base/packages/nextjs/components/scaffold-eth/index.tsx +7 -0
  125. package/templates/base/packages/nextjs/contracts/deployedContracts.ts +9 -0
  126. package/templates/base/packages/nextjs/contracts/externalContracts.ts +16 -0
  127. package/templates/base/packages/nextjs/eslint.config.mjs +32 -0
  128. package/templates/base/packages/nextjs/hooks/scaffold-eth/index.ts +17 -0
  129. package/templates/base/packages/nextjs/hooks/scaffold-eth/useAnimationConfig.ts +20 -0
  130. package/templates/base/packages/nextjs/hooks/scaffold-eth/useContractLogs.ts +40 -0
  131. package/templates/base/packages/nextjs/hooks/scaffold-eth/useCopyToClipboard.ts +19 -0
  132. package/templates/base/packages/nextjs/hooks/scaffold-eth/useDeployedContractInfo.ts +86 -0
  133. package/templates/base/packages/nextjs/hooks/scaffold-eth/useDisplayUsdMode.ts +21 -0
  134. package/templates/base/packages/nextjs/hooks/scaffold-eth/useFetchBlocks.ts +133 -0
  135. package/templates/base/packages/nextjs/hooks/scaffold-eth/useInitializeNativeCurrencyPrice.ts +32 -0
  136. package/templates/base/packages/nextjs/hooks/scaffold-eth/useNativeCurrencyPrice.ts +34 -0
  137. package/templates/base/packages/nextjs/hooks/scaffold-eth/useNetworkColor.ts +22 -0
  138. package/templates/base/packages/nextjs/hooks/scaffold-eth/useOutsideClick.ts +23 -0
  139. package/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldContract.ts +65 -0
  140. package/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldEventHistory.ts +213 -0
  141. package/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldReadContract.ts +80 -0
  142. package/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldWatchContractEvent.ts +40 -0
  143. package/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldWriteContract.ts +191 -0
  144. package/templates/base/packages/nextjs/hooks/scaffold-eth/useSelectedNetwork.ts +18 -0
  145. package/templates/base/packages/nextjs/hooks/scaffold-eth/useTargetNetwork.ts +23 -0
  146. package/templates/base/packages/nextjs/hooks/scaffold-eth/useTransactor.tsx +114 -0
  147. package/templates/base/packages/nextjs/hooks/scaffold-eth/useWatchBalance.ts +21 -0
  148. package/templates/base/packages/nextjs/icons/CompassIcon.tsx +39 -0
  149. package/templates/base/packages/nextjs/icons/DarkBugAntIcon.tsx +30 -0
  150. package/templates/base/packages/nextjs/icons/LightBugAntIcon.tsx +52 -0
  151. package/templates/base/packages/nextjs/next-env.d.ts +5 -0
  152. package/templates/base/packages/nextjs/next.config.js +19 -0
  153. package/templates/base/packages/nextjs/package.json +58 -0
  154. package/templates/base/packages/nextjs/postcss.config.js +6 -0
  155. package/templates/base/packages/nextjs/public/debug-image.png +0 -0
  156. package/templates/base/packages/nextjs/public/favicon.png +0 -0
  157. package/templates/base/packages/nextjs/public/logo.svg +8 -0
  158. package/templates/base/packages/nextjs/public/manifest.json +5 -0
  159. package/templates/base/packages/nextjs/public/thumbnail.jpg +0 -0
  160. package/templates/base/packages/nextjs/react-copy-to-clipboard.d.ts +44 -0
  161. package/templates/base/packages/nextjs/scaffold.config.ts +56 -0
  162. package/templates/base/packages/nextjs/services/store/store.ts +39 -0
  163. package/templates/base/packages/nextjs/services/web3/wagmiConfig.tsx +44 -0
  164. package/templates/base/packages/nextjs/services/web3/wagmiConnectors.tsx +51 -0
  165. package/templates/base/packages/nextjs/styles/globals.css +80 -0
  166. package/templates/base/packages/nextjs/tailwind.config.js +97 -0
  167. package/templates/base/packages/nextjs/tsconfig.json +28 -0
  168. package/templates/base/packages/nextjs/types/abitype/abi.d.ts +16 -0
  169. package/templates/base/packages/nextjs/types/utils.ts +3 -0
  170. package/templates/base/packages/nextjs/utils/scaffold-eth/block.ts +17 -0
  171. package/templates/base/packages/nextjs/utils/scaffold-eth/common.ts +8 -0
  172. package/templates/base/packages/nextjs/utils/scaffold-eth/contract.ts +352 -0
  173. package/templates/base/packages/nextjs/utils/scaffold-eth/contractsData.ts +11 -0
  174. package/templates/base/packages/nextjs/utils/scaffold-eth/decodeTxData.ts +65 -0
  175. package/templates/base/packages/nextjs/utils/scaffold-eth/fetchPriceFromUniswap.ts +72 -0
  176. package/templates/base/packages/nextjs/utils/scaffold-eth/getMetadata.ts +50 -0
  177. package/templates/base/packages/nextjs/utils/scaffold-eth/getParsedError.ts +35 -0
  178. package/templates/base/packages/nextjs/utils/scaffold-eth/index.ts +6 -0
  179. package/templates/base/packages/nextjs/utils/scaffold-eth/notification.tsx +90 -0
  180. package/templates/base/packages/nextjs/utils/scaffold-stylus/burner.ts +59 -0
  181. package/templates/base/packages/nextjs/utils/scaffold-stylus/chain.ts +42 -0
  182. package/templates/base/packages/nextjs/utils/scaffold-stylus/index.ts +3 -0
  183. package/templates/base/packages/nextjs/utils/scaffold-stylus/networks.ts +94 -0
  184. package/templates/base/packages/nextjs/vercel.json +3 -0
  185. package/templates/base/packages/stylus/.env.example +13 -0
  186. package/templates/base/packages/stylus/.eslintrc.js +23 -0
  187. package/templates/base/packages/stylus/.gitignore.template.mjs +7 -0
  188. package/templates/base/packages/stylus/README.md +263 -0
  189. package/templates/base/packages/stylus/header.png +0 -0
  190. package/templates/base/packages/stylus/jest.config.js +15 -0
  191. package/templates/base/packages/stylus/package.json +49 -0
  192. package/templates/base/packages/stylus/scripts/deploy.ts +29 -0
  193. package/templates/base/packages/stylus/scripts/deploy_all_contracts.ts +59 -0
  194. package/templates/base/packages/stylus/scripts/deploy_contract.ts +93 -0
  195. package/templates/base/packages/stylus/scripts/deploy_wrapper.ts +79 -0
  196. package/templates/base/packages/stylus/scripts/export_abi.ts +87 -0
  197. package/templates/base/packages/stylus/scripts/index.ts +0 -0
  198. package/templates/base/packages/stylus/scripts/new_module.sh +35 -0
  199. package/templates/base/packages/stylus/scripts/test_network.ts +31 -0
  200. package/templates/base/packages/stylus/scripts/utils/command.ts +165 -0
  201. package/templates/base/packages/stylus/scripts/utils/contract.ts +219 -0
  202. package/templates/base/packages/stylus/scripts/utils/deployment.ts +136 -0
  203. package/templates/base/packages/stylus/scripts/utils/index.ts +6 -0
  204. package/templates/base/packages/stylus/scripts/utils/network.ts +112 -0
  205. package/templates/base/packages/stylus/scripts/utils/type.ts +48 -0
  206. package/templates/base/packages/stylus/scripts/utils.ts +3 -0
  207. package/templates/base/packages/stylus/tsconfig.json +41 -0
  208. package/templates/base/packages/stylus/your-contract/.cargo/config.toml +18 -0
  209. package/templates/base/packages/stylus/your-contract/Cargo.lock +5744 -0
  210. package/templates/base/packages/stylus/your-contract/Cargo.toml +46 -0
  211. package/templates/base/packages/stylus/your-contract/examples/counter.rs +78 -0
  212. package/templates/base/packages/stylus/your-contract/header.png +0 -0
  213. package/templates/base/packages/stylus/your-contract/licenses/Apache-2.0 +201 -0
  214. package/templates/base/packages/stylus/your-contract/licenses/COPYRIGHT.md +5 -0
  215. package/templates/base/packages/stylus/your-contract/licenses/DCO.txt +34 -0
  216. package/templates/base/packages/stylus/your-contract/licenses/MIT +21 -0
  217. package/templates/base/packages/stylus/your-contract/rust-toolchain.toml +2 -0
  218. package/templates/base/packages/stylus/your-contract/src/lib.rs +211 -0
  219. package/templates/base/packages/stylus/your-contract/src/main.rs +10 -0
  220. package/templates/base/readme.md +187 -0
  221. package/templates/base/yarn.lock +17860 -0
  222. package/tsconfig.json +13 -0
@@ -0,0 +1,35 @@
1
+ #!/bin/bash
2
+
3
+ # Check if module name is provided
4
+ if [ -z "$1" ]; then
5
+ echo "Error: Module name is required"
6
+ echo "Usage: $0 <module_name>"
7
+ exit 1
8
+ fi
9
+
10
+ MODULE_NAME="$1"
11
+
12
+ CARGO_CMD="cargo stylus new \"$MODULE_NAME\""
13
+
14
+ echo "Creating new module: $MODULE_NAME"
15
+
16
+ if output=$(eval $CARGO_CMD 2>&1); then
17
+ cd "$MODULE_NAME"
18
+ # Remove unnecessary files
19
+ rm -rf .git .github ci .gitignore examples licenses .env.example header.png README.md
20
+ # Update Cargo.toml name field to $MODULE_NAME
21
+ if [ -f Cargo.toml ]; then
22
+ sed -i "s/^name = \".*\"/name = \"$MODULE_NAME\"/" Cargo.toml
23
+ fi
24
+ # Update main.rs module name in print_from_args call
25
+ if [ -f src/main.rs ]; then
26
+ MODULE_NAME_UNDERSCORE=$(echo "$MODULE_NAME" | tr '-' '_')
27
+ sed -i "s/^[[:space:]]*stylus_hello_world::print_from_args();/\t$MODULE_NAME_UNDERSCORE::print_from_args();/" src/main.rs
28
+ fi
29
+ cargo generate-lockfile
30
+ echo "New module created successfully"
31
+ else
32
+ echo "Error: Failed to create new module with '$CARGO_CMD'"
33
+ echo "$output"
34
+ exit 1
35
+ fi
@@ -0,0 +1,31 @@
1
+ import { getChain } from "./utils/";
2
+ import { SUPPORTED_NETWORKS } from "./utils/";
3
+
4
+ function testNetworkFunctionality() {
5
+ console.log("🧪 Testing network functionality...\n");
6
+
7
+ const testNetworks = [
8
+ ...Object.keys(SUPPORTED_NETWORKS),
9
+ "invalid-network",
10
+ ];
11
+
12
+ testNetworks.forEach(network => {
13
+ const chain = getChain(network);
14
+ if (chain) {
15
+ console.log(`āœ… ${network}: ${chain.rpcUrl}`);
16
+ } else {
17
+ console.log(`āŒ ${network}: Not found in viem chains`);
18
+ }
19
+ });
20
+
21
+ console.log("\nšŸ“ Usage examples:");
22
+ Object.keys(SUPPORTED_NETWORKS).forEach(network => {
23
+ const chain = getChain(network);
24
+ console.log(` yarn deploy --network ${chain?.name}\t# Deploy to ${chain?.name}`);
25
+ console.log(` yarn deploy --network ${chain?.alias}\t\t# Deploy to ${chain?.name} (alias)`);
26
+ });
27
+ }
28
+
29
+ if (require.main === module) {
30
+ testNetworkFunctionality();
31
+ }
@@ -0,0 +1,165 @@
1
+ import { spawn } from "child_process";
2
+ import { DeploymentConfig, DeployOptions } from "./type";
3
+ import { extractGasPriceFromOutput } from "./contract";
4
+
5
+ export async function buildDeployCommand(
6
+ config: DeploymentConfig,
7
+ deployOptions: DeployOptions,
8
+ ) {
9
+ const baseCommand = `cargo stylus deploy --endpoint='${config.chain?.rpcUrl}' --private-key='${config.privateKey}' --no-verify`;
10
+
11
+ if (deployOptions.estimateGas) {
12
+ return `${baseCommand} --estimate-gas`;
13
+ }
14
+
15
+ if (deployOptions.maxFee) {
16
+ return `${baseCommand} --max-fee-per-gas-gwei=${deployOptions.maxFee}`;
17
+ }
18
+
19
+ // const gasPrice = await estimateGasPrice(config);
20
+ return baseCommand;
21
+ }
22
+
23
+ export async function estimateGasPrice(
24
+ config: DeploymentConfig,
25
+ ): Promise<string> {
26
+ const deployCommand = `cargo stylus deploy --endpoint='${config.chain?.rpcUrl}' --private-key='${config.privateKey}' --no-verify --estimate-gas`;
27
+ const deployOutput = await executeCommand(
28
+ deployCommand,
29
+ config.contractName,
30
+ "Estimating gas price with cargo stylus",
31
+ );
32
+ const gasPrice = extractGasPriceFromOutput(deployOutput);
33
+ if (gasPrice) {
34
+ return gasPrice;
35
+ }
36
+ return "0";
37
+ }
38
+
39
+ export function executeCommand(
40
+ command: string,
41
+ cwd: string,
42
+ description: string,
43
+ ): Promise<string> {
44
+ console.log(`\nšŸ”„ ${description}...`);
45
+ // Sanitize command to hide private key (create a copy to avoid modifying original)
46
+ const sanitizedCommand = command.slice();
47
+ console.log(
48
+ `Executing: ${sanitizedCommand.replace(/--private-key=[^\s]+/g, "--private-key=***")}`,
49
+ );
50
+
51
+ return new Promise((resolve, reject) => {
52
+ const childProcess = spawn(command, [], {
53
+ cwd,
54
+ shell: true,
55
+ stdio: ["inherit", "pipe", "pipe"],
56
+ });
57
+
58
+ let output = "";
59
+ let errorOutput = "";
60
+ const outputLines: string[] = [];
61
+ let errorLines: string[] = [];
62
+
63
+ // Handle stdout
64
+ if (childProcess.stdout) {
65
+ childProcess.stdout.on("data", (data: Buffer) => {
66
+ const chunk = data.toString();
67
+ output += chunk;
68
+ const newLines = chunk.split("\n");
69
+ outputLines.push(...newLines);
70
+ });
71
+ }
72
+
73
+ // Handle stderr
74
+ if (childProcess.stderr) {
75
+ childProcess.stderr.on("data", (data: Buffer) => {
76
+ const chunk = data.toString();
77
+ errorOutput += chunk;
78
+ const newLines = chunk.split("\n");
79
+ errorLines.push(...newLines);
80
+ // Keep only the last 5 lines
81
+ if (errorLines.length > 5) {
82
+ errorLines = errorLines.slice(-5);
83
+ }
84
+ });
85
+ }
86
+
87
+ // Handle process completion
88
+ childProcess.on("close", (code: number | null) => {
89
+ if (code === 0) {
90
+ console.log(`\nāœ… ${description} completed successfully!`);
91
+ // Print output starting from "project metadata hash computed on deployment" or error patterns, or all logs if not found
92
+ // if (outputLines.length > 0) {
93
+ // const metadataIndex = outputLines.findIndex((line) =>
94
+ // line.includes("project metadata hash computed on deployment"),
95
+ // );
96
+ // const errorIndex = outputLines.findIndex((line) =>
97
+ // line.includes("error["),
98
+ // );
99
+
100
+ // let startIndex = -1;
101
+ // if (metadataIndex >= 0) {
102
+ // startIndex = metadataIndex;
103
+ // } else if (errorIndex >= 0) {
104
+ // startIndex = errorIndex;
105
+ // }
106
+
107
+ // if (startIndex >= 0) {
108
+ // const linesToPrint = outputLines.slice(startIndex);
109
+ // linesToPrint.forEach((line) => {
110
+ // if (line.trim()) console.log(line);
111
+ // });
112
+ // } else {
113
+ // outputLines.forEach((line) => {
114
+ // if (line.trim()) console.log(line);
115
+ // });
116
+ // }
117
+ // }
118
+ resolve(output);
119
+ } else {
120
+ console.error(`\nāŒ ${description} failed with exit code ${code}`);
121
+ // Print error output starting from "project metadata hash computed on deployment" or error patterns, or all logs if not found
122
+ if (errorLines.length > 0) {
123
+ const metadataIndex = errorLines.findIndex((line) =>
124
+ line.includes("project metadata hash computed on deployment"),
125
+ );
126
+ const errorIndex = errorLines.findIndex((line) =>
127
+ line.includes("error["),
128
+ );
129
+
130
+ let startIndex = -1;
131
+ if (metadataIndex >= 0) {
132
+ startIndex = metadataIndex;
133
+ } else if (errorIndex >= 0) {
134
+ startIndex = errorIndex;
135
+ }
136
+
137
+ if (startIndex >= 0) {
138
+ const linesToPrint = errorLines.slice(startIndex);
139
+ linesToPrint.forEach((line) => {
140
+ if (line.trim()) console.error(line);
141
+ });
142
+ } else {
143
+ errorLines.forEach((line) => {
144
+ if (line.trim()) console.error(line);
145
+ });
146
+ }
147
+ }
148
+ if (errorOutput) {
149
+ console.error(errorOutput);
150
+ }
151
+ reject(
152
+ new Error(
153
+ `Command failed with exit code ${code}. Error output: ${errorOutput}`,
154
+ ),
155
+ );
156
+ }
157
+ });
158
+
159
+ // Handle process errors
160
+ childProcess.on("error", (error: Error) => {
161
+ console.error(`\nāŒ ${description} failed:`, error);
162
+ reject(error);
163
+ });
164
+ });
165
+ }
@@ -0,0 +1,219 @@
1
+ import * as path from "path";
2
+ import * as fs from "fs";
3
+ import { ethers } from "ethers";
4
+ import toml from "toml";
5
+ import prettier from "prettier";
6
+ import { ExportConfig } from "./type";
7
+ import { getContractDataFromDeployments } from "./deployment";
8
+
9
+ export const generatedContractComment = `
10
+ /**
11
+ * This file is autogenerated by scaffold-stylus.
12
+ * You should not edit it manually or your changes might be overwritten.
13
+ */
14
+ `;
15
+
16
+ /**
17
+ * Reads the contract name from Cargo.toml in the given contract folder.
18
+ * Throws if the file or name field is missing.
19
+ */
20
+ export function getContractNameFromCargoToml(contractFolder: string): string {
21
+ const cargoTomlPath = path.join(contractFolder, "Cargo.toml");
22
+ if (!fs.existsSync(cargoTomlPath)) {
23
+ throw new Error(
24
+ `Cargo.toml not found in contract folder: ${cargoTomlPath}`,
25
+ );
26
+ }
27
+ const tomlContent = fs.readFileSync(cargoTomlPath, "utf8");
28
+ let parsed: any;
29
+ try {
30
+ parsed = toml.parse(tomlContent);
31
+ } catch (e) {
32
+ throw new Error(`Failed to parse Cargo.toml: ${e}`);
33
+ }
34
+ if (!parsed.package || !parsed.package.name) {
35
+ throw new Error(
36
+ `Missing [package] or name field in Cargo.toml at ${cargoTomlPath}`,
37
+ );
38
+ }
39
+ return parsed.package.name;
40
+ }
41
+
42
+ export function getExportConfig(
43
+ contractFolder?: string,
44
+ contractName?: string,
45
+ ): ExportConfig {
46
+ if (!contractFolder) {
47
+ throw new Error("Contract folder is required");
48
+ }
49
+
50
+ contractName = contractName || getContractNameFromCargoToml(contractFolder);
51
+ const deploymentDir = process.env["DEPLOYMENT_DIR"] || "deployments";
52
+
53
+ const deploymentData = getContractDataFromDeployments(
54
+ deploymentDir,
55
+ contractName,
56
+ );
57
+ if (!deploymentData) {
58
+ throw new Error(
59
+ `āŒ Contract address for '${contractName}' not found in ${deploymentDir}/addresses.json. Please deploy the contract first.`,
60
+ );
61
+ }
62
+
63
+ return {
64
+ contractFolder,
65
+ contractName,
66
+ deploymentDir,
67
+ contractAddress: deploymentData.address,
68
+ chainId: deploymentData.chainId,
69
+ };
70
+ }
71
+
72
+ export function isContractFolder(contractFolder: string): boolean {
73
+ const cargoTomlPath = path.join(contractFolder, "Cargo.toml");
74
+ const libRsPath = path.join(contractFolder, "src", "lib.rs");
75
+ const mainRsPath = path.join(contractFolder, "src", "main.rs");
76
+ return (
77
+ fs.existsSync(cargoTomlPath) &&
78
+ fs.existsSync(libRsPath) &&
79
+ fs.existsSync(mainRsPath)
80
+ );
81
+ }
82
+
83
+ export function generateContractAddress(): string {
84
+ // Generate a random private key and derive the address
85
+ const wallet = ethers.Wallet.createRandom();
86
+ return wallet.address;
87
+ }
88
+
89
+ export function extractDeployedAddress(output: string): string | null {
90
+ // Look for the line containing "deployed code at address:"
91
+ const lines = output.split("\n");
92
+ for (const line of lines) {
93
+ if (line.includes("deployed code at address:")) {
94
+ // Simple approach: just extract the hex address directly
95
+ const hexMatch = line.match(/(0x[a-fA-F0-9]{40})/);
96
+ if (hexMatch && hexMatch[1]) {
97
+ return hexMatch[1];
98
+ }
99
+ }
100
+ }
101
+ return null;
102
+ }
103
+
104
+ export function extractGasPriceFromOutput(output: string): string | null {
105
+ const lines = output.split("\n");
106
+ for (const line of lines) {
107
+ if (line.includes("gas price:")) {
108
+ // Remove ANSI color codes first
109
+ // eslint-disable-next-line no-control-regex
110
+ const cleanLine = line.replace(/\x1b\[[0-9;]*m/g, "");
111
+
112
+ // Extract the value inside quotes after 'gas price:'
113
+ const match = cleanLine.match(/gas price:\s*"([^"]+)"/);
114
+ if (match && match[1]) {
115
+ return match[1];
116
+ }
117
+ }
118
+ }
119
+ return null;
120
+ }
121
+
122
+ export async function generateTsAbi(
123
+ abiFilePath: string,
124
+ contractName: string,
125
+ contractAddress: string,
126
+ chainId: string,
127
+ ) {
128
+ const TARGET_DIR = "../nextjs/contracts/";
129
+ const TARGET_FILE = `${TARGET_DIR}deployedContracts.ts`;
130
+ const abiTxt = fs.readFileSync(abiFilePath, "utf8");
131
+
132
+ // Extract from 4th row to the end
133
+ const lines = abiTxt.split("\n");
134
+ const extractedAbi = lines.slice(3).join("\n");
135
+ const abiJson = JSON.parse(extractedAbi);
136
+
137
+ // Helper to generate the contract entry
138
+ const newContractEntry = {
139
+ address: contractAddress,
140
+ abi: abiJson,
141
+ };
142
+
143
+ let deployedContractsObj: any = {};
144
+ const fileHeader =
145
+ generatedContractComment +
146
+ 'import { GenericContractsDeclaration } from "~~/utils/scaffold-eth/contract";\n\n';
147
+
148
+ if (fs.existsSync(TARGET_FILE)) {
149
+ // Read and parse the existing file
150
+ const fileContent = fs.readFileSync(TARGET_FILE, "utf8");
151
+ // Extract the deployedContracts object using regex
152
+ const match = fileContent.match(
153
+ /const deployedContracts = ([\s\S]*?) as const;/,
154
+ );
155
+ if (match) {
156
+ // eslint-disable-next-line no-eval
157
+ deployedContractsObj = eval("(" + match[1] + ")");
158
+ }
159
+ }
160
+
161
+ // Ensure the chainId exists
162
+ if (!deployedContractsObj[chainId]) {
163
+ deployedContractsObj[chainId] = {};
164
+ }
165
+ // Update or insert the contract entry
166
+ deployedContractsObj[chainId][contractName] = newContractEntry;
167
+
168
+ // Stringify the object for TypeScript output
169
+ const contractsString = JSON.stringify(deployedContractsObj, null, 2);
170
+
171
+ const output = `${fileHeader}const deployedContracts = ${contractsString} as const;\n\nexport default deployedContracts satisfies GenericContractsDeclaration;\n`;
172
+
173
+ if (!fs.existsSync(TARGET_DIR)) {
174
+ fs.mkdirSync(TARGET_DIR);
175
+ }
176
+
177
+ fs.writeFileSync(
178
+ TARGET_FILE,
179
+ await prettier.format(output, { parser: "typescript" }),
180
+ );
181
+
182
+ console.log(
183
+ `šŸ“ Updated TypeScript contract definition file on ${TARGET_FILE}`,
184
+ );
185
+ }
186
+
187
+ export function handleSolcError(
188
+ error: Error,
189
+ context: string = "ABI export",
190
+ ): void {
191
+ console.error(`\nāŒ ${context} failed!`);
192
+ console.error("\nšŸ” Error details:", error.message);
193
+
194
+ // Check if the error is related to solc not being found
195
+ if (
196
+ error.message.includes("solc") ||
197
+ error.message.includes("solidity") ||
198
+ error.message.includes("command not found")
199
+ ) {
200
+ console.error(
201
+ "\nšŸ’” It appears that the Solidity compiler (solc) is not installed on your system.",
202
+ );
203
+ console.error("\nšŸ“š To install Solidity, please visit:");
204
+ console.error(
205
+ " https://docs.soliditylang.org/en/latest/installing-solidity.html",
206
+ );
207
+ console.error(
208
+ "\nšŸš€ After installing solc, you can run this command again:",
209
+ );
210
+ console.error(" yarn export-abi");
211
+ console.error("\nšŸ“‹ Quick installation options:");
212
+ console.error(" • npm: npm install --global solc");
213
+ console.error(" • Docker: docker run ethereum/solc:stable --help");
214
+ console.error(" • Homebrew (macOS): brew install solidity");
215
+ console.error(" • Linux: sudo apt-get install solc");
216
+ } else {
217
+ console.error("\nšŸ’” Please check the error details above and try again.");
218
+ }
219
+ }
@@ -0,0 +1,136 @@
1
+ import { config as dotenvConfig } from "dotenv";
2
+ import * as path from "path";
3
+ import * as fs from "fs";
4
+ import { arbitrumNitro } from "../../../nextjs/utils/scaffold-stylus/chain";
5
+ import { DeploymentConfig, DeployOptions } from "./type";
6
+ import { getChain, getPrivateKey } from "./network";
7
+ import { getContractNameFromCargoToml } from "./contract";
8
+
9
+ // Load environment variables from .env file
10
+ const envPath = path.resolve(__dirname, "../../.env");
11
+ if (fs.existsSync(envPath)) {
12
+ dotenvConfig({ path: envPath });
13
+ }
14
+
15
+ export function clearDeploymentDir(): void {
16
+ const deploymentDir = process.env["DEPLOYMENT_DIR"] || "deployments";
17
+ if (fs.existsSync(deploymentDir)) {
18
+ fs.rmSync(deploymentDir, { recursive: true });
19
+ }
20
+ }
21
+
22
+ export function getDeploymentConfig(
23
+ deployOptions: DeployOptions,
24
+ ): DeploymentConfig {
25
+ // If network is specified, try to get RPC URL from viem chains
26
+ if (!deployOptions.network) deployOptions.network = "devnet";
27
+
28
+ const chain = getChain(deployOptions.network);
29
+ if (!chain) throw new Error(`Network ${deployOptions.network} not found`);
30
+
31
+ let contractName: string;
32
+ if (deployOptions.contract) {
33
+ try {
34
+ contractName =
35
+ deployOptions.name ||
36
+ getContractNameFromCargoToml(deployOptions.contract);
37
+ } catch (e) {
38
+ throw new Error(`āŒ Could not read contract name from Cargo.toml: ${e}`);
39
+ }
40
+ } else {
41
+ contractName = "your-contract";
42
+ }
43
+
44
+ return {
45
+ privateKey: getPrivateKey(deployOptions.network),
46
+ contractFolder: deployOptions.contract!,
47
+ contractName,
48
+ deploymentDir: process.env["DEPLOYMENT_DIR"] || "deployments",
49
+ chain,
50
+ };
51
+ }
52
+
53
+ export function ensureDeploymentDirectory(deploymentDir: string): void {
54
+ if (!fs.existsSync(deploymentDir)) {
55
+ console.log(`šŸ“ Creating deployment directory: ${deploymentDir}`);
56
+ fs.mkdirSync(deploymentDir, { recursive: true });
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Save the deployed contract address to addresses.json in the deployment directory.
62
+ * Updates or creates the file, using contractName as the key.
63
+ */
64
+ export function saveDeployedAddress(config: DeploymentConfig) {
65
+ try {
66
+ const addressesPath = path.resolve(config.deploymentDir, "addresses.json");
67
+ let addresses: Record<string, any> = {};
68
+ if (fs.existsSync(addressesPath)) {
69
+ const content = fs.readFileSync(addressesPath, "utf8");
70
+ try {
71
+ addresses = JSON.parse(content);
72
+ } catch (e) {
73
+ console.warn(
74
+ `āš ļø Could not parse existing addresses.json, will overwrite. Error: ${e}`,
75
+ );
76
+ }
77
+ }
78
+
79
+ // Save both address and chain ID
80
+ addresses[config.contractName] = {
81
+ address: config.contractAddress || "",
82
+ chainId: (config.chain?.id || arbitrumNitro.id).toString(),
83
+ };
84
+
85
+ fs.writeFileSync(addressesPath, JSON.stringify(addresses, null, 2));
86
+ console.log(`šŸ’¾ Saved deployed address and chain ID to ${addressesPath}`);
87
+ } catch (e) {
88
+ console.error(`āŒ Failed to save deployed address: ${e}`);
89
+ }
90
+ }
91
+
92
+ export function printDeployedAddresses(deploymentDir: string): void {
93
+ const addressesPath = path.resolve(deploymentDir, "addresses.json");
94
+ if (fs.existsSync(addressesPath)) {
95
+ const addresses = JSON.parse(fs.readFileSync(addressesPath, "utf8"));
96
+ console.log(`šŸ“¦ Deployed contract addresses (${addressesPath}):`);
97
+
98
+ // Format the output to show contract name, address, and chain ID clearly
99
+ Object.entries(addresses).forEach(([contractName, contractData]) => {
100
+ const data = contractData as {
101
+ address: string;
102
+ chainId: string;
103
+ };
104
+ console.log(` ${contractName}:`);
105
+ console.log(` Address: ${data.address}`);
106
+ console.log(` Chain ID: ${data.chainId}`);
107
+ });
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Reads the deployed contract data from addresses.json in the deployment directory.
113
+ * Returns an object with address and chainId for the given contractName, or undefined if not found.
114
+ */
115
+ export function getContractDataFromDeployments(
116
+ deploymentDir: string,
117
+ contractName: string,
118
+ ): { address: string; chainId: string } | undefined {
119
+ const addressesPath = path.resolve(deploymentDir, "addresses.json");
120
+ if (fs.existsSync(addressesPath)) {
121
+ try {
122
+ const addresses = JSON.parse(fs.readFileSync(addressesPath, "utf8"));
123
+ if (addresses[contractName]?.address) {
124
+ return addresses[contractName] as {
125
+ address: string;
126
+ chainId: string;
127
+ };
128
+ }
129
+ } catch (e) {
130
+ console.warn(
131
+ `āš ļø Could not parse addresses.json at ${addressesPath}: ${e}`,
132
+ );
133
+ }
134
+ }
135
+ return undefined;
136
+ }
@@ -0,0 +1,6 @@
1
+ // Re-export all utilities from their respective modules for backward compatibility
2
+ export * from "./deployment";
3
+ export * from "./network";
4
+ export * from "./contract";
5
+ export * from "./command";
6
+ export * from "./type";