Skip to content

Commit

Permalink
feat: add support for ranked choice voting for offchain spaces (#174)
Browse files Browse the repository at this point in the history
* feat: add support for ranked choice voting for offchain spaces

* chore: update changelog

* fix(ui): UI update
  • Loading branch information
wa0x6e authored Mar 15, 2024
1 parent 21dc5c7 commit 5be2175
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/wise-starfishes-tan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@snapshot-labs/sx": patch
---

add ranked choice vote support for OffchainEthereumSig
5 changes: 5 additions & 0 deletions apps/ui/src/components/EditorVotingType.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ const VOTING_TYPES_INFO = computed(() => ({
approval: {
label: 'Approval voting',
description: 'Voters can select multiple choices, each choice receiving full voting power.'
},
'ranked-choice': {
label: 'Ranked choice voting',
description:
'Each voter may select and rank any number of choices. Results are calculated by instant-runoff counting method.'
}
}));
Expand Down
50 changes: 50 additions & 0 deletions apps/ui/src/components/ProposalVoteRankedChoice.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<script setup lang="ts">
import Draggable from 'vuedraggable';
import { Choice, Proposal } from '@/types';
const props = defineProps<{
sendingType: Choice | null;
proposal: Proposal;
}>();
const emit = defineEmits<{
(e: 'vote', value: Choice);
}>();
const selectedChoices = ref<number[]>(props.proposal.choices.map((_, i) => i + 1));
</script>

<template>
<div class="flex flex-col gap-3">
<Draggable
v-model="selectedChoices"
v-bind="{ animation: 200 }"
handle=".handle"
class="flex flex-col gap-2"
item-key="id"
>
<template #item="{ element, index }">
<UiButton class="!h-[48px] text-left w-full flex items-center handle cursor-grab gap-2">
<IC-drag class="text-skin-text" />

<div class="grow truncate">
{{ proposal.choices[element - 1] }}
</div>
<div
class="h-[18px] min-w-[18px] rounded-full leading-[18px] text-[13px] text-white bg-skin-border px-2 text-center inline-block"
>
#{{ index + 1 }}
</div>
</UiButton>
</template>
</Draggable>
<UiButton
primary
class="!h-[48px] w-full"
:loading="!!sendingType"
@click="emit('vote', selectedChoices)"
>
Vote
</UiButton>
</div>
</template>
7 changes: 6 additions & 1 deletion apps/ui/src/helpers/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,9 @@ export const COINGECKO_BASE_ASSETS = {

export const MAX_SYMBOL_LENGTH = 12;
export const BASIC_CHOICES = ['For', 'Against', 'Abstain'];
export const SUPPORTED_VOTING_TYPES: VoteType[] = ['basic', 'single-choice', 'approval'] as const;
export const SUPPORTED_VOTING_TYPES: VoteType[] = [
'basic',
'single-choice',
'approval',
'ranked-choice'
] as const;
2 changes: 1 addition & 1 deletion apps/ui/src/networks/offchain/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ export const EDITOR_PROPOSAL_VALIDATION_VOTING_STRATEGIES = [];
export const EDITOR_EXECUTION_STRATEGIES = [];
export const EDITOR_SNAPSHOT_OFFSET = 4;
export const EDITOR_APP_NAME = 'snapshot-v2';
export const EDITOR_VOTING_TYPES = ['basic', 'single-choice', 'approval'];
export const EDITOR_VOTING_TYPES = ['basic', 'single-choice', 'approval', 'ranked-choice'];

export const DEFAULT_VOTING_DELAY = 60 * 60 * 24 * 7;
2 changes: 1 addition & 1 deletion apps/ui/src/networks/offchain/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export function getSdkChoice(type: string, choice: Choice): number | number[] {
return choice as number;
}

if (type === 'approval') {
if (type === 'approval' || type === 'ranked-choice') {
return choice as number[];
}

Expand Down
6 changes: 6 additions & 0 deletions apps/ui/src/views/Proposal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,12 @@ watchEffect(() => {
:sending-type="sendingType"
@vote="handleVoteClick"
/>
<ProposalVoteRankedChoice
v-else-if="proposal.type === 'ranked-choice'"
:proposal="proposal"
:sending-type="sendingType"
@vote="handleVoteClick"
/>
</ProposalVote>
</template>

Expand Down
2 changes: 2 additions & 0 deletions packages/sx.js/src/clients/offchain/ethereum-sig/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
basicVoteTypes,
singleChoiceVoteTypes,
approvalVoteTypes,
rankedChoiceVoteTypes,
updateProposalTypes,
cancelProposalTypes
} from './types';
Expand Down Expand Up @@ -169,6 +170,7 @@ export class EthereumSig {
let voteType = basicVoteTypes;
if (data.type === 'single-choice') voteType = singleChoiceVoteTypes;
if (data.type === 'approval') voteType = approvalVoteTypes;
if (data.type === 'ranked-choice') voteType = rankedChoiceVoteTypes;

const signatureData = await this.sign(signer, message, voteType);

Expand Down
2 changes: 2 additions & 0 deletions packages/sx.js/src/clients/offchain/ethereum-sig/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export const approvalVoteTypes = {
]
};

export const rankedChoiceVoteTypes = approvalVoteTypes;

export const proposeTypes = {
Proposal: [
{ name: 'from', type: 'address' },
Expand Down

0 comments on commit 5be2175

Please sign in to comment.