Contract interaction
Central to the DAO governance model is its ability to interact with external smart contracts in a decentralized manner. This post aims to shed light on how DAO governance frameworks interact with other contracts, focusing on the role of the Governor contract and the execution of proposals. It will skip over a lot of governance definitions.
The role of the governor contract
The governor contract is at the center of the governance model and handles managing proposals, voting processes, and the execution of decisions based on a successful vote.
Beyond these internal governance functions, the governor contract also plays a crucial role in interacting with other contracts, primarily through ownership and execution of actions.
Ownership of external contracts
Allow me to take a quick detour and discuss contact ownership.
Many smart contracts have an owner
variable that gives that address special privileges. This variable designates an address that has special permissions to perform administrative actions, such as updating contract parameters or executing specific functions. This variable must be written into the contract and is not something that every contract has.
Let’s take a simple contract as an example. Don’t be put off by the length, we will go through the important parts.
// SPDX-License-Idenfitier: UNLICENSED
pragma solidity ^0.8.0;
contract NumberKeeper {
int magicNumber;
mapping (address => bool) public isAuthorized;
event NumberChanged(int indexed newNumber);
constructor() {
isAuthorized[msg.sender] = true;
}
function setNumber (int newNumber) public {
require(isAuthorized[msg.sender] == true, "Not authorized to change");
magicNumber = newNumber;
emit NumberChanged(newNumber);
}
function getNumber () public view returns(int) {
return magicNumber;
}
function checkAccess (address authAddress) public view returns(bool) {
return isAuthorized[authAddress];
}
function changeAccess (address newAddress, bool isGranted) public {
require(isAuthorized[msg.sender] == true, "Not authorized to change");
isAuthorized[newAddress] = isGranted;
}
}
This contract holds a magicNumber and only allows the owner of the contract to change the magic number.
When this contract is deployed the blockchain, the constructor
method is called and the address that deploys it is set as the owner.
Further down in the contract, there is a setNumber
method, and its first line is require(isAuthorized[msg.sender] == true, "Not authorized to change");
. This is a check to make sure that only addresses in the isAuthorized mapping set to true, can execute this function, and change the magic number.
Anyone call read the getNumber()
method.
Then there are two additional methods for access called: checkAccess()
and changeAccess()
which were added in the update to this post.
To recap what this contract does, there is a magic number and only the owner can change the magic number.
What does this have to do with DAO governance you ask, let’s change the owner and I’ll show you.
Changing ownership of the contract
Next we add a method to our contract called changeOwner
, and require the method can only be called by the owner. It accepts a new address and that new address becomes the contracts owner:
function changeOwner (address newOwner) public {
require(msg.sender == owner);
owner = newOwner;
}
When the owner of the contract calls the changeOwner
method with a new address, that new address becomes the owner of the contract and has the ability to call the setNumber
method and change the magicNumber.
In DAO Governance, once the NumberKeeper
contract is completed and ready, I as the owner, can call the changeOwner
method in this contract and pass along the governor contract’s address.
Now, the governor contact is the owner and is the only contract that can change the magic number. How does it change it you ask, through a successful vote of the community.
The steps from proposal to execution
- Proposal Creation: I put in a proposal on-chain stating that I want to change the magic number to 13. In human readable format, it would be:
- If this proposal is successful, call the
setNumber
method on theNumberKeeper
contract at the address0x123...
with the new magic number 13.
- If this proposal is successful, call the
- Voting: Everyone with the governance token has the ability to vote YES/NO/ABSTAIN on the proposal.
- Proposal Passage: Of course the vote is successful and has enough votes to pass the quorum.
- Execution by Governor Contract: As the owner of the external contract, the governor contract has the authority to execute
setNumber
method. - Complete: The magic number has been changed to 13.
We don’t need to have all the contracts in the beginning
The best part of this is that I don’t have to have all the contracts written upfront. Once the governor contract has been deployed we know its address. Then in the future, I write a new set of contracts and set the owner to the governor.
I don’t even have to give all of the control to the governor. Perhaps I have multiple roles owner
, delegate
, zooKeeper
. When I write my contracts I can decide which methods can be executed by each role.
Conclusion
This is a simple and silly example of how the governor contract can interact with an external contract. In the DAO that we are building, the majority of the interactions will follow this pattern but it a bit more complex way.