From 2b4fcbd2dbab6aab754a053067fe64a436541c90 Mon Sep 17 00:00:00 2001 From: gianmalarcon Date: Tue, 6 May 2025 13:20:23 -0500 Subject: [PATCH 1/7] Ops: Update workflow to sync challenges --- .github/workflows/sync_other_challenges.yaml | 52 +++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sync_other_challenges.yaml b/.github/workflows/sync_other_challenges.yaml index 9006020d9..2fbe64d7c 100644 --- a/.github/workflows/sync_other_challenges.yaml +++ b/.github/workflows/sync_other_challenges.yaml @@ -32,15 +32,63 @@ jobs: git checkout base-challenge-template git pull origin base-challenge-template + CHALLENGE_BRANCHES=( + "challenge-0-simple-nft" + "challenge-1-decentralized-staking" + "challenge-2-token-vendor" + "challenge-3-dice-game" + "challenge-4-build-a-dex" + "challenge-5-multisig-wallet" + ) + echo "Merging base-challenge-template into challenge branches..." - git checkout challenge-0-simple-nft && git merge base-challenge-template --no-edit && git push origin challenge-0-simple-nft + + for branch in "${CHALLENGE_BRANCHES[@]}"; do + echo "Processing branch: $branch" + git checkout $branch + git pull origin $branch + + # Try to merge + if git merge base-challenge-template --no-edit; then + echo "Merge successful for $branch, pushing changes" + git push origin $branch + else + echo "Merge conflict detected in $branch, creating PR" + # Abort the current merge + git merge --abort + + # Create a new branch for the PR + CONFLICT_BRANCH="fix-conflicts-$branch-$(date +%s)" + git checkout -b $CONFLICT_BRANCH + + # Try merge again but allow it to record conflicts + git merge base-challenge-template || true + + # Push the branch with conflicts + git push origin $CONFLICT_BRANCH + + # Create PR using GitHub CLI if available, otherwise use API + PR_TITLE="Fix merge conflicts in $branch" + PR_BODY="This PR was automatically created to resolve merge conflicts between base-challenge-template and $branch." + + # Using GitHub API to create PR + curl -X POST \ + -H "Authorization: token ${{ secrets.ORG_GITHUB_TOKEN }}" \ + -H "Accept: application/vnd.github.v3+json" \ + https://api.github.com/repos/Scaffold-Stark/speedrunstark/pulls \ + -d "{\"title\":\"$PR_TITLE\",\"body\":\"$PR_BODY\",\"head\":\"$CONFLICT_BRANCH\",\"base\":\"$branch\"}" + + # Return to original branch + git checkout $branch + fi + done - name: Notify Slack on Success if: success() uses: slackapi/slack-github-action@v1.26.0 with: channel-id: ${{ secrets.SLACK_CHANNEL_ID }} - slack-message: "Successfully synced scaffold-stark-2 changes to speedrunstark repository and updated all step branches." + slack-message: "Successfully processed sync operation for speedrunstark repository. Check for any PRs that may have been created for conflict resolution." env: SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} From 5387829c206375897debd76ed82cf5726d4d2bf5 Mon Sep 17 00:00:00 2001 From: gianmalarcon Date: Tue, 6 May 2025 13:30:07 -0500 Subject: [PATCH 2/7] Ops: Update workflow to sync challenges --- .github/scripts/clean_redundant_files.sh | 47 -------------------- .github/workflows/sync_other_challenges.yaml | 4 ++ 2 files changed, 4 insertions(+), 47 deletions(-) delete mode 100755 .github/scripts/clean_redundant_files.sh diff --git a/.github/scripts/clean_redundant_files.sh b/.github/scripts/clean_redundant_files.sh deleted file mode 100755 index 0479caa5a..000000000 --- a/.github/scripts/clean_redundant_files.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash - -# --- Configuration --- -TARGET_DIRS=("__test__" "__tests__" "public" ".github") - -# --- Script Logic --- -set -e # Exit immediately if a command exits with a non-zero status. - -# Check if inside a git repository -if ! git rev-parse --is-inside-work-tree > /dev/null 2>&1; then - echo "Error: Not a git repository." >&2 # Output errors to stderr - exit 1 -fi - -# Build the grep pattern: (dir1/|dir2/|dir3/) -grep_pattern="(" -first=true -for dir in "${TARGET_DIRS[@]}"; do - if [ "$first" = true ]; then - first=false - else - grep_pattern+="|" - fi - grep_pattern+="${dir}/" -done -grep_pattern+=")" - -# Find staged files matching the patterns -mapfile -t files_to_process < <(git diff --cached --name-only | grep -E "$grep_pattern") - -# Exit if no matching files are staged -if [ ${#files_to_process[@]} -eq 0 ]; then - # Optionally keep this message, or remove it for complete silence when nothing to do - # echo "No staged files found matching target directories." - exit 0 -fi - -# --- Perform Actions --- - -# 1. Unstage the files -git restore --staged --quiet -- "${files_to_process[@]}" - -# 2. Remove newly added files (which became untracked after step 1) -# This command ignores tracked files. -git clean -fd - -exit 0 \ No newline at end of file diff --git a/.github/workflows/sync_other_challenges.yaml b/.github/workflows/sync_other_challenges.yaml index 2fbe64d7c..52d741736 100644 --- a/.github/workflows/sync_other_challenges.yaml +++ b/.github/workflows/sync_other_challenges.yaml @@ -64,6 +64,10 @@ jobs: # Try merge again but allow it to record conflicts git merge base-challenge-template || true + # Commit the conflicts + git add . + git commit -m "Auto-commit merge conflicts between base-challenge-template and $branch" + # Push the branch with conflicts git push origin $CONFLICT_BRANCH From 5b49d8e9d7c68f51c99c6ca6b9f6bf32f9a0ad73 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 8 May 2025 20:29:12 +0000 Subject: [PATCH 3/7] chore: sync files from scaffold-stark-2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1806f16fd..dfd3e4c13 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ss-2", - "version": "1.0.10", + "version": "1.0.11", "author": "Q3 Labs", "license": "MIT", "private": true, From fe88cbb29eb33c20c27e8f5e03151ed9c5351d42 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 11 Jun 2025 09:17:36 +0000 Subject: [PATCH 4/7] chore: sync files from scaffold-stark-2 --- .github/workflows/main.yml | 2 +- .tool-versions | 4 +- package.json | 2 +- .../_components/contract/ContractInput.tsx | 4 + .../_components/contract/utilsContract.tsx | 30 +- .../scaffold-stark/BlockExplorer.tsx | 39 +- .../scaffold-stark/BlockExplorerSepolia.tsx | 59 +- .../AddressInfoDropdown.tsx | 25 +- .../CustomConnectButton/ConnectModal.tsx | 35 +- .../CustomConnectButton/GenericModal.tsx | 6 +- .../WrongNetworkDropdown.tsx | 24 +- .../CustomConnectButton/index.tsx | 72 +- .../hooks/scaffold-stark/useAutoConnect.ts | 69 +- packages/nextjs/package.json | 14 +- packages/nextjs/supportedChains.ts | 7 +- packages/snfoundry/contracts/Scarb.lock | 12 +- packages/snfoundry/contracts/Scarb.toml | 7 +- packages/snfoundry/package.json | 6 +- yarn.lock | 3896 +++++++++-------- 19 files changed, 2255 insertions(+), 2058 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9b0250391..d0ca900e2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,6 +8,7 @@ on: pull_request: branches: - main + - develop paths: - "packages/nextjs/**" - "packages/snfoundry/contracts/**" @@ -30,7 +31,6 @@ jobs: with: node-version: ${{ matrix.node }} cache: "yarn" - #cache-dependency-path: packages/nextjs/yarn.lock - name: Install dependencies (Next.js) run: yarn install --immutable diff --git a/.tool-versions b/.tool-versions index 3004df8f3..1e0292a63 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,3 +1,3 @@ scarb 2.11.4 -starknet-foundry 0.41.0 -starknet-devnet 0.4.0 +starknet-foundry 0.44.0 +starknet-devnet 0.4.1 \ No newline at end of file diff --git a/package.json b/package.json index dfd3e4c13..425e05525 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ss-2", - "version": "1.0.11", + "version": "1.0.12", "author": "Q3 Labs", "license": "MIT", "private": true, diff --git a/packages/nextjs/app/debug/_components/contract/ContractInput.tsx b/packages/nextjs/app/debug/_components/contract/ContractInput.tsx index 846935b77..ec258acdb 100644 --- a/packages/nextjs/app/debug/_components/contract/ContractInput.tsx +++ b/packages/nextjs/app/debug/_components/contract/ContractInput.tsx @@ -18,6 +18,7 @@ import { isCairoTuple, isCairoType, isCairoU256, + isCairoVoid, } from "~~/utils/scaffold-stark"; import { Struct } from "./Struct"; import { Abi } from "abi-wan-kanabi"; @@ -98,6 +99,9 @@ export const ContractInput = ({ !isCairoResult(paramType.type) && !isCairoOption(paramType.type) ) { + if (isCairoVoid(paramType.type)) { + return <>; + } return ; } else { return ( diff --git a/packages/nextjs/app/debug/_components/contract/utilsContract.tsx b/packages/nextjs/app/debug/_components/contract/utilsContract.tsx index 456b9a7ab..583d782aa 100644 --- a/packages/nextjs/app/debug/_components/contract/utilsContract.tsx +++ b/packages/nextjs/app/debug/_components/contract/utilsContract.tsx @@ -176,22 +176,22 @@ export const getArgsAsStringInputFromForm = (form: Record) => { } } - const restructuredEnum = Object.fromEntries( - enumVariants.map((variant) => [ - variant, - (enumObject[variant].value || "").trim().length > 0 - ? _encodeValueFromKey( - (enumObject[variant] as FormStructValue).type, - (enumObject[variant] as FormStructValue).value, - ) - : undefined, - ]), - ); + const activeVariant = enumVariants.find((variant) => { + const v = enumObject[variant]; + return v && (v.value !== undefined || v.value === ""); + }); - if (enumVariants.includes("Some") && enumVariants.includes("None")) { - console.log("options", { restructuredEnum }); - console.log("options", "we found an option"); - } + const restructuredEnum = activeVariant + ? { + [activeVariant]: + enumObject[activeVariant].value !== undefined + ? _encodeValueFromKey( + enumObject[activeVariant].type, + enumObject[activeVariant].value, + ) + : undefined, + } + : {}; return new CairoCustomEnum(restructuredEnum); } diff --git a/packages/nextjs/components/scaffold-stark/BlockExplorer.tsx b/packages/nextjs/components/scaffold-stark/BlockExplorer.tsx index ad321ea03..3ac63f0a6 100644 --- a/packages/nextjs/components/scaffold-stark/BlockExplorer.tsx +++ b/packages/nextjs/components/scaffold-stark/BlockExplorer.tsx @@ -21,11 +21,6 @@ export const BlockExplorer = () => { img: "/voyager-icon.svg", link: "https://voyager.online/", }, - { - name: "Stark Compass", - img: "/starkcompass-icon.svg", - link: "https://starkcompass.com/", - }, ]; const { resolvedTheme } = useTheme(); @@ -51,37 +46,39 @@ export const BlockExplorer = () => { className="modal-toggle" /> -
-

