Skip to content

Commit

Permalink
WIP: proposals list
Browse files Browse the repository at this point in the history
  • Loading branch information
Argeare5 committed Nov 12, 2024
1 parent c285526 commit 4b84462
Show file tree
Hide file tree
Showing 11 changed files with 374 additions and 14 deletions.
3 changes: 3 additions & 0 deletions src/configs/configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ export const fallbackGateways = [
];

export const chainInfoHelper = initChainInformationConfig(CHAINS);

// proposals list page size
export const PAGE_SIZE = 12;
5 changes: 0 additions & 5 deletions src/old/web3/services/govDataService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,11 +323,9 @@ export class GovDataService {
const votingMachineChainIds = initialProposals
.map((data) => data.votingChainId)
.filter((value, index, self) => self.indexOf(value) === index);

const data = await Promise.all(
votingMachineChainIds.map(async (chainId) => {
const votingMachineDataHelper = this.votingMachineDataHelpers[chainId];

const formattedInitialProposals = initialProposals
.filter((proposal) => proposal.votingChainId === chainId)
.map((proposal) => {
Expand All @@ -336,9 +334,7 @@ export class GovDataService {
snapshotBlockHash: proposal.snapshotBlockHash,
};
});

const rpcUrl = this.clients[chainId].chain?.rpcUrls.default.http[0];

try {
if (!!setRpcError && rpcUrl) {
setRpcError({ isError: false, rpcUrl, chainId });
Expand Down Expand Up @@ -369,7 +365,6 @@ export class GovDataService {
}
}),
);

return data.flat();
}

Expand Down
32 changes: 32 additions & 0 deletions src/requests/fetchUserProposalData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {
GetVMProposalsData,
getVMProposalsData,
} from './utils/getVMProposalsData';

export async function fetchUserProposalData({
input,
}: {
input: GetVMProposalsData;
}) {
try {
throw new Error('TODO: API not implemented');
} catch (e) {
console.error(
'Error getting user proposal data from API, using RPC fallback',
e,
);

const preparedData = await getVMProposalsData({ ...input });

return preparedData.map((vmData) => {
return {
proposalId: vmData.proposalData.id,
votedInfo: {
support: vmData.votedInfo.support,
votedPower: vmData.votedInfo.votingPower,
},
isVoted: vmData.votedInfo.votingPower !== 0n,
};
});
}
}
20 changes: 20 additions & 0 deletions src/requests/fetchUserProposalsBalances.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {
GetVotingPowerWithDelegationByBlockHash,
getVotingPowerWithDelegationByBlockHash,
} from './utils/getVotingPowerWithDelegationByBlockHash';

export async function fetchUserProposalsBalances({
input,
}: {
input: GetVotingPowerWithDelegationByBlockHash;
}) {
try {
throw new Error('TODO: API not implemented');
} catch (e) {
console.error(
'Error getting user proposals balances from API, using RPC fallback',
e,
);
return await getVotingPowerWithDelegationByBlockHash({ ...input });
}
}
142 changes: 142 additions & 0 deletions src/requests/utils/getProposalsWithPayloadsData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import {
IGovernanceDataHelper_ABI,
IPayloadsControllerDataHelper_ABI,
} from '@bgd-labs/aave-address-book';
import { Client } from 'viem';
import { readContract } from 'viem/actions';

import { appConfig } from '../../configs/appConfig';
import { PAGE_SIZE } from '../../configs/configs';

enum ProposalState {
Null, // proposal does not exists
Created, // created, waiting for a cooldown to initiate the balances snapshot
Active, // balances snapshot set, voting in progress
Queued, // voting results submitted, but proposal is under grace period when guardian can cancel it
Executed, // results sent to the execution chain(s)
Failed, // voting was not successful
Cancelled, // got cancelled by guardian, or because proposition power of creator dropped below allowed minimum
Expired,
}

enum PayloadState {
None,
Created,
Queued,
Executed,
Cancelled,
Expired,
}

export type GetProposalsWithPayloadsData = {
proposalsCount?: number;
proposalsIds?: number[];
pageSize?: number;
client: Client;
};

export async function getProposalsWithPayloadsData({
proposalsCount,
proposalsIds,
pageSize,
client,
}: GetProposalsWithPayloadsData) {
const ids = proposalsCount
? [...Array(Number(proposalsCount)).keys()]
: (proposalsIds ?? []);

const fr = Math.max.apply(
null,
ids.map((id) => id),
);
const to = Math.min.apply(
null,
ids.map((id) => id),
);

const proposalsData = await readContract(client, {
abi: IGovernanceDataHelper_ABI,
address: appConfig.govCoreConfig.dataHelperContractAddress,
functionName: 'getProposalsData',
args: [
appConfig.govCoreConfig.contractAddress,
BigInt(fr),
BigInt(to || 0),
BigInt(pageSize || proposalsCount || PAGE_SIZE),
],
});

const payloadsData = (
await Promise.all(
proposalsData.map(async (proposal) => {
const payloadsChains = proposal.proposalData.payloads
.map((payload) => payload.chain)
.filter((value, index, self) => self.indexOf(value) === index);

return (
await Promise.all(
payloadsChains.map(async (chain) => {
const payloadsByChain = proposal.proposalData.payloads.filter(
(payload) => payload.chain === chain,
);
const payloadsController = payloadsByChain.map(
(payload) => payload.payloadsController,
)[0];

const payloadsData = await readContract(client, {
abi: IPayloadsControllerDataHelper_ABI,
address:
appConfig.payloadsControllerConfig[Number(chain)]
.dataHelperContractAddress,
functionName: 'getPayloadsData',
args: [
payloadsController,
payloadsByChain.map((payload) => payload.payloadId),
],
});

return payloadsData.map((payload) => {
return {
...payload,
chain,
payloadsController,
};
});
}),
)
).flat();
}),
)
).flat();

return proposalsData.map((proposal) => {
const proposalPayloads = proposal.proposalData.payloads.map((payload) => {
const data = payloadsData.find(
(p) =>
Number(p.id) === payload.payloadId &&
p.chain === payload.chain &&
p.payloadsController === payload.payloadsController,
);
if (data) {
return data;
} else {
return payloadsData[0]; // TODO: need fix
}
});

const isProposalPayloadsFinished = proposalPayloads.every(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
(payload) => payload && payload?.state > PayloadState.Queued,
);

return {
...proposal,
payloads: proposalPayloads,
isFinished:
proposal.proposalData.state === ProposalState.Executed
? isProposalPayloadsFinished
: proposal.proposalData.state > ProposalState.Executed,
};
});
}
63 changes: 63 additions & 0 deletions src/requests/utils/getVMProposalsData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { IVotingMachineDataHelper_ABI } from '@bgd-labs/aave-address-book';
import { Address, Client, getContract, zeroAddress } from 'viem';

import { appConfig } from '../../configs/appConfig';
import { ProposalToGetUserData } from '../../types';

export type GetVMProposalsData = {
initialProposals: ProposalToGetUserData[];
userAddress: string;
clients: Record<number, Client>;
representativeAddress?: string;
};

export async function getVMProposalsData({
initialProposals,
userAddress,
representativeAddress,
clients,
}: GetVMProposalsData) {
const votingMachineChainIds = initialProposals
.map((data) => data.votingChainId)
.filter((value, index, self) => self.indexOf(value) === index);

const data = await Promise.all(
votingMachineChainIds.map(async (chainId) => {
const votingMachineDataHelper = getContract({
abi: IVotingMachineDataHelper_ABI,
address: appConfig.votingMachineConfig[chainId].contractAddress,
client: clients[chainId],
});

const formattedInitialProposals = initialProposals
.filter((proposal) => proposal.votingChainId === chainId)
.map((proposal) => {
return {
id: proposal.id,
snapshotBlockHash: proposal.snapshotBlockHash,
};
});

if (representativeAddress && userAddress) {
if (userAddress) {
return (
(await votingMachineDataHelper.read.getProposalsData([
appConfig.votingMachineConfig[chainId].contractAddress,
formattedInitialProposals,
(representativeAddress || userAddress || zeroAddress) as Address,
])) || []
);
}
}
return (
(await votingMachineDataHelper.read.getProposalsData([
appConfig.votingMachineConfig[chainId].contractAddress,
formattedInitialProposals,
(userAddress || zeroAddress) as Address,
])) || []
);
}),
);

return data.flat();
}
67 changes: 67 additions & 0 deletions src/requests/utils/getVotingPowerWithDelegationByBlockHash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { IAaveTokenV3_ABI } from '@bgd-labs/aave-governance-ui-helpers/dist/abis/IAaveTokenV3';
import { Address, Client, Hex } from 'viem';
import { getBlock, multicall } from 'viem/actions';

import { GovernancePowerType } from '../../types';

export type GetVotingPowerWithDelegationByBlockHash = {
client: Client;
blockHash: Hex;
address: Address;
assets: Address[];
};

export async function getVotingPowerWithDelegationByBlockHash({
client,
blockHash,
address,
assets,
}: GetVotingPowerWithDelegationByBlockHash) {
const blockNumber = await getBlock(client, {
blockHash,
});

const wagmiContracts = assets.map((asset) => {
const wagmiContract = {
address: asset,
abi: IAaveTokenV3_ABI,
} as const;
return wagmiContract;
});

const data = await multicall(client, {
contracts: [
...wagmiContracts.map((contract) => {
return {
...contract,
functionName: 'balanceOf',
args: [address],
};
}),
...wagmiContracts.map((contract) => {
return {
...contract,
functionName: 'getPowerCurrent',
args: [address, GovernancePowerType.VOTING],
};
}),
],
blockNumber: blockNumber.number,
});

return assets.map((asset, index) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
const userBalance = data[index][0].result;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
const votingPower = data[index][1].result;
return {
blockHash,
asset: asset,
votingPower: votingPower as bigint,
userBalance: userBalance as bigint,
isWithDelegatedPower: userBalance !== votingPower,
};
});
}
2 changes: 1 addition & 1 deletion src/server/api/routers/configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ import { createTRPCRouter, publicProcedure } from '../trpc';

export const configsRouter = createTRPCRouter({
get: publicProcedure
.input(z.object({ govCoreClient: z.custom<Client>() }))
.input(z.object({ govCoreClient: z.custom<Client>() })) // TODO: server client
.query(async ({ input }) => await fetchInitialData({ input })),
});
11 changes: 11 additions & 0 deletions src/server/api/routers/proposals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { z } from 'zod';

import { fetchUserProposalsBalances } from '../../../requests/fetchUserProposalsBalances';
import { GetVotingPowerWithDelegationByBlockHash } from '../../../requests/utils/getVotingPowerWithDelegationByBlockHash';
import { createTRPCRouter, publicProcedure } from '../trpc';

export const proposalsRouter = createTRPCRouter({
getBalances: publicProcedure
.input(z.custom<GetVotingPowerWithDelegationByBlockHash>()) // TODO: server client
.query(async (input) => await fetchUserProposalsBalances(input)),
});
Loading

1 comment on commit 4b84462

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit was deployed on ipfs

Please sign in to comment.