A 'stake' records how much GT has been staked by a user, and on which proposals it has been locked against.
data StakeDatum = StakeDatum
{ stakedAmount :: Tagged GTTag Integer
-- ^ Tracks the amount of governance token staked in the datum.
-- This also acts as the voting weight for 'Agora.Proposal.Proposal's.
, owner :: Credential
-- ^ The owner this stake belongs to.
, lockedBy :: [ProposalLock]
-- ^ The current proposals locking this stake. This field must be empty
-- for the stake to be usable for deposits and withdrawals.
}
Proposal locks can be one of two types, a vote lock, or a creation lock. Creating proposals is artificially limited per stake in order to prevent spam.
data ProposalLock
= -- | The stake was used to create a proposal.
--
-- This kind of lock is placed upon the creation of a proposal, in order
-- to limit creation of proposals per stake.
Created
ProposalId
-- ^ The identifier of the proposal.
| -- | The stake was used to vote on a proposal.
--
-- This kind of lock is placed while voting on a proposal, in order to
-- prevent depositing and withdrawing when votes are in place.
Voted
ProposalId
-- ^ The identifier of the proposal.
ResultTag
-- ^ The option which was voted on. This allows votes to be retracted.
When voting for a proposal, the stake UTXO is used to witness the user's staked amount. As a result, the two following state transitions take place:
proposal.votes += (stake.stakedAmount, vote)
stake.lockedBy += Voted (hash proposal.settings) vote
This forms a mutual binding between the proposal and the stake.
A stake may be used to vote on an unlimited number of proposals. Consider a user staking 50GT. They may pledge that 50GT against a proposal p
and another proposal q
.
Altering the amount positioned in a stake is not possible, for as long as that stake is locked against any proposals. This is to prevent vote manipulation. Consider:
p
. p
now has +100GT in-favour.p
. As his stake is 150GT, p
deducts 150GT and p
now has -50GT in-favour.It should be clear how users could alter their stakes to reduce and manipulate the vote count. Preventing alteration of GT in stakes which have voted ensures that there is never a discrepancy between the amount of GT a user holds and the amount the proposal believes that they hold, expressed in the total vote count.
If a user wished to stake more GT, they could always create a new stake that they would be free to lock on proposals. For this reason, it may be useful to include a method of 'merging' stakes, when they are not being locked against any proposals, to allow users to 'streamline' their GT portfolio. A user may, of course, always retract their vote, deposit more GT, and vote again.
Creating proposals works similarly to how voting on them works when it comes to the locking of stakes: When you create a proposal, you must add a Created
lock (see ProposalLock
above). You can only create proposals if you have sufficiently low amount of existing locks. This lock will prevent spamming of proposals to a certain extent. You can still create infinitely many proposals, it’s just that there is a throughput limit now. Of course, a user may create multiple stakes, if they have access to enough GT, but this should make truly malicious DoS a lot harder.
We say that the throughput of creating proposals (in frequency of blocks) of a particular actor is defined as the following:
$$ \text{creationThroughput}(gtOwned) = \frac{gtOwned}{\text{proposalMinGTCreate} \times \text{proposalMinTime}} $$