From b1a92df407f11d395fbd8bd4e808cdbc064fc0c9 Mon Sep 17 00:00:00 2001 From: Pellar-Spike Date: Tue, 10 Feb 2026 10:07:38 +0700 Subject: [PATCH 1/5] init delegate sepilia + ll --- constants.ts | 11 +- .../components/DelegateCard.tsx | 14 +- .../components/UserDelegateCard.tsx | 241 ++++++++++++------ .../hooks/useDelegateAnnouncements.tsx | 15 +- plugins/delegateAnnouncer/pages/index.tsx | 96 +++---- plugins/index.ts | 14 +- 6 files changed, 260 insertions(+), 131 deletions(-) diff --git a/constants.ts b/constants.ts index 4baeda6a..52d77045 100644 --- a/constants.ts +++ b/constants.ts @@ -26,13 +26,20 @@ export const PUB_DELEGATION_ANNOUNCEMENTS_START_BLOCK = BigInt( process.env.NEXT_PUBLIC_DELEGATION_ANNOUNCEMENTS_START_BLOCK || "0" ); -// Target chain +// Target chain (L1 = PUB_CHAIN, L2 = PUB_L2_CHAIN) export const PUB_CHAIN_NAME = (process.env.NEXT_PUBLIC_CHAIN_NAME ?? "sepolia") as ChainName; export const PUB_CHAIN = getChain(PUB_CHAIN_NAME); export const PUB_L2_CHAIN_NAME = (process.env.NEXT_PUBLIC_L2_CHAIN_NAME || "arbitrumSepolia") as ChainName; export const PUB_L2_CHAIN = getChain(PUB_L2_CHAIN_NAME); +/** Token address for delegation: PUB_CHAIN (L1) → PUB_TOKEN_L1_ADDRESS, PUB_L2_CHAIN (L2) → PUB_TOKEN_L2_ADDRESS. */ +export function getTokenAddressByChainId(chainId: number): Address { + if (chainId === PUB_CHAIN.id) return PUB_TOKEN_L1_ADDRESS; + if (chainId === PUB_L2_CHAIN.id) return PUB_TOKEN_L2_ADDRESS; + return PUB_TOKEN_ADDRESS as Address; +} + // Network and services export const PUB_ALCHEMY_API_KEY = process.env.NEXT_PUBLIC_ALCHEMY_API_KEY ?? ""; @@ -60,4 +67,4 @@ export const PUB_DISCORD_URL = "https://discord.com/"; export const PUB_MINTABLE_TOKEN_ADDRESS = (process.env.NEXT_PUBLIC_MINTABLE_TOKEN_ADDRESS ?? "") as Address; export const PUB_PAYMASTER_ADDRESS = (process.env.NEXT_PUBLIC_PAYMASTER_ADDRESS ?? "") as Address; -export const PUB_BLOCKSCOUT_URL = process.env.NEXT_PUBLIC_BLOCKSCOUT_URL ?? ""; \ No newline at end of file +export const PUB_BLOCKSCOUT_URL = process.env.NEXT_PUBLIC_BLOCKSCOUT_URL ?? ""; diff --git a/plugins/delegateAnnouncer/components/DelegateCard.tsx b/plugins/delegateAnnouncer/components/DelegateCard.tsx index 8b76adf7..5800190a 100644 --- a/plugins/delegateAnnouncer/components/DelegateCard.tsx +++ b/plugins/delegateAnnouncer/components/DelegateCard.tsx @@ -9,7 +9,7 @@ import { mainnet } from "wagmi/chains"; import { useReadContract, useWriteContract } from "wagmi"; import { iVotesAbi } from "../artifacts/iVotes.sol"; import { formatHexString } from "@/utils/evm"; -import * as DOMPurify from "dompurify"; +import DOMPurify from "dompurify"; type DelegateCardProps = { delegate: Address; @@ -22,21 +22,31 @@ export const DelegateCard = ({ delegate, message, tokenAddress }: DelegateCardPr const result = useEnsName({ chainId: mainnet.id, address: delegate, + query: { + enabled: !!delegate && delegate !== "0x", + }, }); const avatarResult = useEnsAvatar({ - name: normalize(result.data!), + name: result.data ? normalize(result.data) : undefined, chainId: mainnet.id, gatewayUrls: ["https://cloudflare-ipfs.com"], + query: { + enabled: !!result.data, + }, }); const { data: votingPower } = useReadContract({ abi: iVotesAbi, address: tokenAddress, functionName: "getVotes", args: [delegate], + query: { + enabled: !!tokenAddress && tokenAddress !== "0x" && !!delegate && delegate !== "0x", + }, }); const { writeContract: delegateWrite } = useWriteContract(); const delegateTo = () => { + if (!tokenAddress || tokenAddress === "0x" || !delegate || delegate === "0x") return; delegateWrite({ abi: iVotesAbi, address: tokenAddress, diff --git a/plugins/delegateAnnouncer/components/UserDelegateCard.tsx b/plugins/delegateAnnouncer/components/UserDelegateCard.tsx index 8e0e9a04..e8a2f847 100644 --- a/plugins/delegateAnnouncer/components/UserDelegateCard.tsx +++ b/plugins/delegateAnnouncer/components/UserDelegateCard.tsx @@ -1,17 +1,30 @@ -import { useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { If } from "@/components/if"; import Image from "next/image"; -import { Button, Card, Link, TextAreaRichText } from "@aragon/ods"; -import { Address, formatUnits, toHex } from "viem"; +import { AlertInline, Button, Card, InputText, Link } from "@aragon/ods"; +import { Address, formatUnits, isAddress } from "viem"; import { useEnsName, useEnsAvatar } from "wagmi"; import { normalize } from "viem/ens"; import { mainnet } from "wagmi/chains"; -import { useReadContract, useWriteContract } from "wagmi"; +import { + useChainId, + usePublicClient, + useReadContract, + useSwitchChain, + useWaitForTransactionReceipt, + useWriteContract, +} from "wagmi"; import { iVotesAbi } from "../artifacts/iVotes.sol"; import { formatHexString } from "@/utils/evm"; -import { DelegateAnnouncerAbi } from "@/plugins/delegateAnnouncer/artifacts/DelegateAnnouncer.sol"; -import * as DOMPurify from "dompurify"; -import { PUB_DAO_ADDRESS, PUB_DELEGATION_CONTRACT_ADDRESS } from "@/constants"; +import DOMPurify from "dompurify"; +import { getTokenAddressByChainId, PUB_CHAIN, PUB_CHAIN_NAME, PUB_L2_CHAIN, PUB_L2_CHAIN_NAME } from "@/constants"; +import { pegasus } from "@/utils/chains"; +import { useAlerts } from "@/context/Alerts"; + +const CHAIN_OPTIONS = [ + { name: PUB_CHAIN_NAME, chainId: PUB_CHAIN.id }, + { name: PUB_L2_CHAIN_NAME, chainId: PUB_L2_CHAIN.id }, +] as const; type SelfDelegationProfileCardProps = { address: Address; @@ -23,101 +36,185 @@ type SelfDelegationProfileCardProps = { export const SelfDelegationProfileCard = ({ address, - tokenAddress, + tokenAddress: _tokenAddressProp, message, loading, delegates, }: SelfDelegationProfileCardProps) => { - const [inputDescription, setInputDescription] = useState(); + const [to, setTo] = useState
(); + const [selectedChainId, setSelectedChainId] = useState(PUB_CHAIN.id); + + const tokenAddress = getTokenAddressByChainId(selectedChainId); + const result = useEnsName({ chainId: mainnet.id, address, + query: { + enabled: !!address && address !== "0x", + }, }); const avatarResult = useEnsAvatar({ - name: normalize(result.data!), + name: result.data ? normalize(result.data) : undefined, chainId: mainnet.id, gatewayUrls: ["https://cloudflare-ipfs.com"], + query: { + enabled: !!result.data, + }, }); - const { data: votingPower } = useReadContract({ + const { data: votingPower, refetch: refetchVotingPower } = useReadContract({ abi: iVotesAbi, address: tokenAddress, functionName: "getVotes", args: [address], + chainId: selectedChainId, + query: { + enabled: !!tokenAddress && tokenAddress !== "0x" && !!address && address !== "0x", + }, + }); + const chainId = useChainId(); + const { switchChainAsync } = useSwitchChain(); + const publicClient = usePublicClient({ chainId: selectedChainId }); + const { + writeContract: delegateWrite, + data: delegateTxHash, + status: delegateStatus, + error: delegateError, + } = useWriteContract(); + const { isLoading: isConfirming, isSuccess: isConfirmed } = useWaitForTransactionReceipt({ + hash: delegateTxHash, }); - const { writeContract: delegateWrite } = useWriteContract(); - const { writeContract: delegateAnnouncementWrite } = useWriteContract(); - const delegateTo = () => { - delegateWrite({ - abi: iVotesAbi, - address: tokenAddress, - functionName: "delegate", - args: [address], - }); + const { addAlert } = useAlerts(); + const lastTxHashRef = useRef(); + const lastErrorMsgRef = useRef(null); + + useEffect(() => { + if (delegateError) { + const msg = delegateError.message; + if (lastErrorMsgRef.current !== msg) { + lastErrorMsgRef.current = msg; + addAlert(msg, { type: "error" }); + } + } else { + lastErrorMsgRef.current = null; + } + }, [delegateError?.message]); + + useEffect(() => { + if (delegateTxHash && isConfirmed && delegateTxHash !== lastTxHashRef.current) { + lastTxHashRef.current = delegateTxHash; + addAlert("Delegation confirmed on chain.", { + type: "success", + description: "Your voting power has been delegated.", + txHash: delegateTxHash, + }); + refetchVotingPower(); + } + }, [delegateTxHash, isConfirmed, refetchVotingPower]); + + const isDelegating = delegateStatus === "pending" || isConfirming; + + const delegateTo = async () => { + if (!tokenAddress || tokenAddress === "0x" || !address || address === "0x" || !to || to === "0x") return; + try { + if (chainId !== selectedChainId && switchChainAsync) { + await switchChainAsync({ chainId: selectedChainId }); + } + const args = [to] as [Address]; + if (selectedChainId === pegasus.id && publicClient) { + const fees = await publicClient.estimateFeesPerGas({ type: "legacy" }); + const gasPrice = fees?.gasPrice ?? 1n * 10n ** 9n; + delegateWrite({ + abi: iVotesAbi, + address: tokenAddress, + functionName: "delegate", + args, + chainId: selectedChainId, + type: "legacy", + gasPrice, + }); + } else { + delegateWrite({ + abi: iVotesAbi, + address: tokenAddress, + functionName: "delegate", + args, + chainId: selectedChainId, + }); + } + } catch (e) { + if (e instanceof Error) addAlert(e.message, { type: "error" }); + } }; - const announceDelegate = () => { - delegateAnnouncementWrite({ - abi: DelegateAnnouncerAbi, - address: PUB_DELEGATION_CONTRACT_ADDRESS, - functionName: "announceDelegation", - args: [PUB_DAO_ADDRESS, toHex(inputDescription!)], - }); + const handleTo = (event: React.ChangeEvent) => { + setTo(event?.target?.value as Address); }; return ( - -
+ + {/* Profile */} +
profile pic -
- {result.data ? result.data : formatHexString(address)} -

{votingPower ? formatUnits(votingPower!, 18)! : 0} Voting Power

+
+ + {result.data ?? formatHexString(address)} + +

{votingPower ? formatUnits(votingPower, 18) : "0"} Voting Power

-
- -
- - - +
+ + +
+
+ + +
+
+ + - +
+ {to && !isAddress(to) && }
+
- -
- -
-
- -
- -
-
+
); diff --git a/plugins/delegateAnnouncer/hooks/useDelegateAnnouncements.tsx b/plugins/delegateAnnouncer/hooks/useDelegateAnnouncements.tsx index 2b1e046a..02be3e0d 100644 --- a/plugins/delegateAnnouncer/hooks/useDelegateAnnouncements.tsx +++ b/plugins/delegateAnnouncer/hooks/useDelegateAnnouncements.tsx @@ -6,11 +6,20 @@ import { PUB_DELEGATION_ANNOUNCEMENTS_START_BLOCK } from "@/constants"; const AnnounceDelegationEvent = getAbiItem({ abi: DelegateAnnouncerAbi, name: "AnnounceDelegation" }); -export function useDelegateAnnouncements(publicClient: PublicClient, delegationContract: Address, daoAddress: Address) { +export function useDelegateAnnouncements( + publicClient: PublicClient | null, + delegationContract: Address, + daoAddress: Address +) { const [delegateAnnouncements, setDelegateAnnouncements] = useState([]); const [isLoading, setIsLoading] = useState(true); useEffect(() => { + if (!publicClient || !delegationContract || !daoAddress || delegationContract === "0x" || daoAddress === "0x") { + setIsLoading(false); + return; + } + setIsLoading(true); publicClient .getLogs({ @@ -33,12 +42,12 @@ export function useDelegateAnnouncements(publicClient: PublicClient, delegationC }) .catch((err) => { console.error("Could not fetch the delegates list", err); - return null; + setDelegateAnnouncements([]); }) .finally(() => { setIsLoading(false); }); - }, []); + }, [publicClient, delegationContract, daoAddress]); return { delegateAnnouncements, isLoading }; } diff --git a/plugins/delegateAnnouncer/pages/index.tsx b/plugins/delegateAnnouncer/pages/index.tsx index 0d3489dd..932a05c7 100644 --- a/plugins/delegateAnnouncer/pages/index.tsx +++ b/plugins/delegateAnnouncer/pages/index.tsx @@ -1,6 +1,6 @@ import { usePublicClient, useReadContract } from "wagmi"; import { useAccount } from "wagmi"; -import { PublicClient, parseAbi } from "viem"; +import { PublicClient, parseAbi, Address } from "viem"; import { ReactNode } from "react"; import { Else, ElseIf, If, Then } from "@/components/if"; import { PleaseWaitSpinner } from "@/components/please-wait"; @@ -12,69 +12,75 @@ import { PUB_DAO_ADDRESS, PUB_DELEGATION_CONTRACT_ADDRESS, PUB_TOKEN_ADDRESS } f export default function DelegateAnnouncements() { const publicClient = usePublicClient(); const account = useAccount(); + const { data: delegates, status } = useReadContract({ abi: iVotesAbi, - address: PUB_TOKEN_ADDRESS, + address: PUB_TOKEN_ADDRESS as Address, functionName: "delegates", - args: [account.address!], - }); + args: account.address ? [account.address as Address] : undefined, + query: { + enabled: !!account.address, + }, + } as any); const { delegateAnnouncements, isLoading: delegateAnnouncementsIsLoading } = useDelegateAnnouncements( publicClient as PublicClient, PUB_DELEGATION_CONTRACT_ADDRESS, - PUB_DAO_ADDRESS + account.address as Address ); return ( - - -

Your profile

- an.delegate === account.address)?.message} - /> -
-
- -

Delegates

- - -
- {delegateAnnouncements.map((announcement) => ( - - ))} + + +
+

Your profile

+ an.delegate === account.address)?.message} + />
- - - - - - - - - - There are no delegate announcements on the DAO - -
+ + + {/*
+

Delegates

+ + +
+ {delegateAnnouncements.map((announcement) => ( + + ))} +
+
+ +
+ +
+
+ +

There are no delegate announcements on the DAO

+
+
+
*/} +
); } function MainSection({ children }: { children: ReactNode }) { - return
{children}
; + return
{children}
; } function SectionView({ children }: { children: ReactNode }) { - return
{children}
; + return
{children}
; } const iVotesAbi = parseAbi([ diff --git a/plugins/index.ts b/plugins/index.ts index e8211caa..ef83969d 100644 --- a/plugins/index.ts +++ b/plugins/index.ts @@ -40,13 +40,13 @@ export const plugins: PluginItem[] = [ // icon: IconType.BLOCKCHAIN_BLOCK, // pluginAddress: PUB_LOCK_TO_VOTE_PLUGIN_ADDRESS, // }, - // { - // id: "delegate-wall", - // folderName: "delegateAnnouncer", - // title: "Delegation", - // icon: IconType.FEEDBACK, - // pluginAddress: PUB_DELEGATION_CONTRACT_ADDRESS, - // }, + { + id: "delegate-wall", + folderName: "delegateAnnouncer", + title: "Delegation", + icon: IconType.FEEDBACK, + pluginAddress: PUB_DELEGATION_CONTRACT_ADDRESS, + }, { id: "crosschain-voting", folderName: "toucanVoting", From 3f6c9173e463e62e72c52485b653aa9952d9d1e2 Mon Sep 17 00:00:00 2001 From: Pellar-Spike Date: Wed, 11 Feb 2026 09:31:29 +0700 Subject: [PATCH 2/5] add delegate OP sepolia --- constants.ts | 27 ++++++++++++++----- context/Web3Modal.tsx | 5 +++- .../components/UserDelegateCard.tsx | 18 ++++++++++--- 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/constants.ts b/constants.ts index 52d77045..5fab0c81 100644 --- a/constants.ts +++ b/constants.ts @@ -33,18 +33,12 @@ export const PUB_CHAIN = getChain(PUB_CHAIN_NAME); export const PUB_L2_CHAIN_NAME = (process.env.NEXT_PUBLIC_L2_CHAIN_NAME || "arbitrumSepolia") as ChainName; export const PUB_L2_CHAIN = getChain(PUB_L2_CHAIN_NAME); -/** Token address for delegation: PUB_CHAIN (L1) → PUB_TOKEN_L1_ADDRESS, PUB_L2_CHAIN (L2) → PUB_TOKEN_L2_ADDRESS. */ -export function getTokenAddressByChainId(chainId: number): Address { - if (chainId === PUB_CHAIN.id) return PUB_TOKEN_L1_ADDRESS; - if (chainId === PUB_L2_CHAIN.id) return PUB_TOKEN_L2_ADDRESS; - return PUB_TOKEN_ADDRESS as Address; -} - // Network and services export const PUB_ALCHEMY_API_KEY = process.env.NEXT_PUBLIC_ALCHEMY_API_KEY ?? ""; export const PUB_WEB3_ENDPOINT = (process.env.NEXT_PUBLIC_WEB3_URL_PREFIX ?? "") + PUB_ALCHEMY_API_KEY; export const PUB_WEB3_ENDPOINT_L2 = process.env.NEXT_PUBLIC_WEB3_URL_PREFIX_L2 ?? ""; +export const PUB_WEB3_ENDPOINT_L2_OP = process.env.NEXT_PUBLIC_WEB3_URL_PREFIX_L2_OP ?? ""; export const PUB_ETHERSCAN_API_KEY = process.env.NEXT_PUBLIC_ETHERSCAN_API_KEY ?? ""; @@ -68,3 +62,22 @@ export const PUB_MINTABLE_TOKEN_ADDRESS = (process.env.NEXT_PUBLIC_MINTABLE_TOKE export const PUB_PAYMASTER_ADDRESS = (process.env.NEXT_PUBLIC_PAYMASTER_ADDRESS ?? "") as Address; export const PUB_BLOCKSCOUT_URL = process.env.NEXT_PUBLIC_BLOCKSCOUT_URL ?? ""; + +// L2 Optimism (OP) +export const PUB_BLOCKSCOUT_URL_OP = process.env.NEXT_PUBLIC_BLOCKSCOUT_URL_OP ?? ""; +export const PUB_L2_CHAIN_NAME_OP = (process.env.NEXT_PUBLIC_L2_CHAIN_NAME_OP ?? "optimismSepolia") as ChainName; +export const PUB_L2_START_BLOCK_OP = BigInt(process.env.NEXT_PUBLIC_L2_START_BLOCK_OP ?? "0"); +export const PUB_DAO_ADDRESS_L2_OP = (process.env.NEXT_PUBLIC_DAO_ADDRESS_L2_OP ?? "") as Address; +export const PUB_TOUCAN_VOTING_PLUGIN_L2_ADDRESS_OP = (process.env.NEXT_PUBLIC_TOUCAN_VOTING_PLUGIN_L2_ADDRESS_OP ?? + "") as Address; +export const PUB_TOKEN_L2_ADDRESS_OP = (process.env.NEXT_PUBLIC_TOKEN_L2_ADDRESS_OP ?? "") as Address; +export const PUB_OFT_TOKEN_BRIDGE_ADDRESS_OP = (process.env.NEXT_PUBLIC_OFT_TOKEN_BRIDGE_ADDRESS_OP ?? "") as Address; +export const PUB_WEB3_URL_PREFIX_L2_OP = process.env.NEXT_PUBLIC_WEB3_URL_PREFIX_L2_OP ?? ""; +export const PUB_L2_CHAIN_OP = getChain(PUB_L2_CHAIN_NAME_OP); + +export function getTokenAddressByChainId(chainId: number): Address { + if (chainId === PUB_CHAIN.id) return PUB_TOKEN_L1_ADDRESS; + if (chainId === PUB_L2_CHAIN.id) return PUB_TOKEN_L2_ADDRESS; + if (chainId === PUB_L2_CHAIN_OP.id) return PUB_TOKEN_L2_ADDRESS_OP; + return PUB_TOKEN_ADDRESS as Address; +} diff --git a/context/Web3Modal.tsx b/context/Web3Modal.tsx index dd8fb3bf..219f1271 100644 --- a/context/Web3Modal.tsx +++ b/context/Web3Modal.tsx @@ -5,11 +5,13 @@ import { PUB_APP_NAME, PUB_CHAIN, PUB_L2_CHAIN, + PUB_L2_CHAIN_OP, PUB_PROJECT_URL, PUB_WALLET_CONNECT_PROJECT_ID, PUB_WALLET_ICON, PUB_WEB3_ENDPOINT, PUB_WEB3_ENDPOINT_L2, + PUB_WEB3_ENDPOINT_L2_OP, } from "@/constants"; import { mainnet } from "viem/chains"; @@ -22,12 +24,13 @@ const metadata = { }; export const config = createConfig({ - chains: [PUB_CHAIN, mainnet, PUB_L2_CHAIN], + chains: [PUB_CHAIN, mainnet, PUB_L2_CHAIN, PUB_L2_CHAIN_OP], syncConnectedChain: true, ssr: true, transports: { [PUB_CHAIN.id]: http(PUB_WEB3_ENDPOINT, { batch: true }), [PUB_L2_CHAIN.id]: http(PUB_WEB3_ENDPOINT_L2, { batch: true }), + [PUB_L2_CHAIN_OP.id]: http(PUB_WEB3_ENDPOINT_L2_OP, { batch: true }), [mainnet.id]: http(PUB_WEB3_ENDPOINT, { batch: true }), }, connectors: [ diff --git a/plugins/delegateAnnouncer/components/UserDelegateCard.tsx b/plugins/delegateAnnouncer/components/UserDelegateCard.tsx index e8a2f847..6aab1c80 100644 --- a/plugins/delegateAnnouncer/components/UserDelegateCard.tsx +++ b/plugins/delegateAnnouncer/components/UserDelegateCard.tsx @@ -17,14 +17,24 @@ import { import { iVotesAbi } from "../artifacts/iVotes.sol"; import { formatHexString } from "@/utils/evm"; import DOMPurify from "dompurify"; -import { getTokenAddressByChainId, PUB_CHAIN, PUB_CHAIN_NAME, PUB_L2_CHAIN, PUB_L2_CHAIN_NAME } from "@/constants"; +import { + getTokenAddressByChainId, + PUB_CHAIN, + PUB_CHAIN_NAME, + PUB_L2_CHAIN, + PUB_L2_CHAIN_NAME, + PUB_L2_CHAIN_OP, + PUB_L2_CHAIN_NAME_OP, +} from "@/constants"; import { pegasus } from "@/utils/chains"; +import { readableChainName } from "@/utils/chains"; import { useAlerts } from "@/context/Alerts"; const CHAIN_OPTIONS = [ - { name: PUB_CHAIN_NAME, chainId: PUB_CHAIN.id }, - { name: PUB_L2_CHAIN_NAME, chainId: PUB_L2_CHAIN.id }, -] as const; + { name: readableChainName(PUB_CHAIN_NAME), chainId: PUB_CHAIN.id }, + { name: readableChainName(PUB_L2_CHAIN_NAME), chainId: PUB_L2_CHAIN.id }, + { name: readableChainName(PUB_L2_CHAIN_NAME_OP), chainId: PUB_L2_CHAIN_OP.id }, +]; type SelfDelegationProfileCardProps = { address: Address; From 144d7026b050272be044308f76e0eddf651b0a07 Mon Sep 17 00:00:00 2001 From: Pellar-Spike Date: Wed, 11 Feb 2026 09:49:24 +0700 Subject: [PATCH 3/5] fix const --- constants.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/constants.ts b/constants.ts index 5fab0c81..a4d2689b 100644 --- a/constants.ts +++ b/constants.ts @@ -64,15 +64,8 @@ export const PUB_PAYMASTER_ADDRESS = (process.env.NEXT_PUBLIC_PAYMASTER_ADDRESS export const PUB_BLOCKSCOUT_URL = process.env.NEXT_PUBLIC_BLOCKSCOUT_URL ?? ""; // L2 Optimism (OP) -export const PUB_BLOCKSCOUT_URL_OP = process.env.NEXT_PUBLIC_BLOCKSCOUT_URL_OP ?? ""; export const PUB_L2_CHAIN_NAME_OP = (process.env.NEXT_PUBLIC_L2_CHAIN_NAME_OP ?? "optimismSepolia") as ChainName; -export const PUB_L2_START_BLOCK_OP = BigInt(process.env.NEXT_PUBLIC_L2_START_BLOCK_OP ?? "0"); -export const PUB_DAO_ADDRESS_L2_OP = (process.env.NEXT_PUBLIC_DAO_ADDRESS_L2_OP ?? "") as Address; -export const PUB_TOUCAN_VOTING_PLUGIN_L2_ADDRESS_OP = (process.env.NEXT_PUBLIC_TOUCAN_VOTING_PLUGIN_L2_ADDRESS_OP ?? - "") as Address; export const PUB_TOKEN_L2_ADDRESS_OP = (process.env.NEXT_PUBLIC_TOKEN_L2_ADDRESS_OP ?? "") as Address; -export const PUB_OFT_TOKEN_BRIDGE_ADDRESS_OP = (process.env.NEXT_PUBLIC_OFT_TOKEN_BRIDGE_ADDRESS_OP ?? "") as Address; -export const PUB_WEB3_URL_PREFIX_L2_OP = process.env.NEXT_PUBLIC_WEB3_URL_PREFIX_L2_OP ?? ""; export const PUB_L2_CHAIN_OP = getChain(PUB_L2_CHAIN_NAME_OP); export function getTokenAddressByChainId(chainId: number): Address { From 448cd566eda56ee1e9b05cac97d2fc8917397c6b Mon Sep 17 00:00:00 2001 From: Pellar-Spike Date: Wed, 11 Feb 2026 15:31:26 +0700 Subject: [PATCH 4/5] update show detlegate address --- constants.ts | 2 +- .../components/UserDelegateCard.tsx | 24 +++++------- plugins/delegateAnnouncer/pages/index.tsx | 38 ++++++++----------- 3 files changed, 27 insertions(+), 37 deletions(-) diff --git a/constants.ts b/constants.ts index a4d2689b..fc9171a0 100644 --- a/constants.ts +++ b/constants.ts @@ -26,7 +26,7 @@ export const PUB_DELEGATION_ANNOUNCEMENTS_START_BLOCK = BigInt( process.env.NEXT_PUBLIC_DELEGATION_ANNOUNCEMENTS_START_BLOCK || "0" ); -// Target chain (L1 = PUB_CHAIN, L2 = PUB_L2_CHAIN) +// Target chain export const PUB_CHAIN_NAME = (process.env.NEXT_PUBLIC_CHAIN_NAME ?? "sepolia") as ChainName; export const PUB_CHAIN = getChain(PUB_CHAIN_NAME); diff --git a/plugins/delegateAnnouncer/components/UserDelegateCard.tsx b/plugins/delegateAnnouncer/components/UserDelegateCard.tsx index 6aab1c80..c09d2d0d 100644 --- a/plugins/delegateAnnouncer/components/UserDelegateCard.tsx +++ b/plugins/delegateAnnouncer/components/UserDelegateCard.tsx @@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from "react"; import { If } from "@/components/if"; import Image from "next/image"; import { AlertInline, Button, Card, InputText, Link } from "@aragon/ods"; -import { Address, formatUnits, isAddress } from "viem"; +import { Address, formatUnits, isAddress, zeroAddress } from "viem"; import { useEnsName, useEnsAvatar } from "wagmi"; import { normalize } from "viem/ens"; import { mainnet } from "wagmi/chains"; @@ -38,21 +38,18 @@ const CHAIN_OPTIONS = [ type SelfDelegationProfileCardProps = { address: Address; - tokenAddress: Address; - loading: boolean; - message: string | undefined; delegates: Address; + selectedChainId: number; + setSelectedChainId: (chainId: number) => void; }; export const SelfDelegationProfileCard = ({ address, - tokenAddress: _tokenAddressProp, - message, - loading, delegates, + selectedChainId, + setSelectedChainId, }: SelfDelegationProfileCardProps) => { const [to, setTo] = useState
(); - const [selectedChainId, setSelectedChainId] = useState(PUB_CHAIN.id); const tokenAddress = getTokenAddressByChainId(selectedChainId); @@ -180,11 +177,10 @@ export const SelfDelegationProfileCard = ({
- -
+ +

+ Current delegate: {formatHexString(delegates)} +

@@ -203,7 +199,7 @@ export const SelfDelegationProfileCard = ({
- + (PUB_CHAIN.id); - const { data: delegates, status } = useReadContract({ + const tokenAddress = useMemo(() => getTokenAddressByChainId(selectedChainId), [selectedChainId]); + + const { data: delegates } = useReadContract({ abi: iVotesAbi, - address: PUB_TOKEN_ADDRESS as Address, + address: tokenAddress, functionName: "delegates", args: account.address ? [account.address as Address] : undefined, + chainId: selectedChainId, query: { - enabled: !!account.address, + enabled: !!account.address && !!tokenAddress && tokenAddress !== "0x", }, - } as any); - const { delegateAnnouncements, isLoading: delegateAnnouncementsIsLoading } = useDelegateAnnouncements( - publicClient as PublicClient, - PUB_DELEGATION_CONTRACT_ADDRESS, - account.address as Address - ); + }); return ( @@ -36,10 +31,9 @@ export default function DelegateAnnouncements() {

Your profile

an.delegate === account.address)?.message} + delegates={delegates as Address} + selectedChainId={selectedChainId} + setSelectedChainId={setSelectedChainId} />
From ce74a6be38c862b7e52d7439443ca6d0788a7b76 Mon Sep 17 00:00:00 2001 From: Pellar-Spike Date: Tue, 24 Feb 2026 09:54:31 +0700 Subject: [PATCH 5/5] fix show current delegate --- plugins/delegateAnnouncer/components/UserDelegateCard.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/delegateAnnouncer/components/UserDelegateCard.tsx b/plugins/delegateAnnouncer/components/UserDelegateCard.tsx index c09d2d0d..34c0e995 100644 --- a/plugins/delegateAnnouncer/components/UserDelegateCard.tsx +++ b/plugins/delegateAnnouncer/components/UserDelegateCard.tsx @@ -1,5 +1,4 @@ import { useEffect, useRef, useState } from "react"; -import { If } from "@/components/if"; import Image from "next/image"; import { AlertInline, Button, Card, InputText, Link } from "@aragon/ods"; import { Address, formatUnits, isAddress, zeroAddress } from "viem"; @@ -16,7 +15,6 @@ import { } from "wagmi"; import { iVotesAbi } from "../artifacts/iVotes.sol"; import { formatHexString } from "@/utils/evm"; -import DOMPurify from "dompurify"; import { getTokenAddressByChainId, PUB_CHAIN, @@ -171,17 +169,17 @@ export const SelfDelegationProfileCard = ({ />
- {result.data ?? formatHexString(address)} + {result.data ?? (address ? formatHexString(address) : null)}

{votingPower ? formatUnits(votingPower, 18) : "0"} Voting Power

- + {isAddress(address) && delegates !== zeroAddress && isAddress(delegates) && (

Current delegate: {formatHexString(delegates)}

-
+ )}