# L2 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.

# L2 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.

:::vyper[`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 :logos-ethereum: Ethereum is deployed at [`0xde1e6A7E8297076f070E857130E593107A0E0cF5`](https://etherscan.io/address/0xde1e6A7E8297076f070E857130E593107A0E0cF5) and contract version is `0.0.1`.

<ContractABI>

```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"}]
```

</ContractABI>

:::

---

## 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.

### `delegate`
::::description[`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               |

<SourceCode>

```vyper title="L2veCRVDelegation.vy"
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)

```

</SourceCode>

<Example>

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

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

</Example>

::::

### `delegate_from`
::::description[`L2veCRVDelegation.delegate_from(_chain_id: uint256, _from: address, _to: address)`]

:::guard[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   |

<SourceCode>

<Tabs>
<TabItem value="L2veCRVDelegation.vy" label="L2veCRVDelegation.vy">

```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)
```

</TabItem>
<TabItem value="ownable.vy" label="ownable.vy (Snekmate 🐍)">

```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"
```

</TabItem>
</Tabs>

</SourceCode>

<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')
```

</Example>

::::

### `allow_delegation`
::::description[`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` |

<SourceCode>

```vyper title="L2veCRVDelegation.vy"
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)
```

</SourceCode>

<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
```

</Example>

::::

### `delegation_allowed`
::::description[`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 |

<SourceCode>

```vyper title="L2veCRVDelegation.vy"
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
```

</SourceCode>

<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.

<ContractCall
  address="0xde1e6A7E8297076f070E857130E593107A0E0cF5"
  abi={["function delegation_allowed(uint256, address) view returns (bool)"]}
  method="delegation_allowed"
  args={["146", "0x71F718D3e4d1449D1502A6A7595eb84eBcCB1683"]}
  labels={["_chain_id", "_to"]}
  contractName="L2veCRVDelegation"
/>

</Example>

::::

### `delegated`
::::description[`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   |

<SourceCode>

```vyper title="L2veCRVDelegation.vy"
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
```

</SourceCode>

<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.

<ContractCall
  address="0xde1e6A7E8297076f070E857130E593107A0E0cF5"
  abi={["function delegated(uint256, address) view returns (address)"]}
  method="delegated"
  args={["146", "0x5802ad5D5B1c63b3FC7DE97B55e6db19e5d36462"]}
  labels={["_chain_id", "_from"]}
  contractName="L2veCRVDelegation"
/>

</Example>

::::

### `delegator`
::::description[`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                            |

<SourceCode>

```vyper title="L2veCRVDelegation.vy"
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
```

</SourceCode>

<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.

<ContractCall
  address="0xde1e6A7E8297076f070E857130E593107A0E0cF5"
  abi={["function delegator(uint256, address) view returns (address)"]}
  method="delegator"
  args={["146", "0x71F718D3e4d1449D1502A6A7595eb84eBcCB1683"]}
  labels={["_chain_id", "_to"]}
  contractName="L2veCRVDelegation"
/>

</Example>

::::

---

## 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.

### `owner`
::::description[`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`).

<SourceCode>

<Tabs>
<TabItem value="L2veCRVDelegation.vy" label="L2veCRVDelegation.vy">

```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)
```

</TabItem>
<TabItem value="ownable.vy" label="ownable.vy (Snekmate 🐍)">

```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)
```

</TabItem>
</Tabs>

</SourceCode>

<Example>

<ContractCall
  address="0xde1e6A7E8297076f070E857130E593107A0E0cF5"
  abi={["function owner() view returns (address)"]}
  method="owner"
  contractName="L2veCRVDelegation"
/>

</Example>

::::

### `transfer_ownership`
::::description[`L2veCRVDelegation.transfer_ownership(new_owner: address)`]

:::guard[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  |

<SourceCode>

<Tabs>
<TabItem value="L2veCRVDelegation.vy" label="L2veCRVDelegation.vy">

```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)
```

</TabItem>
<TabItem value="ownable.vy" label="ownable.vy (Snekmate 🐍)">

```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)
```

</TabItem>
</Tabs>

</SourceCode>

<Example>

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

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

</Example>

::::

---

## Other Methods

### `version`
::::description[`L2veCRVDelegation.version() -> String: view`]

Getter for the contract version.

Returns: contract version (`String`).

<SourceCode>

```vyper title="L2veCRVDelegation.vy"
version: public(constant(String[8])) = "0.0.1"
```

</SourceCode>

<Example>

<ContractCall
  address="0xde1e6A7E8297076f070E857130E593107A0E0cF5"
  abi={["function version() view returns (string)"]}
  method="version"
  contractName="L2veCRVDelegation"
/>

</Example>

::::
