From 213c2a3830c1e83b7f6cc1a26e737ebbb965201c Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 21 May 2024 09:01:02 +0300 Subject: [PATCH] Automate tetu bal proposal --- gql/.graphqlconfig | 2 +- gql/codegen.yml | 3 +- scripts/graphql/graph-service.ts | 13 ++++- scripts/graphql/tetu-bal-best-snopashot.ts | 18 ++++++ scripts/utils/create-tetu-bal-proposal.ts | 67 ++++++++++++++++++++-- scripts/utils/tools/voting-utils.ts | 34 +++++++++++ 6 files changed, 129 insertions(+), 8 deletions(-) create mode 100644 scripts/graphql/tetu-bal-best-snopashot.ts diff --git a/gql/.graphqlconfig b/gql/.graphqlconfig index de5e335..0687892 100644 --- a/gql/.graphqlconfig +++ b/gql/.graphqlconfig @@ -8,7 +8,7 @@ "user-agent": "JS GraphQL" }, "introspect": false, - "url": "https://api.thegraph.com/subgraphs/name/alexandersazonof/tetubal" + "url": "https://api.studio.thegraph.com/query/48757/tetu-bal/version/latest" } } } diff --git a/gql/codegen.yml b/gql/codegen.yml index 343c114..7471c3c 100644 --- a/gql/codegen.yml +++ b/gql/codegen.yml @@ -13,4 +13,5 @@ generates: BigInt: string Bytes: string Int8: number -schema: https://api.thegraph.com/subgraphs/name/alexandersazonof/tetubal + Timestamp: string +schema: https://api.studio.thegraph.com/query/48757/tetu-bal/version/latest diff --git a/scripts/graphql/graph-service.ts b/scripts/graphql/graph-service.ts index 3c55051..40e20e0 100644 --- a/scripts/graphql/graph-service.ts +++ b/scripts/graphql/graph-service.ts @@ -1,9 +1,10 @@ -import { UserEntity } from '../../generated/gql'; +import { TetuBlockSnapshot, UserEntity } from '../../generated/gql'; import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client/core'; import { getAllUsersOnBlockQuery } from './all-users-on-block'; +import { getTetuBalBestSnapshotQuery } from './tetu-bal-best-snopashot'; function getSubgraphUrl() { - return process.env.TETU_SUBGRAPH_URL ?? 'https://api.thegraph.com/subgraphs/name/alexandersazonof/tetubal'; + return process.env.TETU_SUBGRAPH_URL ?? 'https://api.studio.thegraph.com/query/48757/tetu-bal/version/latest'; } @@ -41,3 +42,11 @@ export async function getAllUsersOnBlock(block: number): Promise { return allUsers; } + +export async function getTetuBalBestSnapshot(timestamp: number): Promise { + const { data } = await client.query({ + variables: { timestamp }, + query: getTetuBalBestSnapshotQuery(), + }); + return data.tetuBlockSnapshots; +} \ No newline at end of file diff --git a/scripts/graphql/tetu-bal-best-snopashot.ts b/scripts/graphql/tetu-bal-best-snopashot.ts new file mode 100644 index 0000000..0041f23 --- /dev/null +++ b/scripts/graphql/tetu-bal-best-snopashot.ts @@ -0,0 +1,18 @@ +import { DocumentNode, gql } from '@apollo/client/core'; + +export function getTetuBalBestSnapshotQuery(): DocumentNode { + return gql` + query GetTetuBalBestSnapshot($timestamp: Int!) { + tetuBlockSnapshots( + where: {timestamp_gt:$timestamp} + orderBy: tetuBalPercent + orderDirection: desc + first: 1 + ) { + tetuBalPercent + tetuBalPowerPercent + block + } + } + `; +} \ No newline at end of file diff --git a/scripts/utils/create-tetu-bal-proposal.ts b/scripts/utils/create-tetu-bal-proposal.ts index 1f39f95..fab3724 100644 --- a/scripts/utils/create-tetu-bal-proposal.ts +++ b/scripts/utils/create-tetu-bal-proposal.ts @@ -5,7 +5,8 @@ import {config as dotEnvConfig} from "dotenv"; import snapshot from "@snapshot-labs/snapshot.js"; import {Proposal} from "@snapshot-labs/snapshot.js/src/sign/types"; -import {getBalancerGaugesData} from "./tools/voting-utils"; +import { getBalancerGaugesData, getLastTetuSnapshotData } from './tools/voting-utils'; +import { getTetuBalBestSnapshot } from '../graphql/graph-service'; // !!!!!!!!!!!!!!!!!!!!!!!! CHANGE ME !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! const POLYGON_SNAPSHOT_BLOCK_NUMBER = 56752282; // use the-best-block-for-snapshot.ts script @@ -90,16 +91,34 @@ async function main() { return 1 }) + const lastProposal = await getLastTetuSnapshotData(); + console.log(`Last proposal is ${lastProposal.title} at ${lastProposal.end}`) + + const parsedValue = parseTitle(lastProposal.title) + const endDate = Math.floor((new Date(parsedValue.endDate)).getTime() / 1000) + console.log(`New proposal title is ${parsedValue.title}\n New proposal end date is ${parsedValue.endDate}`) + + const tetuBalSnapshot = await getTetuBalBestSnapshot(lastProposal.end); + let block = 0; + + if (tetuBalSnapshot.length > 0) { + const snapshot = tetuBalSnapshot[0] + console.log(`Best block is ${snapshot.block} tetuBal percent ${(+snapshot.tetuBalPercent).toFixed(4)}\n`) + block = +snapshot.block; + } else { + throw new Error('No tetuBal snapshot found'); + } + try { const proposal: Proposal = { space: 'tetubal.eth', type: 'weighted', - title: TITLE, + title: parsedValue.title, body: BODY, choices, start: START_UNIX, - end: END_UNIX, - snapshot: POLYGON_SNAPSHOT_BLOCK_NUMBER, + end: endDate, + snapshot: block, plugins: '{}', app: 'snapshot', discussion: '' @@ -144,6 +163,46 @@ async function getGaugeChoices(): Promise { return Array.from(gaugeChoices.values()).sort(); } +function parseTitle(input: string): { title: string, endDate: string } { + const regex = /(BRV-)(\d+): Gauge Weights for (\d{1,2}) (\w+) - (\d{1,2}) (\w+) (\d{4})/; + const match = input.match(regex); + + if (!match) { + throw Error('Input string does not match the expected format.'); + } + + const [, prefix, number, startDay, startMonth, endDay, endMonth, year] = match; + + const newNumber: number = +number + 1; + const formattedNumber: string = newNumber.toString().padStart(number.length, '0'); + + const startDate: Date = new Date(`${startMonth} ${startDay}, ${year}`); + const endDate: Date = new Date(`${endMonth} ${endDay}, ${year}`); + + startDate.setDate(startDate.getDate() + 14); + endDate.setDate(endDate.getDate() + 14); + + const newYear: number = endDate.getFullYear(); + + const options: Intl.DateTimeFormatOptions = { day: 'numeric', month: 'long' }; + const newStartDate: string = startDate.toLocaleDateString('en-US', options); + const newEndDate: string = endDate.toLocaleDateString('en-US', options); + + const [newStartDay, newStartMonth]: string[] = newStartDate.split(' '); + const [newEndDay, newEndMonth]: string[] = newEndDate.split(' '); + + // new date logic + startDate.setDate(startDate.getDate() - 2); + const formattedNewStartDate = startDate.toUTCString(); + const dateParts = formattedNewStartDate.split(' '); + const newDateText = `${dateParts[2]} ${dateParts[1]} ${dateParts[3]} 20:00:00 UTC`; + + return { + title: `${prefix}${formattedNumber}: Gauge Weights for ${newStartDay} ${newStartMonth} - ${newEndDay} ${newEndMonth} ${newYear}`, + endDate: newDateText, + }; +} + main() .then(() => process.exit(0)) .catch(error => { diff --git a/scripts/utils/tools/voting-utils.ts b/scripts/utils/tools/voting-utils.ts index efae6a4..9ac94ca 100644 --- a/scripts/utils/tools/voting-utils.ts +++ b/scripts/utils/tools/voting-utils.ts @@ -96,6 +96,40 @@ export async function getSnapshotData(proposalId: string): Promise { return resp.proposals[0] } +// tslint:disable-next-line:no-any +export async function getLastTetuSnapshotData(): Promise { + const resp = await request( + SNAPSHOT_GRAPHQL_ENDPOINT, + gql` + query { + proposals ( + first: 1, + skip: 0, + where: { + space_in: ["tetubal.eth"], + state: "closed" + }, + orderBy: "created", + orderDirection: desc + ) { + id + title + choices + start + end + scores + space { + id + name + } + } + } + ` + ) + + return resp.proposals[0] +} + // tslint:disable-next-line:no-any export async function getSnapshotVoters(proposalId: string, voter: string): Promise { const resp = await request(