Skip to content

Commit

Permalink
Add list of stakes
Browse files Browse the repository at this point in the history
  • Loading branch information
tensojka committed Sep 5, 2024
1 parent 2f1eb2f commit f40f842
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 24 deletions.
55 changes: 55 additions & 0 deletions frontend/src/components/staking/StakeList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React from 'react';
import { useStakes } from '../../lib/staking/useStakes';
import { formatBalance } from "../../lib/erc20";

interface StakeListProps {
address: string | undefined;
}

const StakeList: React.FC<StakeListProps> = ({ address }) => {
const { stakes, isLoading, error } = useStakes(address);

if (isLoading) return <div className="text-center">Loading stakes...</div>;
if (error) return <div className="text-center text-red-500">Error: {error.message}</div>;
if (!stakes.length) return <div className="text-center">No stakes found.</div>;

const formatExpirationStatus = (startDate: bigint, length: bigint) => {
const startTimestamp = Number(startDate) * 1000; // Convert to milliseconds
const expirationTimestamp = startTimestamp + (Number(length) * 1000);
const now = Date.now();

if (now > expirationTimestamp) {
return `Expired on ${new Date(expirationTimestamp).toLocaleDateString()}`;
} else {
return `Expires on ${new Date(expirationTimestamp).toLocaleDateString()}`;
}
};

return (
<div className="w-full max-w-[50rem] mt-4">
<h2 className="text-xl font-bold mb-2">Your Stakes</h2>
<table className="w-full border-collapse">
<thead>
<tr className="bg-gray-100">
<th className="border p-2 text-left">Stake ID</th>
<th className="border p-2 text-left">Amount Staked</th>
<th className="border p-2 text-left">Voting Power</th>
<th className="border p-2 text-left">Status</th>
</tr>
</thead>
<tbody>
{stakes.map(stake => (
<tr key={stake.id} className="hover:bg-gray-50">
<td className="border p-2">{stake.id}</td>
<td className="border p-2">{formatBalance(stake.amount_staked)} KONOHA</td>
<td className="border p-2">{formatBalance(stake.amount_voting_token)} veKONOHA</td>
<td className="border p-2">{formatExpirationStatus(stake.start_date, stake.length)}</td>
</tr>
))}
</tbody>
</table>
</div>
);
};

export default StakeList;
11 changes: 3 additions & 8 deletions frontend/src/components/staking/VotingPower.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
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';
import { useStakes } from "../../lib/staking/useStakes";
import StakeList from './StakeList';
import { VOTING_TOKEN_CONTRACT, FLOATING_TOKEN_CONTRACT } from '../../lib/config';

export default function VotingPower() {
const { address } = useAccount();
Expand All @@ -14,8 +14,6 @@ export default function VotingPower() {
];
const { balances, isLoading } = useTokenBalances(tokens, address);

const { stake, isLoading: stakeIsLoading } = useStakes(address);

const floatingBalance = balances.find(b => b.name === "FLOATING")?.balance || "0";

return (
Expand All @@ -26,10 +24,7 @@ export default function VotingPower() {
title="Voting Power"
/>
<NewStake floatingBalance={floatingBalance.toString()} />

{!stakeIsLoading && (
<div>Raw stake data: {stake.amount_staked.toString()}</div>
)}
<StakeList address={address} />
</div>
);
}
59 changes: 43 additions & 16 deletions frontend/src/lib/staking/useStakes.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/* eslint-disable */
import { useState, useCallback, useEffect } from 'react';
import { useContract, useContractRead } from '@starknet-react/core';
import { abi } from "../abi";
import { CONTRACT_ADDR } from "../config";
import { useContractRead } from "@starknet-react/core";

// Assuming CarmineStake interface based on provided information
interface CarmineStake {
amount_staked: bigint;
amount_voting_token: bigint;
Expand All @@ -13,22 +13,49 @@ interface CarmineStake {
id: number;
}

export const useStakes = (address: string) => {
const stakeId = 0;
const result = useContractRead({
functionName: "get_stake",
args: [address, stakeId.toString()],
abi,
const isEmptyStake = (stake: CarmineStake) => stake.amount_staked === BigInt(0);

export const useStakes = (address: string | undefined) => {
const [stakes, setStakes] = useState<CarmineStake[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);

const { contract } = useContract({
address: CONTRACT_ADDR,
watch: false,
abi,
});

console.log('Raw stake data:', result.data);
console.log(result.error);
const fetchStakes = useCallback(async () => {
if (!contract || !address) return;

setIsLoading(true);
setError(null);

try {
const fetchedStakes: CarmineStake[] = [];
let stakeId = 0;

while (true) {
const result = await contract.call('get_stake', [address, stakeId.toString()]);
const stake = result as unknown as CarmineStake;

if (isEmptyStake(stake)) break;

fetchedStakes.push({ ...stake, id: stakeId });
stakeId++;
}

setStakes(fetchedStakes);
} catch (err) {
setError(err instanceof Error ? err : new Error('An error occurred while fetching stakes'));
} finally {
setIsLoading(false);
}
}, [contract, address]);

useEffect(() => {
fetchStakes();
}, [fetchStakes]);

return {
stake: result.data as unknown as CarmineStake,
isLoading: result.isLoading,
error: result.error
};
return { stakes, isLoading, error, refetch: fetchStakes };
};

0 comments on commit f40f842

Please sign in to comment.