forked from tornadocash/tornado-cli
Added Governance Subgraph
This commit is contained in:
parent
8f656af0ac
commit
5eb3a310c5
@ -1282,6 +1282,7 @@ export function tornadoProgram() {
|
||||
const {
|
||||
tornadoSubgraph,
|
||||
registrySubgraph,
|
||||
governanceSubgraph,
|
||||
tokens,
|
||||
nativeCurrency,
|
||||
routerContract,
|
||||
@ -1301,9 +1302,8 @@ export function tornadoProgram() {
|
||||
const governanceService = new NodeGovernanceService({
|
||||
netId,
|
||||
provider,
|
||||
// to-do connect governance with subgraph
|
||||
graphApi: '',
|
||||
subgraphName: '',
|
||||
graphApi,
|
||||
subgraphName: governanceSubgraph,
|
||||
Governance: Governance__factory.connect(governanceContract, provider),
|
||||
deployedBlock: GOVERNANCE_BLOCK,
|
||||
fetchDataOptions,
|
||||
|
@ -22,6 +22,7 @@ import type {
|
||||
DepositsEvents,
|
||||
WithdrawalsEvents,
|
||||
EncryptedNotesEvents,
|
||||
AllGovernanceEvents,
|
||||
GovernanceProposalCreatedEvents,
|
||||
GovernanceVotedEvents,
|
||||
GovernanceDelegatedEvents,
|
||||
@ -589,12 +590,6 @@ export class BaseEncryptedNotesService extends BaseEventsService<EncryptedNotesE
|
||||
}
|
||||
}
|
||||
|
||||
export type BaseGovernanceEventTypes =
|
||||
| GovernanceProposalCreatedEvents
|
||||
| GovernanceVotedEvents
|
||||
| GovernanceDelegatedEvents
|
||||
| GovernanceUndelegatedEvents;
|
||||
|
||||
export type BaseGovernanceServiceConstructor = {
|
||||
netId: number | string;
|
||||
provider: Provider;
|
||||
@ -605,7 +600,7 @@ export type BaseGovernanceServiceConstructor = {
|
||||
fetchDataOptions?: fetchDataOptions;
|
||||
};
|
||||
|
||||
export class BaseGovernanceService extends BaseEventsService<BaseGovernanceEventTypes> {
|
||||
export class BaseGovernanceService extends BaseEventsService<AllGovernanceEvents> {
|
||||
batchTransactionService: BatchTransactionService;
|
||||
|
||||
constructor({
|
||||
@ -634,70 +629,71 @@ export class BaseGovernanceService extends BaseEventsService<BaseGovernanceEvent
|
||||
}
|
||||
|
||||
getGraphMethod() {
|
||||
return 'getGovernanceEvents';
|
||||
return 'getAllGovernanceEvents';
|
||||
}
|
||||
|
||||
async formatEvents(events: EventLog[]): Promise<BaseGovernanceEventTypes[]> {
|
||||
const formattedEvents = events
|
||||
.map(({ blockNumber, index: logIndex, transactionHash, args, eventName: event }) => {
|
||||
const eventObjects = {
|
||||
blockNumber,
|
||||
logIndex,
|
||||
transactionHash,
|
||||
event,
|
||||
};
|
||||
async formatEvents(events: EventLog[]): Promise<AllGovernanceEvents[]> {
|
||||
const proposalEvents: GovernanceProposalCreatedEvents[] = [];
|
||||
const votedEvents: GovernanceVotedEvents[] = [];
|
||||
const delegatedEvents: GovernanceDelegatedEvents[] = [];
|
||||
const undelegatedEvents: GovernanceUndelegatedEvents[] = [];
|
||||
|
||||
if (event === 'ProposalCreated') {
|
||||
const { id, proposer, target, startTime, endTime, description } = args;
|
||||
return {
|
||||
...eventObjects,
|
||||
id,
|
||||
proposer,
|
||||
target,
|
||||
startTime,
|
||||
endTime,
|
||||
description,
|
||||
} as GovernanceProposalCreatedEvents;
|
||||
}
|
||||
events.forEach(({ blockNumber, index: logIndex, transactionHash, args, eventName: event }) => {
|
||||
const eventObjects = {
|
||||
blockNumber,
|
||||
logIndex,
|
||||
transactionHash,
|
||||
event,
|
||||
};
|
||||
|
||||
if (event === 'Voted') {
|
||||
const { proposalId, voter, support, votes } = args;
|
||||
return {
|
||||
...eventObjects,
|
||||
proposalId,
|
||||
voter,
|
||||
support,
|
||||
votes,
|
||||
} as GovernanceVotedEvents;
|
||||
}
|
||||
if (event === 'ProposalCreated') {
|
||||
const { id, proposer, target, startTime, endTime, description } = args;
|
||||
|
||||
if (event === 'Delegated') {
|
||||
const { account, to: delegateTo } = args;
|
||||
return {
|
||||
...eventObjects,
|
||||
account,
|
||||
delegateTo,
|
||||
} as GovernanceDelegatedEvents;
|
||||
}
|
||||
proposalEvents.push({
|
||||
...eventObjects,
|
||||
id: Number(id),
|
||||
proposer,
|
||||
target,
|
||||
startTime: Number(startTime),
|
||||
endTime: Number(endTime),
|
||||
description,
|
||||
});
|
||||
}
|
||||
|
||||
if (event === 'Undelegated') {
|
||||
const { account, from: delegateFrom } = args;
|
||||
return {
|
||||
...eventObjects,
|
||||
account,
|
||||
delegateFrom,
|
||||
} as GovernanceUndelegatedEvents;
|
||||
}
|
||||
})
|
||||
.filter((e) => e) as BaseGovernanceEventTypes[];
|
||||
if (event === 'Voted') {
|
||||
const { proposalId, voter, support, votes } = args;
|
||||
|
||||
type GovernanceVotedEventsIndexed = GovernanceVotedEvents & {
|
||||
index: number;
|
||||
};
|
||||
votedEvents.push({
|
||||
...eventObjects,
|
||||
proposalId: Number(proposalId),
|
||||
voter,
|
||||
support,
|
||||
votes,
|
||||
from: '',
|
||||
input: '',
|
||||
});
|
||||
}
|
||||
|
||||
const votedEvents = formattedEvents
|
||||
.map((event, index) => ({ ...event, index }))
|
||||
.filter(({ event }) => event === 'Voted') as GovernanceVotedEventsIndexed[];
|
||||
if (event === 'Delegated') {
|
||||
const { account, to: delegateTo } = args;
|
||||
|
||||
delegatedEvents.push({
|
||||
...eventObjects,
|
||||
account,
|
||||
delegateTo,
|
||||
});
|
||||
}
|
||||
|
||||
if (event === 'Undelegated') {
|
||||
const { account, from: delegateFrom } = args;
|
||||
|
||||
undelegatedEvents.push({
|
||||
...eventObjects,
|
||||
account,
|
||||
delegateFrom,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (votedEvents.length) {
|
||||
this.updateTransactionProgress({ percentage: 0 });
|
||||
@ -706,7 +702,7 @@ export class BaseGovernanceService extends BaseEventsService<BaseGovernanceEvent
|
||||
...new Set(votedEvents.map(({ transactionHash }) => transactionHash)),
|
||||
]);
|
||||
|
||||
votedEvents.forEach((event) => {
|
||||
votedEvents.forEach((event, index) => {
|
||||
// eslint-disable-next-line prefer-const
|
||||
let { data: input, from } = txs.find((t) => t.hash === event.transactionHash) as TransactionResponse;
|
||||
|
||||
@ -715,19 +711,17 @@ export class BaseGovernanceService extends BaseEventsService<BaseGovernanceEvent
|
||||
input = '';
|
||||
}
|
||||
|
||||
// @ts-expect-error check formattedEvents types later
|
||||
formattedEvents[event.index].from = from;
|
||||
// @ts-expect-error check formattedEvents types later
|
||||
formattedEvents[event.index].input = input;
|
||||
votedEvents[index].from = from;
|
||||
votedEvents[index].input = input;
|
||||
});
|
||||
}
|
||||
|
||||
return formattedEvents;
|
||||
return [...proposalEvents, ...votedEvents, ...delegatedEvents, ...undelegatedEvents];
|
||||
}
|
||||
|
||||
async getEventsFromGraph({ fromBlock }: { fromBlock: number }): Promise<BaseEvents<BaseGovernanceEventTypes>> {
|
||||
async getEventsFromGraph({ fromBlock }: { fromBlock: number }): Promise<BaseEvents<AllGovernanceEvents>> {
|
||||
// TheGraph doesn't support governance subgraphs
|
||||
if (!this.graphApi || this.graphApi.includes('api.thegraph.com')) {
|
||||
if (!this.graphApi || !this.subgraphName || this.graphApi.includes('api.thegraph.com')) {
|
||||
return {
|
||||
events: [],
|
||||
lastBlock: fromBlock,
|
||||
|
@ -11,7 +11,6 @@ import {
|
||||
BaseEncryptedNotesServiceConstructor,
|
||||
BaseGovernanceServiceConstructor,
|
||||
BaseRegistryServiceConstructor,
|
||||
BaseGovernanceEventTypes,
|
||||
BaseEchoServiceConstructor,
|
||||
BaseEchoService,
|
||||
} from './base';
|
||||
@ -21,6 +20,7 @@ import type {
|
||||
WithdrawalsEvents,
|
||||
EncryptedNotesEvents,
|
||||
RegistersEvents,
|
||||
AllGovernanceEvents,
|
||||
EchoEvents,
|
||||
} from './types';
|
||||
|
||||
@ -561,7 +561,7 @@ export class NodeGovernanceService extends BaseGovernanceService {
|
||||
};
|
||||
}
|
||||
|
||||
const savedEvents = await loadSavedEvents<BaseGovernanceEventTypes>({
|
||||
const savedEvents = await loadSavedEvents<AllGovernanceEvents>({
|
||||
name: this.getInstanceName(),
|
||||
userDirectory: this.userDirectory,
|
||||
deployedBlock: this.deployedBlock,
|
||||
@ -585,7 +585,7 @@ export class NodeGovernanceService extends BaseGovernanceService {
|
||||
};
|
||||
}
|
||||
|
||||
const cachedEvents = await loadCachedEvents<BaseGovernanceEventTypes>({
|
||||
const cachedEvents = await loadCachedEvents<AllGovernanceEvents>({
|
||||
name: this.getInstanceName(),
|
||||
cacheDirectory: this.cacheDirectory,
|
||||
deployedBlock: this.deployedBlock,
|
||||
@ -597,7 +597,7 @@ export class NodeGovernanceService extends BaseGovernanceService {
|
||||
return cachedEvents;
|
||||
}
|
||||
|
||||
async saveEvents({ events, lastBlock }: BaseEvents<BaseGovernanceEventTypes>) {
|
||||
async saveEvents({ events, lastBlock }: BaseEvents<AllGovernanceEvents>) {
|
||||
const instanceName = this.getInstanceName();
|
||||
|
||||
console.log('\ntotalEvents count - ', events.length);
|
||||
|
@ -48,6 +48,12 @@ export type GovernanceUndelegatedEvents = GovernanceEvents & {
|
||||
delegateFrom: string;
|
||||
};
|
||||
|
||||
export type AllGovernanceEvents =
|
||||
| GovernanceProposalCreatedEvents
|
||||
| GovernanceVotedEvents
|
||||
| GovernanceDelegatedEvents
|
||||
| GovernanceUndelegatedEvents;
|
||||
|
||||
export type RegistersEvents = MinimalEvents & RelayerParams;
|
||||
|
||||
export type DepositsEvents = MinimalEvents & {
|
||||
|
@ -9,6 +9,11 @@ import type {
|
||||
EncryptedNotesEvents,
|
||||
BatchGraphOnProgress,
|
||||
EchoEvents,
|
||||
AllGovernanceEvents,
|
||||
GovernanceProposalCreatedEvents,
|
||||
GovernanceVotedEvents,
|
||||
GovernanceDelegatedEvents,
|
||||
GovernanceUndelegatedEvents,
|
||||
} from '../events';
|
||||
import {
|
||||
_META,
|
||||
@ -19,6 +24,7 @@ import {
|
||||
GET_NOTE_ACCOUNTS,
|
||||
GET_ENCRYPTED_NOTES,
|
||||
GET_ECHO_EVENTS,
|
||||
GET_GOVERNANCE_EVENTS,
|
||||
} from './queries';
|
||||
|
||||
export * from './queries';
|
||||
@ -910,3 +916,223 @@ export async function getAllEncryptedNotes({
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export interface GraphGovernanceEvents {
|
||||
proposals: {
|
||||
blockNumber: number;
|
||||
logIndex: number;
|
||||
transactionHash: string;
|
||||
proposalId: number;
|
||||
proposer: string;
|
||||
target: string;
|
||||
startTime: number;
|
||||
endTime: number;
|
||||
description: string;
|
||||
}[];
|
||||
votes: {
|
||||
blockNumber: number;
|
||||
logIndex: number;
|
||||
transactionHash: string;
|
||||
proposalId: number;
|
||||
voter: string;
|
||||
support: boolean;
|
||||
votes: string;
|
||||
from: string;
|
||||
input: string;
|
||||
}[];
|
||||
delegates: {
|
||||
blockNumber: number;
|
||||
logIndex: number;
|
||||
transactionHash: string;
|
||||
account: string;
|
||||
delegateTo: string;
|
||||
}[];
|
||||
undelegates: {
|
||||
blockNumber: number;
|
||||
logIndex: number;
|
||||
transactionHash: string;
|
||||
account: string;
|
||||
delegateFrom: string;
|
||||
}[];
|
||||
_meta: {
|
||||
block: {
|
||||
number: number;
|
||||
};
|
||||
hasIndexingErrors: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export interface getGovernanceEventsParams {
|
||||
graphApi: string;
|
||||
subgraphName: string;
|
||||
fromBlock: number;
|
||||
fetchDataOptions?: fetchDataOptions;
|
||||
onProgress?: BatchGraphOnProgress;
|
||||
}
|
||||
|
||||
export function getGovernanceEvents({
|
||||
graphApi,
|
||||
subgraphName,
|
||||
fromBlock,
|
||||
fetchDataOptions,
|
||||
}: getGovernanceEventsParams): Promise<GraphGovernanceEvents> {
|
||||
return queryGraph<GraphGovernanceEvents>({
|
||||
graphApi,
|
||||
subgraphName,
|
||||
query: GET_GOVERNANCE_EVENTS,
|
||||
variables: {
|
||||
first,
|
||||
fromBlock,
|
||||
},
|
||||
fetchDataOptions,
|
||||
});
|
||||
}
|
||||
|
||||
export async function getAllGovernanceEvents({
|
||||
graphApi,
|
||||
subgraphName,
|
||||
fromBlock,
|
||||
fetchDataOptions,
|
||||
onProgress,
|
||||
}: getGovernanceEventsParams): Promise<BaseGraphEvents<AllGovernanceEvents>> {
|
||||
try {
|
||||
const result = [];
|
||||
|
||||
let lastSyncBlock = fromBlock;
|
||||
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
const {
|
||||
proposals,
|
||||
votes,
|
||||
delegates,
|
||||
undelegates,
|
||||
_meta: {
|
||||
block: { number: currentBlock },
|
||||
},
|
||||
} = await getGovernanceEvents({ graphApi, subgraphName, fromBlock, fetchDataOptions });
|
||||
|
||||
lastSyncBlock = currentBlock;
|
||||
|
||||
const eventsLength = proposals.length + votes.length + delegates.length + undelegates.length;
|
||||
|
||||
if (eventsLength === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
const formattedProposals: GovernanceProposalCreatedEvents[] = proposals.map(
|
||||
({ blockNumber, logIndex, transactionHash, proposalId, proposer, target, startTime, endTime, description }) => {
|
||||
return {
|
||||
blockNumber: Number(blockNumber),
|
||||
logIndex: Number(logIndex),
|
||||
transactionHash,
|
||||
event: 'ProposalCreated',
|
||||
id: Number(proposalId),
|
||||
proposer: getAddress(proposer),
|
||||
target: getAddress(target),
|
||||
startTime: Number(startTime),
|
||||
endTime: Number(endTime),
|
||||
description,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
const formattedVotes: GovernanceVotedEvents[] = votes.map(
|
||||
({ blockNumber, logIndex, transactionHash, proposalId, voter, support, votes, from, input }) => {
|
||||
// Filter spammy txs
|
||||
if (!input || input.length > 2048) {
|
||||
input = '';
|
||||
}
|
||||
|
||||
return {
|
||||
blockNumber: Number(blockNumber),
|
||||
logIndex: Number(logIndex),
|
||||
transactionHash,
|
||||
event: 'Voted',
|
||||
proposalId: Number(proposalId),
|
||||
voter: getAddress(voter),
|
||||
support,
|
||||
votes,
|
||||
from: getAddress(from),
|
||||
input,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
const formattedDelegates: GovernanceDelegatedEvents[] = delegates.map(
|
||||
({ blockNumber, logIndex, transactionHash, account, delegateTo }) => {
|
||||
return {
|
||||
blockNumber: Number(blockNumber),
|
||||
logIndex: Number(logIndex),
|
||||
transactionHash,
|
||||
event: 'Delegated',
|
||||
account: getAddress(account),
|
||||
delegateTo: getAddress(delegateTo),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
const formattedUndelegates: GovernanceUndelegatedEvents[] = undelegates.map(
|
||||
({ blockNumber, logIndex, transactionHash, account, delegateFrom }) => {
|
||||
return {
|
||||
blockNumber: Number(blockNumber),
|
||||
logIndex: Number(logIndex),
|
||||
transactionHash,
|
||||
event: 'Undelegated',
|
||||
account: getAddress(account),
|
||||
delegateFrom: getAddress(delegateFrom),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
let formattedEvents = [
|
||||
...formattedProposals,
|
||||
...formattedVotes,
|
||||
...formattedDelegates,
|
||||
...formattedUndelegates,
|
||||
].sort((a, b) => {
|
||||
if (a.blockNumber === b.blockNumber) {
|
||||
return a.logIndex - b.logIndex;
|
||||
}
|
||||
return a.blockNumber - b.blockNumber;
|
||||
});
|
||||
|
||||
if (eventsLength < 900) {
|
||||
result.push(...formattedEvents);
|
||||
break;
|
||||
}
|
||||
|
||||
const [firstEvent] = formattedEvents;
|
||||
const [lastEvent] = formattedEvents.slice(-1);
|
||||
|
||||
if (typeof onProgress === 'function') {
|
||||
onProgress({
|
||||
type: 'Governance Events',
|
||||
fromBlock: Number(firstEvent.blockNumber),
|
||||
toBlock: Number(lastEvent.blockNumber),
|
||||
count: eventsLength,
|
||||
});
|
||||
}
|
||||
|
||||
formattedEvents = formattedEvents.filter(({ blockNumber }) => blockNumber !== lastEvent.blockNumber);
|
||||
|
||||
fromBlock = Number(lastEvent.blockNumber);
|
||||
|
||||
result.push(...formattedEvents);
|
||||
}
|
||||
|
||||
const [lastEvent] = result.slice(-1);
|
||||
|
||||
return {
|
||||
events: result,
|
||||
lastSyncBlock: lastEvent && lastEvent.blockNumber >= lastSyncBlock ? lastEvent.blockNumber + 1 : lastSyncBlock,
|
||||
};
|
||||
} catch (err) {
|
||||
console.log('Error from getAllGovernance query');
|
||||
console.log(err);
|
||||
return {
|
||||
events: [],
|
||||
lastSyncBlock: fromBlock,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -140,3 +140,58 @@ export const GET_ENCRYPTED_NOTES = `
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const GET_GOVERNANCE_EVENTS = `
|
||||
query getGovernanceEvents($first: Int, $fromBlock: Int) {
|
||||
proposals(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
|
||||
blockNumber
|
||||
logIndex
|
||||
transactionHash
|
||||
proposalId
|
||||
proposer
|
||||
target
|
||||
startTime
|
||||
endTime
|
||||
description
|
||||
}
|
||||
votes(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
|
||||
blockNumber
|
||||
logIndex
|
||||
transactionHash
|
||||
proposalId
|
||||
voter
|
||||
support
|
||||
votes
|
||||
from
|
||||
input
|
||||
}
|
||||
delegates(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
|
||||
blockNumber
|
||||
logIndex
|
||||
transactionHash
|
||||
account
|
||||
delegateTo
|
||||
}
|
||||
undelegates(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
|
||||
blockNumber
|
||||
logIndex
|
||||
transactionHash
|
||||
account
|
||||
delegateFrom
|
||||
}
|
||||
_meta {
|
||||
block {
|
||||
number
|
||||
}
|
||||
hasIndexingErrors
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const GET_GOVERNANCE_APY = `
|
||||
stakeDailyBurns(first: 30, orderBy: date, orderDirection: desc) {
|
||||
id
|
||||
date
|
||||
dailyAmountBurned
|
||||
}
|
||||
`;
|
||||
|
@ -67,6 +67,7 @@ export type Config = {
|
||||
ovmGasPriceOracleContract?: string;
|
||||
tornadoSubgraph: string;
|
||||
registrySubgraph?: string;
|
||||
governanceSubgraph?: string;
|
||||
subgraphs: SubgraphUrls;
|
||||
tokens: TokenInstances;
|
||||
optionalTokens?: string[];
|
||||
@ -158,6 +159,7 @@ export const defaultConfig: networkConfig = {
|
||||
reverseRecordsContract: '0x3671aE578E63FdF66ad4F3E12CC0c0d71Ac7510C',
|
||||
tornadoSubgraph: 'tornadocash/mainnet-tornado-subgraph',
|
||||
registrySubgraph: 'tornadocash/tornado-relayer-registry',
|
||||
governanceSubgraph: 'tornadocash/tornado-governance',
|
||||
subgraphs: {
|
||||
tornado,
|
||||
theGraph,
|
||||
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user