From 5952710146d96f92b3b8ade3e83204b927ebd531 Mon Sep 17 00:00:00 2001 From: ethzoomer Date: Sat, 5 Aug 2023 02:00:53 -0500 Subject: [PATCH 1/9] add RelaySugar --- contracts/RelaySugar.vy | 51 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 contracts/RelaySugar.vy diff --git a/contracts/RelaySugar.vy b/contracts/RelaySugar.vy new file mode 100644 index 0000000..483bce5 --- /dev/null +++ b/contracts/RelaySugar.vy @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: BUSL-1.1 +# @version >=0.3.6 <0.4.0 + +# @title Velodrome Finance Relay Sugar v2 +# @author stas, ZoomerAnon +# @notice Makes it nicer to work with autocompounders. + +MAX_COMPOUNDERS: constant(uint256) = 50 + +struct AutoCompounder: + name: String[100] + tokenId: uint256 + address: address + +interface IAutoCompounderFactory: + def autoCompounders() -> DynArray[address, MAX_COMPOUNDERS]: view + +interface IAutoCompounder: + def name() -> String[100]: view + def tokenId() -> uint256: view + +# Vars +factory: public(IAutoCompounderFactory) + +@external +def __init__(_factory: address): + """ + @dev Set up our external factory contract + """ + self.factory = IAutoCompounderFactory(_factory) + +@external +@view +def all() -> DynArray[AutoCompounder, MAX_COMPOUNDERS]: + """ + @notice Returns all AutoCompounders + @return Array of AutoCompounder structs + """ + compounders: DynArray[AutoCompounder, MAX_COMPOUNDERS] = empty(DynArray[AutoCompounder, MAX_COMPOUNDERS]) + addresses: DynArray[address, MAX_COMPOUNDERS] = self.factory.autoCompounders() + + for index in range(0, len(addresses)): + autocompounder: IAutoCompounder = IAutoCompounder(addresses[index]) + + compounders.append(AutoCompounder({ + name: autocompounder.name(), + tokenId: autocompounder.tokenId(), + address: addresses[index] + })) + + return compounders From 78e1562b9f23c108e41f256688ed00664cd7b281 Mon Sep 17 00:00:00 2001 From: ethzoomer Date: Wed, 9 Aug 2023 01:11:39 -0400 Subject: [PATCH 2/9] Account deposits() function, add inactive field --- contracts/RelaySugar.vy | 79 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 8 deletions(-) diff --git a/contracts/RelaySugar.vy b/contracts/RelaySugar.vy index 483bce5..0dee8de 100644 --- a/contracts/RelaySugar.vy +++ b/contracts/RelaySugar.vy @@ -6,11 +6,44 @@ # @notice Makes it nicer to work with autocompounders. MAX_COMPOUNDERS: constant(uint256) = 50 +# Inherited from veSugar +MAX_RESULTS: constant(uint256) = 1000 +MAX_PAIRS: constant(uint256) = 30 -struct AutoCompounder: +struct LpVotes: + lp: address + weight: uint256 + +struct VeNFT: + id: uint256 + account: address + decimals: uint8 + amount: uint128 + voting_amount: uint256 + rebase_amount: uint256 + expires_at: uint256 + voted_at: uint256 + votes: DynArray[LpVotes, MAX_PAIRS] + token: address + permanent: bool + +struct Deposit: + deposited_nft: VeNFT + manager_id: uint256 + +struct Relay: name: String[100] - tokenId: uint256 + managed_nft: VeNFT address: address + inactive: bool + +interface IVeSugar: + def byId(_id: uint256) -> VeNFT: view + def byAccount(_account: address) -> DynArray[VeNFT, MAX_RESULTS]: view + +interface IVotingEscrow: + def idToManaged(_venft_id: uint256) -> uint256: view + def deactivated(_venft_id: uint256) -> bool: view interface IAutoCompounderFactory: def autoCompounders() -> DynArray[address, MAX_COMPOUNDERS]: view @@ -21,31 +54,61 @@ interface IAutoCompounder: # Vars factory: public(IAutoCompounderFactory) +ve_sugar: public(IVeSugar) +ve: public(IVotingEscrow) @external -def __init__(_factory: address): +def __init__(_factory: address, _ve_sugar: address, _ve: address): """ @dev Set up our external factory contract """ self.factory = IAutoCompounderFactory(_factory) + self.ve_sugar = IVeSugar(_ve_sugar) + self.ve = IVotingEscrow(_ve) @external @view -def all() -> DynArray[AutoCompounder, MAX_COMPOUNDERS]: +def all() -> DynArray[Relay, MAX_COMPOUNDERS]: """ @notice Returns all AutoCompounders @return Array of AutoCompounder structs """ - compounders: DynArray[AutoCompounder, MAX_COMPOUNDERS] = empty(DynArray[AutoCompounder, MAX_COMPOUNDERS]) + compounders: DynArray[Relay, MAX_COMPOUNDERS] = empty(DynArray[Relay, MAX_COMPOUNDERS]) addresses: DynArray[address, MAX_COMPOUNDERS] = self.factory.autoCompounders() for index in range(0, len(addresses)): autocompounder: IAutoCompounder = IAutoCompounder(addresses[index]) + managed_id: uint256 = autocompounder.tokenId() + managed_nft: VeNFT = self.ve_sugar.byId(managed_id) + inactive: bool = self.ve.deactivated(managed_id) - compounders.append(AutoCompounder({ + compounders.append(Relay({ name: autocompounder.name(), - tokenId: autocompounder.tokenId(), - address: addresses[index] + managed_nft: managed_nft, + address: addresses[index], + inactive: inactive })) return compounders + +@external +@view +def deposits(_account: address) -> DynArray[Deposit, MAX_RESULTS]: + """ + @notice Returns all of an account's Relay Deposits + @param _account The account address + @return Array of Deposits + """ + deposits: DynArray[Deposit, MAX_RESULTS] = empty(DynArray[Deposit, MAX_RESULTS]) + nfts: DynArray[VeNFT, MAX_RESULTS] = self.ve_sugar.byAccount(_account) + + for index in range(0, len(nfts)): + manager_id: uint256 = self.ve.idToManaged(nfts[index].id) + + if manager_id != 0: + deposits.append(Deposit({ + deposited_nft: nfts[index], + manager_id: manager_id + })) + + return deposits From 7b2b6caea8e4b0f5f2d2d309fa42839b860a8260 Mon Sep 17 00:00:00 2001 From: Zoomer <87513793+ethzoomer@users.noreply.github.com> Date: Wed, 16 Aug 2023 21:23:40 -0400 Subject: [PATCH 3/9] Relay struct flattened (#20) * Relay struct flattened * Add account veNFT ids to Relay struct * Remove VeSugar dependency * Remove Deposit struct and function * Vyper range bound check * byId() -> byAddress() --- contracts/RelaySugar.vy | 162 ++++++++++++++++++++++++++-------------- 1 file changed, 108 insertions(+), 54 deletions(-) diff --git a/contracts/RelaySugar.vy b/contracts/RelaySugar.vy index 0dee8de..5da58ec 100644 --- a/contracts/RelaySugar.vy +++ b/contracts/RelaySugar.vy @@ -6,7 +6,6 @@ # @notice Makes it nicer to work with autocompounders. MAX_COMPOUNDERS: constant(uint256) = 50 -# Inherited from veSugar MAX_RESULTS: constant(uint256) = 1000 MAX_PAIRS: constant(uint256) = 30 @@ -14,36 +13,37 @@ struct LpVotes: lp: address weight: uint256 -struct VeNFT: - id: uint256 - account: address +struct Relay: + venft_id: uint256 decimals: uint8 amount: uint128 voting_amount: uint256 - rebase_amount: uint256 - expires_at: uint256 voted_at: uint256 votes: DynArray[LpVotes, MAX_PAIRS] token: address - permanent: bool - -struct Deposit: - deposited_nft: VeNFT - manager_id: uint256 - -struct Relay: - name: String[100] - managed_nft: VeNFT - address: address + manager: address + compounder: address inactive: bool + name: String[100] + account_venft_ids: DynArray[uint256, MAX_RESULTS] -interface IVeSugar: - def byId(_id: uint256) -> VeNFT: view - def byAccount(_account: address) -> DynArray[VeNFT, MAX_RESULTS]: view +interface IVoter: + def ve() -> address: view + def lastVoted(_venft_id: uint256) -> uint256: view + def poolVote(_venft_id: uint256, _index: uint256) -> address: view + def votes(_venft_id: uint256, _lp: address) -> uint256: view + def usedWeights(_venft_id: uint256) -> uint256: view interface IVotingEscrow: def idToManaged(_venft_id: uint256) -> uint256: view def deactivated(_venft_id: uint256) -> bool: view + def token() -> address: view + def decimals() -> uint8: view + def ownerOf(_venft_id: uint256) -> address: view + def balanceOfNFT(_venft_id: uint256) -> uint256: view + def locked(_venft_id: uint256) -> (uint128, uint256, bool): view + def ownerToNFTokenIdList(_account: address, _index: uint256) -> uint256: view + def voted(_venft_id: uint256) -> bool: view interface IAutoCompounderFactory: def autoCompounders() -> DynArray[address, MAX_COMPOUNDERS]: view @@ -54,61 +54,115 @@ interface IAutoCompounder: # Vars factory: public(IAutoCompounderFactory) -ve_sugar: public(IVeSugar) +voter: public(IVoter) ve: public(IVotingEscrow) +token: public(address) @external -def __init__(_factory: address, _ve_sugar: address, _ve: address): +def __init__(_factory: address, _voter: address): """ @dev Set up our external factory contract """ self.factory = IAutoCompounderFactory(_factory) - self.ve_sugar = IVeSugar(_ve_sugar) - self.ve = IVotingEscrow(_ve) + self.voter = IVoter(_voter) + self.ve = IVotingEscrow(self.voter.ve()) + self.token = self.ve.token() @external @view -def all() -> DynArray[Relay, MAX_COMPOUNDERS]: +def all(_account: address) -> DynArray[Relay, MAX_COMPOUNDERS]: """ - @notice Returns all AutoCompounders - @return Array of AutoCompounder structs + @notice Returns all Relays and account's deposits + @return Array of Relay structs + """ + return self._autocompounders(_account) + +@internal +@view +def _autocompounders(_account: address) -> DynArray[Relay, MAX_COMPOUNDERS]: + """ + @notice Returns all Relays and account's deposits + @return Array of Relay structs """ compounders: DynArray[Relay, MAX_COMPOUNDERS] = empty(DynArray[Relay, MAX_COMPOUNDERS]) addresses: DynArray[address, MAX_COMPOUNDERS] = self.factory.autoCompounders() - - for index in range(0, len(addresses)): - autocompounder: IAutoCompounder = IAutoCompounder(addresses[index]) - managed_id: uint256 = autocompounder.tokenId() - managed_nft: VeNFT = self.ve_sugar.byId(managed_id) - inactive: bool = self.ve.deactivated(managed_id) - - compounders.append(Relay({ - name: autocompounder.name(), - managed_nft: managed_nft, - address: addresses[index], - inactive: inactive - })) + + for index in range(0, MAX_COMPOUNDERS): + if index == len(addresses): + break + + relay: Relay = self._byAddress(addresses[index], _account) + compounders.append(relay) return compounders -@external +@internal @view -def deposits(_account: address) -> DynArray[Deposit, MAX_RESULTS]: +def _byAddress(_compounder: address, _account: address) -> Relay: """ - @notice Returns all of an account's Relay Deposits - @param _account The account address - @return Array of Deposits + @notice Returns Relay data based on address, with optional account arg + @param _id The Relay address to lookup + @return Relay struct """ - deposits: DynArray[Deposit, MAX_RESULTS] = empty(DynArray[Deposit, MAX_RESULTS]) - nfts: DynArray[VeNFT, MAX_RESULTS] = self.ve_sugar.byAccount(_account) + + autocompounder: IAutoCompounder = IAutoCompounder(_compounder) + managed_id: uint256 = autocompounder.tokenId() + + account_venft_ids: DynArray[uint256, MAX_RESULTS] = empty(DynArray[uint256, MAX_RESULTS]) + + for venft_index in range(MAX_RESULTS): + account_venft_id: uint256 = self.ve.ownerToNFTokenIdList(_account, venft_index) + + if account_venft_id == 0: + break + + 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) + + votes: DynArray[LpVotes, MAX_PAIRS] = [] + amount: uint128 = self.ve.locked(managed_id)[0] + last_voted: uint256 = 0 + manager: address = self.ve.ownerOf(managed_id) + inactive: bool = self.ve.deactivated(managed_id) - for index in range(0, len(nfts)): - manager_id: uint256 = self.ve.idToManaged(nfts[index].id) + if self.ve.voted(managed_id): + last_voted = self.voter.lastVoted(managed_id) - if manager_id != 0: - deposits.append(Deposit({ - deposited_nft: nfts[index], - manager_id: manager_id - })) + vote_weight: uint256 = self.voter.usedWeights(managed_id) + # Since we don't have a way to see how many pools the veNFT voted... + left_weight: uint256 = vote_weight + + for index in range(MAX_PAIRS): + if left_weight == 0: + break + + lp: address = self.voter.poolVote(managed_id, index) + + if lp == empty(address): + break + + weight: uint256 = self.voter.votes(managed_id, lp) + + votes.append(LpVotes({ + lp: lp, + weight: weight + })) - return deposits + # Remove _counted_ weight to see if there are other pool votes left... + left_weight -= weight + + return Relay({ + venft_id: managed_id, + decimals: self.ve.decimals(), + amount: amount, + voting_amount: self.ve.balanceOfNFT(managed_id), + voted_at: last_voted, + votes: votes, + token: self.token, + manager: manager, + compounder: _compounder, + inactive: inactive, + name: autocompounder.name(), + account_venft_ids: account_venft_ids + }) From bc39b7335bb34955b52ac32a611ca980e177af21 Mon Sep 17 00:00:00 2001 From: Zoomer <87513793+ethzoomer@users.noreply.github.com> Date: Wed, 6 Sep 2023 12:08:01 -0500 Subject: [PATCH 4/9] feat: use RelayFactory, add latest epoch compounded rewards (#22) * feat: use RelayFactory, add latest epoch compounded rewards * refactor: rename rewards_compounded --- contracts/RelaySugar.vy | 55 ++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/contracts/RelaySugar.vy b/contracts/RelaySugar.vy index 5da58ec..d83a4e2 100644 --- a/contracts/RelaySugar.vy +++ b/contracts/RelaySugar.vy @@ -3,11 +3,12 @@ # @title Velodrome Finance Relay Sugar v2 # @author stas, ZoomerAnon -# @notice Makes it nicer to work with autocompounders. +# @notice Makes it nicer to work with Relay. -MAX_COMPOUNDERS: constant(uint256) = 50 +MAX_RELAYS: constant(uint256) = 50 MAX_RESULTS: constant(uint256) = 1000 MAX_PAIRS: constant(uint256) = 30 +WEEK: constant(uint256) = 7 * 24 * 60 * 60 struct LpVotes: lp: address @@ -21,8 +22,9 @@ struct Relay: voted_at: uint256 votes: DynArray[LpVotes, MAX_PAIRS] token: address + compounded: uint256 manager: address - compounder: address + relay: address inactive: bool name: String[100] account_venft_ids: DynArray[uint256, MAX_RESULTS] @@ -45,15 +47,18 @@ interface IVotingEscrow: def ownerToNFTokenIdList(_account: address, _index: uint256) -> uint256: view def voted(_venft_id: uint256) -> bool: view -interface IAutoCompounderFactory: - def autoCompounders() -> DynArray[address, MAX_COMPOUNDERS]: view +interface IRelayFactory: + def relays() -> DynArray[address, MAX_RELAYS]: view -interface IAutoCompounder: +interface IRelay: def name() -> String[100]: view def tokenId() -> uint256: view + def token() -> address: view + # Latest epoch rewards + def amountTokenEarned(_epoch_ts: uint256) -> uint256: view # Vars -factory: public(IAutoCompounderFactory) +factory: public(IRelayFactory) voter: public(IVoter) ve: public(IVotingEscrow) token: public(address) @@ -63,50 +68,50 @@ def __init__(_factory: address, _voter: address): """ @dev Set up our external factory contract """ - self.factory = IAutoCompounderFactory(_factory) + self.factory = IRelayFactory(_factory) self.voter = IVoter(_voter) self.ve = IVotingEscrow(self.voter.ve()) self.token = self.ve.token() @external @view -def all(_account: address) -> DynArray[Relay, MAX_COMPOUNDERS]: +def all(_account: address) -> DynArray[Relay, MAX_RELAYS]: """ @notice Returns all Relays and account's deposits @return Array of Relay structs """ - return self._autocompounders(_account) + return self._relays(_account) @internal @view -def _autocompounders(_account: address) -> DynArray[Relay, MAX_COMPOUNDERS]: +def _relays(_account: address) -> DynArray[Relay, MAX_RELAYS]: """ @notice Returns all Relays and account's deposits @return Array of Relay structs """ - compounders: DynArray[Relay, MAX_COMPOUNDERS] = empty(DynArray[Relay, MAX_COMPOUNDERS]) - addresses: DynArray[address, MAX_COMPOUNDERS] = self.factory.autoCompounders() + relays: DynArray[Relay, MAX_RELAYS] = empty(DynArray[Relay, MAX_RELAYS]) + addresses: DynArray[address, MAX_RELAYS] = self.factory.relays() - for index in range(0, MAX_COMPOUNDERS): + for index in range(0, MAX_RELAYS): if index == len(addresses): break relay: Relay = self._byAddress(addresses[index], _account) - compounders.append(relay) + relays.append(relay) - return compounders + return relays @internal @view -def _byAddress(_compounder: address, _account: address) -> Relay: +def _byAddress(_relay: address, _account: address) -> Relay: """ @notice Returns Relay data based on address, with optional account arg @param _id The Relay address to lookup @return Relay struct """ - autocompounder: IAutoCompounder = IAutoCompounder(_compounder) - managed_id: uint256 = autocompounder.tokenId() + relay: IRelay = IRelay(_relay) + managed_id: uint256 = relay.tokenId() account_venft_ids: DynArray[uint256, MAX_RESULTS] = empty(DynArray[uint256, MAX_RESULTS]) @@ -126,6 +131,11 @@ def _byAddress(_compounder: address, _account: address) -> Relay: manager: address = self.ve.ownerOf(managed_id) inactive: bool = self.ve.deactivated(managed_id) + epoch_start_ts: uint256 = block.timestamp / WEEK * WEEK + + # Rewards claimed this epoch + rewards_compounded: uint256 = relay.amountTokenEarned(epoch_start_ts) + if self.ve.voted(managed_id): last_voted = self.voter.lastVoted(managed_id) @@ -159,10 +169,11 @@ def _byAddress(_compounder: address, _account: address) -> Relay: voting_amount: self.ve.balanceOfNFT(managed_id), voted_at: last_voted, votes: votes, - token: self.token, + token: relay.token(), + compounded: rewards_compounded, manager: manager, - compounder: _compounder, + relay: _relay, inactive: inactive, - name: autocompounder.name(), + name: relay.name(), account_venft_ids: account_venft_ids }) From e6c7e41f938aa3c55c6308bcbc50d900f75732c5 Mon Sep 17 00:00:00 2001 From: Zoomer <87513793+ethzoomer@users.noreply.github.com> Date: Wed, 13 Sep 2023 04:39:09 -0500 Subject: [PATCH 5/9] refactor: fetch Relays from Registry (#24) --- contracts/RelaySugar.vy | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/contracts/RelaySugar.vy b/contracts/RelaySugar.vy index d83a4e2..0c58a48 100644 --- a/contracts/RelaySugar.vy +++ b/contracts/RelaySugar.vy @@ -47,28 +47,28 @@ interface IVotingEscrow: def ownerToNFTokenIdList(_account: address, _index: uint256) -> uint256: view def voted(_venft_id: uint256) -> bool: view -interface IRelayFactory: - def relays() -> DynArray[address, MAX_RELAYS]: view +interface IRelayRegistry: + def getAll() -> DynArray[address, MAX_RELAYS]: view interface IRelay: def name() -> String[100]: view - def tokenId() -> uint256: view + def mTokenId() -> uint256: view def token() -> address: view # Latest epoch rewards def amountTokenEarned(_epoch_ts: uint256) -> uint256: view # Vars -factory: public(IRelayFactory) +registry: public(IRelayRegistry) voter: public(IVoter) ve: public(IVotingEscrow) token: public(address) @external -def __init__(_factory: address, _voter: address): +def __init__(_registry: address, _voter: address): """ - @dev Set up our external factory contract + @dev Set up our external registry and voter contracts """ - self.factory = IRelayFactory(_factory) + self.registry = IRelayRegistry(_registry) self.voter = IVoter(_voter) self.ve = IVotingEscrow(self.voter.ve()) self.token = self.ve.token() @@ -90,7 +90,7 @@ def _relays(_account: address) -> DynArray[Relay, MAX_RELAYS]: @return Array of Relay structs """ relays: DynArray[Relay, MAX_RELAYS] = empty(DynArray[Relay, MAX_RELAYS]) - addresses: DynArray[address, MAX_RELAYS] = self.factory.relays() + addresses: DynArray[address, MAX_RELAYS] = self.registry.getAll() for index in range(0, MAX_RELAYS): if index == len(addresses): @@ -106,12 +106,13 @@ def _relays(_account: address) -> DynArray[Relay, MAX_RELAYS]: def _byAddress(_relay: address, _account: address) -> Relay: """ @notice Returns Relay data based on address, with optional account arg - @param _id The Relay address to lookup + @param _relay The Relay address to lookup + @param _account The account address to lookup deposits @return Relay struct """ relay: IRelay = IRelay(_relay) - managed_id: uint256 = relay.tokenId() + managed_id: uint256 = relay.mTokenId() account_venft_ids: DynArray[uint256, MAX_RESULTS] = empty(DynArray[uint256, MAX_RESULTS]) From 43b45fd0f162f5b2e03fc506ab0eb66cfa609c7e Mon Sep 17 00:00:00 2001 From: Zoomer <87513793+ethzoomer@users.noreply.github.com> Date: Fri, 15 Sep 2023 10:52:06 -0500 Subject: [PATCH 6/9] refactor: fetch all Relays from Registry's factories (#26) --- contracts/RelaySugar.vy | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/contracts/RelaySugar.vy b/contracts/RelaySugar.vy index 0c58a48..506f8fa 100644 --- a/contracts/RelaySugar.vy +++ b/contracts/RelaySugar.vy @@ -50,6 +50,9 @@ interface IVotingEscrow: interface IRelayRegistry: def getAll() -> DynArray[address, MAX_RELAYS]: view +interface IRelayFactory: + def relays() -> DynArray[address, MAX_RELAYS]: view + interface IRelay: def name() -> String[100]: view def mTokenId() -> uint256: view @@ -90,14 +93,21 @@ def _relays(_account: address) -> DynArray[Relay, MAX_RELAYS]: @return Array of Relay structs """ relays: DynArray[Relay, MAX_RELAYS] = empty(DynArray[Relay, MAX_RELAYS]) - addresses: DynArray[address, MAX_RELAYS] = self.registry.getAll() + factories: DynArray[address, MAX_RELAYS] = self.registry.getAll() - 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 From 316c20d0232b9f3eb81de00fc767905d4912559e Mon Sep 17 00:00:00 2001 From: Zoomer <87513793+ethzoomer@users.noreply.github.com> Date: Mon, 25 Sep 2023 12:47:48 -0500 Subject: [PATCH 7/9] feat: add run_at to Relay struct output (#27) --- contracts/RelaySugar.vy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contracts/RelaySugar.vy b/contracts/RelaySugar.vy index 506f8fa..18267b6 100644 --- a/contracts/RelaySugar.vy +++ b/contracts/RelaySugar.vy @@ -23,6 +23,7 @@ struct Relay: votes: DynArray[LpVotes, MAX_PAIRS] token: address compounded: uint256 + run_at: uint256 manager: address relay: address inactive: bool @@ -57,6 +58,7 @@ interface IRelay: def name() -> String[100]: view def mTokenId() -> uint256: view def token() -> address: view + def keeperLastRun() -> uint256: view # Latest epoch rewards def amountTokenEarned(_epoch_ts: uint256) -> uint256: view @@ -182,6 +184,7 @@ def _byAddress(_relay: address, _account: address) -> Relay: votes: votes, token: relay.token(), compounded: rewards_compounded, + run_at: relay.keeperLastRun(), manager: manager, relay: _relay, inactive: inactive, From e5a04a5951cf182bc3d32959e74bee3e0059de87 Mon Sep 17 00:00:00 2001 From: ethzoomer Date: Mon, 25 Sep 2023 13:16:52 -0500 Subject: [PATCH 8/9] Add Relay to readme --- readme.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/readme.md b/readme.md index 0f85484..6f38f93 100644 --- a/readme.md +++ b/readme.md @@ -165,6 +165,35 @@ The available methods are: * `byId(_id: uint256) -> VeNFT` - returns the `VeNFT` struct for a specific NFT id. +### Relay Data + +`RelaySugar.vy` is deployed at `0x7f609cf1a99318652859aED5B00C7F5F187E0077` + +It allows fetching Relay autocompounder/autoconverter data. +The returned data/struct of type `Relay` values represent: + + * `venft_id` - token ID of the Relay veNFT + * `decimals` - Relay veNFT token decimals + * `amount` - Relay veNFT locked amount + * `voting_amount` - Relay veNFT voting power + * `voted_at` - Relay veNFT last vote timestamp + * `votes` - Relay veNFT list of pools with vote weights casted in the form of + `LpVotes` + * `token` - token address the Relay is compounding into + * `compounded` - amount of tokens compounded into in the recent epoch + * `run_at` - timestamp of last compounding + * `manager` - Relay manager + * `relay` - Relay address + * `inactive` - Relay active/inactive status + * `name` - Relay name + * `account_venft_ids` - token IDs of the account's deposits into this Relay + +--- + +The available methods are: + + * `all(_account: address) -> Relay[]` - returns a list of all `Relay` structs. + ## Development To setup the environment, build the Docker image first: From 09741cee0697d4c25fd5faf105cb4408e4b6665d Mon Sep 17 00:00:00 2001 From: ethzoomer Date: Mon, 25 Sep 2023 13:29:36 -0500 Subject: [PATCH 9/9] Add Relay tests --- env.example | 2 ++ tests/test_relay_sugar.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 tests/test_relay_sugar.py diff --git a/env.example b/env.example index d9187d7..50399f7 100644 --- a/env.example +++ b/env.example @@ -5,3 +5,5 @@ DIST_ADDRESS=0x9D4736EC60715e71aFe72973f7885DCBC21EA99b CONVERTOR_ADDRESS=0x585Af0b397AC42dbeF7f18395426BF878634f18D LP_SUGAR_ADDRESS=0xD2B1D1B75a0f226722b3A174dAE54e6dD14af1a1 VE_SUGAR_ADDRESS=0x0eCc2593E3a6A9be3628940Fa4D928CC257B588B +RELAY_SUGAR_ADDRESS=0x7f609cf1a99318652859aED5B00C7F5F187E0077 +RELAY_REGISTRY_ADDRESS=0xBC3dc970f891ffdd3049FA3a649985CC6626d486 diff --git a/tests/test_relay_sugar.py b/tests/test_relay_sugar.py new file mode 100644 index 0000000..b7479e0 --- /dev/null +++ b/tests/test_relay_sugar.py @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: BUSL-1.1 +import os +import pytest + +from collections import namedtuple +from web3.constants import ADDRESS_ZERO + + +@pytest.fixture +def sugar_contract(RelaySugar, accounts): + # Since we depend on the rest of the protocol, + # we just point to an existing deployment + yield RelaySugar.at(os.getenv('RELAY_SUGAR_ADDRESS')) + + +@pytest.fixture +def RelayStruct(sugar_contract): + method_output = sugar_contract.all.abi['outputs'][0] + members = list(map(lambda _e: _e['name'], method_output['components'])) + + yield namedtuple('RelayStruct', members) + + +def test_initial_state(sugar_contract): + assert sugar_contract.voter() == os.getenv('VOTER_ADDRESS') + assert sugar_contract.registry() == \ + os.getenv('RELAY_REGISTRY_ADDRESS') + + +def test_all(sugar_contract, RelayStruct): + relays = list(map( + lambda _r: RelayStruct(*_r), + sugar_contract.all(ADDRESS_ZERO) + )) + + assert relays is not None