Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: remove V1 migration logic and support checkpoints on balance_of… #20

Merged
merged 6 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions ape-config.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
name: curve-veboost

ethereum:
default_network: local
default_network: mainnet-fork
local:
default_provider: hardhat
default_provider: foundry

dependencies:
- name: curve-dao
Expand Down
61 changes: 20 additions & 41 deletions contracts/BoostV2.vy
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
# @version 0.3.3
# @version 0.3.10
"""
@title Boost Delegation V2
@title Boost Delegation V3
@author CurveFi
"""


event Approval:
_owner: indexed(address)
_spender: indexed(address)
Expand All @@ -22,15 +21,6 @@ event Boost:
_slope: uint256
_start: uint256

event Migrate:
_token_id: indexed(uint256)


interface BoostV1:
def ownerOf(_token_id: uint256) -> address: view
def token_boost(_token_id: uint256) -> int256: view
def token_expiry(_token_id: uint256) -> uint256: view

interface VotingEscrow:
def balanceOf(_user: address) -> uint256: view
def totalSupply() -> uint256: view
Expand All @@ -45,15 +35,14 @@ struct Point:

NAME: constant(String[32]) = "Vote-Escrowed Boost"
SYMBOL: constant(String[8]) = "veBoost"
VERSION: constant(String[8]) = "v2.0.0"
VERSION: constant(String[8]) = "v3.0.0"

EIP712_TYPEHASH: constant(bytes32) = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)")
PERMIT_TYPEHASH: constant(bytes32) = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")

WEEK: constant(uint256) = 86400 * 7


BOOST_V1: immutable(address)
DOMAIN_SEPARATOR: immutable(bytes32)
VE: immutable(address)

Expand All @@ -67,12 +56,8 @@ delegated_slope_changes: public(HashMap[address, HashMap[uint256, uint256]])
received: public(HashMap[address, Point])
received_slope_changes: public(HashMap[address, HashMap[uint256, uint256]])

migrated: public(HashMap[uint256, bool])


@external
def __init__(_boost_v1: address, _ve: address):
BOOST_V1 = _boost_v1
def __init__(_ve: address):
DOMAIN_SEPARATOR = keccak256(_abi_encode(EIP712_TYPEHASH, keccak256(NAME), keccak256(VERSION), chain.id, self, block.prevhash))
VE = _ve

Expand Down Expand Up @@ -229,21 +214,6 @@ def boost(_to: address, _amount: uint256, _endtime: uint256, _from: address = ms
self._boost(_from, _to, _amount, _endtime)


@external
def migrate(_token_id: uint256):
assert not self.migrated[_token_id]

self._boost(
convert(shift(_token_id, -96), address), # from
BoostV1(BOOST_V1).ownerOf(_token_id), # to
convert(BoostV1(BOOST_V1).token_boost(_token_id), uint256), # amount
BoostV1(BOOST_V1).token_expiry(_token_id), # expiry
)

self.migrated[_token_id] = True
log Migrate(_token_id)


@external
def checkpoint_user(_user: address):
self.delegated[_user] = self._checkpoint_write(_user, True)
Expand Down Expand Up @@ -311,6 +281,21 @@ def adjusted_balance_of(_user: address) -> uint256:
return self._balance_of(_user)


@external
def adjusted_balance_of_write(_user: address) -> uint256:
amount: uint256 = VotingEscrow(VE).balanceOf(_user)

point: Point = self._checkpoint_write(_user, True)
self.delegated[_user] = point
amount -= (point.bias - point.slope * (block.timestamp - point.ts))

point = self._checkpoint_write(_user, False)
self.received[_user] = point
amount += (point.bias - point.slope * (block.timestamp - point.ts))

return amount


@view
@external
def totalSupply() -> uint256:
Expand Down Expand Up @@ -356,12 +341,6 @@ def decimals() -> uint8:
return 18


@pure
@external
def BOOST_V1() -> address:
return BOOST_V1


@pure
@external
def DOMAIN_SEPARATOR() -> bytes32:
Expand All @@ -371,4 +350,4 @@ def DOMAIN_SEPARATOR() -> bytes32:
@pure
@external
def VE() -> address:
return VE
return VE
5 changes: 2 additions & 3 deletions scripts/deploy.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from ape import accounts, project
from ape import accounts, project, Contract

primary = accounts.load("primary")


def main():
project.BoostV2.deploy(
"0xd30DD0B919cB4012b3AdD78f6Dcb6eb7ef225Ac8",
"0x5f3b5DfEb7B28CDbD7FAba78963EE202a494e2A2",
sender=primary,
)
)
4 changes: 2 additions & 2 deletions tests/fixtures/deployments.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ def veboost_v1(alice, project, ve):


@pytest.fixture(scope="session")
def veboost(alice, project, ve, veboost_v1):
yield project.BoostV2.deploy(veboost_v1, ve, sender=alice)
def veboost(alice, project, ve):
yield project.BoostV2.deploy(ve, sender=alice)
43 changes: 42 additions & 1 deletion tests/test_boost.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import ape
from ape import chain
import pytest

AMOUNT = 10**21

DAY = 86400
WEEK = DAY * 7
YEAR = DAY * 365

@pytest.mark.parametrize("idx", range(5))
def test_initial_state(accounts, veboost, ve, idx):
Expand Down Expand Up @@ -69,3 +72,41 @@ def test_boost_fails_with_invalid_amount_greater_than_delegable_balance(
def test_boost_fails_with_invalid_allowance(alice, bob, veboost, lock_unlock_time):
with ape.reverts():
veboost.boost(bob, AMOUNT, lock_unlock_time, alice, sender=bob)

def test_adjusted_balance_of_write(alice, bob, veboost, ve, lock_unlock_time):
balance = ve.balanceOf(alice)
amount = balance / 4

# Delegate to bob
veboost.boost(bob, int(amount), lock_unlock_time, sender=alice)

delegated_point = veboost.delegated(alice)
received_point = veboost.received(alice)

for i in range(10):
tx = veboost.adjusted_balance_of_write(alice, sender=bob)
delegable = veboost.delegable_balance(alice)
delegated = veboost.delegated_balance(alice)
ve_balance = ve.balanceOf(alice)

# Verify new point has been written (checkpoint)
new_point = veboost.delegated(alice)
assert new_point.ts > delegated_point.ts
delegated_point = new_point

new_point = veboost.received(alice)
assert new_point.ts > received_point.ts
received_point = new_point

# Invariant: VE balance is always the sum of delegable + delegated
assert ve_balance == delegable + delegated
assert veboost.balanceOf(alice) == tx.return_value == delegable

# Ensure bob's adjusted balance is equal to what Alice has delegated
tx = veboost.adjusted_balance_of_write(bob, sender=bob)
assert tx.return_value == veboost.balanceOf(bob)
assert veboost.delegated_balance(alice) == veboost.balanceOf(bob)
assert veboost.delegable_balance(bob) == 0 # Bob doesn't have any VE balance

chain.pending_timestamp += DAY * i
chain.mine()
39 changes: 0 additions & 39 deletions tests/test_migrate.py

This file was deleted.