Skip to content

Commit

Permalink
Merge branch 'v2' into clamm
Browse files Browse the repository at this point in the history
  • Loading branch information
stas authored Jan 16, 2024
2 parents 448198a + bc690bb commit 153c858
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 53 deletions.
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ RUN npm install -g ganache
COPY . /app
WORKDIR /app

RUN pip install --no-cache-dir -r requirements.txt
RUN pip install "cython<3.0.0" && pip install --no-build-isolation pyyaml==5.4.1
RUN pip install -r requirements.txt

RUN brownie networks modify optimism-test host=https://goerli.optimism.io
RUN brownie networks modify optimism-main host=https://optimism-mainnet.wallet.coinbase.com
62 changes: 44 additions & 18 deletions contracts/RelaySugar.vy
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,24 @@
MAX_RELAYS: constant(uint256) = 50
MAX_RESULTS: constant(uint256) = 1000
MAX_PAIRS: constant(uint256) = 30
MAX_REGISTRIES: constant(uint256) = 20
WEEK: constant(uint256) = 7 * 24 * 60 * 60

struct LpVotes:
lp: address
weight: uint256

struct ManagedVenft:
id: uint256
amount: uint256
earned: uint256

struct Relay:
venft_id: uint256
decimals: uint8
amount: uint128
voting_amount: uint256
used_voting_amount: uint256
voted_at: uint256
votes: DynArray[LpVotes, MAX_PAIRS]
token: address
Expand All @@ -29,7 +36,7 @@ struct Relay:
relay: address
inactive: bool
name: String[100]
account_venft_ids: DynArray[uint256, MAX_RESULTS]
account_venfts: DynArray[ManagedVenft, MAX_RESULTS]


interface IERC20:
Expand All @@ -52,6 +59,11 @@ interface IVotingEscrow:
def locked(_venft_id: uint256) -> (uint128, uint256, bool): view
def ownerToNFTokenIdList(_account: address, _index: uint256) -> uint256: view
def voted(_venft_id: uint256) -> bool: view
def managedToLocked(_managed_venft_id: uint256) -> address: view
def weights(_venft_id: uint256, _managed_venft_id: uint256) -> uint256: view

interface IReward:
def earned(_token: address, _venft_id: uint256) -> uint256: view

interface IRelayRegistry:
def getAll() -> DynArray[address, MAX_RELAYS]: view
Expand All @@ -70,17 +82,17 @@ interface IRelay:
def getRoleMember(_role: bytes32, _index: uint256) -> address: view

# Vars
registry: public(IRelayRegistry)
registries: public(DynArray[address, MAX_REGISTRIES])
voter: public(IVoter)
ve: public(IVotingEscrow)
token: public(address)

