DelegateRegistry Contract

Last Update: February 2023

The DelegateRegistry contract is a governance smart contract developed by Gnosis and used by Snapshot Labs to facilitate vote delegation in Snapshot-based governance spaces. Users can authorise another user to vote on their behalf using their voting power, by delegating to them on Ethereum. Users can also remove their delegations at any time.

Through this mechanism, trusted voices in the community can leverage their support with a small amount of effort on the part of their supporters. It also allows those short on time to ensure that their voting power is participating in governance, without requiring them to engage with every proposal that arises.

Contract Mapping

The DelegateRegistry contract is first and foremost a repository of information on existing delegations, which are stored in the "delegation" mapping in the contract:

// The first key is the delegator and the second key a id. 
// The value is the address of the delegate 

mapping (address => mapping (bytes32 => address)) public delegation;

Effectively, each delegation is stored by reference to the delegator's address (i.e. the user that is delegated their voting power), which is then mapped to the relevant Snapshot space ID (stored as a bytes32 value) and the delegate's address (i.e. the user that voting power is delegated to).

For full details of all delegations managed by Snapshot Labs' DelegateRegistry contract, you can explore the Snapshot Subgraph here.

Contract Events

The DelegateRegistry contract emits two possible events in its ordinary operations.

SetDelegate

Signifies that the contract's setDelegate() function has successfully been called, and consequently the caller has selected a new delegate.

// Using these events it is possible to process the events to build up reverse lookups.
// The indeces allow it to be very partial about how to build this lookup (e.g. only for a specific delegate).

event SetDelegate(address indexed delegator, bytes32 indexed id, address indexed delegate);

ClearDelegate

Signifies that one of the below Contract Functions has successfully been called, and consequently the former delegate has been cleared for the caller.

// Using these events it is possible to process the events to build up reverse lookups.
// The indeces allow it to be very partial about how to build this lookup (e.g. only for a specific delegate).

event ClearDelegate(address indexed delegator, bytes32 indexed id, address indexed delegate);

Contract Functions

The functionality behind the DelegateRegistry contract is very straightforward, and consists of just two functions to set and remove delegates.

setDelegate()

To set a delegate, the contract requires two inputs: the relevant Snapshot space ID (stored as a bytes32 value); and the delegate's address (i.e. the user that voting power is delegated to). It then tests the inputs against a few requirements (e.g. the user is not delegating to itself, to a null address or to its current delegate) before executing the call.

/// @dev Sets a delegate for the msg.sender and a specific id.
///      The combination of msg.sender and the id can be seen as a unique key.
/// @param id Id for which the delegate should be set
/// @param delegate Address of the delegate

function setDelegate(bytes32 id, address delegate) public {
        require (delegate != msg.sender, "Can't delegate to self");
        require (delegate != address(0), "Can't delegate to 0x0");
        address currentDelegate = delegation[msg.sender][id];
        require (delegate != currentDelegate, "Already delegated to this address");
        
        // Update delegation mapping
        delegation[msg.sender][id] = delegate;
        
        if (currentDelegate != address(0)) {
            emit ClearDelegate(msg.sender, id, currentDelegate);
        }

        emit SetDelegate(msg.sender, id, delegate);
}

Where the call is successful, the function will update the delegation mapping to the new delegate address. It then tests whether the user had previously delegated to another user, and if so it will then emit the ClearDelegate event to signify that the old delegate has been removed. Finally it will emit the SetDelegateevent to signify that the new delegate has been added.

Please note that the function does not also require the contract to use the clearDelegate() function, which is only used to remove an existing delegate.

clearDelegate()

To clear the current delegate, the contract requires one input, which is the relevant Snapshot space ID (stored as a bytes32 value). It then tests to ensure that a delegate has in fact been set before executing the call.

/// @dev Clears a delegate for the msg.sender and a specific id.
///      The combination of msg.sender and the id can be seen as a unique key.
/// @param id Id for which the delegate should be set

function clearDelegate(bytes32 id) public {
        address currentDelegate = delegation[msg.sender][id];
        require (currentDelegate != address(0), "No delegate set");
        
        // update delegation mapping
        delegation[msg.sender][id] = address(0);
        
        emit ClearDelegate(msg.sender, id, currentDelegate);
}

Where the call is successful, the function will update the delegation mapping to the null address (i.e. singifying that the user has not delegated their voting power), and then emits the ClearDelegate event to signify that the old delegate has been removed.

Delegation Walkthrough

There are two main methods that users can adopt to interact with the DelegateRegistry contract: either interacting through the Snapshot interface or by interacting directly with the contract (e.g. through a relevant block explorer). Below are brief walkthroughs for each methods.

Through the Snapshot Interface

  1. Connect your wallet the site, so that it can detect your address and so you can call the setDelegate() function.

  2. Make sure you're connected to Ethereum with your wallet. No other chain will work.

  3. Input the address or ENS name of the user you want to delegate your voting power to.

  4. If you want to, you can select "limit delegation to a specific space" to confine your delegation to only the Beefy Snapshot space. To do so, input the following space: beefydao.eth.

  5. Click "Confirm" to initiate the transaction in your wallet. As this is a write transaction (i.e. submitting information for storage on the blockchain), you will have to pay a small amount of gas to facilitate the transaction.

  6. Once the transaction goes through, you will have successfully delegated to the user address that you provided across all chains that our BIFI token is deployed to.

Through a Block Explorer

  1. Go to the DelegateRegistry contract address on Etherscan.

  2. Navigate to the "Write Contract" subtab on the "Contract" tab on the contract page (as below).

  3. Select the "Connect to Web3" button to connect your wallet and interact with the contract.

  4. Make sure your wallet is set to Ethereum. No other chain will work.

  1. Scroll down to the setDelegate() function, and input the required details, those being:

    1. id (bytes32) - the address of the Snapshot space for which you are delegating (i.e. the Beefy Space ID: 0x626565667964616f2e657468).

    2. delegate (address) - the address of the user you want to delegate your voting power to.

  2. Click "Write" to initiate the transaction in your wallet. As this is a write transaction (i.e. submitting information for storage on the blockchain), you will have to pay a small amount of gas to facilitate the transaction.

  3. Once the transaction goes through, you will have successfully delegated to the user address that you provided across all chains that our BIFI token is deployed to.

Contracts

The DelegateRegistry contract and Beefy snapshot space are deployed at the following addresses:

More Information

For full details of all delegations managed by Snapshot Labs' DelegateRegistry contract, you can explore the Snapshot Subgraph here. Further information in available in Snapshot's documentation.

Last updated