Skip to content

Commit cac0b30

Browse files
committed
refactor: centralize fiat handling in a dedicated helper
1 parent 0e77cd4 commit cac0b30

File tree

3 files changed

+131
-10
lines changed

3 files changed

+131
-10
lines changed

src/helpers/entityValue.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
type Proposal = {
2+
scores_by_strategy: string;
3+
vp_value_by_strategy: string;
4+
};
5+
6+
/**
7+
* Calculates the total proposal value based on the vote's total voting power and the proposal's value per strategy.
8+
* @returns The total value of the given proposal's votes, in the currency unit specified by the proposal's vp_value_by_strategy values
9+
*/
10+
export function getProposalValue(proposal: Proposal): number {
11+
const scoresByStrategy: number[][] = JSON.parse(proposal.scores_by_strategy);
12+
const vpValueByStrategy: number[] = JSON.parse(proposal.vp_value_by_strategy);
13+
14+
return (
15+
scoresByStrategy[0]
16+
?.map((_, index) => scoresByStrategy.reduce((sum, array) => sum + array[index], 0))
17+
?.map((value, index) => value * vpValueByStrategy[index])
18+
?.reduce((sum, value) => sum + value, 0) || 0
19+
);
20+
}

src/scores.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import snapshot from '@snapshot-labs/snapshot.js';
2+
import { getProposalValue } from './helpers/entityValue';
23
import log from './helpers/log';
34
import db from './helpers/mysql';
45
import { getDecryptionKey } from './helpers/shutter';
@@ -102,17 +103,8 @@ async function updateProposalScoresValue(proposalId: string) {
102103
'SELECT vp_value_by_strategy, scores_by_strategy FROM proposals WHERE id = ? LIMIT 1;',
103104
[proposalId]
104105
);
105-
const scoresByStrategy: number[][] = JSON.parse(proposal.scores_by_strategy);
106-
const vpValueByStrategy: number[] = JSON.parse(proposal.vp_value_by_strategy);
107-
108-
const score_total_value =
109-
scoresByStrategy[0]
110-
?.map((_, index) => scoresByStrategy.reduce((sum, array) => sum + array[index], 0))
111-
?.map((value, index) => value * vpValueByStrategy[index])
112-
?.reduce((sum, value) => sum + value, 0) || 0;
113-
114106
const query = 'UPDATE proposals SET scores_total_value = ? WHERE id = ? LIMIT 1;';
115-
await db.queryAsync(query, [score_total_value, proposalId]);
107+
await db.queryAsync(query, [getProposalValue(proposal), proposalId]);
116108
}
117109

118110
const pendingRequests = {};

test/unit/helpers/entityValue.test.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { getProposalValue } from '../../../src/helpers/entityValue';
2+
3+
describe('getProposalValue', () => {
4+
it('should calculate correct proposal value with single strategy', () => {
5+
const proposal = {
6+
scores_by_strategy: '[[100], [200]]',
7+
vp_value_by_strategy: '[2.5]'
8+
};
9+
10+
const result = getProposalValue(proposal);
11+
12+
expect(result).toBe(750); // (100 + 200) * 2.5 = 300 * 2.5 = 750
13+
});
14+
15+
it('should calculate correct proposal value with multiple strategies', () => {
16+
const proposal = {
17+
scores_by_strategy: '[[100, 50], [200, 75], [300, 25]]',
18+
vp_value_by_strategy: '[1.5, 3.0]'
19+
};
20+
21+
const result = getProposalValue(proposal);
22+
23+
expect(result).toBe(1350); // (100+200+300)*1.5 + (50+75+25)*3.0 = 600*1.5 + 150*3.0 = 900 + 450 = 1350
24+
});
25+
26+
it('should return 0 when scores_by_strategy is empty', () => {
27+
const proposal = {
28+
scores_by_strategy: '[]',
29+
vp_value_by_strategy: '[2.0]'
30+
};
31+
32+
const result = getProposalValue(proposal);
33+
34+
expect(result).toBe(0);
35+
});
36+
37+
it('should return 0 when first strategy array is empty', () => {
38+
const proposal = {
39+
scores_by_strategy: '[[]]',
40+
vp_value_by_strategy: '[2.0]'
41+
};
42+
43+
const result = getProposalValue(proposal);
44+
45+
expect(result).toBe(0);
46+
});
47+
48+
it('should handle zero values correctly', () => {
49+
const proposal = {
50+
scores_by_strategy: '[[0, 0], [0, 0]]',
51+
vp_value_by_strategy: '[2.0, 1.5]'
52+
};
53+
54+
const result = getProposalValue(proposal);
55+
56+
expect(result).toBe(0);
57+
});
58+
59+
it('should handle zero vp_value_by_strategy correctly', () => {
60+
const proposal = {
61+
scores_by_strategy: '[[100, 50], [200, 75]]',
62+
vp_value_by_strategy: '[0, 0]'
63+
};
64+
65+
const result = getProposalValue(proposal);
66+
67+
expect(result).toBe(0);
68+
});
69+
70+
it('should handle decimal values correctly', () => {
71+
const proposal = {
72+
scores_by_strategy: '[[10.5, 20.5], [15.5, 25.5]]',
73+
vp_value_by_strategy: '[0.1, 0.2]'
74+
};
75+
76+
const result = getProposalValue(proposal);
77+
78+
expect(result).toBe(11.8); // (10.5+15.5)*0.1 + (20.5+25.5)*0.2 = 26*0.1 + 46*0.2 = 2.6 + 9.2 = 11.8
79+
});
80+
81+
it('should handle single vote scenario', () => {
82+
const proposal = {
83+
scores_by_strategy: '[[100]]',
84+
vp_value_by_strategy: '[2.0]'
85+
};
86+
87+
const result = getProposalValue(proposal);
88+
89+
expect(result).toBe(200); // 100 * 2.0 = 200
90+
});
91+
92+
it('should throw error for invalid JSON in scores_by_strategy', () => {
93+
const proposal = {
94+
scores_by_strategy: 'invalid json',
95+
vp_value_by_strategy: '[2.0]'
96+
};
97+
98+
expect(() => getProposalValue(proposal)).toThrow();
99+
});
100+
101+
it('should throw error for invalid JSON in vp_value_by_strategy', () => {
102+
const proposal = {
103+
scores_by_strategy: '[[100]]',
104+
vp_value_by_strategy: 'invalid json'
105+
};
106+
107+
expect(() => getProposalValue(proposal)).toThrow();
108+
});
109+
});

0 commit comments

Comments
 (0)