Mainnet Block Explorers

- -
-
-
+
+
+

Mainnet Block Explorers

+ +
+
{blockExplorers.length && blockExplorers.map((blockexplorer, id) => (
Starknet Developers Hub
- {blockexplorer.name} +

{blockexplorer.name}

))}
diff --git a/packages/nextjs/components/scaffold-stark/BlockExplorerSepolia.tsx b/packages/nextjs/components/scaffold-stark/BlockExplorerSepolia.tsx index bc45fc4da..4b54e9eec 100644 --- a/packages/nextjs/components/scaffold-stark/BlockExplorerSepolia.tsx +++ b/packages/nextjs/components/scaffold-stark/BlockExplorerSepolia.tsx @@ -21,11 +21,6 @@ export const BlockExplorerSepolia = () => { img: "/voyager-icon.svg", link: "https://sepolia.voyager.online/", }, - { - name: "Stark Compass", - img: "/starkcompass-icon.svg", - link: "https://starkcompass.com/sepolia/", - }, ]; const { resolvedTheme } = useTheme(); @@ -51,7 +46,7 @@ export const BlockExplorerSepolia = () => { className="modal-toggle" /> - <> +

Sepolia Block Explorers

-
-
- {sepoliaBlockExplorers.length && - sepoliaBlockExplorers.map((blockexplorer, id) => ( - -
- Starknet Developers Hub -
-

{blockexplorer.name}

-
- ))} -
+
+ {sepoliaBlockExplorers.length && + sepoliaBlockExplorers.map((blockexplorer, id) => ( + +
+ {blockexplorer.name} +
+

{blockexplorer.name}

+
+ ))}
- +
); diff --git a/packages/nextjs/components/scaffold-stark/CustomConnectButton/AddressInfoDropdown.tsx b/packages/nextjs/components/scaffold-stark/CustomConnectButton/AddressInfoDropdown.tsx index dda1430fe..96a88b079 100644 --- a/packages/nextjs/components/scaffold-stark/CustomConnectButton/AddressInfoDropdown.tsx +++ b/packages/nextjs/components/scaffold-stark/CustomConnectButton/AddressInfoDropdown.tsx @@ -16,7 +16,7 @@ import { useLocalStorage } from "usehooks-ts"; import { BlockieAvatar, isENS } from "~~/components/scaffold-stark"; import { useOutsideClick } from "~~/hooks/scaffold-stark"; import { BurnerConnector, burnerAccounts } from "@scaffold-stark/stark-burner"; -import { getTargetNetworks } from "~~/utils/scaffold-stark"; +import { getTargetNetworks, notification } from "~~/utils/scaffold-stark"; import { Address } from "@starknet-react/chains"; import { useDisconnect, useNetwork, useConnect } from "@starknet-react/core"; import { getStarknetPFPIfExists } from "~~/utils/profile"; @@ -77,6 +77,27 @@ export const AddressInfoDropdown = ({ }, ); + const [, setWasDisconnectedManually] = useLocalStorage( + "wasDisconnectedManually", + false, + { + initializeWithValue: false, + }, + ); + + const handleDisconnect = () => { + try { + disconnect(); + localStorage.removeItem("lastUsedConnector"); + localStorage.removeItem("lastConnectionTime"); + setWasDisconnectedManually(true); + window.dispatchEvent(new Event("manualDisconnect")); + notification.success("Disconnect successfully!"); + } catch (err) { + console.log(err); + notification.success("Disconnect failure!"); + } + }; return ( <>
@@ -268,7 +289,7 @@ export const AddressInfoDropdown = ({
))} diff --git a/packages/nextjs/components/scaffold-stark/CustomConnectButton/GenericModal.tsx b/packages/nextjs/components/scaffold-stark/CustomConnectButton/GenericModal.tsx index c8afe867a..223ff6b2a 100644 --- a/packages/nextjs/components/scaffold-stark/CustomConnectButton/GenericModal.tsx +++ b/packages/nextjs/components/scaffold-stark/CustomConnectButton/GenericModal.tsx @@ -2,7 +2,7 @@ import { useTheme } from "next-themes"; const GenericModal = ({ children, - className = "modal-box modal-border bg-modal rounded-[8px] border flex flex-col gap-3 justify-around relative", + className = "modal-box modal-border bg-modal rounded-[8px] border flex flex-col relative w-full max-w-xs p-6", modalId, }: { children: React.ReactNode; @@ -12,8 +12,8 @@ const GenericModal = ({ const { resolvedTheme } = useTheme(); const isDarkMode = resolvedTheme === "dark"; return ( -
-
- setInputAddress(value as AddressType)} - /> - setSendValue(value)} - /> +
+
+ setInputAddress(value as AddressType)} + /> +
+
+ setSendValue(value)} + /> +
+
-
diff --git a/packages/nextjs/hooks/scaffold-stark/useScaffoldEventHistory.ts b/packages/nextjs/hooks/scaffold-stark/useScaffoldEventHistory.ts index 8cd96d125..d8d702dad 100644 --- a/packages/nextjs/hooks/scaffold-stark/useScaffoldEventHistory.ts +++ b/packages/nextjs/hooks/scaffold-stark/useScaffoldEventHistory.ts @@ -75,6 +75,29 @@ export const useScaffoldEventHistory = < }); }, [targetNetwork.rpcUrls.public.http]); + // Get back event full name + const matchingAbiEvents = useMemo(() => { + return (deployedContractData?.abi as Abi)?.filter( + (part) => + part.type === "event" && + part.name.split("::").slice(-1)[0] === (eventName as string), + ) as ExtractAbiEvent, TEventName>[]; + }, [deployedContractData, deployedContractLoading]); + // const matchingAbiEvents = + + if (matchingAbiEvents?.length === 0) { + throw new Error(`Event ${eventName as string} not found in contract ABI`); + } + + if (matchingAbiEvents?.length > 1) { + throw new Error( + `Ambiguous event "${eventName as string}". ABI contains ${matchingAbiEvents.length} events with that name`, + ); + } + + const eventAbi = matchingAbiEvents?.[0]; + const fullName = eventAbi?.name; + const readEvents = async (fromBlock?: bigint) => { if (!enabled) { setIsLoading(false); @@ -92,7 +115,9 @@ export const useScaffoldEventHistory = < } const event = (deployedContractData.abi as Abi).find( - (part) => part.type === "event" && part.name === eventName, + (part) => + part.type === "event" && + part.name.split("::").slice(-1)[0] === eventName, ) as ExtractAbiEvent, TEventName>; const blockNumber = (await publicClient.getBlockLatestAccepted()) @@ -102,9 +127,7 @@ export const useScaffoldEventHistory = < (fromBlock && blockNumber >= fromBlock) || blockNumber >= fromBlockUpdated ) { - let keys: string[][] = [ - [hash.getSelectorFromName(event.name.split("::").slice(-1)[0])], - ]; + let keys: string[][] = [[hash.getSelectorFromName(eventName)]]; if (filters) { keys = keys.concat( composeEventFilterKeys(filters, event, deployedContractData.abi), @@ -217,7 +240,7 @@ export const useScaffoldEventHistory = < CallData.getAbiStruct(deployedContractData.abi), CallData.getAbiEnum(deployedContractData.abi), ); - const args = parsed.length ? parsed[0][eventName] : {}; + const args = parsed.length ? parsed[0][fullName] : {}; const { event: rawEvent, ...rest } = event; return { type: rawEvent.members, diff --git a/packages/nextjs/utils/scaffold-stark/contract.ts b/packages/nextjs/utils/scaffold-stark/contract.ts index 000cf426c..6fb993117 100644 --- a/packages/nextjs/utils/scaffold-stark/contract.ts +++ b/packages/nextjs/utils/scaffold-stark/contract.ts @@ -360,6 +360,10 @@ export type EventFilters< } >;*/ +type BaseName = S extends `${infer _Prefix}::${infer Rest}` + ? BaseName + : S; + export type UseScaffoldEventHistoryConfig< TContractName extends ContractName, TEventName extends ExtractAbiEventNames>, @@ -368,7 +372,8 @@ export type UseScaffoldEventHistoryConfig< TReceiptData extends boolean = false, > = { contractName: TContractName; - eventName: IsContractDeclarationMissing; + eventName: // | IsContractDeclarationMissing + BaseName>; fromBlock: bigint; filters?: { [key: string]: any }; blockData?: TBlockData; diff --git a/packages/nextjs/utils/scaffold-stark/types.ts b/packages/nextjs/utils/scaffold-stark/types.ts index 2d704d40c..66e617b32 100644 --- a/packages/nextjs/utils/scaffold-stark/types.ts +++ b/packages/nextjs/utils/scaffold-stark/types.ts @@ -23,7 +23,8 @@ export const isCairoBigInt = (type: string): type is CairoBigInt => /core::integer::(u|i)(64|128)$/.test(type); export const isCairoU256 = (type: string): type is CairoU256 => - /core::integer::u256$/.test(type); + /core::integer::u256$/.test(type) || + /core::zeroable::NonZero::$/.test(type); export const isCairoContractAddress = ( type: string, From 95c7e3a3e49e3e6ccb14d44a6b33d55778fc7c5f Mon Sep 17 00:00:00 2001 From: truthixify <124162045+truthixify@users.noreply.github.com> Date: Mon, 30 Jun 2025 06:19:59 +0100 Subject: [PATCH 6/7] fix: fixed build issues by adding the contract (#254) --- .../contracts/src/YourContract.cairo | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 packages/snfoundry/contracts/src/YourContract.cairo diff --git a/packages/snfoundry/contracts/src/YourContract.cairo b/packages/snfoundry/contracts/src/YourContract.cairo new file mode 100644 index 000000000..7562231e1 --- /dev/null +++ b/packages/snfoundry/contracts/src/YourContract.cairo @@ -0,0 +1,113 @@ +#[starknet::interface] +pub trait IYourContract { + fn greeting(self: @TContractState) -> ByteArray; + fn set_greeting(ref self: TContractState, new_greeting: ByteArray, amount_strk: Option); + fn withdraw(ref self: TContractState); + fn premium(self: @TContractState) -> bool; +} + +#[starknet::contract] +pub mod YourContract { + use openzeppelin_access::ownable::OwnableComponent; + use openzeppelin_token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; + use starknet::storage::{ + Map, StorageMapReadAccess, StorageMapWriteAccess, StoragePointerReadAccess, + StoragePointerWriteAccess, + }; + use starknet::{ContractAddress, 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; + + pub const FELT_STRK_CONTRACT: felt252 = + 0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d; + + #[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: Option, + } + + #[storage] + struct Storage { + greeting: ByteArray, + premium: bool, + total_counter: u256, + user_greeting_counter: Map, + #[substorage(v0)] + ownable: OwnableComponent::Storage, + } + + #[constructor] + fn constructor(ref self: ContractState, owner: ContractAddress) { + self.greeting.write("Building Unstoppable Apps!!!"); + self.ownable.initializer(owner); + } + + #[abi(embed_v0)] + impl YourContractImpl of IYourContract { + fn greeting(self: @ContractState) -> ByteArray { + self.greeting.read() + } + fn set_greeting( + ref self: ContractState, new_greeting: ByteArray, amount_strk: Option, + ) { + self.greeting.write(new_greeting); + self.total_counter.write(self.total_counter.read() + 1); + let user_counter = self.user_greeting_counter.read(get_caller_address()); + self.user_greeting_counter.write(get_caller_address(), user_counter + 1); + + match amount_strk { + Option::Some(amount_strk) => { + // In `Debug Contract` or UI implementation, call `approve` on STRK contract + // before invoking fn set_greeting() + let strk_contract_address: ContractAddress = FELT_STRK_CONTRACT + .try_into() + .unwrap(); + let strk_dispatcher = IERC20Dispatcher { + contract_address: strk_contract_address, + }; + strk_dispatcher + .transfer_from(get_caller_address(), get_contract_address(), amount_strk); + self.premium.write(true); + }, + Option::None => { self.premium.write(false); }, + } + self + .emit( + GreetingChanged { + greeting_setter: get_caller_address(), + new_greeting: self.greeting.read(), + premium: true, + value: amount_strk, + }, + ); + } + fn withdraw(ref self: ContractState) { + self.ownable.assert_only_owner(); + let strk_contract_address = FELT_STRK_CONTRACT.try_into().unwrap(); + let strk_dispatcher = IERC20Dispatcher { contract_address: strk_contract_address }; + let balance = strk_dispatcher.balance_of(get_contract_address()); + strk_dispatcher.transfer(self.ownable.owner(), balance); + } + fn premium(self: @ContractState) -> bool { + self.premium.read() + } + } +} From bdda268e4a1b25be6e099a303d57e731e4e61c3d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 16 Jul 2025 04:11:49 +0000 Subject: [PATCH 7/7] chore: sync files from scaffold-stark-2 --- .github/workflows/main.yml | 2 +- .tool-versions | 4 +- package.json | 2 +- .../contract/ReadOnlyFunctionForm.tsx | 23 +- .../CustomConnectButton/ConnectModal.tsx | 70 +- .../nextjs/contracts/predeployedContracts.ts | 8 +- .../useScaffoldWatchContractEvent.ts | 198 ++++ packages/nextjs/package.json | 3 +- packages/nextjs/utils/Constants.ts | 1047 +++++++++++++++-- .../nextjs/utils/scaffold-stark/common.ts | 11 + .../nextjs/utils/scaffold-stark/contract.ts | 9 + .../utils/scaffold-stark/notification.tsx | 27 +- packages/snfoundry/contracts/Scarb.lock | 33 +- packages/snfoundry/contracts/Scarb.toml | 11 +- .../snfoundry/scripts-ts/deploy-contract.ts | 72 +- packages/snfoundry/scripts-ts/deploy.ts | 4 + .../scripts-ts/helpers/parse-deployments.ts | 44 +- yarn.lock | 26 + 18 files changed, 1440 insertions(+), 154 deletions(-) create mode 100644 packages/nextjs/hooks/scaffold-stark/useScaffoldWatchContractEvent.ts diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d0ca900e2..d2eb3585d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -65,7 +65,7 @@ jobs: working-directory: ./packages/nextjs - name: Run Next.js tests - run: yarn test run + run: yarn test working-directory: ./packages/nextjs - name: Build Next.js project diff --git a/.tool-versions b/.tool-versions index 1e0292a63..b4d1cbbd2 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,3 +1,3 @@ scarb 2.11.4 -starknet-foundry 0.44.0 -starknet-devnet 0.4.1 \ No newline at end of file +starknet-foundry 0.45.0 +starknet-devnet 0.4.3 \ No newline at end of file diff --git a/package.json b/package.json index 2d91fa9c8..12511dd15 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ss-2", - "version": "1.0.14", + "version": "1.0.15", "author": "Q3 Labs", "license": "MIT", "private": true, diff --git a/packages/nextjs/app/debug/_components/contract/ReadOnlyFunctionForm.tsx b/packages/nextjs/app/debug/_components/contract/ReadOnlyFunctionForm.tsx index ff5017f4f..26c908752 100644 --- a/packages/nextjs/app/debug/_components/contract/ReadOnlyFunctionForm.tsx +++ b/packages/nextjs/app/debug/_components/contract/ReadOnlyFunctionForm.tsx @@ -17,6 +17,7 @@ import { AbiFunction } from "~~/utils/scaffold-stark/contract"; import { BlockNumber } from "starknet"; import { useContract, useReadContract } from "@starknet-react/core"; import { ContractInput } from "./ContractInput"; +import { isValidContractArgs } from "~~/utils/scaffold-stark/common"; type ReadOnlyFunctionFormProps = { contractAddress: Address; @@ -42,11 +43,16 @@ export const ReadOnlyFunctionForm = ({ address: contractAddress, }); + const inputIsValidArray = isValidContractArgs( + inputValue, + abiFunction.inputs.length, + ); + const { isFetching, data, refetch, error } = useReadContract({ address: contractAddress, functionName: abiFunction.name, abi: [...abi], - args: inputValue || [], + args: inputIsValidArray ? inputValue : undefined, enabled: !!inputValue && !!contractInstance, blockIdentifier: "pending" as BlockNumber, }); @@ -76,10 +82,25 @@ export const ReadOnlyFunctionForm = ({ const handleRead = () => { const newInputValue = getArgsAsStringInputFromForm(form); + const expectedArgCount = abiFunction.inputs.length; + + const isValidInput = isValidContractArgs(newInputValue, expectedArgCount); + + if (!isValidInput) { + /** + * Todo: add extra logging in future release. + */ + console.warn( + `Read blocked: Expected ${expectedArgCount} args, got ${newInputValue.length}`, + ); + return; + } + if (JSON.stringify(form) !== JSON.stringify(lastForm.current)) { setInputValue(newInputValue); lastForm.current = form; } + refetch(); }; diff --git a/packages/nextjs/components/scaffold-stark/CustomConnectButton/ConnectModal.tsx b/packages/nextjs/components/scaffold-stark/CustomConnectButton/ConnectModal.tsx index d70899c7b..f6184b340 100644 --- a/packages/nextjs/components/scaffold-stark/CustomConnectButton/ConnectModal.tsx +++ b/packages/nextjs/components/scaffold-stark/CustomConnectButton/ConnectModal.tsx @@ -7,6 +7,7 @@ import { useTheme } from "next-themes"; import { BlockieAvatar } from "../BlockieAvatar"; import GenericModal from "./GenericModal"; import { LAST_CONNECTED_TIME_LOCALSTORAGE_KEY } from "~~/utils/Constants"; +import { useTargetNetwork } from "~~/hooks/scaffold-stark/useTargetNetwork"; const loader = ({ src }: { src: string }) => src; @@ -28,6 +29,19 @@ const ConnectModal = () => { "wasDisconnectedManually", false, ); + const { targetNetwork } = useTargetNetwork(); + const [showOtherOptions, setShowOtherOptions] = useState(false); + + // Identify devnet by network name + const isDevnet = targetNetwork.network === "devnet"; + + // Split connectors into main and other options for devnet + let mainConnectors = connectors; + let otherConnectors: typeof connectors = []; + if (isDevnet) { + mainConnectors = connectors.filter((c) => c.id === "burner-wallet"); + otherConnectors = connectors.filter((c) => c.id !== "burner-wallet"); + } const handleCloseModal = () => { if (modalRef.current) modalRef.current.checked = false; @@ -81,10 +95,17 @@ const ConnectModal = () => { <>

- {isBurnerWallet ? "Choose account" : "Connect a Wallet"} + {isBurnerWallet + ? "Choose account" + : showOtherOptions + ? "Other Wallet Options" + : "Connect a Wallet"}