Skip to content

feat: set vote fiat value #566

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ STARKNET_RPC_URL= # optional
AUTH_SECRET=1dfd84a695705665668c260222ded178d1f1d62d251d7bee8148428dac6d0487 # optional
WALLETCONNECT_PROJECT_ID=e6454bd61aba40b786e866a69bd4c5c6
REOWN_SECRET= # optional
LAST_CB=1
1 change: 1 addition & 0 deletions src/helpers/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export async function getProposal(space, id) {
params: {}
};
proposal.choices = jsonParse(proposal.choices);
proposal.vp_value_by_strategy = jsonParse(proposal.vp_value_by_strategy, []);

return proposal;
}
Expand Down
22 changes: 22 additions & 0 deletions src/helpers/entityValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
type Vote = {
vp_by_strategy: number[];
};

type Proposal = {
vp_value_by_strategy: number[];
};

/**
* Calculates the total vote value based on the voting power and the proposal's value per strategy.
* @returns The total vote value, in the currency unit specified by the proposal's vp_value_by_strategy values
**/
export function getVoteValue(proposal: Proposal, vote: Vote): number {
if (proposal.vp_value_by_strategy.length !== vote.vp_by_strategy.length) {
throw new Error('invalid data to compute vote value');
}

return proposal.vp_value_by_strategy.reduce(
(sum, value, index) => sum + value * vote.vp_by_strategy[index],
0
);
}
18 changes: 17 additions & 1 deletion src/writer/vote.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { capture } from '@snapshot-labs/snapshot-sentry';
import snapshot from '@snapshot-labs/snapshot.js';
import { getProposal } from '../helpers/actions';
import { getVoteValue } from '../helpers/entityValue';
import log from '../helpers/log';
import db from '../helpers/mysql';
import { captureError, hasStrategyOverride, jsonParse } from '../helpers/utils';
import { updateProposalAndVotes } from '../scores';

const LAST_CB = parseInt(process.env.LAST_CB ?? '1');
const scoreAPIUrl = process.env.SCORE_API_URL || 'https://score.snapshot.org';

// async function isLimitReached(space) {
Expand Down Expand Up @@ -116,6 +119,18 @@ export async function action(body, ipfs, receipt, id, context): Promise<void> {
const withOverride = hasStrategyOverride(context.proposal.strategies);
if (vpState === 'final' && withOverride) vpState = 'pending';

// Get proposal voting power value
// Value is set on creation, and not updated on vote update
let vpValue = 0;
let cb = 0;

try {
vpValue = getVoteValue(context.proposal, context.vp);
cb = LAST_CB;
} catch (e: any) {
capture(e, { msg, proposalId, context });
}

const params = {
id,
ipfs,
Expand All @@ -130,7 +145,8 @@ export async function action(body, ipfs, receipt, id, context): Promise<void> {
vp: context.vp.vp,
vp_by_strategy: JSON.stringify(context.vp.vp_by_strategy),
vp_state: vpState,
cb: 0
vp_value: vpValue,
cb
};

// Check if voter already voted
Expand Down
2 changes: 2 additions & 0 deletions test/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ CREATE TABLE votes (
vp DECIMAL(64,30) NOT NULL,
vp_by_strategy JSON NOT NULL,
vp_state VARCHAR(24) NOT NULL,
vp_value DECIMAL(13,3) NOT NULL DEFAULT 0.000,
cb INT(11) NOT NULL,
PRIMARY KEY (voter, space, proposal),
UNIQUE KEY id (id),
Expand All @@ -111,6 +112,7 @@ CREATE TABLE votes (
INDEX app (app),
INDEX vp (vp),
INDEX vp_state (vp_state),
INDEX vp_value (vp_value),
INDEX cb (cb)
);

Expand Down
71 changes: 71 additions & 0 deletions test/unit/helpers/entityValue.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { getVoteValue } from '../../../src/helpers/entityValue';

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

const result = getVoteValue(proposal, vote);

expect(result).toBe(250);
});

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

const result = getVoteValue(proposal, vote);

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

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

const result = getVoteValue(proposal, vote);

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

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

const result = getVoteValue(proposal, vote);

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

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

const result = getVoteValue(proposal, vote);

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

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

expect(() => getVoteValue(proposal, vote)).toThrow('invalid data to compute vote value');
});

it('should throw error when vote has more strategies than proposal', () => {
const proposal = { vp_value_by_strategy: [1.0] };
const vote = { vp_by_strategy: [100, 50] };

expect(() => getVoteValue(proposal, vote)).toThrow('invalid data to compute vote value');
});

it('should handle empty arrays', () => {
const proposal = { vp_value_by_strategy: [] };
const vote = { vp_by_strategy: [] };

const result = getVoteValue(proposal, vote);

expect(result).toBe(0);
});
});
Loading