Skip to content
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

Resolving all issues #36

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
4 changes: 3 additions & 1 deletion components/nav/navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import WalletContainer from "@/components/WalletContainer";
import { plugins } from "@/plugins";
// import { plugins } from "@/plugins";
import { AvatarIcon, IconType } from "@aragon/ods";
import classNames from "classnames";
import Image from "next/image";
Expand All @@ -8,12 +8,14 @@ import { useState } from "react";
import { MobileNavDialog } from "./mobileNavDialog";
import { NavLink, type INavLink } from "./navLink";
import { useChainId } from "wagmi";
import usePlugins from "@/hooks/usePlugins";

export const Navbar: React.FC = () => {
const [open, setOpen] = useState(false);
const chainId = useChainId();

console.log("chain id", chainId);
const plugins = usePlugins();

const navLinks: INavLink[] = [
// { path: "/", id: "dashboard", name: "Dashboard", icon: IconType.APP_DASHBOARD },
Expand Down
47 changes: 47 additions & 0 deletions hooks/ethers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"use client";

import { FallbackProvider, JsonRpcProvider, BrowserProvider, JsonRpcSigner } from "ethers";
import { useMemo } from "react";
import type { Chain, Client, Transport, Account } from "viem";
import { type Config, useClient, useConnectorClient } from "wagmi";

export function clientToProvider(client: Client<Transport, Chain>) {
const { chain, transport } = client;
const network = {
chainId: chain.id,
name: chain.name,
ensAddress: chain.contracts?.ensRegistry?.address,
};
if (transport.type === "fallback") {
const providers = (transport.transports as ReturnType<Transport>[]).map(
({ value }) => new JsonRpcProvider(value?.url, network)
);
if (providers.length === 1) return providers[0];
return new FallbackProvider(providers);
}
return new JsonRpcProvider(transport.url, network);
}

/** Action to convert a viem Client to an ethers.js Provider. */
export function useEthersProvider({ chainId }: { chainId?: number } = {}) {
const client = useClient<Config>({ chainId });
return useMemo(() => (client ? clientToProvider(client) : undefined), [client]);
}

export function clientToSigner(client: Client<Transport, Chain, Account>) {
const { account, chain, transport } = client;
const network = {
chainId: chain.id,
name: chain.name,
ensAddress: chain.contracts?.ensRegistry?.address,
};
const provider = new BrowserProvider(transport, network);
const signer = new JsonRpcSigner(provider, account.address);
return signer;
}

/** Hook to convert a viem Wallet Client to an ethers.js Signer. */
export function useEthersSigner({ chainId }: { chainId?: number } = {}) {
const { data: client } = useConnectorClient<Config>({ chainId });
return useMemo(() => (client ? clientToSigner(client) : undefined), [client]);
}
80 changes: 80 additions & 0 deletions hooks/useConstant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { useEffect, useState } from "react";
import { crab, darwinia } from "viem/chains";
import { useChainId } from "wagmi";

export default function useConstant() {
const [publicDaoAddress, setPublicDaoAddress] = useState<string>("");
const [publicTokenAddress, setPublicTokenAddress] = useState<string>("");
const [publicTokenVotingPluginAddress, setPublicTokenVotingPluginAddress] = useState<string>("");
const [publicDelegationContractAddress, setPublicDelegationContractAddress] = useState<string>("");
const [publicDelegationAnnouncementsStartBlock, setPublicDelegationAnnouncementsStartBlock] = useState<string>("");

const chainId = useChainId();

useEffect(() => {
if (chainId === darwinia.id) {
console.log("switched to darwinia");
setPublicDaoAddress(process.env.NEXT_PUBLIC_DAO_ADDRESS ? process.env.NEXT_PUBLIC_DAO_ADDRESS : "");
setPublicTokenAddress(process.env.NEXT_PUBLIC_TOKEN_ADDRESS ? process.env.NEXT_PUBLIC_TOKEN_ADDRESS : "");
setPublicTokenVotingPluginAddress(
process.env.NEXT_PUBLIC_TOKEN_VOTING_PLUGIN_ADDRESS ? process.env.NEXT_PUBLIC_TOKEN_VOTING_PLUGIN_ADDRESS : ""
);
setPublicDelegationContractAddress(
process.env.NEXT_PUBLIC_DELEGATION_CONTRACT_ADDRESS ? process.env.NEXT_PUBLIC_DELEGATION_CONTRACT_ADDRESS : ""
);
setPublicDelegationAnnouncementsStartBlock(
process.env.NEXT_PUBLIC_DELEGATION_ANNOUNCEMENTS_START_BLOCK
? process.env.NEXT_PUBLIC_DELEGATION_ANNOUNCEMENTS_START_BLOCK
: ""
);
} else if (chainId === crab.id) {
console.log("switched to crab");
setPublicDaoAddress(process.env.NEXT_PUBLIC_DAO_ADDRESS_CRAD ? process.env.NEXT_PUBLIC_DAO_ADDRESS_CRAD : "");
setPublicTokenAddress(
process.env.NEXT_PUBLIC_TOKEN_ADDRESS_CRAD ? process.env.NEXT_PUBLIC_TOKEN_ADDRESS_CRAD : ""
);
setPublicTokenVotingPluginAddress(
process.env.NEXT_PUBLIC_TOKEN_VOTING_PLUGIN_ADDRESS_CRAD
? process.env.NEXT_PUBLIC_TOKEN_VOTING_PLUGIN_ADDRESS_CRAD
: ""
);
setPublicDelegationContractAddress(
process.env.NEXT_PUBLIC_DELEGATION_CONTRACT_ADDRESS_CRAD
? process.env.NEXT_PUBLIC_DELEGATION_CONTRACT_ADDRESS_CRAD
: ""
);
setPublicDelegationAnnouncementsStartBlock(
process.env.NEXT_PUBLIC_DELEGATION_ANNOUNCEMENTS_START_BLOCK_CRAD
? process.env.NEXT_PUBLIC_DELEGATION_ANNOUNCEMENTS_START_BLOCK_CRAD
: ""
);
} else if (chainId === 701) {
console.log("switched to koi");
setPublicDaoAddress(process.env.NEXT_PUBLIC_DAO_ADDRESS_KOI ? process.env.NEXT_PUBLIC_DAO_ADDRESS_KOI : "");
setPublicTokenAddress(process.env.NEXT_PUBLIC_TOKEN_ADDRESS_KOI ? process.env.NEXT_PUBLIC_TOKEN_ADDRESS_KOI : "");
setPublicTokenVotingPluginAddress(
process.env.NEXT_PUBLIC_TOKEN_VOTING_PLUGIN_ADDRESS_KOI
? process.env.NEXT_PUBLIC_TOKEN_VOTING_PLUGIN_ADDRESS_KOI
: ""
);
setPublicDelegationContractAddress(
process.env.NEXT_PUBLIC_DELEGATION_CONTRACT_ADDRESS_KOI
? process.env.NEXT_PUBLIC_DELEGATION_CONTRACT_ADDRESS_KOI
: ""
);
setPublicDelegationAnnouncementsStartBlock(
process.env.NEXT_PUBLIC_DELEGATION_ANNOUNCEMENTS_START_BLOCK_KOI
? process.env.NEXT_PUBLIC_DELEGATION_ANNOUNCEMENTS_START_BLOCK_KOI
: ""
);
}
}, [chainId]);

return {
publicDaoAddress,
publicTokenAddress,
publicTokenVotingPluginAddress,
publicDelegationContractAddress,
publicDelegationAnnouncementsStartBlock,
};
}
9 changes: 6 additions & 3 deletions hooks/usePermit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ import { useReadContracts, useSignTypedData, useAccount } from "wagmi";
import { hexToSignature, Address } from "viem";
import { ERC20PermitAbi } from "@/artifacts/ERC20Permit.sol";
import { useAlerts, AlertContextProps } from "@/context/Alerts";
import { PUB_CHAIN, PUB_TOKEN_ADDRESS } from "@/constants";
import { PUB_CHAIN } from "@/constants";
import useConstant from "./useConstant";

export function usePermit() {
const { addAlert } = useAlerts() as AlertContextProps;

const { publicTokenAddress } = useConstant();

const account_address = useAccount().address!;
const erc20Contract = {
address: PUB_TOKEN_ADDRESS,
address: publicTokenAddress as Address,
abi: ERC20PermitAbi,
};
const { data: erc20data, refetch: erc20refetch } = useReadContracts({
Expand Down Expand Up @@ -70,7 +73,7 @@ export function usePermit() {
chainId: PUB_CHAIN.id,
name: erc20_name,
version: versionFromContract,
verifyingContract: PUB_TOKEN_ADDRESS,
verifyingContract: publicTokenAddress as Address,
};

const types = {
Expand Down
84 changes: 84 additions & 0 deletions hooks/usePlugins.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { useEffect, useState } from "react";
import { useChainId } from "wagmi";
import { IconType } from "@aragon/ods";
import { Address } from "viem";
import { crab, darwinia } from "viem/chains";

type PluginItem = {
/** The URL fragment after /plugins */
id: string;
/** The name of the folder within `/plugins` */
folderName: string;
/** Title on menu */
title: string;
icon: IconType;
pluginAddress: string;
};

const darwiniaPlugins: PluginItem[] = [
{
id: "community-proposals",
folderName: "tokenVoting",
title: "Community proposals",
icon: IconType.BLOCKCHAIN_BLOCKCHAIN,
pluginAddress: (process.env.NEXT_PUBLIC_TOKEN_VOTING_PLUGIN_ADDRESS ?? "") as Address,
},
{
id: "delegate-wall",
folderName: "delegateAnnouncer",
title: "Delegation",
icon: IconType.FEEDBACK,
pluginAddress: (process.env.NEXT_PUBLIC_DELEGATION_CONTRACT_ADDRESS ?? "") as Address,
},
];

const carbPlugins: PluginItem[] = [
{
id: "community-proposals",
folderName: "tokenVoting",
title: "Community proposals",
icon: IconType.BLOCKCHAIN_BLOCKCHAIN,
pluginAddress: (process.env.NEXT_PUBLIC_TOKEN_VOTING_PLUGIN_ADDRESS_CRAB ?? "") as Address,
},
{
id: "delegate-wall",
folderName: "delegateAnnouncer",
title: "Delegation",
icon: IconType.FEEDBACK,
pluginAddress: (process.env.NEXT_PUBLIC_DELEGATION_CONTRACT_ADDRESS_CRAB ?? "") as Address,
},
];

const koiPlugins: PluginItem[] = [
{
id: "community-proposals",
folderName: "tokenVoting",
title: "Community proposals",
icon: IconType.BLOCKCHAIN_BLOCKCHAIN,
pluginAddress: (process.env.NEXT_PUBLIC_TOKEN_VOTING_PLUGIN_ADDRESS_KOI ?? "") as Address,
},
{
id: "delegate-wall",
folderName: "delegateAnnouncer",
title: "Delegation",
icon: IconType.FEEDBACK,
pluginAddress: (process.env.NEXT_PUBLIC_DELEGATION_CONTRACT_ADDRESS_KOI ?? "") as Address,
},
];

export default function usePlugins() {
const chainId = useChainId();
const [plugins, setPlugins] = useState<PluginItem[]>(darwiniaPlugins);

useEffect(() => {
if (chainId === darwinia.id) {
setPlugins([...darwiniaPlugins]);
} else if (chainId === crab.id) {
setPlugins([...carbPlugins]);
} else if (chainId === 701) {
setPlugins([...koiPlugins]);
}
}, [chainId]);

return plugins;
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"classnames": "^2.5.1",
"dayjs": "^1.11.10",
"dompurify": "^3.0.11",
"ethers": "^6.13.4",
"ipfs-http-client": "51.0.0",
"next": "14.1.4",
"react": "^18.2.0",
Expand Down
5 changes: 4 additions & 1 deletion pages/plugins/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ import { useRouter } from "next/router";
import { PleaseWaitSpinner } from "@/components/please-wait";
import { resolveQueryParam } from "@/utils/query";
import { NotFound } from "@/components/not-found";
import { plugins } from "@/plugins";
// import { plugins } from "@/plugins";
import { logger } from "@/services/logger";
import usePlugins from "@/hooks/usePlugins";

const PluginLoader: FC = () => {
const { query } = useRouter();
const pluginId = resolveQueryParam(query.id);
const [PageComponent, setPageComponent] = useState<FC | null>(null);
const [componentLoading, setComponentLoading] = useState(true);

const plugins = usePlugins();

useEffect(() => {
if (!pluginId) return;

Expand Down
8 changes: 5 additions & 3 deletions plugins/delegateAnnouncer/components/UserDelegateCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ 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 useConstant from "@/hooks/useConstant";

type SelfDelegationProfileCardProps = {
address: Address;
Expand Down Expand Up @@ -56,12 +56,14 @@ export const SelfDelegationProfileCard = ({
});
};

const { publicDaoAddress, publicDelegationContractAddress } = useConstant();

const announceDelegate = () => {
delegateAnnouncementWrite({
abi: DelegateAnnouncerAbi,
address: PUB_DELEGATION_CONTRACT_ADDRESS,
address: publicDelegationContractAddress as Address,
functionName: "announceDelegation",
args: [PUB_DAO_ADDRESS, toHex(inputDescription!)],
args: [publicDaoAddress as Address, toHex(inputDescription!)],
});
};

Expand Down
5 changes: 3 additions & 2 deletions plugins/delegateAnnouncer/hooks/useDelegateAnnouncements.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { useEffect, useState } from "react";
import { DelegateAnnouncerAbi } from "@/plugins/delegateAnnouncer/artifacts/DelegateAnnouncer.sol";
import { Address, PublicClient, getAbiItem, fromHex } from "viem";
import { DelegateAnnounce } from "../utils/types";
import { PUB_DELEGATION_ANNOUNCEMENTS_START_BLOCK } from "@/constants";
import useConstant from "@/hooks/useConstant";

const AnnounceDelegationEvent = getAbiItem({ abi: DelegateAnnouncerAbi, name: "AnnounceDelegation" });

export function useDelegateAnnouncements(publicClient: PublicClient, delegationContract: Address, daoAddress: Address) {
const [delegateAnnouncements, setDelegateAnnouncements] = useState<DelegateAnnounce[]>([]);
const [isLoading, setIsLoading] = useState<boolean>(true);
const { publicDelegationAnnouncementsStartBlock } = useConstant();

useEffect(() => {
setIsLoading(true);
Expand All @@ -19,7 +20,7 @@ export function useDelegateAnnouncements(publicClient: PublicClient, delegationC
args: {
dao: daoAddress,
} as any,
fromBlock: PUB_DELEGATION_ANNOUNCEMENTS_START_BLOCK,
fromBlock: BigInt(publicDelegationAnnouncementsStartBlock),
toBlock: "latest",
})
.then((logs) => {
Expand Down
17 changes: 9 additions & 8 deletions plugins/delegateAnnouncer/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
import { usePublicClient, useReadContract } from "wagmi";
import { useAccount } from "wagmi";
import { PublicClient, parseAbi } from "viem";
import { ReactNode } from "react";
import { Address, PublicClient, parseAbi } from "viem";
import { ReactNode, useState } from "react";
import { Else, ElseIf, If, Then } from "@/components/if";
import { PleaseWaitSpinner } from "@/components/please-wait";
import { useDelegateAnnouncements } from "../hooks/useDelegateAnnouncements";
import { DelegateCard } from "@/plugins/delegateAnnouncer/components/DelegateCard";
import { SelfDelegationProfileCard } from "../components/UserDelegateCard";
import { PUB_DAO_ADDRESS, PUB_DELEGATION_CONTRACT_ADDRESS, PUB_TOKEN_ADDRESS } from "@/constants";
import useConstant from "@/hooks/useConstant";

export default function DelegateAnnouncements() {
const publicClient = usePublicClient();
const account = useAccount();
const { publicDaoAddress, publicDelegationContractAddress, publicTokenAddress } = useConstant();
const { data: delegates, status } = useReadContract({
abi: iVotesAbi,
address: PUB_TOKEN_ADDRESS,
address: publicTokenAddress as Address,
functionName: "delegates",
args: [account.address!],
});
const { delegateAnnouncements, isLoading: delegateAnnouncementsIsLoading } = useDelegateAnnouncements(
publicClient as PublicClient,
PUB_DELEGATION_CONTRACT_ADDRESS,
PUB_DAO_ADDRESS
publicDelegationContractAddress as Address,
publicDaoAddress as Address
);

return (
Expand All @@ -31,7 +32,7 @@ export default function DelegateAnnouncements() {
<h2 className="pb-3 text-xl font-semibold text-[#fff]">Your profile</h2>
<SelfDelegationProfileCard
address={account.address!}
tokenAddress={PUB_TOKEN_ADDRESS}
tokenAddress={publicTokenAddress as Address}
delegates={delegates!}
loading={status === "pending"}
message={delegateAnnouncements.findLast((an) => an.delegate === account.address)?.message}
Expand All @@ -49,7 +50,7 @@ export default function DelegateAnnouncements() {
delegates={delegates!}
delegate={announcement.delegate}
message={announcement.message}
tokenAddress={PUB_TOKEN_ADDRESS}
tokenAddress={publicTokenAddress as Address}
/>
))}
</div>
Expand Down
Loading
Loading