Skip to content

Commit

Permalink
feat: support global delegation
Browse files Browse the repository at this point in the history
  • Loading branch information
Sekhmet committed Aug 23, 2024
1 parent 42a6127 commit 490bf60
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 21 deletions.
97 changes: 78 additions & 19 deletions src/compute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@ import { formatUnits } from '@ethersproject/units';
import { register } from '@snapshot-labs/checkpoint/dist/src/register';
import snapshotjs from '@snapshot-labs/snapshot.js';
import { Mutex } from 'async-mutex';
import { COMPUTE_DELAY_SECONDS, SCORE_API_URL } from './constants';
import { getSpace } from './hub';
import {
NETWORK_COMPUTE_DELAY_SECONDS,
SCORE_API_URL,
SPACE_COMPUTE_DELAY_SECONDS
} from './constants';
import { getSpace, Space } from './hub';
import { Delegate, Governance } from '../.checkpoint/models';

type NetworkCache = {
timestamp: number;
data: Awaited<ReturnType<typeof snapshotjs.utils.getDelegatesBySpace>>;
};

const DECIMALS = 18;
const DELEGATION_STRATEGIES = [
'delegation',
Expand All @@ -14,9 +23,65 @@ const DELEGATION_STRATEGIES = [
'delegation-with-overrides'
];

const lastCompute = new Map<string, number>();
const networkDelegationsCache = new Map<string, NetworkCache>();
const lastSpaceCompute = new Map<string, number>();
const mutex = new Mutex();

async function getNetworkDelegations(network: string) {
const cache = networkDelegationsCache.get(network);
const now = Math.floor(Date.now() / 1000);

if (cache && now - cache.timestamp < NETWORK_COMPUTE_DELAY_SECONDS) {
return cache.data;
}

const delegations = await snapshotjs.utils.getDelegatesBySpace(
network,
null,

Check failure on line 40 in src/compute.ts

View workflow job for this annotation

GitHub Actions / lint-build

Argument of type 'null' is not assignable to parameter of type 'string'.
'latest'
);

networkDelegationsCache.set(network, {
timestamp: now,
data: delegations
});

return delegations;
}

async function getScores(
network: string,
governance: string,
strategies: Space['strategies'],
delegatesAddresses: string[]
): Promise<Record<string, number>> {
const chunks = delegatesAddresses.reduce((acc, address, i) => {
const chunkIndex = Math.floor(i / 1000);
if (!acc[chunkIndex]) acc[chunkIndex] = [];
acc[chunkIndex].push(address);
return acc;
}, [] as string[][]);

let scores: Record<string, number> = {};
for (const chunk of chunks) {
const result = await snapshotjs.utils.getScores(
governance,
strategies,
network,
chunk,
'latest',
`${SCORE_API_URL}/api/scores`
);

scores = {
...scores,
...result[0]
};
}

return scores;
}

// This function is called everytime governance information is queried from GraphQL API.
// It receives array of governances that we want to update information about.
//
Expand All @@ -33,20 +98,16 @@ export async function compute(governances: string[]) {
for (const governance of governances) {
console.log('computing', governance);

const computedAt = lastCompute.get(governance) ?? 0;
const computedAt = lastSpaceCompute.get(governance) ?? 0;
const now = Math.floor(Date.now() / 1000);
if (now - computedAt < COMPUTE_DELAY_SECONDS) {
if (now - computedAt < SPACE_COMPUTE_DELAY_SECONDS) {
console.log('ignoring because of recent compute');
continue;
}
lastCompute.set(governance, now);
lastSpaceCompute.set(governance, now);

const space = await getSpace(governance);
const delegations = await snapshotjs.utils.getDelegatesBySpace(
space.network,
governance,
'latest'
);
const delegations = await getNetworkDelegations(space.network);

const delegatorCounter = {};
for (const delegation of delegations) {
Expand All @@ -67,19 +128,17 @@ export async function compute(governances: string[]) {
DELEGATION_STRATEGIES.includes(strategy.name)
);

const scores = await snapshotjs.utils.getScores(
const scores = await getScores(
space.network,
governance,
strategies,
space.network,
delegatesAddresses,
'latest',
`${SCORE_API_URL}/api/scores`
delegatesAddresses
);

const delegates = uniqueDelegates.map(delegate => ({
...delegate,
score: BigInt(
Math.floor((scores[0][delegate.delegate] ?? 0) * 10 ** DECIMALS)
Math.floor((scores[delegate.delegate] ?? 0) * 10 ** DECIMALS)
)
}));

Expand All @@ -93,8 +152,8 @@ export async function compute(governances: string[]) {
);

const governanceEntity = new Governance(governance);
governanceEntity.currentDelegates = uniqueDelegates.length;
governanceEntity.totalDelegates = uniqueDelegates.length;
governanceEntity.currentDelegates = sortedDelegates.length;
governanceEntity.totalDelegates = sortedDelegates.length;
governanceEntity.delegatedVotesRaw = totalVotes.toString();
governanceEntity.delegatedVotes = formatUnits(totalVotes, DECIMALS);
await governanceEntity.save();
Expand Down
3 changes: 2 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export const HUB_URL = process.env.HUB_URL ?? 'https://hub.snapshot.org';
export const SCORE_API_URL =
process.env.SCORE_API_URL ?? 'https://score.snapshot.org';

export const COMPUTE_DELAY_SECONDS = 30 * 60; // 30 minutes
export const NETWORK_COMPUTE_DELAY_SECONDS = 24 * 60 * 60; // 24 hours
export const SPACE_COMPUTE_DELAY_SECONDS = 24 * 60 * 60; // 24 hours
2 changes: 1 addition & 1 deletion src/hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const SPACE_QUERY = gql`
}
`;

type Space = {
export type Space = {
id: string;
network: string;
strategies: {
Expand Down

0 comments on commit 490bf60

Please sign in to comment.