diff --git a/.env.example b/.env.example deleted file mode 100644 index 0ec5e11..0000000 --- a/.env.example +++ /dev/null @@ -1 +0,0 @@ -NEXT_PUBLIC_APP_ROOT = "/" \ No newline at end of file diff --git a/.github/workflows/nextjs.yml b/.github/workflows/nextjs.yml index 1ecb1c4..c2f53ff 100644 --- a/.github/workflows/nextjs.yml +++ b/.github/workflows/nextjs.yml @@ -77,6 +77,7 @@ jobs: env: NEXT_PUBLIC_APP_ROOT: ${{ vars.NEXT_PUBLIC_APP_ROOT }} NEXT_PUBLIC_CHAIN_ID: ${{ vars.NEXT_PUBLIC_CHAIN_ID }} + NEXT_PUBLIC_FUND_MANAGER_ADDR: ${{ vars.NEXT_PUBLIC_FUND_MANAGER_ADDR }} run: | echo "Chain is ${{ vars.NEXT_PUBLIC_CHAIN_ID }}" ${{ steps.detect-package-manager.outputs.runner }} next build diff --git a/contracts/src/constants.cairo b/contracts/src/constants.cairo index a6a44ce..0eef46e 100644 --- a/contracts/src/constants.cairo +++ b/contracts/src/constants.cairo @@ -1,3 +1,5 @@ pub mod funds; pub mod donator; pub mod errors; +pub mod starknet; +pub mod fund_manager; diff --git a/contracts/src/constants/fund_manager.cairo b/contracts/src/constants/fund_manager.cairo new file mode 100644 index 0000000..2d06e6a --- /dev/null +++ b/contracts/src/constants/fund_manager.cairo @@ -0,0 +1 @@ +pub mod fund_manager_constants; diff --git a/contracts/src/constants/funds/fund_manager_constants.cairo b/contracts/src/constants/fund_manager/fund_manager_constants.cairo similarity index 100% rename from contracts/src/constants/funds/fund_manager_constants.cairo rename to contracts/src/constants/fund_manager/fund_manager_constants.cairo diff --git a/contracts/src/constants/funds.cairo b/contracts/src/constants/funds.cairo index 77c459e..bb8203a 100644 --- a/contracts/src/constants/funds.cairo +++ b/contracts/src/constants/funds.cairo @@ -1,4 +1,2 @@ -pub mod state_constants; pub mod fund_constants; -pub mod fund_manager_constants; -pub mod starknet_constants; + diff --git a/contracts/src/constants/funds/fund_constants.cairo b/contracts/src/constants/funds/fund_constants.cairo index afc1cb2..d9baf4d 100644 --- a/contracts/src/constants/funds/fund_constants.cairo +++ b/contracts/src/constants/funds/fund_constants.cairo @@ -7,3 +7,20 @@ pub mod FundConstants { pub const INITIAL_GOAL: u256 = 0; pub const MINIMUM_GOAL: u256 = 500; } +// ************************************************************************* +// FUND TYPES CONSTANTS +// ************************************************************************* +pub mod FundTypeConstants { + pub const PROJECT: u8 = 1; + pub const CHARITY: u8 = 2; +} +// ************************************************************************* +// FUND STATES CONSTANTS +// ************************************************************************* +pub mod FundStates { + pub const INNACTIVE: u8 = 0; + pub const RECOLLECTING_VOTES: u8 = 1; + pub const RECOLLECTING_DONATIONS: u8 = 2; + pub const CLOSED: u8 = 3; + pub const WITHDRAW: u8 = 4; +} diff --git a/contracts/src/constants/funds/state_constants.cairo b/contracts/src/constants/funds/state_constants.cairo deleted file mode 100644 index 0c04d65..0000000 --- a/contracts/src/constants/funds/state_constants.cairo +++ /dev/null @@ -1,10 +0,0 @@ -// ************************************************************************* -// STATES -// ************************************************************************* -pub mod FundStates { - pub const INNACTIVE: u8 = 0; - pub const RECOLLECTING_VOTES: u8 = 1; - pub const RECOLLECTING_DONATIONS: u8 = 2; - pub const CLOSED: u8 = 3; - pub const WITHDRAW: u8 = 4; -} diff --git a/contracts/src/constants/starknet.cairo b/contracts/src/constants/starknet.cairo new file mode 100644 index 0000000..640dcad --- /dev/null +++ b/contracts/src/constants/starknet.cairo @@ -0,0 +1 @@ +pub mod starknet_constants; diff --git a/contracts/src/constants/funds/starknet_constants.cairo b/contracts/src/constants/starknet/starknet_constants.cairo similarity index 100% rename from contracts/src/constants/funds/starknet_constants.cairo rename to contracts/src/constants/starknet/starknet_constants.cairo diff --git a/contracts/src/donatorManager.cairo b/contracts/src/donator_manager.cairo similarity index 100% rename from contracts/src/donatorManager.cairo rename to contracts/src/donator_manager.cairo diff --git a/contracts/src/fund.cairo b/contracts/src/fund.cairo index f282ff6..ee57cb9 100644 --- a/contracts/src/fund.cairo +++ b/contracts/src/fund.cairo @@ -4,6 +4,7 @@ use starknet::ContractAddress; pub trait IFund { fn get_id(self: @TContractState) -> u128; fn get_owner(self: @TContractState) -> ContractAddress; + fn is_owner(self: @TContractState, caller: ContractAddress) -> bool; fn set_name(ref self: TContractState, name: ByteArray); fn get_name(self: @TContractState) -> ByteArray; fn set_reason(ref self: TContractState, reason: ByteArray); @@ -16,12 +17,14 @@ pub trait IFund { fn get_current_goal_state(self: @TContractState) -> u256; fn set_state(ref self: TContractState, state: u8); fn get_state(self: @TContractState) -> u8; - fn get_voter(self: @TContractState) -> u32; + fn get_voter(self: @TContractState, user: ContractAddress) -> u32; fn withdraw(ref self: TContractState); fn set_evidence_link(ref self: TContractState, evidence: ByteArray); fn get_evidence_link(self: @TContractState) -> ByteArray; fn set_contact_handle(ref self: TContractState, contact_handle: ByteArray); fn get_contact_handle(self: @TContractState) -> ByteArray; + fn set_type(ref self: TContractState, fund_type: u8); + fn get_type(self: @TContractState) -> u8; } #[starknet::contract] @@ -34,11 +37,10 @@ pub mod Fund { use starknet::contract_address_const; use starknet::get_contract_address; use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; - use gostarkme::constants::{funds::{state_constants::FundStates},}; - use gostarkme::constants::{ - funds::{fund_constants::FundConstants, fund_manager_constants::FundManagerConstants}, - }; - use gostarkme::constants::{funds::{starknet_constants::StarknetConstants},}; + use gostarkme::constants::{funds::{fund_constants::FundStates},}; + use gostarkme::constants::{funds::{fund_constants::FundConstants},}; + use gostarkme::constants::{fund_manager::{fund_manager_constants::FundManagerConstants},}; + use gostarkme::constants::{starknet::{starknet_constants::StarknetConstants},}; // ************************************************************************* // EVENTS @@ -89,7 +91,8 @@ pub mod Fund { goal: u256, state: u8, evidence_link: ByteArray, - contact_handle: ByteArray + contact_handle: ByteArray, + fund_type: u8, } // ************************************************************************* @@ -104,7 +107,8 @@ pub mod Fund { goal: u256, evidence_link: ByteArray, contact_handle: ByteArray, - reason: ByteArray + reason: ByteArray, + fund_type: u8, ) { self.id.write(id); self.owner.write(owner); @@ -115,6 +119,7 @@ pub mod Fund { self.state.write(FundStates::RECOLLECTING_VOTES); self.evidence_link.write(evidence_link); self.contact_handle.write(contact_handle); + self.fund_type.write(fund_type); } // ************************************************************************* @@ -128,6 +133,9 @@ pub mod Fund { fn get_owner(self: @ContractState) -> ContractAddress { return self.owner.read(); } + fn is_owner(self: @ContractState, caller: ContractAddress) -> bool { + return (self.owner.read() == caller); + } fn set_name(ref self: ContractState, name: ByteArray) { let caller = get_caller_address(); let valid_address_1 = contract_address_const::(); @@ -226,8 +234,9 @@ pub mod Fund { fn get_state(self: @ContractState) -> u8 { return self.state.read(); } - fn get_voter(self: @ContractState) -> u32 { - return self.voters.read(get_caller_address()); + fn get_voter(self: @ContractState, user: ContractAddress) -> u32 { + let voter = self.voters.read(user); + return voter; } fn withdraw(ref self: ContractState) { let caller = get_caller_address(); @@ -282,6 +291,19 @@ pub mod Fund { fn get_contact_handle(self: @ContractState) -> ByteArray { return self.contact_handle.read(); } + fn set_type(ref self: ContractState, fund_type: u8) { + let caller = get_caller_address(); + let valid_address_1 = contract_address_const::(); + let valid_address_2 = contract_address_const::(); + assert!( + valid_address_1 == caller || valid_address_2 == caller, + "Only Admins can change the fund type." + ); + self.fund_type.write(fund_type); + } + fn get_type(self: @ContractState) -> u8 { + return self.fund_type.read(); + } } // ************************************************************************* // INTERNALS diff --git a/contracts/src/fundManager.cairo b/contracts/src/fund_manager.cairo similarity index 97% rename from contracts/src/fundManager.cairo rename to contracts/src/fund_manager.cairo index c1a7202..2ea65b8 100755 --- a/contracts/src/fundManager.cairo +++ b/contracts/src/fund_manager.cairo @@ -9,7 +9,8 @@ pub trait IFundManager { goal: u256, evidence_link: ByteArray, contact_handle: ByteArray, - reason: ByteArray + reason: ByteArray, + fund_type: u8, ); fn get_current_id(self: @TContractState) -> u128; fn get_fund(self: @TContractState, id: u128) -> ContractAddress; @@ -85,6 +86,7 @@ pub mod FundManager { evidence_link: ByteArray, contact_handle: ByteArray, reason: ByteArray, + fund_type: u8 ) { assert(goal >= FundConstants::MINIMUM_GOAL, 'Goal must be at least 500'); let mut call_data: Array = array![]; @@ -95,6 +97,7 @@ pub mod FundManager { Serde::serialize(@evidence_link, ref call_data); Serde::serialize(@contact_handle, ref call_data); Serde::serialize(@reason, ref call_data); + Serde::serialize(@fund_type, ref call_data); let (new_fund_address, _) = deploy_syscall( self.fund_class_hash.read(), 12345, call_data.span(), false ) diff --git a/contracts/src/lib.cairo b/contracts/src/lib.cairo index a593d2c..45d8e1c 100644 --- a/contracts/src/lib.cairo +++ b/contracts/src/lib.cairo @@ -1,7 +1,7 @@ pub mod constants; // FUND pub mod fund; -pub mod fundManager; +pub mod fund_manager; // DONATOR -pub mod donatorManager; +pub mod donator_manager; pub mod donator; diff --git a/contracts/tests/test_donator.cairo b/contracts/tests/test_donator.cairo index da6803b..64090a6 100644 --- a/contracts/tests/test_donator.cairo +++ b/contracts/tests/test_donator.cairo @@ -76,3 +76,4 @@ fn test_update_donator_values() { assert(total_stark_donations == 11, 'Invalid total stark donations'); assert(max_stark_donations_to_next_level == 20, 'Invalid total stark donations'); } + diff --git a/contracts/tests/test_donator_manager.cairo b/contracts/tests/test_donator_manager.cairo index 78dc4a9..f9ec8d4 100644 --- a/contracts/tests/test_donator_manager.cairo +++ b/contracts/tests/test_donator_manager.cairo @@ -12,7 +12,7 @@ use snforge_std::{ use openzeppelin::utils::serde::SerializedAppend; -use gostarkme::donatorManager::{ +use gostarkme::donator_manager::{ DonatorManager, IDonatorManagerDispatcher, IDonatorManagerDispatcherTrait }; diff --git a/contracts/tests/test_fund.cairo b/contracts/tests/test_fund.cairo index 4e37982..3172255 100644 --- a/contracts/tests/test_fund.cairo +++ b/contracts/tests/test_fund.cairo @@ -16,9 +16,10 @@ use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTr use gostarkme::fund::Fund; use gostarkme::fund::IFundDispatcher; use gostarkme::fund::IFundDispatcherTrait; -use gostarkme::constants::{funds::{fund_manager_constants::FundManagerConstants},}; -use gostarkme::constants::{funds::{state_constants::FundStates},}; -use gostarkme::constants::{funds::{starknet_constants::StarknetConstants},}; +use gostarkme::constants::{fund_manager::{fund_manager_constants::FundManagerConstants},}; +use gostarkme::constants::{funds::{fund_constants::FundStates},}; +use gostarkme::constants::{funds::{fund_constants::FundTypeConstants},}; +use gostarkme::constants::{starknet::{starknet_constants::StarknetConstants},}; const ONE_E18: u256 = 1000000000000000000_u256; fn ID() -> u128 { @@ -73,6 +74,8 @@ fn _setup_() -> ContractAddress { calldata.append_serde(EVIDENCE_LINK_1()); calldata.append_serde(CONTACT_HANDLE_1()); calldata.append_serde(REASON_1()); + calldata.append_serde(FundTypeConstants::PROJECT); + let (contract_address, _) = contract.deploy(@calldata).unwrap(); contract_address } @@ -223,12 +226,12 @@ fn test_set_goal_unauthorized() { fn test_receive_vote_successful() { let contract_address = _setup_(); let dispatcher = IFundDispatcher { contract_address }; + start_cheat_caller_address_global(OTHER_USER()); dispatcher.receive_vote(); - let me = dispatcher.get_voter(); - // Owner vote, fund have one vote - assert(me == 1, 'Owner is not in the voters'); - let votes = dispatcher.get_up_votes(); - assert(votes == 1, 'Vote unuseccessful'); + let other_user_votes = dispatcher.get_voter(OTHER_USER()); + assert(other_user_votes == 1, 'Other user is not in the voters'); + let fund_votes = dispatcher.get_up_votes(); + assert(fund_votes == 1, 'Vote unuseccessful'); } #[test] @@ -236,13 +239,14 @@ fn test_receive_vote_successful() { fn test_receive_vote_unsuccessful_double_vote() { let contract_address = _setup_(); let dispatcher = IFundDispatcher { contract_address }; + start_cheat_caller_address_global(OTHER_USER()); dispatcher.receive_vote(); - let me = dispatcher.get_voter(); - // Owner vote, fund have one vote - assert(me == 1, 'Owner is not in the voters'); + let other_user_votes = dispatcher.get_voter(OTHER_USER()); + // User vote, fund have one vote + assert(other_user_votes == 1, 'Owner is not in the voters'); let votes = dispatcher.get_up_votes(); assert(votes == 1, 'Vote unuseccessful'); - // Owner vote, second time + // User vote, second time dispatcher.receive_vote(); } @@ -602,3 +606,18 @@ fn test_set_contact_handle_success() { assert(reverted_contact_handle == CONTACT_HANDLE_1(), ' revert ') } +#[test] +fn test_set_type() { + let contract_address = _setup_(); + let dispatcher = IFundDispatcher { contract_address }; + let current_type = dispatcher.get_type(); + assert(current_type == FundTypeConstants::PROJECT, 'Invalid type'); + start_cheat_caller_address(contract_address, VALID_ADDRESS_1()); + dispatcher.set_type(FundTypeConstants::CHARITY); + let new_type = dispatcher.get_type(); + assert(new_type == FundTypeConstants::CHARITY, 'Set type method not working'); + start_cheat_caller_address(contract_address, VALID_ADDRESS_2()); + dispatcher.set_type(FundTypeConstants::PROJECT); + let new_type = dispatcher.get_type(); + assert(new_type == FundTypeConstants::PROJECT, 'Set type method not working'); +} diff --git a/contracts/tests/test_fund_manager.cairo b/contracts/tests/test_fund_manager.cairo index 98be44d..9b1d814 100755 --- a/contracts/tests/test_fund_manager.cairo +++ b/contracts/tests/test_fund_manager.cairo @@ -12,9 +12,10 @@ use snforge_std::{ use openzeppelin::utils::serde::SerializedAppend; -use gostarkme::fundManager::IFundManagerDispatcher; -use gostarkme::fundManager::IFundManagerDispatcherTrait; -use gostarkme::fundManager::FundManager; +use gostarkme::fund_manager::IFundManagerDispatcher; +use gostarkme::fund_manager::IFundManagerDispatcherTrait; +use gostarkme::fund_manager::FundManager; +use gostarkme::constants::{funds::{fund_constants::FundTypeConstants},}; fn ID() -> u128 { 1 @@ -54,6 +55,7 @@ fn _setup_() -> (ContractAddress, ClassHash) { fund_calldata.append_serde(EVIDENCE_LINK()); fund_calldata.append_serde(CONTACT_HANDLE()); fund_calldata.append_serde(REASON()); + fund_calldata.append_serde(FundTypeConstants::PROJECT); let (fund_contract_address, _) = fund.deploy(@fund_calldata).unwrap(); let fund_class_hash = get_class_hash(fund_contract_address); @@ -87,7 +89,10 @@ fn test_new_fund() { start_cheat_caller_address_global(OWNER()); let (contract_address, fund_class_hash) = _setup_(); let fund_manager_contract = IFundManagerDispatcher { contract_address }; - fund_manager_contract.new_fund(NAME(), GOAL(), EVIDENCE_LINK(), CONTACT_HANDLE(), REASON()); + fund_manager_contract + .new_fund( + NAME(), GOAL(), EVIDENCE_LINK(), CONTACT_HANDLE(), REASON(), FundTypeConstants::PROJECT + ); let expected_fund_class_hash = get_class_hash(fund_manager_contract.get_fund(1)); let current_id = fund_manager_contract.get_current_id(); assert(expected_fund_class_hash == fund_class_hash, 'Invalid fund address'); @@ -100,7 +105,15 @@ fn test_new_fund_bad_goal() { start_cheat_caller_address_global(OWNER()); let (contract_address, _) = _setup_(); let fund_manager_contract = IFundManagerDispatcher { contract_address }; - fund_manager_contract.new_fund(NAME(), BAD_GOAL(), EVIDENCE_LINK(), CONTACT_HANDLE(), REASON()); + fund_manager_contract + .new_fund( + NAME(), + BAD_GOAL(), + EVIDENCE_LINK(), + CONTACT_HANDLE(), + REASON(), + FundTypeConstants::PROJECT + ); } #[test] @@ -113,7 +126,10 @@ fn test_fund_deployed_event() { let mut spy = spy_events(); let current_id = fund_manager_contract.get_current_id(); - fund_manager_contract.new_fund(NAME(), GOAL(), EVIDENCE_LINK(), CONTACT_HANDLE(), REASON()); + fund_manager_contract + .new_fund( + NAME(), GOAL(), EVIDENCE_LINK(), CONTACT_HANDLE(), REASON(), FundTypeConstants::PROJECT + ); let expected_fund_class_hash = fund_manager_contract.get_fund(1); diff --git a/frontend/gostarkme-web/.env.example b/frontend/gostarkme-web/.env.example deleted file mode 100644 index bfd3bb9..0000000 --- a/frontend/gostarkme-web/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -NEXT_PUBLIC_APP_ROOT = "/" -NEXT_PUBLIC_CHAIN_ID = "SN_SEPOLIA" \ No newline at end of file diff --git a/frontend/gostarkme-web/app/app/page.tsx b/frontend/gostarkme-web/app/app/page.tsx index 1bb2deb..6001c44 100644 --- a/frontend/gostarkme-web/app/app/page.tsx +++ b/frontend/gostarkme-web/app/app/page.tsx @@ -19,30 +19,32 @@ const Dashboard = () => { async function getFunds() { const fundManagerContract = new Contract(fundManager, FUND_MANAGER_ADDR); - const id = await fundManagerContract.getCurrentId(); + const id = await fundManagerContract.get_current_id(); let fundings = []; for (let i = 1; i < id; i++) { // GET FUND ADDRESS - let fundaddr = await fundManagerContract.getFund(i); + let fundaddr = await fundManagerContract.get_fund(i); fundaddr = "0x" + fundaddr.toString(16); const fundContract = new Contract(fundAbi, fundaddr); // GET FUND STATE - let state = await fundContract.getState(); + let state = await fundContract.get_state(); if (state == 4 || state == 0) { continue; } // GET FUND NAME - let name = await fundContract.getName(); + let name = await fundContract.get_name(); // GET FUND DESCRIPTION - let desc = await fundContract.getReason(); + let desc = await fundContract.get_reason(); let desclen = desc.length; if (desclen > 50) { desc = desc.substring(0, 50) + "..."; } // GET FUND ID - let fund_id = await fundContract.getId(); + let fund_id = await fundContract.get_id(); + // GET FUND TYPE + let fund_type = await fundContract.get_type(); fundings.push({ - type: "Project", + type: Number(BigInt(fund_type)) === 1 ? "Project" : "Charity", title: name, description: desc, fund_id: fund_id.toString(), diff --git a/frontend/gostarkme-web/components/dashboard/fundCard.tsx b/frontend/gostarkme-web/components/dashboard/fundCard.tsx index 5b4ad4d..a61690d 100644 --- a/frontend/gostarkme-web/components/dashboard/fundCard.tsx +++ b/frontend/gostarkme-web/components/dashboard/fundCard.tsx @@ -11,7 +11,7 @@ interface FundCardProps { type: string; title: string; description: string; - fund_id: string + fund_id: string; }; index: number; } diff --git a/frontend/gostarkme-web/components/modules/Fund/Fund.tsx b/frontend/gostarkme-web/components/modules/Fund/Fund.tsx index 6e3c3e4..b723ac7 100644 --- a/frontend/gostarkme-web/components/modules/Fund/Fund.tsx +++ b/frontend/gostarkme-web/components/modules/Fund/Fund.tsx @@ -29,29 +29,28 @@ const Fund = () => { const clickedFund = useAtomValue(clickedFundState); async function getDetails() { - let addr = await fundManagerContract.getFund(clickedFund?.id); + let addr = await fundManagerContract.get_fund(clickedFund?.id); addr = "0x" + addr.toString(16); const fundContract = new Contract(fundAbi, addr, wallet?.account); try { // Fetch fund details - let name = await fundContract.getName(); - let desc = await fundContract.getReason(); + let name = await fundContract.get_name(); + let desc = await fundContract.get_reason(); if (desc == " ") { desc = "No description provided"; } - let state = await fundContract.getState(); + let state = await fundContract.get_state(); let currentBalance = await fundContract.get_current_goal_state(); currentBalance = BigInt(currentBalance) / BigInt(10 ** 18); - let goal = await fundContract.getGoal(); + let goal = await fundContract.get_goal(); goal = BigInt(goal) / BigInt(10 ** 18); - let upVotes = await fundContract.getUpVotes(); + let upVotes = await fundContract.get_up_votes(); let evidenceLink = await fundContract.get_evidence_link(); let contactHandle = await fundContract.get_contact_handle(); - - console.log(wallet?.account?.address.toLowerCase()); // Fetch owner - const owner = (await fundContract.getOwner()).toString(); - setIsOwner(owner.toLowerCase() === wallet?.account?.address.toLowerCase()); + setIsOwner(await fundContract.is_owner(wallet?.account.address)); + // USER VOTED? + let voted = await fundContract.get_voter(wallet != undefined ? wallet?.account.address : "0x0000000000"); setFund({ name: name, @@ -63,6 +62,7 @@ const Fund = () => { addr: addr, evidenceLink: evidenceLink, contactHandle: contactHandle, + voted: voted }); } catch (error) { console.error("Error fetching fund details:", error); @@ -102,23 +102,23 @@ const Fund = () => { {Number(fund.state) === 0 &&

