diff --git a/frontend/src/components/TokenBalance.tsx b/frontend/src/components/TokenBalance.tsx index 30ff2dbe..916e13da 100644 --- a/frontend/src/components/TokenBalance.tsx +++ b/frontend/src/components/TokenBalance.tsx @@ -1,40 +1,18 @@ -import React, { useMemo } from "react"; -import { useContractRead } from "@starknet-react/core"; -import { abi, formatBalance } from "../lib/erc20"; +import React from "react"; +import { formatBalance } from "../lib/erc20"; -interface Token { +interface TokenBalanceItem { name: string; - address: string; + balance: bigint; } interface TokenBalanceProps { - tokens: Token[]; - accountAddress: string; + balances: TokenBalanceItem[]; title: string; + isLoading: boolean; } -export default function TokenBalance({ tokens, accountAddress, title }: TokenBalanceProps) { - const balanceReads = tokens.map(token => - useContractRead({ - functionName: "balanceOf", - args: [accountAddress], - abi, - address: token.address, - watch: true, - }) - ); - - const balances = useMemo(() => - tokens.map((token, index) => ({ - name: token.name, - balance: balanceReads[index].data, - isLoading: balanceReads[index].isLoading, - })), - [tokens, balanceReads] - ); - - const isLoading = balances.some(b => b.isLoading); - +export default function TokenBalance({ balances, title, isLoading }: TokenBalanceProps) { return (
{title}
@@ -49,7 +27,7 @@ export default function TokenBalance({ tokens, accountAddress, title }: TokenBal
- {formatBalance(BigInt(token.balance.toString()))} + {formatBalance(token.balance)}
diff --git a/frontend/src/components/TreasuryStatus.tsx b/frontend/src/components/TreasuryStatus.tsx index 768e37c7..ca292a0b 100644 --- a/frontend/src/components/TreasuryStatus.tsx +++ b/frontend/src/components/TreasuryStatus.tsx @@ -1,17 +1,19 @@ import { TREASURY_ADDRESS, ETH_ADDRESS, STRK_ADDRESS } from '../lib/config'; import React from "react"; import TokenBalance from './TokenBalance'; +import { useTokenBalances } from "../lib/useTokenBalances"; export default function TreasuryStatus() { const tokens = [ { name: "ETH", address: ETH_ADDRESS }, { name: "STRK", address: STRK_ADDRESS }, ]; + const { balances, isLoading } = useTokenBalances(tokens, TREASURY_ADDRESS); return ( ); diff --git a/frontend/src/components/staking/NewStake.tsx b/frontend/src/components/staking/NewStake.tsx new file mode 100644 index 00000000..8ae01e4a --- /dev/null +++ b/frontend/src/components/staking/NewStake.tsx @@ -0,0 +1,89 @@ +import React, { useState, useMemo } from "react"; +import { useContractWrite } from "@starknet-react/core"; +import { CONTRACT_ADDR, FLOATING_TOKEN_CONTRACT } from '../../lib/config'; // +import toast from "react-hot-toast"; + +const stakingPeriods = [ + { label: "1 month", value: "2629743" } +]; + +interface NewStakeProps { + floatingBalance: string; +} + +export default function NewStake({ floatingBalance }: NewStakeProps) { + const [amount, setAmount] = useState(""); + const [period, setPeriod] = useState(stakingPeriods[0].value); + + const calls = useMemo(() => { + if (!amount) return []; + return [ + { + contractAddress: FLOATING_TOKEN_CONTRACT, + entrypoint: "approve", + calldata: [CONTRACT_ADDR, 0, amount] + }, + { + contractAddress: CONTRACT_ADDR, + entrypoint: "stake", + calldata: [period, amount], + } + ]; + }, [amount, period]); + + const { writeAsync } = useContractWrite({ calls }); + + const handleStake = (e: React.FormEvent) => { + e.preventDefault(); + if (!amount) { + toast.error("Please enter an amount to stake"); + return; + } + writeAsync() + .then(() => toast.success("Stake successful")) + .catch((e) => { + toast.error("Staking failed"); + console.error(e); + }); + }; + + if (BigInt(floatingBalance) === 0n) return null; + + return ( +
+
Stake your floating token
+
+ + +
+
+ + setAmount(e.target.value)} + /> +
+ +
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/staking/VotingPower.tsx b/frontend/src/components/staking/VotingPower.tsx index d8ce242c..0b358bbc 100644 --- a/frontend/src/components/staking/VotingPower.tsx +++ b/frontend/src/components/staking/VotingPower.tsx @@ -2,6 +2,8 @@ import { VOTING_TOKEN_CONTRACT, FLOATING_TOKEN_CONTRACT } from '../../lib/config import React from "react"; import { useAccount } from "@starknet-react/core"; import TokenBalance from '../TokenBalance'; +import { useTokenBalances } from "../../lib/useTokenBalances"; +import NewStake from './NewStake'; export default function VotingPower() { const { address } = useAccount(); @@ -9,12 +11,18 @@ export default function VotingPower() { { name: "VOTING", address: VOTING_TOKEN_CONTRACT }, { name: "FLOATING", address: FLOATING_TOKEN_CONTRACT }, ]; + const { balances, isLoading } = useTokenBalances(tokens, address); + + const floatingBalance = balances.find(b => b.name === "FLOATING")?.balance || "0"; return ( - +
+ + +
); } \ No newline at end of file diff --git a/frontend/src/lib/useTokenBalances.ts b/frontend/src/lib/useTokenBalances.ts new file mode 100644 index 00000000..6972a751 --- /dev/null +++ b/frontend/src/lib/useTokenBalances.ts @@ -0,0 +1,32 @@ +import { useMemo } from "react"; +import { useContractRead } from "@starknet-react/core"; +import { abi } from "./erc20"; + +interface Token { + name: string; + address: string; +} + +export function useTokenBalances(tokens: Token[], accountAddress: string) { + const balanceReads = tokens.map(token => + useContractRead({ + functionName: "balanceOf", + args: [accountAddress], + abi, + address: token.address, + watch: true, + }) + ); + + const balances = useMemo(() => + tokens.map((token, index) => ({ + name: token.name, + balance: BigInt(balanceReads[index].data?.toString() || "0"), + })), + [tokens, balanceReads] + ); + + const isLoading = balanceReads.some(read => read.isLoading); + + return { balances, isLoading }; +} \ No newline at end of file