@external
def __init__(_registry: address, _voter: address):
def __init__(_registries: DynArray[address, MAX_REGISTRIES], _voter: address):
"""
@dev Set up our external registry and voter contracts
"""
self.registry = IRelayRegistry(_registry)
self.registries = _registries
self.voter = IVoter(_voter)
self.ve = IVotingEscrow(self.voter.ve())
self.token = self.ve.token()
Expand All @@ -102,21 +114,26 @@ def _relays(_account: address) -> DynArray[Relay, MAX_RELAYS]:
@return Array of Relay structs
"""
relays: DynArray[Relay, MAX_RELAYS] = empty(DynArray[Relay, MAX_RELAYS])
factories: DynArray[address, MAX_RELAYS] = self.registry.getAll()

for factory_index in range(0, MAX_RELAYS):
if factory_index == len(factories):
for registry_index in range(0, MAX_REGISTRIES):
if registry_index == len(self.registries):
break

relay_registry: IRelayRegistry = IRelayRegistry(self.registries[registry_index])
factories: DynArray[address, MAX_RELAYS] = relay_registry.getAll()

relay_factory: IRelayFactory = IRelayFactory(factories[factory_index])
addresses: DynArray[address, MAX_RELAYS] = relay_factory.relays()

for index in range(0, MAX_RELAYS):
if index == len(addresses):
for factory_index in range(0, MAX_RELAYS):
if factory_index == len(factories):
break

relay: Relay = self._byAddress(addresses[index], _account)
relays.append(relay)
relay_factory: IRelayFactory = IRelayFactory(factories[factory_index])
addresses: DynArray[address, MAX_RELAYS] = relay_factory.relays()

for index in range(0, MAX_RELAYS):
if index == len(addresses):
break

relay: Relay = self._byAddress(addresses[index], _account)
relays.append(relay)

return relays

Expand All @@ -133,7 +150,7 @@ def _byAddress(_relay: address, _account: address) -> Relay:
relay: IRelay = IRelay(_relay)
managed_id: uint256 = relay.mTokenId()

account_venft_ids: DynArray[uint256, MAX_RESULTS] = empty(DynArray[uint256, MAX_RESULTS])
account_venfts: DynArray[ManagedVenft, MAX_RESULTS] = empty(DynArray[ManagedVenft, MAX_RESULTS])

for venft_index in range(MAX_RESULTS):
account_venft_id: uint256 = self.ve.ownerToNFTokenIdList(_account, venft_index)
Expand All @@ -143,7 +160,15 @@ def _byAddress(_relay: address, _account: address) -> Relay:

account_venft_manager_id: uint256 = self.ve.idToManaged(account_venft_id)
if account_venft_manager_id == managed_id:
account_venft_ids.append(account_venft_id)
locked_reward: IReward = IReward(self.ve.managedToLocked(account_venft_manager_id))
venft_weight: uint256 = self.ve.weights(account_venft_id, account_venft_manager_id)
earned: uint256 = locked_reward.earned(self.token, account_venft_id)

account_venfts.append(ManagedVenft({
id: account_venft_id,
amount: venft_weight,
earned: earned
}))

votes: DynArray[LpVotes, MAX_PAIRS] = []
amount: uint128 = self.ve.locked(managed_id)[0]
Expand Down Expand Up @@ -195,6 +220,7 @@ def _byAddress(_relay: address, _account: address) -> Relay:
decimals: self.ve.decimals(),
amount: amount,
voting_amount: self.ve.balanceOfNFT(managed_id),
used_voting_amount: vote_weight,
voted_at: last_voted,
votes: votes,
token: relay.token(),
Expand All @@ -205,5 +231,5 @@ def _byAddress(_relay: address, _account: address) -> Relay:
relay: _relay,
inactive: inactive,
name: relay.name(),
account_venft_ids: account_venft_ids
account_venfts: account_venfts
})
22 changes: 19 additions & 3 deletions contracts/VeSugar.vy
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ struct VeNFT:
decimals: uint8
amount: uint128
voting_amount: uint256
governance_amount: uint256
rebase_amount: uint256
expires_at: uint256
voted_at: uint256
votes: DynArray[LpVotes, MAX_PAIRS]
token: address
permanent: bool
delegate_id: uint256

# Our contracts / Interfaces

Expand All @@ -51,25 +53,32 @@ interface IVotingEscrow:
def locked(_venft_id: uint256) -> (uint128, uint256, bool): view
def ownerToNFTokenIdList(_account: address, _index: uint256) -> uint256: view
def voted(_venft_id: uint256) -> bool: view
def delegates(_venft_id: uint256) -> uint256: view
def idToManaged(_venft_id: uint256) -> uint256: view

interface IGovernor:
def getVotes(_venft_id: uint256, _timepoint: uint256) -> uint256: view

# Vars

voter: public(IVoter)
token: public(address)
ve: public(IVotingEscrow)
dist: public(IRewardsDistributor)
gov: public(IGovernor)

# Methods

@external
def __init__(_voter: address, _rewards_distributor: address):
def __init__(_voter: address, _rewards_distributor: address, _gov: address):
"""
@dev Sets up our external contract addresses
"""
self.voter = IVoter(_voter)
self.ve = IVotingEscrow(self.voter.ve())
self.token = self.ve.token()
self.dist = IRewardsDistributor(_rewards_distributor)
self.gov = IGovernor(_gov)

@external
@view
Expand Down Expand Up @@ -146,7 +155,12 @@ def _byId(_id: uint256) -> VeNFT:
amount, expires_at, perma = self.ve.locked(_id)
last_voted: uint256 = 0

if self.ve.voted(_id):
governance_amount: uint256 = self.gov.getVotes(_id, block.timestamp)

delegate_id: uint256 = self.ve.delegates(_id)
managed_id: uint256 = self.ve.idToManaged(_id)

if managed_id != 0 or self.ve.voted(_id):
last_voted = self.voter.lastVoted(_id)

vote_weight: uint256 = self.voter.usedWeights(_id)
Expand Down Expand Up @@ -179,10 +193,12 @@ def _byId(_id: uint256) -> VeNFT:

amount: amount,
voting_amount: self.ve.balanceOfNFT(_id),
governance_amount: governance_amount,
rebase_amount: self.dist.claimable(_id),
expires_at: expires_at,
voted_at: last_voted,
votes: votes,
token: self.token,
permanent: perma
permanent: perma,
delegate_id: delegate_id
})
6 changes: 3 additions & 3 deletions env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ REGISTRY_ADDRESS=0xF4c67CdEAaB8360370F41514d06e32CcD8aA1d7B
DIST_ADDRESS=0x9D4736EC60715e71aFe72973f7885DCBC21EA99b
CONVERTOR_ADDRESS=0x585Af0b397AC42dbeF7f18395426BF878634f18D
LP_SUGAR_ADDRESS=0x6eDCAb198EAdDBDA3865f813A83F6bC9012F16e9
VE_SUGAR_ADDRESS=0x0eCc2593E3a6A9be3628940Fa4D928CC257B588B
RELAY_SUGAR_ADDRESS=0x7f609cf1a99318652859aED5B00C7F5F187E0077
RELAY_REGISTRY_ADDRESS=0xBC3dc970f891ffdd3049FA3a649985CC6626d486
VE_SUGAR_ADDRESS=0x37403dBd6f1b583ea244F7956fF9e37EF45c63eB
RELAY_SUGAR_ADDRESS=0x062185EEF2726EFc11880856CD356FA2Ac2B38Ff
RELAY_REGISTRY_ADDRESS=0xe9F00f2e61CB0c6fb00A2e457546aCbF0fC303C2
14 changes: 11 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ To fetch a list of rewards for a specific veNFT, this method is available:

### Vote-Escrow Locked NFT (veNFT) Data

`VeSugar.vy` is deployed at `0x0eCc2593E3a6A9be3628940Fa4D928CC257B588B`
`VeSugar.vy` is deployed at `0x37403dBd6f1b583ea244F7956fF9e37EF45c63eB`

It allows fetching on-chain veNFT data (including the rewards accrued).
The returned data/struct of type `VeNFT` values represent:
Expand All @@ -155,13 +155,15 @@ The returned data/struct of type `VeNFT` values represent:
* `decimals` - veNFT token decimals
* `amount` - veNFT locked amount
* `voting_amount` - veNFT voting power
* `governance_amount` - veNFT voting power in governance
* `rebase_amount` - veNFT accrued reabses amount
* `expires_at` - veNFT lock expiration timestamp
* `voted_at` - veNFT last vote timestamp
* `votes` - veNFT list of pools with vote weights casted in the form of
`LpVotes`
* `token` - veNFT locked token address
* `permanent` - veNFT permanent lock enabled flag
* `delegate_id` - token ID of the veNFT being delegated to

The pool votes struct values represent:
* `lp` - the pool address
Expand All @@ -180,7 +182,7 @@ The available methods are:

### Relay Data

`RelaySugar.vy` is deployed at `0xeBf8F5818D429785A584693599b695AFc3BeE3c6`
`RelaySugar.vy` is deployed at `0x062185EEF2726EFc11880856CD356FA2Ac2B38Ff`

It allows fetching Relay autocompounder/autoconverter data.
The returned data/struct of type `Relay` values represent:
Expand All @@ -189,6 +191,7 @@ The returned data/struct of type `Relay` values represent:
* `decimals` - Relay veNFT token decimals
* `amount` - Relay veNFT locked amount
* `voting_amount` - Relay veNFT voting power
* `used_voting_amount` - Relay veNFT voting power used for last vote
* `voted_at` - Relay veNFT last vote timestamp
* `votes` - Relay veNFT list of pools with vote weights casted in the form of
`LpVotes`
Expand All @@ -199,7 +202,12 @@ The returned data/struct of type `Relay` values represent:
* `relay` - Relay address
* `inactive` - Relay active/inactive status
* `name` - Relay name
* `account_venft_ids` - token IDs of the account's deposits into this Relay
* `account_venfts` - List of veNFTs deposited into this Relay by the account in the form of `ManagedVenft`

The managed veNFT deposit struct values represent:
* `id` - the token ID of the veNFT
* `amount` - the weight of the veNFT
* `earned` - earned emissions of the veNFT

---

Expand Down
7 changes: 4 additions & 3 deletions tests/test_relay_sugar.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ def RelayStruct(sugar_contract):

def test_initial_state(sugar_contract):
assert sugar_contract.voter() == os.getenv('VOTER_ADDRESS')
assert sugar_contract.registry() == \
os.getenv('RELAY_REGISTRY_ADDRESS')
assert sugar_contract.registries(0) == os.getenv('RELAY_REGISTRY_ADDRESS')
assert sugar_contract.ve() is not None
assert sugar_contract.token() is not None


def test_all(sugar_contract, RelayStruct):
Expand All @@ -33,4 +34,4 @@ def test_all(sugar_contract, RelayStruct):
sugar_contract.all(ADDRESS_ZERO)
))

assert relays is not None
assert len(relays) > 5
26 changes: 4 additions & 22 deletions tests/test_ve_sugar.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,6 @@ def sugar_contract(VeSugar, accounts):
yield VeSugar.at(os.getenv('VE_SUGAR_ADDRESS'))


@pytest.fixture
def RewardStruct(sugar_contract):
method_output = sugar_contract.rewards.abi['outputs'][0]
members = list(map(lambda _e: _e['name'], method_output['components']))

yield namedtuple('RewardStruct', members)


@pytest.fixture
def VeNFTStruct(sugar_contract):
method_output = sugar_contract.byId.abi['outputs'][0]
Expand All @@ -30,20 +22,19 @@ def VeNFTStruct(sugar_contract):

def test_initial_state(sugar_contract):
assert sugar_contract.voter() == os.getenv('VOTER_ADDRESS')
assert sugar_contract.rewards_distributor() == \
os.getenv('REWARDS_DIST_ADDRESS')
assert sugar_contract.dist() == \
os.getenv('DIST_ADDRESS')
assert sugar_contract.ve() is not None


def test_byId(sugar_contract, VeNFTStruct):
venft = VeNFTStruct(*sugar_contract.byId(1))

assert venft is not None
assert len(venft) == 11
assert len(venft) == 13
assert venft.id is not None
assert len(venft.votes) > 0
assert venft.voted_at > 0
assert venft.attachments > 0


def test_byAccount(sugar_contract, VeNFTStruct):
Expand All @@ -54,7 +45,7 @@ def test_byAccount(sugar_contract, VeNFTStruct):
))

assert venft is not None
assert len(venft) == 11
assert len(venft) == 13
assert venft.account == acc_venft[0].account


Expand Down Expand Up @@ -92,12 +83,3 @@ def test_all_limit_offset(sugar_contract, VeNFTStruct):

assert venft1.id == second_venft.id
assert venft1.account == second_venft.account


def test_rewards(sugar_contract, RewardStruct):
rewards = list(map(
lambda _r: RewardStruct(*_r),
sugar_contract.rewards(100, 0, 1)
))

assert len(rewards) > 0

0 comments on commit 153c858

Please sign in to comment.