Fund is currently inactive.

} {Number(fund.state) === 1 && ( )} {Number(fund.state) === 2 && ( <> - {!isOwner && ( - )} )} {Number(fund.state) === 3 && isOwner && ( diff --git a/frontend/gostarkme-web/components/modules/Fund/FundDonate.tsx b/frontend/gostarkme-web/components/modules/Fund/FundDonate.tsx index 472f4f4..bb940c8 100644 --- a/frontend/gostarkme-web/components/modules/Fund/FundDonate.tsx +++ b/frontend/gostarkme-web/components/modules/Fund/FundDonate.tsx @@ -2,12 +2,15 @@ import { calculatePorcentage } from "@/app/utils"; import ProgressBar from "@/components/ui/ProgressBar"; +import ShareXButton from "@/components/ui/ShareOnX"; import { provider } from "@/constants"; import { addrSTRK } from "@/contracts/addresses"; +import { activeChainId } from "@/state/activeChain"; import { walletStarknetkitLatestAtom, } from "@/state/connectedWallet"; -import { useAtomValue } from "jotai"; +import { latestTxAtom } from "@/state/latestTx"; +import { useAtom, useAtomValue } from "jotai"; import Image, { StaticImageData } from "next/image"; import { useState } from "react"; import { CallData, cairo } from "starknet"; @@ -16,23 +19,23 @@ interface FundDonateProps { currentBalance: number; goal: number; addr: string; + name: string; icon?: StaticImageData; } -const FundDonate = ({ currentBalance, goal, addr, icon }: FundDonateProps) => { +const FundDonate = ({ currentBalance, goal, addr, name, icon }: FundDonateProps) => { const [amount, setAmount] = useState(""); const [error, setError] = useState(""); const [isLoading, setIsLoading] = useState(false); const [localBalance, setLocalBalance] = useState(currentBalance); + const [showSuccessPopup, setShowSuccessPopup] = useState(false); + const [latestTx, setLatestTx] = useAtom(latestTxAtom); + const [isDonating, setIsDonating] = useState(false); const wallet = useAtomValue(walletStarknetkitLatestAtom); - const [network, setNetwork] = useState(wallet?.chainId); + const chainId = useAtomValue(activeChainId); const networkEnvironment = process.env.NEXT_PUBLIC_CHAIN_ID; const progress = calculatePorcentage(localBalance, goal); - - const handleNetwork = (chainId?: string, accounts?: string[]) => { - setNetwork(wallet?.chainId); - }; - wallet?.on('networkChanged', handleNetwork); + const [donationMessage, setDonationMessage] = useState(''); const handleAmountChange = (e: React.ChangeEvent) => { const value = e.target.value === "" ? "" : Number(e.target.value); @@ -65,6 +68,7 @@ const FundDonate = ({ currentBalance, goal, addr, icon }: FundDonateProps) => { setError(""); setIsLoading(true); + setIsDonating(true); try { const tx = await wallet?.account.execute([ @@ -92,7 +96,10 @@ const FundDonate = ({ currentBalance, goal, addr, icon }: FundDonateProps) => { if (typeof amount === 'number') { setLocalBalance(prev => Number(prev) + amount); } + setDonationMessage(`πŸ™Œ Supporting ${name} on Go Stark Me with ${amount} $STRK! Donate now: https://web3wagers.github.io/gostarkme/ πŸ’ͺ @undefined_org_ @Starknet`); setAmount(""); + setLatestTx(tx.transaction_hash); + setShowSuccessPopup(true); setError("Transaction successful!"); setTimeout(() => { setError(""); @@ -103,8 +110,11 @@ const FundDonate = ({ currentBalance, goal, addr, icon }: FundDonateProps) => { } } catch (error: any) { setError(error.message || "Transaction failed. Please try again."); + setIsLoading(false); + setIsDonating(false); } finally { setIsLoading(false); + setIsDonating(false); } }; @@ -135,15 +145,15 @@ const FundDonate = ({ currentBalance, goal, addr, icon }: FundDonateProps) => { )}
- {wallet && network !== networkEnvironment && ( + {wallet && chainId !== networkEnvironment && (

Your wallet is currently connected to the wrong network. Please switch to {networkEnvironment} to continue. @@ -155,6 +165,28 @@ const FundDonate = ({ currentBalance, goal, addr, icon }: FundDonateProps) => {

)}
+ + {showSuccessPopup && ( +
+
+ +

Success

+

Your donation was received, take a look at the transaction here.

+

Share your contribution via X to tell everyone how cool you are

+ +
+
+ )} ); }; diff --git a/frontend/gostarkme-web/components/modules/Fund/FundVote.tsx b/frontend/gostarkme-web/components/modules/Fund/FundVote.tsx index e89a441..d533ab2 100644 --- a/frontend/gostarkme-web/components/modules/Fund/FundVote.tsx +++ b/frontend/gostarkme-web/components/modules/Fund/FundVote.tsx @@ -1,92 +1,83 @@ import { calculatePorcentage } from "@/app/utils"; import { Button } from "@/components/ui/Button"; import ProgressBar from "@/components/ui/ProgressBar"; -import { fundAbi } from "@/contracts/abis/fund"; import { walletStarknetkitLatestAtom, } from "@/state/connectedWallet"; import { latestTxAtom } from "@/state/latestTx"; -import { useAtomValue, useSetAtom } from "jotai"; -import { Contract } from "starknet"; -import { useRouter } from "next/navigation"; -import { useState, useEffect } from "react"; +import { useAtom, useAtomValue } from "jotai"; +import { useState } from "react"; +import ShareXButton from "@/components/ui/ShareOnX"; +import { provider } from "@/constants"; +import { CallData } from "starknet"; +import { activeChainId } from "@/state/activeChain"; interface FundVoteProps { + name: String, upVotes: number, upVotesNeeded: number, addr: string, + voted: any, setLoading: (load: boolean) => void, - getDetails: () => void, } -export const FundVote = ({ upVotes, upVotesNeeded, addr, setLoading }: FundVoteProps) => { - +export const FundVote = ({ name, upVotes, upVotesNeeded, addr, voted, setLoading }: FundVoteProps) => { const wallet = useAtomValue(walletStarknetkitLatestAtom); - const [network, setNetwork] = useState(wallet?.chainId); + const chainId = useAtomValue(activeChainId); const networkEnvironment = process.env.NEXT_PUBLIC_CHAIN_ID; - const progress = calculatePorcentage(upVotes, upVotesNeeded); - - const setLatestTx = useSetAtom(latestTxAtom); - - const router = useRouter(); + const [progress, setProgress] = useState(calculatePorcentage(upVotes, upVotesNeeded)); + const [currentUpvotes, setCurrentUpvotes] = useState(upVotes); + const voteMessage = `πŸ—³οΈ Voted for ${name} on Go Stark Me! Support now: https://web3wagers.github.io/gostarkme/ πŸ™ŒπŸ’« @undefined_org_ @Starknet`; - const [isChecking, setIsChecking] = useState(true); - const [voteStatus, setVoteStatus] = useState(false); const [isVoting, setIsVoting] = useState(false); + const [showSuccessPopup, setShowSuccessPopup] = useState(false); + const [latestTx, setLatestTx] = useAtom(latestTxAtom); + const [canVote, setCanVote] = useState((voted != BigInt(0) ? false : true)); - const handleNetwork = (chainId?: string, accounts?: string[]) => { - setNetwork(wallet?.chainId); + const waitForTransaction = async (hash: string) => { + try { + await provider.waitForTransaction(hash); + return true; + } catch (error) { + console.error("Error waiting for transaction:", error); + return false; + } }; - wallet?.on('networkChanged', handleNetwork); - useEffect(() => { - const checkVoteStatus = async () => { - if (!wallet?.account) { - setIsChecking(false); - return; - } + const handleVoteClick = async (e: React.MouseEvent) => { + e.preventDefault(); - setIsChecking(true); - try { - const fundContract = new Contract(fundAbi, addr, wallet.account); + setIsVoting(true); - try { - await fundContract.estimate('receiveVote'); - setVoteStatus(false); - } catch (error: any) { - if (error?.toString().includes('User already voted')) { - setVoteStatus(true); - } - } - } catch (error) { - console.error("Contract interaction error:", error); - } finally { - setIsChecking(false); - } - }; + try { + const tx = await wallet?.account.execute([ + { + contractAddress: addr, + entrypoint: 'receive_vote', + calldata: CallData.compile({ + }), + }, + ]); - checkVoteStatus(); - }, [wallet?.account, addr]); + if (tx) { + const isConfirmed = await waitForTransaction(tx.transaction_hash); - const handleVote = async () => { - if (!wallet?.account) return; - setLoading(true); - setIsVoting(true); - try { - const fundContract = new Contract(fundAbi, addr, wallet.account); - const resp = await fundContract.invoke('receiveVote'); - setLatestTx({ txHash: resp.transaction_hash, type: "vote" }); - setVoteStatus(true); - router.push("/app/confirmation"); - } catch (error: any) { - console.error("Vote failed:", error); - if (error?.toString().includes('User already voted')) { - console.log("User has already voted"); - setVoteStatus(true); + if (isConfirmed) { + setLatestTx(tx.transaction_hash); + setCanVote(false); + setShowSuccessPopup(true); + setCurrentUpvotes(prev => Number(BigInt(prev) + BigInt(1))); + setProgress(calculatePorcentage(Number(BigInt(upVotes) + BigInt(1)), Number(upVotesNeeded))) + setTimeout(() => { + }, 3000); + } else { + console.log('tx not successfull') + } } + } catch (error: any) { + console.log(error.message || "Transaction failed. Please try again."); } finally { - setLoading(false); setIsVoting(false); } }; @@ -95,20 +86,20 @@ export const FundVote = ({ upVotes, upVotesNeeded, addr, setLoading }: FundVoteP
-

{upVotes.toString()} / {upVotesNeeded.toString()}

+

{currentUpvotes.toString()} / {upVotesNeeded.toString()}

🌟

- {isChecking ? ( //if isChecking is true render a button that checks the vote status + {isVoting ? (
- ) : wallet ? ( // Check if a wallet is connected by evaluating 'wallet' condition - voteStatus ? ( //if voteStatus is true button is disabled + ) : wallet ? ( + !canVote ? (
+ {chainId !== networkEnvironment && (

Your wallet is currently connected to the wrong network. Please switch to {networkEnvironment} to continue. @@ -137,7 +126,7 @@ export const FundVote = ({ upVotes, upVotesNeeded, addr, setLoading }: FundVoteP )}

) - ) : ( // If the wallet is not connected, render a disabled vote button with instructions + ) : (
)} + {showSuccessPopup && ( +
+
+ +

Success

+

Your vote was submitted, take a look at the transaction here.

+

Share your contribution via X to tell everyone how cool you are

+ +
+
+ )}
); }; \ No newline at end of file diff --git a/frontend/gostarkme-web/components/modules/confirmation/Confirmation.tsx b/frontend/gostarkme-web/components/modules/confirmation/Confirmation.tsx index 563c5f5..545ad3f 100644 --- a/frontend/gostarkme-web/components/modules/confirmation/Confirmation.tsx +++ b/frontend/gostarkme-web/components/modules/confirmation/Confirmation.tsx @@ -1,23 +1,19 @@ 'use client'; -import React, { useEffect } from "react"; +import React from "react"; import CreationConfirmation from "./CreationConfirmation"; -import VoteConfirmation from "./VoteConfirmation"; import DonationConfirmation from "./DonationConfirmation"; -import { useAtom, useAtomValue } from "jotai"; +import { useAtomValue } from "jotai"; import { latestTxAtom } from "@/state/latestTx"; import Navbar from "@/components/ui/Navbar"; import { navItems } from "@/constants"; import { clickedFundState } from "@/state/nFunds"; -import { walletStarknetkitLatestAtom } from "@/state/connectedWallet"; import WithdrawConfirmation from "./WithdrawConfirmation"; const Confirmation = () => { const tx = useAtomValue(latestTxAtom); const actualFund = useAtomValue(clickedFundState); - const voteMessage = ` πŸ—³οΈ Just cast my vote for an amazing cause called ${actualFund?.name} on Go Stark Me! This fund needs more votes to start raising fundsβ€”every vote counts! Let’s support projects that make a difference at https://web3wagers.github.io/gostarkme/ @undefined_org_ πŸ™ŒπŸ’« #GoStarkMe #Starknet #CommunityPower`; - const donationMessage = `πŸ™Œ Proud to support ${actualFund?.name} on Go Stark Me! Donations make a difference. πŸ’ͺ Go ahead and donate at https://web3wagers.github.io/gostarkme/ @undefined_org_ #Starknet #GoStarkMe #Web3Wagers`; - const newFundMessage = `πŸš€ Just launched a new fund on Go Stark Me called ${actualFund?.name}! I’m raising support for an important cause, and every contribution makes a difference. Join me in making an impact at https://web3wagers.github.io/gostarkme/! πŸ’ͺ🌍 Check it out on @undefined_org_ #GoStarkMe #Starknet #BlockchainForGood`; - const withdrawnMessage = `πŸŽ‰ We did it! The goal for ${actualFund?.name} on Go Stark Me has been reached, and funds have been successfully withdrawn! πŸ™Œ Huge thanks to everyone who contributed and made this possible. Let’s keep making an impact! 🌍πŸ’ͺ Check it out at https://web3wagers.github.io/gostarkme/ #GoStarkMe #Starknet #CommunitySuccess`; + const newFundMessage = `πŸš€ Launched a new fund on Go Stark Me: ${actualFund?.name}! Support this cause and make a difference: https://web3wagers.github.io/gostarkme/ πŸ’ͺ🌍 @undefined_org_ @Starknet`; + const withdrawnMessage = `πŸŽ‰ Goal reached for ${actualFund?.name} on Go Stark Me! Funds successfully withdrawnβ€”thank you to all who contributed! 🌍πŸ’ͺ https://web3wagers.github.io/gostarkme/ @undefined_org_ @Starknet`; return ( <> @@ -44,14 +40,6 @@ const Confirmation = () => { } - {tx?.type === "vote" && - - } - - {tx?.type === "donation" && - - } - {tx?.type === "withdrawn" && } diff --git a/frontend/gostarkme-web/components/modules/newfunding/FundingStep.tsx b/frontend/gostarkme-web/components/modules/newfunding/FundingStep.tsx index 688a189..5163b05 100644 --- a/frontend/gostarkme-web/components/modules/newfunding/FundingStep.tsx +++ b/frontend/gostarkme-web/components/modules/newfunding/FundingStep.tsx @@ -9,8 +9,10 @@ interface FundingStepProps { setEvidenceLink: (name: string) => void; contactHandle: string; setContactHandle: (name: string) => void; - errors: { fundingName: string; goal: string ;evidenceLink: string; contactHandle: string }; // Expecting errors as props - setErrors: React.Dispatch>; // Specify the type for setErrors + type: string; + setType: (name: string) => void; + errors: { fundingName: string; goal: string ;evidenceLink: string; contactHandle: string; type: string}; + setErrors: React.Dispatch>; } const FundingStep: React.FC = ({ @@ -22,6 +24,8 @@ const FundingStep: React.FC = ({ setEvidenceLink, contactHandle, setContactHandle, + type, + setType, errors, setErrors, }) => { @@ -67,9 +71,19 @@ const FundingStep: React.FC = ({ const newValue = e.target.value; setContactHandle(newValue); if (!newValue) { - setErrors((prev) => ({ ...prev, evidenceLink: 'Contact handle is required.' })); + setErrors((prev) => ({ ...prev, contactHandle: 'Contact handle is required.' })); } else { - setErrors((prev) => ({ ...prev, evidenceLink: '' })); + setErrors((prev) => ({ ...prev, contactHandle: '' })); + } + }; + + const handleType = (e: React.ChangeEvent) => { + const newValue = e.target.value; + setType(newValue); + if (!newValue) { + setErrors((prev) => ({ ...prev, type: 'Type is required.' })); + } else { + setErrors((prev) => ({ ...prev, type: '' })); } }; @@ -108,6 +122,16 @@ const FundingStep: React.FC = ({ className={`mt-4 p-2 pl-4 border rounded w-full placeholder:text-base ${errors.contactHandle ? 'border-red-500' : 'border-black'}`} required /> + {/* Error Messages */} {errors.fundingName && ( @@ -122,6 +146,9 @@ const FundingStep: React.FC = ({ {errors.contactHandle && (

{errors.contactHandle}

)} + {errors.type && ( +

{errors.type}

+ )} ); }; diff --git a/frontend/gostarkme-web/components/modules/newfunding/Stages.tsx b/frontend/gostarkme-web/components/modules/newfunding/Stages.tsx index 76da91d..a684603 100644 --- a/frontend/gostarkme-web/components/modules/newfunding/Stages.tsx +++ b/frontend/gostarkme-web/components/modules/newfunding/Stages.tsx @@ -16,9 +16,10 @@ const Stages = () => { const [fundingName, setFundingName] = useState(""); const [goal, setGoal] = useState(""); const [fundingDescription, setFundingDescription] = useState(""); - const [errors, setErrors] = useState({ fundingName: "", goal: "", evidenceLink: "", contactHandle: "" }); + const [errors, setErrors] = useState({ fundingName: "", goal: "", evidenceLink: "", contactHandle: "" ,type:""}); const [evidenceLink, setEvidenceLink] = useState(""); const [contactHandle, setContactHandle] = useState(""); + const [type, setType] = useState(""); const setLatesTx = useSetAtom(latestTxAtom); const setActualFund = useSetAtom(clickedFundState); @@ -27,7 +28,7 @@ const Stages = () => { const handleNextStep = () => { // Reset errors - setErrors({ fundingName: "", goal: "", evidenceLink: "", contactHandle: "" }); + setErrors({ fundingName: "", goal: "", evidenceLink: "", contactHandle: "" ,type:""}); // Validate fields let hasErrors = false; @@ -48,6 +49,10 @@ const Stages = () => { setErrors((prev) => ({ ...prev, contactHandle: "The contact handle is required." })); hasErrors = true; } + if (!type) { + setErrors((prev) => ({ ...prev, type: "The type is required." })); + hasErrors = true; + } } // If there are no errors, proceed to the next step @@ -65,8 +70,9 @@ const Stages = () => { }; async function newFund() { + const fundManagerContract = new Contract(fundManager, FUND_MANAGER_ADDR, wallet?.account); - fundManagerContract.newFund(fundingName, cairo.uint256(Number(goal) * Number(10) ** Number(18) ) , evidenceLink, contactHandle, fundingDescription) + fundManagerContract.new_fund(fundingName, cairo.uint256(Number(goal) * Number(10) ** Number(18) ) , evidenceLink, contactHandle, fundingDescription, Number(type) ) .then(async (resp: InvokeFunctionResponse) => { setLatesTx({ txHash: resp.transaction_hash, type: "newfund" }); setActualFund({id: 0, name: fundingName}); @@ -88,6 +94,8 @@ const Stages = () => { setEvidenceLink={setEvidenceLink} contactHandle={contactHandle} setContactHandle={setContactHandle} + type={type} + setType={setType} errors={errors} // Pass errors down setErrors={setErrors} // Pass setErrors down, /> diff --git a/frontend/gostarkme-web/components/ui/ConnectWalletButton.tsx b/frontend/gostarkme-web/components/ui/ConnectWalletButton.tsx index dc4ab78..65d4199 100644 --- a/frontend/gostarkme-web/components/ui/ConnectWalletButton.tsx +++ b/frontend/gostarkme-web/components/ui/ConnectWalletButton.tsx @@ -1,11 +1,18 @@ "use client"; import { ARGENT_WEBWALLET_URL, CHAIN_ID, provider } from "@/constants"; +import { activeChainId } from "@/state/activeChain"; import { walletStarknetkitLatestAtom } from "@/state/connectedWallet"; import { useAtom, useSetAtom } from "jotai"; import { connect, disconnect } from "starknetkit"; export default function WalletConnector() { const [wallet, setWallet] = useAtom(walletStarknetkitLatestAtom) + const [activeChain, setActiveChain] = useAtom(activeChainId); + + const handleNetwork = (chainId?: string, accounts?: string[]) => { + setActiveChain(wallet?.chainId); + }; + wallet?.on('networkChanged', handleNetwork); const handleConnect = async (event:any) => { try { @@ -21,7 +28,8 @@ export default function WalletConnector() { }, }) - setWallet(wallet) + setWallet(wallet); + setActiveChain(wallet?.chainId); } catch (e) { console.error(e) alert((e as any).message) diff --git a/frontend/gostarkme-web/constants/index.ts b/frontend/gostarkme-web/constants/index.ts index 7bf84a9..81f393b 100644 --- a/frontend/gostarkme-web/constants/index.ts +++ b/frontend/gostarkme-web/constants/index.ts @@ -28,7 +28,7 @@ export const ARGENT_WEBWALLET_URL = process.env.NEXT_PUBLIC_ARGENT_WEBWALLET_URL || "https://web.argent.xyz" export const FUND_MANAGER_ADDR = - "0x05c1701879e0322024c174f074cd4a279ed5b536ecaa2447240cec1958f8f8e2" + "0x04e75cce044fcb8012eacd7532dee521925a468301d58d09b866ccc43580e84a" export const navItems = [ // { label: 'My Profile', href: '/app/myprofile' }, diff --git a/frontend/gostarkme-web/contracts/abis/fund.ts b/frontend/gostarkme-web/contracts/abis/fund.ts index a57df12..d910524 100644 --- a/frontend/gostarkme-web/contracts/abis/fund.ts +++ b/frontend/gostarkme-web/contracts/abis/fund.ts @@ -4,6 +4,20 @@ export const fundAbi = [ "name": "FundImpl", "interface_name": "gostarkme::fund::IFund" }, + { + "type": "enum", + "name": "core::bool", + "variants": [ + { + "name": "False", + "type": "()" + }, + { + "name": "True", + "type": "()" + } + ] + }, { "type": "struct", "name": "core::byte_array::ByteArray", @@ -42,7 +56,7 @@ export const fundAbi = [ "items": [ { "type": "function", - "name": "getId", + "name": "get_id", "inputs": [], "outputs": [ { @@ -53,7 +67,7 @@ export const fundAbi = [ }, { "type": "function", - "name": "getOwner", + "name": "get_owner", "inputs": [], "outputs": [ { @@ -64,7 +78,23 @@ export const fundAbi = [ }, { "type": "function", - "name": "setName", + "name": "is_owner", + "inputs": [ + { + "name": "caller", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "set_name", "inputs": [ { "name": "name", @@ -76,7 +106,7 @@ export const fundAbi = [ }, { "type": "function", - "name": "getName", + "name": "get_name", "inputs": [], "outputs": [ { @@ -87,7 +117,7 @@ export const fundAbi = [ }, { "type": "function", - "name": "setReason", + "name": "set_reason", "inputs": [ { "name": "reason", @@ -99,7 +129,7 @@ export const fundAbi = [ }, { "type": "function", - "name": "getReason", + "name": "get_reason", "inputs": [], "outputs": [ { @@ -110,14 +140,14 @@ export const fundAbi = [ }, { "type": "function", - "name": "receiveVote", + "name": "receive_vote", "inputs": [], "outputs": [], "state_mutability": "external" }, { "type": "function", - "name": "getUpVotes", + "name": "get_up_votes", "inputs": [], "outputs": [ { @@ -128,7 +158,7 @@ export const fundAbi = [ }, { "type": "function", - "name": "setGoal", + "name": "set_goal", "inputs": [ { "name": "goal", @@ -140,7 +170,7 @@ export const fundAbi = [ }, { "type": "function", - "name": "getGoal", + "name": "get_goal", "inputs": [], "outputs": [ { @@ -174,7 +204,7 @@ export const fundAbi = [ }, { "type": "function", - "name": "setState", + "name": "set_state", "inputs": [ { "name": "state", @@ -186,7 +216,7 @@ export const fundAbi = [ }, { "type": "function", - "name": "getState", + "name": "get_state", "inputs": [], "outputs": [ { @@ -197,8 +227,13 @@ export const fundAbi = [ }, { "type": "function", - "name": "getVoter", - "inputs": [], + "name": "get_voter", + "inputs": [ + { + "name": "user", + "type": "core::starknet::contract_address::ContractAddress" + } + ], "outputs": [ { "type": "core::integer::u32" @@ -258,6 +293,29 @@ export const fundAbi = [ } ], "state_mutability": "view" + }, + { + "type": "function", + "name": "set_type", + "inputs": [ + { + "name": "fund_type", + "type": "core::integer::u8" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "type": "function", + "name": "get_type", + "inputs": [], + "outputs": [ + { + "type": "core::integer::u8" + } + ], + "state_mutability": "view" } ] }, @@ -292,6 +350,10 @@ export const fundAbi = [ { "name": "reason", "type": "core::byte_array::ByteArray" + }, + { + "name": "fund_type", + "type": "core::integer::u8" } ] }, @@ -388,4 +450,4 @@ export const fundAbi = [ } ] } -] \ No newline at end of file +] diff --git a/frontend/gostarkme-web/contracts/abis/fundManager.ts b/frontend/gostarkme-web/contracts/abis/fundManager.ts index 37f60f9..1c06c7d 100644 --- a/frontend/gostarkme-web/contracts/abis/fundManager.ts +++ b/frontend/gostarkme-web/contracts/abis/fundManager.ts @@ -1,12 +1,12 @@ export const fundManager = [ { - "type": "impl", "name": "FundManagerImpl", - "interface_name": "gostarkme::fundManager::IFundManager" + "type": "impl", + "interface_name": "gostarkme::fund_manager::IFundManager" }, { - "type": "struct", "name": "core::byte_array::ByteArray", + "type": "struct", "members": [ { "name": "data", @@ -23,8 +23,8 @@ export const fundManager = [ ] }, { - "type": "struct", "name": "core::integer::u256", + "type": "struct", "members": [ { "name": "low", @@ -37,12 +37,12 @@ export const fundManager = [ ] }, { + "name": "gostarkme::fund_manager::IFundManager", "type": "interface", - "name": "gostarkme::fundManager::IFundManager", "items": [ { + "name": "new_fund", "type": "function", - "name": "newFund", "inputs": [ { "name": "name", @@ -63,14 +63,18 @@ export const fundManager = [ { "name": "reason", "type": "core::byte_array::ByteArray" + }, + { + "name": "fund_type", + "type": "core::integer::u8" } ], "outputs": [], "state_mutability": "external" }, { + "name": "get_current_id", "type": "function", - "name": "getCurrentId", "inputs": [], "outputs": [ { @@ -80,8 +84,8 @@ export const fundManager = [ "state_mutability": "view" }, { + "name": "get_fund", "type": "function", - "name": "getFund", "inputs": [ { "name": "id", @@ -96,8 +100,8 @@ export const fundManager = [ "state_mutability": "view" }, { + "name": "get_owner", "type": "function", - "name": "getOwner", "inputs": [], "outputs": [ { @@ -107,8 +111,8 @@ export const fundManager = [ "state_mutability": "view" }, { + "name": "get_fund_class_hash", "type": "function", - "name": "getFundClassHash", "inputs": [], "outputs": [ { @@ -120,8 +124,8 @@ export const fundManager = [ ] }, { - "type": "constructor", "name": "constructor", + "type": "constructor", "inputs": [ { "name": "fund_class_hash", @@ -130,36 +134,36 @@ export const fundManager = [ ] }, { - "type": "event", - "name": "gostarkme::fundManager::FundManager::FundDeployed", "kind": "struct", + "name": "gostarkme::fund_manager::FundManager::FundDeployed", + "type": "event", "members": [ { + "kind": "key", "name": "owner", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "key" + "type": "core::starknet::contract_address::ContractAddress" }, { + "kind": "data", "name": "fund_address", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" + "type": "core::starknet::contract_address::ContractAddress" }, { + "kind": "data", "name": "fund_id", - "type": "core::integer::u128", - "kind": "data" + "type": "core::integer::u128" } ] }, { - "type": "event", - "name": "gostarkme::fundManager::FundManager::Event", "kind": "enum", + "name": "gostarkme::fund_manager::FundManager::Event", + "type": "event", "variants": [ { + "kind": "nested", "name": "FundDeployed", - "type": "gostarkme::fundManager::FundManager::FundDeployed", - "kind": "nested" + "type": "gostarkme::fund_manager::FundManager::FundDeployed" } ] } diff --git a/frontend/gostarkme-web/package-lock.json b/frontend/gostarkme-web/package-lock.json index 2348186..b2daf9b 100644 --- a/frontend/gostarkme-web/package-lock.json +++ b/frontend/gostarkme-web/package-lock.json @@ -17,6 +17,7 @@ "next": "14.2.10", "react": "^18", "react-dom": "^18", + "react-modal": "^3.16.1", "react-router-dom": "^6.28.0", "starknet": "^6.11.0", "starknetkit": "^1.1.5", @@ -26,6 +27,7 @@ "@types/node": "^20", "@types/react": "^18.3.4", "@types/react-dom": "^18", + "@types/react-modal": "^3.16.3", "eslint": "^8", "eslint-config-next": "14.2.5", "postcss": "^8", @@ -1822,6 +1824,15 @@ "@types/react": "*" } }, + "node_modules/@types/react-modal": { + "version": "3.16.3", + "resolved": "https://registry.npmjs.org/@types/react-modal/-/react-modal-3.16.3.tgz", + "integrity": "sha512-xXuGavyEGaFQDgBv4UVm8/ZsG+qxeQ7f77yNrW3n+1J6XAstUy5rYHeIHPh1KzsGc6IkCIdu6lQ2xWzu1jBTLg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@typescript-eslint/parser": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz", @@ -3902,6 +3913,11 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/exenv": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", + "integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -6415,6 +6431,29 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "node_modules/react-modal": { + "version": "3.16.1", + "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.1.tgz", + "integrity": "sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==", + "dependencies": { + "exenv": "^1.2.0", + "prop-types": "^15.7.2", + "react-lifecycles-compat": "^3.0.0", + "warning": "^4.0.3" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18", + "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18" + } + }, "node_modules/react-remove-scroll": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.0.tgz", @@ -7866,6 +7905,14 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/frontend/gostarkme-web/package.json b/frontend/gostarkme-web/package.json index bc853de..ba25b4b 100644 --- a/frontend/gostarkme-web/package.json +++ b/frontend/gostarkme-web/package.json @@ -18,6 +18,7 @@ "next": "14.2.10", "react": "^18", "react-dom": "^18", + "react-modal": "^3.16.1", "react-router-dom": "^6.28.0", "starknet": "^6.11.0", "starknetkit": "^1.1.5", @@ -27,6 +28,7 @@ "@types/node": "^20", "@types/react": "^18.3.4", "@types/react-dom": "^18", + "@types/react-modal": "^3.16.3", "eslint": "^8", "eslint-config-next": "14.2.5", "postcss": "^8", diff --git a/frontend/gostarkme-web/state/activeChain.ts b/frontend/gostarkme-web/state/activeChain.ts new file mode 100644 index 0000000..2457a5f --- /dev/null +++ b/frontend/gostarkme-web/state/activeChain.ts @@ -0,0 +1,5 @@ +import { atomWithReset } from "jotai/utils" + +export const activeChainId = atomWithReset< + string | null | undefined +>(undefined) \ No newline at end of file diff --git a/scripts/run_tests.sh b/scripts/run_tests.sh index 88a611e..3af7709 100644 --- a/scripts/run_tests.sh +++ b/scripts/run_tests.sh @@ -1,6 +1,13 @@ #!/bin/bash set -e +# First, attempt to compile everything +echo "::group::Compilation" +if ! (cd contracts && scarb build); then + echo "::error::Compilation failed" + exit 1 +fi +echo "::endgroup::" tests_output=$(cd contracts && scarb run test || true) @@ -27,4 +34,4 @@ if [ "$tests_failed" -gt 0 ]; then echo "::error::Tests failed:" echo "$failed_tests" exit 1 -fi \ No newline at end of file +fi