diff --git a/.changeset/tame-moons-clean.md b/.changeset/tame-moons-clean.md
new file mode 100644
index 00000000000..42d7a85bb8b
--- /dev/null
+++ b/.changeset/tame-moons-clean.md
@@ -0,0 +1,5 @@
+---
+"create-fuels": patch
+---
+
+feat: `create fuels` template revamp
diff --git a/apps/create-fuels-counter-guide/.env.example b/apps/create-fuels-counter-guide/.env.example
index e9a1ac1f1de..2bc55e185bf 100644
--- a/apps/create-fuels-counter-guide/.env.example
+++ b/apps/create-fuels-counter-guide/.env.example
@@ -1,2 +1,3 @@
VITE_FUEL_NODE_PORT=4000
-VITE_DAPP_ENVIRONMENT=local
\ No newline at end of file
+VITE_DAPP_ENVIRONMENT=local
+VITE_GENESIS_WALLET_PRIVATE_KEY=0x01
\ No newline at end of file
diff --git a/apps/create-fuels-counter-guide/.gitignore b/apps/create-fuels-counter-guide/.gitignore
index 7ea0ef9b6b2..e88e6b4af18 100644
--- a/apps/create-fuels-counter-guide/.gitignore
+++ b/apps/create-fuels-counter-guide/.gitignore
@@ -33,5 +33,5 @@ src/sway-api/index.ts
sway-programs/**/out
.turbo
-playwright-report
-test-results
\ No newline at end of file
+test-results
+playwright-report
\ No newline at end of file
diff --git a/apps/create-fuels-counter-guide/eslint.config.js b/apps/create-fuels-counter-guide/eslint.config.js
index c4bc8f33c0b..714d9a5246c 100644
--- a/apps/create-fuels-counter-guide/eslint.config.js
+++ b/apps/create-fuels-counter-guide/eslint.config.js
@@ -1,8 +1,8 @@
-import js from '@eslint/js'
-import globals from 'globals'
-import reactHooks from 'eslint-plugin-react-hooks'
-import reactRefresh from 'eslint-plugin-react-refresh'
-import tseslint from 'typescript-eslint'
+import js from '@eslint/js';
+import reactHooks from 'eslint-plugin-react-hooks';
+import reactRefresh from 'eslint-plugin-react-refresh';
+import globals from 'globals';
+import tseslint from 'typescript-eslint';
export default tseslint.config({
extends: [js.configs.recommended, ...tseslint.configs.recommended],
@@ -18,9 +18,6 @@ export default tseslint.config({
},
rules: {
...reactHooks.configs.recommended.rules,
- 'react-refresh/only-export-components': [
- 'warn',
- { allowConstantExport: true },
- ],
+ 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
},
-})
+});
diff --git a/apps/create-fuels-counter-guide/fuel-toolchain.toml b/apps/create-fuels-counter-guide/fuel-toolchain.toml
index cf8d5119a82..8bf9fc3664b 100644
--- a/apps/create-fuels-counter-guide/fuel-toolchain.toml
+++ b/apps/create-fuels-counter-guide/fuel-toolchain.toml
@@ -2,5 +2,5 @@
channel = "testnet"
[components]
-forc = "0.63.1"
-fuel-core = "0.34.0"
+forc = "0.63.5"
+fuel-core = "0.35.0"
diff --git a/apps/create-fuels-counter-guide/fuels.config.ts b/apps/create-fuels-counter-guide/fuels.config.ts
index 1c0ea70a379..0d70c7d00bc 100644
--- a/apps/create-fuels-counter-guide/fuels.config.ts
+++ b/apps/create-fuels-counter-guide/fuels.config.ts
@@ -1,7 +1,7 @@
// #region fuels-config-file-env
import { createConfig } from 'fuels';
import dotenv from 'dotenv';
-import { NODE_URL } from './src/lib';
+import { providerUrl } from './src/lib';
dotenv.config({
path: ['.env.local', '.env'],
@@ -14,8 +14,8 @@ export default createConfig({
workspace: './sway-programs', // Path to your Sway workspace
output: './src/sway-api', // Where your generated types will be saved
fuelCorePort,
- providerUrl: NODE_URL,
- fuelCorePath: 'fuels-core',
+ providerUrl,
forcPath: 'fuels-forc',
+ fuelCorePath: 'fuels-core',
});
// #endregion fuels-config-file-env
\ No newline at end of file
diff --git a/apps/create-fuels-counter-guide/index.html b/apps/create-fuels-counter-guide/index.html
index 999540e98ed..fda9ea040f7 100644
--- a/apps/create-fuels-counter-guide/index.html
+++ b/apps/create-fuels-counter-guide/index.html
@@ -3,7 +3,7 @@
-
+
Fuel dApp
diff --git a/apps/create-fuels-counter-guide/package.json b/apps/create-fuels-counter-guide/package.json
index aa3507ae139..8f3698d79f4 100644
--- a/apps/create-fuels-counter-guide/package.json
+++ b/apps/create-fuels-counter-guide/package.json
@@ -17,24 +17,22 @@
"@fuels/connectors": "^0.27.1",
"@fuels/react": "^0.27.1",
"@tanstack/react-query": "^5.55.4",
- "@tanstack/react-router": "^1.58.3",
- "fuels": "workspace:*",
+ "clsx": "2.1.1",
"@wagmi/connectors": "^5.1.12",
"@wagmi/core": "^2.13.6",
"dotenv": "^16.4.5",
+ "fuels": "workspace:*",
"react": "^18.3.1",
"react-dom": "^18.3.1",
- "react-hot-toast": "^2.4.1",
- "react-use": "^17.5.1"
+ "react-toastify": "^10.0.5"
},
"devDependencies": {
+ "@vitejs/plugin-react": "^4.3.1",
"@eslint/js": "^9.10.0",
- "@tanstack/router-plugin": "^1.56.4",
"@types/node": "^22.5.5",
"@playwright/test": "^1.47.2",
"@types/react": "^18.3.8",
"@types/react-dom": "^18.3",
- "@vitejs/plugin-react-swc": "^3.7.0",
"autoprefixer": "^10.4.20",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.2",
@@ -45,6 +43,6 @@
"typescript": "~5.6.2",
"typescript-eslint": "^8.5.0",
"vite": "^5.4.7",
- "vitest": "^2.0.5"
+ "vitest": "~2.0.5"
}
}
diff --git a/apps/create-fuels-counter-guide/playwright.config.ts b/apps/create-fuels-counter-guide/playwright.config.ts
index f085454c869..93a1f056062 100644
--- a/apps/create-fuels-counter-guide/playwright.config.ts
+++ b/apps/create-fuels-counter-guide/playwright.config.ts
@@ -37,6 +37,6 @@ export default defineConfig({
command: 'pnpm run fuels:dev',
port: 4000,
reuseExistingServer: !process.env.CI,
- }
- ]
+ },
+ ],
});
diff --git a/apps/create-fuels-counter-guide/postcss.config.js b/apps/create-fuels-counter-guide/postcss.config.js
index 2e7af2b7f1a..2aa7205d4b4 100644
--- a/apps/create-fuels-counter-guide/postcss.config.js
+++ b/apps/create-fuels-counter-guide/postcss.config.js
@@ -3,4 +3,4 @@ export default {
tailwindcss: {},
autoprefixer: {},
},
-}
+};
diff --git a/apps/create-fuels-counter-guide/public/copy.svg b/apps/create-fuels-counter-guide/public/copy.svg
deleted file mode 100644
index 132dc32ce7c..00000000000
--- a/apps/create-fuels-counter-guide/public/copy.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/apps/create-fuels-counter-guide/public/fuel.ico b/apps/create-fuels-counter-guide/public/favicon.ico
similarity index 100%
rename from apps/create-fuels-counter-guide/public/fuel.ico
rename to apps/create-fuels-counter-guide/public/favicon.ico
diff --git a/apps/create-fuels-counter-guide/public/logo_white.png b/apps/create-fuels-counter-guide/public/logo_white.png
new file mode 100644
index 00000000000..39bfbae892b
Binary files /dev/null and b/apps/create-fuels-counter-guide/public/logo_white.png differ
diff --git a/apps/create-fuels-counter-guide/src/App.tsx b/apps/create-fuels-counter-guide/src/App.tsx
new file mode 100644
index 00000000000..0ab05d2a1c4
--- /dev/null
+++ b/apps/create-fuels-counter-guide/src/App.tsx
@@ -0,0 +1,102 @@
+import { useConnectUI, useIsConnected, useNetwork } from "@fuels/react";
+import { useEffect } from "react";
+
+import { useRouter } from "./hooks/useRouter";
+import Button from "./components/Button";
+import Info from "./components/Info";
+import Wallet from "./components/Wallet";
+import Contract from "./components/Contract";
+import Predicate from "./components/Predicate";
+import Script from "./components/Script";
+import Faucet from "./components/Faucet";
+import { providerUrl } from './lib'
+
+function App() {
+ const { connect } = useConnectUI();
+ const { isConnected, refetch } = useIsConnected();
+ const { network } = useNetwork();
+ const { view, views, setRoute } = useRouter();
+ const isConnectedToCorrectNetwork = network?.url === providerUrl;
+
+
+ useEffect(() => {
+ refetch();
+ }, [refetch]);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {!isConnected && (
+
+ connect()}>Connect Wallet
+
+ )}
+
+ {isConnected && !isConnectedToCorrectNetwork && (
+
+
+ You are connected to the wrong network. Please switch to{" "}
+
+ {providerUrl}
+
+ in your wallet.
+
+
+ )}
+
+ {isConnected && isConnectedToCorrectNetwork && (
+
+
+ {views.map((viewName) => (
+ setRoute(viewName)}
+ >
+ {viewName}
+
+ ))}
+
+
+ {view === "wallet" && }
+ {view === "contract" && }
+ {view === "predicate" && }
+ {view === "script" && }
+ {view === "faucet" && }
+
+ )}
+
+
+
+
+
+
+
+ );
+}
+
+export default App;
diff --git a/apps/create-fuels-counter-guide/src/components/Button.tsx b/apps/create-fuels-counter-guide/src/components/Button.tsx
index 17f64436ddf..ed8426aa784 100644
--- a/apps/create-fuels-counter-guide/src/components/Button.tsx
+++ b/apps/create-fuels-counter-guide/src/components/Button.tsx
@@ -1,14 +1,27 @@
-export const Button: React.FC<{
- children: React.ReactNode;
- className?: string;
- onClick?: () => void;
-}> = ({ children, className, onClick }) => {
+import React from "react";
+import clsx from "clsx";
+
+export type Props = {
+ color?: "primary" | "secondary" | "inactive";
+} & React.ComponentProps<"button">;
+
+export default function Button(props: Props) {
+ const { color = "primary", children, disabled, className, ...rest } = props;
+
return (
{children}
);
-};
+}
diff --git a/apps/create-fuels-counter-guide/src/components/Contract.tsx b/apps/create-fuels-counter-guide/src/components/Contract.tsx
new file mode 100644
index 00000000000..45dde445761
--- /dev/null
+++ b/apps/create-fuels-counter-guide/src/components/Contract.tsx
@@ -0,0 +1,139 @@
+import { useEffect, useState } from "react";
+import { useWallet } from "@fuels/react";
+
+import LocalFaucet from "./LocalFaucet";
+import { TestContract } from "../sway-api";
+import Button from "./Button";
+import { isLocal, contractId } from "../lib.tsx";
+import { useNotification } from "../hooks/useNotification.tsx";
+
+export default function Contract() {
+ const { errorNotification, transactionSubmitNotification, transactionSuccessNotification } = useNotification();
+ useNotification();
+ const [contract, setContract] = useState();
+ const [counter, setCounter] = useState();
+ const [isLoading, setIsLoading] = useState(false);
+
+ const { wallet, refetch } = useWallet();
+
+ useEffect(() => {
+ if (wallet) {
+ const testContract = new TestContract(contractId, wallet);
+ setContract(testContract);
+ }
+ }, [wallet]);
+
+ useEffect(() => {
+ if (contract && !counter) {
+ const getCount = async () => {
+ const { value } = await contract.functions.get_count().get();
+ setCounter(value.toNumber());
+ };
+
+ getCount();
+ }
+ }, [contract, counter]);
+
+ async function incrementCounter() {
+ if (!wallet || !contract) return;
+ setIsLoading(true);
+
+ try {
+ const call = await contract.functions.increment_counter(1).call();
+ transactionSubmitNotification(call.transactionId);
+ const result = await call.waitForResult();
+ transactionSuccessNotification(result.transactionId);
+ setCounter(result.value.toNumber());
+ } catch (error) {
+ console.error(error);
+ errorNotification("Error incrementing counter");
+ }
+ setIsLoading(false);
+ }
+
+ // #region create-fuels-counter-guide-on-decrement-react-function
+ async function decrementCounter() {
+ if (!wallet || !contract) return;
+ setIsLoading(true);
+
+ try {
+ const call = await contract.functions.decrement_counter(1).call();
+ transactionSubmitNotification(call.transactionId);
+ const result = await call.waitForResult();
+ transactionSuccessNotification(result.transactionId);
+ setCounter(result.value.toNumber());
+ } catch (error) {
+ console.error(error);
+ errorNotification("Error decrementing counter");
+ }
+ setIsLoading(false);
+ }
+ // #endregion create-fuels-counter-guide-on-decrement-react-function
+
+ return (
+ <>
+
+
+ Counter
+
+
+
+
+ Increment
+
+
+
+ Decrement
+
+
+
+
+
+ Contracts are a core program type on the Fuel network. You can read
+ more about them{" "}
+
+ here
+
+ .
+
+
+ This is a simple counter contract which you can edit at{" "}
+ sway-programs/contract/src/main.sw
.
+
+
+ Extend this example by adding decrement functionality by working
+ through{" "}
+
+ this guide
+
+ .
+
+
+ {isLocal && }
+ >
+ );
+}
diff --git a/apps/create-fuels-counter-guide/src/components/Faucet.tsx b/apps/create-fuels-counter-guide/src/components/Faucet.tsx
new file mode 100644
index 00000000000..195ccba8760
--- /dev/null
+++ b/apps/create-fuels-counter-guide/src/components/Faucet.tsx
@@ -0,0 +1,100 @@
+import { useBalance, useWallet } from "@fuels/react";
+import { useEffect, useState } from "react";
+
+import LocalFaucet from "./LocalFaucet";
+import Button from "./Button";
+import { isLocal, renderFormattedBalance, testnetFaucetUrl } from "../lib.tsx";
+
+export default function Faucet() {
+ const { wallet } = useWallet();
+ const connectedWalletAddress = wallet?.address.toB256() || "";
+
+ const [addressToFund, setAddressToFund] = useState("");
+
+ const { balance, refetch } = useBalance({ address: addressToFund });
+
+ const [initialAddressLoaded, setInitialAddressLoaded] = useState(false);
+
+ useEffect(() => {
+ if (connectedWalletAddress && !initialAddressLoaded && !addressToFund) {
+ setAddressToFund(connectedWalletAddress);
+ setInitialAddressLoaded(true);
+ }
+ }, [connectedWalletAddress, addressToFund, initialAddressLoaded]);
+
+ useEffect(() => {
+ refetch();
+ }, [refetch]);
+
+ useEffect(() => {
+ const interval = setInterval(() => refetch(), 5000);
+ return () => clearInterval(interval);
+ }, [refetch]);
+
+ return (
+ <>
+
+
+ Address
+
+
+ setAddressToFund(e.target.value)}
+ />
+
+
+
+
+
+ Balance
+
+
+
+ refetch()}>
+ Refresh
+
+
+
+
+
+ {!isLocal && (
+ <>
+
+ Testnet Faucet
+
+
+ >
+ )}
+
+ {isLocal && (
+ <>
+
+
+ If you would like to visit the testnet faucet, you can do so{" "}
+
+ here
+
+ .
+
+ >
+ )}
+ >
+ );
+}
diff --git a/apps/create-fuels-counter-guide/src/components/FuelLogo.tsx b/apps/create-fuels-counter-guide/src/components/FuelLogo.tsx
deleted file mode 100644
index 359489fdcca..00000000000
--- a/apps/create-fuels-counter-guide/src/components/FuelLogo.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-export const FuelLogo = () => {
- return (
-
-
-
-
- );
-};
diff --git a/apps/create-fuels-counter-guide/src/components/Info.tsx b/apps/create-fuels-counter-guide/src/components/Info.tsx
new file mode 100644
index 00000000000..939ec41849c
--- /dev/null
+++ b/apps/create-fuels-counter-guide/src/components/Info.tsx
@@ -0,0 +1,47 @@
+import { providerUrl, playgroundUrl } from "../lib.tsx";
+
+export default function Info() {
+ return (
+
+
Welcome to Fuel
+
+ This Vite + React template was bootstrapped with the{" "}
+
+ Fuels CLI
+
+
+
+ You are currently connected to:{" "}
+
+ {providerUrl}
+
+
+
+ Fuel Docs
+
+
+ GraphQL Playground
+
+
+ );
+}
diff --git a/apps/create-fuels-counter-guide/src/components/Input.tsx b/apps/create-fuels-counter-guide/src/components/Input.tsx
deleted file mode 100644
index 083b1bb5a33..00000000000
--- a/apps/create-fuels-counter-guide/src/components/Input.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-export const Input: React.FC<{
- value?: string;
- onChange: (e: React.ChangeEvent) => void;
- placeholder?: string;
- type?: string;
- className?: string;
- id?: string;
-}> = ({ value, onChange, placeholder, type, className, id }) => {
- return (
-
- );
-};
diff --git a/apps/create-fuels-counter-guide/src/components/Layout.tsx b/apps/create-fuels-counter-guide/src/components/Layout.tsx
deleted file mode 100644
index 9874fc97d34..00000000000
--- a/apps/create-fuels-counter-guide/src/components/Layout.tsx
+++ /dev/null
@@ -1,117 +0,0 @@
-import toast, { Toaster } from "react-hot-toast";
-import { Link } from "./Link";
-import { Button } from "./Button";
-import { CURRENT_ENVIRONMENT, NODE_URL, TESTNET_FAUCET_LINK } from "../lib";
-import { useConnectUI, useDisconnect } from "@fuels/react";
-import { WalletDisplay } from "./WalletDisplay";
-import { useBrowserWallet } from "../hooks/useBrowserWallet";
-import { useActiveWallet } from "../hooks/useActiveWallet";
-import { useFaucet } from "../hooks/useFaucet";
-import { bn } from "fuels";
-
-export const Layout = ({ children }: { children: React.ReactNode }) => {
- const { faucetWallet } = useFaucet();
-
- const {
- wallet: browserWallet,
- isConnected: isBrowserWalletConnected,
- network: browserWalletNetwork,
- } = useBrowserWallet();
-
- const { connect } = useConnectUI();
- const { disconnect } = useDisconnect();
-
- const { wallet, refreshWalletBalance, walletBalance } = useActiveWallet();
-
- const topUpWallet = async () => {
- if (!wallet) {
- return console.error("Unable to topup wallet because wallet is not set.");
- }
-
- /**
- * If the current environment is local, transfer 5 ETH to the wallet
- * from the local faucet wallet
- */
- if (CURRENT_ENVIRONMENT === "local") {
- if (!faucetWallet) {
- return toast.error("Faucet wallet not found.");
- }
-
- const tx = await faucetWallet?.transfer(
- wallet.address,
- bn.parseUnits("5"),
- );
- await tx?.waitForResult();
-
- toast.success("Wallet topped up!");
-
- return await refreshWalletBalance?.();
- }
-
- // If the current environment is testnet, open the testnet faucet link in a new tab
- if (CURRENT_ENVIRONMENT === "testnet") {
- return window.open(
- `${TESTNET_FAUCET_LINK}?address=${wallet.address.toAddress()}`,
- "_blank",
- );
- }
- };
-
- const showTopUpButton = walletBalance?.lt(bn.parseUnits("5"));
-
- const showAddNetworkButton =
- browserWallet &&
- browserWalletNetwork &&
- browserWalletNetwork?.url !== NODE_URL;
-
- const tryToAddNetwork = () => {
- return alert(
- `Please add the network ${NODE_URL} to your Fuel wallet, or swtich to it if you have it already, and refresh the page.`,
- );
- };
-
- return (
- <>
-
-
-
- Home
-
-
- Faucet
-
-
- {isBrowserWalletConnected && (
- Disconnect Wallet
- )}
- {!isBrowserWalletConnected && (
- Connect Wallet
- )}
-
- {showAddNetworkButton && (
-
- Wrong Network
-
- )}
-
-
-
-
-
- {showTopUpButton && (
- topUpWallet()}>Top-up Wallet
- )}
-
-
-
- {children}
-
-
- >
- );
-};
diff --git a/apps/create-fuels-counter-guide/src/components/Link.tsx b/apps/create-fuels-counter-guide/src/components/Link.tsx
deleted file mode 100644
index c5441949104..00000000000
--- a/apps/create-fuels-counter-guide/src/components/Link.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import { Link as ReactRouterLink } from "@tanstack/react-router";
-
-export const Link = ({
- href,
- children,
- className,
- target,
-}: {
- href: string;
- children: React.ReactNode;
- className?: string;
- target?: string;
-}) => {
- return (
-
- {children}
-
- );
-};
diff --git a/apps/create-fuels-counter-guide/src/components/LocalFaucet.tsx b/apps/create-fuels-counter-guide/src/components/LocalFaucet.tsx
new file mode 100644
index 00000000000..699ec828ae2
--- /dev/null
+++ b/apps/create-fuels-counter-guide/src/components/LocalFaucet.tsx
@@ -0,0 +1,62 @@
+import { Address, bn, WalletUnlocked } from "fuels";
+import { useWallet } from "@fuels/react";
+import { useState } from "react";
+
+import Button from "./Button";
+import { useNotification } from "../hooks/useNotification.tsx";
+
+type Props = {
+ refetch: () => void;
+ addressToFund?: string;
+};
+
+export default function LocalFaucet({ refetch, addressToFund }: Props) {
+ const [isLoading, setIsLoading] = useState(false);
+ const { errorNotification, transactionSubmitNotification, transactionSuccessNotification } = useNotification();
+
+ const { wallet } = useWallet();
+
+ async function localTransfer() {
+ if (!wallet) return;
+ setIsLoading(true);
+ try {
+ const genesis = new WalletUnlocked(
+ process.env.VITE_GENESIS_WALLET_PRIVATE_KEY as string,
+ wallet.provider,
+ );
+ const tx = await genesis.transfer(
+ Address.fromB256(addressToFund || wallet.address.toB256()),
+ bn(5_000_000_000),
+ );
+ transactionSubmitNotification(tx.id);
+ await tx.waitForResult();
+ transactionSuccessNotification(tx.id);
+ } catch (error) {
+ console.error(error);
+ errorNotification("Error transferring funds.");
+ }
+ setIsLoading(false);
+ refetch();
+ }
+
+ return (
+ <>
+
+
+
+
+ As the dApp is running locally, you can transfer 5 ETH to your
+ address via the genesis wallet.
+
+
+ Transfer 5 ETH
+
+
+
+ >
+ );
+}
diff --git a/apps/create-fuels-counter-guide/src/components/Predicate.tsx b/apps/create-fuels-counter-guide/src/components/Predicate.tsx
new file mode 100644
index 00000000000..51095a26ef8
--- /dev/null
+++ b/apps/create-fuels-counter-guide/src/components/Predicate.tsx
@@ -0,0 +1,220 @@
+import { useBalance, useWallet } from "@fuels/react";
+import { useEffect, useState } from "react";
+import { bn, Predicate as FuelPredicate, InputValue } from "fuels";
+
+import { TestPredicate } from "../sway-api/predicates";
+import Button from "./Button";
+import LocalFaucet from "./LocalFaucet";
+import { isLocal, renderFormattedBalance } from "../lib.tsx";
+import { useNotification } from "../hooks/useNotification.tsx";
+
+export default function Predicate() {
+ const { errorNotification, transactionSubmitNotification, transactionSuccessNotification, successNotification } = useNotification();
+ useNotification();
+ const [predicate, setPredicate] = useState>();
+ const [predicatePin, setPredicatePin] = useState();
+ const [isLoading, setIsLoading] = useState(false);
+
+ const { wallet } = useWallet();
+ const address = wallet?.address.toB256() || "";
+ const { balance: walletBalance, refetch: refetchWallet } = useBalance({
+ address,
+ });
+ const predicateAddress = predicate?.address?.toB256();
+ const { balance: predicateBalance, refetch: refetchPredicate } = useBalance({
+ address: predicateAddress,
+ });
+
+ useEffect(() => {
+ if (wallet) {
+ const testPredicate = new TestPredicate(wallet);
+ setPredicate(testPredicate);
+ }
+ }, [wallet]);
+
+ const refetch = () => {
+ refetchWallet();
+ refetchPredicate();
+ };
+
+ const transferToPredicate = async () => {
+ if (!wallet || !predicate) return;
+ setIsLoading(true);
+
+ try {
+ const tx = await wallet.transfer(predicate.address, bn(2_000_000));
+ transactionSubmitNotification(tx.id);
+ await tx.waitForResult();
+ transactionSuccessNotification(tx.id);
+ } catch (error) {
+ console.error(error);
+ errorNotification("Error transferring funds. Check your wallet balance.");
+ }
+ setIsLoading(false);
+ refetch();
+ };
+
+ const transferToWallet = async () => {
+ if (!wallet || !predicate) return;
+ setIsLoading(true);
+
+ try {
+ const newPredicate = new TestPredicate({
+ provider: wallet.provider,
+ data: [bn(predicatePin)],
+ });
+
+ const tx = await newPredicate.transfer(wallet.address, bn(1_000_000));
+ transactionSubmitNotification(tx.id);
+ await tx.waitForResult();
+ transactionSuccessNotification(tx.id);
+ } catch (error) {
+ console.error(error);
+ errorNotification(
+ "Error transferring funds. Check the predicate balance and that you are using the correct pin.",
+ );
+ }
+ setIsLoading(false);
+ refetch();
+ };
+
+ // #region change-pin-react-function
+ const changePin = async () => {
+ if (!wallet || !predicate) return;
+ setIsLoading(true);
+
+ try {
+ const configurableConstants = { PIN: bn(predicatePin) };
+ const newPredicate = new TestPredicate({
+ provider: wallet.provider,
+ data: [configurableConstants.PIN],
+ configurableConstants,
+ });
+
+ const tx = await wallet.transfer(newPredicate.address, bn(2_000_000));
+ transactionSubmitNotification(tx.id);
+ await tx.waitForResult();
+ transactionSuccessNotification(tx.id);
+ } catch (error) {
+ console.error(error);
+ errorNotification(
+ "Error changing pin.",
+ );
+ }
+ setIsLoading(false);
+ refetch();
+ };
+ // #endregion change-pin-react-function
+
+ const copyAddress = () => {
+ if (!predicate) return;
+ navigator.clipboard.writeText(predicate?.address.toB256());
+ successNotification("Address copied to clipboard");
+ };
+
+ return (
+ <>
+
+
+ Predicates are specific types of programs that return a boolean value,
+ meaning they function like rules that a transaction must follow to be
+ valid. You can read more about them{" "}
+
+ here
+
+ .
+
+
+ In the below example, we transfer{" "}
+ 0.002 ETH to the
+ predicate but need to pass a pin to unlock the predicate and transfer{" "}
+ 0.001 ETH back to us.
+
+
+ You can alter the logic in the predicate at{" "}
+ sway-programs/predicates/src/main.sw
.
+
+
+
+
+
+ Predicate Address
+
+
+
+
+ Copy
+
+
+
+
+
+
+ Wallet Balance
+
+
+
+
+ Transfer
+
+
+
+
+
+
+ Unlock and Transfer
+
+
+ Change Pin
+
+
+ {isLocal && }
+ >
+ );
+}
diff --git a/apps/create-fuels-counter-guide/src/components/Script.tsx b/apps/create-fuels-counter-guide/src/components/Script.tsx
new file mode 100644
index 00000000000..db1ce3263f6
--- /dev/null
+++ b/apps/create-fuels-counter-guide/src/components/Script.tsx
@@ -0,0 +1,108 @@
+import { useWallet } from "@fuels/react";
+import { BigNumberish, BN, Script as FuelScript } from "fuels";
+import { useEffect, useState } from "react";
+
+import { TestScript } from "../sway-api";
+import Button from "./Button";
+import LocalFaucet from "./LocalFaucet";
+import { isLocal } from "../lib.tsx";
+import { useNotification } from "../hooks/useNotification.tsx";
+
+export default function Script() {
+ const { errorNotification, transactionSubmitNotification, transactionSuccessNotification } = useNotification();
+ useNotification();
+ const [script, setScript] = useState>();
+ const [input, setInput] = useState();
+ const [result, setResult] = useState();
+ const [isLoading, setIsLoading] = useState(false);
+
+ const { wallet, refetch } = useWallet();
+
+ useEffect(() => {
+ if (wallet) {
+ const testScript = new TestScript(wallet);
+ setScript(testScript);
+ }
+ }, [wallet]);
+
+ const submit = async () => {
+ if (!script || !input) return;
+ setIsLoading(true);
+ try {
+ const tx = await script.functions.main(input).call();
+ transactionSubmitNotification(tx.transactionId);
+ const result = await tx.waitForResult();
+ transactionSuccessNotification(result.transactionId);
+ setResult(result.value.toNumber());
+ } catch (error) {
+ console.error(error);
+ errorNotification(
+ "Error running script. Please make sure your wallet has enough funds. You can top it up using the faucet.",
+ );
+ }
+ setIsLoading(false);
+ };
+
+ return (
+ <>
+
+
+ Scripts are runnable bytecode on the chain which execute once to
+ perform some task. Scripts can return a single value of any type. You
+ can read more about them{" "}
+
+ here
+
+ .
+
+
+ In the below example we can pass a number input and it returns the
+ same value in the transaction output.
+
+
+ You can alter the logic in the script at{" "}
+ sway-programs/scripts/src/main.sw
.
+
+
+
+
+
+ Script Input
+
+
+ setInput(Number(e.target.value))}
+ className="w-full bg-gray-800 rounded-md px-2 py-1 truncate font-mono"
+ placeholder="Hint: 123"
+ />
+
+
+
+
+ Script Output
+
+
+
+
+
+
+
+
+ Submit
+
+
+ {isLocal && }
+ >
+ );
+}
diff --git a/apps/create-fuels-counter-guide/src/components/Wallet.tsx b/apps/create-fuels-counter-guide/src/components/Wallet.tsx
new file mode 100644
index 00000000000..75ab9a6a411
--- /dev/null
+++ b/apps/create-fuels-counter-guide/src/components/Wallet.tsx
@@ -0,0 +1,75 @@
+import { useDisconnect, useWallet, useBalance } from "@fuels/react";
+import { useEffect } from "react";
+
+import Button from "./Button";
+import LocalFaucet from "./LocalFaucet";
+import { isLocal, renderFormattedBalance } from "../lib.tsx";
+
+export default function Wallet() {
+ const { disconnect } = useDisconnect();
+ const { wallet } = useWallet();
+ const address = wallet?.address.toB256() || "";
+ const { balance, refetch } = useBalance({ address });
+
+ useEffect(() => {
+ refetch();
+ }, [refetch]);
+
+ useEffect(() => {
+ const interval = setInterval(() => refetch(), 5000);
+ return () => clearInterval(interval);
+ }, [refetch]);
+
+ return (
+ <>
+
+
+ Address
+
+
+
+ disconnect()} className="w-1/3">
+ Disconnect
+
+
+
+
+
+ Balance
+
+
+
+ refetch()} className="w-1/3">
+ Refresh
+
+
+
+
+
+ Fuel supports a range of wallets. This dApp utilizes wallet connectors
+ to provide simple wallet integration. You can read more about them{" "}
+
+ here
+
+ .
+
+
+ {isLocal && }
+ >
+ );
+}
diff --git a/apps/create-fuels-counter-guide/src/components/WalletDisplay.tsx b/apps/create-fuels-counter-guide/src/components/WalletDisplay.tsx
deleted file mode 100644
index 9a8b889b493..00000000000
--- a/apps/create-fuels-counter-guide/src/components/WalletDisplay.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import toast from "react-hot-toast";
-import { useActiveWallet } from "../hooks/useActiveWallet";
-
-const getTruncatedAddress = (address: string) => {
- return address.slice(0, 6) + "..." + address.slice(-4);
-};
-
-const copyToClipboard = (text: string) => {
- navigator.clipboard.writeText(text);
- toast.success("Address copied to clipboard");
-};
-
-export const WalletDisplay = () => {
- const { wallet, walletBalance } = useActiveWallet();
-
- return (
- wallet && (
-
-
- {getTruncatedAddress(wallet.address.toB256() as string)}
-
-
copyToClipboard(wallet.address.toB256() as string)}
- />
-
- Balance:{" "}
- {walletBalance?.format({
- precision: 3,
- })}{" "}
- ETH
-
-
- )
- );
-};
diff --git a/apps/create-fuels-counter-guide/src/hooks/useActiveWallet.tsx b/apps/create-fuels-counter-guide/src/hooks/useActiveWallet.tsx
deleted file mode 100644
index 51a875fd22f..00000000000
--- a/apps/create-fuels-counter-guide/src/hooks/useActiveWallet.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import { useEffect, useState, createContext, useContext } from "react";
-import { useBrowserWallet } from "./useBrowserWallet";
-import { useBurnerWallet } from "./useBurnerWallet";
-import { AppWallet } from "../lib";
-
-/**
- * burner -> a burner wallet embedded inside of the template app and stored in local storage
- * browser -> a wallet connected via a browser extension like the Fuel Wallet
- *
- * Whenever a browser wallet is connected, this hook will return an instance of the browser wallet.
- * Otherwise, it will return an instance of a local burner wallet.
- */
-type WalletTypes = "burner" | "browser";
-
-const ActiveWalletContext = createContext({});
-
-export const ActiveWalletProvider = ({
- children,
-}: {
- children: React.ReactNode;
-}) => {
- const [activeWallet, setActiveWallet] = useState("burner");
- const {
- wallet: burnerWallet,
- walletBalance: burnerWalletBalance,
- refreshWalletBalance: refreshBurnerWalletBalance,
- } = useBurnerWallet();
- const {
- wallet: browserWallet,
- walletBalance: browserWalletBalance,
- refreshWalletBalance: refreshBrowserWalletBalance,
- isConnected: isBrowserWalletConnected,
- } = useBrowserWallet();
-
- useEffect(() => {
- if (isBrowserWalletConnected) {
- setActiveWallet("browser");
- refreshBrowserWalletBalance?.();
- } else {
- setActiveWallet("burner");
- refreshBurnerWalletBalance?.();
- }
- }, [isBrowserWalletConnected]);
-
- const value = {
- wallet: activeWallet === "browser" ? browserWallet : burnerWallet,
- walletBalance:
- activeWallet === "browser" ? browserWalletBalance : burnerWalletBalance,
- refreshWalletBalance:
- activeWallet == "browser"
- ? refreshBrowserWalletBalance
- : refreshBurnerWalletBalance,
- };
-
- return (
-
- {children}
-
- );
-};
-
-export const useActiveWallet = (): AppWallet => useContext(ActiveWalletContext);
diff --git a/apps/create-fuels-counter-guide/src/hooks/useBrowserWallet.tsx b/apps/create-fuels-counter-guide/src/hooks/useBrowserWallet.tsx
deleted file mode 100644
index f4891f78727..00000000000
--- a/apps/create-fuels-counter-guide/src/hooks/useBrowserWallet.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import { AppWallet } from "../lib";
-import { useIsConnected, useNetwork, useWallet } from "@fuels/react";
-import { BN } from "fuels";
-import { useCallback, useState } from "react";
-import useAsync from "react-use/lib/useAsync";
-
-interface BrowserWallet extends AppWallet {
- isConnected: boolean | null;
- network: any;
-}
-
-/**
- * This hook returns an instance of the browser wallet connected via
- * any of the supported connectors.
- **/
-export const useBrowserWallet: () => BrowserWallet = () => {
- const { wallet } = useWallet();
- const [browserWalletBalance, setBrowserWalletBalance] = useState();
- const { isConnected } = useIsConnected();
- const { network } = useNetwork();
-
- useAsync(async () => {
- if (wallet) {
- await wallet.getBalance().then(setBrowserWalletBalance);
- }
- }, [wallet]);
-
- const refreshBrowserWalletBalance = useCallback(async () => {
- if (wallet) {
- const browserWalletBalance = await wallet.getBalance();
- setBrowserWalletBalance(browserWalletBalance);
- }
- }, [wallet]);
-
- return {
- wallet: wallet || undefined,
- walletBalance: browserWalletBalance,
- refreshWalletBalance: refreshBrowserWalletBalance,
- isConnected,
- network,
- };
-};
diff --git a/apps/create-fuels-counter-guide/src/hooks/useBurnerWallet.tsx b/apps/create-fuels-counter-guide/src/hooks/useBurnerWallet.tsx
deleted file mode 100644
index 1fb0e1e01b9..00000000000
--- a/apps/create-fuels-counter-guide/src/hooks/useBurnerWallet.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import { AppWallet, NODE_URL } from "../lib";
-import { BN, Provider, Wallet, WalletUnlocked } from "fuels";
-import { useCallback, useState } from "react";
-import useAsync from "react-use/lib/useAsync";
-
-const BURNER_WALLET_LOCAL_STORAGE_KEY = "create-fuels-burner-wallet-pk";
-
-/**
- * This hook returns an instance of a burner wallet that lives in local storage.
- */
-export const useBurnerWallet: () => AppWallet = () => {
- const [burnerWallet, setBurnerWallet] = useState();
- const [burnerWalletBalance, setBurnerWalletBalance] = useState();
-
- useAsync(async () => {
- // check if burner wallet pk is stored in local storage
- const burnerWalletPk = localStorage.getItem(
- BURNER_WALLET_LOCAL_STORAGE_KEY,
- );
-
- let wallet: WalletUnlocked;
-
- if (burnerWalletPk) {
- const provider = await Provider.create(NODE_URL);
- wallet = Wallet.fromPrivateKey(burnerWalletPk, provider);
- setBurnerWallet(wallet);
- } else {
- // if not, create a new burner wallet
- const provider = await Provider.create(NODE_URL);
- wallet = Wallet.generate({ provider });
-
- localStorage.setItem(BURNER_WALLET_LOCAL_STORAGE_KEY, wallet.privateKey);
- setBurnerWallet(wallet);
- }
-
- const burnerWalletBalance = await wallet?.getBalance();
- setBurnerWalletBalance(burnerWalletBalance);
- }, []);
-
- const refreshBurnerWalletBalance = useCallback(async () => {
- const burnerWalletBalance = await burnerWallet?.getBalance();
- setBurnerWalletBalance(burnerWalletBalance);
- }, [burnerWallet]);
-
- return {
- wallet: burnerWallet,
- walletBalance: burnerWalletBalance,
- refreshWalletBalance: refreshBurnerWalletBalance,
- };
-};
diff --git a/apps/create-fuels-counter-guide/src/hooks/useFaucet.tsx b/apps/create-fuels-counter-guide/src/hooks/useFaucet.tsx
deleted file mode 100644
index 72d72106949..00000000000
--- a/apps/create-fuels-counter-guide/src/hooks/useFaucet.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { FAUCET_PRIVATE_KEY, NODE_URL } from "../lib";
-import { Provider, Wallet, WalletUnlocked } from "fuels";
-import { useState } from "react";
-import useAsync from "react-use/lib/useAsync";
-
-/**
- * This hook returns an instance of a faucet wallet.
- * The value of `FAUCET_PRIVATE_KEY` depends on your chain config.
- */
-export const useFaucet = () => {
- const [faucetWallet, setFaucetWallet] = useState();
-
- useAsync(async () => {
- if (!faucetWallet) {
- const provider = await Provider.create(NODE_URL);
- const wallet = Wallet.fromPrivateKey(FAUCET_PRIVATE_KEY, provider);
- setFaucetWallet(wallet);
- }
- }, [faucetWallet]);
-
- return {
- faucetWallet,
- };
-};
diff --git a/apps/create-fuels-counter-guide/src/hooks/useNotification.tsx b/apps/create-fuels-counter-guide/src/hooks/useNotification.tsx
new file mode 100644
index 00000000000..fc03b71b86e
--- /dev/null
+++ b/apps/create-fuels-counter-guide/src/hooks/useNotification.tsx
@@ -0,0 +1,49 @@
+import { ReactNode, useRef } from "react";
+
+import { toast, Id, TypeOptions } from "react-toastify";
+import { renderTransactionId } from "../lib.tsx";
+
+/**
+ * Hook to handle app notifications
+ */
+export const useNotification = () => {
+ const notification = useRef(null);
+
+ const onClose = () => {
+ notification.current = null;
+ };
+
+ const showNotification = (render: string | ReactNode, type: TypeOptions) => {
+ if (notification.current) {
+ toast.update(notification.current, { render, type, onClose });
+ } else {
+ notification.current =
+ type === "default"
+ ? toast.info(render, { onClose })
+ : toast[type](render, { onClose });
+ }
+ };
+
+ return {
+ infoNotification: (render: string | ReactNode) =>
+ showNotification(render, "info"),
+ successNotification: (render: string | ReactNode) =>
+ showNotification(render, "success"),
+ errorNotification: (render: string | ReactNode) =>
+ showNotification(render, "error"),
+ transactionSubmitNotification: (transactionId: string) =>
+ showNotification(
+
+ Transaction submitted: {renderTransactionId(transactionId)}
+ ,
+ "info",
+ ),
+ transactionSuccessNotification: (transactionId: string) =>
+ showNotification(
+
+ Transaction successful: {renderTransactionId(transactionId)}
+ ,
+ "success",
+ ),
+ };
+};
diff --git a/apps/create-fuels-counter-guide/src/hooks/useRouter.ts b/apps/create-fuels-counter-guide/src/hooks/useRouter.ts
new file mode 100644
index 00000000000..245d854a355
--- /dev/null
+++ b/apps/create-fuels-counter-guide/src/hooks/useRouter.ts
@@ -0,0 +1,35 @@
+import { useEffect, useState } from 'react';
+
+/**
+ * Hook for basic route and view management
+ */
+export const useRouter = () => {
+ const views = ['wallet', 'contract', 'predicate', 'script', 'faucet'];
+ const [view, setView] = useState('wallet');
+ const queryParam = 'v';
+
+ const getViewFromUrl = () => {
+ const url = new URL(window.location.href);
+ return url.searchParams.get(queryParam);
+ };
+
+ const setRoute = (newView: string) => {
+ const url = new URL(window.location.href);
+ url.searchParams.set(queryParam, newView);
+ window.history.pushState({}, '', url);
+ setView(newView);
+ };
+
+ useEffect(() => {
+ const viewFromUrl = getViewFromUrl();
+ if (viewFromUrl) {
+ setView(viewFromUrl);
+ }
+ }, []);
+
+ return {
+ view,
+ views,
+ setRoute,
+ };
+};
diff --git a/apps/create-fuels-counter-guide/src/index.css b/apps/create-fuels-counter-guide/src/index.css
index b5c61c95671..1d92b2d0fe3 100644
--- a/apps/create-fuels-counter-guide/src/index.css
+++ b/apps/create-fuels-counter-guide/src/index.css
@@ -1,3 +1,81 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
+
+:root {
+ --toastify-color-dark: #121212;
+ --toastify-color-info: #3f3f46;
+ --toastify-color-success: rgb(74 222 128);
+ --toastify-color-error: #e74c3c;
+
+ --toastify-icon-color-info: var(--toastify-color-info);
+ --toastify-icon-color-success: rgb(74 222 128);
+ --toastify-icon-color-error: var(--toastify-color-error);
+}
+
+body {
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ @apply m-0 bg-black;
+}
+
+/* Button */
+.btn {
+ @apply rounded-md px-1.5 py-1.5 font-mono text-sm font-medium transition-all duration-200;
+}
+
+.btn-primary {
+ @apply border border-green-400/10 bg-green-900/50 text-green-400 hover:border-green-400/20 hover:bg-green-900/60;
+}
+
+.btn-primary:disabled {
+ @apply cursor-not-allowed border border-zinc-400/25 bg-zinc-950 text-zinc-400;
+}
+
+.btn-inactive {
+ @apply border border-zinc-400/25 bg-zinc-950 text-zinc-400 hover:border-zinc-400/40 hover:bg-zinc-900;
+}
+
+.btn-secondary {
+ @apply border border-indigo-400/10 bg-indigo-950 text-indigo-400 hover:border-indigo-400/25;
+}
+
+/* Grain */
+
+/* zinc-800 #27272a */
+/* zinc-700 #3f3f46 */
+/* zinc-600 #52525b */
+
+.grain {
+ @apply bg-zinc-900;
+ /* Graph Paper */
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Cg fill-rule='evenodd'%3E%3Cg fill='%233f3f46' fill-opacity='0.4'%3E%3Cpath opacity='.5' d='M96 95h4v1h-4v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9zm-1 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9z'/%3E%3Cpath d='M6 5V0H5v5H0v1h5v94h1V6h94V5H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
+}
+
+/* Gradient border */
+.gradient-border {
+ position: relative;
+ background-clip: padding-box;
+ border: solid 1px transparent;
+}
+
+.gradient-border::before {
+ content: "";
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: -1;
+ margin: -1px;
+ border-radius: inherit;
+ background: linear-gradient(
+ to bottom,
+ theme("colors.zinc.600 / 0.8"),
+ theme("colors.zinc.800 / 0.9")
+ );
+}
+
+input {
+ outline: none !important;
+}
diff --git a/apps/create-fuels-counter-guide/src/lib.ts b/apps/create-fuels-counter-guide/src/lib.ts
deleted file mode 100644
index 5425f2a0bc8..00000000000
--- a/apps/create-fuels-counter-guide/src/lib.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { Account, BN } from 'fuels';
-
-// #region deploying-dapp-to-testnet-lib-current-environment
-// The two environments for the dapp are local and testnet.
-export const Environments = {
- LOCAL: 'local',
- TESTNET: 'testnet',
-} as const;
-type Environment = (typeof Environments)[keyof typeof Environments];
-
-/**
- * The current environment is determined by the
- * `VITE_DAPP_ENVIRONMENT` environment variable.
- * If it's not set, the default is `local`.
- */
-export const CURRENT_ENVIRONMENT: Environment =
- (process.env.VITE_DAPP_ENVIRONMENT as Environment) || Environments.LOCAL;
-// #endregion deploying-dapp-to-testnet-lib-current-environment
-
-// The node URL is determined by the current environment too.
-export const NODE_URL =
- CURRENT_ENVIRONMENT === Environments.LOCAL
- ? `http://127.0.0.1:${process.env.VITE_FUEL_NODE_PORT || 4000}/v1/graphql`
- : 'https://testnet.fuel.network/v1/graphql';
-
-export interface AppWallet {
- wallet?: Account;
- walletBalance?: BN;
- refreshWalletBalance?: () => Promise;
-}
-
-export const TESTNET_FAUCET_LINK = 'https://faucet-testnet.fuel.network/';
-
-export const FAUCET_LINK =
- CURRENT_ENVIRONMENT === Environments.LOCAL ? '/faucet' : TESTNET_FAUCET_LINK;
-
-export const FAUCET_PRIVATE_KEY = '0x01';
-
-export const DOCS_URL = 'https://docs.fuel.network';
-
-export const TESTNET_CONTRACT_ID = process.env.VITE_TESTNET_CONTRACT_ID as string;
diff --git a/apps/create-fuels-counter-guide/src/lib.tsx b/apps/create-fuels-counter-guide/src/lib.tsx
new file mode 100644
index 00000000000..cd4fbd76e68
--- /dev/null
+++ b/apps/create-fuels-counter-guide/src/lib.tsx
@@ -0,0 +1,43 @@
+import { BN } from 'fuels'
+import contractIds from './sway-api/contract-ids.json';
+
+// #region deploying-dapp-to-testnet-lib-current-environment
+export const environments = { LOCAL: 'local', TESTNET: 'testnet' };
+export const environment = process.env.VITE_DAPP_ENVIRONMENT || environments.LOCAL;
+export const isLocal = environment === environments.LOCAL;
+export const isTestnet = environment === environments.TESTNET;
+// #endregion deploying-dapp-to-testnet-lib-current-environment
+
+export const localProviderUrl = `http://127.0.0.1:${process.env.VITE_FUEL_NODE_PORT || 4000}/v1/graphql`;
+export const testnetProviderUrl = 'https://testnet.fuel.network/v1/graphql';
+export const providerUrl = isLocal ? localProviderUrl : testnetProviderUrl;
+export const playgroundUrl = providerUrl.replace('v1/graphql', 'v1/playground');
+
+// #region deploying-dapp-to-testnet-frontend-contract-id
+export const localContractId = contractIds.testContract;
+export const testnetContractId = process.env.VITE_TESTNET_CONTRACT_ID as string;
+export const contractId = isLocal ? localContractId : testnetContractId;
+// #endregion deploying-dapp-to-testnet-frontend-contract-id
+
+export const testnetFaucetUrl = 'https://faucet-testnet.fuel.network/';
+
+export const renderTransactionId = (transactionId: string) => {
+ if (isLocal) {
+ return transactionId;
+ }
+
+ return (
+
+ {transactionId}
+
+ )
+}
+
+export const renderFormattedBalance = (balance: BN) => {
+ return balance.format({ precision: 4 });
+}
\ No newline at end of file
diff --git a/apps/create-fuels-counter-guide/src/main.tsx b/apps/create-fuels-counter-guide/src/main.tsx
index 4460d8602bc..4f0e9214b4a 100644
--- a/apps/create-fuels-counter-guide/src/main.tsx
+++ b/apps/create-fuels-counter-guide/src/main.tsx
@@ -1,29 +1,31 @@
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+import { defaultConnectors } from "@fuels/connectors";
+import { FuelProvider } from "@fuels/react";
import { StrictMode } from "react";
-import ReactDOM from "react-dom/client";
-import { RouterProvider, createRouter } from "@tanstack/react-router";
+import { createRoot } from "react-dom/client";
+import { ToastContainer } from "react-toastify";
+import { Provider } from "fuels";
-import "./index.css";
+import App from "./App.tsx";
+import { providerUrl } from "./lib.tsx";
-// Import the generated route tree
-import { routeTree } from "./routeTree.gen";
+import "react-toastify/dist/ReactToastify.css";
+import "./index.css";
-// Create a new router instance
-const router = createRouter({ routeTree });
+const queryClient = new QueryClient();
-// Register the router instance for type safety
-declare module "@tanstack/react-router" {
- interface Register {
- router: typeof router;
- }
-}
+const connectors = defaultConnectors({
+ devMode: true,
+ burnerWalletConfig: { fuelProvider: Provider.create(providerUrl) },
+});
-// Render the app
-const rootElement = document.getElementById("root")!;
-if (!rootElement.innerHTML) {
- const root = ReactDOM.createRoot(rootElement);
- root.render(
-
-
- ,
- );
-}
+createRoot(document.getElementById("root")!).render(
+
+
+
+
+
+
+
+ ,
+);
diff --git a/apps/create-fuels-counter-guide/src/routeTree.gen.ts b/apps/create-fuels-counter-guide/src/routeTree.gen.ts
deleted file mode 100644
index 7ab63a9a40d..00000000000
--- a/apps/create-fuels-counter-guide/src/routeTree.gen.ts
+++ /dev/null
@@ -1,118 +0,0 @@
-/* prettier-ignore-start */
-
-/* eslint-disable */
-
-// @ts-nocheck
-
-// noinspection JSUnusedGlobalSymbols
-
-// This file is auto-generated by TanStack Router
-
-import { createFileRoute } from '@tanstack/react-router'
-
-// Import Routes
-
-import { Route as rootRoute } from './routes/__root'
-
-// Create Virtual Routes
-
-const ScriptLazyImport = createFileRoute('/script')()
-const PredicateLazyImport = createFileRoute('/predicate')()
-const FaucetLazyImport = createFileRoute('/faucet')()
-const IndexLazyImport = createFileRoute('/')()
-
-// Create/Update Routes
-
-const ScriptLazyRoute = ScriptLazyImport.update({
- path: '/script',
- getParentRoute: () => rootRoute,
-} as any).lazy(() => import('./routes/script.lazy').then((d) => d.Route))
-
-const PredicateLazyRoute = PredicateLazyImport.update({
- path: '/predicate',
- getParentRoute: () => rootRoute,
-} as any).lazy(() => import('./routes/predicate.lazy').then((d) => d.Route))
-
-const FaucetLazyRoute = FaucetLazyImport.update({
- path: '/faucet',
- getParentRoute: () => rootRoute,
-} as any).lazy(() => import('./routes/faucet.lazy').then((d) => d.Route))
-
-const IndexLazyRoute = IndexLazyImport.update({
- path: '/',
- getParentRoute: () => rootRoute,
-} as any).lazy(() => import('./routes/index.lazy').then((d) => d.Route))
-
-// Populate the FileRoutesByPath interface
-
-declare module '@tanstack/react-router' {
- interface FileRoutesByPath {
- '/': {
- id: '/'
- path: '/'
- fullPath: '/'
- preLoaderRoute: typeof IndexLazyImport
- parentRoute: typeof rootRoute
- }
- '/faucet': {
- id: '/faucet'
- path: '/faucet'
- fullPath: '/faucet'
- preLoaderRoute: typeof FaucetLazyImport
- parentRoute: typeof rootRoute
- }
- '/predicate': {
- id: '/predicate'
- path: '/predicate'
- fullPath: '/predicate'
- preLoaderRoute: typeof PredicateLazyImport
- parentRoute: typeof rootRoute
- }
- '/script': {
- id: '/script'
- path: '/script'
- fullPath: '/script'
- preLoaderRoute: typeof ScriptLazyImport
- parentRoute: typeof rootRoute
- }
- }
-}
-
-// Create and export the route tree
-
-export const routeTree = rootRoute.addChildren({
- IndexLazyRoute,
- FaucetLazyRoute,
- PredicateLazyRoute,
- ScriptLazyRoute,
-})
-
-/* prettier-ignore-end */
-
-/* ROUTE_MANIFEST_START
-{
- "routes": {
- "__root__": {
- "filePath": "__root.tsx",
- "children": [
- "/",
- "/faucet",
- "/predicate",
- "/script"
- ]
- },
- "/": {
- "filePath": "index.lazy.tsx"
- },
- "/faucet": {
- "filePath": "faucet.lazy.tsx"
- },
- "/predicate": {
- "filePath": "predicate.lazy.tsx"
- },
- "/script": {
- "filePath": "script.lazy.tsx"
- }
- }
-}
-ROUTE_MANIFEST_END */
diff --git a/apps/create-fuels-counter-guide/src/routes/__root.tsx b/apps/create-fuels-counter-guide/src/routes/__root.tsx
deleted file mode 100644
index da59c371212..00000000000
--- a/apps/create-fuels-counter-guide/src/routes/__root.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-/* eslint-disable react-hooks/rules-of-hooks */
-import { createRootRoute, Outlet } from "@tanstack/react-router";
-import { Layout } from "../components/Layout";
-import { useEffect, useMemo, useState } from "react";
-import { Provider } from "fuels";
-import { NODE_URL } from "../lib";
-import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
-import { FuelProvider } from "@fuels/react";
-import {
- BakoSafeConnector,
- BurnerWalletConnector,
- FuelWalletConnector,
- FuelWalletDevelopmentConnector,
- FueletWalletConnector,
- WalletConnectConnector,
-} from "@fuels/connectors";
-import { ActiveWalletProvider } from "../hooks/useActiveWallet";
-
-/**
- * react-query is a peer dependency of @fuels/react, so we set it up here.
- * See https://docs.fuel.network/docs/wallet/dev/getting-started/#installation-1
- */
-const queryClient = new QueryClient();
-
-export const Route = createRootRoute({
- component: () => {
- const [isMounted, setIsMounted] = useState(false);
-
- /**
- * Create a Provider instance.
- * We memoize it to avoid creating a new instance on every render.
- */
- const providerToUse = useMemo(() => Provider.create(NODE_URL), [NODE_URL]);
-
- useEffect(() => {
- setIsMounted(true);
- }, []);
-
- // Only render the component if the page has been mounted.
- if (!isMounted) return null;
-
- return (
- <>
-
-
-
-
-
-
-
-
-
- >
- );
- },
-});
diff --git a/apps/create-fuels-counter-guide/src/routes/faucet.lazy.tsx b/apps/create-fuels-counter-guide/src/routes/faucet.lazy.tsx
deleted file mode 100644
index ccf7aa928b0..00000000000
--- a/apps/create-fuels-counter-guide/src/routes/faucet.lazy.tsx
+++ /dev/null
@@ -1,88 +0,0 @@
-import { createLazyFileRoute } from "@tanstack/react-router";
-import { Button } from "../components/Button";
-import { Input } from "../components/Input";
-import { useActiveWallet } from "../hooks/useActiveWallet";
-import { useFaucet } from "../hooks/useFaucet";
-import { bn } from "fuels";
-import { useEffect, useState } from "react";
-import toast from "react-hot-toast";
-
-export const Route = createLazyFileRoute("/faucet")({
- component: Index,
-});
-
-function Index() {
- // Get the faucet wallet instance from the useFaucet hook
- const { faucetWallet } = useFaucet();
-
- const { wallet, refreshWalletBalance } = useActiveWallet();
-
- const [receiverAddress, setReceiverAddress] = useState("");
- const [amountToSend, setAmountToSend] = useState("5");
-
- useEffect(() => {
- if (wallet) {
- setReceiverAddress(wallet.address.toB256());
- }
- }, [wallet]);
-
- const sendFunds = async () => {
- if (!faucetWallet) {
- return toast.error("Faucet wallet not found.");
- }
-
- if (!receiverAddress) {
- return toast.error("Receiver address not set");
- }
-
- if (!amountToSend) {
- return toast.error("Amount cannot be empty");
- }
-
- // Transfer the specified amount of ETH to the receiver address
- const tx = await faucetWallet.transfer(
- receiverAddress,
- bn.parseUnits(amountToSend.toString()),
- );
- await tx.waitForResult();
-
- toast.success("Funds sent!");
-
- await refreshWalletBalance?.();
- };
-
- return (
- <>
- Local Faucet
-
-
-
- Receiving address:
-
- setReceiverAddress(e.target.value)}
- placeholder="0x..."
- id="receiver-address-input"
- />
-
-
-
-
- Amount (ETH):
-
- setAmountToSend(e.target.value)}
- placeholder="5"
- type="number"
- id="amount-input"
- />
-
-
- Send Funds
- >
- );
-}
diff --git a/apps/create-fuels-counter-guide/src/routes/index.lazy.tsx b/apps/create-fuels-counter-guide/src/routes/index.lazy.tsx
deleted file mode 100644
index 647eeb842b6..00000000000
--- a/apps/create-fuels-counter-guide/src/routes/index.lazy.tsx
+++ /dev/null
@@ -1,156 +0,0 @@
-import { createLazyFileRoute } from "@tanstack/react-router";
-import { TestContract } from "../sway-api";
-import contractIds from "../sway-api/contract-ids.json";
-import { FuelLogo } from "../components/FuelLogo";
-import { bn } from "fuels";
-import { useState } from "react";
-import { Link } from "../components/Link";
-import { Button } from "../components/Button";
-import toast from "react-hot-toast";
-import { useActiveWallet } from "../hooks/useActiveWallet";
-import useAsync from "react-use/lib/useAsync";
-import {
- CURRENT_ENVIRONMENT,
- DOCS_URL,
- Environments,
- FAUCET_LINK,
- TESTNET_CONTRACT_ID,
-} from "../lib";
-
-export const Route = createLazyFileRoute("/")({
- component: Index,
-});
-
-// #region deploying-dapp-to-testnet-frontend-contract-id
-const contractId =
- CURRENT_ENVIRONMENT === Environments.LOCAL
- ? contractIds.testContract
- : TESTNET_CONTRACT_ID; // Testnet Contract ID
-// #endregion deploying-dapp-to-testnet-frontend-contract-id
-
-function Index() {
- const { wallet, walletBalance, refreshWalletBalance } = useActiveWallet();
- const [contract, setContract] = useState();
- const [counter, setCounter] = useState();
-
- /**
- * useAsync is a wrapper around useEffect that allows us to run asynchronous code
- * See: https://github.com/streamich/react-use/blob/master/docs/useAsync.md
- */
- useAsync(async () => {
- if (wallet) {
- // Create a new instance of the contract
- const testContract = new TestContract(contractId, wallet);
- setContract(testContract);
-
- // Read the current value of the counter
- const { value } = await testContract.functions.get_count().get();
- setCounter(value.toNumber());
- }
- }, [wallet]);
-
- const onIncrementPressed = async () => {
- if (!contract) {
- return toast.error("Contract not loaded");
- }
-
- if (walletBalance?.eq(0)) {
- return toast.error(
-
- Your wallet does not have enough funds. Please top it up using the{" "}
-
- faucet.
-
- ,
- );
- }
-
- // Call the increment_counter function on the contract
- const { waitForResult } = await contract.functions
- .increment_counter(bn(1))
- .call();
-
- // Wait for the transaction to be mined, and then read the value returned
- const { value } = await waitForResult();
-
- setCounter(value.toNumber());
-
- await refreshWalletBalance?.();
- };
-
- // #region create-fuels-counter-guide-on-decrement-react-function
- const onDecrementPressed = async () => {
- if (!contract) {
- return toast.error("Contract not loaded");
- }
-
- if (walletBalance?.eq(0)) {
- return toast.error(
-
- Your wallet does not have enough funds. Please top it up using the{" "}
- faucet.
- ,
- );
- }
-
- const { waitForResult } = await contract.functions.decrement_counter(bn(1)).call();
- const { value } = await waitForResult();
-
- setCounter(value.toNumber());
-
- await refreshWalletBalance?.();
- };
- // #endregion create-fuels-counter-guide-on-decrement-react-function
-
-
- return (
- <>
-
-
-
Welcome to Fuel
-
-
-
- Get started by editing sway-programs/contract/main.sw or{" "}
- src/pages/index.tsx .
-
-
-
- This template uses the new{" "}
-
- Fuels CLI
- {" "}
- to enable type-safe hot-reloading for your Sway programs.
-
-
- <>
- Counter
-
-
- {counter}
-
-
-
- Increment Counter
-
-
- {/* #region create-fuels-counter-guide-on-decrement-ui */}
-
- Decrement Counter
-
- {/* #endregion create-fuels-counter-guide-on-decrement-ui */}
- >
-
-
- Predicate Example
-
-
-
- Script Example
-
-
- Fuel Docs
-
- >
- );
-}
diff --git a/apps/create-fuels-counter-guide/src/routes/predicate.lazy.tsx b/apps/create-fuels-counter-guide/src/routes/predicate.lazy.tsx
deleted file mode 100644
index 8a73257779e..00000000000
--- a/apps/create-fuels-counter-guide/src/routes/predicate.lazy.tsx
+++ /dev/null
@@ -1,246 +0,0 @@
-import { createLazyFileRoute } from "@tanstack/react-router";
-import { Button } from "../components/Button";
-import { FuelLogo } from "../components/FuelLogo";
-import { Input } from "../components/Input";
-import { Link } from "../components/Link";
-import { useActiveWallet } from "../hooks/useActiveWallet";
-import { TestPredicate } from "../sway-api/predicates/index";
-import { FAUCET_LINK } from "../lib";
-import { BN, InputValue, Predicate } from "fuels";
-import { bn } from "fuels";
-import { useState } from "react";
-import toast from "react-hot-toast";
-import useAsync from "react-use/lib/useAsync";
-
-export const Route = createLazyFileRoute("/predicate")({
- component: Index,
-});
-
-function Index() {
- let baseAssetId: string;
-
- const { wallet, walletBalance, refreshWalletBalance } = useActiveWallet();
-
- const [predicate, setPredicate] = useState>();
-
- const [predicateBalance, setPredicateBalance] = useState();
-
- const [pin, setPin] = useState();
-
- useAsync(async () => {
- if (wallet) {
- baseAssetId = wallet.provider.getBaseAssetId();
- // Initialize a new predicate instance
- const predicate = new TestPredicate({
- provider: wallet.provider,
- });
- setPredicate(predicate);
- setPredicateBalance(await predicate.getBalance());
- }
- }, [wallet]);
-
- const refreshBalances = async () => {
- await refreshWalletBalance?.();
- setPredicateBalance(await predicate?.getBalance());
- };
-
- const transferFundsToPredicate = async (amount: BN) => {
- try {
- if (!predicate) {
- return toast.error("Predicate not loaded");
- }
-
- if (!wallet) {
- return toast.error("Wallet not loaded");
- }
-
- await wallet.transfer(predicate.address, amount, baseAssetId, {
- gasLimit: 10_000,
- });
-
- await refreshBalances();
-
- return toast.success("Funds transferred to predicate.");
- } catch (e) {
- console.error(e);
- toast.error(
-
- Failed to transfer funds. Please make sure your wallet has enough
- funds. You can top it up using the{" "}
-
- faucet.
-
- ,
- );
- }
- };
-
- const unlockPredicateAndTransferFundsBack = async (amount: BN) => {
- try {
- if (!wallet) {
- return toast.error("Wallet not loaded");
- }
-
- // Initialize a new predicate instance with the entered pin
- const reInitializePredicate = new TestPredicate({
- provider: wallet.provider,
- data: [bn(pin)],
- });
-
- if (!reInitializePredicate) {
- return toast.error("Failed to initialize predicate");
- }
-
- /*
- Try to 'unlock' the predicate and transfer the funds back to the wallet.
- If the pin is correct, this transfer transaction will succeed.
- If the pin is incorrect, this transaction will fail.
- */
- const tx = await reInitializePredicate.transfer(
- wallet.address,
- amount,
- baseAssetId,
- );
- const { isStatusSuccess } = await tx.wait();
-
- if (!isStatusSuccess) {
- toast.error("Failed to unlock predicate");
- }
-
- if (isStatusSuccess) {
- toast.success("Predicate unlocked");
- }
-
- await refreshBalances();
- } catch (e) {
- console.error(e);
- toast.error(
- "Failed to unlock predicate. You probably entered the wrong pin, or the predicate does not have enough balance. Try again.",
- );
- }
- };
-
- // #region change-pin-react-function
- const changePin = async () => {
- if (!wallet) {
- return toast.error("Wallet not loaded");
- }
- if (!predicate) {
- return toast.error("Predicate not loaded");
- }
-
- if (walletBalance?.eq(0)) {
- return toast.error(
- Your wallet does not have enough funds. Please top it up using the faucet.
- );
- }
-
- if (!pin) {
- return toast.error("Please enter a pin");
- }
-
- const configurableConstants = { PIN: bn(pin) };
- // instantiate predicate with configurable constants
- const reInitializePredicate = new TestPredicate({
- provider: wallet.provider,
- data: [configurableConstants.PIN],
- configurableConstants,
- });
-
- if (!reInitializePredicate) {
- return toast.error("Failed to initialize predicate");
- }
-
- // transferring funds to the predicate
- const tx = await wallet.transfer(reInitializePredicate.address, 1000, baseAssetId, {
- gasLimit: 10_000,
- });
-
- const { isStatusSuccess } = await tx.wait();
-
- if (!isStatusSuccess) {
- toast.error("Failed to update pin in predicate");
- return;
- }
-
- if (isStatusSuccess) {
- toast.success("Predicate pin updated");
- }
-
- await refreshWalletBalance?.();
- };
- // #endregion change-pin-react-function
-
-
- return (
- <>
-
-
-
Predicate
-
-
-
-
Wallet Balance:
-
- {walletBalance?.format({
- precision: 3,
- })}{" "}
- ETH
-
-
-
-
-
Predicate Balance:
-
- {predicateBalance?.format({
- precision: 3,
- })}{" "}
- ETH
-
-
-
-
- await transferFundsToPredicate(bn.parseUnits("0.1"))
- }
- >
- Transfer 0.1 ETH to Predicate
-
-
- Change Pin
-
- setPin(e.target.value)}
- placeholder="Enter a new pin"
- />
-
-
- await unlockPredicateAndTransferFundsBack(bn.parseUnits("0.09"))
- }
- >
- Unlock Predicate and Transfer 0.09 ETH back to Wallet
-
-
-
- Do note that when you 'unlock' a predicate, the predicate also pays for
- the gas of the transaction.
- This is why you will notice that the balance of the predicate gets
- reduced by 0.09 ETH + a nominal gas fee.
-
-
-
- Learn more about Predicates
-
-
-
- Back to Home
-
- >
- );
-}
diff --git a/apps/create-fuels-counter-guide/src/routes/script.lazy.tsx b/apps/create-fuels-counter-guide/src/routes/script.lazy.tsx
deleted file mode 100644
index 204d9006a3c..00000000000
--- a/apps/create-fuels-counter-guide/src/routes/script.lazy.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-import { createLazyFileRoute } from "@tanstack/react-router";
-import { Button } from "../components/Button";
-import { FuelLogo } from "../components/FuelLogo";
-import { Input } from "../components/Input";
-import { Link } from "../components/Link";
-import { useActiveWallet } from "../hooks/useActiveWallet";
-import { TestScript } from "../sway-api";
-import { FAUCET_LINK } from "../lib";
-import { BN, BigNumberish, Script, bn } from "fuels";
-import { useState } from "react";
-import toast from "react-hot-toast";
-import useAsync from "react-use/lib/useAsync";
-
-export const Route = createLazyFileRoute("/script")({
- component: Index,
-});
-
-function Index() {
- const { wallet } = useActiveWallet();
-
- const [script, setScript] = useState
-