diff --git a/.env.example b/.env.example
deleted file mode 100644
index 9f2cf6a..0000000
--- a/.env.example
+++ /dev/null
@@ -1,23 +0,0 @@
-# General
-NEXT_PUBLIC_DAO_ADDRESS=0x768A2d62781368AD3475F80D234778adb461aB47
-NEXT_PUBLIC_TOKEN_ADDRESS=0xBF7105C7f1cB7CB556Ad2754636f8C8D9707029e
-NEXT_PUBLIC_PWN_TOKEN_ADDRESS=0x0FE826395b1971d80A94543613E56a8b2fDF3d11
-NEXT_PUBLIC_PWN_EPOCH_CLOCK_ADDRESS=0x19e3293196aee99BB3080f28B9D3b4ea7F232b8d
-
-# Plugin addresses
-NEXT_PUBLIC_TOKEN_VOTING_PLUGIN_ADDRESS=0xED79E70122E06bB036EB6668e772FaCE4566a4cC
-NEXT_PUBLIC_DUAL_GOVERNANCE_PLUGIN_ADDRESS=0x017c1EBb09340a00Af1861C96bcaAD0Ddd37AC18 # PWNOptimisticGovernanaPlugin address
-
-NEXT_PUBLIC_STEWARD_SAFE_MULTISIG_ADDRESS=0x282D9663815b1F9929a3C84a9a1290BE882E125f
-
-# Network and services
-NEXT_PUBLIC_CHAIN_NAME="TODO (either 'sepolia' or 'mainnet')"
-NEXT_PUBLIC_RPC_URL="TODO (can be also tenderly virtual testnet)"
-
-NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID="TODO"
-
-NEXT_PUBLIC_IPFS_ENDPOINTS=https://ipfs.io/ipfs,https://api-staging.pwn.xyz/ipfs,https://gateway.pinata.cloud/ipfs,https://dweb.link/ipfs,https://4everland.io/ipfs
-
-NEXT_PUBLIC_WEB3_STORAGE_KEY="TODO"
-
-NEXT_PUBLIC_ETHERSCAN_API_KEY="TODO"
diff --git a/.github/workflows/app-build.yml b/.github/workflows/app-build.yml
deleted file mode 100644
index bf99aeb..0000000
--- a/.github/workflows/app-build.yml
+++ /dev/null
@@ -1,30 +0,0 @@
-name: Pull request build
-on:
- pull_request:
-
-jobs:
- run-build:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout code
- uses: actions/checkout@v3
-
- - uses: oven-sh/setup-bun@v1
- - run: bun install
- - name: Build
- run: bun run build
- env:
- # NEXT_PUBLIC_DAO_ADDRESS: ${{secrets.DAO_ADDRESS}}
- NEXT_PUBLIC_DAO_ADDRESS: "0x1234567890123456789012345678901234567890"
- NEXT_PUBLIC_TOKEN_ADDRESS: "0x1234567890123456789012345678901234567890"
- NEXT_PUBLIC_DELEGATION_ANNOUNCEMENTS_START_BLOCK: 1234567
- NEXT_PUBLIC_TOKEN_VOTING_PLUGIN_ADDRESS: "0x1234567890123456789012345678901234567890"
- NEXT_PUBLIC_DELEGATION_CONTRACT_ADDRESS: "0x1234567890123456789012345678901234567890"
- NEXT_PUBLIC_DUAL_GOVERNANCE_PLUGIN_ADDRESS: "0x1234567890123456789012345678901234567890"
- NEXT_PUBLIC_CHAIN_NAME: sepolia
- NEXT_PUBLIC_RPC_URL: x
- NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: x
- NEXT_PUBLIC_IPFS_ENDPOINT: https://ipfs/
- NEXT_PUBLIC_IPFS_API_KEY: x
- NEXT_PUBLIC_ETHERSCAN_API_KEY: x
- NODE_ENV: production
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 0000000..ea2ede1
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,109 @@
+name: Deploy app
+on:
+ workflow_call:
+ pull_request:
+ push:
+ branches:
+ - "master"
+
+env:
+ BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Determine environment file
+ shell: bash
+ run: |
+ if [ "$BRANCH_NAME" = "master" ]; then
+ echo "ENV_FILE=.env.app.production" >> $GITHUB_ENV
+ echo "BUILD_COMMAND=bun run build-prod" >> $GITHUB_ENV
+ echo "ENABLED_CHAIN_IDS=1" >> $GITHUB_ENV
+ echo "PREFERRED_CHAIN_ID=1" >> $GITHUB_ENV
+ else
+ echo "ENV_FILE=.env.app.development" >> $GITHUB_ENV
+ echo "BUILD_COMMAND=bun run build-dev" >> $GITHUB_ENV
+ echo "ENABLED_CHAIN_IDS=1,11155111" >> $GITHUB_ENV
+ echo "PREFERRED_CHAIN_ID=11155111" >> $GITHUB_ENV
+ fi
+
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Install Node
+ uses: actions/setup-node@v4
+ with:
+ node-version-file: 'package.json'
+
+ - uses: oven-sh/setup-bun@v2
+
+ - uses: actions/cache@v4
+ with:
+ path: |
+ ~/.bun/install/cache
+ key: |
+ ${{ runner.os }}-${{ matrix.bun }}-bun-${{ hashFiles('**/bun.lockb') }}
+ restore-keys: |
+ ${{ runner.os }}-${{ matrix.bun }}-bun-
+
+ - name: Install dependencies
+ run: bun install --frozen-lockfile
+
+ - name: Fill env values with secrets
+ uses: Langsdorf/env-replace@v1.0.7
+ with:
+ file: "env/${{ env.ENV_FILE }}"
+ replace-all: |
+ NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=${{ secrets.NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID }}
+ NEXT_PUBLIC_ETHEREUM_RPC_URL=${{ secrets.NEXT_PUBLIC_ETHEREUM_RPC_URL }}
+ NEXT_PUBLIC_SEPOLIA_RPC_URL=${{ secrets.NEXT_PUBLIC_SEPOLIA_RPC_URL }}
+ NEXT_PUBLIC_WEB3_STORAGE_KEY=${{ secrets.NEXT_PUBLIC_WEB3_STORAGE_KEY }}
+ NEXT_PUBLIC_ETHERSCAN_API_KEY=${{ secrets.NEXT_PUBLIC_ETHERSCAN_API_KEY }}
+ NEXT_PUBLIC_IPFS_ENDPOINTS=https://ipfs.io/ipfs,https://api-staging.pwn.xyz/ipfs,https://gateway.pinata.cloud/ipfs,https://dweb.link/ipfs,https://4everland.io/ipfs
+ NEXT_PUBLIC_PREFERRED_CHAIN_ID=${{ env.PREFERRED_CHAIN_ID }}
+ NEXT_PUBLIC_ENABLED_CHAIN_IDS=${{ env.ENABLED_CHAIN_IDS }}
+ upsert: true
+
+ - name: Build
+ run: ${{ env.BUILD_COMMAND }}
+
+ - name: Deploy to Cloudflare
+ id: deploy-cloudflare
+ uses: cloudflare/wrangler-action@v3
+ with:
+ apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
+ accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
+ gitHubToken: ${{ secrets.GITHUB_TOKEN }}
+ command: pages deploy out --project-name=pwn-voting-ui --branch=${{ env.BRANCH_NAME }} --commit-hash=${{ github.sha }}
+
+ - name: Add comment to PR with deployed URL
+ if: github.event_name == 'pull_request'
+ uses: thollander/actions-comment-pull-request@v3
+ with:
+ message: |
+ Deployed on <${{ steps.deploy-cloudflare.outputs.pages-deployment-alias-url }}>!
+ comment-tag: execution
+
+ # TODO make this work!
+ # - name: Deploy to IPFS
+ # if: ${{ env.BRANCH_NAME }} == 'master'
+ # uses: web3-storage/add-to-web3@v3
+ # id: deploy-ipfs
+ # with:
+ # path_to_add: '.output/public'
+ # proof: './delegation.car'
+ # secret_key: ${{ secrets.WEB3_STORAGE_KEY }}
+
+ # - name: Update DNSLink
+ # if: ${{ env.BRANCH_NAME }} == 'master'
+ # env:
+ # CLOUDFLARE_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
+ # CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }}
+ # RECORD_DOMAIN: "pwn.xyz"
+ # RECORD_NAME: "_dnslink.staking"
+ # uses: PabiGamito/cloudflare-update-dnslink@master
+ # with:
+ # cid: ${{ steps.deploy-ipfs.outputs.cid }}
+
+
diff --git a/.gitignore b/.gitignore
index 47f58cb..aacf6f2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,3 +38,6 @@ next-env.d.ts
# editor
.vscode
+
+# https://github.com/nektos/act
+bin/*
diff --git a/README.md b/README.md
index 44c939c..fcaa081 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
## Steps for testing
-1. In your wallet, set the RPC URL for Sepolia network to https://virtual.sepolia.rpc.tenderly.co/f8099806-f215-4c8a-bd88-aa34015f6b34 . This makes sure that we use the forked Sepolia network, where we can e.g. move forward time to test accepting proposals and other scenarios easily without needing to wait x days.
+1. In your wallet, set the RPC URL for Sepolia network to Tenderly fork. This makes sure that we use the forked Sepolia network, where we can e.g. move forward time to test accepting proposals and other scenarios easily without needing to wait x days.
### Gaining voting power
@@ -9,7 +9,7 @@
### Optimistic (stewards) proposals
-1. For testing creating optimistic proposals, you can import our shared dev EOA account from the pwn_secrets KeePass file to your wallet. After that go to app.safe.global, connect this imported wallet and select this safe on Sepolia 0x282D9663815b1F9929a3C84a9a1290BE882E125f . With this safe connect to the Voting UI via WalletConnect.
+1. For testing creating optimistic proposals, you can import our shared dev EOA account. After that go to app.safe.global, connect this imported wallet and select this safe on Sepolia 0x282D9663815b1F9929a3C84a9a1290BE882E125f . With this safe connect to the Voting UI via WalletConnect.
2. Now you should be able to create optimistic proposals.
# Governance App Template
diff --git a/bun.lockb b/bun.lockb
index 5e28483..8db78b4 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/components/WalletContainer.tsx b/components/WalletContainer.tsx
index 5012ad3..e498f03 100644
--- a/components/WalletContainer.tsx
+++ b/components/WalletContainer.tsx
@@ -1,28 +1,26 @@
-import { PUB_CHAIN, PUB_RPC_URL } from "@/constants";
+import { PUB_RPC_URL } from "@/constants";
import { formatHexString } from "@/utils/evm";
-import { useWeb3Modal } from "@web3modal/wagmi/react";
+import { useAppKit } from "@reown/appkit/react";
import classNames from "classnames";
-import { useEffect } from "react";
import { createClient, http } from "viem";
-import { createConfig, useAccount, useEnsName, useSwitchChain } from "wagmi";
+import { createConfig, useAccount, useEnsName } from "wagmi";
import { mainnet } from "wagmi/chains";
const config = createConfig({
- chains: [PUB_CHAIN],
+ chains: [mainnet],
ssr: true,
client({ chain }) {
return createClient({
chain,
- transport: http(PUB_RPC_URL, { batch: true }),
+ transport: http(PUB_RPC_URL[1], { batch: true }),
});
},
});
// TODO: update with ODS wallet module - [https://linear.app/aragon/issue/RD-198/create-ods-walletmodule]
const WalletContainer = () => {
- const { open } = useWeb3Modal();
- const { address, isConnected, chainId } = useAccount();
- const { switchChain } = useSwitchChain();
+ const { open } = useAppKit();
+ const { address, isConnected } = useAccount();
const { data: ensName } = useEnsName({
config,
@@ -30,13 +28,6 @@ const WalletContainer = () => {
address: address,
});
- useEffect(() => {
- if (!chainId) return;
- else if (chainId === PUB_CHAIN.id) return;
-
- switchChain({ chainId: PUB_CHAIN.id });
- }, [chainId]);
-
return (
-
+
There are no resources yet. Click the button below to add the first one.
@@ -283,7 +283,7 @@ const PlaceHolderOr = ({
canCreate: boolean | undefined;
children: ReactNode;
}) => {
- const { open } = useWeb3Modal();
+ const { open } = useAppKit();
return (
diff --git a/plugins/optimistic-proposals/pages/proposal-list.tsx b/plugins/optimistic-proposals/pages/proposal-list.tsx
index 2b52039..412289d 100644
--- a/plugins/optimistic-proposals/pages/proposal-list.tsx
+++ b/plugins/optimistic-proposals/pages/proposal-list.tsx
@@ -12,11 +12,12 @@ import {
DataListPagination,
} from "@aragon/gov-ui-kit";
import { Else, If, Then } from "@/components/if";
-import { PUB_DUAL_GOVERNANCE_PLUGIN_ADDRESS, PUB_CHAIN } from "@/constants";
+import { PUB_DUAL_GOVERNANCE_PLUGIN_ADDRESS } from "@/constants";
import { MainSection } from "@/components/layout/main-section";
import { MissingContentView } from "@/components/MissingContentView";
import { useCanCreateProposal } from "../hooks/useCanCreateProposal";
import { OptimisticTokenVotingPluginAbi } from "../artifacts/OptimisticTokenVotingPlugin.sol";
+import { useChainIdTypesafe } from "@/utils/chains";
const DEFAULT_PAGE_SIZE = 6;
@@ -24,6 +25,7 @@ export default function Proposals() {
const { isConnected } = useAccount();
const canCreateProposal = useCanCreateProposal();
const { data: blockNumber } = useBlockNumber({ watch: true });
+ const chainId = useChainIdTypesafe();
const {
data: proposalCountResponse,
@@ -32,16 +34,16 @@ export default function Proposals() {
isFetching: isFetchingNextPage,
refetch,
} = useReadContract({
- address: PUB_DUAL_GOVERNANCE_PLUGIN_ADDRESS,
+ address: PUB_DUAL_GOVERNANCE_PLUGIN_ADDRESS[chainId],
abi: OptimisticTokenVotingPluginAbi,
functionName: "proposalCount",
- chainId: PUB_CHAIN.id,
+ chainId,
});
const proposalCount = Number(proposalCountResponse);
useEffect(() => {
refetch();
- }, [blockNumber]);
+ }, [blockNumber, chainId]);
const entityLabel = proposalCount === 1 ? "Proposal" : "Proposals";
diff --git a/plugins/tokenVoting/hooks/useCanCreateProposal.ts b/plugins/tokenVoting/hooks/useCanCreateProposal.ts
index b757d43..201577c 100644
--- a/plugins/tokenVoting/hooks/useCanCreateProposal.ts
+++ b/plugins/tokenVoting/hooks/useCanCreateProposal.ts
@@ -2,17 +2,19 @@ import { Address } from "viem";
import { useState, useEffect } from "react";
import { useBalance, useAccount, useReadContracts } from "wagmi";
import { TokenVotingPluginAbi } from "../artifacts/TokenVoting.sol";
-import { PUB_CHAIN, PUB_TOKEN_VOTING_PLUGIN_ADDRESS } from "@/constants";
+import { PUB_TOKEN_VOTING_PLUGIN_ADDRESS } from "@/constants";
import { ADDRESS_ZERO } from "@/utils/evm";
+import { useChainIdTypesafe } from "@/utils/chains";
export function useCanCreateProposal() {
const { address } = useAccount();
const [minProposerVotingPower, setMinProposerVotingPower] = useState();
const [votingToken, setVotingToken] = useState();
+ const chainId = useChainIdTypesafe();
const { data: balance } = useBalance({
address,
token: votingToken,
- chainId: PUB_CHAIN.id,
+ chainId,
query: {
enabled: !!votingToken && votingToken !== ADDRESS_ZERO && !!address,
},
@@ -21,14 +23,14 @@ export function useCanCreateProposal() {
const { data: contractReads } = useReadContracts({
contracts: [
{
- chainId: PUB_CHAIN.id,
- address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS,
+ chainId,
+ address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS[chainId],
abi: TokenVotingPluginAbi,
functionName: "minProposerVotingPower",
},
{
- chainId: PUB_CHAIN.id,
- address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS,
+ chainId,
+ address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS[chainId],
abi: TokenVotingPluginAbi,
functionName: "getVotingToken",
},
@@ -40,7 +42,7 @@ export function useCanCreateProposal() {
setMinProposerVotingPower(contractReads[0].result as bigint);
setVotingToken(contractReads[1].result as Address);
- }, [contractReads?.[0]?.status, contractReads?.[1]?.status]);
+ }, [contractReads?.[0]?.result, contractReads?.[1]?.result]);
if (!address) return false;
else if (!minProposerVotingPower) return true;
diff --git a/plugins/tokenVoting/hooks/useCanVote.ts b/plugins/tokenVoting/hooks/useCanVote.ts
index 1320a28..2983ac9 100644
--- a/plugins/tokenVoting/hooks/useCanVote.ts
+++ b/plugins/tokenVoting/hooks/useCanVote.ts
@@ -2,13 +2,16 @@ import { useAccount, useBlockNumber, useReadContract } from "wagmi";
import { TokenVotingPluginAbi } from "../artifacts/TokenVoting.sol";
import { useEffect } from "react";
import { PUB_TOKEN_VOTING_PLUGIN_ADDRESS } from "@/constants";
+import { useChainIdTypesafe } from "@/utils/chains";
export function useCanVote(proposalId: number) {
const { address } = useAccount();
const { data: blockNumber } = useBlockNumber({ watch: true });
+ const chainId = useChainIdTypesafe();
const { data: canVote, refetch: refreshCanVote } = useReadContract({
- address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS,
+ address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS[chainId],
+ chainId,
abi: TokenVotingPluginAbi,
functionName: "canVote",
args: [BigInt(proposalId), address!, 1],
@@ -17,7 +20,7 @@ export function useCanVote(proposalId: number) {
useEffect(() => {
refreshCanVote();
- }, [blockNumber]);
+ }, [blockNumber, chainId]);
return canVote;
}
diff --git a/plugins/tokenVoting/hooks/useCreateProposal.ts b/plugins/tokenVoting/hooks/useCreateProposal.ts
index 8b4ddaf..05078af 100644
--- a/plugins/tokenVoting/hooks/useCreateProposal.ts
+++ b/plugins/tokenVoting/hooks/useCreateProposal.ts
@@ -2,7 +2,7 @@ import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import { ProposalMetadata, RawAction } from "@/utils/types";
import { useAlerts } from "@/context/Alerts";
-import { PUB_APP_NAME, PUB_CHAIN, PUB_TOKEN_VOTING_PLUGIN_ADDRESS, PUB_PROJECT_URL } from "@/constants";
+import { PUB_TOKEN_VOTING_PLUGIN_ADDRESS } from "@/constants";
import { uploadToWeb3Storage } from "@/utils/ipfs";
import { TokenVotingPluginAbi } from "../artifacts/TokenVoting.sol";
import { URL_PATTERN } from "@/utils/input-values";
@@ -11,6 +11,7 @@ import { VotingMode } from "../utils/types";
import { useTransactionManager } from "@/hooks/useTransactionManager";
import { useGovernanceSettings } from "./useGovernanceSettings";
import { useBlock } from "wagmi";
+import { useChainIdTypesafe } from "@/utils/chains";
const UrlRegex = new RegExp(URL_PATTERN);
@@ -29,6 +30,7 @@ export function useCreateProposal() {
const { data: blockInfo } = useBlock({
blockTag: "latest",
});
+ const chainId = useChainIdTypesafe();
useEffect(() => {
if (!minDuration || duration !== undefined) {
@@ -110,9 +112,9 @@ export function useCreateProposal() {
const endDate = blockInfo?.timestamp + BigInt(duration * 86400) + BigInt(10 * 3600);
createProposalWrite({
- chainId: PUB_CHAIN.id,
+ chainId,
abi: TokenVotingPluginAbi,
- address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS,
+ address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS[chainId],
functionName: "createProposal",
args: [toHex(ipfsPin), actions, BigInt(0), startDate, endDate, VotingMode.Standard],
});
diff --git a/plugins/tokenVoting/hooks/useGovernanceSettings.ts b/plugins/tokenVoting/hooks/useGovernanceSettings.ts
index d0a7f52..7291ef9 100644
--- a/plugins/tokenVoting/hooks/useGovernanceSettings.ts
+++ b/plugins/tokenVoting/hooks/useGovernanceSettings.ts
@@ -1,13 +1,15 @@
-import { PUB_CHAIN, PUB_DUAL_GOVERNANCE_PLUGIN_ADDRESS, PUB_TOKEN_VOTING_PLUGIN_ADDRESS } from "@/constants";
+import { PUB_TOKEN_VOTING_PLUGIN_ADDRESS } from "@/constants";
import { useReadContracts } from "wagmi";
import { useEffect, useState } from "react";
import { TokenVotingPluginAbi } from "../artifacts/TokenVoting.sol";
+import { useChainIdTypesafe } from "@/utils/chains";
export function useGovernanceSettings() {
const [minProposerVotingPower, setMinProposerVotingPower] = useState();
const [minDuration, setMinDuration] = useState(); // in seconds
const [minParticipation, setMinParticipation] = useState();
const [supportThreshold, setSupportThreshold] = useState();
+ const chainId = useChainIdTypesafe();
const {
data: contractReads,
@@ -17,26 +19,26 @@ export function useGovernanceSettings() {
} = useReadContracts({
contracts: [
{
- chainId: PUB_CHAIN.id,
- address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS,
+ chainId,
+ address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS[chainId],
abi: TokenVotingPluginAbi,
functionName: "minDuration",
},
{
- chainId: PUB_CHAIN.id,
- address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS,
+ chainId,
+ address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS[chainId],
abi: TokenVotingPluginAbi,
functionName: "minParticipation",
},
{
- chainId: PUB_CHAIN.id,
- address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS,
+ chainId,
+ address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS[chainId],
abi: TokenVotingPluginAbi,
functionName: "minProposerVotingPower",
},
{
- chainId: PUB_CHAIN.id,
- address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS,
+ chainId,
+ address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS[chainId],
abi: TokenVotingPluginAbi,
functionName: "supportThreshold",
},
@@ -59,7 +61,7 @@ export function useGovernanceSettings() {
setMinParticipation(contractReads[1].result);
setMinProposerVotingPower(contractReads[2].result);
setSupportThreshold(contractReads[3].result);
- }, [contractReads?.[0]?.status, contractReads?.[1]?.status, contractReads?.[2]?.status, contractReads?.[3]?.status]);
+ }, [contractReads?.[0]?.result, contractReads?.[1]?.result, contractReads?.[2]?.result, contractReads?.[3]?.result]);
return {
minDuration,
diff --git a/plugins/tokenVoting/hooks/usePastSupply.ts b/plugins/tokenVoting/hooks/usePastSupply.ts
index b360a34..c542712 100644
--- a/plugins/tokenVoting/hooks/usePastSupply.ts
+++ b/plugins/tokenVoting/hooks/usePastSupply.ts
@@ -1,12 +1,16 @@
import { PUB_TOKEN_ADDRESS } from "@/constants";
import { useReadContract } from "wagmi";
import { parseAbi } from "viem";
+import { useChainIdTypesafe } from "@/utils/chains";
const erc20Votes = parseAbi(["function getPastTotalSupply(uint256 blockNumber) view returns (uint256)"]);
export function usePastSupply(epoch: bigint | undefined) {
+ const chainId = useChainIdTypesafe();
+
const { data: pastSupply } = useReadContract({
- address: PUB_TOKEN_ADDRESS,
+ address: PUB_TOKEN_ADDRESS[chainId],
+ chainId,
abi: erc20Votes,
functionName: "getPastTotalSupply",
args: [BigInt(epoch || 0)],
diff --git a/plugins/tokenVoting/hooks/useProposal.ts b/plugins/tokenVoting/hooks/useProposal.ts
index ab7f0ec..2b9d6c9 100644
--- a/plugins/tokenVoting/hooks/useProposal.ts
+++ b/plugins/tokenVoting/hooks/useProposal.ts
@@ -6,6 +6,7 @@ import { RawAction, ProposalMetadata } from "@/utils/types";
import { Proposal, ProposalParameters, Tally } from "../utils/types";
import { PUB_TOKEN_VOTING_PLUGIN_ADDRESS } from "@/constants";
import { useMetadata } from "@/hooks/useMetadata";
+import { useChainIdTypesafe } from "@/utils/chains";
type ProposalCreatedLogResponse = {
args: {
@@ -29,6 +30,7 @@ export function useProposal(proposalId: number, autoRefresh = false) {
const [proposalCreationEvent, setProposalCreationEvent] = useState();
const [metadataUri, setMetadataUri] = useState();
const { data: blockNumber } = useBlockNumber();
+ const chainId = useChainIdTypesafe();
// Proposal on-chain data
const {
@@ -37,7 +39,8 @@ export function useProposal(proposalId: number, autoRefresh = false) {
fetchStatus: proposalFetchStatus,
refetch: proposalRefetch,
} = useReadContract({
- address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS,
+ address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS[chainId],
+ chainId,
abi: TokenVotingPluginAbi,
functionName: "getProposal",
args: [BigInt(proposalId)],
@@ -46,7 +49,7 @@ export function useProposal(proposalId: number, autoRefresh = false) {
useEffect(() => {
if (autoRefresh) proposalRefetch();
- }, [blockNumber]);
+ }, [blockNumber, chainId]);
// Creation event
useEffect(() => {
@@ -54,7 +57,7 @@ export function useProposal(proposalId: number, autoRefresh = false) {
publicClient
.getLogs({
- address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS,
+ address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS[chainId],
event: ProposalCreatedEvent,
args: {
proposalId: BigInt(proposalId),
@@ -73,7 +76,7 @@ export function useProposal(proposalId: number, autoRefresh = false) {
.catch((err) => {
console.error(`Could not fetch the proposal details for proposal id == ${proposalId}.`, err);
});
- }, [proposalData?.tally.yes, proposalData?.tally.no, proposalData?.tally.abstain, !!publicClient]);
+ }, [proposalData?.tally.yes, proposalData?.tally.no, proposalData?.tally.abstain, !!publicClient, chainId]);
// JSON metadata
const {
diff --git a/plugins/tokenVoting/hooks/useProposalExecute.ts b/plugins/tokenVoting/hooks/useProposalExecute.ts
index 22ca10d..18fd0d0 100644
--- a/plugins/tokenVoting/hooks/useProposalExecute.ts
+++ b/plugins/tokenVoting/hooks/useProposalExecute.ts
@@ -2,21 +2,23 @@ import { useState } from "react";
import { useReadContract } from "wagmi";
import { TokenVotingPluginAbi } from "../artifacts/TokenVoting.sol";
import { useRouter } from "next/router";
-import { PUB_CHAIN, PUB_TOKEN_VOTING_PLUGIN_ADDRESS } from "@/constants";
+import { PUB_TOKEN_VOTING_PLUGIN_ADDRESS } from "@/constants";
import { useTransactionManager } from "@/hooks/useTransactionManager";
+import { useChainIdTypesafe } from "@/utils/chains";
export function useProposalExecute(proposalId: number) {
const { reload } = useRouter();
const [isExecuting, setIsExecuting] = useState(false);
+ const chainId = useChainIdTypesafe();
const {
data: canExecute,
isError: isCanVoteError,
isLoading: isCanVoteLoading,
} = useReadContract({
- address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS,
+ address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS[chainId],
abi: TokenVotingPluginAbi,
- chainId: PUB_CHAIN.id,
+ chainId,
functionName: "canExecute",
args: [BigInt(proposalId)],
});
@@ -40,9 +42,9 @@ export function useProposalExecute(proposalId: number) {
setIsExecuting(true);
writeContract({
- chainId: PUB_CHAIN.id,
+ chainId,
abi: TokenVotingPluginAbi,
- address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS,
+ address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS[chainId],
functionName: "execute",
args: [BigInt(proposalId)],
});
diff --git a/plugins/tokenVoting/hooks/useProposalVoteList.ts b/plugins/tokenVoting/hooks/useProposalVoteList.ts
index 3c51ef3..cc4c622 100644
--- a/plugins/tokenVoting/hooks/useProposalVoteList.ts
+++ b/plugins/tokenVoting/hooks/useProposalVoteList.ts
@@ -4,12 +4,14 @@ import { TokenVotingPluginAbi } from "../artifacts/TokenVoting.sol";
import { Proposal, VoteCastEvent } from "../utils/types";
import { usePublicClient } from "wagmi";
import { PUB_TOKEN_VOTING_PLUGIN_ADDRESS } from "@/constants";
+import { useChainIdTypesafe } from "@/utils/chains";
const event = getAbiItem({ abi: TokenVotingPluginAbi, name: "VoteCast" });
export function useProposalVoteList(proposalId: number, proposal: Proposal | null) {
const publicClient = usePublicClient();
const [proposalLogs, setLogs] = useState([]);
+ const chainId = useChainIdTypesafe();
async function getLogs() {
// TODO do we need some kind of if here?
@@ -17,7 +19,7 @@ export function useProposalVoteList(proposalId: number, proposal: Proposal | nul
if (!publicClient) return;
const logs = await publicClient.getLogs({
- address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS,
+ address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS[chainId],
event: event,
args: {
proposalId: BigInt(proposalId),
@@ -32,7 +34,7 @@ export function useProposalVoteList(proposalId: number, proposal: Proposal | nul
useEffect(() => {
getLogs();
- }, [proposalId, proposal?.parameters?.snapshotEpoch]);
+ }, [proposalId, proposal?.parameters?.snapshotEpoch, chainId]);
return proposalLogs;
}
diff --git a/plugins/tokenVoting/hooks/useProposalVoting.ts b/plugins/tokenVoting/hooks/useProposalVoting.ts
index 59e20da..276d8d2 100644
--- a/plugins/tokenVoting/hooks/useProposalVoting.ts
+++ b/plugins/tokenVoting/hooks/useProposalVoting.ts
@@ -2,9 +2,11 @@ import { TokenVotingPluginAbi } from "@/plugins/tokenVoting/artifacts/TokenVotin
import { useRouter } from "next/router";
import { PUB_TOKEN_VOTING_PLUGIN_ADDRESS } from "@/constants";
import { useTransactionManager } from "@/hooks/useTransactionManager";
+import { useChainIdTypesafe } from "@/utils/chains";
export function useProposalVoting(proposalIdx: number) {
const { reload } = useRouter();
+ const chainId = useChainIdTypesafe();
const {
writeContract,
@@ -20,8 +22,9 @@ export function useProposalVoting(proposalIdx: number) {
// TODO remove autoExecute
const voteProposal = (votingOption: number, autoExecute: boolean = false) => {
writeContract({
+ chainId,
abi: TokenVotingPluginAbi,
- address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS,
+ address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS[chainId],
functionName: "vote",
args: [BigInt(proposalIdx), votingOption],
});
diff --git a/plugins/tokenVoting/hooks/useToken.ts b/plugins/tokenVoting/hooks/useToken.ts
index dd4f6dc..e7b0fd5 100644
--- a/plugins/tokenVoting/hooks/useToken.ts
+++ b/plugins/tokenVoting/hooks/useToken.ts
@@ -1,14 +1,18 @@
import { erc20Abi } from "viem";
import { useReadContract } from "wagmi";
import { PUB_TOKEN_ADDRESS } from "@/constants";
+import { useChainIdTypesafe } from "@/utils/chains";
export function useToken() {
+ const chainId = useChainIdTypesafe();
+
const {
data: tokenSupply,
isError: isError1,
isLoading: isLoading1,
} = useReadContract({
- address: PUB_TOKEN_ADDRESS,
+ chainId,
+ address: PUB_TOKEN_ADDRESS[chainId],
abi: erc20Abi,
functionName: "totalSupply",
});
@@ -18,13 +22,13 @@ export function useToken() {
isError: isError2,
isLoading: isLoading2,
} = useReadContract({
- address: PUB_TOKEN_ADDRESS,
+ address: PUB_TOKEN_ADDRESS[chainId],
abi: erc20Abi,
functionName: "symbol",
});
return {
- address: PUB_TOKEN_ADDRESS,
+ address: PUB_TOKEN_ADDRESS[chainId],
tokenSupply,
symbol: tokenSymbol,
status: {
diff --git a/plugins/tokenVoting/pages/new.tsx b/plugins/tokenVoting/pages/new.tsx
index 55962cc..de6cda3 100644
--- a/plugins/tokenVoting/pages/new.tsx
+++ b/plugins/tokenVoting/pages/new.tsx
@@ -7,7 +7,7 @@ import { useCreateProposal } from "../hooks/useCreateProposal";
import { useAccount } from "wagmi";
import { useCanCreateProposal } from "../hooks/useCanCreateProposal";
import { MissingContentView } from "@/components/MissingContentView";
-import { useWeb3Modal } from "@web3modal/wagmi/react";
+import { useAppKit } from "@reown/appkit/react";
import { Address } from "viem";
import { NewActionDialog, NewActionType } from "@/components/dialogs/NewActionDialog";
import { AddActionCard } from "@/components/cards/AddActionCard";
@@ -144,7 +144,7 @@ export default function Create() {
Add links to external resources
-
+
There are no resources yet. Click the button below to add the first one.
@@ -283,7 +283,7 @@ const PlaceHolderOr = ({
canCreate: boolean | undefined;
children: ReactNode;
}) => {
- const { open } = useWeb3Modal();
+ const { open } = useAppKit();
return (
diff --git a/plugins/tokenVoting/pages/proposal-list.tsx b/plugins/tokenVoting/pages/proposal-list.tsx
index a9cc075..1e0fb52 100644
--- a/plugins/tokenVoting/pages/proposal-list.tsx
+++ b/plugins/tokenVoting/pages/proposal-list.tsx
@@ -4,7 +4,6 @@ import ProposalCard from "../components/proposal";
import { TokenVotingPluginAbi } from "../artifacts/TokenVoting.sol";
import {
Button,
- DataList,
DataListContainer,
DataListPagination,
DataListRoot,
@@ -15,9 +14,10 @@ import {
import { useCanCreateProposal } from "../hooks/useCanCreateProposal";
import Link from "next/link";
import { Else, If, Then } from "@/components/if";
-import { PUB_TOKEN_VOTING_PLUGIN_ADDRESS, PUB_CHAIN } from "@/constants";
+import { PUB_TOKEN_VOTING_PLUGIN_ADDRESS } from "@/constants";
import { MainSection } from "@/components/layout/main-section";
import { MissingContentView } from "@/components/MissingContentView";
+import { useChainIdTypesafe } from "@/utils/chains";
const DEFAULT_PAGE_SIZE = 6;
@@ -25,6 +25,7 @@ export default function Proposals() {
const { isConnected } = useAccount();
const canCreate = useCanCreateProposal();
const { data: blockNumber } = useBlockNumber({ watch: true });
+ const chainId = useChainIdTypesafe();
const {
data: proposalCountResponse,
@@ -33,16 +34,16 @@ export default function Proposals() {
isFetching: isFetchingNextPage,
refetch,
} = useReadContract({
- address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS,
+ address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS[chainId],
abi: TokenVotingPluginAbi,
functionName: "proposalCount",
- chainId: PUB_CHAIN.id,
+ chainId,
});
const proposalCount = Number(proposalCountResponse);
useEffect(() => {
refetch();
- }, [blockNumber]);
+ }, [blockNumber, chainId]);
const entityLabel = proposalCount === 1 ? "Proposal" : "Proposals";
diff --git a/plugins/tokenVoting/pages/proposal.tsx b/plugins/tokenVoting/pages/proposal.tsx
index 03a375e..4aceaef 100644
--- a/plugins/tokenVoting/pages/proposal.tsx
+++ b/plugins/tokenVoting/pages/proposal.tsx
@@ -20,6 +20,7 @@ import { PUB_TOKEN_SYMBOL, PUB_TOKEN_VOTING_PLUGIN_ADDRESS } from "@/constants";
import { useProposalVoteList } from "../hooks/useProposalVoteList";
import { usePastSupply } from "../hooks/usePastSupply";
import { useProposalRewards } from "@/hooks/useProposalRewards";
+import { useChainIdTypesafe } from "@/utils/chains";
const ABSTAIN_VALUE = 1;
const VOTE_YES_VALUE = 2;
@@ -32,6 +33,7 @@ export default function ProposalDetail({ index: proposalIdx }: { index: number }
const votes = useProposalVoteList(proposalIdx, proposal);
const { symbol: tokenSymbol } = useToken();
const [showVotingModal, setShowVotingModal] = useState(false);
+ const chainId = useChainIdTypesafe();
const { executeProposal, canExecute, isConfirming: isConfirmingExecution } = useProposalExecute(proposalIdx);
const pastSupply = usePastSupply(proposal?.parameters.snapshotEpoch);
@@ -66,7 +68,7 @@ export default function ProposalDetail({ index: proposalIdx }: { index: number }
};
}
- const { proposalRewardsResult } = useProposalRewards(PUB_TOKEN_VOTING_PLUGIN_ADDRESS, BigInt(proposalIdx));
+ const { proposalRewardsResult } = useProposalRewards(PUB_TOKEN_VOTING_PLUGIN_ADDRESS[chainId], BigInt(proposalIdx));
const onVote = (voteOption: number | null) => {
switch (voteOption) {
diff --git a/utils/chains.ts b/utils/chains.ts
index 61b6c71..b267cf2 100644
--- a/utils/chains.ts
+++ b/utils/chains.ts
@@ -1,23 +1,44 @@
-import { polygon, mainnet, sepolia, holesky, arbitrum, polygonMumbai, Chain } from "@wagmi/core/chains";
+import { config } from "@/context/Web3Modal";
+import { AppKitNetwork } from "@reown/appkit/networks";
+import { Chain, mainnet, sepolia } from "@wagmi/core/chains";
+import { mainnet as mainnetAppKit, sepolia as sepoliaAppKit } from "@reown/appkit/networks";
+import { useChainId } from "wagmi";
+import { getChainId } from "wagmi/actions";
-const chainNames = ["mainnet", "polygon", "sepolia", "holesky", "mumbai", "arbitrum"] as const;
-export type ChainName = (typeof chainNames)[number];
+export const ALLOWED_CHAIN_IDS: [1, 11155111] = [1, 11155111];
+export type ChainId = (typeof ALLOWED_CHAIN_IDS)[number];
+export type ChainName = "mainnet" | "sepolia";
-export function getChain(chainName: ChainName): Chain {
- switch (chainName) {
- case "mainnet":
+export function getViemChain(chainId: ChainId): Chain {
+ switch (chainId) {
+ case 1:
return mainnet;
- case "polygon":
- return polygon;
- case "arbitrum":
- return arbitrum;
- case "sepolia":
+ case 11155111:
return sepolia;
- case "holesky":
- return holesky;
- case "mumbai":
- return polygonMumbai;
default:
- throw new Error("Unknown chain");
+ throw new Error(`Unknown chain: ${chainId} in getViemChain.`);
}
}
+
+export function getAppKitChain(chainId: ChainId): AppKitNetwork {
+ switch (chainId) {
+ case 1:
+ return mainnetAppKit;
+ case 11155111:
+ return sepoliaAppKit;
+ default:
+ throw new Error(`Unknown chain: ${chainId} in getAppKitChain.`);
+ }
+}
+
+export function useChainIdTypesafe() {
+ return useChainId() as ChainId;
+}
+
+export function getChainIdTypesafe() {
+ return getChainId(config) as ChainId;
+}
+
+export function getCurrentViemChain() {
+ return getViemChain(getChainIdTypesafe());
+}
diff --git a/utils/proxies.ts b/utils/proxies.ts
index 139515d..181cd73 100644
--- a/utils/proxies.ts
+++ b/utils/proxies.ts
@@ -1,5 +1,5 @@
import { Address, BaseError, ContractFunctionRevertedError, Hex, PublicClient, keccak256, parseAbi, toHex } from "viem";
-import { PUB_CHAIN } from "@/constants";
+import { getCurrentViemChain } from "./chains";
const proxyAbi = parseAbi([
"function implementation() external view returns (address)",
@@ -17,7 +17,7 @@ export function isProxyContract(publicClient: PublicClient, contractAddress: Add
abi: proxyAbi,
functionName: "implementation",
args: [],
- chain: PUB_CHAIN,
+ chain: getCurrentViemChain(),
})
.then(() => true)
.catch((e: any) => {