631 lines
21 KiB
JavaScript
631 lines
21 KiB
JavaScript
import { useReadContract, useReadContracts } from "wagmi";
|
|
import { simulateContract, writeContract, waitForTransactionReceipt } from "@wagmi/core";
|
|
import toast from "react-hot-toast";
|
|
import { keccak256, stringToBytes } from 'viem'
|
|
|
|
import { config } from "../../config";
|
|
|
|
import {
|
|
GHOST_GOVERNANCE_ADDRESSES,
|
|
} from "../../constants/addresses";
|
|
import { abi as GovernorAbi } from "../../abi/GhostGovernor.json";
|
|
import { abi as GovernorCountingAbi } from "../../abi/GovernorGhostCounting.json";
|
|
import { abi as GovernorStorageAbi } from "../../abi/GovernorStorage.json";
|
|
import { abi as GovernorVotesQuorumFractionAbi } from "../../abi/GovernorVotesQuorumFraction.json";
|
|
|
|
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
|
|
import { getTokenDecimals, getTokenAbi, getTokenAddress } from "../helpers";
|
|
|
|
export const getVoteValue = (forVotes, totalVotes) => {
|
|
if (totalVotes == 0n) return 0;
|
|
const value = forVotes * 100n / totalVotes;
|
|
return Math.floor(Number(value.toString()));
|
|
}
|
|
|
|
export const getVoteTarget = (totalVotes, totalSupply) => {
|
|
if (totalSupply == 0n) return 80;
|
|
|
|
const precision = 10n ** 3n;
|
|
const valueRaw = (totalVotes * precision) / totalSupply;
|
|
const value = Number(valueRaw) / Number(precision);
|
|
|
|
const result = (5 - Math.sqrt(1 + 80/9 * (value - 0.1) )) / 4;
|
|
return Math.floor(result * 100);
|
|
}
|
|
|
|
export const useProposalVoteOf = (chainId, proposalId, who) => {
|
|
const { data, error } = useReadContract({
|
|
abi: GovernorAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "voteOf",
|
|
args: [proposalId, who],
|
|
scopeKey: `voteOf-${chainId}-${proposalId?.toString()}-${who}`,
|
|
chainId: chainId,
|
|
});
|
|
const voteOf = data ? BigInt(data) : 0n;
|
|
return { voteOf };
|
|
}
|
|
|
|
export const useProposalHash = (chainId, functions) => {
|
|
const { proposalCount } = useProposalCount(chainId);
|
|
const proposalDescription = `Proposal #${proposalCount}`;
|
|
const descriptionHash = keccak256(stringToBytes(proposalDescription));
|
|
|
|
const { data: proposalHash, refetch } = useReadContract({
|
|
abi: GovernorAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "hashProposal",
|
|
args: [
|
|
functions.map(f => f.target),
|
|
functions.map(f => f.value),
|
|
functions.map(f => f.calldata),
|
|
descriptionHash
|
|
],
|
|
scopeKey: `hashProposal-${chainId}-${functions.map(f => f.calldata)}`,
|
|
chainId: chainId,
|
|
});
|
|
|
|
return { proposalHash, proposalDescription };
|
|
}
|
|
|
|
export const useActiveProposedLock = (chainId) => {
|
|
const decimals = getTokenDecimals("GHST");
|
|
|
|
const { data: activeProposedLock, refetch } = useReadContract({
|
|
abi: GovernorAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "activeProposedLock",
|
|
scopeKey: `activeProposedLock-${chainId}`,
|
|
chainId: chainId,
|
|
});
|
|
|
|
const result = new DecimalBigNumber(
|
|
activeProposedLock ? activeProposedLock : 0n,
|
|
decimals
|
|
);
|
|
|
|
return result;
|
|
}
|
|
|
|
export const useMinQuorum = (chainId) => {
|
|
const { data: quorumNumerator, refetch: quorumNumeratorRefetch } = useReadContract({
|
|
abi: GovernorVotesQuorumFractionAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "quorumNumerator",
|
|
scopeKey: `quorumNumerator-${chainId}`,
|
|
chainId: chainId,
|
|
});
|
|
|
|
const { data: quorumDenominator, refetch: quorumDenominatorRefetch } = useReadContract({
|
|
abi: GovernorVotesQuorumFractionAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "quorumDenominator",
|
|
scopeKey: `quorumDenominator-${chainId}`,
|
|
chainId: chainId,
|
|
});
|
|
|
|
const numerator = quorumNumerator ?? 0n;
|
|
const denominator = quorumDenominator ?? 1n;
|
|
const percentage = Number(100n * numerator / denominator) / 100;
|
|
|
|
return { numerator, denominator, percentage }
|
|
}
|
|
|
|
export const useProposalThreshold = (chainId, name) => {
|
|
const decimals = getTokenDecimals(name);
|
|
const { proposalCount } = useProposalCount(chainId);
|
|
|
|
const { data } = useReadContract({
|
|
abi: GovernorStorageAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "proposalThreshold",
|
|
scopeKey: `proposalThreshold-${chainId}`,
|
|
chainId: chainId,
|
|
});
|
|
|
|
const { data: activeProposedLock } = useReadContract({
|
|
abi: GovernorAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "activeProposedLock",
|
|
scopeKey: `activeProposedLock-${chainId}`,
|
|
chainId: chainId,
|
|
});
|
|
|
|
const lastIndex = proposalCount === 0n ? 0n : proposalCount - 1n;
|
|
const { proposalId } = useProposalDetailsAt(chainId, lastIndex, {
|
|
enabled: proposalCount !== 0n
|
|
});
|
|
const { state } = useProposalState(chainId, proposalId, {
|
|
enabled: proposalCount !== 0n && !!proposalId
|
|
});
|
|
|
|
let threshold = new DecimalBigNumber(data ?? 0n, decimals);
|
|
|
|
if (proposalCount !== 0n && state !== undefined && state < 2) {
|
|
threshold = new DecimalBigNumber(activeProposedLock ?? 0n, decimals);
|
|
}
|
|
|
|
return { threshold };
|
|
}
|
|
|
|
export const useProposalCount = (chainId) => {
|
|
const { data, refetch } = useReadContract({
|
|
abi: GovernorStorageAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "proposalCount",
|
|
scopeKey: `proposalCount-${chainId}`,
|
|
chainId: chainId,
|
|
});
|
|
const proposalCount = data ?? 0n;
|
|
return { proposalCount };
|
|
}
|
|
|
|
export const useProposalState = (chainId, proposalId) => {
|
|
const { data } = useReadContract({
|
|
abi: GovernorAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "state",
|
|
args: [proposalId],
|
|
scopeKey: `state-${chainId}`,
|
|
chainId: chainId,
|
|
});
|
|
const state = data ?? 0;
|
|
return { state };
|
|
}
|
|
|
|
export const useProposalProposer = (chainId, proposalId) => {
|
|
const { data } = useReadContract({
|
|
abi: GovernorAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "proposalProposer",
|
|
args: [proposalId],
|
|
scopeKey: `proposalProposer-${chainId}`,
|
|
chainId: chainId,
|
|
});
|
|
const proposer = data ? data : "0x0000000000000000000000000000000000000000";
|
|
return { proposer };
|
|
}
|
|
|
|
export const useProposalLocked = (chainId, proposalId) => {
|
|
const decimals = getTokenDecimals(name);
|
|
const { data } = useReadContract({
|
|
abi: GovernorAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "lockedAmounts",
|
|
args: [proposalId],
|
|
scopeKey: `lockedAmounts-${chainId}`,
|
|
chainId: chainId,
|
|
});
|
|
const locked = new DecimalBigNumber(data ?? 0n, decimals);
|
|
return { locked }
|
|
}
|
|
|
|
export const useProposalQuorum = (chainId, proposalId) => {
|
|
const decimals = getTokenDecimals(name);
|
|
const { snapshot } = useProposalSnapshot(chainId, proposalId);
|
|
const { data } = useReadContract({
|
|
abi: GovernorAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "quorum",
|
|
args: [snapshot],
|
|
scopeKey: `quorum-${chainId}`,
|
|
chainId: chainId,
|
|
});
|
|
const quorum = new DecimalBigNumber(data ?? 0n, decimals);
|
|
return { quorum }
|
|
}
|
|
|
|
export const useProposalVotes = (chainId, proposalId) => {
|
|
const decimals = getTokenDecimals(name);
|
|
|
|
const { data } = useReadContract({
|
|
abi: GovernorCountingAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "proposalVotes",
|
|
args: [proposalId],
|
|
scopeKey: `proposalVotes-${chainId}`,
|
|
chainId: chainId,
|
|
});
|
|
|
|
const againstVotes = new DecimalBigNumber(data?.at(0) ?? 0n, decimals);
|
|
const forVotes = new DecimalBigNumber(data?.at(1) ?? 0n, decimals);
|
|
const totalVotes = new DecimalBigNumber(
|
|
(data?.at(0) ?? 0n) + (data?.at(1) ?? 0n),
|
|
decimals
|
|
);
|
|
|
|
return { forVotes, againstVotes, totalVotes }
|
|
}
|
|
|
|
export const useProposalSnapshot = (chainId, proposalId) => {
|
|
const { data, error } = useReadContract({
|
|
abi: GovernorAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "proposalSnapshot",
|
|
args: [proposalId],
|
|
scopeKey: `proposalSnapshot-${chainId}`,
|
|
chainId: chainId,
|
|
});
|
|
const snapshot = data ?? 0n;
|
|
return { snapshot };
|
|
}
|
|
|
|
export const useProposalDetailsAt = (chainId, index) => {
|
|
const { data, error } = useReadContract({
|
|
abi: GovernorAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "proposalDetailsAt",
|
|
args: [index],
|
|
scopeKey: `proposalDetailsAt-${chainId}-${index}`,
|
|
chainId: chainId,
|
|
});
|
|
|
|
const proposalId = data?.at(0) ?? 0n;
|
|
const proposalDetailsAt = data?.at(1)?.map((target, index) => ({
|
|
target,
|
|
value: data?.at(2)?.at(index),
|
|
calldata: data?.at(3)?.at(index),
|
|
|
|
}));
|
|
|
|
return { proposalDetailsAt, proposalId };
|
|
}
|
|
|
|
export const useProposalDetails = (chainId, proposalId) => {
|
|
const { data } = useReadContract({
|
|
abi: GovernorStorageAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "proposalDetails",
|
|
args: [proposalId],
|
|
scopeKey: `proposalDetails-${chainId}-${proposalId}`,
|
|
chainId: chainId,
|
|
});
|
|
|
|
const proposalDetails = data?.at(0)?.map((target, index) => ({
|
|
target,
|
|
value: data?.at(1)?.at(index),
|
|
calldata: data?.at(2)?.at(index),
|
|
}));
|
|
|
|
return { proposalDetails };
|
|
}
|
|
|
|
export const useProposalDeadline = (chainId, proposalId) => {
|
|
const { data, refetch } = useReadContract({
|
|
abi: GovernorStorageAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
args: [proposalId],
|
|
functionName: "proposalDeadline",
|
|
scopeKey: `proposalDeadline-${chainId}`,
|
|
chainId: chainId,
|
|
});
|
|
const deadline = data ?? 0n;
|
|
return { deadline };
|
|
}
|
|
|
|
export const useProposalVotingDelay = (chainId) => {
|
|
const { data } = useReadContract({
|
|
abi: GovernorAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "votingDelay",
|
|
scopeKey: `votingDelay-${chainId}`,
|
|
chainId: chainId,
|
|
});
|
|
const delay = data ?? 0n;
|
|
return { delay };
|
|
}
|
|
|
|
export const useProposals = (chainId, depth, searchedIndexes) => {
|
|
const decimals = getTokenDecimals("GHST");
|
|
const ghstAbi = getTokenAbi("GHST");
|
|
const ghstAddress = getTokenAddress(chainId, "GHST");
|
|
|
|
const { proposalCount } = useProposalCount(chainId);
|
|
|
|
const start = Number(proposalCount);
|
|
const end = Math.max(0, start - depth);
|
|
const indexes = searchedIndexes
|
|
? searchedIndexes.map((_, i) => i)
|
|
: [...Array(start - end)].map((_, i) => start - 1 - i);
|
|
|
|
const { data: proposalsDetailsAt } = useReadContracts({
|
|
contracts: indexes?.map(index => {
|
|
return {
|
|
abi: GovernorStorageAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "proposalDetailsAt",
|
|
args: [index],
|
|
scopeKey: `proposalDetailsAt-${chainId}-${index}`,
|
|
chainId: chainId,
|
|
}
|
|
})
|
|
});
|
|
|
|
const { data: proposalDetails } = useReadContracts({
|
|
contracts: searchedIndexes?.map(index => {
|
|
return {
|
|
abi: GovernorStorageAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "proposalDetails",
|
|
args: [index],
|
|
scopeKey: `proposalDetails-${chainId}-${index}`,
|
|
chainId: chainId,
|
|
}
|
|
})
|
|
});
|
|
|
|
const { data: proposalDeadlines } = useReadContracts({
|
|
contracts: indexes?.map(index => {
|
|
const proposalId = searchedIndexes
|
|
? searchedIndexes?.at(index)
|
|
: proposalsDetailsAt?.at(index)?.result?.at(0);
|
|
|
|
return {
|
|
abi: GovernorStorageAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "proposalDeadline",
|
|
args: [proposalId],
|
|
scopeKey: `proposalDeadline-${chainId}-${index}`,
|
|
chainId: chainId,
|
|
}
|
|
})
|
|
});
|
|
|
|
const { data: proposalVotes } = useReadContracts({
|
|
contracts: indexes?.map(index => {
|
|
const proposalId = searchedIndexes
|
|
? searchedIndexes?.at(index)
|
|
: proposalsDetailsAt?.at(index)?.result?.at(0);
|
|
|
|
return {
|
|
abi: GovernorCountingAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "proposalVotes",
|
|
args: [proposalId],
|
|
scopeKey: `proposalVotes-${chainId}-${index}`,
|
|
chainId: chainId,
|
|
}
|
|
})
|
|
});
|
|
|
|
const { data: proposalStates } = useReadContracts({
|
|
contracts: indexes?.map(index => {
|
|
const proposalId = searchedIndexes
|
|
? searchedIndexes?.at(index)
|
|
: proposalsDetailsAt?.at(index)?.result?.at(0);
|
|
|
|
return {
|
|
abi: GovernorAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "state",
|
|
args: [proposalId],
|
|
scopeKey: `state-${chainId}-${index}`,
|
|
chainId: chainId,
|
|
}
|
|
})
|
|
});
|
|
|
|
const { data: proposalSnapshots } = useReadContracts({
|
|
contracts: indexes?.map(index => {
|
|
const proposalId = searchedIndexes
|
|
? searchedIndexes?.at(index)
|
|
: proposalsDetailsAt?.at(index)?.result?.at(0);
|
|
|
|
return {
|
|
abi: GovernorAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "proposalSnapshot",
|
|
args: [proposalId],
|
|
scopeKey: `proposalSnapshot-${chainId}-${index}`,
|
|
chainId: chainId,
|
|
}
|
|
})
|
|
});
|
|
|
|
const { data: proposalQuorums } = useReadContracts({
|
|
contracts: proposalSnapshots?.map((proposal, index) => {
|
|
const timepoint = proposal?.result;
|
|
return {
|
|
abi: GovernorAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "quorum",
|
|
args: [timepoint],
|
|
scopeKey: `quorum-${chainId}-${index}`,
|
|
chainId: chainId,
|
|
}
|
|
})
|
|
});
|
|
|
|
const { data: pastTotalSupplies } = useReadContracts({
|
|
contracts: proposalSnapshots?.map((proposal, index) => {
|
|
const timepoint = proposal?.result;
|
|
return {
|
|
abi: ghstAbi,
|
|
address: ghstAddress,
|
|
functionName: "getPastTotalSupply",
|
|
args: [timepoint],
|
|
scopeKey: `getPastTotalSupply-${chainId}-${index}`,
|
|
chainId: chainId,
|
|
}
|
|
})
|
|
});
|
|
|
|
const { data: proposalProposer } = useReadContracts({
|
|
contracts: indexes?.map(index => {
|
|
const proposalId = searchedIndexes
|
|
? searchedIndexes?.at(index)
|
|
: proposalsDetailsAt?.at(index)?.result?.at(0);
|
|
|
|
return {
|
|
abi: GovernorAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: "proposalProposer",
|
|
args: [proposalId],
|
|
scopeKey: `proposalProposer-${chainId}-${index}`,
|
|
chainId: chainId,
|
|
}
|
|
})
|
|
});
|
|
|
|
const hashes = indexes?.map(index => {
|
|
let result = { short: index + 1, full: undefined };
|
|
const proposalId = searchedIndexes
|
|
? searchedIndexes?.at(index)
|
|
: proposalsDetailsAt?.at(index)?.result?.at(0);
|
|
|
|
if (proposalId) {
|
|
const hash = "0x" + proposalId.toString(16);
|
|
result.short = hash.slice(-5);
|
|
result.full = hash;
|
|
}
|
|
return result;
|
|
});
|
|
|
|
const voteValues = indexes?.map(idx => {
|
|
const index = indexes.length - idx - 1;
|
|
const againstVotes = proposalVotes?.at(index)?.result?.at(0) ?? 0n
|
|
const forVotes = proposalVotes?.at(index)?.result?.at(1) ?? 0n;
|
|
|
|
const totalVotes = againstVotes + forVotes;
|
|
return getVoteValue(forVotes, totalVotes);
|
|
});
|
|
|
|
const voteTargets = indexes?.map(idx => {
|
|
const index = indexes.length - idx - 1;
|
|
const againstVotes = proposalVotes?.at(index)?.result?.at(0) ?? 0n
|
|
const forVotes = proposalVotes?.at(index)?.result?.at(1) ?? 0n;
|
|
const totalSupply = pastTotalSupplies?.at(index)?.result ?? 0n;
|
|
|
|
const totalVotes = againstVotes + forVotes;
|
|
return getVoteTarget(totalVotes, totalSupply);
|
|
});
|
|
|
|
const proposals = indexes?.map(index => ({
|
|
hashes: hashes?.at(index),
|
|
proposer: proposalProposer?.at(index)?.result,
|
|
details: proposalsDetailsAt?.at(index)?.result,
|
|
deadline: proposalDeadlines?.at(index)?.result ?? 0n,
|
|
snapshot: proposalSnapshots?.at(index)?.result ?? 0n,
|
|
state: proposalStates?.at(index)?.result ?? 0,
|
|
pastTotalSupply: new DecimalBigNumber(pastTotalSupplies?.at(index)?.result ?? 0n, decimals),
|
|
quorum: new DecimalBigNumber(proposalQuorums?.at(index)?.result ?? 0n, decimals),
|
|
votes: proposalVotes?.at(index)?.result?.map(vote => new DecimalBigNumber(vote ?? 0n, decimals)),
|
|
voteValue: voteValues?.at(index),
|
|
voteTarget: voteTargets?.at(index),
|
|
}));
|
|
|
|
return { proposals };
|
|
}
|
|
|
|
export const releaseLocked = async (chainId, account, proposalId) => {
|
|
try {
|
|
const { request } = await simulateContract(config, {
|
|
abi: GovernorAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: 'releaseLocked',
|
|
args: [proposalId],
|
|
account: account,
|
|
chainId: chainId
|
|
});
|
|
|
|
const txHash = await writeContract(config, request);
|
|
await waitForTransactionReceipt(config, {
|
|
hash: txHash,
|
|
onReplaced: () => toast("Release locked transaction was replaced. Wait for inclusion please."),
|
|
chainId
|
|
});
|
|
|
|
toast.success("Successfully release locked funds from the governor.");
|
|
return true;
|
|
} catch (err) {
|
|
console.error(err);
|
|
toast.error("Release locked funds failed. Check logs for error detalization.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export const executeProposal = async (chainId, account, proposalId) => {
|
|
try {
|
|
const { request } = await simulateContract(config, {
|
|
abi: GovernorAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: 'execute',
|
|
args: [proposalId],
|
|
account: account,
|
|
chainId: chainId
|
|
});
|
|
|
|
const txHash = await writeContract(config, request);
|
|
await waitForTransactionReceipt(config, {
|
|
hash: txHash,
|
|
onReplaced: () => toast("Proposal execution transaction was replaced. Wait for inclusion please."),
|
|
chainId
|
|
});
|
|
|
|
toast.success("Proposal execution was successful, wait for updates.");
|
|
return true;
|
|
} catch (err) {
|
|
console.error(err);
|
|
toast.error("Proposal execution failed. Check logs for error detalization.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export const castVote = async (chainId, account, proposalId, support) => {
|
|
try {
|
|
const { request } = await simulateContract(config, {
|
|
abi: GovernorAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: 'castVote',
|
|
args: [proposalId, support],
|
|
account: account,
|
|
chainId: chainId
|
|
});
|
|
|
|
const txHash = await writeContract(config, request);
|
|
await waitForTransactionReceipt(config, {
|
|
hash: txHash,
|
|
onReplaced: () => toast("Cast vote transaction was replaced. Wait for inclusion please."),
|
|
chainId
|
|
});
|
|
|
|
toast.success("Successfully casted a vote, should be applied the proposal.");
|
|
return true;
|
|
} catch (err) {
|
|
console.error(err);
|
|
toast.error("Vote cast failed. Check logs for error detalization.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export const propose = async (chainId, account, functions, description) => {
|
|
const targets = functions.map(f => f.target);
|
|
const values = functions.map(f => f.value);
|
|
const calldatas = functions.map(f => f.calldata);
|
|
|
|
try {
|
|
const { request } = await simulateContract(config, {
|
|
abi: GovernorAbi,
|
|
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
|
functionName: 'propose',
|
|
args: [targets, values, calldatas, description],
|
|
account: account,
|
|
chainId: chainId
|
|
});
|
|
|
|
const txHash = await writeContract(config, request);
|
|
await waitForTransactionReceipt(config, {
|
|
hash: txHash,
|
|
onReplaced: () => toast("Proposal transaction was replaced. Wait for inclusion please."),
|
|
chainId
|
|
});
|
|
|
|
toast.success("Successfully proposed a set of functions to be executed.");
|
|
return true;
|
|
} catch (err) {
|
|
console.error(err);
|
|
toast.error("Proposal creation failed. Check logs for error detalization.");
|
|
return false;
|
|
}
|
|
}
|