Skip to content

Fix : Gateway staging feedback fixes #823

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/components/boba/Bridge/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { networkDetails } from "@/utils/networks";
import { IconSwitchHorizontal } from '@tabler/icons-react';
import { useState } from 'react';
import BridgeInfoCard from "./BridgeInfoCard";
import { isAddress } from "viem";


export const NetworkSelection = () => {
Expand Down Expand Up @@ -133,6 +134,14 @@ export const DestinationAddress: React.FC = () => {
const handlePasteAddress = async () => {
try {
const clipboardAddress = await navigator.clipboard.readText();
if (!isAddress(clipboardAddress)) {
toast({
variant: "destructive",
title: "Invalid Address",
description: "The pasted content is not a valid Ethereum address"
});
return;
}
setTargetAddress(clipboardAddress);
} catch (error) {
toast({
Expand Down
6 changes: 3 additions & 3 deletions src/components/boba/DatePickerWIthRange/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ export const DatePickerWithRange: React.FC<DatePickerWithRangeProps> = ({
{selectedDates?.from ? (
selectedDates.to ? (
<>
{format(selectedDates.from, "LLL dd, y")} -{" "}
{format(selectedDates.to, "LLL dd, y")}
{format(selectedDates.from, "LL/dd/y")} -{" "}
{format(selectedDates.to, "LL/d/y")}
</>
) : (
format(selectedDates.from, "LLL dd, y")
format(selectedDates.from, "LL/d/y")
)
) : (
<span>Pick a date</span>
Expand Down
2 changes: 1 addition & 1 deletion src/components/boba/Footer/useFooter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const footerNavLinks: Array<FooterLinkItem> = [
},
{
label: "Dev Tools",
href: "https://docs.boba.network/developer",
href: "https://docs.boba.network",
target: "_blank"
},
{
Expand Down
2 changes: 1 addition & 1 deletion src/components/boba/History/action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ const HistoryActions: React.FC<HistoryActionsProps> = ({

return (
<>
<div className="w-full space-y-4 px-4 pt-4">
<div className="w-full space-y-4 pt-4">
<SearchInput onChange={(e) => setInputSearch(e.target.value)} />
<div className="w-full rounded-t-3xl shadow-md border border-gray-400 dark:border-dark-gray-300 py-4 overflow-x-scroll">
<div className="flex flex-grow px-4 justify-between items-center gap-4">
Expand Down
38 changes: 38 additions & 0 deletions src/components/ui/tabs.smooth.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { AnimatePresence, motion } from "motion/react";
import { ReactNode } from "react";

interface TabContentWrapperProps {
children: ReactNode;
isSelected?: boolean;
}

export const TabContentWrapper = ({ children, isSelected }: TabContentWrapperProps) => (
<AnimatePresence mode="wait" initial={false}>
{isSelected && (
<motion.div
initial={{ opacity: 0, x: 15 }}
animate={{
opacity: 1,
x: 0,
transition: {
duration: 0.25,
ease: "easeOut",
opacity: { duration: 0.2 }
}
}}
exit={{
opacity: 0,
x: -15,
transition: {
duration: 0.2,
ease: "easeInOut",
opacity: { duration: 0.15 }
}
}}
className="gap-4 flex flex-col my-2"
>
{children}
</motion.div>
)}
</AnimatePresence>
);
50 changes: 26 additions & 24 deletions src/hooks/dao/useDaoStats.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { daoContractConfig } from '@/config/contracts'
import { publicClientConfig } from '@/wagmi/publicconfig'
import { readContracts } from '@wagmi/core'
import { getProvider } from '@/lib/viem'
import { useCallback, useState } from 'react'
import { formatUnits } from 'viem'
import { useAccount } from 'wagmi'
import { formatEther } from 'viem'
import { readContract } from 'viem/actions'
import { useAccount, useChainId } from 'wagmi'

export function useDaoStats() {

const { address } = useAccount()
const chainId = useChainId();

const [stats, setStats] = useState({
bobaVotes: 0,
Expand All @@ -24,30 +24,32 @@ export function useDaoStats() {
setStats(prev => ({ ...prev, isLoading: true, error: null, isError: false }))

try {
// @todo review and test the implementation
const data = await readContracts(publicClientConfig, {
contracts: [
{
...daoContractConfig.bobaToken,
functionName: 'getCurrentVotes',
args: [address!],
},

const provider = getProvider(chainId);

const _threshold = await readContract(provider!, {
address: daoContractConfig.governorBravo.address,
abi: daoContractConfig.governorBravo.abi,
functionName: "proposalThreshold"
})

const _bobaVotes = await readContract(provider!, {
...daoContractConfig.bobaToken,
functionName: 'getCurrentVotes',
args: [address!],
})

const _xBobaVotes = await readContract(provider!,
{
...daoContractConfig.xBobaToken,
functionName: 'getCurrentVotes',
args: [address!],
},
{
...daoContractConfig.governorBravo,
functionName: 'proposalThreshold',
}
] as any
})
})

const bobaVotes = data && data[0]?.result ? Number(formatUnits(data[0].result as bigint, 18)) : 0
const xBobaVotes = data && data[1]?.result ? Number(formatUnits(data[1].result as bigint, 18)) : 0
const proposalThreshold = data && data[2]?.result ? Number(formatUnits(data[2].result as bigint, 18)) : 0
const totalVotes = (bobaVotes + xBobaVotes);
const proposalThreshold = Number(formatEther(_threshold as bigint))
const bobaVotes = Number(formatEther(_bobaVotes as bigint))
const xBobaVotes = Number(formatEther(_xBobaVotes as bigint))
const totalVotes = (Number(bobaVotes) + Number(xBobaVotes));
const hasSufficientVotes = totalVotes >= Number(proposalThreshold)

setStats({
Expand Down
105 changes: 58 additions & 47 deletions src/hooks/dao/useProposals.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { daoContractConfig } from '@/config/contracts'
import { getProvider } from '@/lib/viem'
import { useDaoStore } from '@/stores/dao.store'
import type { Proposal } from '@/types/dao'
import { publicClientConfig } from '@/wagmi/publicconfig'
import { useQuery } from '@tanstack/react-query'
import { readContract } from '@wagmi/core'
import { useEffect } from 'react'
import { useEffect, useState } from 'react'
import { formatUnits } from 'viem'
import { useReadContract } from 'wagmi'
import { useChainId, useReadContract } from 'wagmi'

const proposalStates = [
'Pending',
Expand All @@ -20,61 +18,74 @@ const proposalStates = [
]

export function useProposals() {

const { setProposals } = useDaoStore()
const chainId = useChainId()
const [isLoading, setIsLoading] = useState(true)
const [proposals, setLocalProposals] = useState<Proposal[]>([])

// Get proposal count
const { data: proposalCount } = useReadContract({
...daoContractConfig.governorBravo,
functionName: 'proposalCount',
})

// Fetch all proposals
const { data: proposals, isLoading } = useQuery({
queryKey: ['proposals', Number(proposalCount)],
queryFn: async () => {
if (!proposalCount) return []
useEffect(() => {
async function fetchProposals() {
if (!proposalCount) {
setIsLoading(false)
return
}

const proposals: Proposal[] = []
for (let i = 1; i <= Number(proposalCount); i++) {
const [id, , , description, _, startTimestamp, endTimestamp, forVotes, againstVotes, abstainVotes] = await readContract(publicClientConfig, {
...daoContractConfig.governorBravo,
functionName: 'proposals',
args: [BigInt(i)],
}) as unknown as any
try {
setIsLoading(true)
const newProposals: Proposal[] = []
const provider = getProvider(chainId)

const state = await readContract(publicClientConfig, {
...daoContractConfig.governorBravo,
functionName: 'state',
args: [BigInt(i)],
}) as unknown as any
for (let i = 1; i <= Number(proposalCount); i++) {
try {
const [id, , , description, _, startTimestamp, endTimestamp, forVotes, againstVotes, abstainVotes] = await provider.readContract({
...daoContractConfig.governorBravo,
functionName: 'proposals',
args: [BigInt(i)],
}) as unknown as any

proposals.push({
id: id.toString(),
number: Number(id),
title: description,
status: proposalStates[state],
startDate: new Date(Number(startTimestamp) * 1000).toLocaleString(),
endDate: new Date(Number(endTimestamp) * 1000).toLocaleString(),
totalVotes: Number(formatUnits(forVotes, 18)) + Number(formatUnits(againstVotes, 18)) + Number(formatUnits(abstainVotes, 18)),
votes: [
{ type: 'For', count: Number(formatUnits(forVotes, 18)), color: 'bg-green-500' },
{ type: 'Against', count: Number(formatUnits(againstVotes, 18)), color: 'bg-red-500' },
{ type: 'Abstain', count: Number(formatUnits(abstainVotes, 18)), color: 'bg-gray-300' },
],
isExpanded: false,
})
}
return proposals
},
enabled: !!proposalCount,
})
const state = await provider.readContract({
...daoContractConfig.governorBravo,
functionName: 'state',
args: [BigInt(i)],
}) as unknown as any

useEffect(() => {
if (proposals) {
setProposals(proposals)
newProposals.push({
id: id.toString(),
number: Number(id),
title: description,
status: proposalStates[state],
startDate: new Date(Number(startTimestamp) * 1000).toLocaleString(),
endDate: new Date(Number(endTimestamp) * 1000).toLocaleString(),
totalVotes: Number(formatUnits(forVotes, 18)) + Number(formatUnits(againstVotes, 18)) + Number(formatUnits(abstainVotes, 18)),
votes: [
{ type: 'For', count: Number(formatUnits(forVotes, 18)), color: 'bg-green-500' },
{ type: 'Against', count: Number(formatUnits(againstVotes, 18)), color: 'bg-red-500' },
{ type: 'Abstain', count: Number(formatUnits(abstainVotes, 18)), color: 'bg-gray-300' },
],
isExpanded: false,
})
} catch (error) {
console.log(`Handling error for proposal ${i}:`, error);
}
}

setLocalProposals(newProposals)
setProposals(newProposals)
} catch (error) {
console.error('Error fetching proposals:', error)
} finally {
setIsLoading(false)
}
}
}, [proposals])

fetchProposals()
}, [chainId, proposalCount, setProposals])

return {
proposals,
Expand Down
38 changes: 1 addition & 37 deletions src/layout/bridge/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { DestinationAddress, NetworkSelection, ReceiveAmount, TokenAmountSelection } from '@/components/boba/Bridge';
import TitleIconWrapper from '@/components/boba/TitleIcon';
import { Card, CardContent, CardHeader, CardTitle, Tabs, TabsContent, TabsList, TabsTrigger, Text, Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui';
import { TabContentWrapper } from '@/components/ui/tabs.smooth';
import { useBridgeSetup } from '@/hooks/bridge/useBridgeSetup';
import { useBridgeStore } from '@/stores/bridge.store';
import { useModalStore } from '@/stores/modal.store';
import { BridgeType } from '@/types/bridge';
import { ModalIds } from '@/types/modal';
import { IconHelp, IconSettings } from '@tabler/icons-react';
import { AnimatePresence, motion } from 'motion/react';
import { type ReactNode } from 'react';
import { useAccount } from "wagmi";
import BridgeActionButton from './BridgeActionButton';
import BridgeAlertCard from './BridgeAlertCard';
Expand Down Expand Up @@ -42,41 +41,6 @@ const ClassicBridgeCard = () => {
);
};

interface TabContentWrapperProps {
children: ReactNode;
isSelected?: boolean;
}

const TabContentWrapper = ({ children, isSelected }: TabContentWrapperProps) => (
<AnimatePresence mode="wait" initial={false}>
{isSelected && (
<motion.div
initial={{ opacity: 0, x: 15 }}
animate={{
opacity: 1,
x: 0,
transition: {
duration: 0.25,
ease: "easeOut",
opacity: { duration: 0.2 }
}
}}
exit={{
opacity: 0,
x: -15,
transition: {
duration: 0.2,
ease: "easeInOut",
opacity: { duration: 0.15 }
}
}}
className="gap-4 flex flex-col my-2"
>
{children}
</motion.div>
)}
</AnimatePresence>
);

const BridgePage = () => {
useBridgeSetup()
Expand Down
Loading