Skip to content

Commit 5725853

Browse files
committed
feat: add script to refresh votes' vp_value async
1 parent c70da7a commit 5725853

File tree

4 files changed

+118
-35
lines changed

4 files changed

+118
-35
lines changed

src/helpers/entityValue.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import { jsonRpcRequest } from './utils';
22

3-
type Vote = {
4-
vp_by_strategy: number[];
5-
};
6-
73
type Proposal = {
84
network: string;
95
strategies: any[];
@@ -33,15 +29,12 @@ export async function getVpValueByStrategy(proposal: Proposal): Promise<number[]
3329
* Calculates the total vote value based on the voting power and the proposal's value per strategy.
3430
* @returns The total vote value, in the currency unit specified by the proposal's vp_value_by_strategy values
3531
**/
36-
export function getVoteValue(proposal: { vp_value_by_strategy: number[] }, vote: Vote): number {
37-
if (!proposal.vp_value_by_strategy.length) return 0;
32+
export function getVoteValue(vp_value_by_strategy: number[], vp_by_strategy: number[]): number {
33+
if (!vp_value_by_strategy.length) return 0;
3834

39-
if (proposal.vp_value_by_strategy.length !== vote.vp_by_strategy.length) {
35+
if (vp_value_by_strategy.length !== vp_by_strategy.length) {
4036
throw new Error('invalid data to compute vote value');
4137
}
4238

43-
return proposal.vp_value_by_strategy.reduce(
44-
(sum, value, index) => sum + value * vote.vp_by_strategy[index],
45-
0
46-
);
39+
return vp_value_by_strategy.reduce((sum, value, index) => sum + value * vp_by_strategy[index], 0);
4740
}

src/helpers/votesVpValue.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// import { capture } from '@snapshot-labs/snapshot-sentry';
2+
import snapshot from '@snapshot-labs/snapshot.js';
3+
import { getVoteValue } from './entityValue';
4+
import db from './mysql';
5+
import { CB } from '../constants';
6+
7+
const REFRESH_INTERVAL = 60 * 1000;
8+
const BATCH_SIZE = 100;
9+
10+
type Datum = {
11+
id: string;
12+
vp_by_strategy: number[];
13+
vp_value_by_strategy: number[];
14+
};
15+
16+
async function getVotes(): Promise<Datum[]> {
17+
const query = `
18+
SELECT votes.id, votes.vp_by_strategy, proposals.vp_value_by_strategy
19+
FROM votes
20+
JOIN proposals ON votes.proposal = proposals.id
21+
WHERE proposals.cb = ? AND votes.cb = ?
22+
ORDER BY votes.created DESC
23+
LIMIT ?`;
24+
const results = await db.queryAsync(query, [CB.PENDING_CLOSE, CB.PENDING_SYNC, BATCH_SIZE]);
25+
26+
return results.map((p: any) => {
27+
p.vp_value_by_strategy = JSON.parse(p.vp_value_by_strategy);
28+
p.vp_by_strategy = JSON.parse(p.vp_by_strategy);
29+
return p;
30+
});
31+
}
32+
33+
async function refreshVotesVpValues(data: Datum[]) {
34+
const query: string[] = [];
35+
const params: any[] = [];
36+
37+
for (const datum of data) {
38+
buildQuery(datum, query, params);
39+
}
40+
41+
if (query.length) {
42+
await db.queryAsync(query.join(';'), params);
43+
}
44+
}
45+
46+
function buildQuery(datum: Datum, query: string[], params: any[]) {
47+
try {
48+
const value = getVoteValue(datum.vp_value_by_strategy, datum.vp_by_strategy);
49+
50+
query.push('UPDATE votes SET vp_value = ?, cb = ? WHERE id = ? LIMIT 1');
51+
params.push(value, CB.PENDING_CLOSE, datum.id);
52+
} catch (e) {
53+
// TODO: enable only after whole database is synced
54+
// capture(e, { extra: { proposal } });
55+
}
56+
}
57+
58+
async function refreshPendingVotes() {
59+
while (true) {
60+
const votes = await getVotes();
61+
62+
if (votes.length === 0) break;
63+
64+
await refreshVotesVpValues(votes);
65+
66+
if (votes.length < BATCH_SIZE) break;
67+
}
68+
}
69+
70+
export default async function run() {
71+
await refreshPendingVotes();
72+
await snapshot.utils.sleep(REFRESH_INTERVAL);
73+
74+
run();
75+
}

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ import {
1515
stop as stopStrategies
1616
} from './helpers/strategies';
1717
import { trackTurboStatuses } from './helpers/turbo';
18+
import refreshVotesVpValue from './helpers/votesVpValue';
1819

1920
const app = express();
2021

2122
async function startServer() {
2223
initLogger(app);
2324
refreshModeration();
2425
refreshProposalsVpValue();
26+
refreshVotesVpValue();
2527

2628
await initializeStrategies();
2729
refreshStrategies();

test/unit/helpers/entityValue.test.ts

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,69 +2,82 @@ import { getVoteValue } from '../../../src/helpers/entityValue';
22

33
describe('getVoteValue', () => {
44
it('should calculate correct vote value with single strategy', () => {
5-
const proposal = { vp_value_by_strategy: [2.5] };
6-
const vote = { vp_by_strategy: [100] };
5+
const vp_value_by_strategy = [2.5];
6+
const vp_by_strategy = [100];
77

8-
const result = getVoteValue(proposal, vote);
8+
const result = getVoteValue(vp_value_by_strategy, vp_by_strategy);
99

1010
expect(result).toBe(250);
1111
});
1212

1313
it('should calculate correct vote value with multiple strategies', () => {
14-
const proposal = { vp_value_by_strategy: [1.5, 3.0, 0.5] };
15-
const vote = { vp_by_strategy: [100, 50, 200] };
14+
const vp_value_by_strategy = [1.5, 3.0, 0.5];
15+
const vp_by_strategy = [100, 50, 200];
1616

17-
const result = getVoteValue(proposal, vote);
17+
const result = getVoteValue(vp_value_by_strategy, vp_by_strategy);
1818

1919
expect(result).toBe(400); // (1.5 * 100) + (3.0 * 50) + (0.5 * 200) = 150 + 150 + 100 = 400
2020
});
2121

2222
it('should return 0 when vote has no voting power', () => {
23-
const proposal = { vp_value_by_strategy: [2.0, 1.5] };
24-
const vote = { vp_by_strategy: [0, 0] };
23+
const vp_value_by_strategy = [2.0, 1.5];
24+
const vp_by_strategy = [0, 0];
2525

26-
const result = getVoteValue(proposal, vote);
26+
const result = getVoteValue(vp_value_by_strategy, vp_by_strategy);
2727

2828
expect(result).toBe(0);
2929
});
3030

3131
it('should return 0 when proposal has no value per strategy', () => {
32-
const proposal = { vp_value_by_strategy: [0, 0] };
33-
const vote = { vp_by_strategy: [100, 50] };
32+
const vp_value_by_strategy = [0, 0];
33+
const vp_by_strategy = [100, 50];
3434

35-
const result = getVoteValue(proposal, vote);
35+
const result = getVoteValue(vp_value_by_strategy, vp_by_strategy);
3636

3737
expect(result).toBe(0);
3838
});
3939

4040
it('should handle decimal values correctly', () => {
41-
const proposal = { vp_value_by_strategy: [0.1, 0.25] };
42-
const vote = { vp_by_strategy: [10, 20] };
41+
const vp_value_by_strategy = [0.1, 0.25];
42+
const vp_by_strategy = [10, 20];
4343

44-
const result = getVoteValue(proposal, vote);
44+
const result = getVoteValue(vp_value_by_strategy, vp_by_strategy);
4545

4646
expect(result).toBe(6); // (0.1 * 10) + (0.25 * 20) = 1 + 5 = 6
4747
});
4848

4949
it('should throw error when strategy arrays have different lengths', () => {
50-
const proposal = { vp_value_by_strategy: [1.0, 2.0] };
51-
const vote = { vp_by_strategy: [100] };
50+
const vp_value_by_strategy = [1.0, 2.0];
51+
const vp_by_strategy = [100];
5252

53-
expect(() => getVoteValue(proposal, vote)).toThrow('invalid data to compute vote value');
53+
expect(() => getVoteValue(vp_value_by_strategy, vp_by_strategy)).toThrow(
54+
'invalid data to compute vote value'
55+
);
5456
});
5557

5658
it('should throw error when vote has more strategies than proposal', () => {
57-
const proposal = { vp_value_by_strategy: [1.0] };
58-
const vote = { vp_by_strategy: [100, 50] };
59+
const vp_value_by_strategy = [1.0];
60+
const vp_by_strategy = [100, 50];
5961

60-
expect(() => getVoteValue(proposal, vote)).toThrow('invalid data to compute vote value');
62+
expect(() => getVoteValue(vp_value_by_strategy, vp_by_strategy)).toThrow(
63+
'invalid data to compute vote value'
64+
);
6165
});
6266

6367
it('should handle empty arrays', () => {
64-
const proposal = { vp_value_by_strategy: [] };
65-
const vote = { vp_by_strategy: [] };
68+
const vp_value_by_strategy = [];
69+
const vp_by_strategy = [];
6670

67-
const result = getVoteValue(proposal, vote);
71+
const result = getVoteValue(vp_value_by_strategy, vp_by_strategy);
72+
73+
expect(result).toBe(0);
74+
});
75+
76+
it('should return 0 when vp_value_by_strategy is empty', () => {
77+
const vp_value_by_strategy = [];
78+
const vp_by_strategy = [100, 50];
79+
80+
const result = getVoteValue(vp_value_by_strategy, vp_by_strategy);
6881

6982
expect(result).toBe(0);
7083
});

0 commit comments

Comments
 (0)