🎓 A1Learner
🔍
0 of 45 lessons completed

Solidity Delegation

Delegation is a system where a token holder hands their voting power to another address. The delegated power then counts for the delegate, not for the holder who gave it away.

Delegation moves the counted power

A holder with 100 delegates to a holder with 10. The first holder counts 0 and the second counts 110 after the move.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract Delegation {
    address public owner;
    mapping(address => uint256) public ownPower;
    mapping(address => uint256) public countedPower;
    mapping(address => address) public delegateOf;
    constructor() {
        owner = msg.sender;
    }
    function grant(address user, uint256 amount) public {
        require(msg.sender == owner, "only owner");
        ownPower[user] += amount;
        countedPower[user] += amount;
    }
    function delegate(address to) public {
        require(delegateOf[msg.sender] == address(0), "already delegated");
        require(to != msg.sender, "cannot delegate to self");
        uint256 p = ownPower[msg.sender];
        require(p > 0, "no power");
        countedPower[msg.sender] -= p;
        countedPower[to] += p;
        delegateOf[msg.sender] = to;
    }
    function undelegate() public {
        address to = delegateOf[msg.sender];
        require(to != address(0), "not delegated");
        uint256 p = ownPower[msg.sender];
        countedPower[to] -= p;
        countedPower[msg.sender] += p;
        delegateOf[msg.sender] = address(0);
    }
}

Taking power back moves it again

After delegating, the holder takes the power back. The holder counts 100 again and the delegate falls back to 10.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract Delegation {
    address public owner;
    mapping(address => uint256) public ownPower;
    mapping(address => uint256) public countedPower;
    mapping(address => address) public delegateOf;
    constructor() {
        owner = msg.sender;
    }
    function grant(address user, uint256 amount) public {
        require(msg.sender == owner, "only owner");
        ownPower[user] += amount;
        countedPower[user] += amount;
    }
    function delegate(address to) public {
        require(delegateOf[msg.sender] == address(0), "already delegated");
        require(to != msg.sender, "cannot delegate to self");
        uint256 p = ownPower[msg.sender];
        require(p > 0, "no power");
        countedPower[msg.sender] -= p;
        countedPower[to] += p;
        delegateOf[msg.sender] = to;
    }
    function undelegate() public {
        address to = delegateOf[msg.sender];
        require(to != address(0), "not delegated");
        uint256 p = ownPower[msg.sender];
        countedPower[to] -= p;
        countedPower[msg.sender] += p;
        delegateOf[msg.sender] = address(0);
    }
}

A second delegation reverts

A holder who already delegated cannot delegate again to someone else. The holder must take the power back first.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract Delegation {
    address public owner;
    mapping(address => uint256) public ownPower;
    mapping(address => uint256) public countedPower;
    mapping(address => address) public delegateOf;
    constructor() {
        owner = msg.sender;
    }
    function grant(address user, uint256 amount) public {
        require(msg.sender == owner, "only owner");
        ownPower[user] += amount;
        countedPower[user] += amount;
    }
    function delegate(address to) public {
        require(delegateOf[msg.sender] == address(0), "already delegated");
        require(to != msg.sender, "cannot delegate to self");
        uint256 p = ownPower[msg.sender];
        require(p > 0, "no power");
        countedPower[msg.sender] -= p;
        countedPower[to] += p;
        delegateOf[msg.sender] = to;
    }
    function undelegate() public {
        address to = delegateOf[msg.sender];
        require(to != address(0), "not delegated");
        uint256 p = ownPower[msg.sender];
        countedPower[to] -= p;
        countedPower[msg.sender] += p;
        delegateOf[msg.sender] = address(0);
    }
}

Example

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract Delegation {
    address public owner;
    mapping(address => uint256) public ownPower;
    mapping(address => uint256) public countedPower;
    mapping(address => address) public delegateOf;
    constructor() {
        owner = msg.sender;
    }
    function grant(address user, uint256 amount) public {
        require(msg.sender == owner, "only owner");
        ownPower[user] += amount;
        countedPower[user] += amount;
    }
    function delegate(address to) public {
        require(delegateOf[msg.sender] == address(0), "already delegated");
        require(to != msg.sender, "cannot delegate to self");
        uint256 p = ownPower[msg.sender];
        require(p > 0, "no power");
        countedPower[msg.sender] -= p;
        countedPower[to] += p;
        delegateOf[msg.sender] = to;
    }
    function undelegate() public {
        address to = delegateOf[msg.sender];
        require(to != address(0), "not delegated");
        uint256 p = ownPower[msg.sender];
        countedPower[to] -= p;
        countedPower[msg.sender] += p;
        delegateOf[msg.sender] = address(0);
    }
}

Key points

  • Delegation: Delegation is a system where a token holder hands their voting power to another address.
  • Delegate: A delegate is the address that receives voting power from another holder.
  • Counted power: Counted power is the voting power that counts for an address after delegation.
  • Own power: Own power is the voting power a holder starts with before delegating.
  • Redelegation: Redelegation here means taking delegated power back, so it counts for the holder again.
  • Restatement: Delegated power counts for the delegate, not for the holder, until the holder takes it back.

In this example, two holders delegate 40 and 60 to a third holder who already has 10, so the third holder counts 110. When the second holder takes power back, the third holder falls to 50 and the second holder counts 60 again.

Note

This lesson tracks power as numbers set by the owner, so the movement of power stays visible without a full token.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Remix IDE

Exercise

A holder with 40 delegates to a delegate who already has 10. How much does the delegate count?