From b1efaa9daf0598b6fd1a8200e550e7d6783c064a Mon Sep 17 00:00:00 2001 From: Divya-Solulab Date: Fri, 30 Aug 2024 15:22:15 +0530 Subject: [PATCH] fix: claim rewards --- packages/packages.json | 8 +- .../valory/agents/optimus/aea-config.yaml | 4 +- packages/valory/services/optimus/service.yaml | 2 +- .../liquidity_trader_abci/behaviours.py | 159 +++++++++--------- .../skills/liquidity_trader_abci/skill.yaml | 4 +- .../valory/skills/optimus_abci/skill.yaml | 2 +- 6 files changed, 87 insertions(+), 92 deletions(-) diff --git a/packages/packages.json b/packages/packages.json index e455449..ed2945e 100644 --- a/packages/packages.json +++ b/packages/packages.json @@ -6,10 +6,10 @@ "contract/valory/uniswap_v3_non_fungible_position_manager/0.1.0": "bafybeieljamerttxyo7z2yokwripnnhzkn4zply5lz457vsixf5wfu5px4", "contract/valory/uniswap_v3_pool/0.1.0": "bafybeidglijnyueahpgivaykbhio2r3ovfeo23a256y3yb6g7be4hngx3a", "contract/valory/merkl_distributor/0.1.0": "bafybeifctofnyhdic2sxmkqujvf3j2wwydhtvzhi6kdeutykenymplf4e4", - "skill/valory/liquidity_trader_abci/0.1.0": "bafybeihml6udc4leqoexdbqrjchxj4nlymqky5ejh6vlnqg7mlh64jp2zi", - "skill/valory/optimus_abci/0.1.0": "bafybeia3bmcjh2xyfqqlxkapegfkuy4fdezvsjhmu5ans33gdcexlr54ha", - "agent/valory/optimus/0.1.0": "bafybeihiq4tplmbmlodqnfyojrnvyzkdoz25c667v6gocpv3r5chohnlgm", - "service/valory/optimus/0.1.0": "bafybeic6ohhyrjk7yxtllxfhepd35boyqqqolevnxnftftv6by2qhnypcu" + "skill/valory/liquidity_trader_abci/0.1.0": "bafybeia6pmuwqvpb6bdk75uywqxgr3puarikzcfkxj5xvwbyu7lxqzmkgi", + "skill/valory/optimus_abci/0.1.0": "bafybeifaid4m5y6nr6zhqhoyonl34gjq4sloajvnwfxp6odybb5tzdepnm", + "agent/valory/optimus/0.1.0": "bafybeih32imiqyeh23pu5yjtjbxcbvmblpjwl6an7vxpisqri4yeuqkwby", + "service/valory/optimus/0.1.0": "bafybeid3sapqas3ow57jc5pdnngyzrmxutih4k5y56elgbez6jjxaqzfiu" }, "third_party": { "protocol/open_aea/signing/1.0.0": "bafybeihv62fim3wl2bayavfcg3u5e5cxu3b7brtu4cn5xoxd6lqwachasi", diff --git a/packages/valory/agents/optimus/aea-config.yaml b/packages/valory/agents/optimus/aea-config.yaml index dcb4195..d23fb5a 100644 --- a/packages/valory/agents/optimus/aea-config.yaml +++ b/packages/valory/agents/optimus/aea-config.yaml @@ -35,8 +35,8 @@ protocols: skills: - valory/abstract_abci:0.1.0:bafybeihu2bcgjk2tqjiq2zhk3uogtfszqn4osvdt7ho3fubdpdj4jgdfjm - valory/abstract_round_abci:0.1.0:bafybeibovsktd3uxur45nrcomq5shcn46cgxd5idmhxbmjhg32c5abyqim -- valory/liquidity_trader_abci:0.1.0:bafybeihml6udc4leqoexdbqrjchxj4nlymqky5ejh6vlnqg7mlh64jp2zi -- valory/optimus_abci:0.1.0:bafybeia3bmcjh2xyfqqlxkapegfkuy4fdezvsjhmu5ans33gdcexlr54ha +- valory/liquidity_trader_abci:0.1.0:bafybeia6pmuwqvpb6bdk75uywqxgr3puarikzcfkxj5xvwbyu7lxqzmkgi +- valory/optimus_abci:0.1.0:bafybeifaid4m5y6nr6zhqhoyonl34gjq4sloajvnwfxp6odybb5tzdepnm - valory/registration_abci:0.1.0:bafybeicnth5q4httefsusywx3zrrq4al47owvge72dqf2fziruicq6hqta - valory/reset_pause_abci:0.1.0:bafybeievjciqdvxhqxfjd4whqs27h6qbxqzrae7wwj7fpvxlvmtw3x35im - valory/termination_abci:0.1.0:bafybeid54buqxipiuduw7b6nnliiwsxajnltseuroad53wukfonpxca2om diff --git a/packages/valory/services/optimus/service.yaml b/packages/valory/services/optimus/service.yaml index d7bd6cb..9df922d 100644 --- a/packages/valory/services/optimus/service.yaml +++ b/packages/valory/services/optimus/service.yaml @@ -6,7 +6,7 @@ aea_version: '>=1.0.0, <2.0.0' license: Apache-2.0 fingerprint: {} fingerprint_ignore_patterns: [] -agent: valory/optimus:0.1.0:bafybeihiq4tplmbmlodqnfyojrnvyzkdoz25c667v6gocpv3r5chohnlgm +agent: valory/optimus:0.1.0:bafybeih32imiqyeh23pu5yjtjbxcbvmblpjwl6an7vxpisqri4yeuqkwby number_of_agents: 1 deployment: {} --- diff --git a/packages/valory/skills/liquidity_trader_abci/behaviours.py b/packages/valory/skills/liquidity_trader_abci/behaviours.py index fd4e0b5..5b56cd8 100644 --- a/packages/valory/skills/liquidity_trader_abci/behaviours.py +++ b/packages/valory/skills/liquidity_trader_abci/behaviours.py @@ -422,41 +422,6 @@ def async_act(self) -> Generator: if invest_in_pool: actions = yield from self.get_order_of_transactions() - current_timestamp = cast( - SharedState, self.context.state - ).round_sequence.last_round_transition_timestamp.timestamp() - - # Check if rewards can be claimed. Rewards can be claimed if either: - # 1. No rewards have been claimed yet (last_reward_claimed_timestamp is None), or - # 2. The current timestamp exceeds the allowed reward claiming time period since the last claim. - claim_rewards = ( - True - if self.synchronized_data.last_reward_claimed_timestamp is None - else current_timestamp - >= self.synchronized_data.last_reward_claimed_timestamp - + self.params.reward_claiming_time_period - ) - if claim_rewards: - # check current reward - allowed_chains = self.params.allowed_chains - if not allowed_chains: - self.context.logger.warning("No chains found") - return None - # we can claim all our token rewards at once - # hence we build only one action per chain - for chain in allowed_chains: - chain_id = self.params.chain_to_chain_id_mapping.get(chain) - safe_address = self.params.safe_contract_addresses.get(chain) - rewards = yield from self.get_rewards(chain_id, safe_address) - if not rewards: - self.context.logger.warning( - f"No rewards to claim for user address {safe_address} on chain {chain}" - ) - continue - action = yield from self.build_claim_reward_action(rewards, chain) - if action: - actions.append(action) - self.context.logger.info(f"Actions: {actions}") serialized_actions = json.dumps(actions) sender = self.context.agent_address @@ -573,7 +538,7 @@ def _get_filtered_pools(self) -> Generator[None, None, Optional[Dict[str, Any]]] # use this if you want to test with script # api_url = self.params.pool_data_api_url - self.context.logger.info(f"{api_url}") + self.context.logger.info(f"Fetching campaigns from {api_url}") response = yield from self.get_http_response( method="GET", @@ -634,17 +599,18 @@ def _filter_campaigns(self, chain, campaigns, filtered_pools): # type 1 and 2 stand for ERC20 and Concentrated liquidity campaigns respectively # https://docs.merkl.xyz/integrate-merkl/integrate-merkl-to-your-app#merkl-api if campaign_type in [1, 2]: - if campaign_apr > self.current_pool.get("apr", 0.0): - campaign_pool_address = campaign.get("mainParameter") - if not campaign_pool_address: - self.context.logger.warning( - "No pool address found for campaign" - ) - continue - current_pool_address = self.current_pool.get("address") - # The pool should not be the current pool - if campaign_pool_address != current_pool_address: - filtered_pools[dex_type][chain].append(campaign) + if not campaign_apr > self.current_pool.get("apr", 0.0): + self.context.logger.info( + "APR does not exceed the current pool APR" + ) + continue + campaign_pool_address = campaign.get("mainParameter") + if not campaign_pool_address: + continue + current_pool_address = self.current_pool.get("address") + # The pool should not be the current pool + if campaign_pool_address != current_pool_address: + filtered_pools[dex_type][chain].append(campaign) def get_order_of_transactions( self, @@ -652,6 +618,20 @@ def get_order_of_transactions( """Get the order of transactions to perform based on the current pool status and token balances.""" actions = [] + if self._can_claim_rewards(): + # check current reward + allowed_chains = self.params.allowed_chains + # we can claim all our token rewards at once + # hence we build only one action per chain + for chain in allowed_chains: + chain_id = self.params.chain_to_chain_id_mapping.get(chain) + safe_address = self.params.safe_contract_addresses.get(chain) + rewards = yield from self._get_rewards(chain_id, safe_address) + if not rewards: + continue + action = self._build_claim_reward_action(rewards, chain) + actions.append(action) + if not self.current_pool: tokens = self._get_tokens_over_min_balance() if not tokens or len(tokens) < 2: @@ -684,7 +664,6 @@ def get_order_of_transactions( return None actions.append(enter_pool_action) - self.context.logger.info(f"Actions: {actions}") return actions def _get_tokens_over_min_balance(self) -> Optional[List[Any]]: @@ -969,6 +948,19 @@ def _build_enter_pool_action(self) -> Dict[str, Any]: "apr": self.highest_apr_pool.get("apr"), } + def _build_claim_reward_action( + self, rewards: Dict[str, Any], chain: str + ) -> Dict[str, Any]: + return { + "action": Action.CLAIM_REWARDS.value, + "chain": chain, + "users": rewards.get("users"), + "tokens": rewards.get("tokens"), + "claims": rewards.get("claims"), + "proofs": rewards.get("proofs"), + "token_symbols": rewards.get("symbols"), + } + def _get_asset_symbol(self, chain: str, address: str) -> Optional[str]: positions = self.synchronized_data.positions for position in positions: @@ -979,7 +971,7 @@ def _get_asset_symbol(self, chain: str, address: str) -> Optional[str]: return None - def get_rewards( + def _get_rewards( self, chain_id: int, user_address: str ) -> Generator[None, None, Optional[Dict[str, Any]]]: base_url = self.params.merkl_user_rewards_url @@ -1002,18 +994,29 @@ def get_rewards( self.context.logger.info(f"User rewards: {data}") tokens = [k for k, v in data.items() if v.get("proof")] if not tokens: - self.context.logger.warning("No tokens to claim") + self.context.logger.info("No tokens to claim!") return None - claims = [int(data[t].get("unclaimed", 0)) for t in tokens] + symbols = [data[t].get("symbol") for t in tokens] + claims = [int(data[t].get("accumulated", 0)) for t in tokens] + # Check if all claims are zero if all(claim == 0 for claim in claims): - self.context.logger.warning("All claims are zero, nothing to claim") + self.context.logger.info("All claims are zero, nothing to claim") + return None + + unclaimed = [int(data[t].get("unclaimed", 0)) for t in tokens] + # Check if everything has been already claimed are zero + if all(claim == 0 for claim in unclaimed): + self.context.logger.info( + "All accumulated claims already made. Nothing left to claim." + ) return None proofs = [data[t].get("proof") for t in tokens] return { "users": [user_address] * len(tokens), "tokens": tokens, + "symbols": symbols, "claims": claims, "proofs": proofs, } @@ -1024,35 +1027,22 @@ def get_rewards( ) return None - def build_claim_reward_action( - self, rewards: Dict[str, Any], chain: str - ) -> Generator[None, None, Optional[Dict[str, Any]]]: - action = {} - action["action"] = Action.CLAIM_REWARDS.value - action["chain"] = chain - action["users"] = rewards.get("users") - action["tokens"] = rewards.get("tokens") - action["claims"] = rewards.get("claims") - action["proofs"] = rewards.get("proofs") - - token_symbols = [] - # take each token and add its symbol - for token in rewards.get("tokens"): - token_symbol = yield from self.contract_interact( - performative=ContractApiMessage.Performative.GET_RAW_TRANSACTION, - contract_address=token, - contract_public_id=ERC20.contract_id, - contract_callable="get_token_symbol", - data_key="data", - chain_id=chain, - ) - if not token_symbol: - token_symbols.append("unknown") - else: - token_symbols.append(token_symbol) + def _can_claim_rewards(self) -> bool: + # Check if rewards can be claimed. Rewards can be claimed if either: + # 1. No rewards have been claimed yet (last_reward_claimed_timestamp is None), or + # 2. The current timestamp exceeds the allowed reward claiming time period since the last claim. - action["token_symbols"] = token_symbols - return action + current_timestamp = cast( + SharedState, self.context.state + ).round_sequence.last_round_transition_timestamp.timestamp() + + last_claimed_timestamp = self.synchronized_data.last_reward_claimed_timestamp + if last_claimed_timestamp is None: + return True + return ( + current_timestamp + >= last_claimed_timestamp + self.params.reward_claiming_time_period + ) class DecisionMakingBehaviour(LiquidityTraderBaseBehaviour): @@ -1187,6 +1177,7 @@ def get_next_event(self) -> Generator[None, None, Tuple[str, Dict, Optional[Dict # If last action was Claim Rewards and it was successful we update the list of assets and the last_reward_claimed_timestamp if ( last_executed_action_index is not None + and last_round_id != DecisionMakingRound.auto_round_id() and Action(actions[last_executed_action_index]["action"]) == Action.CLAIM_REWARDS ): @@ -1196,12 +1187,16 @@ def get_next_event(self) -> Generator[None, None, Tuple[str, Dict, Optional[Dict action.get("tokens"), action.get("token_symbols") ): self._add_token_to_assets(chain, token, token_symbol) - + current_timestamp = cast( SharedState, self.context.state ).round_sequence.last_round_transition_timestamp.timestamp() - return Event.UPDATE.value, {"last_reward_claimed_timestamp": current_timestamp}, {} + return ( + Event.UPDATE.value, + {"last_reward_claimed_timestamp": current_timestamp}, + {}, + ) # if all actions have been executed we exit DecisionMaking if current_action_index >= len(self.synchronized_data.actions): diff --git a/packages/valory/skills/liquidity_trader_abci/skill.yaml b/packages/valory/skills/liquidity_trader_abci/skill.yaml index ca68f25..84fe029 100644 --- a/packages/valory/skills/liquidity_trader_abci/skill.yaml +++ b/packages/valory/skills/liquidity_trader_abci/skill.yaml @@ -7,7 +7,7 @@ license: Apache-2.0 aea_version: '>=1.0.0, <2.0.0' fingerprint: __init__.py: bafybeia7bn2ahqqwkf63ptje6rfnftuwrsp33sswgpcbh5osbesxxr6g4m - behaviours.py: bafybeiduwq37ba5bdtus2iwomcb2bhxerdiriuhpr2at5wzjxvqut2xjwm + behaviours.py: bafybeidczsocvdmtdhqh6jm4bdjzveuvcmxstmwwyomdrdvpels7r5qipa dialogues.py: bafybeiay23otskx2go5xhtgdwfw2kd6rxd62sxxdu3njv7hageorl5zxzm fsm_specification.yaml: bafybeia7dh7vuifjebf63wdqlw6gtrwcss5mapmdzhbju7i6edsw3zqxki handlers.py: bafybeidxw2lvgiifmo4siobpwuwbxscuifrdo3gnkjyn6bgexotj5f7zf4 @@ -16,7 +16,7 @@ fingerprint: pool_behaviour.py: bafybeiaheuesscgqzwjbpyrezgwpdbdfurlmfwbc462qv6rblwwxlx5dpm pools/balancer.py: bafybeieo53qtt557632vtp3x3huagdpqevptextbwd7euk6hpoz6ybtuci pools/uniswap.py: bafybeif2cjbtjh5altlgranmgrif4yaevnn344fn3askbjde5h4y4rh2mq - rounds.py: bafybeiattfoicojezsnc5pidpqkrl2ncglzhbzqufkmy37t7v2b7qta53q + rounds.py: bafybeico4zvu3fpnqbw3k2et3gzvliejjjkosmqrnbh3imblqppiui4alu strategies/simple_strategy.py: bafybeig2iygxz5gewmiyeawubs5f4pr5e3st22lmdkxthlfq7t5kbluw4a strategy_behaviour.py: bafybeidk6sorg47kuuubamcccksi65x3txldyo7y2hm5opbye2ghmz2ljy fingerprint_ignore_patterns: [] diff --git a/packages/valory/skills/optimus_abci/skill.yaml b/packages/valory/skills/optimus_abci/skill.yaml index 18441f7..c09e482 100644 --- a/packages/valory/skills/optimus_abci/skill.yaml +++ b/packages/valory/skills/optimus_abci/skill.yaml @@ -22,7 +22,7 @@ skills: - valory/registration_abci:0.1.0:bafybeicnth5q4httefsusywx3zrrq4al47owvge72dqf2fziruicq6hqta - valory/reset_pause_abci:0.1.0:bafybeievjciqdvxhqxfjd4whqs27h6qbxqzrae7wwj7fpvxlvmtw3x35im - valory/termination_abci:0.1.0:bafybeid54buqxipiuduw7b6nnliiwsxajnltseuroad53wukfonpxca2om -- valory/liquidity_trader_abci:0.1.0:bafybeihml6udc4leqoexdbqrjchxj4nlymqky5ejh6vlnqg7mlh64jp2zi +- valory/liquidity_trader_abci:0.1.0:bafybeia6pmuwqvpb6bdk75uywqxgr3puarikzcfkxj5xvwbyu7lxqzmkgi - valory/transaction_settlement_abci:0.1.0:bafybeihq2yenstblmaadzcjousowj5kfn5l7ns5pxweq2gcrsczfyq5wzm behaviours: main: