Class: VotingReputation
VotingReputation (Motions & Disputes)
The VotingReputation extension allows any member of a Colony to propose a Motion to take an action that will pass after a security delay unless somebody Objects. This applies to all actions, such as creating an expenditure, managing funds, or managing teams.
Extension parameters
Before the VotingReputation extension can be used you need to initialize it using a few parameters that heavily influence the Motion lifecycle.
For that please see the VotingReputation.initialize function and also this document for an in-depth explanation of the various parameters.
What is a Motion?
Within Colony a Motion is an on-chain proposal whose two sides (Yay or Nay) can be supported by staking the Colony's native token for that side. Motions always have an action attached to them which is a contract transaction that can take place in a Colony. For example, Motions could be created to move funds from one team to another, for paying someone that did work or minting new native tokens (even arbitrary transaction on any contract in the name of the Colony are supported!).
Motions that pass (the outcome is Yay) will execute the action once they are finalized. If the outcome is Nay, there is no action taking place.
The Motion lifecycle
The exact lifecycle is determined by the parameters that were set when the VotingReputation extension was installed for the Colony. You can find a detailed explanation of all the parameters here.
Creating a Motion
See ColonyTxCreator.motion or ColonyTxCreator.metaMotion.
Anyone within a Colony can start a motion. In Colony SDK, this is as easy as sending a transaction of the same kind. There the action (the contract transaction) for the Motion will be defined. This is essentially nothing else than an encoded contract function string alongside its parameters (see for detailed info here - but don't worry. In Colony SDK this will all be taken care of by the ColonyTxCreator).
Staking
See VotingReputation.stakeMotion.
Once a motion is created, native tokens can be staked to support either of its sides (Yay or Nay). A user can only stake as many tokens for either side as they have reputation in the team the motion is created in. A side can be staked by the motion creator and an arbitrary number of additional users as long as they adhere to the minimum stake per user that was defined in the extensions parameters. The maximum amount a user can stake is also determined by the amount of reputation in the team in which the Motion takes place.
Once a side of a Motion is activated, further attempts to stake tokens for that side will revert. It is not necessary or sensible to stake more tokens than needed. Keep in mind that both sides can be activated at the same time (see below for what happens then). You can use the VotingReputation.getRemainingStakes method to see how many tokens need to be staked on each side for them to activate.
After the staking phase (which has a predetermined runtime) one of these four cases can happen:
- Case 1: Yay side reaches minimum stakes, Nay side does not: the motion passed and can be finalized. All stakes are returned
- Case 2: Nay side reaches minimum stakes, Yay side does not: the motion failed and will not execute. All stakes are returned
- Case 3: Neither Yay or Nay side have reached minimum stakes: nothing happens. All stakes are returned.
- Case 4: Both Yay and Nay sides are activated. The Motion will go into Voting immediately (and will not wait out the staking period).
Voting
See VotingReputation.submitVote.
As soon as the voting phase starts, anyone with reputation in the team the Motion was created in can vote for a side. Votes are secret (i.e. they're encrypted using your private key), and weighted by reputation. Vote weight is proportional to the voter's reputation in the team in which the vote is occurring.
Voters receive a reward for voting proportional to their reputation in the team in which the vote is occurring. The reward comes from the stake provided by whichever side of the dispute loses. It doesn't matter whether the voter voters for the winning or the losing side, they are rewarded the same either way.
Revealing the votes
See VotingReputation.revealVote.
After the voting period is complete, the Reveal phase starts. Votes must be Revealed in order to be counted, and for the voter to receive their voter reward. The Reveal phase ends when either the time runs out, or everyone who has voted reveals their vote, whichever comes first.
If you staked tokens for the side that eventually lost you will lose some of your stake. The amount lost will be smaller the closer the decision ultimately ended up being.
The structure of a Motion
You can - at any point in the lifecycle inspect the current state of a Motion. Using the VotingReputation.getMotion method, the resulting object will have the following properties:
| Property | Types | Description |
|---|---|---|
events | [BigNumberish, BigNumberish, BigNumberish] | Timestamps (in seconds) for when the phases for staking, voting and revealing end |
rootHash | BytesLike | The root hash of the reputation tree at the time the motion was created |
domainId | BigNumberish | The domain/team the Motion is in |
skillId | BigNumberish | The skillId associated with the domain |
skillRep | BigNumberish | The total amount of reputation (among all users) that can vote for this motion |
repSubmitted | BigNumberish | The amount of reputation that has submitted a vote |
paidVoterComp | BigNumberish | Total compensation that was paid out to voters who revealed their votes |
pastVoterComp | [BigNumberish, BigNumberish] | Keeps track of previous compensations when escalated |
stakes | [BigNumberish, BigNumberish] | The amounts staked for each of the sides |
votes | [BigNumberish, BigNumberish] | The amount of reputation voted for each side |
escalated | boolean | Whether a Motion was escelated to a higher team |
finalized | boolean | Whether the motion is finalized (done) |
altTarget | string | A contract the motion should act on. Can be a Colony or an extension contract |
action | BytesLike | The data for the contract method that will be executed once the motion passes |
Constructors
new VotingReputation()
new VotingReputation(
colony,votingReputationContract,deployedVersion):VotingReputation
Parameters
colony
votingReputationContract
SupportedVotingReputationContract
deployedVersion
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13
Returns
Properties
address
address:
string
version
version:
1|2|3|4|5|6|7|8|9|10|11|12|13
extensionType
staticextensionType:VotingReputation=Extension.VotingReputation
supportedVersions
staticsupportedVersions: ({factory: typeofIVotingReputation__factory;version:number; } | {factory: typeofIVotingReputation__factory;version:number; } | {factory: typeofIVotingReputation__factory;version:number; })[]
Methods
annotateDecision()
annotateDecision(
txHash,metadata):MetaTxCreator\<SupportedColonyContract,"annotateTransaction", {agent:string;metadata:string;txHash:string; },Decision>
Annotate a decision with IPFS metadata to provide extra information
This will annotate a decision with certain metadata (see below). This only really works for transactions that happened within this Colony. This will connect the decision to the (optionally generated) IPFS hash accordingly.
Parameters
txHash
string
Transaction hash of the transaction to annotate (within the Colony)
metadata
The annotation metadata you would like to annotate the transaction with (or an IPFS CID pointing to valid metadata)
string | DecisionData
Returns
MetaTxCreator\<SupportedColonyContract, "annotateTransaction", { agent: string; metadata: string; txHash: string; }, Decision>
A transaction creator
Event data
| Property | Type | Description |
|---|---|---|
agent | string | The address that is responsible for triggering this event |
txHash | string | The hash of the annotated transaction |
metadata | string | The IPFS hash (CID) of the metadata object |
Metadata
(can be obtained by calling and awaiting the getMetadata function)
| Property | Type | Description |
|---|---|---|
title | string | Title of the decision |
description | string | Longer description of the decision |
motionDomainId | number | Team the decision was created in |
Remarks
If DecisionData is provided directly (as opposed to a CID for a JSON file) this requires an IpfsAdapter that can upload and pin to IPFS. See its documentation for more information. Keep in mind that the annotation itself is a transaction.
Example
// Immediately executing async function
(async function() {
// Create a motion to pay 10 of the native token to some (maybe your own?) address
const [, { transactionHash }] = await colony.ext.motions.createDecision().tx().mined();
// Annotate the decision transaction with important data
// (forced transaction example)
await colony.ext.motions.annotateDecision(
transactionHash,
{
title: 'Should we make the naked-mole-rat our official mascot?',
description: 'I think it is time',
},
).tx().mined();
})();
approveStake()
approveStake(
amount,teamId):MetaTxCreator\<SupportedColonyContract,"approveStake", {amount:BigNumber;approvedBy:string;token:string;user:string; },MetadataType>
Approve amount of the "activated" native tokens of a user for staking in a specific team
After a token was "activated" (approved and deposited via the native token interface) it can be used for staking motions. To stake a motion, the token amount for staking has to be approved for the domain the motion was created in. See also the example in VotingReputation.stakeMotion
Parameters
amount
BigNumberish
Amount of "activated" tokens to be approved for staking
teamId
BigNumberish = Id.RootDomain
Team that the approved tokens can be used in for staking motions
Returns
MetaTxCreator\<SupportedColonyContract, "approveStake", { amount: BigNumber; approvedBy: string; token: string; user: string; }, MetadataType>
A transaction creator
Event data
| Property | Type | Description |
|---|---|---|
token | string | The address of the token that is approved for staking |
user | string | The address of the VotingReputation contract |
approvedBy | string | The address of the Colony |
amount | BigNumber | Amount of the token that was approved for staking |
Remarks
This method can't be executed as a motion
createDecision()
createDecision(
team):MetaTxCreator\<SupportedVotingReputationContract,"createMotion", {creator:string;domainId:BigNumber;motionId:BigNumber; },MetadataType>
Annotate a decision with IPFS metadata to provide extra information
Keep in mind that a decision is just a motion without an on-chain action that is being triggered once it finalizes
Parameters
team
BigNumberish = Id.RootDomain
Team id to create the decision in
Returns
MetaTxCreator\<SupportedVotingReputationContract, "createMotion", { creator: string; domainId: BigNumber; motionId: BigNumber; }, MetadataType>
A transaction creator
Event data
| Property | Type | Description |
|---|---|---|
motionId | BigNumber | Id of the decision (motion) |
creator | string | Address of the user who created the decision |
domainId | BigNumber | Team the decision was created in |
Remarks
If DecisionData is provided directly (as opposed to a CID for a JSON file) this requires an IpfsAdapter that can upload and pin to IPFS. See its documentation for more information. Keep in mind that the annotation itself is a transaction.
Example
// Immediately executing async function
(async function() {
// Create an empty decision in the Root team
const [, { transactionHash }] = await colony.ext.motions.createDecision().tx().mined();
// Annotate the decision transaction with important data
// (forced transaction example)
await colony.ext.motions.annotateDecision(
transactionHash,
{
title: 'Should we make the naked-mole-rat our official mascot?',
description: 'I think it is time',
},
).tx().mined();
})();
escalateMotion()
escalateMotion(
motionId,newTeamId):MetaTxCreator\<SupportedVotingReputationContract,"escalateMotion", {domainId:BigNumber;escalator:string;motionId:BigNumber;newDomainId:BigNumber; },MetadataType>
Escalate a motion to a parent team
If all votes for a motion have been revealed but a user is not happy with the outcome, it can be escalated to a parent team (including grandparents, great-grandparents, etc.) as long as the escalation period has not passed yet.
Parameters
motionId
BigNumberish
The motionId of the motion to be escalated
newTeamId
BigNumberish
The id of the team the motion should be escalated to
Returns
MetaTxCreator\<SupportedVotingReputationContract, "escalateMotion", { domainId: BigNumber; escalator: string; motionId: BigNumber; newDomainId: BigNumber; }, MetadataType>
A transaction creator
Event data
| Property | Type | Description |
|---|---|---|
motionId | BigNumber | ID of the motion to be escalated |
newTeamId | string | The ID of the team the motion should be escalated to (has to be a direct parent of the previous team) |
finalizeMotion()
finalizeMotion(
motionId):MetaTxCreator\<SupportedVotingReputationContract,"finalizeMotion", {action:string;executed:boolean;motionId:BigNumber; },MetadataType>
Finalize a motion, executing its action
Parameters
motionId
BigNumberish
The motionId of the motion to be finalized
Returns
MetaTxCreator\<SupportedVotingReputationContract, "finalizeMotion", { action: string; executed: boolean; motionId: BigNumber; }, MetadataType>
A transaction creator
Event data
| Property | Type | Description |
|---|---|---|
motionId | BigNumber | ID of the motion created |
action | string | The action that was executed in case Yay won |
exectued | boolean | Whether the action was executed (Yay won) |
Remarks
- A motion cannot be finalized if:
- The required Yay or Nay stake amount has not been reached
- The staking period is not up yet
- Voting is still in progress
- The motion was already finalized
- This method can't be executed as a motion
getInitializationOptions()
getInitializationOptions():
Promise\<{endVoteThreshold:BigNumber;escalationPhaseDuration:BigNumber;minimumUserStake:BigNumber;requiredStake:BigNumber;revealPhaseDuration:BigNumber;stakePhaseDuration:BigNumber;voterReward:BigNumber;votingPhaseDuration:BigNumber; }>
Get the initialized configuration options
Get the configuration options that were set when the extension was initialized. For more information on the individual values see https://docs.colony.io/learn/governance/parameters
Returns
Promise\<{ endVoteThreshold: BigNumber; escalationPhaseDuration: BigNumber; minimumUserStake: BigNumber; requiredStake: BigNumber; revealPhaseDuration: BigNumber; stakePhaseDuration: BigNumber; voterReward: BigNumber; votingPhaseDuration: BigNumber; }>
The extensions configuration options
getMinStake()
getMinStake(
motion,vote):Promise\<BigNumber>
Get the minimum stake that has to be supplied for a motion and a certain vote (NOT for activation)
Parameters
motion
A Motion struct object
vote
A vote for (Yay) or against (Nay) the motion
Returns
Promise\<BigNumber>
The minimum stake amount
Remarks
To get the missing amount for activation, call getRemainingStakes
getMotion()
getMotion(
motionId):Promise\<Motion>
Get a motion by its id
Parameters
motionId
BigNumberish
The motionId to get the information for
Returns
Promise\<Motion>
A Motion object
Remarks
Will throw if motionId does not exist
getMotionResult()
getMotionResult(
motionId):Promise\<null|Vote>
Get the result of a motion (if it is finalizeable or finalized)
Parameters
motionId
BigNumberish
The motionId to get the result for
Returns
Promise\<null | Vote>
The result of the motion (0 = Nay, 1 = Yay)
Remarks
Will return null if the motion is not finalizeable yet
getMotionState()
getMotionState(
motionId):Promise\<number>
Get the motion state as a number
Will be one of the following:
Staking(= 1)Submit(= 2)Reveal(= 3)Closed(= 4)Finalizable(= 5)Finalized(= 6)Failed(= 7)
Parameters
motionId
BigNumberish
The motionId to get the state for
Returns
Promise\<number>
The motion state
Example
import { MotionState } from '@colony/sdk';
// Immediately executing async function
(async function() {
// Get state of Motion `1`
const state = await colony.ext.motions.getMotionState(1);
const stateStr = MotionState[state]; // `Staking` (or another of the above)
})();
Remarks
Will throw if motionId does not exist
getRemainingStakes()
getRemainingStakes(
motionId):Promise\<{remainingToFullyNayStaked:BigNumber;remainingToFullyYayStaked:BigNumber; }>
Get the amounts remaining for Yay/Nay sides to be activated
Parameters
motionId
BigNumberish
The motionId of the motion
Returns
Promise\<{ remainingToFullyNayStaked: BigNumber; remainingToFullyYayStaked: BigNumber; }>
An object containing the remaining amounts
initialize()
initialize(
totalStakeFraction,voterRewardFraction,userMinStakeFraction,maxVoteFraction,stakePeriod,submitPeriod,revealPeriod,escalationPeriod):MetaTxCreator\<SupportedVotingReputationContract,"initialise", {},MetadataType>
Before you can use the extension you need to initialize it by calling this function.
Parameters
totalStakeFraction
BigNumber
The fraction of the domain's reputation we need to stake
voterRewardFraction
BigNumber
The fraction of the total stake paid out to voters as rewards
userMinStakeFraction
BigNumber
The minimum per-user stake as fraction of total stake
maxVoteFraction
BigNumber
The fraction of the domain's reputation which must submit for quick-end
stakePeriod
number
The length of the staking period in seconds
submitPeriod
number
The length of the submit period in seconds
revealPeriod
number
The length of the reveal period in seconds
escalationPeriod
number
The length of the escalation period in seconds
Returns
MetaTxCreator\<SupportedVotingReputationContract, "initialise", {}, MetadataType>
A transaction creator
Event data
No event data attached
Remarks
- Fractions are in wei to allow for precise adjustments (you can use the
whelper method to specify fractions - see example) - All periods values are given in seconds
See also this page for a detailed explanation of the parameters.
Example
import { w } from '@colony/sdk';
// Immediately executing async function
(async function() {
// After installing the extension, initialize the extension with the dApp's default values
await colony.ext.motions.initialize(
w`0.01`,
w`0.2`,
w`0.01`,
w`0.7`,
72 * 60 * 60,
72 * 60 * 60,
72 * 60 * 60,
72 * 60 * 60,
).tx().mined();
})();
revealVote()
revealVote(
motionId,vote?):MetaTxCreator\<SupportedVotingReputationContract,"revealVote", {motionId:BigNumber;vote:BigNumber;voter:string; },MetadataType>
Reveal a vote for a motion
Parameters
motionId
BigNumberish
The motionId of the motion to be finalized
vote?
The vote that was cast. If not provided Colony SDK will try to find out which side was voted on (not recommended)
Returns
MetaTxCreator\<SupportedVotingReputationContract, "revealVote", { motionId: BigNumber; vote: BigNumber; voter: string; }, MetadataType>
A transaction creator
Event data
| Property | Type | Description |
|---|---|---|
motionId | BigNumber | ID of the motion to be revealed |
voter | string | The address of the user who voted |
vote | BigNumber | The vote that was cast (0 = Nay, 1 = Yay) |
Remarks
- In order for a vote to count it has to be revealed within the reveal period. Only then rewards can be paid out to the voter.
- This method can't be executed as a motion
stakeMotion()
stakeMotion(
motionId,vote,amount):MetaTxCreator\<SupportedVotingReputationContract,"stakeMotion", {amount:BigNumber;eventIndex:BigNumber;motionId:BigNumber;staker:string;vote:BigNumber; },MetadataType>
Stake amount to support a motion with your vote
Parameters
motionId
BigNumberish
vote
amount
BigNumberish
Amount of the token to be approved
Returns
MetaTxCreator\<SupportedVotingReputationContract, "stakeMotion", { amount: BigNumber; eventIndex: BigNumber; motionId: BigNumber; staker: string; vote: BigNumber; }, MetadataType>
A transaction creator
Event data
| Property | Type | Description |
|---|---|---|
motionId | BigNumber | ID of the motion to stake for |
staker | string | The address that staked the tokens |
vote | Vote | The vote that was staked for (Yay or Nay). See Vote |
amount | BigNumber | The amount that was staked for that vote |
eventIndex | BigNumber | If the stake triggered a motion event, this will contain its index |
Remarks
- In order to stake a motion the amount to stake (or any amount higher than that) needs to be "activated" and approved for staking in the motion's team first. See below for a full example. Usually you would have more tokens "activated" or even approved for a domain than you would stake, to make this process more seamless
- This method can't be executed as a motion
Example
import { Vote, w } from '@colony/sdk';
// Immediately executing async function
(async function() {
const token = colony.getToken();
// Approve 200 tokens to be "activated"
await token.approve(w`200`);
// Deposit all of approved the tokens
await token.deposit(w`200`);
// Approve 150 tokens for staking in the root domain (can only be forced)
await colony.ext.motions.approveStake(w`150`).tx().mined();
// Stake 100 tokens for motion with id 3
await colony.ext.motions.stakeMotion(3, Vote.Yay, w`100`);
})();
submitVote()
submitVote(
motionId,vote):MetaTxCreator\<SupportedVotingReputationContract,"submitVote", {motionId:BigNumber;voter:string; },MetadataType>
Submit a vote for a motion
Parameters
motionId
BigNumberish
The motionId of the motion to be finalized
vote
A vote for (Yay) or against (Nay) the motion
Returns
MetaTxCreator\<SupportedVotingReputationContract, "submitVote", { motionId: BigNumber; voter: string; }, MetadataType>
A transaction creator
Event data
| Property | Type | Description |
|---|---|---|
motionId | BigNumber | ID of the motion to submit a vote for |
voter | string | The address of the user who voted |
Remarks
This method can't be executed as a motion
upgrade()
upgrade(
toVersion?):ColonyTxCreator\<SupportedColonyContract,"upgradeExtension", {colony:string;extensionId:string;version:BigNumber; },MetadataType>
Upgrade this extension to the next or a custom version
This method upgrades this extension to a specified version or, if no version is provided to the next higher version.
Parameters
toVersion?
BigNumberish
Specify a custom version to upgrade the extension to
Returns
ColonyTxCreator\<SupportedColonyContract, "upgradeExtension", { colony: string; extensionId: string; version: BigNumber; }, MetadataType>
A transaction creator
Event data
| Property | Type | Description |
|---|---|---|
extensionId | string | Extension id (name of the extension) that was upgraded |
oldVersion | BigNumber | Version of the extension before the upgrade |
newVersion | BigNumber | Version of the extension after the upgrade |
Remarks
- Only users with Root role are allowed to upgrade an extension (or another extension with appropriate permissions)
- Downgrading of extensions is not possible
connect()
staticconnect(colony):Promise\<VotingReputation>
Parameters
colony
Returns
Promise\<VotingReputation>
getLatestSupportedVersion()
staticgetLatestSupportedVersion():1|2|3|4|5|6|7|8|9|10|11|12|13
Returns
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13