Added Governance Event Functions

This commit is contained in:
Tornado Contrib 2024-10-18 02:18:06 +00:00
parent 0e2da438ca
commit 8600bd8842
Signed by: tornadocontrib
GPG Key ID: 60B4DF1A076C64B1
6 changed files with 741 additions and 16 deletions

38
dist/events/base.d.ts vendored

@ -5,7 +5,8 @@ import { fetchDataOptions } from '../providers';
import { type NetIdType, type SubdomainMap } from '../networkConfig';
import { RelayerParams } from '../relayerClient';
import type { TovarishClient } from '../tovarishClient';
import type { BaseEvents, CachedEvents, MinimalEvents, DepositsEvents, WithdrawalsEvents, EncryptedNotesEvents, AllGovernanceEvents, RegistersEvents, EchoEvents } from './types';
import type { ReverseRecords } from '../typechain';
import type { BaseEvents, CachedEvents, MinimalEvents, DepositsEvents, WithdrawalsEvents, EncryptedNotesEvents, AllGovernanceEvents, GovernanceProposalCreatedEvents, GovernanceVotedEvents, RegistersEvents, EchoEvents } from './types';
export declare const DEPOSIT = "deposit";
export declare const WITHDRAWAL = "withdrawal";
export interface BaseEventsServiceConstructor {
@ -137,10 +138,31 @@ export declare class BaseEncryptedNotesService extends BaseEventsService<Encrypt
getGraphMethod(): string;
formatEvents(events: EventLog[]): Promise<EncryptedNotesEvents[]>;
}
export declare const proposalState: {
[key: string]: string;
};
export interface GovernanceProposals extends GovernanceProposalCreatedEvents {
title: string;
forVotes: bigint;
againstVotes: bigint;
executed: boolean;
extended: boolean;
quorum: string;
state: string;
}
export interface GovernanceVotes extends GovernanceVotedEvents {
contact: string;
message: string;
}
export interface BaseGovernanceServiceConstructor extends Omit<BaseEventsServiceConstructor, 'contract' | 'type'> {
Governance: Governance;
Aggregator: Aggregator;
ReverseRecords: ReverseRecords;
}
export declare class BaseGovernanceService extends BaseEventsService<AllGovernanceEvents> {
Governance: Governance;
Aggregator: Aggregator;
ReverseRecords: ReverseRecords;
batchTransactionService: BatchTransactionService;
constructor(serviceConstructor: BaseGovernanceServiceConstructor);
getInstanceName(): string;
@ -150,6 +172,20 @@ export declare class BaseGovernanceService extends BaseEventsService<AllGovernan
getEventsFromGraph({ fromBlock }: {
fromBlock: number;
}): Promise<BaseEvents<AllGovernanceEvents>>;
getAllProposals(): Promise<GovernanceProposals[]>;
getVotes(proposalId: number): Promise<{
votes: GovernanceVotes[];
ensNames: {
[key: string]: string;
};
}>;
getDelegatedBalance(ethAccount: string): Promise<{
delegatedAccs: string[];
undelegatedAccs: string[];
uniq: string[];
balances: bigint[];
balance: bigint;
}>;
}
export declare function getTovarishNetworks(registryService: BaseRegistryService, relayers: CachedRelayerInfo[]): Promise<void>;
/**

159
dist/index.js vendored

@ -3158,15 +3158,91 @@ class BaseEncryptedNotesService extends BaseEventsService {
}).filter((e) => e);
}
}
const abiCoder = ethers.AbiCoder.defaultAbiCoder();
const proposalState = {
0: "Pending",
1: "Active",
2: "Defeated",
3: "Timelocked",
4: "AwaitingExecution",
5: "Executed",
6: "Expired"
};
function parseDescription(id, text) {
switch (id) {
case 1:
return {
title: text,
description: "See: https://torn.community/t/proposal-1-enable-torn-transfers/38"
};
case 10:
text = text.replace("\n", "\\n\\n");
break;
case 11:
text = text.replace('"description"', ',"description"');
break;
case 13:
text = text.replace(/\\\\n\\\\n(\s)?(\\n)?/g, "\\n");
break;
case 15:
text = text.replaceAll("'", '"');
text = text.replace('"description"', ',"description"');
break;
case 16:
text = text.replace("#16: ", "");
break;
case 21:
return {
title: "Proposal #21: Restore Governance",
description: ""
};
}
let title, description, rest;
try {
({ title, description } = JSON.parse(text));
} catch {
[title, ...rest] = text.split("\n", 2);
description = rest.join("\n");
}
return {
title,
description
};
}
function parseComment(Governance, calldata) {
try {
const methodLength = 4;
const result = abiCoder.decode(["address[]", "uint256", "bool"], ethers.dataSlice(calldata, methodLength));
const data = Governance.interface.encodeFunctionData("castDelegatedVote", result);
const length = ethers.dataLength(data);
const str = abiCoder.decode(["string"], ethers.dataSlice(calldata, length))[0];
const [contact, message] = JSON.parse(str);
return {
contact,
message
};
} catch {
return {
contact: "",
message: ""
};
}
}
class BaseGovernanceService extends BaseEventsService {
Governance;
Aggregator;
ReverseRecords;
batchTransactionService;
constructor(serviceConstructor) {
const { Governance: contract, provider } = serviceConstructor;
const { Governance, Aggregator, ReverseRecords, provider } = serviceConstructor;
super({
...serviceConstructor,
contract,
contract: Governance,
type: "*"
});
this.Governance = Governance;
this.Aggregator = Aggregator;
this.ReverseRecords = ReverseRecords;
this.batchTransactionService = new BatchTransactionService({
provider,
onProgress: this.updateTransactionProgress
@ -3259,6 +3335,84 @@ class BaseGovernanceService extends BaseEventsService {
}
return super.getEventsFromGraph({ fromBlock });
}
async getAllProposals() {
const { events } = await this.updateEvents();
const [QUORUM_VOTES, proposalStatus] = await Promise.all([
this.Governance.QUORUM_VOTES(),
this.Aggregator.getAllProposals(this.Governance.target)
]);
return events.filter((e) => e.event === "ProposalCreated").map(
(event, index) => {
const { id, description: text } = event;
const status = proposalStatus[index];
const { forVotes, againstVotes, executed, extended, state } = status;
const { title, description } = parseDescription(id, text);
const quorum = (Number(forVotes + againstVotes) / Number(QUORUM_VOTES) * 100).toFixed(0) + "%";
return {
...event,
title,
description,
forVotes,
againstVotes,
executed,
extended,
quorum,
state: proposalState[String(state)]
};
}
);
}
async getVotes(proposalId) {
const { events } = await this.getSavedEvents();
const votedEvents = events.filter(
(e) => e.event === "Voted" && e.proposalId === proposalId
);
const votes = votedEvents.map((event) => {
const { contact, message } = parseComment(this.Governance, event.input);
return {
...event,
contact,
message
};
});
const allVoters = [...new Set(votedEvents.map((e) => [e.from, e.voter]).flat())];
const names = await this.ReverseRecords.getNames(allVoters);
const ensNames = allVoters.reduce(
(acc, address, index) => {
if (names[index]) {
acc[address] = names[index];
}
return acc;
},
{}
);
return {
votes,
ensNames
};
}
async getDelegatedBalance(ethAccount) {
const { events } = await this.getSavedEvents();
const delegatedAccs = events.filter((e) => e.event === "Delegated" && e.delegateTo === ethAccount).map((e) => e.account);
const undelegatedAccs = events.filter((e) => e.event === "Undelegated" && e.delegateFrom === ethAccount).map((e) => e.account);
const undel = [...undelegatedAccs];
const uniq = delegatedAccs.filter((acc) => {
const indexUndelegated = undel.indexOf(acc);
if (indexUndelegated !== -1) {
undel.splice(indexUndelegated, 1);
return false;
}
return true;
});
const balances = await this.Aggregator.getGovernanceBalances(this.Governance.target, uniq);
return {
delegatedAccs,
undelegatedAccs,
uniq,
balances,
balance: balances.reduce((acc, curr) => acc + curr, BigInt(0))
};
}
}
async function getTovarishNetworks(registryService, relayers) {
await Promise.all(
@ -7412,6 +7566,7 @@ exports.pedersen = pedersen;
exports.pickWeightedRandomRelayer = pickWeightedRandomRelayer;
exports.populateTransaction = populateTransaction;
exports.proofSchemaType = proofSchemaType;
exports.proposalState = proposalState;
exports.queryGraph = queryGraph;
exports.rBigInt = rBigInt;
exports.registeredEventsSchema = registeredEventsSchema;

162
dist/index.mjs vendored

@ -1,4 +1,4 @@
import { FetchRequest, JsonRpcProvider, Network, EnsPlugin, GasCostPlugin, Wallet, HDNodeWallet, VoidSigner, JsonRpcSigner, BrowserProvider, getAddress, isAddress, parseEther, namehash, formatEther, Interface, Contract, computeAddress, parseUnits, Transaction, ZeroAddress } from 'ethers';
import { FetchRequest, JsonRpcProvider, Network, EnsPlugin, GasCostPlugin, Wallet, HDNodeWallet, VoidSigner, JsonRpcSigner, BrowserProvider, getAddress, isAddress, parseEther, AbiCoder, namehash, formatEther, dataSlice, dataLength, Interface, Contract, computeAddress, parseUnits, Transaction, ZeroAddress } from 'ethers';
import crossFetch from 'cross-fetch';
import { webcrypto } from 'crypto';
import BN from 'bn.js';
@ -3137,15 +3137,91 @@ class BaseEncryptedNotesService extends BaseEventsService {
}).filter((e) => e);
}
}
const abiCoder = AbiCoder.defaultAbiCoder();
const proposalState = {
0: "Pending",
1: "Active",
2: "Defeated",
3: "Timelocked",
4: "AwaitingExecution",
5: "Executed",
6: "Expired"
};
function parseDescription(id, text) {
switch (id) {
case 1:
return {
title: text,
description: "See: https://torn.community/t/proposal-1-enable-torn-transfers/38"
};
case 10:
text = text.replace("\n", "\\n\\n");
break;
case 11:
text = text.replace('"description"', ',"description"');
break;
case 13:
text = text.replace(/\\\\n\\\\n(\s)?(\\n)?/g, "\\n");
break;
case 15:
text = text.replaceAll("'", '"');
text = text.replace('"description"', ',"description"');
break;
case 16:
text = text.replace("#16: ", "");
break;
case 21:
return {
title: "Proposal #21: Restore Governance",
description: ""
};
}
let title, description, rest;
try {
({ title, description } = JSON.parse(text));
} catch {
[title, ...rest] = text.split("\n", 2);
description = rest.join("\n");
}
return {
title,
description
};
}
function parseComment(Governance, calldata) {
try {
const methodLength = 4;
const result = abiCoder.decode(["address[]", "uint256", "bool"], dataSlice(calldata, methodLength));
const data = Governance.interface.encodeFunctionData("castDelegatedVote", result);
const length = dataLength(data);
const str = abiCoder.decode(["string"], dataSlice(calldata, length))[0];
const [contact, message] = JSON.parse(str);
return {
contact,
message
};
} catch {
return {
contact: "",
message: ""
};
}
}
class BaseGovernanceService extends BaseEventsService {
Governance;
Aggregator;
ReverseRecords;
batchTransactionService;
constructor(serviceConstructor) {
const { Governance: contract, provider } = serviceConstructor;
const { Governance, Aggregator, ReverseRecords, provider } = serviceConstructor;
super({
...serviceConstructor,
contract,
contract: Governance,
type: "*"
});
this.Governance = Governance;
this.Aggregator = Aggregator;
this.ReverseRecords = ReverseRecords;
this.batchTransactionService = new BatchTransactionService({
provider,
onProgress: this.updateTransactionProgress
@ -3238,6 +3314,84 @@ class BaseGovernanceService extends BaseEventsService {
}
return super.getEventsFromGraph({ fromBlock });
}
async getAllProposals() {
const { events } = await this.updateEvents();
const [QUORUM_VOTES, proposalStatus] = await Promise.all([
this.Governance.QUORUM_VOTES(),
this.Aggregator.getAllProposals(this.Governance.target)
]);
return events.filter((e) => e.event === "ProposalCreated").map(
(event, index) => {
const { id, description: text } = event;
const status = proposalStatus[index];
const { forVotes, againstVotes, executed, extended, state } = status;
const { title, description } = parseDescription(id, text);
const quorum = (Number(forVotes + againstVotes) / Number(QUORUM_VOTES) * 100).toFixed(0) + "%";
return {
...event,
title,
description,
forVotes,
againstVotes,
executed,
extended,
quorum,
state: proposalState[String(state)]
};
}
);
}
async getVotes(proposalId) {
const { events } = await this.getSavedEvents();
const votedEvents = events.filter(
(e) => e.event === "Voted" && e.proposalId === proposalId
);
const votes = votedEvents.map((event) => {
const { contact, message } = parseComment(this.Governance, event.input);
return {
...event,
contact,
message
};
});
const allVoters = [...new Set(votedEvents.map((e) => [e.from, e.voter]).flat())];
const names = await this.ReverseRecords.getNames(allVoters);
const ensNames = allVoters.reduce(
(acc, address, index) => {
if (names[index]) {
acc[address] = names[index];
}
return acc;
},
{}
);
return {
votes,
ensNames
};
}
async getDelegatedBalance(ethAccount) {
const { events } = await this.getSavedEvents();
const delegatedAccs = events.filter((e) => e.event === "Delegated" && e.delegateTo === ethAccount).map((e) => e.account);
const undelegatedAccs = events.filter((e) => e.event === "Undelegated" && e.delegateFrom === ethAccount).map((e) => e.account);
const undel = [...undelegatedAccs];
const uniq = delegatedAccs.filter((acc) => {
const indexUndelegated = undel.indexOf(acc);
if (indexUndelegated !== -1) {
undel.splice(indexUndelegated, 1);
return false;
}
return true;
});
const balances = await this.Aggregator.getGovernanceBalances(this.Governance.target, uniq);
return {
delegatedAccs,
undelegatedAccs,
uniq,
balances,
balance: balances.reduce((acc, curr) => acc + curr, BigInt(0))
};
}
}
async function getTovarishNetworks(registryService, relayers) {
await Promise.all(
@ -7257,4 +7411,4 @@ async function calculateSnarkProof(input, circuit, provingKey) {
return { proof, args };
}
export { BaseEchoService, BaseEncryptedNotesService, BaseEventsService, BaseGovernanceService, BaseRegistryService, BaseTornadoService, BatchBlockService, BatchEventsService, BatchTransactionService, DBTornadoService, DEPOSIT, Deposit, ENS__factory, ERC20__factory, GET_DEPOSITS, GET_ECHO_EVENTS, GET_ENCRYPTED_NOTES, GET_GOVERNANCE_APY, GET_GOVERNANCE_EVENTS, GET_NOTE_ACCOUNTS, GET_REGISTERED, GET_STATISTIC, GET_WITHDRAWALS, INDEX_DB_ERROR, IndexedDB, Invoice, MAX_FEE, MAX_TOVARISH_EVENTS, MIN_FEE, MIN_STAKE_BALANCE, MerkleTreeService, Mimc, Multicall__factory, NetId, NoteAccount, OffchainOracle__factory, OvmGasPriceOracle__factory, Pedersen, RelayerClient, ReverseRecords__factory, TokenPriceOracle, TornadoBrowserProvider, TornadoFeeOracle, TornadoRpcSigner, TornadoVoidSigner, TornadoWallet, TovarishClient, WITHDRAWAL, _META, addNetwork, addressSchemaType, ajv, base64ToBytes, bigIntReplacer, bnSchemaType, bnToBytes, buffPedersenHash, bufferToBytes, bytes32BNSchemaType, bytes32SchemaType, bytesToBN, bytesToBase64, bytesToHex, calculateScore, calculateSnarkProof, chunk, concatBytes, convertETHToTokenAmount, createDeposit, crypto, customConfig, defaultConfig, defaultUserAgent, depositsEventsSchema, digest, downloadZip, echoEventsSchema, enabledChains, encryptedNotesSchema, index as factories, fetch, fetchData, fetchGetUrlFunc, gasZipID, gasZipInbounds, gasZipInput, gasZipMinMax, getActiveTokenInstances, getActiveTokens, getAllDeposits, getAllEncryptedNotes, getAllGovernanceEvents, getAllGraphEchoEvents, getAllRegisters, getAllWithdrawals, getConfig, getDeposits, getEncryptedNotes, getEventsSchemaValidator, getGovernanceEvents, getGraphEchoEvents, getHttpAgent, getIndexedDB, getInstanceByAddress, getMeta, getNetworkConfig, getNoteAccounts, getProvider, getProviderWithNetId, getRegisters, getRelayerEnsSubdomains, getStatistic, getStatusSchema, getSupportedInstances, getTokenBalances, getTovarishNetworks, getWeightRandom, getWithdrawals, governanceEventsSchema, hexToBytes, initGroth16, isNode, jobRequestSchema, jobsSchema, leBuff2Int, leInt2Buff, loadDBEvents, loadRemoteEvents, mimc, multicall, packEncryptedMessage, pedersen, pickWeightedRandomRelayer, populateTransaction, proofSchemaType, queryGraph, rBigInt, registeredEventsSchema, saveDBEvents, sleep, substring, toFixedHex, toFixedLength, unpackEncryptedMessage, unzipAsync, validateUrl, withdrawalsEventsSchema, zipAsync };
export { BaseEchoService, BaseEncryptedNotesService, BaseEventsService, BaseGovernanceService, BaseRegistryService, BaseTornadoService, BatchBlockService, BatchEventsService, BatchTransactionService, DBTornadoService, DEPOSIT, Deposit, ENS__factory, ERC20__factory, GET_DEPOSITS, GET_ECHO_EVENTS, GET_ENCRYPTED_NOTES, GET_GOVERNANCE_APY, GET_GOVERNANCE_EVENTS, GET_NOTE_ACCOUNTS, GET_REGISTERED, GET_STATISTIC, GET_WITHDRAWALS, INDEX_DB_ERROR, IndexedDB, Invoice, MAX_FEE, MAX_TOVARISH_EVENTS, MIN_FEE, MIN_STAKE_BALANCE, MerkleTreeService, Mimc, Multicall__factory, NetId, NoteAccount, OffchainOracle__factory, OvmGasPriceOracle__factory, Pedersen, RelayerClient, ReverseRecords__factory, TokenPriceOracle, TornadoBrowserProvider, TornadoFeeOracle, TornadoRpcSigner, TornadoVoidSigner, TornadoWallet, TovarishClient, WITHDRAWAL, _META, addNetwork, addressSchemaType, ajv, base64ToBytes, bigIntReplacer, bnSchemaType, bnToBytes, buffPedersenHash, bufferToBytes, bytes32BNSchemaType, bytes32SchemaType, bytesToBN, bytesToBase64, bytesToHex, calculateScore, calculateSnarkProof, chunk, concatBytes, convertETHToTokenAmount, createDeposit, crypto, customConfig, defaultConfig, defaultUserAgent, depositsEventsSchema, digest, downloadZip, echoEventsSchema, enabledChains, encryptedNotesSchema, index as factories, fetch, fetchData, fetchGetUrlFunc, gasZipID, gasZipInbounds, gasZipInput, gasZipMinMax, getActiveTokenInstances, getActiveTokens, getAllDeposits, getAllEncryptedNotes, getAllGovernanceEvents, getAllGraphEchoEvents, getAllRegisters, getAllWithdrawals, getConfig, getDeposits, getEncryptedNotes, getEventsSchemaValidator, getGovernanceEvents, getGraphEchoEvents, getHttpAgent, getIndexedDB, getInstanceByAddress, getMeta, getNetworkConfig, getNoteAccounts, getProvider, getProviderWithNetId, getRegisters, getRelayerEnsSubdomains, getStatistic, getStatusSchema, getSupportedInstances, getTokenBalances, getTovarishNetworks, getWeightRandom, getWithdrawals, governanceEventsSchema, hexToBytes, initGroth16, isNode, jobRequestSchema, jobsSchema, leBuff2Int, leInt2Buff, loadDBEvents, loadRemoteEvents, mimc, multicall, packEncryptedMessage, pedersen, pickWeightedRandomRelayer, populateTransaction, proofSchemaType, proposalState, queryGraph, rBigInt, registeredEventsSchema, saveDBEvents, sleep, substring, toFixedHex, toFixedLength, unpackEncryptedMessage, unzipAsync, validateUrl, withdrawalsEventsSchema, zipAsync };

170
dist/tornado.umd.js vendored

@ -59057,11 +59057,14 @@ class NoteAccount {
/* harmony export */ cE: () => (/* binding */ BaseRegistryService),
/* harmony export */ e0: () => (/* binding */ BaseTornadoService),
/* harmony export */ oW: () => (/* binding */ WITHDRAWAL),
/* harmony export */ sf: () => (/* binding */ proposalState),
/* harmony export */ uw: () => (/* binding */ BaseEventsService)
/* harmony export */ });
/* harmony import */ var ethers__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(30031);
/* harmony import */ var ethers__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(64563);
/* harmony import */ var ethers__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(99770);
/* harmony import */ var ethers__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(35273);
/* harmony import */ var ethers__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(36212);
/* harmony import */ var ethers__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(64563);
/* harmony import */ var ethers__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(99770);
/* harmony import */ var _graphql__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(52049);
/* harmony import */ var _batch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9723);
/* harmony import */ var _providers__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(68434);
@ -59471,15 +59474,91 @@ class BaseEncryptedNotesService extends BaseEventsService {
}).filter((e) => e);
}
}
const abiCoder = ethers__WEBPACK_IMPORTED_MODULE_6__/* .AbiCoder */ .y.defaultAbiCoder();
const proposalState = {
0: "Pending",
1: "Active",
2: "Defeated",
3: "Timelocked",
4: "AwaitingExecution",
5: "Executed",
6: "Expired"
};
function parseDescription(id, text) {
switch (id) {
case 1:
return {
title: text,
description: "See: https://torn.community/t/proposal-1-enable-torn-transfers/38"
};
case 10:
text = text.replace("\n", "\\n\\n");
break;
case 11:
text = text.replace('"description"', ',"description"');
break;
case 13:
text = text.replace(/\\\\n\\\\n(\s)?(\\n)?/g, "\\n");
break;
case 15:
text = text.replaceAll("'", '"');
text = text.replace('"description"', ',"description"');
break;
case 16:
text = text.replace("#16: ", "");
break;
case 21:
return {
title: "Proposal #21: Restore Governance",
description: ""
};
}
let title, description, rest;
try {
({ title, description } = JSON.parse(text));
} catch {
[title, ...rest] = text.split("\n", 2);
description = rest.join("\n");
}
return {
title,
description
};
}
function parseComment(Governance, calldata) {
try {
const methodLength = 4;
const result = abiCoder.decode(["address[]", "uint256", "bool"], (0,ethers__WEBPACK_IMPORTED_MODULE_7__/* .dataSlice */ .ZG)(calldata, methodLength));
const data = Governance.interface.encodeFunctionData("castDelegatedVote", result);
const length = (0,ethers__WEBPACK_IMPORTED_MODULE_7__/* .dataLength */ .pO)(data);
const str = abiCoder.decode(["string"], (0,ethers__WEBPACK_IMPORTED_MODULE_7__/* .dataSlice */ .ZG)(calldata, length))[0];
const [contact, message] = JSON.parse(str);
return {
contact,
message
};
} catch {
return {
contact: "",
message: ""
};
}
}
class BaseGovernanceService extends BaseEventsService {
Governance;
Aggregator;
ReverseRecords;
batchTransactionService;
constructor(serviceConstructor) {
const { Governance: contract, provider } = serviceConstructor;
const { Governance, Aggregator, ReverseRecords, provider } = serviceConstructor;
super({
...serviceConstructor,
contract,
contract: Governance,
type: "*"
});
this.Governance = Governance;
this.Aggregator = Aggregator;
this.ReverseRecords = ReverseRecords;
this.batchTransactionService = new _batch__WEBPACK_IMPORTED_MODULE_1__/* .BatchTransactionService */ .AF({
provider,
onProgress: this.updateTransactionProgress
@ -59572,6 +59651,84 @@ class BaseGovernanceService extends BaseEventsService {
}
return super.getEventsFromGraph({ fromBlock });
}
async getAllProposals() {
const { events } = await this.updateEvents();
const [QUORUM_VOTES, proposalStatus] = await Promise.all([
this.Governance.QUORUM_VOTES(),
this.Aggregator.getAllProposals(this.Governance.target)
]);
return events.filter((e) => e.event === "ProposalCreated").map(
(event, index) => {
const { id, description: text } = event;
const status = proposalStatus[index];
const { forVotes, againstVotes, executed, extended, state } = status;
const { title, description } = parseDescription(id, text);
const quorum = (Number(forVotes + againstVotes) / Number(QUORUM_VOTES) * 100).toFixed(0) + "%";
return {
...event,
title,
description,
forVotes,
againstVotes,
executed,
extended,
quorum,
state: proposalState[String(state)]
};
}
);
}
async getVotes(proposalId) {
const { events } = await this.getSavedEvents();
const votedEvents = events.filter(
(e) => e.event === "Voted" && e.proposalId === proposalId
);
const votes = votedEvents.map((event) => {
const { contact, message } = parseComment(this.Governance, event.input);
return {
...event,
contact,
message
};
});
const allVoters = [...new Set(votedEvents.map((e) => [e.from, e.voter]).flat())];
const names = await this.ReverseRecords.getNames(allVoters);
const ensNames = allVoters.reduce(
(acc, address, index) => {
if (names[index]) {
acc[address] = names[index];
}
return acc;
},
{}
);
return {
votes,
ensNames
};
}
async getDelegatedBalance(ethAccount) {
const { events } = await this.getSavedEvents();
const delegatedAccs = events.filter((e) => e.event === "Delegated" && e.delegateTo === ethAccount).map((e) => e.account);
const undelegatedAccs = events.filter((e) => e.event === "Undelegated" && e.delegateFrom === ethAccount).map((e) => e.account);
const undel = [...undelegatedAccs];
const uniq = delegatedAccs.filter((acc) => {
const indexUndelegated = undel.indexOf(acc);
if (indexUndelegated !== -1) {
undel.splice(indexUndelegated, 1);
return false;
}
return true;
});
const balances = await this.Aggregator.getGovernanceBalances(this.Governance.target, uniq);
return {
delegatedAccs,
undelegatedAccs,
uniq,
balances,
balance: balances.reduce((acc, curr) => acc + curr, BigInt(0))
};
}
}
async function getTovarishNetworks(registryService, relayers) {
await Promise.all(
@ -59678,7 +59835,7 @@ class BaseRegistryService extends BaseEventsService {
}
return false;
});
const relayerNameHashes = uniqueRegisters.map((r) => (0,ethers__WEBPACK_IMPORTED_MODULE_6__/* .namehash */ .kM)(r.ensName));
const relayerNameHashes = uniqueRegisters.map((r) => (0,ethers__WEBPACK_IMPORTED_MODULE_8__/* .namehash */ .kM)(r.ensName));
const [relayersData, timestamp] = await Promise.all([
this.Aggregator.relayersData.staticCall(relayerNameHashes, subdomains.concat("tovarish-relayer")),
this.provider.getBlock(lastBlock).then((b) => Number(b?.timestamp))
@ -59705,7 +59862,7 @@ class BaseRegistryService extends BaseEventsService {
relayerAddress,
isRegistered,
owner,
stakeBalance: (0,ethers__WEBPACK_IMPORTED_MODULE_7__/* .formatEther */ .ck)(stakeBalance),
stakeBalance: (0,ethers__WEBPACK_IMPORTED_MODULE_9__/* .formatEther */ .ck)(stakeBalance),
hostnames,
tovarishHost
};
@ -59900,6 +60057,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export */ getTovarishNetworks: () => (/* reexport safe */ _base__WEBPACK_IMPORTED_MODULE_1__.EU),
/* harmony export */ loadDBEvents: () => (/* reexport safe */ _db__WEBPACK_IMPORTED_MODULE_2__.w8),
/* harmony export */ loadRemoteEvents: () => (/* reexport safe */ _db__WEBPACK_IMPORTED_MODULE_2__.Oz),
/* harmony export */ proposalState: () => (/* reexport safe */ _base__WEBPACK_IMPORTED_MODULE_1__.sf),
/* harmony export */ saveDBEvents: () => (/* reexport safe */ _db__WEBPACK_IMPORTED_MODULE_2__.Fb)
/* harmony export */ });
/* harmony import */ var _types__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(61060);

File diff suppressed because one or more lines are too long

@ -8,6 +8,9 @@ import {
ContractEventName,
namehash,
formatEther,
AbiCoder,
dataLength,
dataSlice,
} from 'ethers';
import type {
@ -35,6 +38,7 @@ import { enabledChains, type NetIdType, type SubdomainMap } from '../networkConf
import { RelayerParams, MIN_STAKE_BALANCE } from '../relayerClient';
import type { TovarishClient } from '../tovarishClient';
import type { ReverseRecords } from '../typechain';
import type {
BaseEvents,
CachedEvents,
@ -597,22 +601,130 @@ export class BaseEncryptedNotesService extends BaseEventsService<EncryptedNotesE
}
}
const abiCoder = AbiCoder.defaultAbiCoder();
export const proposalState: { [key: string]: string } = {
0: 'Pending',
1: 'Active',
2: 'Defeated',
3: 'Timelocked',
4: 'AwaitingExecution',
5: 'Executed',
6: 'Expired',
};
function parseDescription(id: number, text: string): { title: string; description: string } {
switch (id) {
case 1:
return {
title: text,
description: 'See: https://torn.community/t/proposal-1-enable-torn-transfers/38',
};
case 10:
text = text.replace('\n', '\\n\\n');
break;
case 11:
text = text.replace('"description"', ',"description"');
break;
case 13:
text = text.replace(/\\\\n\\\\n(\s)?(\\n)?/g, '\\n');
break;
// Fix invalid JSON in proposal 15: replace single quotes with double and add comma before description
case 15:
// eslint-disable-next-line prettier/prettier
text = text.replaceAll('\'', '"');
text = text.replace('"description"', ',"description"');
break;
case 16:
text = text.replace('#16: ', '');
break;
// Add title to empty (without title and description) hacker proposal 21
case 21:
return {
title: 'Proposal #21: Restore Governance',
description: '',
};
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let title: string, description: string, rest: any;
try {
({ title, description } = JSON.parse(text));
} catch {
[title, ...rest] = text.split('\n', 2);
description = rest.join('\n');
}
return {
title,
description,
};
}
function parseComment(Governance: Governance, calldata: string): { contact: string; message: string } {
try {
const methodLength = 4;
const result = abiCoder.decode(['address[]', 'uint256', 'bool'], dataSlice(calldata, methodLength));
// @ts-expect-error encodeFunctionData is broken lol
const data = Governance.interface.encodeFunctionData('castDelegatedVote', result);
const length = dataLength(data);
const str: string = abiCoder.decode(['string'], dataSlice(calldata, length))[0];
const [contact, message] = JSON.parse(str) as string[];
return {
contact,
message,
};
} catch {
return {
contact: '',
message: '',
};
}
}
export interface GovernanceProposals extends GovernanceProposalCreatedEvents {
title: string;
forVotes: bigint;
againstVotes: bigint;
executed: boolean;
extended: boolean;
quorum: string;
state: string;
}
export interface GovernanceVotes extends GovernanceVotedEvents {
contact: string;
message: string;
}
export interface BaseGovernanceServiceConstructor extends Omit<BaseEventsServiceConstructor, 'contract' | 'type'> {
Governance: Governance;
Aggregator: Aggregator;
ReverseRecords: ReverseRecords;
}
export class BaseGovernanceService extends BaseEventsService<AllGovernanceEvents> {
Governance: Governance;
Aggregator: Aggregator;
ReverseRecords: ReverseRecords;
batchTransactionService: BatchTransactionService;
constructor(serviceConstructor: BaseGovernanceServiceConstructor) {
const { Governance: contract, provider } = serviceConstructor;
const { Governance, Aggregator, ReverseRecords, provider } = serviceConstructor;
super({
...serviceConstructor,
contract,
contract: Governance,
type: '*',
});
this.Governance = Governance;
this.Aggregator = Aggregator;
this.ReverseRecords = ReverseRecords;
this.batchTransactionService = new BatchTransactionService({
provider,
onProgress: this.updateTransactionProgress,
@ -729,6 +841,116 @@ export class BaseGovernanceService extends BaseEventsService<AllGovernanceEvents
return super.getEventsFromGraph({ fromBlock });
}
async getAllProposals(): Promise<GovernanceProposals[]> {
const { events } = await this.updateEvents();
const [QUORUM_VOTES, proposalStatus] = await Promise.all([
this.Governance.QUORUM_VOTES(),
this.Aggregator.getAllProposals(this.Governance.target),
]);
return (events.filter((e) => e.event === 'ProposalCreated') as GovernanceProposalCreatedEvents[]).map(
(event, index) => {
const { id, description: text } = event;
const status = proposalStatus[index];
const { forVotes, againstVotes, executed, extended, state } = status;
const { title, description } = parseDescription(id, text);
const quorum = ((Number(forVotes + againstVotes) / Number(QUORUM_VOTES)) * 100).toFixed(0) + '%';
return {
...event,
title,
description,
forVotes,
againstVotes,
executed,
extended,
quorum,
state: proposalState[String(state)],
};
},
);
}
async getVotes(proposalId: number): Promise<{
votes: GovernanceVotes[];
ensNames: {
[key: string]: string;
};
}> {
const { events } = await this.getSavedEvents();
const votedEvents = events.filter(
(e) => e.event === 'Voted' && (e as GovernanceVotedEvents).proposalId === proposalId,
) as GovernanceVotedEvents[];
const votes = votedEvents.map((event) => {
const { contact, message } = parseComment(this.Governance, event.input);
return {
...event,
contact,
message,
};
});
const allVoters = [...new Set(votedEvents.map((e) => [e.from, e.voter]).flat())];
const names = await this.ReverseRecords.getNames(allVoters);
const ensNames = allVoters.reduce(
(acc, address, index) => {
if (names[index]) {
acc[address] = names[index];
}
return acc;
},
{} as { [key: string]: string },
);
return {
votes,
ensNames,
};
}
async getDelegatedBalance(ethAccount: string) {
const { events } = await this.getSavedEvents();
const delegatedAccs = events
.filter((e) => e.event === 'Delegated' && (e as GovernanceDelegatedEvents).delegateTo === ethAccount)
.map((e) => (e as GovernanceDelegatedEvents).account);
const undelegatedAccs = events
.filter((e) => e.event === 'Undelegated' && (e as GovernanceUndelegatedEvents).delegateFrom === ethAccount)
.map((e) => (e as GovernanceUndelegatedEvents).account);
const undel = [...undelegatedAccs];
const uniq = delegatedAccs.filter((acc) => {
const indexUndelegated = undel.indexOf(acc);
if (indexUndelegated !== -1) {
undel.splice(indexUndelegated, 1);
return false;
}
return true;
});
const balances = await this.Aggregator.getGovernanceBalances(this.Governance.target, uniq);
return {
delegatedAccs,
undelegatedAccs,
uniq,
balances,
balance: balances.reduce((acc, curr) => acc + curr, BigInt(0)),
};
}
}
export async function getTovarishNetworks(registryService: BaseRegistryService, relayers: CachedRelayerInfo[]) {