@snapshot-labs/snapshot.js 0.12.49 → 0.12.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -351,6 +351,8 @@ class Client {
351
351
  }
352
352
  proposal(web3, address, message) {
353
353
  return __awaiter(this, void 0, void 0, function* () {
354
+ if (!message.labels)
355
+ message.labels = [];
354
356
  if (!message.discussion)
355
357
  message.discussion = '';
356
358
  if (!message.app)
@@ -1088,6 +1090,7 @@ var definitions$1 = {
1088
1090
  "approval",
1089
1091
  "ranked-choice",
1090
1092
  "quadratic",
1093
+ "copeland",
1091
1094
  "weighted",
1092
1095
  "custom",
1093
1096
  "basic"
@@ -3942,6 +3945,121 @@ class RankedChoiceVoting {
3942
3945
  }
3943
3946
  }
3944
3947
 
3948
+ // CopelandVoting implements ranked choice voting using Copeland's method
3949
+ // This method compares each pair of choices and awards points based on pairwise victories
3950
+ class CopelandVoting {
3951
+ constructor(proposal, votes, strategies, selected) {
3952
+ this.proposal = proposal;
3953
+ this.votes = votes;
3954
+ this.strategies = strategies;
3955
+ this.selected = selected;
3956
+ }
3957
+ // Validates if a vote choice is valid for the given proposal
3958
+ // Allows partial ranking (not all choices need to be ranked)
3959
+ static isValidChoice(voteChoice, proposalChoices) {
3960
+ if (!Array.isArray(voteChoice) ||
3961
+ voteChoice.length === 0 ||
3962
+ voteChoice.length > proposalChoices.length ||
3963
+ new Set(voteChoice).size !== voteChoice.length) {
3964
+ return false;
3965
+ }
3966
+ return voteChoice.every((choice) => Number.isInteger(choice) &&
3967
+ choice >= 1 &&
3968
+ choice <= proposalChoices.length);
3969
+ }
3970
+ // Returns only the valid votes
3971
+ getValidVotes() {
3972
+ return this.votes.filter((vote) => CopelandVoting.isValidChoice(vote.choice, this.proposal.choices));
3973
+ }
3974
+ // Calculates the Copeland scores for each choice
3975
+ getScores() {
3976
+ const validVotes = this.getValidVotes();
3977
+ const choicesCount = this.proposal.choices.length;
3978
+ const pairwiseComparisons = Array.from({ length: choicesCount }, () => Array(choicesCount).fill(0));
3979
+ // Calculate pairwise comparisons
3980
+ for (const vote of validVotes) {
3981
+ for (let i = 0; i < vote.choice.length; i++) {
3982
+ for (let j = i + 1; j < vote.choice.length; j++) {
3983
+ const winner = vote.choice[i] - 1;
3984
+ const loser = vote.choice[j] - 1;
3985
+ pairwiseComparisons[winner][loser] += vote.balance;
3986
+ pairwiseComparisons[loser][winner] -= vote.balance;
3987
+ }
3988
+ }
3989
+ }
3990
+ // Calculate Copeland scores
3991
+ const scores = Array(choicesCount).fill(0);
3992
+ for (let i = 0; i < choicesCount; i++) {
3993
+ for (let j = 0; j < choicesCount; j++) {
3994
+ if (i !== j) {
3995
+ if (pairwiseComparisons[i][j] > 0) {
3996
+ scores[i]++;
3997
+ }
3998
+ else if (pairwiseComparisons[i][j] < 0) {
3999
+ scores[j]++;
4000
+ }
4001
+ else {
4002
+ scores[i] += 0.5;
4003
+ scores[j] += 0.5;
4004
+ }
4005
+ }
4006
+ }
4007
+ }
4008
+ return scores;
4009
+ }
4010
+ // Calculates the Copeland scores for each choice, broken down by strategy
4011
+ getScoresByStrategy() {
4012
+ const validVotes = this.getValidVotes();
4013
+ const choicesCount = this.proposal.choices.length;
4014
+ const strategiesCount = this.strategies.length;
4015
+ const pairwiseComparisons = Array.from({ length: choicesCount }, () => Array.from({ length: choicesCount }, () => Array(strategiesCount).fill(0)));
4016
+ // Calculate pairwise comparisons for each strategy
4017
+ for (const vote of validVotes) {
4018
+ for (let i = 0; i < vote.choice.length; i++) {
4019
+ for (let j = i + 1; j < vote.choice.length; j++) {
4020
+ const winner = vote.choice[i] - 1;
4021
+ const loser = vote.choice[j] - 1;
4022
+ for (let s = 0; s < strategiesCount; s++) {
4023
+ pairwiseComparisons[winner][loser][s] += vote.scores[s];
4024
+ pairwiseComparisons[loser][winner][s] -= vote.scores[s];
4025
+ }
4026
+ }
4027
+ }
4028
+ }
4029
+ // Calculate Copeland scores for each strategy
4030
+ const scores = Array.from({ length: choicesCount }, () => Array(strategiesCount).fill(0));
4031
+ for (let i = 0; i < choicesCount; i++) {
4032
+ for (let j = 0; j < choicesCount; j++) {
4033
+ if (i !== j) {
4034
+ for (let s = 0; s < strategiesCount; s++) {
4035
+ if (pairwiseComparisons[i][j][s] > 0) {
4036
+ scores[i][s]++;
4037
+ }
4038
+ else if (pairwiseComparisons[i][j][s] < 0) {
4039
+ scores[j][s]++;
4040
+ }
4041
+ else {
4042
+ scores[i][s] += 0.5;
4043
+ scores[j][s] += 0.5;
4044
+ }
4045
+ }
4046
+ }
4047
+ }
4048
+ }
4049
+ return scores;
4050
+ }
4051
+ // Calculates the total score (sum of all valid vote balances)
4052
+ getScoresTotal() {
4053
+ return this.getValidVotes().reduce((total, vote) => total + vote.balance, 0);
4054
+ }
4055
+ // Returns a string representation of the selected choices
4056
+ getChoiceString() {
4057
+ return this.selected
4058
+ .map((choice) => this.proposal.choices[choice - 1])
4059
+ .join(', ');
4060
+ }
4061
+ }
4062
+
3945
4063
  function percentageOfTotal(i, values, total) {
3946
4064
  const reducedTotal = total.reduce((a, b) => a + b, 0);
3947
4065
  const percent = (values[i] / reducedTotal) * 100;
@@ -4014,6 +4132,7 @@ var voting = {
4014
4132
  approval: ApprovalVoting,
4015
4133
  quadratic: QuadraticVoting,
4016
4134
  'ranked-choice': RankedChoiceVoting,
4135
+ copeland: CopelandVoting,
4017
4136
  weighted: WeightedVoting,
4018
4137
  basic: SingleChoiceVoting
4019
4138
  };
@@ -341,6 +341,8 @@ class Client {
341
341
  }
342
342
  proposal(web3, address, message) {
343
343
  return __awaiter(this, void 0, void 0, function* () {
344
+ if (!message.labels)
345
+ message.labels = [];
344
346
  if (!message.discussion)
345
347
  message.discussion = '';
346
348
  if (!message.app)
@@ -1078,6 +1080,7 @@ var definitions$1 = {
1078
1080
  "approval",
1079
1081
  "ranked-choice",
1080
1082
  "quadratic",
1083
+ "copeland",
1081
1084
  "weighted",
1082
1085
  "custom",
1083
1086
  "basic"
@@ -3932,6 +3935,121 @@ class RankedChoiceVoting {
3932
3935
  }
3933
3936
  }
3934
3937
 
3938
+ // CopelandVoting implements ranked choice voting using Copeland's method
3939
+ // This method compares each pair of choices and awards points based on pairwise victories
3940
+ class CopelandVoting {
3941
+ constructor(proposal, votes, strategies, selected) {
3942
+ this.proposal = proposal;
3943
+ this.votes = votes;
3944
+ this.strategies = strategies;
3945
+ this.selected = selected;
3946
+ }
3947
+ // Validates if a vote choice is valid for the given proposal
3948
+ // Allows partial ranking (not all choices need to be ranked)
3949
+ static isValidChoice(voteChoice, proposalChoices) {
3950
+ if (!Array.isArray(voteChoice) ||
3951
+ voteChoice.length === 0 ||
3952
+ voteChoice.length > proposalChoices.length ||
3953
+ new Set(voteChoice).size !== voteChoice.length) {
3954
+ return false;
3955
+ }
3956
+ return voteChoice.every((choice) => Number.isInteger(choice) &&
3957
+ choice >= 1 &&
3958
+ choice <= proposalChoices.length);
3959
+ }
3960
+ // Returns only the valid votes
3961
+ getValidVotes() {
3962
+ return this.votes.filter((vote) => CopelandVoting.isValidChoice(vote.choice, this.proposal.choices));
3963
+ }
3964
+ // Calculates the Copeland scores for each choice
3965
+ getScores() {
3966
+ const validVotes = this.getValidVotes();
3967
+ const choicesCount = this.proposal.choices.length;
3968
+ const pairwiseComparisons = Array.from({ length: choicesCount }, () => Array(choicesCount).fill(0));
3969
+ // Calculate pairwise comparisons
3970
+ for (const vote of validVotes) {
3971
+ for (let i = 0; i < vote.choice.length; i++) {
3972
+ for (let j = i + 1; j < vote.choice.length; j++) {
3973
+ const winner = vote.choice[i] - 1;
3974
+ const loser = vote.choice[j] - 1;
3975
+ pairwiseComparisons[winner][loser] += vote.balance;
3976
+ pairwiseComparisons[loser][winner] -= vote.balance;
3977
+ }
3978
+ }
3979
+ }
3980
+ // Calculate Copeland scores
3981
+ const scores = Array(choicesCount).fill(0);
3982
+ for (let i = 0; i < choicesCount; i++) {
3983
+ for (let j = 0; j < choicesCount; j++) {
3984
+ if (i !== j) {
3985
+ if (pairwiseComparisons[i][j] > 0) {
3986
+ scores[i]++;
3987
+ }
3988
+ else if (pairwiseComparisons[i][j] < 0) {
3989
+ scores[j]++;
3990
+ }
3991
+ else {
3992
+ scores[i] += 0.5;
3993
+ scores[j] += 0.5;
3994
+ }
3995
+ }
3996
+ }
3997
+ }
3998
+ return scores;
3999
+ }
4000
+ // Calculates the Copeland scores for each choice, broken down by strategy
4001
+ getScoresByStrategy() {
4002
+ const validVotes = this.getValidVotes();
4003
+ const choicesCount = this.proposal.choices.length;
4004
+ const strategiesCount = this.strategies.length;
4005
+ const pairwiseComparisons = Array.from({ length: choicesCount }, () => Array.from({ length: choicesCount }, () => Array(strategiesCount).fill(0)));
4006
+ // Calculate pairwise comparisons for each strategy
4007
+ for (const vote of validVotes) {
4008
+ for (let i = 0; i < vote.choice.length; i++) {
4009
+ for (let j = i + 1; j < vote.choice.length; j++) {
4010
+ const winner = vote.choice[i] - 1;
4011
+ const loser = vote.choice[j] - 1;
4012
+ for (let s = 0; s < strategiesCount; s++) {
4013
+ pairwiseComparisons[winner][loser][s] += vote.scores[s];
4014
+ pairwiseComparisons[loser][winner][s] -= vote.scores[s];
4015
+ }
4016
+ }
4017
+ }
4018
+ }
4019
+ // Calculate Copeland scores for each strategy
4020
+ const scores = Array.from({ length: choicesCount }, () => Array(strategiesCount).fill(0));
4021
+ for (let i = 0; i < choicesCount; i++) {
4022
+ for (let j = 0; j < choicesCount; j++) {
4023
+ if (i !== j) {
4024
+ for (let s = 0; s < strategiesCount; s++) {
4025
+ if (pairwiseComparisons[i][j][s] > 0) {
4026
+ scores[i][s]++;
4027
+ }
4028
+ else if (pairwiseComparisons[i][j][s] < 0) {
4029
+ scores[j][s]++;
4030
+ }
4031
+ else {
4032
+ scores[i][s] += 0.5;
4033
+ scores[j][s] += 0.5;
4034
+ }
4035
+ }
4036
+ }
4037
+ }
4038
+ }
4039
+ return scores;
4040
+ }
4041
+ // Calculates the total score (sum of all valid vote balances)
4042
+ getScoresTotal() {
4043
+ return this.getValidVotes().reduce((total, vote) => total + vote.balance, 0);
4044
+ }
4045
+ // Returns a string representation of the selected choices
4046
+ getChoiceString() {
4047
+ return this.selected
4048
+ .map((choice) => this.proposal.choices[choice - 1])
4049
+ .join(', ');
4050
+ }
4051
+ }
4052
+
3935
4053
  function percentageOfTotal(i, values, total) {
3936
4054
  const reducedTotal = total.reduce((a, b) => a + b, 0);
3937
4055
  const percent = (values[i] / reducedTotal) * 100;
@@ -4004,6 +4122,7 @@ var voting = {
4004
4122
  approval: ApprovalVoting,
4005
4123
  quadratic: QuadraticVoting,
4006
4124
  'ranked-choice': RankedChoiceVoting,
4125
+ copeland: CopelandVoting,
4007
4126
  weighted: WeightedVoting,
4008
4127
  basic: SingleChoiceVoting
4009
4128
  };