@@ -52,33 +68,49 @@ export const Balance = ({ address, className = "", usdMode }: BalanceProps) => {
);
}
- //const formattedBalance = balance ? Number(balance.formatted) : 0;
+ // Calculate the total balance in USD
+ const ethBalanceInUsd = parseFloat(formatted) * price;
+ const strkBalanceInUsd = parseFloat(strkFormatted) * strkPrice;
+ const totalBalanceInUsd = ethBalanceInUsd + strkBalanceInUsd;
return (
-
+ <>
+
+ >
);
};
diff --git a/packages/nextjs/components/scaffold-stark/BlockExplorer.tsx b/packages/nextjs/components/scaffold-stark/BlockExplorer.tsx
new file mode 100644
index 000000000..68ca23c88
--- /dev/null
+++ b/packages/nextjs/components/scaffold-stark/BlockExplorer.tsx
@@ -0,0 +1,86 @@
+"use client";
+
+import { Address as AddressType, mainnet } from "@starknet-react/chains";
+import { MagnifyingGlassIcon } from "@heroicons/react/24/outline";
+import { useNetwork } from "@starknet-react/core";
+import Image from "next/image";
+
+export const BlockExplorer = () => {
+ const { chain: ConnectedChain } = useNetwork();
+
+ const blockExplorers = [
+ {
+ name: "Starkscan",
+ img: "/sn-symbol-gradient.png",
+ link: "https://starkscan.co/",
+ },
+ {
+ name: "Voyager",
+ img: "/voyager-icon.svg",
+ link: "https://voyager.online/",
+ },
+ {
+ name: "Stark Compass",
+ img: "/starkcompass-icon.svg",
+ link: "https://starkcompass.com/",
+ },
+ ];
+
+ // Render only on mainnet chain
+ if (ConnectedChain?.id !== mainnet.id) {
+ return null;
+ }
+
+ return (
+
+ );
+};
diff --git a/packages/nextjs/components/scaffold-stark/BlockExplorerSepolia.tsx b/packages/nextjs/components/scaffold-stark/BlockExplorerSepolia.tsx
new file mode 100644
index 000000000..bad4d1019
--- /dev/null
+++ b/packages/nextjs/components/scaffold-stark/BlockExplorerSepolia.tsx
@@ -0,0 +1,89 @@
+"use client";
+
+import { Address as AddressType, sepolia } from "@starknet-react/chains";
+import { MagnifyingGlassIcon } from "@heroicons/react/24/outline";
+import { useNetwork } from "@starknet-react/core";
+import Image from "next/image";
+
+export const BlockExplorerSepolia = () => {
+ const { chain: ConnectedChain } = useNetwork();
+
+ const sepoliaBlockExplorers = [
+ {
+ name: "Starkscan",
+ img: "/sn-symbol-gradient.png",
+ link: "https://sepolia.starkscan.co/",
+ },
+ {
+ name: "Voyager",
+ img: "/voyager-icon.svg",
+ link: "https://sepolia.voyager.online/",
+ },
+ {
+ name: "Stark Compass",
+ img: "/starkcompass-icon.svg",
+ link: "https://starkcompass.com/sepolia/",
+ },
+ ];
+
+ // Render only on sepolia chain
+ if (ConnectedChain?.id !== sepolia.id) {
+ return null;
+ }
+
+ return (
+
+ );
+};
diff --git a/packages/nextjs/components/scaffold-stark/ClassHash.tsx b/packages/nextjs/components/scaffold-stark/ClassHash.tsx
new file mode 100644
index 000000000..32b3e501d
--- /dev/null
+++ b/packages/nextjs/components/scaffold-stark/ClassHash.tsx
@@ -0,0 +1,85 @@
+"use client";
+
+import { useState } from "react";
+import Link from "next/link";
+import { CopyToClipboard } from "react-copy-to-clipboard";
+import { Address as AddressType } from "@starknet-react/chains";
+import { devnet } from "@starknet-react/chains";
+import {
+ CheckCircleIcon,
+ DocumentDuplicateIcon,
+} from "@heroicons/react/24/outline";
+import { useTargetNetwork } from "~~/hooks/scaffold-stark/useTargetNetwork";
+import { getBlockExplorerClasshashLink } from "~~/utils/scaffold-stark";
+
+type ClasshashProps = {
+ classHash: AddressType;
+ format?: "short" | "long";
+ size?: "xs" | "sm" | "base" | "lg" | "xl" | "2xl" | "3xl";
+};
+
+/**
+ * Displays a Classhash and option to copy classHash.
+ */
+export const ClassHash = ({
+ classHash,
+ format,
+ size = "xs",
+}: ClasshashProps) => {
+ const [addressCopied, setAddressCopied] = useState(false);
+ const { targetNetwork } = useTargetNetwork();
+
+ const blockExplorerAddressLink = getBlockExplorerClasshashLink(
+ targetNetwork,
+ classHash,
+ );
+
+ let displayClasshash = classHash?.slice(0, 6) + "..." + classHash?.slice(-4);
+
+ if (format === "long") {
+ displayClasshash = classHash;
+ }
+
+ return (
+
+
+ class hash:
+
+ {targetNetwork.network === devnet.network ? (
+
+ {displayClasshash}
+
+ ) : (
+
+ {displayClasshash}
+
+ )}
+ {addressCopied ? (
+
+ ) : (
+
{
+ setAddressCopied(true);
+ setTimeout(() => {
+ setAddressCopied(false);
+ }, 800);
+ }}
+ >
+
+
+ )}
+
+ );
+};
diff --git a/packages/nextjs/components/scaffold-stark/CustomConnectButton/AddressInfoDropdown.tsx b/packages/nextjs/components/scaffold-stark/CustomConnectButton/AddressInfoDropdown.tsx
index ca122245c..7cf496903 100644
--- a/packages/nextjs/components/scaffold-stark/CustomConnectButton/AddressInfoDropdown.tsx
+++ b/packages/nextjs/components/scaffold-stark/CustomConnectButton/AddressInfoDropdown.tsx
@@ -188,7 +188,7 @@ export const AddressInfoDropdown = ({
<>
-
+
diff --git a/packages/nextjs/components/scaffold-stark/Faucet.tsx b/packages/nextjs/components/scaffold-stark/Faucet.tsx
index 05a3ff484..0abf9060b 100644
--- a/packages/nextjs/components/scaffold-stark/Faucet.tsx
+++ b/packages/nextjs/components/scaffold-stark/Faucet.tsx
@@ -41,8 +41,7 @@ export const Faucet = () => {
useEffect(() => {
const checkChain = async () => {
try {
- const providerInfo = await publicClient.getBlockWithTxHashes();
- console.log(providerInfo);
+ const providerInfo = await publicClient.getBlock();
} catch (error) {
console.error("⚡️ ~ file: Faucet.tsx:checkChain ~ error", error);
notification.error(
@@ -83,16 +82,16 @@ export const Faucet = () => {
return;
}
- try {
- setLoading(true);
- await mintEth(inputAddress, sendValue);
- setLoading(false);
- setInputAddress(undefined);
- setSendValue("");
- } catch (error) {
- console.error("⚡️ ~ file: Faucet.tsx:sendETH ~ error", error);
+ const res = await mintEth(inputAddress, sendValue);
+ if (!res.new_balance) {
setLoading(false);
+ notification.error(`${res}`);
+ return;
}
+ setLoading(false);
+ setInputAddress(undefined);
+ setSendValue("");
+ notification.success("ETH sent successfully!");
};
// Render only on local chain
@@ -121,17 +120,7 @@ export const Faucet = () => {
>
✕
-
-
+
{
+ const { chain: ConnectedChain } = useNetwork();
+ const { targetNetwork } = useTargetNetwork();
+
+ const sepoliaFaucets = [
+ {
+ name: "Starknet Foundation",
+ img: "/sn-symbol-gradient.png",
+ link: "https://starknet-faucet.vercel.app/",
+ },
+ {
+ name: "Alchemy",
+ img: "/logo_alchemy.png",
+ link: "https://www.alchemy.com/faucets/starknet-sepolia",
+ },
+ {
+ name: "Blast",
+ img: "/blast-icon-color.svg",
+ link: "https://blastapi.io/faucets/starknet-sepolia-eth",
+ },
+ ];
+
+ const publicNodeUrl = targetNetwork.rpcUrls.public.http[0];
+
+ // Use useMemo to memoize the publicClient object
+ const publicClient = useMemo(() => {
+ return new RpcProvider({
+ nodeUrl: publicNodeUrl,
+ });
+ }, [publicNodeUrl]);
+
+ useEffect(() => {
+ const checkChain = async () => {
+ try {
+ const providerInfo = await publicClient.getBlock();
+ } catch (error) {
+ console.error("⚡️ ~ file: Faucet.tsx:checkChain ~ error", error);
+ notification.error(
+ <>
+
+ Cannot connect to local provider
+
+
+ - Did you forget to run{" "}
+
+ yarn chain
+
{" "}
+ ?
+
+
+ - Or you can change{" "}
+
+ targetNetwork
+
{" "}
+ in{" "}
+
+ scaffold.config.ts
+
+
+ >,
+ {
+ duration: 5000,
+ },
+ );
+ }
+ };
+ checkChain().then();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ // Render only on sepolia chain
+ if (ConnectedChain?.id !== sepolia.id) {
+ return null;
+ }
+
+ return (
+
+ );
+};
diff --git a/packages/nextjs/contracts/predeployedContracts.ts b/packages/nextjs/contracts/predeployedContracts.ts
index dd925ba51..4c3b211e3 100644
--- a/packages/nextjs/contracts/predeployedContracts.ts
+++ b/packages/nextjs/contracts/predeployedContracts.ts
@@ -5,6 +5,9 @@
const universalEthAddress =
"0x49D36570D4E46F48E99674BD3FCC84644DDD6B96F7C741B1562B82F9E004DC7";
+const universalStrkAddress =
+ "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d";
+
const preDeployedContracts = {
devnet: {
Eth: {
@@ -212,6 +215,216 @@ const preDeployedContracts = {
],
},
],
+ classHash:
+ "0x046ded64ae2dead6448e247234bab192a9c483644395b66f2155f2614e5804b0",
+ },
+ Strk: {
+ address: universalStrkAddress,
+ abi: [
+ {
+ type: "impl",
+ name: "ERC20Impl",
+ interface_name: "openzeppelin::token::erc20::interface::IERC20",
+ },
+ {
+ name: "openzeppelin::token::erc20::interface::IERC20",
+ type: "interface",
+ items: [
+ {
+ name: "name",
+ type: "function",
+ inputs: [],
+ outputs: [
+ {
+ type: "core::felt252",
+ },
+ ],
+ state_mutability: "view",
+ },
+ {
+ name: "symbol",
+ type: "function",
+ inputs: [],
+ outputs: [
+ {
+ type: "core::felt252",
+ },
+ ],
+ state_mutability: "view",
+ },
+ {
+ name: "decimals",
+ type: "function",
+ inputs: [],
+ outputs: [
+ {
+ type: "core::integer::u8",
+ },
+ ],
+ state_mutability: "view",
+ },
+ {
+ name: "allowance",
+ type: "function",
+ inputs: [
+ {
+ name: "owner",
+ type: "core::starknet::contract_address::ContractAddress",
+ },
+ {
+ name: "spender",
+ type: "core::starknet::contract_address::ContractAddress",
+ },
+ ],
+ outputs: [
+ {
+ type: "core::integer::u256",
+ },
+ ],
+ state_mutability: "view",
+ },
+ {
+ name: "transfer",
+ type: "function",
+ inputs: [
+ {
+ name: "recipient",
+ type: "core::starknet::contract_address::ContractAddress",
+ },
+ {
+ name: "amount",
+ type: "core::integer::u256",
+ },
+ ],
+ outputs: [
+ {
+ type: "core::bool",
+ },
+ ],
+ state_mutability: "external",
+ },
+ {
+ name: "approve",
+ type: "function",
+ inputs: [
+ {
+ name: "spender",
+ type: "core::starknet::contract_address::ContractAddress",
+ },
+ {
+ name: "amount",
+ type: "core::integer::u256",
+ },
+ ],
+ outputs: [
+ {
+ type: "core::bool",
+ },
+ ],
+ state_mutability: "external",
+ },
+ ],
+ },
+ {
+ name: "ERC20CamelOnlyImpl",
+ type: "impl",
+ interface_name:
+ "openzeppelin::token::erc20::interface::IERC20CamelOnly",
+ },
+ {
+ type: "interface",
+ name: "openzeppelin::token::erc20::interface::IERC20CamelOnly",
+ items: [
+ {
+ name: "totalSupply",
+ type: "function",
+ inputs: [],
+ outputs: [
+ {
+ type: "core::integer::u256",
+ },
+ ],
+ state_mutability: "view",
+ },
+ {
+ name: "balanceOf",
+ type: "function",
+ inputs: [
+ {
+ name: "account",
+ type: "core::starknet::contract_address::ContractAddress",
+ },
+ ],
+ outputs: [
+ {
+ type: "core::integer::u256",
+ },
+ ],
+ state_mutability: "view",
+ },
+ {
+ name: "transferFrom",
+ type: "function",
+ inputs: [
+ {
+ name: "sender",
+ type: "core::starknet::contract_address::ContractAddress",
+ },
+ {
+ name: "recipient",
+ type: "core::starknet::contract_address::ContractAddress",
+ },
+ {
+ name: "amount",
+ type: "core::integer::u256",
+ },
+ ],
+ outputs: [
+ {
+ type: "core::bool",
+ },
+ ],
+ state_mutability: "external",
+ },
+ ],
+ },
+ {
+ kind: "struct",
+ name: "openzeppelin::token::erc20_v070::erc20::ERC20::Transfer",
+ type: "event",
+ members: [
+ {
+ kind: "data",
+ name: "from",
+ type: "core::starknet::contract_address::ContractAddress",
+ },
+ {
+ kind: "data",
+ name: "to",
+ type: "core::starknet::contract_address::ContractAddress",
+ },
+ {
+ kind: "data",
+ name: "value",
+ type: "core::integer::u256",
+ },
+ ],
+ },
+ {
+ kind: "enum",
+ name: "openzeppelin::token::erc20_v070::erc20::ERC20::Event",
+ type: "event",
+ variants: [
+ {
+ kind: "nested",
+ name: "Transfer",
+ type: "openzeppelin::token::erc20_v070::erc20::ERC20::Transfer",
+ },
+ ],
+ },
+ ],
+ classHash:
+ "0x046ded64ae2dead6448e247234bab192a9c483644395b66f2155f2614e5804b0",
},
},
sepolia: {
@@ -471,6 +684,267 @@ const preDeployedContracts = {
],
},
],
+ classHash:
+ "0x07f3777c99f3700505ea966676aac4a0d692c2a9f5e667f4c606b51ca1dd3420",
+ },
+ Strk: {
+ address: universalStrkAddress,
+ abi: [
+ {
+ type: "impl",
+ name: "ERC20Impl",
+ interface_name: "openzeppelin::token::erc20::interface::IERC20",
+ },
+ {
+ name: "openzeppelin::token::erc20::interface::IERC20",
+ type: "interface",
+ items: [
+ {
+ name: "name",
+ type: "function",
+ inputs: [],
+ outputs: [
+ {
+ type: "core::felt252",
+ },
+ ],
+ state_mutability: "view",
+ },
+ {
+ name: "symbol",
+ type: "function",
+ inputs: [],
+ outputs: [
+ {
+ type: "core::felt252",
+ },
+ ],
+ state_mutability: "view",
+ },
+ {
+ name: "decimals",
+ type: "function",
+ inputs: [],
+ outputs: [
+ {
+ type: "core::integer::u8",
+ },
+ ],
+ state_mutability: "view",
+ },
+ {
+ name: "total_supply",
+ type: "function",
+ inputs: [],
+ outputs: [
+ {
+ type: "core::integer::u256",
+ },
+ ],
+ state_mutability: "view",
+ },
+ {
+ name: "balance_of",
+ type: "function",
+ inputs: [
+ {
+ name: "account",
+ type: "core::starknet::contract_address::ContractAddress",
+ },
+ ],
+ outputs: [
+ {
+ type: "core::integer::u256",
+ },
+ ],
+ state_mutability: "view",
+ },
+ {
+ name: "allowance",
+ type: "function",
+ inputs: [
+ {
+ name: "owner",
+ type: "core::starknet::contract_address::ContractAddress",
+ },
+ {
+ name: "spender",
+ type: "core::starknet::contract_address::ContractAddress",
+ },
+ ],
+ outputs: [
+ {
+ type: "core::integer::u256",
+ },
+ ],
+ state_mutability: "view",
+ },
+ {
+ name: "transfer",
+ type: "function",
+ inputs: [
+ {
+ name: "recipient",
+ type: "core::starknet::contract_address::ContractAddress",
+ },
+ {
+ name: "amount",
+ type: "core::integer::u256",
+ },
+ ],
+ outputs: [
+ {
+ type: "core::bool",
+ },
+ ],
+ state_mutability: "external",
+ },
+ {
+ name: "transfer_from",
+ type: "function",
+ inputs: [
+ {
+ name: "sender",
+ type: "core::starknet::contract_address::ContractAddress",
+ },
+ {
+ name: "recipient",
+ type: "core::starknet::contract_address::ContractAddress",
+ },
+ {
+ name: "amount",
+ type: "core::integer::u256",
+ },
+ ],
+ outputs: [
+ {
+ type: "core::bool",
+ },
+ ],
+ state_mutability: "external",
+ },
+ {
+ name: "approve",
+ type: "function",
+ inputs: [
+ {
+ name: "spender",
+ type: "core::starknet::contract_address::ContractAddress",
+ },
+ {
+ name: "amount",
+ type: "core::integer::u256",
+ },
+ ],
+ outputs: [
+ {
+ type: "core::bool",
+ },
+ ],
+ state_mutability: "external",
+ },
+ ],
+ },
+ {
+ name: "ERC20CamelOnlyImpl",
+ type: "impl",
+ interface_name:
+ "openzeppelin::token::erc20::interface::IERC20CamelOnly",
+ },
+ {
+ type: "interface",
+ name: "openzeppelin::token::erc20::interface::IERC20CamelOnly",
+ items: [
+ {
+ name: "totalSupply",
+ type: "function",
+ inputs: [],
+ outputs: [
+ {
+ type: "core::integer::u256",
+ },
+ ],
+ state_mutability: "view",
+ },
+ {
+ name: "balanceOf",
+ type: "function",
+ inputs: [
+ {
+ name: "account",
+ type: "core::starknet::contract_address::ContractAddress",
+ },
+ ],
+ outputs: [
+ {
+ type: "core::integer::u256",
+ },
+ ],
+ state_mutability: "view",
+ },
+ {
+ name: "transferFrom",
+ type: "function",
+ inputs: [
+ {
+ name: "sender",
+ type: "core::starknet::contract_address::ContractAddress",
+ },
+ {
+ name: "recipient",
+ type: "core::starknet::contract_address::ContractAddress",
+ },
+ {
+ name: "amount",
+ type: "core::integer::u256",
+ },
+ ],
+ outputs: [
+ {
+ type: "core::bool",
+ },
+ ],
+ state_mutability: "external",
+ },
+ ],
+ },
+ {
+ kind: "struct",
+ name: "openzeppelin::token::erc20_v070::erc20::ERC20::Transfer",
+ type: "event",
+ members: [
+ {
+ kind: "data",
+ name: "from",
+ type: "core::starknet::contract_address::ContractAddress",
+ },
+ {
+ kind: "data",
+ name: "to",
+ type: "core::starknet::contract_address::ContractAddress",
+ },
+ {
+ kind: "data",
+ name: "value",
+ type: "core::integer::u256",
+ },
+ ],
+ },
+ {
+ kind: "enum",
+ name: "openzeppelin::token::erc20_v070::erc20::ERC20::Event",
+ type: "event",
+ variants: [
+ {
+ kind: "nested",
+ name: "Transfer",
+ type: "openzeppelin::token::erc20_v070::erc20::ERC20::Transfer",
+ },
+ ],
+ },
+ ],
+ classHash:
+ "0x04ad3c1dc8413453db314497945b6903e1c766495a1e60492d44da9c2a986e4b",
},
},
} as const;
diff --git a/packages/nextjs/hooks/scaffold-stark/useNativeCurrencyPrice.ts b/packages/nextjs/hooks/scaffold-stark/useNativeCurrencyPrice.ts
index 1562dff9c..4a9fd14f3 100644
--- a/packages/nextjs/hooks/scaffold-stark/useNativeCurrencyPrice.ts
+++ b/packages/nextjs/hooks/scaffold-stark/useNativeCurrencyPrice.ts
@@ -13,23 +13,33 @@ export const useNativeCurrencyPrice = () => {
const nativeCurrencyPrice = useGlobalState(
(state) => state.nativeCurrencyPrice,
);
+ const strkCurrencyPrice = useGlobalState((state) => state.strkCurrencyPrice);
const setNativeCurrencyPrice = useGlobalState(
(state) => state.setNativeCurrencyPrice,
);
- // Get the price of ETH from Coingecko on mount
+ const setStrkCurrencyPrice = useGlobalState(
+ (state) => state.setStrkCurrencyPrice,
+ );
+ // Get the price of ETH & STRK from Coingecko on mount
useEffect(() => {
(async () => {
if (nativeCurrencyPrice == 0) {
- const price = await fetchPriceFromCoingecko(targetNetwork);
+ const price = await fetchPriceFromCoingecko("ETH");
setNativeCurrencyPrice(price);
}
+ if (strkCurrencyPrice == 0) {
+ const strkPrice = await fetchPriceFromCoingecko("STRK");
+ setStrkCurrencyPrice(strkPrice);
+ }
})();
}, [targetNetwork]);
- // Get the price of ETH from Coingecko at a given interval
+ // Get the price of ETH & STRK from Coingecko at a given interval
useInterval(async () => {
- const price = await fetchPriceFromCoingecko(targetNetwork);
+ const price = await fetchPriceFromCoingecko("ETH");
setNativeCurrencyPrice(price);
+ const strkPrice = await fetchPriceFromCoingecko("STRK");
+ setStrkCurrencyPrice(strkPrice);
}, scaffoldConfig.pollingInterval);
//return nativeCurrencyPrice;
diff --git a/packages/nextjs/hooks/scaffold-stark/useScaffoldStrkBalance.ts b/packages/nextjs/hooks/scaffold-stark/useScaffoldStrkBalance.ts
new file mode 100644
index 000000000..ac80b742c
--- /dev/null
+++ b/packages/nextjs/hooks/scaffold-stark/useScaffoldStrkBalance.ts
@@ -0,0 +1,34 @@
+import { Address } from "@starknet-react/chains";
+import { useDeployedContractInfo } from "./useDeployedContractInfo";
+import { useContractRead } from "@starknet-react/core";
+import { BlockNumber } from "starknet";
+import { Abi } from "abi-wan-kanabi";
+import { formatUnits } from "ethers";
+
+type UseScaffoldStrkBalanceProps = {
+ address?: Address | string;
+};
+
+const useScaffoldStrkBalance = ({ address }: UseScaffoldStrkBalanceProps) => {
+ const { data: deployedContract } = useDeployedContractInfo("Strk");
+
+ const { data, ...props } = useContractRead({
+ functionName: "balanceOf",
+ address: deployedContract?.address,
+ abi: deployedContract?.abi as Abi as any[],
+ watch: true,
+ enabled: true,
+ args: address ? [address] : [],
+ blockIdentifier: "pending" as BlockNumber,
+ });
+
+ return {
+ value: data as unknown as bigint,
+ decimals: 18,
+ symbol: "STRK",
+ formatted: data ? formatUnits(data as unknown as bigint) : "0",
+ ...props,
+ };
+};
+
+export default useScaffoldStrkBalance;
diff --git a/packages/nextjs/public/blast-icon-color.svg b/packages/nextjs/public/blast-icon-color.svg
new file mode 100644
index 000000000..6de463649
--- /dev/null
+++ b/packages/nextjs/public/blast-icon-color.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/packages/nextjs/public/debug-image.png b/packages/nextjs/public/debug-image.png
new file mode 100644
index 000000000..e6795e8aa
Binary files /dev/null and b/packages/nextjs/public/debug-image.png differ
diff --git a/packages/nextjs/public/logo_alchemy.png b/packages/nextjs/public/logo_alchemy.png
new file mode 100644
index 000000000..9c4be1670
Binary files /dev/null and b/packages/nextjs/public/logo_alchemy.png differ
diff --git a/packages/nextjs/public/sn-symbol-gradient.png b/packages/nextjs/public/sn-symbol-gradient.png
new file mode 100644
index 000000000..c7856ba8e
Binary files /dev/null and b/packages/nextjs/public/sn-symbol-gradient.png differ
diff --git a/packages/nextjs/public/starkcompass-icon.svg b/packages/nextjs/public/starkcompass-icon.svg
new file mode 100644
index 000000000..c6eee44b1
--- /dev/null
+++ b/packages/nextjs/public/starkcompass-icon.svg
@@ -0,0 +1,28 @@
+
+
+
diff --git a/packages/nextjs/public/voyager-icon.svg b/packages/nextjs/public/voyager-icon.svg
new file mode 100644
index 000000000..6d4a76754
--- /dev/null
+++ b/packages/nextjs/public/voyager-icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/nextjs/services/store/store.ts b/packages/nextjs/services/store/store.ts
index 691b7e579..1137f3ea7 100644
--- a/packages/nextjs/services/store/store.ts
+++ b/packages/nextjs/services/store/store.ts
@@ -13,15 +13,20 @@ import { ChainWithAttributes } from "~~/utils/scaffold-stark";
type GlobalState = {
nativeCurrencyPrice: number;
+ strkCurrencyPrice: number;
setNativeCurrencyPrice: (newNativeCurrencyPriceState: number) => void;
+ setStrkCurrencyPrice: (newNativeCurrencyPriceState: number) => void;
targetNetwork: ChainWithAttributes;
setTargetNetwork: (newTargetNetwork: ChainWithAttributes) => void;
};
export const useGlobalState = create((set) => ({
nativeCurrencyPrice: 0,
+ strkCurrencyPrice: 0,
setNativeCurrencyPrice: (newValue: number): void =>
set(() => ({ nativeCurrencyPrice: newValue })),
+ setStrkCurrencyPrice: (newValue: number): void =>
+ set(() => ({ strkCurrencyPrice: newValue })),
targetNetwork: scaffoldConfig.targetNetworks[0],
setTargetNetwork: (newTargetNetwork: ChainWithAttributes) =>
set(() => ({ targetNetwork: newTargetNetwork })),
diff --git a/packages/nextjs/services/web3/faucet.ts b/packages/nextjs/services/web3/faucet.ts
index cd61e2115..a4e4f620b 100644
--- a/packages/nextjs/services/web3/faucet.ts
+++ b/packages/nextjs/services/web3/faucet.ts
@@ -1,15 +1,25 @@
import { Address } from "@starknet-react/chains";
export async function mintEth(inputAddress: Address, eth: string) {
- await fetch("http://0.0.0.0:5050/mint", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- address: inputAddress,
- amount: parseFloat(eth) * 10 ** 18,
- unit: "WEI",
- }),
- });
+ try {
+ const response = await fetch("http://0.0.0.0:5050/mint", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ address: inputAddress,
+ amount: parseFloat(eth) * 10 ** 18,
+ unit: "WEI",
+ }),
+ });
+ if (!response.ok) {
+ throw new Error(`${response.statusText}`);
+ }
+ const data = await response.json();
+ return data;
+ } catch (error) {
+ console.error("There was a problem with the operation", error);
+ return error;
+ }
}
diff --git a/packages/nextjs/utils/scaffold-stark/contract.ts b/packages/nextjs/utils/scaffold-stark/contract.ts
index ac9df3cc2..4f4d08908 100644
--- a/packages/nextjs/utils/scaffold-stark/contract.ts
+++ b/packages/nextjs/utils/scaffold-stark/contract.ts
@@ -63,7 +63,9 @@ export enum ContractCodeStatus {
export type GenericContract = {
address: Address;
abi: Abi;
+ classHash: String;
};
+
export type GenericContractsDeclaration = {
[network: string]: {
[contractName: string]: GenericContract;
diff --git a/packages/nextjs/utils/scaffold-stark/fetchPriceFromCoingecko.ts b/packages/nextjs/utils/scaffold-stark/fetchPriceFromCoingecko.ts
index f4b5e345d..801258b6e 100644
--- a/packages/nextjs/utils/scaffold-stark/fetchPriceFromCoingecko.ts
+++ b/packages/nextjs/utils/scaffold-stark/fetchPriceFromCoingecko.ts
@@ -4,15 +4,10 @@ import { ChainWithAttributes } from "~~/utils/scaffold-stark";
const priceCache: Record = {};
export const fetchPriceFromCoingecko = async (
- targetNetwork: ChainWithAttributes,
+ symbol: string,
retryCount = 3,
): Promise => {
- const { symbol } = targetNetwork.nativeCurrency;
- if (
- symbol !== "ETH" &&
- symbol !== "SEP" &&
- !targetNetwork.nativeCurrencyTokenAddress
- ) {
+ if (symbol !== "ETH" && symbol !== "SEP" && symbol !== "STRK") {
return 0;
}
@@ -32,11 +27,15 @@ const updatePriceCache = async (
let attempt = 0;
while (attempt < retries) {
try {
- const response = await fetch(
- `https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd`,
- );
+ let apiUrl = "";
+ if (symbol === "ETH") {
+ apiUrl = `https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd`;
+ } else if (symbol === "STRK") {
+ apiUrl = `https://api.coingecko.com/api/v3/simple/price?ids=starknet&vs_currencies=usd`;
+ }
+ const response = await fetch(apiUrl);
const data = await response.json();
- const price = data.ethereum.usd;
+ const price = symbol === "ETH" ? data.ethereum.usd : data.starknet.usd;
priceCache[symbol] = price;
console.log(`Price updated for ${symbol}: ${price}`);
return price;
diff --git a/packages/nextjs/utils/scaffold-stark/networks.ts b/packages/nextjs/utils/scaffold-stark/networks.ts
index 4601bf92a..4ff4a9905 100644
--- a/packages/nextjs/utils/scaffold-stark/networks.ts
+++ b/packages/nextjs/utils/scaffold-stark/networks.ts
@@ -66,6 +66,26 @@ export function getBlockExplorerAddressLink(
return `${blockExplorerBaseURL}/contract/${address}`;
}
+/**
+ * Gives the block explorer URL for a given classhash.
+ * Defaults to Etherscan if no (wagmi) block explorer is configured for the network.
+ */
+export function getBlockExplorerClasshashLink(
+ network: chains.Chain,
+ address: string,
+) {
+ const blockExplorerBaseURL = network.explorers?.starkscan[0];
+ if (network.network === chains.devnet.network) {
+ return `/blockexplorer/class/${address}`;
+ }
+
+ if (!blockExplorerBaseURL) {
+ return `https://starkscan.co/class/${address}`;
+ }
+
+ return `${blockExplorerBaseURL}/class/${address}`;
+}
+
export function getBlockExplorerLink(network: chains.Chain) {
switch (network) {
case chains.mainnet:
diff --git a/packages/snfoundry/contracts/src/YourContract.cairo b/packages/snfoundry/contracts/src/YourContract.cairo
new file mode 100644
index 000000000..21e185095
--- /dev/null
+++ b/packages/snfoundry/contracts/src/YourContract.cairo
@@ -0,0 +1,103 @@
+#[starknet::interface]
+pub trait IYourContract {
+ fn gretting(self: @TContractState) -> ByteArray;
+ fn set_gretting(ref self: TContractState, new_greeting: ByteArray, amount_eth: u256);
+ fn withdraw(ref self: TContractState);
+ fn premium(self: @TContractState) -> bool;
+}
+
+#[starknet::contract]
+mod YourContract {
+ use openzeppelin::access::ownable::OwnableComponent;
+ use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait};
+ use starknet::{ContractAddress, contract_address_const};
+ use starknet::{get_caller_address, get_contract_address};
+ use super::{IYourContract};
+
+ component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
+
+ #[abi(embed_v0)]
+ impl OwnableImpl = OwnableComponent::OwnableImpl;
+ impl OwnableInternalImpl = OwnableComponent::InternalImpl;
+
+ const ETH_CONTRACT_ADDRESS: felt252 =
+ 0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7;
+
+ #[event]
+ #[derive(Drop, starknet::Event)]
+ enum Event {
+ #[flat]
+ OwnableEvent: OwnableComponent::Event,
+ GreetingChanged: GreetingChanged
+ }
+
+ #[derive(Drop, starknet::Event)]
+ struct GreetingChanged {
+ #[key]
+ greeting_setter: ContractAddress,
+ #[key]
+ new_greeting: ByteArray,
+ premium: bool,
+ value: u256,
+ }
+
+ #[storage]
+ struct Storage {
+ eth_token: IERC20CamelDispatcher,
+ greeting: ByteArray,
+ premium: bool,
+ total_counter: u256,
+ user_gretting_counter: LegacyMap,
+ #[substorage(v0)]
+ ownable: OwnableComponent::Storage,
+ }
+
+ #[constructor]
+ fn constructor(ref self: ContractState, owner: ContractAddress) {
+ let eth_contract_address = contract_address_const::();
+ self.eth_token.write(IERC20CamelDispatcher { contract_address: eth_contract_address });
+ self.greeting.write("Building Unstoppable Apps!!!");
+ self.ownable.initializer(owner);
+ }
+
+ #[abi(embed_v0)]
+ impl YourContractImpl of IYourContract {
+ fn gretting(self: @ContractState) -> ByteArray {
+ self.greeting.read()
+ }
+ fn set_gretting(ref self: ContractState, new_greeting: ByteArray, amount_eth: u256) {
+ self.greeting.write(new_greeting);
+ self.total_counter.write(self.total_counter.read() + 1);
+ let user_counter = self.user_gretting_counter.read(get_caller_address());
+ self.user_gretting_counter.write(get_caller_address(), user_counter + 1);
+
+ if amount_eth > 0 {
+ // In `Debug Contract` or UI implementation call `approve` on ETH contract before invoke fn set_gretting()
+ self
+ .eth_token
+ .read()
+ .transferFrom(get_caller_address(), get_contract_address(), amount_eth);
+ self.premium.write(true);
+ } else {
+ self.premium.write(false);
+ }
+ self
+ .emit(
+ GreetingChanged {
+ greeting_setter: get_caller_address(),
+ new_greeting: self.greeting.read(),
+ premium: true,
+ value: 100
+ }
+ );
+ }
+ fn withdraw(ref self: ContractState) {
+ self.ownable.assert_only_owner();
+ let balance = self.eth_token.read().balanceOf(get_contract_address());
+ self.eth_token.read().transfer(self.ownable.owner(), balance);
+ }
+ fn premium(self: @ContractState) -> bool {
+ self.premium.read()
+ }
+ }
+}
diff --git a/packages/snfoundry/contracts/src/lib.cairo b/packages/snfoundry/contracts/src/lib.cairo
index 853bd00aa..568f9f72a 100644
--- a/packages/snfoundry/contracts/src/lib.cairo
+++ b/packages/snfoundry/contracts/src/lib.cairo
@@ -1,3 +1,4 @@
+mod YourContract;
#[cfg(test)]
mod test {
mod TestContract;
diff --git a/packages/snfoundry/package.json b/packages/snfoundry/package.json
index 17456ef37..06aa5cc1a 100644
--- a/packages/snfoundry/package.json
+++ b/packages/snfoundry/package.json
@@ -4,6 +4,7 @@
"scripts": {
"chain": "starknet-devnet --seed 0 --account-class cairo1",
"deploy": "ts-node scripts-ts/helpers/deploy-wrapper.ts",
+ "deploy:reset": "ts-node scripts-ts/helpers/deploy-wrapper.ts --reset",
"test": "cd contracts && snforge test",
"test-eslint": "node eslint-contract-name/eslint-plugin-contract-names.test.js",
"compile": "cd contracts && scarb build",
diff --git a/packages/snfoundry/scripts-ts/deploy-contract.ts b/packages/snfoundry/scripts-ts/deploy-contract.ts
index a332ee8a3..dd9ed7c67 100644
--- a/packages/snfoundry/scripts-ts/deploy-contract.ts
+++ b/packages/snfoundry/scripts-ts/deploy-contract.ts
@@ -3,40 +3,63 @@ import path from "path";
import { networks } from "./helpers/networks";
import yargs from "yargs";
import {
- BlockIdentifier,
CallData,
- hash,
stark,
RawArgs,
- constants,
- ec,
- validateAndParseAddress,
transaction,
-} from "starknet";
-import { Network } from "./types";
-import {
- LegacyContractClass,
- CompiledSierra,
extractContractHashes,
+ DeclareContractPayload,
+ UniversalDetails,
} from "starknet";
+import { DeployContractParams, Network } from "./types";
+import { green, red, yellow } from "./helpers/colorize-log";
-const argv = yargs(process.argv.slice(2)).argv;
-const networkName: string = argv["network"];
+interface Arguments {
+ network: string;
+ reset: boolean;
+ [x: string]: unknown;
+ _: (string | number)[];
+ $0: string;
+}
-let deployments = {};
+const argv = yargs(process.argv.slice(2))
+ .option("network", {
+ type: "string",
+ description: "Specify the network",
+ demandOption: true,
+ })
+ .option("reset", {
+ alias: "r",
+ type: "boolean",
+ description: "Reset deployments",
+ default: false,
+ })
+ .parseSync() as Arguments;
+
+const networkName: string = argv.network;
+const resetDeployments: boolean = argv.reset;
+let deployments = {};
let deployCalls = [];
const { provider, deployer }: Network = networks[networkName];
-const declareIfNot_NotWait = async (payload: any) => {
+const declareIfNot_NotWait = async (
+ payload: DeclareContractPayload,
+ options?: UniversalDetails
+) => {
const declareContractPayload = extractContractHashes(payload);
try {
await provider.getClassByHash(declareContractPayload.classHash);
} catch (error) {
- let { transaction_hash } = await deployer.declare(payload);
- if (networkName == "sepolia" || networkName == "mainnet") {
- await provider.waitForTransaction(transaction_hash);
+ try {
+ const { transaction_hash } = await deployer.declare(payload, options);
+ if (networkName === "sepolia" || networkName === "mainnet") {
+ await provider.waitForTransaction(transaction_hash);
+ }
+ } catch (e) {
+ console.error(red("Error declaring contract:"), e);
+ throw e;
}
}
return {
@@ -49,38 +72,64 @@ const deployContract_NotWait = async (payload: {
classHash: string;
constructorCalldata: RawArgs;
}) => {
- let { calls, addresses } = transaction.buildUDCCall(
- payload,
- deployer.address
- );
- deployCalls.push(...calls);
- return {
- contractAddress: addresses[0],
- };
+ try {
+ const { calls, addresses } = transaction.buildUDCCall(
+ payload,
+ deployer.address
+ );
+ deployCalls.push(...calls);
+ return {
+ contractAddress: addresses[0],
+ };
+ } catch (error) {
+ console.error(red("Error building UDC call:"), error);
+ throw error;
+ }
};
+/**
+ * Deploy a contract using the specified parameters.
+ *
+ * @param {DeployContractParams} params - The parameters for deploying the contract.
+ * @param {string} params.contract - The name of the contract to deploy.
+ * @param {string} [params.contractName] - The name to export the contract as (optional).
+ * @param {RawArgs} [params.constructorArgs] - The constructor arguments for the contract (optional).
+ * @param {UniversalDetails} [params.options] - Additional deployment options (optional).
+ *
+ * @returns {Promise<{ classHash: string; address: string }>} The deployed contract's class hash and address.
+ *
+ * @example
+ * ///Example usage of deployContract function
+ * await deployContract({
+ * contract: "YourContract",
+ * contractName: "YourContractExportName",
+ * constructorArgs: { owner: deployer.address },
+ * options: { maxFee: BigInt(1000000000000) }
+ * });
+ */
const deployContract = async (
- constructorArgs: RawArgs,
- contractName: string,
- exportContractName?: string,
- options?: {
- maxFee: bigint;
- }
+ params: DeployContractParams
): Promise<{
classHash: string;
address: string;
}> => {
+ const { contract, constructorArgs, contractName, options } = params;
+
try {
await deployer.getContractVersion(deployer.address);
} catch (e) {
if (e.toString().includes("Contract not found")) {
- throw new Error(
- `The wallet you're using to deploy the contract is not deployed in ${networkName} network`
- );
+ const errorMessage = `The wallet you're using to deploy the contract is not deployed in the ${networkName} network.`;
+ console.error(red(errorMessage));
+ throw new Error(errorMessage);
+ } else {
+ console.error(red("Error getting contract version: "), e);
+ throw e;
}
}
let compiledContractCasm;
+ let compiledContractSierra;
try {
compiledContractCasm = JSON.parse(
@@ -88,7 +137,7 @@ const deployContract = async (
.readFileSync(
path.resolve(
__dirname,
- `../contracts/target/dev/contracts_${contractName}.compiled_contract_class.json`
+ `../contracts/target/dev/contracts_${contract}.compiled_contract_class.json`
)
)
.toString("ascii")
@@ -102,12 +151,14 @@ const deployContract = async (
const match = error.message.match(
/\/dev\/(.+?)\.compiled_contract_class/
);
- const contractName = match ? match[1].split("_").pop() : "Unknown";
+ const missingContract = match ? match[1].split("_").pop() : "Unknown";
console.error(
- `The contract "${contractName}" doesn't exist or is not compiled`
+ red(
+ `The contract "${missingContract}" doesn't exist or is not compiled`
+ )
);
} else {
- console.error(error);
+ console.error(red("Error reading compiled contract class file: "), error);
}
return {
classHash: "",
@@ -115,27 +166,38 @@ const deployContract = async (
};
}
- const compiledContractSierra = JSON.parse(
- fs
- .readFileSync(
- path.resolve(
- __dirname,
- `../contracts/target/dev/contracts_${contractName}.contract_class.json`
+ try {
+ compiledContractSierra = JSON.parse(
+ fs
+ .readFileSync(
+ path.resolve(
+ __dirname,
+ `../contracts/target/dev/contracts_${contract}.contract_class.json`
+ )
)
- )
- .toString("ascii")
- );
+ .toString("ascii")
+ );
+ } catch (error) {
+ console.error(red("Error reading contract class file: "), error);
+ return {
+ classHash: "",
+ address: "",
+ };
+ }
const contractCalldata = new CallData(compiledContractSierra.abi);
const constructorCalldata = constructorArgs
? contractCalldata.compile("constructor", constructorArgs)
: [];
- console.log("Deploying Contract ", contractName);
+ console.log(yellow("Deploying Contract "), contract);
- let { classHash } = await declareIfNot_NotWait({
- contract: compiledContractSierra,
- casm: compiledContractCasm,
- });
+ let { classHash } = await declareIfNot_NotWait(
+ {
+ contract: compiledContractSierra,
+ casm: compiledContractCasm,
+ },
+ options
+ );
let randomSalt = stark.randomAddress();
@@ -145,14 +207,14 @@ const deployContract = async (
constructorCalldata,
});
- console.log("Contract Deployed at ", contractAddress);
+ console.log(green("Contract Deployed at "), contractAddress);
- let finalContractName = exportContractName || contractName;
+ let finalContractName = contractName || contract;
deployments[finalContractName] = {
classHash: classHash,
address: contractAddress,
- contract: contractName,
+ contract: contract,
};
return {
@@ -161,26 +223,45 @@ const deployContract = async (
};
};
-const executeDeployCalls = async () => {
+const executeDeployCalls = async (options?: UniversalDetails) => {
+ if (deployCalls.length < 1) {
+ throw new Error(
+ red(
+ "Aborted: No contract to deploy. Please prepare the contracts with `deployContract`"
+ )
+ );
+ }
+
try {
- let { transaction_hash } = await deployer.execute(deployCalls);
- console.log("Deploy Calls Executed at ", transaction_hash);
- if (networkName == "sepolia" || networkName == "mainnet") {
+ let { transaction_hash } = await deployer.execute(deployCalls, options);
+ console.log(green("Deploy Calls Executed at "), transaction_hash);
+ if (networkName === "sepolia" || networkName === "mainnet") {
await provider.waitForTransaction(transaction_hash);
}
} catch (error) {
+ console.error(red("Error executing deploy calls: "), error);
// split the calls in half and try again recursively
if (deployCalls.length > 1) {
- let half = deployCalls.length / 2;
+ let half = Math.ceil(deployCalls.length / 2);
let firstHalf = deployCalls.slice(0, half);
- let secondHalf = deployCalls.slice(half, deployCalls.length);
+ let secondHalf = deployCalls.slice(half);
deployCalls = firstHalf;
- await executeDeployCalls();
+ await executeDeployCalls(options);
deployCalls = secondHalf;
- await executeDeployCalls();
+ await executeDeployCalls(options);
}
}
};
+const loadExistingDeployments = () => {
+ const networkPath = path.resolve(
+ __dirname,
+ `../deployments/${networkName}_latest.json`
+ );
+ if (fs.existsSync(networkPath)) {
+ return JSON.parse(fs.readFileSync(networkPath, "utf8"));
+ }
+ return {};
+};
const exportDeployments = () => {
const networkPath = path.resolve(
@@ -188,7 +269,11 @@ const exportDeployments = () => {
`../deployments/${networkName}_latest.json`
);
- if (fs.existsSync(networkPath)) {
+ let finalDeployments = resetDeployments
+ ? deployments
+ : { ...loadExistingDeployments(), ...deployments };
+
+ if (fs.existsSync(networkPath) && !resetDeployments) {
const currentTimestamp = new Date().getTime();
fs.renameSync(
networkPath,
@@ -196,13 +281,15 @@ const exportDeployments = () => {
);
}
- fs.writeFileSync(networkPath, JSON.stringify(deployments, null, 2));
+ fs.writeFileSync(networkPath, JSON.stringify(finalDeployments, null, 2));
};
export {
deployContract,
provider,
deployer,
+ loadExistingDeployments,
exportDeployments,
executeDeployCalls,
+ resetDeployments,
};
diff --git a/packages/snfoundry/scripts-ts/deploy.ts b/packages/snfoundry/scripts-ts/deploy.ts
index 4489299b2..101aa7cfe 100644
--- a/packages/snfoundry/scripts-ts/deploy.ts
+++ b/packages/snfoundry/scripts-ts/deploy.ts
@@ -4,21 +4,57 @@ import {
exportDeployments,
deployer,
} from "./deploy-contract";
+import { green } from "./helpers/colorize-log";
+/**
+ * Deploy a contract using the specified parameters.
+ *
+ * @example (deploy contract with contructorArgs)
+ * const deployScript = async (): Promise => {
+ * await deployContract(
+ * {
+ * contract: "YourContract",
+ * contractName: "YourContractExportName",
+ * constructorArgs: {
+ * owner: deployer.address,
+ * },
+ * options: {
+ * maxFee: BigInt(1000000000000)
+ * }
+ * }
+ * );
+ * };
+ *
+ * @example (deploy contract without contructorArgs)
+ * const deployScript = async (): Promise => {
+ * await deployContract(
+ * {
+ * contract: "YourContract",
+ * contractName: "YourContractExportName",
+ * options: {
+ * maxFee: BigInt(1000000000000)
+ * }
+ * }
+ * );
+ * };
+ *
+ *
+ * @returns {Promise}
+ */
const deployScript = async (): Promise => {
- await deployContract(
- {
- owner: deployer.address, // the deployer address is the owner of the contract
+ await deployContract({
+ contract: "YourContract",
+ constructorArgs: {
+ owner: deployer.address,
},
- "YourContract"
- );
+ });
};
deployScript()
- .then(() => {
- executeDeployCalls().then(() => {
- exportDeployments();
- });
- console.log("All Setup Done");
+ .then(async () => {
+ await executeDeployCalls();
+ exportDeployments();
+
+ console.log(green("All Setup Done"));
})
.catch(console.error);
diff --git a/packages/snfoundry/scripts-ts/helpers/colorize-log.ts b/packages/snfoundry/scripts-ts/helpers/colorize-log.ts
new file mode 100644
index 000000000..1e37792c2
--- /dev/null
+++ b/packages/snfoundry/scripts-ts/helpers/colorize-log.ts
@@ -0,0 +1,16 @@
+const colors = {
+ reset: "\x1b[0m",
+ red: "\x1b[31m",
+ green: "\x1b[32m",
+ yellow: "\x1b[33m",
+};
+
+const colorize = (color: string, message: string): string => {
+ return `${color}${message}${colors.reset}`;
+};
+
+export const red = (message: string): string => colorize(colors.red, message);
+export const green = (message: string): string =>
+ colorize(colors.green, message);
+export const yellow = (message: string): string =>
+ colorize(colors.yellow, message);
diff --git a/packages/snfoundry/scripts-ts/helpers/deploy-wrapper.ts b/packages/snfoundry/scripts-ts/helpers/deploy-wrapper.ts
index 59adce370..60ecb9450 100644
--- a/packages/snfoundry/scripts-ts/helpers/deploy-wrapper.ts
+++ b/packages/snfoundry/scripts-ts/helpers/deploy-wrapper.ts
@@ -6,22 +6,28 @@ interface CommandLineOptions {
_: string[]; // Non-hyphenated arguments are usually under the `_` key
$0: string; // The script name or path is under the `$0` key
network?: string; // The --network option
+ reset?: boolean;
}
const argv = yargs(process.argv.slice(2))
.options({
network: { type: "string" },
+ reset: { type: "boolean", default: false },
})
.parseSync() as CommandLineOptions;
// Set the NETWORK environment variable based on the --network argument
process.env.NETWORK = argv.network || "devnet";
+// Set the RESET environment variable based on the --reset flag
+
// Execute the deploy script
execSync(
- "cd contracts && scarb build && ts-node ../scripts-ts/deploy.ts --network " +
+ "cd contracts && scarb build && ts-node ../scripts-ts/deploy.ts" +
+ " --network " +
process.env.NETWORK +
+ (argv.reset ? " --reset" : "") +
" && ts-node ../scripts-ts/helpers/parse-deployments.ts" +
- " && cd ../..",
+ " && cd ..",
{ stdio: "inherit" }
);
diff --git a/packages/snfoundry/scripts-ts/helpers/parse-deployments.ts b/packages/snfoundry/scripts-ts/helpers/parse-deployments.ts
index a64d12bdd..aa2490c50 100644
--- a/packages/snfoundry/scripts-ts/helpers/parse-deployments.ts
+++ b/packages/snfoundry/scripts-ts/helpers/parse-deployments.ts
@@ -14,11 +14,11 @@ const generatedContractComment = `/**
const getContractDataFromDeployments = (): Record<
string,
- Record
+ Record
> => {
const allContractsData: Record<
string,
- Record
+ Record
> = {};
files.forEach((file) => {
@@ -49,6 +49,7 @@ const getContractDataFromDeployments = (): Record<
[contractName]: {
address: contractData.address,
abi: abiContent.abi.filter((item) => item.type !== "l1_handler"),
+ classHash: contractData.classHash,
},
};
} catch (e) {}
diff --git a/packages/snfoundry/scripts-ts/types.ts b/packages/snfoundry/scripts-ts/types.ts
index a5965a61b..eabc5c9f4 100644
--- a/packages/snfoundry/scripts-ts/types.ts
+++ b/packages/snfoundry/scripts-ts/types.ts
@@ -1,4 +1,4 @@
-import { Account, RpcProvider } from "starknet";
+import { Account, RawArgs, RpcProvider, UniversalDetails } from "starknet";
export type Networks = Record<"devnet" | "sepolia" | "mainnet", Network>;
@@ -6,3 +6,10 @@ export type Network = {
provider: RpcProvider;
deployer: Account;
};
+
+export type DeployContractParams = {
+ contract: string;
+ contractName?: string;
+ constructorArgs?: RawArgs;
+ options?: UniversalDetails;
+};