Skip to content

Instantly share code, notes, and snippets.

@jordaniza
Last active September 15, 2025 16:30
Show Gist options
  • Select an option

  • Save jordaniza/2ce9cd5e5314612a0d07f78cc49afb10 to your computer and use it in GitHub Desktop.

Select an option

Save jordaniza/2ce9cd5e5314612a0d07f78cc49afb10 to your computer and use it in GitHub Desktop.
ERC20Votes TAIKO Delegation in Aerodrome Pools - Simple delegateTaiko() Solution

ERC20Votes Auto-Delegation for TAIKO in Aerodrome Pools

Executive Summary

This document analyzes the implementation of TAIKO token delegation in Aerodrome liquidity pools to prevent "dead" governance tokens from accumulating in the DEX. The solution is elegantly simple: have pools delegate to a multisig once, and all future TAIKO deposits automatically contribute voting power.

How ERC20Votes Delegation Works

The Key Mechanism

When tokens are transferred, ERC20Votes moves voting power between the delegates of the sender and receiver:

// In _transferVotingUnits (simplified)
_moveDelegateVotes(delegates(from), delegates(to), amount)

This means:

  • If the receiver (Pool) has delegated to a multisig, ALL incoming tokens add to the multisig's voting power
  • The sender's delegation status is irrelevant for the receiver's voting power
  • Once delegated, the pool automatically captures voting power from all deposits

The Solution

Option 1: Delegate on Pool Creation

Add delegation to pool initialization for TAIKO pools:

function initialize(address _token0, address _token1, bool _stable) external {
    // ... existing initialization ...
    
    address TAIKO = 0x...; // TAIKO address
    address MULTISIG = 0x...; // Governance multisig
    
    // If this pool contains TAIKO, delegate all voting power to multisig
    if (_token0 == TAIKO || _token1 == TAIKO) {
        IERC20Votes(TAIKO).delegate(MULTISIG);
    }
}

Option 2: On-Demand Delegation

Add a callable function for existing pools:

contract Pool is IPool, ERC20Permit, ReentrancyGuard {
    // ... existing code ...
    
    /// @notice Delegates all TAIKO voting power in this pool to governance
    /// @dev Only needs to be called once per pool
    function delegateTaiko() external {
        address TAIKO = 0x...; // TAIKO address
        address MULTISIG = 0x...; // Governance multisig
        
        require(
            token0 == TAIKO || token1 == TAIKO, 
            "Pool does not contain TAIKO"
        );
        
        // Check if already delegated to avoid wasted gas
        if (IERC20Votes(TAIKO).delegates(address(this)) == MULTISIG) {
            return;
        }
        
        IERC20Votes(TAIKO).delegate(MULTISIG);
        emit TaikoDelegated(IERC20(TAIKO).balanceOf(address(this)));
    }
    
    event TaikoDelegated(uint256 currentBalance);
}

How It Works

Example Flow

  1. Pool Creation: Pool is created with TAIKO as one token
  2. Delegation: Pool calls delegate(multisig) (once)
  3. User Deposits:
    • User A (never delegated) deposits 1000 TAIKO → Multisig gets +1000 voting power
    • User B (self-delegated) deposits 500 TAIKO → Multisig gets +500 voting power
    • User C (delegated elsewhere) deposits 200 TAIKO → Multisig gets +200 voting power

Why This Works

The _transferVotingUnits function in ERC20Votes:

// When User A transfers to Pool:
// - delegates(User A) = address(0) (never delegated)
// - delegates(Pool) = Multisig (Pool delegated)
// Result: _moveDelegateVotes(address(0), Multisig, amount)
// Multisig gains voting power!

Implementation Options

Option 1: Factory-Level Configuration

// PoolFactory.sol
address public taikoAddress;
address public taikoMultisig;

function createPool(address tokenA, address tokenB, bool stable) external returns (address pool) {
    pool = super.createPool(tokenA, tokenB, stable);
    
    // Auto-delegate for TAIKO pools
    if (tokenA == taikoAddress || tokenB == taikoAddress) {
        IPool(pool).delegateTaiko();
    }
}

// Pool.sol
function delegateTaiko() external {
    (address taiko, address multisig) = IPoolFactory(factory).getTaikoConfig();
    require(token0 == taiko || token1 == taiko, "No TAIKO");
    IERC20Votes(taiko).delegate(multisig);
}

Option 2: Direct Implementation

// Hardcode addresses if known at deployment
function initialize(address _token0, address _token1, bool _stable) external {
    // ... existing initialization ...
    
    if (_token0 == 0x... || _token1 == 0x...) { // TAIKO address
        IERC20Votes(0x...).delegate(0x...); // delegate to multisig
    }
}

Option 3: Minimal Retroactive Fix

For existing pools, add a simple function:

function delegateTaiko() external {
    IERC20Votes(0x...).delegate(0x...); // TAIKO to multisig
}

Benefits

  1. One-Time Setup: Delegate once per pool, works forever
  2. Automatic: All future deposits contribute voting power
  3. Gas Efficient: No per-deposit operations
  4. Simple: Minimal code changes required
  5. Effective: Captures 100% of pool voting power

Considerations

Access Control (Optional)

While anyone can safely call delegation, you might want to restrict it:

modifier onlyGovernance() {
    require(
        msg.sender == IPoolFactory(factory).voter() || 
        msg.sender == MULTISIG,
        "Unauthorized"
    );
    _;
}

function delegateTaiko() external onlyGovernance {
    // ... delegation logic
}

Monitoring

Track delegation status:

function getTaikoDelegation() external view returns (address) {
    address TAIKO = 0x...;
    if (token0 == TAIKO || token1 == TAIKO) {
        return IERC20Votes(TAIKO).delegates(address(this));
    }
    return address(0);
}

Conclusion

The elegant property of ERC20Votes is that once a contract delegates, all received tokens automatically contribute voting power to its chosen delegate. This makes the solution surprisingly simple:

  1. One delegation per pool - Called once, works forever
  2. Automatic capture - All deposits contribute voting power
  3. No maintenance - Set and forget

Whether implemented at pool creation or retroactively, this approach effectively prevents "dead" governance tokens from accumulating in Aerodrome pools with minimal complexity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment