# L2 veCRV Delegation

Source: [https://docs.curve.finance/developer/curve-dao/voting-escrow/crosschain/vecrv-delegation](https://docs.curve.finance/developer/curve-dao/voting-escrow/crosschain/vecrv-delegation)

The `L2veCRVDelegation` contract enables users to delegate their veCRV voting power to different addresses on other networks (Layer 2s or sidechains). This is essential for veCRV-utility activities like veCRV boosting on chains where their original address may not be available or convenient. The contract supports both user-initiated delegation and DAO-administered delegation for special cases (e.g., lost keys or non-reachable addresses). The contract also includes mechanisms to allow or revoke delegation to an address and to prevent frontrunning attacks.

`L2veCRVDelegation.vy`

The source code for the `L2veCRVDelegation.vy` contract is available on [GitHub](https://github.com/curvefi/storage-proofs/blob/main/contracts/vecrv/VecrvDelegate.vy). The contract is written in [Vyper](https://vyperlang.org/) version `0.4.0`.

The `L2veCRVDelegation` on Ethereum is deployed at [`0xde1e6A7E8297076f070E857130E593107A0E0cF5`](https://etherscan.io/address/0xde1e6A7E8297076f070E857130E593107A0E0cF5) and contract version is `0.0.1`.

**{ }Contract ABI▼**

```json
[{"anonymous":false,"inputs":[{"indexed":true,"name":"_chain_id","type":"uint256"},{"indexed":true,"name":"_to","type":"address"}],"name":"AllowDelegation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_chain_id","type":"uint256"},{"indexed":true,"name":"_from","type":"address"},{"indexed":false,"name":"_to","type":"address"}],"name":"Delegate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previous_owner","type":"address"},{"indexed":true,"name":"new_owner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"name":"new_owner","type":"address"}],"name":"transfer_ownership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"_chain_id","type":"uint256"},{"name":"_from","type":"address"}],"name":"delegated","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"_chain_id","type":"uint256"},{"name":"_to","type":"address"}],"name":"delegator","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"_chain_id","type":"uint256"},{"name":"_to","type":"address"}],"name":"delegation_allowed","outputs":[{"name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"_chain_id","type":"uint256"},{"name":"_to","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_chain_id","type":"uint256"}],"name":"allow_delegation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_chain_id","type":"uint256"},{"name":"_allow","type":"bool"}],"name":"allow_delegation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_chain_id","type":"uint256"},{"name":"_from","type":"address"},{"name":"_to","type":"address"}],"name":"delegate_from","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"version","outputs":[{"name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"_owner","type":"address"}],"outputs":[],"stateMutability":"nonpayable","type":"constructor"}]
```

* * *

<a id="delegation"></a>

## Delegation

The delegation system in `L2veCRVDelegation` is designed to be flexible and secure. Users can delegate their veCRV voting power to another address on a specific chain, revoke delegation, or allow others to delegate to them. The contract also provides DAO-level controls for exceptional cases, ensuring that delegation can be managed even if a user is unable to interact directly. The following functions describe the available delegation mechanisms and their intended use cases.

<a id="delegate"></a>

### `delegate`

`L2veCRVDelegation.delegate(_chain_id: uint256, _to: address)`

Function to delegate veCRV to another address on a specific chain. Only addresses that have explicitly allowed delegation (via `allow_delegation`) can be delegated to. To revoke delegation, delegate to your own address.

Emits: `Delegate` event.

| Input | Type | Description |
| --- | --- | --- |
| `_chain_id` | `uint256` | Chain ID where to set the delegation |
| `_to` | `address` | Address to delegate to |

**<>Source code▼**

L2veCRVDelegation.vy

```vyper
event Delegate:
    _chain_id: indexed(uint256)
    _from: indexed(address)
    _to: address

# [chain id][address from][address to]
delegation_from: HashMap[uint256, HashMap[address, address]]
delegation_to: HashMap[uint256, HashMap[address, address]]

@external
def delegate(_chain_id: uint256, _to: address):
    """
    @notice Delegate veCRV balance to another address
    @dev To revoke delegation set delegation to yourself
    @param _chain_id Chain ID where to set
    @param _to Address to delegate to
    """
    assert self.delegation_to[_chain_id][_to] == self, "Not allowed"
    self._delegate(_chain_id, msg.sender, _to)

def _delegate(_chain_id: uint256, _from: address, _to: address):
    # Clean previous delegation
    prev_to: address = self.delegation_from[_chain_id][_from]
    if prev_to not in [empty(address), self]:
        self.delegation_to[_chain_id][prev_to] = empty(address)

    self.delegation_from[_chain_id][_from] = _to
    self.delegation_to[_chain_id][_to] = _from
    log Delegate(_chain_id, _from, _to)
```

**▶Example▼**

This example delegates the caller's veCRV balance to `0x71F718D3e4d1449D1502A6A7595eb84eBcCB1683` on chain `146`.

```shell
>>> L2veCRVDelegation.delegate(146, '0x71F718D3e4d1449D1502A6A7595eb84eBcCB1683')
```

<a id="delegate_from"></a>

### `delegate_from`

`L2veCRVDelegation.delegate_from(_chain_id: uint256, _from: address, _to: address)`

Guarded Method by [Snekmate 🐍](https://github.com/pcaversaccio/snekmate)

This contract makes use of a Snekmate module to manage roles and permissions. This specific function can only be called by the current `owner` of the contract.

DAO-only function to set delegation for addresses that cannot interact directly (e.g., lost keys or non-reachable addresses). Only callable by the contract owner.

Emits: `Delegate` event.

| Input | Type | Description |
| --- | --- | --- |
| `_chain_id` | `uint256` | Chain ID where to set the delegation |
| `_from` | `address` | Address that delegates |
| `_to` | `address` | Address balance being delegated to |

**<>Source code▼**

```vyper
from snekmate.auth import ownable

event Delegate:
    _chain_id: indexed(uint256)
    _from: indexed(address)
    _to: address

# [chain id][address from][address to]
delegation_from: HashMap[uint256, HashMap[address, address]]
delegation_to: HashMap[uint256, HashMap[address, address]]

@external
def delegate_from(_chain_id: uint256, _from: address, _to: address):
    """
    @notice DAO-owned method to set delegation for non-reachable addresses
    @param _chain_id Chain ID where to set
    @param _from Address that delegates
    @param _to Address balance being delegated to
    """
    ownable._check_owner()

    self._delegate(_chain_id, _from, _to)

def _delegate(_chain_id: uint256, _from: address, _to: address):
    # Clean previous delegation
    prev_to: address = self.delegation_from[_chain_id][_from]
    if prev_to not in [empty(address), self]:
        self.delegation_to[_chain_id][prev_to] = empty(address)

    self.delegation_from[_chain_id][_from] = _to
    self.delegation_to[_chain_id][_to] = _from
    log Delegate(_chain_id, _from, _to)
```

```vyper
owner: public(address)

@internal
def _check_owner():
    """
    @dev Throws if the sender is not the owner.
    """
    assert msg.sender == self.owner, "ownable: caller is not the owner"
```

**▶Example▼**

In this example, the DAO delegates the veCRV balance from `0x5802ad5D5B1c63b3FC7DE97B55e6db19e5d36462` to `0x71F718D3e4d1449D1502A6A7595eb84eBcCB1683` on chain `146`.

```shell
# DAO sets delegation for a non-reachable address
>>> L2veCRVDelegation.delegate_from(146, '0x5802ad5D5B1c63b3FC7DE97B55e6db19e5d36462', '0x71F718D3e4d1449D1502A6A7595eb84eBcCB1683')
```

<a id="allow_delegation"></a>

### `allow_delegation`

`L2veCRVDelegation.allow_delegation(_chain_id: uint256, _allow: bool = True)`

Allows or revokes permission for others to delegate veCRV to your address on a specific chain. This must be called before anyone can delegate to you, and is required to prevent frontrunning attacks.

Emits: `AllowDelegation` or `Delegate` event.

| Input | Type | Description |
| --- | --- | --- |
| `_chain_id` | `uint256` | Chain ID |
| `_allow` | `bool` | `true` to allow delegation, `false` to remove; defaults to `true` |

**<>Source code▼**

L2veCRVDelegation.vy

```vyper
event AllowDelegation:
    _chain_id: indexed(uint256)
    _to: indexed(address)

event Delegate:
    _chain_id: indexed(uint256)
    _from: indexed(address)
    _to: address

# [chain id][address from][address to]
delegation_from: HashMap[uint256, HashMap[address, address]]
delegation_to: HashMap[uint256, HashMap[address, address]]

@external
def allow_delegation(_chain_id: uint256, _allow: bool = True):
    """
    @notice Allow delegation to your address
    @dev Needed to deal with frontrun
    @param _chain_id Chaind ID to allow for
    @param _allow True(default) if allow, and False to remove delegation
    """
    # Clean current delegation
    _from: address = self.delegation_to[_chain_id][msg.sender]
    if _from not in [empty(address), self]:
        self.delegation_from[_chain_id][_from] = empty(address)
        log Delegate(_chain_id, _from, empty(address))

    if _allow:
        self.delegation_to[_chain_id][msg.sender] = self
        log AllowDelegation(_chain_id, msg.sender)
    else:
        self.delegation_to[_chain_id][msg.sender] = empty(address)
```

**▶Example▼**

This example shows how to allow and revoke delegation.

```shell
>>> L2veCRVDelegation.allow_delegation(146, True)       # Allow delegation to your address on chain 146

>>> L2veCRVDelegation.allow_delegation(146, False)      # Revoke delegation
```

<a id="delegation_allowed"></a>

### `delegation_allowed`

`L2veCRVDelegation.delegation_allowed(_chain_id: uint256, _to: address) -> bool: view`

Getter method to check whether delegation to a given address is currently allowed on a specific chain.

Returns: `true` if delegation is allowed, `false` otherwise (`bool`).

| Input | Type | Description |
| --- | --- | --- |
| `_chain_id` | `uint256` | Chain ID |
| `_to` | `address` | Address to check for |

**<>Source code▼**

L2veCRVDelegation.vy

```vyper
delegation_to: HashMap[uint256, HashMap[address, address]]

@external
@view
def delegation_allowed(_chain_id: uint256, _to: address) -> bool:
    """
    @notice Check whether delegation to this address is allowed
    @param _chain_id Chain ID to check for
    @param _to Address to check for
    @return True if allowed to delegate
    """
    return self.delegation_to[_chain_id][_to] == self
```

**▶Example▼**

This example checks if delegation for a given address is allowed on a specific chain. Enter a chain ID and address, then click **Query** to fetch the value live from the blockchain.

<a id="delegated"></a>

### `delegated`

`L2veCRVDelegation.delegated(_chain_id: uint256, _from: address) -> address: view`

Returns the address to which a given user's veCRV balance is delegated on a specific chain. If no delegation is set, returns the original address.

Returns: delegation destination (`address`).

| Input | Type | Description |
| --- | --- | --- |
| `_chain_id` | `uint256` | Chain ID |
| `_from` | `address` | Delegator |

**<>Source code▼**

L2veCRVDelegation.vy

```vyper
delegation_from: HashMap[uint256, HashMap[address, address]]

@external
@view
def delegated(_chain_id: uint256, _from: address) -> address:
    """
    @notice Get contract balance being delegated to
    @param _chain_id Chain ID to check for
    @param _from Address of delegator
    @return Destination address of delegation
    """
    addr: address = self.delegation_from[_chain_id][_from]
    if addr == empty(address):
        addr = _from
    return addr
```

**▶Example▼**

This example returns the address to which a given user's veCRV balance is delegated on a specific chain. Enter a chain ID and delegator address, then click **Query** to fetch the value live from the blockchain.

<a id="delegator"></a>

### `delegator`

`L2veCRVDelegation.delegator(_chain_id: uint256, _to: address) -> address: view`

Getter for the address that delegated their veCRV balance to a given address on a specific chain. If no delegator is set, returns the `_to` address itself.

Returns: delegator (`address`).

| Input | Type | Description |
| --- | --- | --- |
| `_chain_id` | `uint256` | Chain ID to check for |
| `_to` | `address` | Delegatee |

**<>Source code▼**

L2veCRVDelegation.vy

```vyper
delegation_to: HashMap[uint256, HashMap[address, address]]

@external
@view
def delegator(_chain_id: uint256, _to: address) -> address:
    """
    @notice Get contract delegating balance to `_to`
    @param _chain_id Chain ID to check for
    @param _to Address of delegated to
    @return Address of delegator
    """
    addr: address = self.delegation_to[_chain_id][_to]
    if addr in [empty(address), self]:
        return _to
    return addr
```

**▶Example▼**

This example returns the address that delegated their veCRV balance to a given address on a specific chain. Enter a chain ID and delegatee address, then click **Query** to fetch the value live from the blockchain.

* * *

<a id="contract-ownership"></a>

## Contract Ownership

Ownership of the contract is managed using the [`ownable.vy`](https://github.com/pcaversaccio/snekmate/blob/main/src/snekmate/auth/ownable.vy) module from 🐍 [Snekmate](https://github.com/pcaversaccio/snekmate) which implements a basic access control mechanism, where there is an `owner` that can be granted exclusive access to specific functions.

<a id="owner"></a>

### `owner`

`L2veCRVDelegation.owner() -> address: view`

Getter for the owner of the contract. This is the address that can call restricted functions like `transfer_ownership` and `delegate_from`.

Returns: contract owner (`address`).

**<>Source code▼**

```vyper
from snekmate.auth import ownable
initializes: ownable
exports: (
    ownable.transfer_ownership,
    ownable.owner,
)

@deploy
def __init__(_owner: address):
    """
    @notice Contract constructor
    @param _owner Owner address
    """
    ownable.__init__()
    ownable._transfer_ownership(_owner)
```

```vyper
owner: public(address)

@deploy
@payable
def __init__():
    """
    @dev To omit the opcodes for checking the `msg.value`
        in the creation-time EVM bytecode, the constructor
        is declared as `payable`.
    @notice The `owner` role will be assigned to
            the `msg.sender`.
    """
    self._transfer_ownership(msg.sender)
```

**▶Example▼**

<a id="transfer_ownership"></a>

### `transfer_ownership`

`L2veCRVDelegation.transfer_ownership(new_owner: address)`

Guarded Method by [Snekmate 🐍](https://github.com/pcaversaccio/snekmate)

This contract makes use of a Snekmate module to manage roles and permissions. This specific function can only be called by the current `owner` of the contract.

Function to transfer the ownership of the contract to a new address.

Emits: `OwnershipTransferred`

| Input | Type | Description |
| --- | --- | --- |
| `new_owner` | `address` | New owner of the contract |

**<>Source code▼**

```vyper
from snekmate.auth import ownable
initializes: ownable
exports: (
    ownable.transfer_ownership,
    ownable.owner,
)

@deploy
def __init__(_owner: address):
    """
    @notice Contract constructor
    @param _owner Owner address
    """
    ownable.__init__()
    ownable._transfer_ownership(_owner)
```

```vyper
owner: public(address)

event OwnershipTransferred:
    previous_owner: indexed(address)
    new_owner: indexed(address)

@external
def transfer_ownership(new_owner: address):
    """
    @dev Transfers the ownership of the contract
        to a new account `new_owner`.
    @notice Note that this function can only be
            called by the current `owner`. Also,
            the `new_owner` cannot be the zero address.
    @param new_owner The 20-byte address of the new owner.
    """
    self._check_owner()
    assert new_owner != empty(address), "ownable: new owner is the zero address"
    self._transfer_ownership(new_owner)

@internal
def _check_owner():
    """
    @dev Throws if the sender is not the owner.
    """
    assert msg.sender == self.owner, "ownable: caller is not the owner"

@internal
def _transfer_ownership(new_owner: address):
    """
    @dev Transfers the ownership of the contract
        to a new account `new_owner`.
    @notice This is an `internal` function without
            access restriction.
    @param new_owner The 20-byte address of the new owner.
    """
    old_owner: address = self.owner
    self.owner = new_owner
    log OwnershipTransferred(old_owner, new_owner)
```

**▶Example▼**

In this example, the ownership of the contract is transferred to a new address.

```shell
>>> L2veCRVDelegation.transfer_ownership("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")
```

* * *

<a id="other-methods"></a>

## Other Methods

<a id="version"></a>

### `version`

`L2veCRVDelegation.version() -> String: view`

Getter for the contract version.

Returns: contract version (`String`).

**<>Source code▼**

L2veCRVDelegation.vy

```vyper
version: public(constant(String[8])) = "0.0.1"
```

**▶Example▼**
