Agora Liquid Delegator (codename Alligator) allows token holders to subdelegate all their votes to multiple people according to a set of rules. Any delegatee can further sub-delegate their votes to others, optionally adding even more rules.
For example, a token holder 0xAAA
delegates their votes to 0xBBB
, but only allows 0xBBB
use the tokens on proposals that move less than 100 ETH from the treasury. 0xBBB
sub-delegates their votes to 0xCCC
, but only allows voting in the last 12 hours before the vote closes. 0xCCC
can now use 0xAAA
's voting power to cast a vote, but only on small proposals and only in the last 12 hours.
- Sub-delegate to as any number of subjects
- Add rules to sub-delegation:
- Permissions: create proposal, vote, sign (via EIP-1271)
- Limit number of re-delegations
- Set timestamp range when sub-delegation is active
- Limit voting to a number of blocks before vote closes
- Custom rules (calls external contract to validate)
- Casting votes, creating new proposals and voting on Prop House
- Batched operations
- Gas refund
There's a few TODO items in the code and the tests are not comprehensive. But the repo is in good enough shape to ask for external feedback on the architecture & approach.
Alligator is designed to work without holding user's tokens.
- Alligator deploys a proxy contract for every token holder who wants to use the system. The proxy's address is deterministic and the proxy can be deployed by anyone. The proxy only allows commands from Alligator itself.
- The user who wants to delegate their voting power via Alligator's system must first delegate (not transfer!) the original tokens (ERC20s or ERC721s) to the corresponding Alligator's proxy.
- The user can now configure sub-delegations. They can sub-delegate to any number of other users and can limit the sub-delegation to a set of specific rules.
- The user who is delegated to can now vote by calling the corresponding function on Alligator. The user must include one or more delegation chains that they want to exercise.
- The rules engine checks if the subdelegations are allowed, and forwards the request to vote to a proxy. The proxy casts a vote on Governor contract.
[2] VotingToken
β β β β β β β β β .delegateTo β β β β β β β β β β β
(0xAAA's proxy) β
β
ββββββββββββ β
βUser 0xAAAβ ββββββββββββββββββββ βΌ
ββββββββββββ β Alligator β ββββββββββββββββββββ
β ββββββββββββββββββββ€ ββββββΆβ 0xAAA's proxy βββββ
[3] Alligator β- Sub-delegations β β ββββββββββββββββββββ β
.subDelegateββββββΆβ- Signatures β β ββββββββββββββββββββ β ββββββββββββββββββββ
(0xBBB, {rules}) ββββββββββββββββββββ€ vote β ... β vote β β
ββββββββββββββββββββ β ββββββββββββββββββββ ββββΆβ Governor β
ββ ββ β [1] Alligator β β
[4] Alligator ββ RULES ββ β deploys a proxy for ββββββββββββββββββββ
.vote([0xAAA])βββββΆβ ENGINE ββββββ each user to a
β ββ ββ deterministic
β ββββββββββββββββββββ address via CREATE2
ββββββββββββ ββββββββββββββββββββ
βUser 0xBBBβ [5] Check 0xAAA subdelegated
ββββββββββββ to 0xBBB and validate rules
When a user votes via Alligator, they need to construct an authority chain off-chain and send it with their request (similar to Uniswap routing). This way Alligator can only store the essential information and pushes the complexity of finding the most benefitial chain.
The authority chain can be constructed by listening to on-chain subdelegation events and reconstructing graphs. It's possible there will be a few authority chains available to the same user with different constraints.
SubDelegations: Authority Chains:
ββββββββββ¬βββββββββ¬ββββββββββββββββ
β FROM β TO β RULES β βββββββββ βββββββββ βββββββββ
ββββββββββΌβββββββββΌββββββββββββββββ€ β 0xAAA ββββΆβ 0xBBB ββββΆβ 0xCCC β
β 0xAAA β 0xBBB β < 100 ETH β βββββββββ βββββββββ βββββββββ
ββββββββββΌβββββββββΌββββββββββββββββ€
β 0xBBB β 0xCCC β Last 12h β βββββββββ βββββββββ
ββββββββββΌβββββββββΌββββββββββββββββ€ β 0xFFF ββββΆβ 0xCCC β
β 0xFFF β 0xCCC βExpires on 2023β βββββββββ βββββββββ
ββββββββββ΄βββββββββ΄ββββββββββββββββ
In the example above, the user 0xCCC
can use 0xAAA
's and 0xFFF
's voting powerby calling:
Alligator.castVotesWithReasonBatched([[0xAAA, 0xBBB, 0xCCC], [0xFFF, 0xCCC]], 1, 1, "");
From the Governor's perspective, it looks like users 0xAAA's proxy
and 0xFFF's proxy
are casting their votes.
ββββββββββββ ββββββββββββββββββββ ββββββββββββββββββββ
βUser 0xAAAββ delegated ββΆβ 0xAAA's proxy βββvoteβββΆβ β
ββββββββββββ ββββββββββββββββββββ β Governor β
ββββββββββββ ββββββββββββββββββββ β β
βUser 0xFFFββ delegated ββΆβ 0xFFF's proxy βββvoteβββΆβ β
ββββββββββββ ββββββββββββββββββββ ββββββββββββββββββββ
-
Should the rules be scoped to proxy or delegator? The initial prototype of Alligator used per-proxy rules, but we don't think in real life users want different sets of rules for different lots, and it's not how the original Governor works. E.g. if
0xAAA
subdelegates to0xBBB
, we assume A trusts B (within the set rules) so if0xCCC
delegates to0xAAA
,0xBBB
should be able to use these votes. It's also less on-chain storage and better scaling. -
Should we support exclusive delegation? I.e. if
0xAAA
delegates to0xBBB
, should0xAAA
still be able to use that voting power? Supporting this would increase the complexity and raise many questions (e.g. if0xCCC
delegates to0xAAA
, can0xAAA
still vote?). It could also easily be tricked by0xAAA
via un-delegating, voting, and re-delegating back to0xBBB
. So unless we introduce snapshots, adding exclusive delegation support doesn't give us much. -
Is it possible to subdelegate a fraction of the tokens? According to our research, it's not possible unless we either hold users' tokens or change how the base token voting snapshots work. Alligator could theoretically used with something like Franchiser (but we haven't tested this).
-
We are trying to make the contract as ergonomic as possible for the end users. Any friction will result in less actions taken, and we want more government participation. So ideally it should take the minimum number of transactions to set things up and use. For the setup, we need proxy deployment, delegate the original tokens and configure the rules. The proxy deployment is permissionless and can be done beforehand for big users. We can't get around the original token delegation transaction. Configuring the rules should be batched too. For voting, we want to have batched versions of castVote, offering a user to use different chains of authority to vote on a single proposal. We also offer a refund, if funds for it are available.
-
To limit EIP-1271 signatures to Prop House only, we are planning to do EIP-712 hashing on the contract side and check if the domain corresponds to the Prop House.
-
Should the base implementation of Alligator be upgradeable? Not sure, it adds marginal gas cost and security considerations. If we work on extending the feature set of Alligator, we can ask the users who want the extra features to re-delegate to Alligator v2, v3, etc.
- Install foundry
forge test
- Fund
0x77777101E31b4F3ECafF209704E947855eFbd014
with SepoliaETH - Get Etherscan API key
forge script script/DeployAlligator.s.sol -vvvv --fork-url https://rpc-sepolia.rockx.com --chain-id 11155111 --broadcast --verify --etherscan-api-key $ETHERSCAN_API_KEY
Alligator does not hold user's tokens, so it's not possible to steal the tokens using a potential bug in the contract. However, it controls voting power which can be abused to vote on malicious proposals (e.g. transfer all the treasury tokens to evil.eth).