So my goal is to have a system that can quickly fill our short terms needs while also providing enough of a framework to scale for future needs.
At a high level, I think we want to have a comprehensive and flexible Proposal datatype which can account for the nuances of governance. And we want our voting process to use the parameters of the Proposal regarding things such as vote duration, quorum, majority vs super-majority.
If we get that right, we can then iterate on the methods that can create proposals.
For the most flexible methods, we should have the most restrictive voting parameters.
But the governance system may want to allow itself more lenient voting for certain pre-approved items, which is where a templating system can be used.
For transactions we use the ABI for performance, but for low frequency items that require flexibility, a json data structure might be easier. In theory we could do this by defining new ABI, but not sure it’s worth it.
So let’s first start with a wrapper ABI to store our proposal json and some relevant fields for update/vote checking.
{"type":"variable", "name":"proposalInfo", "inputs":[
{"name":"id", "type":"hash"},
{"name":"proposal", "type":"bytes"},
{"name":"creationTimestamp","type":"int64"},
{"name":"updatedTimestamp","type":"int64"},
{"name":"status","type":"uint8"},
]},
Let’s then define a json schema for a Proposal datatype
We will have some fields for the voting parameters.
And another for the transactions to be sent.
We can come up with a json-schema later for client validation, but here’s an example.
{
"voteType" : 0,
"voteParams" : {
"duration": 14,
"quorum": 0,
"threshold": 50
},
"transactions": [
{
"address": "z1qxemdeddedxsp0rkxxxxxxxxxxxxxxxx956u48",
"tokenStandard": "zts1znnxxxxxxxxxxxxx9z4ulx",
"amount": "0",
"data": "SporkCreationDataInBase64"
},
{
"address": "z1qxemdeddedxg0vernancexxxxxxxxxxxvmwpcx",
"tokenStandard": "zts1znnxxxxxxxxxxxxx9z4ulx",
"amount": "0",
"data": "UpdateVariableDataInBase64"
}
]
}
This is actually pretty similar to the example payload that Sol explored.
His example supported dynamic constants and contract method calls as first class concepts.
His example also mixed voting params with dynamic variables.
I think we should separate out the voting parameters as that cannot be set by the proposer.
And we should treat transactions as the first class concept. It is much more flexible.
Contract methods are just used to render tx data, and dynamic constants can be done with a transaction interface.
In this example, we have a proposal for the embedded governance address send two transactions, one to the spork address, and another to itself. We need to understand the consequences of sending to itself, and the risks of recursion, so we may need to limit what can be sent to itself. Or we handle self sends with special internal logic instead of actually creating transactions.
Let’s go over the different fields.
transactions
is obvious.
An earlier post of mine describes a layer of indirection between rendered transactions and proposals.
That is really only useful to save bandwidth if rendered transactions are going to be reused. I think transaction templates will be reused, and these operations will be infrequent in either case.
voteType
is used to signify which ruleset to use to evaluate if a proposal has passed.
For example, a voteType
of 0
can be used to indicate pillar vote, and voteType
of 1
could be used for single address admin vote.
Different voteType
s have different voteParam
requirements.
In the case of a pillar vote, we need the duration, threshold, and maybe quorum.
Quorum is a little strange because even with hidden votes, there are incentives to not vote to possibly prevent an otherwise successful vote from meeting quorum.
A quorum makes sense in systems where you need to gather enough voters into a session together to all vote at once. In a distributed system, it seems to make less sense. So I think we should allow a 0 value for no quorum.
One modification we should consider where quorum actually becomes useful is a layered voting system. Instead of:
"voteType" : 0,
"voteParams" : {
"duration": 14,
"quorum": 0,
"threshold": 50
},
We could do something like:
"governanceChain": [
{
"voteType" : 0,
"voteParams" : {
"duration": 14,
"quorum": 33,
"threshold": 50
},
{
"voteType" : 1,
"voteParams" : {
"duration": 7,
"address": "z1qpxswrfnlll355wrx868xh58j7e2gu2n2u5czv"
},
]
For each sequence in the governance chain, there are 3 outcomes, PASS, FAIL, INVALID
If the sequence is INVALID, it initiates the next vote type in the sequence.
What this system doesn’t capture is the possibility for parallel voting, but not sure if we want that.
In the above example, there would first be a pillar vote for 2 weeks. If it reached quorum of >33% of pillars, that result would count. If it didn’t reach the quorum it would then move to an admin vote by a single address.
Okay now that we have our flexible proposal datatype which can be voted on, we need a way to create these. Let’s first start with simplest ones.
{"type":"function","name":"ProposeTransaction","inputs":[
{"name":"address", "type":"address"},
{"name":"tokenStandard", "type":"tokenStandard"},
{"name":"amount", "type":"uint256"},
{"name":"data", "type":"bytes"},
]}
{"type":"function","name":"ProposeTransactions","inputs":[
{"name":"txs", "type":"bytes"},
]}
ProposeTransaction
is simpler and somewhat redundant if ProposeTransactions
(plural) exists.
Both methods would create a proposalInfo variable, with a hardcoded governance chain and the relevant tx(s) in the transaction list. In the case of ProposeTransactions
, the txs
field would directly map to the transactions
field of the proposal data.
To actually make this contract work of course as before we need the actual methods for voting, updating, and probably a way to fund/donate fund the contract as well.
In terms of scaling and allowing for ohter governanceChains, in the future we can have other methods such as, ProposeTemplate, ProposeTemplateDeletion, ProposeRenderedTemplates
ProposeTemplate and ProposeTemplate deletion would be meta-proposals that use a strict (e.g supermajority) vote to allow for different/more flexible governance chains for certain types of proposed transactions.
Ideas such as Sol’s spam prevention mechanism can be added as well, but that is just another instance of FAIL outcome with different rules.