diff --git a/public/logos/superbridge.jpg b/public/logos/superbridge.jpg
new file mode 100644
index 0000000..c1775bb
Binary files /dev/null and b/public/logos/superbridge.jpg differ
diff --git a/src/app/bridge/page.tsx b/src/app/bridge/page.tsx
index 21aebe3..7b0869e 100644
--- a/src/app/bridge/page.tsx
+++ b/src/app/bridge/page.tsx
@@ -6,6 +6,8 @@ import { SolidButton } from 'src/components/buttons/SolidButton';
import { ChevronIcon } from 'src/components/icons/Chevron';
import { Section } from 'src/components/layout/Section';
import { H1 } from 'src/components/text/headers';
+import { config } from 'src/config/config';
+import { useIsCel2 } from 'src/features/account/hooks';
import PortalLogo from 'src/images/logos/portal-bridge.jpg';
import SquidLogo from 'src/images/logos/squid-router.jpg';
@@ -14,9 +16,17 @@ interface Bridge {
operator: string;
href: string;
logo: any;
+ cel2Only?: boolean;
}
const BRIDGES: Bridge[] = [
+ {
+ name: 'Superbridge',
+ operator: 'Superbridge',
+ href: `https://superbridge.app/celo${config.isAlfajores ? '-testnet' : ''}`,
+ logo: '/logos/superbridge.jpg',
+ cel2Only: true,
+ },
{
name: 'Squid Router',
operator: 'Squid',
@@ -32,17 +42,18 @@ const BRIDGES: Bridge[] = [
];
export default function Page() {
+ const isCel2 = useIsCel2();
return (
Bridge to Celo
- {BRIDGES.map((bridge) => (
+ {BRIDGES.filter((x) => (x.cel2Only ? isCel2 : true)).map((bridge) => (
))}
- {/*
+
These bridges are independent, third-party service providers.
Celo assumes no responsibility for their operation.
-
*/}
+
);
}
diff --git a/src/app/delegate/[address]/page.tsx b/src/app/delegate/[address]/page.tsx
index ffffb12..b0841ac 100644
--- a/src/app/delegate/[address]/page.tsx
+++ b/src/app/delegate/[address]/page.tsx
@@ -53,7 +53,10 @@ function DelegateeDescription({ delegatee }: { delegatee: Delegatee }) {
{delegatee.name}
-
+
•
{`Since ${dateString}`}
diff --git a/src/components/logos/Bug.tsx b/src/components/logos/Bug.tsx
new file mode 100644
index 0000000..cd56071
--- /dev/null
+++ b/src/components/logos/Bug.tsx
@@ -0,0 +1,16 @@
+import { SVGProps, memo } from 'react';
+
+function _Bug(props: SVGProps
) {
+ const { fill, ...rest } = props;
+ return (
+ // Font Awesome Free 6.7.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.
+
+ );
+}
+
+export const Bug = memo(_Bug);
diff --git a/src/components/logos/SocialLogo.tsx b/src/components/logos/SocialLogo.tsx
index f01cb75..5c504c5 100644
--- a/src/components/logos/SocialLogo.tsx
+++ b/src/components/logos/SocialLogo.tsx
@@ -1,5 +1,6 @@
import { SVGProps } from 'react';
import { A_Blank } from 'src/components/buttons/A_Blank';
+import { Bug } from 'src/components/logos/Bug';
import { Discord } from 'src/components/logos/Discord';
import { Github } from 'src/components/logos/Github';
import { Twitter } from 'src/components/logos/Twitter';
@@ -18,6 +19,7 @@ const LOGOS: Record> = {
[SocialLinkType.Twitter]: Twitter,
[SocialLinkType.Github]: Github,
[SocialLinkType.Discord]: Discord,
+ [SocialLinkType.Bug]: Bug,
};
export function SocialLogo({ svgProps, type, className, size = 18 }: Props) {
diff --git a/src/components/nav/Footer.tsx b/src/components/nav/Footer.tsx
index dc2a8a5..6e8377e 100644
--- a/src/components/nav/Footer.tsx
+++ b/src/components/nav/Footer.tsx
@@ -12,6 +12,7 @@ export function Footer() {
+
diff --git a/src/components/text/CopyInline.tsx b/src/components/text/CopyInline.tsx
new file mode 100644
index 0000000..74e03aa
--- /dev/null
+++ b/src/components/text/CopyInline.tsx
@@ -0,0 +1,15 @@
+import { ButtonHTMLAttributes } from 'react';
+import { useCopyHandler } from 'src/utils/clipboard';
+
+type Props = ButtonHTMLAttributes
& {
+ text: string;
+};
+
+export function CopyInline({ text, ...props }: Props) {
+ const onClick = useCopyHandler(text);
+ return (
+
+ );
+}
diff --git a/src/components/text/ShortAddress.tsx b/src/components/text/ShortAddress.tsx
index 381f17a..50e2245 100644
--- a/src/components/text/ShortAddress.tsx
+++ b/src/components/text/ShortAddress.tsx
@@ -1,16 +1,11 @@
import { ButtonHTMLAttributes } from 'react';
+import { CopyInline } from 'src/components/text/CopyInline';
import { shortenAddress } from 'src/utils/addresses';
-import { useCopyHandler } from 'src/utils/clipboard';
type Props = ButtonHTMLAttributes & {
address: Address;
};
export function ShortAddress({ address, ...props }: Props) {
- const onClick = useCopyHandler(address);
- return (
-
- );
+ return ;
}
diff --git a/src/config/links.ts b/src/config/links.ts
index d9a5ec9..32a2ebf 100644
--- a/src/config/links.ts
+++ b/src/config/links.ts
@@ -1,9 +1,11 @@
import { config } from 'src/config/config';
+const github = 'https://github.com/celo-org/celo-mondo';
export const links = {
home: 'https://mondo.celo.org',
discord: 'https://discord.gg/celo',
- github: 'https://github.com/celo-org/celo-mondo',
+ github,
+ bug: github + '/issues/new?assignees=&labels=&projects=&template=bug_report.md&title=',
twitter: 'https://twitter.com/celo',
docs: 'https://docs.celo.org',
forum: 'https://forum.celo.org',
diff --git a/src/config/types.ts b/src/config/types.ts
index 36f8ba3..0441253 100644
--- a/src/config/types.ts
+++ b/src/config/types.ts
@@ -5,6 +5,7 @@ export enum SocialLinkType {
Twitter = 'twitter',
Github = 'github',
Discord = 'discord',
+ Bug = 'bug',
}
export const SocialLinksSchema = z.record(z.nativeEnum(SocialLinkType), z.string().url());
diff --git a/src/features/account/hooks.ts b/src/features/account/hooks.ts
index e17450f..75539b0 100644
--- a/src/features/account/hooks.ts
+++ b/src/features/account/hooks.ts
@@ -1,9 +1,11 @@
import { accountsABI, lockedGoldABI } from '@celo/abis';
+import { useEffect, useState } from 'react';
import { useToastError } from 'src/components/notifications/useToastError';
import { BALANCE_REFRESH_INTERVAL, ZERO_ADDRESS } from 'src/config/consts';
import { Addresses } from 'src/config/contracts';
+import { isCel2 } from 'src/utils/is-cel2';
import { isNullish } from 'src/utils/typeof';
-import { useBalance as _useBalance, useReadContract } from 'wagmi';
+import { useBalance as _useBalance, usePublicClient, useReadContract } from 'wagmi';
export function useBalance(address?: Address) {
const { data, isError, isLoading, error, refetch } = _useBalance({
@@ -72,3 +74,15 @@ export function useAccountDetails(address?: Address) {
refetch,
};
}
+
+export function useIsCel2() {
+ const publicClient = usePublicClient();
+ const [_isCel2, setIsCel2] = useState(undefined);
+ useEffect(() => {
+ if (!publicClient) return;
+
+ void isCel2(publicClient).then((value) => setIsCel2(value));
+ }, [publicClient]);
+
+ return _isCel2;
+}
diff --git a/src/features/delegation/components/DelegatorsTable.tsx b/src/features/delegation/components/DelegatorsTable.tsx
index 4f86a6a..47d0737 100644
--- a/src/features/delegation/components/DelegatorsTable.tsx
+++ b/src/features/delegation/components/DelegatorsTable.tsx
@@ -3,9 +3,9 @@ import { SpinnerWithLabel } from 'src/components/animation/Spinner';
import { sortAndCombineChartData } from 'src/components/charts/chartData';
import { Collapse } from 'src/components/menus/Collapse';
import { formatNumberString } from 'src/components/numbers/Amount';
+import { ShortAddress } from 'src/components/text/ShortAddress';
import { useDelegators } from 'src/features/delegation/hooks/useDelegators';
import { Delegatee } from 'src/features/delegation/types';
-import { shortenAddress } from 'src/utils/addresses';
import { fromWei } from 'src/utils/amount';
import { objKeys } from 'src/utils/objects';
@@ -28,7 +28,7 @@ function DelegatorsTableContent({ delegatee }: { delegatee: Delegatee }) {
const tableData = useMemo(() => {
if (!delegatorToAmount) return [];
const data = objKeys(delegatorToAmount).map((address) => ({
- label: shortenAddress(address),
+ label: address,
value: fromWei(delegatorToAmount[address]),
}));
return sortAndCombineChartData(data, NUM_TO_SHOW);
@@ -51,7 +51,9 @@ function DelegatorsTableContent({ delegatee }: { delegatee: Delegatee }) {
{tableData.map((row) => (
- {row.label} |
+
+
+ |
{`${formatNumberString(row.value)} CELO`} |
))}
diff --git a/src/features/governance/components/ProposalCard.tsx b/src/features/governance/components/ProposalCard.tsx
index 7e02ad2..3c68f68 100644
--- a/src/features/governance/components/ProposalCard.tsx
+++ b/src/features/governance/components/ProposalCard.tsx
@@ -112,7 +112,10 @@ export function ProposalBadgeRow({
{showProposer && proposer && (
<>
•
-
+
>
)}
{/* Show one of proposer or executedTimeValue but not both, too crowded */}
diff --git a/src/features/governance/components/ProposalVotersTable.tsx b/src/features/governance/components/ProposalVotersTable.tsx
index 2751ca7..b551e97 100644
--- a/src/features/governance/components/ProposalVotersTable.tsx
+++ b/src/features/governance/components/ProposalVotersTable.tsx
@@ -3,6 +3,7 @@ import { SpinnerWithLabel } from 'src/components/animation/Spinner';
import { ChartDataItem, sortAndCombineChartData } from 'src/components/charts/chartData';
import { Collapse } from 'src/components/menus/Collapse';
import { formatNumberString } from 'src/components/numbers/Amount';
+import { CopyInline } from 'src/components/text/CopyInline';
import { MergedProposalData } from 'src/features/governance/hooks/useGovernanceProposals';
import { useProposalVoters } from 'src/features/governance/hooks/useProposalVoters';
import { ProposalStage, VoteType } from 'src/features/governance/types';
@@ -17,19 +18,28 @@ import { toTitleCase } from 'src/utils/strings';
const NUM_TO_SHOW = 20;
export function ProposalVotersTable({ propData }: { propData: MergedProposalData }) {
+ const votersData = useProposalVoters(propData.id);
return (
Voters}
+ button={
+
+ Voters
+
+ }
buttonClasses="w-full"
defaultOpen={propData.stage >= ProposalStage.Execution}
>
-
+
);
}
-function VoterTableContent({ propData }: { propData: MergedProposalData }) {
- const { isLoading, voters, totals } = useProposalVoters(propData.id);
+function VoterTableContent({
+ propData,
+}: {
+ propData: MergedProposalData & { votersData: ReturnType };
+}) {
+ const { isLoading, voters, totals } = propData.votersData;
const { addressToGroup } = useValidatorGroups();
const tableData = useMemo(() => {
@@ -82,7 +92,11 @@ function VoterTableContent({ propData }: { propData: MergedProposalData }) {
{tableData.map((row) => (
- {row.label} |
+
+
+ |
{toTitleCase(row.type)} |
@@ -98,3 +112,9 @@ function VoterTableContent({ propData }: { propData: MergedProposalData }) {
);
}
+
+function VotersCount({ isLoading, voters }: ReturnType ) {
+ if (isLoading) return null;
+
+ return {Object.keys(voters!).length};
+}
diff --git a/src/features/locking/LockForm.tsx b/src/features/locking/LockForm.tsx
index cf130cd..461a497 100644
--- a/src/features/locking/LockForm.tsx
+++ b/src/features/locking/LockForm.tsx
@@ -82,6 +82,9 @@ export function LockForm({
return validateForm(values, lockedBalances, walletBalance, stakeBalances, isVoting);
};
+ const shouldShowWithdrawalTip = lockedBalances?.pendingFree === 0n && lockedBalances?.locked > 0n;
+ const isUnstaking = lockedBalances && lockedBalances.pendingBlocked > 0n;
+
return (
initialValues={{
@@ -102,6 +105,20 @@ export function LockForm({
Lock CELO to begin.
)}
+ {values.action === LockActionType.Withdraw && shouldShowWithdrawalTip && (
+
+ You currently have no available unlocked CELO. Unlocking takes 3 days.{' '}
+ {isUnstaking && (
+ <>
+
+
+ You are currently unlocking {fromWeiRounded(lockedBalances.pendingBlocked)}{' '}
+ CELO.
+
+ >
+ )}
+
+ )}
(
{label}
- {value}
+ {value}
))}
diff --git a/tailwind.config.js b/tailwind.config.js
index e4a5da9..8ac90a6 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -39,7 +39,7 @@ module.exports = {
fontFamily: {
sans: ['var(--font-inter)', 'sans-serif'],
serif: ['var(--font-alpina)', 'serif'],
- mono: ['Courier New', 'monospace'],
+ mono: ['monospace'],
},
fontSize: {
xs: '0.725rem',
|