diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..ee3c46d --- /dev/null +++ b/docs/README.md @@ -0,0 +1,18 @@ +# agora specification & documentation + +This folder contains all of the specification and architecture documentation of agora. + +## Technical design + +The `tech-design/` subdirectory contains high level description of architecture involved in building agora's governance system. + +## Implementation + +Agora makes extensive use of plutarch. So it's prerequisite for understanding the implementation. Plutarch features an [_extensive_ guide](https://github.com/Plutonomicon/plutarch/blob/master/docs/GUIDE.md), which explains many intricacies of plutarch and its use. + +## Glossary + +The following is a list of words/shorthands that are frequently used: + +- **Governance Token (GT)**: The token that holds value within the protocol and is used for voting, rewards, etc. _Examples: Liqwid's LQ_. +- **Authority Token (GAT)**: A token that delegates authority of a particular script / token. See [tech-design/authority-token.md](..) diff --git a/docs/tech-design/authority-tokens.md b/docs/tech-design/authority-tokens.md new file mode 100644 index 0000000..f6e3d94 --- /dev/null +++ b/docs/tech-design/authority-tokens.md @@ -0,0 +1,52 @@ +# Authority token technical design + +| Spec Status | Impl Status | Last Revision | +|-------------|-------------|---------------| +| WIP | WIP | 2022-01-18 | + +-------------------- + +**Spec Ownership:** [@Emily Martins] + +**Authors**: [@Emily Martins] + +**Impl Owner:** [@Emily Martins] + +[@Emily Martins]: https://github.com/emiflake + +-------------------- + +## Authority Tokens + +In order to allow proposals to have a large number of potential effects, and to be extensible for different applications of governance, it is useful to have the effects be decided at a later time. In Ethereum systems, often times this is done by encoding some untyped data and calls to specific contract hashes. Compound encodes it with `address[] Proposal.targets` (the contracts to be called) and `bytes[] Proposal.calldatas` (the data to be passed). Of course, this doesn't quite translate to Cardano's EUTXO model, so we need to achieve this some different way. + +In order to allow this flexibility, there are two facts that we rely on: +- We trust the community to validate the proposal entirely, including whether or not the effects encoded in it are written correctly. (This may mean we have a set of known and trusted effects we agree are correct and safe, collectively) +- The effects are given authority only after the proposal that promises them succeeds. + +To achieve the former is rather simple, the effect validator's source code is available for anyone to look at, and it hashes correctly to the hash stored in the proposal itself. So, the LQ holders can decide on whether it is a positive for the system. + +In order to achieve the latter, we must introduce some way to give effects authority to perform their actions. + +We do this by handing out "Governance Authority Tokens" (GATs) to each of the the effects belonging to a proposal after the proposal passes. When these authority tokens are *burned*, they act as a way of saying "the DAO validated this, so trust that I will ensure this transaction is correct". + +The components that need to be adjustable at a later point, will need to allow this as means for proving authority and validation of a transaction. So, for example, a Liqwid Market might need to have its parameters updated, the following diagram shows how this would happen after a proposal has successfully been voted on and passed: + +![](../diagrams/GovernanceAuthorityToken.svg) + +As a result of this approach, there's a number of benefits, but also details we need to watch out for. + +#### Handling multiple effects in a single Proposal + +Handling multile effects this way is very doable. For one, a single effect can do multiple things at once, if the script sizes allow it. But another point is that a proposal can have a *list* of hashes that it distributes GATs to. And this is all without growing size almost at all, because the actual effects are encoded in their scripts. + +There is the concern of expiration date of effects and incomplete execution now, however. But the customizability allows for failsafes (like retrying) and encoding expiration correctly. Due to the nature of these effects being handled by the DAO, it's assumed no conflicting effects will be executed in short succession. This is essentially impossible to encode in the system itself, so this tradeoff must be taken. + +#### Writing effect code needs a *lot* of care + +Having delegated the authority of *the entire system* in a single token is a lot of power for one tiny script. And with great power comes great responsibility. It is important that this token doesn't fall into the wrong hands or is executed in a way that was unexpected (by the community). There's a few things we can do to help mitigate this: + +- We write the validator with the transaction in mind specified out in fullest. No extra inputs, no extra outputs, no extra mints, etc. Essentially this is the opposite of what we do in other places, where we try to be only as specific as we need. +- We have the transaction be executed by one of a number of community trusted members. This of course is something that is encoded in the effect, rather than anywhere else. + +Hopefully, this is sufficient to ensure the transactions are created correctly, and nothing unexpected slips in. This problem is no more complicated or dangerous than elsewhere where we delegate certain trust or authority to just the validation or movement of a token. diff --git a/docs/tech-design/staking-pool.md b/docs/tech-design/staking-pool.md new file mode 100644 index 0000000..a887fec --- /dev/null +++ b/docs/tech-design/staking-pool.md @@ -0,0 +1,66 @@ +# StakingPool technical design + +| Spec Status | Impl Status | Last Revision | +|-------------|-------------|---------------| +| WIP | WIP | 2022-01-18 | + +-------------------- + +**Spec Ownership:** [@Emily Martins] + +**Authors**: [@Emily Martins] + +**Impl Owner:** [@Emily Martins] + +[@Emily Martins]: https://github.com/emiflake + +-------------------- + +## The StakingPool + +In order to be able to count votes at all, some means of proving a user's skin in the game on-chain must exist. We propose having a central StakingPool contract which mints separate per-user UTXOs in which the governance token can be deposited. The MintingPolicy of the state threads ensures that it is paid to the script and with valid initial state. This circumvents the need for a central token, and makes the minting of such tokens concurrently possible. + +### Stake UTXOs + +A stake UTXO stores the information to allow accessing your staked GT as if it was a safe. + +```haskell +data Stake = Stake + { -- | Which proposals has this Stake been used to vote on? + lockedByProposals :: [(DatumHash, ProposalVote)] + , -- | The amount staked by this utxo + stakedAmount :: GT + , -- | Who owns this Stake + owner :: PubKeyHash + } +``` + +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 (pseudocode): + +```haskell +proposal.votes += (stake.stakedAmount, vote) +stake.lockedByProposals += (hash proposal.settings, vote) +``` + +A sort of mutual binding between the proposal and the stake is created and undoing one undoes the other, which is exactly what we want! + +Depositing and withdrawing is made illegal when `stake.lockedByProposals` isn't empty. Withdrawing is illegal so that you can't have GT in a vote, without having it anymore, whereas Depositing is illegal so that you can't deposit after a vote and unvote it again in order to retract more than you originally voted. Thus preserving that + +#### Delegating stake + +Most things like Cosigning sort of work trivially by just witnessing Stake, but delegation requires an extra step. We add a field to what `Stake` stores. + +```haskell +data Stake = Stake + { -- | Which proposals has this Stake been used to vote on? + lockedByProposals :: [(DatumHash, ProposalVote)] + , -- | The amount staked by this utxo + stakedAmount :: GT + , -- | Who can spend our utxo for us when voting + delegatedTo :: Maybe ValidatorHash + , -- | Who owns this Stake + owner :: PubKeyHash + } +``` + +We simply link one stake to another. When voting now the voter can check which Stake utxos are delegated to them off-chain, and include them in the transaction for voting. **This will lock the delegators' utxos**, but that's no big deal because they can themselves unlock it just like usual. The validity of the hash provided to the Stake is irrelevant. It simply delegates its _authentication_ to the particular hash. Note, it only delegates authentication for _voting_ but not for example for withdrawing.