create-stylus 0.0.5

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 (220) 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/.github/workflows/release-manual.yml +26 -0
  5. package/.yarnrc.yml +1 -0
  6. package/CONTRIBUTING.md +42 -0
  7. package/README.md +66 -0
  8. package/bin/create-dapp-ss.js +4 -0
  9. package/package.json +46 -0
  10. package/rollup.config.js +22 -0
  11. package/src/cli.ts +14 -0
  12. package/src/extensions.json +14 -0
  13. package/src/main.ts +72 -0
  14. package/src/tasks/copy-extension-file.ts +227 -0
  15. package/src/tasks/copy-template-files.ts +252 -0
  16. package/src/tasks/create-first-git-commit.ts +35 -0
  17. package/src/tasks/create-project-directory.ts +34 -0
  18. package/src/tasks/index.ts +5 -0
  19. package/src/tasks/install-packages.ts +15 -0
  20. package/src/tasks/prettier-format.ts +17 -0
  21. package/src/types.ts +31 -0
  22. package/src/utils/consts.ts +1 -0
  23. package/src/utils/find-files-recursively.ts +19 -0
  24. package/src/utils/link.ts +44 -0
  25. package/src/utils/load-extensions.ts +10 -0
  26. package/src/utils/merge-package-json.ts +33 -0
  27. package/src/utils/parse-arguments-into-options.ts +38 -0
  28. package/src/utils/prompt-for-missing-options.ts +53 -0
  29. package/src/utils/render-intro-message.ts +11 -0
  30. package/src/utils/render-outro-message.ts +34 -0
  31. package/templates/base/.github/ISSUE_TEMPLATE/bug_report.yml +58 -0
  32. package/templates/base/.github/ISSUE_TEMPLATE/config.yml +8 -0
  33. package/templates/base/.github/pull_request_template.md +16 -0
  34. package/templates/base/.github/workflows/lint.yaml +300 -0
  35. package/templates/base/.gitignore.template.mjs +19 -0
  36. package/templates/base/.gitmodules +0 -0
  37. package/templates/base/.husky/pre-commit +4 -0
  38. package/templates/base/.lintstagedrc.js +21 -0
  39. package/templates/base/.vscode/settings.json +7 -0
  40. package/templates/base/.yarn/plugins/@yarnpkg/plugin-typescript.cjs +9 -0
  41. package/templates/base/.yarn/releases/yarn-3.2.3.cjs +783 -0
  42. package/templates/base/.yarnrc.yml +11 -0
  43. package/templates/base/CONTRIBUTING.md +86 -0
  44. package/templates/base/LICENCE +21 -0
  45. package/templates/base/dist/cli.js +683 -0
  46. package/templates/base/dist/cli.js.map +1 -0
  47. package/templates/base/nitro-devnode/LICENSE +201 -0
  48. package/templates/base/nitro-devnode/README.md +70 -0
  49. package/templates/base/nitro-devnode/run-dev-node.sh +132 -0
  50. package/templates/base/nitro-devnode/start-chain-with-cors.sh +150 -0
  51. package/templates/base/nitro-devnode/stylus-deployer-bytecode.txt +1 -0
  52. package/templates/base/nitro-devnode/stylus-dev/Dockerfile +10 -0
  53. package/templates/base/package.json +44 -0
  54. package/templates/base/packages/nextjs/.env.example +13 -0
  55. package/templates/base/packages/nextjs/.eslintignore +11 -0
  56. package/templates/base/packages/nextjs/.eslintrc.json +15 -0
  57. package/templates/base/packages/nextjs/.gitignore.template.mjs +42 -0
  58. package/templates/base/packages/nextjs/.prettierrc.js +9 -0
  59. package/templates/base/packages/nextjs/.prettierrc.json +8 -0
  60. package/templates/base/packages/nextjs/app/blockexplorer/_components/AddressCodeTab.tsx +27 -0
  61. package/templates/base/packages/nextjs/app/blockexplorer/_components/AddressComponent.tsx +36 -0
  62. package/templates/base/packages/nextjs/app/blockexplorer/_components/AddressLogsTab.tsx +21 -0
  63. package/templates/base/packages/nextjs/app/blockexplorer/_components/AddressStorageTab.tsx +61 -0
  64. package/templates/base/packages/nextjs/app/blockexplorer/_components/BackButton.tsx +12 -0
  65. package/templates/base/packages/nextjs/app/blockexplorer/_components/ContractTabs.tsx +102 -0
  66. package/templates/base/packages/nextjs/app/blockexplorer/_components/PaginationButton.tsx +39 -0
  67. package/templates/base/packages/nextjs/app/blockexplorer/_components/SearchBar.tsx +49 -0
  68. package/templates/base/packages/nextjs/app/blockexplorer/_components/TransactionHash.tsx +28 -0
  69. package/templates/base/packages/nextjs/app/blockexplorer/_components/TransactionsTable.tsx +71 -0
  70. package/templates/base/packages/nextjs/app/blockexplorer/_components/index.tsx +7 -0
  71. package/templates/base/packages/nextjs/app/blockexplorer/address/[address]/page.tsx +101 -0
  72. package/templates/base/packages/nextjs/app/blockexplorer/layout.tsx +12 -0
  73. package/templates/base/packages/nextjs/app/blockexplorer/page.tsx +83 -0
  74. package/templates/base/packages/nextjs/app/blockexplorer/transaction/[txHash]/page.tsx +23 -0
  75. package/templates/base/packages/nextjs/app/blockexplorer/transaction/_components/TransactionComp.tsx +152 -0
  76. package/templates/base/packages/nextjs/app/debug/_components/DebugContracts.tsx +73 -0
  77. package/templates/base/packages/nextjs/app/debug/_components/contract/ContractInput.tsx +84 -0
  78. package/templates/base/packages/nextjs/app/debug/_components/contract/ContractReadMethods.tsx +43 -0
  79. package/templates/base/packages/nextjs/app/debug/_components/contract/ContractUI.tsx +164 -0
  80. package/templates/base/packages/nextjs/app/debug/_components/contract/ContractVariables.tsx +50 -0
  81. package/templates/base/packages/nextjs/app/debug/_components/contract/ContractWriteMethods.tsx +49 -0
  82. package/templates/base/packages/nextjs/app/debug/_components/contract/DisplayVariable.tsx +85 -0
  83. package/templates/base/packages/nextjs/app/debug/_components/contract/InheritanceTooltip.tsx +14 -0
  84. package/templates/base/packages/nextjs/app/debug/_components/contract/ReadOnlyFunctionForm.tsx +102 -0
  85. package/templates/base/packages/nextjs/app/debug/_components/contract/Tuple.tsx +44 -0
  86. package/templates/base/packages/nextjs/app/debug/_components/contract/TupleArray.tsx +142 -0
  87. package/templates/base/packages/nextjs/app/debug/_components/contract/TxReceipt.tsx +42 -0
  88. package/templates/base/packages/nextjs/app/debug/_components/contract/WriteOnlyFunctionForm.tsx +144 -0
  89. package/templates/base/packages/nextjs/app/debug/_components/contract/index.tsx +8 -0
  90. package/templates/base/packages/nextjs/app/debug/_components/contract/utilsContract.tsx +166 -0
  91. package/templates/base/packages/nextjs/app/debug/_components/contract/utilsDisplay.tsx +114 -0
  92. package/templates/base/packages/nextjs/app/debug/page.tsx +14 -0
  93. package/templates/base/packages/nextjs/app/layout.tsx +67 -0
  94. package/templates/base/packages/nextjs/app/not-found.tsx +16 -0
  95. package/templates/base/packages/nextjs/app/page.tsx +94 -0
  96. package/templates/base/packages/nextjs/components/Background.tsx +37 -0
  97. package/templates/base/packages/nextjs/components/Card.tsx +40 -0
  98. package/templates/base/packages/nextjs/components/Footer.tsx +93 -0
  99. package/templates/base/packages/nextjs/components/Header.tsx +114 -0
  100. package/templates/base/packages/nextjs/components/ScaffoldEthAppWithProviders.tsx +77 -0
  101. package/templates/base/packages/nextjs/components/SwitchTheme.tsx +41 -0
  102. package/templates/base/packages/nextjs/components/ThemeProvider.tsx +13 -0
  103. package/templates/base/packages/nextjs/components/assets/BuidlGuidlLogo.tsx +18 -0
  104. package/templates/base/packages/nextjs/components/scaffold-eth/Address/Address.tsx +187 -0
  105. package/templates/base/packages/nextjs/components/scaffold-eth/Address/AddressCopyIcon.tsx +23 -0
  106. package/templates/base/packages/nextjs/components/scaffold-eth/Address/AddressLinkWrapper.tsx +29 -0
  107. package/templates/base/packages/nextjs/components/scaffold-eth/Balance.tsx +75 -0
  108. package/templates/base/packages/nextjs/components/scaffold-eth/BlockieAvatar.tsx +17 -0
  109. package/templates/base/packages/nextjs/components/scaffold-eth/Faucet.tsx +131 -0
  110. package/templates/base/packages/nextjs/components/scaffold-eth/FaucetButton.tsx +75 -0
  111. package/templates/base/packages/nextjs/components/scaffold-eth/Input/AddressInput.tsx +120 -0
  112. package/templates/base/packages/nextjs/components/scaffold-eth/Input/Bytes32Input.tsx +31 -0
  113. package/templates/base/packages/nextjs/components/scaffold-eth/Input/BytesInput.tsx +28 -0
  114. package/templates/base/packages/nextjs/components/scaffold-eth/Input/EtherInput.tsx +128 -0
  115. package/templates/base/packages/nextjs/components/scaffold-eth/Input/InputBase.tsx +66 -0
  116. package/templates/base/packages/nextjs/components/scaffold-eth/Input/IntegerInput.tsx +63 -0
  117. package/templates/base/packages/nextjs/components/scaffold-eth/Input/index.ts +9 -0
  118. package/templates/base/packages/nextjs/components/scaffold-eth/Input/utils.ts +109 -0
  119. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressInfoDropdown.tsx +121 -0
  120. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressQRCodeModal.tsx +33 -0
  121. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/BurnerWalletModal.tsx +63 -0
  122. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/NetworkOptions.tsx +48 -0
  123. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/WrongNetworkDropdown.tsx +32 -0
  124. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/index.tsx +89 -0
  125. package/templates/base/packages/nextjs/components/scaffold-eth/index.tsx +7 -0
  126. package/templates/base/packages/nextjs/contracts/deployedContracts.ts +9 -0
  127. package/templates/base/packages/nextjs/contracts/externalContracts.ts +16 -0
  128. package/templates/base/packages/nextjs/eslint.config.mjs +32 -0
  129. package/templates/base/packages/nextjs/hooks/scaffold-eth/index.ts +17 -0
  130. package/templates/base/packages/nextjs/hooks/scaffold-eth/useAnimationConfig.ts +20 -0
  131. package/templates/base/packages/nextjs/hooks/scaffold-eth/useContractLogs.ts +40 -0
  132. package/templates/base/packages/nextjs/hooks/scaffold-eth/useCopyToClipboard.ts +19 -0
  133. package/templates/base/packages/nextjs/hooks/scaffold-eth/useDeployedContractInfo.ts +86 -0
  134. package/templates/base/packages/nextjs/hooks/scaffold-eth/useDisplayUsdMode.ts +21 -0
  135. package/templates/base/packages/nextjs/hooks/scaffold-eth/useFetchBlocks.ts +133 -0
  136. package/templates/base/packages/nextjs/hooks/scaffold-eth/useInitializeNativeCurrencyPrice.ts +32 -0
  137. package/templates/base/packages/nextjs/hooks/scaffold-eth/useNativeCurrencyPrice.ts +34 -0
  138. package/templates/base/packages/nextjs/hooks/scaffold-eth/useNetworkColor.ts +22 -0
  139. package/templates/base/packages/nextjs/hooks/scaffold-eth/useOutsideClick.ts +23 -0
  140. package/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldContract.ts +65 -0
  141. package/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldEventHistory.ts +213 -0
  142. package/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldReadContract.ts +80 -0
  143. package/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldWatchContractEvent.ts +40 -0
  144. package/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldWriteContract.ts +191 -0
  145. package/templates/base/packages/nextjs/hooks/scaffold-eth/useSelectedNetwork.ts +18 -0
  146. package/templates/base/packages/nextjs/hooks/scaffold-eth/useTargetNetwork.ts +23 -0
  147. package/templates/base/packages/nextjs/hooks/scaffold-eth/useTransactor.tsx +114 -0
  148. package/templates/base/packages/nextjs/hooks/scaffold-eth/useWatchBalance.ts +21 -0
  149. package/templates/base/packages/nextjs/icons/CompassIcon.tsx +39 -0
  150. package/templates/base/packages/nextjs/icons/DarkBugAntIcon.tsx +30 -0
  151. package/templates/base/packages/nextjs/icons/LightBugAntIcon.tsx +52 -0
  152. package/templates/base/packages/nextjs/next-env.d.ts +5 -0
  153. package/templates/base/packages/nextjs/next.config.js +19 -0
  154. package/templates/base/packages/nextjs/package.json +58 -0
  155. package/templates/base/packages/nextjs/postcss.config.js +6 -0
  156. package/templates/base/packages/nextjs/public/debug-image.png +0 -0
  157. package/templates/base/packages/nextjs/public/favicon.png +0 -0
  158. package/templates/base/packages/nextjs/public/logo.svg +8 -0
  159. package/templates/base/packages/nextjs/public/manifest.json +5 -0
  160. package/templates/base/packages/nextjs/public/thumbnail.jpg +0 -0
  161. package/templates/base/packages/nextjs/react-copy-to-clipboard.d.ts +44 -0
  162. package/templates/base/packages/nextjs/scaffold.config.ts +56 -0
  163. package/templates/base/packages/nextjs/services/store/store.ts +39 -0
  164. package/templates/base/packages/nextjs/services/web3/wagmiConfig.tsx +44 -0
  165. package/templates/base/packages/nextjs/services/web3/wagmiConnectors.tsx +51 -0
  166. package/templates/base/packages/nextjs/styles/globals.css +80 -0
  167. package/templates/base/packages/nextjs/tailwind.config.js +97 -0
  168. package/templates/base/packages/nextjs/tsconfig.json +28 -0
  169. package/templates/base/packages/nextjs/types/abitype/abi.d.ts +16 -0
  170. package/templates/base/packages/nextjs/types/utils.ts +3 -0
  171. package/templates/base/packages/nextjs/utils/scaffold-eth/block.ts +17 -0
  172. package/templates/base/packages/nextjs/utils/scaffold-eth/common.ts +8 -0
  173. package/templates/base/packages/nextjs/utils/scaffold-eth/contract.ts +352 -0
  174. package/templates/base/packages/nextjs/utils/scaffold-eth/contractsData.ts +11 -0
  175. package/templates/base/packages/nextjs/utils/scaffold-eth/decodeTxData.ts +65 -0
  176. package/templates/base/packages/nextjs/utils/scaffold-eth/fetchPriceFromUniswap.ts +72 -0
  177. package/templates/base/packages/nextjs/utils/scaffold-eth/getMetadata.ts +50 -0
  178. package/templates/base/packages/nextjs/utils/scaffold-eth/getParsedError.ts +35 -0
  179. package/templates/base/packages/nextjs/utils/scaffold-eth/index.ts +6 -0
  180. package/templates/base/packages/nextjs/utils/scaffold-eth/notification.tsx +90 -0
  181. package/templates/base/packages/nextjs/utils/scaffold-stylus/burner.ts +59 -0
  182. package/templates/base/packages/nextjs/utils/scaffold-stylus/chain.ts +42 -0
  183. package/templates/base/packages/nextjs/utils/scaffold-stylus/index.ts +3 -0
  184. package/templates/base/packages/nextjs/utils/scaffold-stylus/networks.ts +94 -0
  185. package/templates/base/packages/nextjs/vercel.json +3 -0
  186. package/templates/base/packages/stylus/.env.example +16 -0
  187. package/templates/base/packages/stylus/.eslintrc.js +23 -0
  188. package/templates/base/packages/stylus/.gitignore.template.mjs +7 -0
  189. package/templates/base/packages/stylus/jest.config.js +15 -0
  190. package/templates/base/packages/stylus/package.json +48 -0
  191. package/templates/base/packages/stylus/scripts/deploy.ts +46 -0
  192. package/templates/base/packages/stylus/scripts/deploy_contract.ts +84 -0
  193. package/templates/base/packages/stylus/scripts/deploy_wrapper.ts +39 -0
  194. package/templates/base/packages/stylus/scripts/export_abi.ts +87 -0
  195. package/templates/base/packages/stylus/scripts/index.ts +0 -0
  196. package/templates/base/packages/stylus/scripts/new_module.sh +35 -0
  197. package/templates/base/packages/stylus/scripts/test_network.ts +31 -0
  198. package/templates/base/packages/stylus/scripts/utils/command.ts +152 -0
  199. package/templates/base/packages/stylus/scripts/utils/contract.ts +228 -0
  200. package/templates/base/packages/stylus/scripts/utils/deployment.ts +260 -0
  201. package/templates/base/packages/stylus/scripts/utils/index.ts +6 -0
  202. package/templates/base/packages/stylus/scripts/utils/network.ts +132 -0
  203. package/templates/base/packages/stylus/scripts/utils/type.ts +51 -0
  204. package/templates/base/packages/stylus/scripts/utils.ts +3 -0
  205. package/templates/base/packages/stylus/tsconfig.json +41 -0
  206. package/templates/base/packages/stylus/your-contract/.cargo/config.toml +18 -0
  207. package/templates/base/packages/stylus/your-contract/Cargo.lock +5761 -0
  208. package/templates/base/packages/stylus/your-contract/Cargo.toml +48 -0
  209. package/templates/base/packages/stylus/your-contract/examples/counter.rs +78 -0
  210. package/templates/base/packages/stylus/your-contract/header.png +0 -0
  211. package/templates/base/packages/stylus/your-contract/licenses/Apache-2.0 +201 -0
  212. package/templates/base/packages/stylus/your-contract/licenses/COPYRIGHT.md +5 -0
  213. package/templates/base/packages/stylus/your-contract/licenses/DCO.txt +34 -0
  214. package/templates/base/packages/stylus/your-contract/licenses/MIT +21 -0
  215. package/templates/base/packages/stylus/your-contract/rust-toolchain.toml +2 -0
  216. package/templates/base/packages/stylus/your-contract/src/lib.rs +241 -0
  217. package/templates/base/packages/stylus/your-contract/src/main.rs +10 -0
  218. package/templates/base/readme.md +352 -0
  219. package/templates/base/yarn.lock +17859 -0
  220. package/tsconfig.json +13 -0
@@ -0,0 +1,75 @@
1
+ "use client";
2
+
3
+ import { Address, formatEther } from "viem";
4
+ import { useDisplayUsdMode } from "~~/hooks/scaffold-eth/useDisplayUsdMode";
5
+ import { useTargetNetwork } from "~~/hooks/scaffold-eth/useTargetNetwork";
6
+ import { useWatchBalance } from "~~/hooks/scaffold-eth/useWatchBalance";
7
+ import { useGlobalState } from "~~/services/store/store";
8
+
9
+ type BalanceProps = {
10
+ address?: Address;
11
+ className?: string;
12
+ usdMode?: boolean;
13
+ };
14
+
15
+ /**
16
+ * Display (ETH & USD) balance of an ETH address.
17
+ */
18
+ export const Balance = ({ address, className = "", usdMode }: BalanceProps) => {
19
+ const { targetNetwork } = useTargetNetwork();
20
+ const nativeCurrencyPrice = useGlobalState(state => state.nativeCurrency.price);
21
+ const isNativeCurrencyPriceFetching = useGlobalState(state => state.nativeCurrency.isFetching);
22
+
23
+ const {
24
+ data: balance,
25
+ isError,
26
+ isLoading,
27
+ } = useWatchBalance({
28
+ address,
29
+ });
30
+
31
+ const { displayUsdMode, toggleDisplayUsdMode } = useDisplayUsdMode({ defaultUsdMode: usdMode });
32
+
33
+ if (!address || isLoading || balance === null || (isNativeCurrencyPriceFetching && nativeCurrencyPrice === 0)) {
34
+ return (
35
+ <div className="animate-pulse flex space-x-4">
36
+ <div className="rounded-md bg-slate-300 h-6 w-6"></div>
37
+ <div className="flex items-center space-y-6">
38
+ <div className="h-2 w-28 bg-slate-300 rounded-sm"></div>
39
+ </div>
40
+ </div>
41
+ );
42
+ }
43
+
44
+ if (isError) {
45
+ return (
46
+ <div className="border-2 border-base-content/30 rounded-md px-2 flex flex-col items-center max-w-fit cursor-pointer">
47
+ <div className="text-warning">Error</div>
48
+ </div>
49
+ );
50
+ }
51
+
52
+ const formattedBalance = balance ? Number(formatEther(balance.value)) : 0;
53
+
54
+ return (
55
+ <button
56
+ className={`btn btn-sm btn-ghost flex flex-col font-normal items-center hover:bg-transparent ${className}`}
57
+ onClick={toggleDisplayUsdMode}
58
+ type="button"
59
+ >
60
+ <div className="w-full flex items-center justify-center">
61
+ {displayUsdMode ? (
62
+ <>
63
+ <span className="text-[0.8em] font-bold mr-1">$</span>
64
+ <span>{(formattedBalance * nativeCurrencyPrice).toFixed(2)}</span>
65
+ </>
66
+ ) : (
67
+ <>
68
+ <span>{formattedBalance.toFixed(4)}</span>
69
+ <span className="text-[0.8em] font-bold ml-1">{targetNetwork.nativeCurrency.symbol}</span>
70
+ </>
71
+ )}
72
+ </div>
73
+ </button>
74
+ );
75
+ };
@@ -0,0 +1,17 @@
1
+ "use client";
2
+
3
+ import { AvatarComponent } from "@rainbow-me/rainbowkit";
4
+ import { blo } from "blo";
5
+
6
+ // Custom Avatar for RainbowKit
7
+ export const BlockieAvatar: AvatarComponent = ({ address, ensImage, size }) => (
8
+ // Don't want to use nextJS Image here (and adding remote patterns for the URL)
9
+ // eslint-disable-next-line @next/next/no-img-element
10
+ <img
11
+ className="rounded-full"
12
+ src={ensImage || blo(address as `0x${string}`)}
13
+ width={size}
14
+ height={size}
15
+ alt={`${address} avatar`}
16
+ />
17
+ );
@@ -0,0 +1,131 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+ import { Address as AddressType, createWalletClient, http, parseEther } from "viem";
5
+ import { privateKeyToAccount } from "viem/accounts";
6
+ import { useAccount } from "wagmi";
7
+ import { BanknotesIcon } from "@heroicons/react/24/outline";
8
+ import { Address, AddressInput, Balance, EtherInput } from "~~/components/scaffold-eth";
9
+ import { useTransactor } from "~~/hooks/scaffold-eth";
10
+ import { notification } from "~~/utils/scaffold-eth";
11
+ import { arbitrumNitro } from "~~/utils/scaffold-stylus/chain";
12
+
13
+ // Account index to use from generated arbitrum accounts.
14
+ const FAUCET_ACCOUNT_INDEX = 0;
15
+
16
+ const localWalletClient = createWalletClient({
17
+ account: privateKeyToAccount(arbitrumNitro.accounts[0].privateKey),
18
+ chain: arbitrumNitro,
19
+ transport: http(arbitrumNitro.rpcUrls.default.http[0]),
20
+ });
21
+
22
+ /**
23
+ * Faucet modal which lets you send ETH to any address.
24
+ */
25
+ export const Faucet = () => {
26
+ const [loading, setLoading] = useState(false);
27
+ const [inputAddress, setInputAddress] = useState<AddressType>();
28
+ const [faucetAddress, setFaucetAddress] = useState<AddressType>(arbitrumNitro.accounts[0].address);
29
+ const [sendValue, setSendValue] = useState("");
30
+
31
+ const { chain: ConnectedChain } = useAccount();
32
+
33
+ const faucetTxn = useTransactor(localWalletClient);
34
+
35
+ useEffect(() => {
36
+ const getFaucetAddress = async () => {
37
+ try {
38
+ const accounts = await localWalletClient.getAddresses();
39
+ setFaucetAddress(accounts[FAUCET_ACCOUNT_INDEX]);
40
+ } catch (error) {
41
+ notification.error(
42
+ <>
43
+ <p className="font-bold mt-0 mb-1">Cannot connect to local provider</p>
44
+ <p className="m-0">
45
+ - Did you forget to run <code className="italic bg-base-300 text-base font-bold">yarn chain</code> ?
46
+ </p>
47
+ <p className="mt-1 break-normal">
48
+ - Or you can change <code className="italic bg-base-300 text-base font-bold">targetNetwork</code> in{" "}
49
+ <code className="italic bg-base-300 text-base font-bold">scaffold.config.ts</code>
50
+ </p>
51
+ </>,
52
+ );
53
+ console.error("⚡️ ~ file: Faucet.tsx:getFaucetAddress ~ error", error);
54
+ }
55
+ };
56
+ getFaucetAddress();
57
+ }, []);
58
+
59
+ const sendETH = async () => {
60
+ if (!faucetAddress || !inputAddress) {
61
+ return;
62
+ }
63
+ try {
64
+ setLoading(true);
65
+ await faucetTxn({
66
+ to: inputAddress,
67
+ value: parseEther(sendValue as `${number}`),
68
+ account: faucetAddress,
69
+ });
70
+ setLoading(false);
71
+ setInputAddress(undefined);
72
+ setSendValue("");
73
+ } catch (error) {
74
+ console.error("⚡️ ~ file: Faucet.tsx:sendETH ~ error", error);
75
+ setLoading(false);
76
+ }
77
+ };
78
+
79
+ // Render only on local chain
80
+ if (ConnectedChain?.id !== arbitrumNitro.id) {
81
+ return null;
82
+ }
83
+
84
+ return (
85
+ <div>
86
+ <label htmlFor="faucet-modal" className="btn btn-primary btn-sm font-normal gap-1">
87
+ <BanknotesIcon className="h-4 w-4" />
88
+ <span>Faucet</span>
89
+ </label>
90
+ <input type="checkbox" id="faucet-modal" className="modal-toggle" />
91
+ <label htmlFor="faucet-modal" className="modal cursor-pointer">
92
+ <label className="modal-box relative">
93
+ {/* dummy input to capture event onclick on modal box */}
94
+ <input className="h-0 w-0 absolute top-0 left-0" />
95
+ <h3 className="text-xl font-bold mb-3">Local Faucet</h3>
96
+ <label htmlFor="faucet-modal" className="btn btn-ghost btn-sm btn-circle absolute right-3 top-3">
97
+
98
+ </label>
99
+ <div className="space-y-3">
100
+ <div className="flex space-x-4">
101
+ <div>
102
+ <span className="text-sm font-bold">From:</span>
103
+ <Address address={faucetAddress} onlyEnsOrAddress />
104
+ </div>
105
+ <div>
106
+ <span className="text-sm font-bold pl-3">Available:</span>
107
+ <Balance address={faucetAddress} />
108
+ </div>
109
+ </div>
110
+ <div className="flex flex-col space-y-3">
111
+ <AddressInput
112
+ placeholder="Destination Address"
113
+ value={inputAddress ?? ""}
114
+ onChange={value => setInputAddress(value as AddressType)}
115
+ />
116
+ <EtherInput placeholder="Amount to send" value={sendValue} onChange={value => setSendValue(value)} />
117
+ <button className="h-10 btn btn-primary btn-sm px-2 rounded-full" onClick={sendETH} disabled={loading}>
118
+ {!loading ? (
119
+ <BanknotesIcon className="h-6 w-6" />
120
+ ) : (
121
+ <span className="loading loading-spinner loading-sm"></span>
122
+ )}
123
+ <span>Send</span>
124
+ </button>
125
+ </div>
126
+ </div>
127
+ </label>
128
+ </label>
129
+ </div>
130
+ );
131
+ };
@@ -0,0 +1,75 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { createWalletClient, http, parseEther } from "viem";
5
+ import { privateKeyToAccount } from "viem/accounts";
6
+ import { useAccount } from "wagmi";
7
+ import { useWatchBalance } from "~~/hooks/scaffold-eth/useWatchBalance";
8
+ import { BanknotesIcon } from "@heroicons/react/24/outline";
9
+ import { useTransactor } from "~~/hooks/scaffold-eth";
10
+ import { arbitrumNitro } from "~~/utils/scaffold-stylus/chain";
11
+
12
+ // Number of ETH faucet sends to an address
13
+ const NUM_OF_ETH = "1";
14
+ const FAUCET_ADDRESS = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";
15
+
16
+ const localWalletClient = createWalletClient({
17
+ account: privateKeyToAccount(arbitrumNitro.accounts[0].privateKey),
18
+ chain: arbitrumNitro,
19
+ transport: http(arbitrumNitro.rpcUrls.default.http[0]),
20
+ });
21
+
22
+ /**
23
+ * FaucetButton button which lets you grab eth.
24
+ */
25
+ export const FaucetButton = () => {
26
+ const { address, chain: ConnectedChain } = useAccount();
27
+
28
+ const { data: balance } = useWatchBalance({ address });
29
+
30
+ const [loading, setLoading] = useState(false);
31
+
32
+ const faucetTxn = useTransactor(localWalletClient);
33
+
34
+ const sendETH = async () => {
35
+ if (!address) return;
36
+ try {
37
+ setLoading(true);
38
+ await faucetTxn({
39
+ account: FAUCET_ADDRESS,
40
+ to: address,
41
+ value: parseEther(NUM_OF_ETH),
42
+ });
43
+ setLoading(false);
44
+ } catch (error) {
45
+ console.error("⚡️ ~ file: FaucetButton.tsx:sendETH ~ error", error);
46
+ setLoading(false);
47
+ }
48
+ };
49
+
50
+ // Render only on local chain
51
+ if (ConnectedChain?.id !== arbitrumNitro.id) {
52
+ return null;
53
+ }
54
+
55
+ const isBalanceZero = balance && balance.value === 0n;
56
+
57
+ return (
58
+ <div
59
+ className={
60
+ !isBalanceZero
61
+ ? "ml-1"
62
+ : "ml-1 tooltip tooltip-bottom tooltip-primary tooltip-open font-bold before:left-auto before:transform-none before:content-[attr(data-tip)] before:-translate-x-2/5"
63
+ }
64
+ data-tip="Grab funds from faucet"
65
+ >
66
+ <button className="btn btn-secondary btn-sm px-2 rounded-full" onClick={sendETH} disabled={loading}>
67
+ {!loading ? (
68
+ <BanknotesIcon className="h-4 w-4" />
69
+ ) : (
70
+ <span className="loading loading-spinner loading-xs"></span>
71
+ )}
72
+ </button>
73
+ </div>
74
+ );
75
+ };
@@ -0,0 +1,120 @@
1
+ import { useEffect, useState } from "react";
2
+ import { blo } from "blo";
3
+ import { useDebounceValue } from "usehooks-ts";
4
+ import { Address, isAddress } from "viem";
5
+ import { normalize } from "viem/ens";
6
+ import { useEnsAddress, useEnsAvatar, useEnsName } from "wagmi";
7
+ import { CommonInputProps, InputBase, isENS } from "~~/components/scaffold-eth";
8
+
9
+ /**
10
+ * Address input with ENS name resolution
11
+ */
12
+ export const AddressInput = ({ value, name, placeholder, onChange, disabled }: CommonInputProps<Address | string>) => {
13
+ // Debounce the input to keep clean RPC calls when resolving ENS names
14
+ // If the input is an address, we don't need to debounce it
15
+ const [_debouncedValue] = useDebounceValue(value, 500);
16
+ const debouncedValue = isAddress(value) ? value : _debouncedValue;
17
+ const isDebouncedValueLive = debouncedValue === value;
18
+
19
+ // If the user changes the input after an ENS name is already resolved, we want to remove the stale result
20
+ const settledValue = isDebouncedValueLive ? debouncedValue : undefined;
21
+
22
+ const {
23
+ data: ensAddress,
24
+ isLoading: isEnsAddressLoading,
25
+ isError: isEnsAddressError,
26
+ isSuccess: isEnsAddressSuccess,
27
+ } = useEnsAddress({
28
+ name: settledValue,
29
+ chainId: 1,
30
+ query: {
31
+ gcTime: 30_000,
32
+ enabled: isDebouncedValueLive && isENS(debouncedValue),
33
+ },
34
+ });
35
+
36
+ const [enteredEnsName, setEnteredEnsName] = useState<string>();
37
+ const {
38
+ data: ensName,
39
+ isLoading: isEnsNameLoading,
40
+ isError: isEnsNameError,
41
+ isSuccess: isEnsNameSuccess,
42
+ } = useEnsName({
43
+ address: settledValue as Address,
44
+ chainId: 1,
45
+ query: {
46
+ enabled: isAddress(debouncedValue),
47
+ gcTime: 30_000,
48
+ },
49
+ });
50
+
51
+ const { data: ensAvatar, isLoading: isEnsAvatarLoading } = useEnsAvatar({
52
+ name: ensName ? normalize(ensName) : undefined,
53
+ chainId: 1,
54
+ query: {
55
+ enabled: Boolean(ensName),
56
+ gcTime: 30_000,
57
+ },
58
+ });
59
+
60
+ // ens => address
61
+ useEffect(() => {
62
+ if (!ensAddress) return;
63
+
64
+ // ENS resolved successfully
65
+ setEnteredEnsName(debouncedValue);
66
+ onChange(ensAddress);
67
+ }, [ensAddress, onChange, debouncedValue]);
68
+
69
+ useEffect(() => {
70
+ setEnteredEnsName(undefined);
71
+ }, [value]);
72
+
73
+ const reFocus =
74
+ isEnsAddressError ||
75
+ isEnsNameError ||
76
+ isEnsNameSuccess ||
77
+ isEnsAddressSuccess ||
78
+ ensName === null ||
79
+ ensAddress === null;
80
+
81
+ return (
82
+ <InputBase<Address>
83
+ name={name}
84
+ placeholder={placeholder}
85
+ error={ensAddress === null}
86
+ value={value as Address}
87
+ onChange={onChange}
88
+ disabled={isEnsAddressLoading || isEnsNameLoading || disabled}
89
+ reFocus={reFocus}
90
+ prefix={
91
+ ensName ? (
92
+ <div className="flex bg-base-300 rounded-l-full items-center">
93
+ {isEnsAvatarLoading && <div className="skeleton bg-base-200 w-[35px] h-[35px] rounded-full shrink-0"></div>}
94
+ {ensAvatar ? (
95
+ <span className="w-[35px]">
96
+ {
97
+ // eslint-disable-next-line
98
+ <img className="w-full rounded-full" src={ensAvatar} alt={`${ensAddress} avatar`} />
99
+ }
100
+ </span>
101
+ ) : null}
102
+ <span className="text-accent px-2">{enteredEnsName ?? ensName}</span>
103
+ </div>
104
+ ) : (
105
+ (isEnsNameLoading || isEnsAddressLoading) && (
106
+ <div className="flex bg-base-300 rounded-l-full items-center gap-2 pr-2">
107
+ <div className="skeleton bg-base-200 w-[35px] h-[35px] rounded-full shrink-0"></div>
108
+ <div className="skeleton bg-base-200 h-3 w-20"></div>
109
+ </div>
110
+ )
111
+ )
112
+ }
113
+ suffix={
114
+ // Don't want to use nextJS Image here (and adding remote patterns for the URL)
115
+ // eslint-disable-next-line @next/next/no-img-element
116
+ value && <img alt="" className="rounded-full!" src={blo(value as `0x${string}`)} width="35" height="35" />
117
+ }
118
+ />
119
+ );
120
+ };
@@ -0,0 +1,31 @@
1
+ import { useCallback } from "react";
2
+ import { hexToString, isHex, stringToHex } from "viem";
3
+ import { CommonInputProps, InputBase } from "~~/components/scaffold-eth";
4
+
5
+ export const Bytes32Input = ({ value, onChange, name, placeholder, disabled }: CommonInputProps) => {
6
+ const convertStringToBytes32 = useCallback(() => {
7
+ if (!value) {
8
+ return;
9
+ }
10
+ onChange(isHex(value) ? hexToString(value, { size: 32 }) : stringToHex(value, { size: 32 }));
11
+ }, [onChange, value]);
12
+
13
+ return (
14
+ <InputBase
15
+ name={name}
16
+ value={value}
17
+ placeholder={placeholder}
18
+ onChange={onChange}
19
+ disabled={disabled}
20
+ suffix={
21
+ <button
22
+ className="self-center cursor-pointer text-xl font-semibold px-4 text-accent"
23
+ onClick={convertStringToBytes32}
24
+ type="button"
25
+ >
26
+ #
27
+ </button>
28
+ }
29
+ />
30
+ );
31
+ };
@@ -0,0 +1,28 @@
1
+ import { useCallback } from "react";
2
+ import { bytesToString, isHex, toBytes, toHex } from "viem";
3
+ import { CommonInputProps, InputBase } from "~~/components/scaffold-eth";
4
+
5
+ export const BytesInput = ({ value, onChange, name, placeholder, disabled }: CommonInputProps) => {
6
+ const convertStringToBytes = useCallback(() => {
7
+ onChange(isHex(value) ? bytesToString(toBytes(value)) : toHex(toBytes(value)));
8
+ }, [onChange, value]);
9
+
10
+ return (
11
+ <InputBase
12
+ name={name}
13
+ value={value}
14
+ placeholder={placeholder}
15
+ onChange={onChange}
16
+ disabled={disabled}
17
+ suffix={
18
+ <button
19
+ className="self-center cursor-pointer text-xl font-semibold px-4 text-accent"
20
+ onClick={convertStringToBytes}
21
+ type="button"
22
+ >
23
+ #
24
+ </button>
25
+ }
26
+ />
27
+ );
28
+ };
@@ -0,0 +1,128 @@
1
+ import { useMemo, useState } from "react";
2
+ import { ArrowsRightLeftIcon } from "@heroicons/react/24/outline";
3
+ import { CommonInputProps, InputBase, SIGNED_NUMBER_REGEX } from "~~/components/scaffold-eth";
4
+ import { useDisplayUsdMode } from "~~/hooks/scaffold-eth/useDisplayUsdMode";
5
+ import { useGlobalState } from "~~/services/store/store";
6
+
7
+ const MAX_DECIMALS_USD = 2;
8
+
9
+ function etherValueToDisplayValue(usdMode: boolean, etherValue: string, nativeCurrencyPrice: number) {
10
+ if (usdMode && nativeCurrencyPrice) {
11
+ const parsedEthValue = parseFloat(etherValue);
12
+ if (Number.isNaN(parsedEthValue)) {
13
+ return etherValue;
14
+ } else {
15
+ // We need to round the value rather than use toFixed,
16
+ // since otherwise a user would not be able to modify the decimal value
17
+ return (
18
+ Math.round(parsedEthValue * nativeCurrencyPrice * 10 ** MAX_DECIMALS_USD) /
19
+ 10 ** MAX_DECIMALS_USD
20
+ ).toString();
21
+ }
22
+ } else {
23
+ return etherValue;
24
+ }
25
+ }
26
+
27
+ function displayValueToEtherValue(usdMode: boolean, displayValue: string, nativeCurrencyPrice: number) {
28
+ if (usdMode && nativeCurrencyPrice) {
29
+ const parsedDisplayValue = parseFloat(displayValue);
30
+ if (Number.isNaN(parsedDisplayValue)) {
31
+ // Invalid number.
32
+ return displayValue;
33
+ } else {
34
+ // Compute the ETH value if a valid number.
35
+ return (parsedDisplayValue / nativeCurrencyPrice).toString();
36
+ }
37
+ } else {
38
+ return displayValue;
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Input for ETH amount with USD conversion.
44
+ *
45
+ * onChange will always be called with the value in ETH
46
+ */
47
+ export const EtherInput = ({
48
+ value,
49
+ name,
50
+ placeholder,
51
+ onChange,
52
+ disabled,
53
+ usdMode,
54
+ }: CommonInputProps & { usdMode?: boolean }) => {
55
+ const [transitoryDisplayValue, setTransitoryDisplayValue] = useState<string>();
56
+ const nativeCurrencyPrice = useGlobalState(state => state.nativeCurrency.price);
57
+ const isNativeCurrencyPriceFetching = useGlobalState(state => state.nativeCurrency.isFetching);
58
+
59
+ const { displayUsdMode, toggleDisplayUsdMode } = useDisplayUsdMode({ defaultUsdMode: usdMode });
60
+
61
+ // The displayValue is derived from the ether value that is controlled outside of the component
62
+ // In usdMode, it is converted to its usd value, in regular mode it is unaltered
63
+ const displayValue = useMemo(() => {
64
+ const newDisplayValue = etherValueToDisplayValue(displayUsdMode, value, nativeCurrencyPrice || 0);
65
+ if (transitoryDisplayValue && parseFloat(newDisplayValue) === parseFloat(transitoryDisplayValue)) {
66
+ return transitoryDisplayValue;
67
+ }
68
+ // Clear any transitory display values that might be set
69
+ setTransitoryDisplayValue(undefined);
70
+ return newDisplayValue;
71
+ }, [nativeCurrencyPrice, transitoryDisplayValue, displayUsdMode, value]);
72
+
73
+ const handleChangeNumber = (newValue: string) => {
74
+ if (newValue && !SIGNED_NUMBER_REGEX.test(newValue)) {
75
+ return;
76
+ }
77
+
78
+ // Following condition is a fix to prevent usdMode from experiencing different display values
79
+ // than what the user entered. This can happen due to floating point rounding errors that are introduced in the back and forth conversion
80
+ if (displayUsdMode) {
81
+ const decimals = newValue.split(".")[1];
82
+ if (decimals && decimals.length > MAX_DECIMALS_USD) {
83
+ return;
84
+ }
85
+ }
86
+
87
+ // Since the display value is a derived state (calculated from the ether value), usdMode would not allow introducing a decimal point.
88
+ // This condition handles a transitory state for a display value with a trailing decimal sign
89
+ if (newValue.endsWith(".") || newValue.endsWith(".0")) {
90
+ setTransitoryDisplayValue(newValue);
91
+ } else {
92
+ setTransitoryDisplayValue(undefined);
93
+ }
94
+
95
+ const newEthValue = displayValueToEtherValue(displayUsdMode, newValue, nativeCurrencyPrice || 0);
96
+ onChange(newEthValue);
97
+ };
98
+
99
+ return (
100
+ <InputBase
101
+ name={name}
102
+ value={displayValue}
103
+ placeholder={placeholder}
104
+ onChange={handleChangeNumber}
105
+ disabled={disabled}
106
+ prefix={<span className="pl-4 -mr-2 text-accent self-center">{displayUsdMode ? "$" : "Ξ"}</span>}
107
+ suffix={
108
+ <div
109
+ className={`${
110
+ nativeCurrencyPrice > 0
111
+ ? ""
112
+ : "tooltip tooltip-secondary before:content-[attr(data-tip)] before:right-[-10px] before:left-auto before:transform-none"
113
+ }`}
114
+ data-tip={isNativeCurrencyPriceFetching ? "Fetching price" : "Unable to fetch price"}
115
+ >
116
+ <button
117
+ className="btn btn-primary h-[2.2rem] min-h-[2.2rem]"
118
+ onClick={toggleDisplayUsdMode}
119
+ disabled={!displayUsdMode && !nativeCurrencyPrice}
120
+ type="button"
121
+ >
122
+ <ArrowsRightLeftIcon className="h-3 w-3 cursor-pointer" aria-hidden="true" />
123
+ </button>
124
+ </div>
125
+ }
126
+ />
127
+ );
128
+ };
@@ -0,0 +1,66 @@
1
+ import { ChangeEvent, FocusEvent, ReactNode, useCallback, useEffect, useRef } from "react";
2
+ import { CommonInputProps } from "~~/components/scaffold-eth";
3
+
4
+ type InputBaseProps<T> = CommonInputProps<T> & {
5
+ error?: boolean;
6
+ prefix?: ReactNode;
7
+ suffix?: ReactNode;
8
+ reFocus?: boolean;
9
+ };
10
+
11
+ export const InputBase = <T extends { toString: () => string } | undefined = string>({
12
+ name,
13
+ value,
14
+ onChange,
15
+ placeholder,
16
+ error,
17
+ disabled,
18
+ prefix,
19
+ suffix,
20
+ reFocus,
21
+ }: InputBaseProps<T>) => {
22
+ const inputReft = useRef<HTMLInputElement>(null);
23
+
24
+ let modifier = "";
25
+ if (error) {
26
+ modifier = "border-error";
27
+ } else if (disabled) {
28
+ modifier = "border-disabled bg-base-300";
29
+ }
30
+
31
+ const handleChange = useCallback(
32
+ (e: ChangeEvent<HTMLInputElement>) => {
33
+ onChange(e.target.value as unknown as T);
34
+ },
35
+ [onChange],
36
+ );
37
+
38
+ // Runs only when reFocus prop is passed, useful for setting the cursor
39
+ // at the end of the input. Example AddressInput
40
+ const onFocus = (e: FocusEvent<HTMLInputElement, Element>) => {
41
+ if (reFocus !== undefined) {
42
+ e.currentTarget.setSelectionRange(e.currentTarget.value.length, e.currentTarget.value.length);
43
+ }
44
+ };
45
+ useEffect(() => {
46
+ if (reFocus !== undefined && reFocus === true) inputReft.current?.focus();
47
+ }, [reFocus]);
48
+
49
+ return (
50
+ <div className={`flex border-2 border-base-300 bg-base-200 rounded-full text-accent ${modifier}`}>
51
+ {prefix}
52
+ <input
53
+ className="input input-ghost focus-within:border-transparent focus:outline-hidden focus:bg-transparent h-[2.2rem] min-h-[2.2rem] px-4 border w-full font-medium placeholder:text-accent/70 text-base-content/70 focus:text-base-content/70"
54
+ placeholder={placeholder}
55
+ name={name}
56
+ value={value?.toString()}
57
+ onChange={handleChange}
58
+ disabled={disabled}
59
+ autoComplete="off"
60
+ ref={inputReft}
61
+ onFocus={onFocus}
62
+ />
63
+ {suffix}
64
+ </div>
65
+ );
66
+ };