diff --git a/.github/workflows/common_checks.yaml b/.github/workflows/common_checks.yaml index a09c08584..d7e5ee978 100644 --- a/.github/workflows/common_checks.yaml +++ b/.github/workflows/common_checks.yaml @@ -63,7 +63,7 @@ jobs: - name: Install dependencies run: pip install tomte[tox,cli]==0.2.14 - name: Check copyright headers - run: tomte check-copyright --author valory --exclude-part abci --exclude-part http_client --exclude-part ipfs --exclude-part ledger --exclude-part p2p_libp2p_client --exclude-part gnosis_safe --exclude-part gnosis_safe_proxy_factory --exclude-part multisend --exclude-part service_registry --exclude-part protocols --exclude-part abstract_abci --exclude-part abstract_round_abci --exclude-part registration_abci --exclude-part reset_pause_abci --exclude-part termination_abci --exclude-part transaction_settlement_abci --exclude-part websocket_client --exclude-part contract_subscription --exclude-part mech --exclude-part mech_interact_abci --exclude-part http_server --exclude-part mech_marketplace + run: tomte check-copyright --author valory --exclude-part abci --exclude-part http_client --exclude-part ipfs --exclude-part ledger --exclude-part p2p_libp2p_client --exclude-part gnosis_safe --exclude-part gnosis_safe_proxy_factory --exclude-part multisend --exclude-part service_registry --exclude-part protocols --exclude-part abstract_abci --exclude-part abstract_round_abci --exclude-part registration_abci --exclude-part reset_pause_abci --exclude-part termination_abci --exclude-part transaction_settlement_abci --exclude-part websocket_client --exclude-part contract_subscription --exclude-part mech --exclude-part mech_interact_abci --exclude-part http_server --exclude-part mech_marketplace --exclude-part erc20 - name: License compatibility check run: tox -e liccheck - name: Check dependencies diff --git a/.gitleaksignore b/.gitleaksignore index c06beb102..c22a6699f 100644 --- a/.gitleaksignore +++ b/.gitleaksignore @@ -27,6 +27,9 @@ ae28aeb8ad56e1873bfb3996c5d5ad8367709cd1:packages/valory/skills/decision_maker_a ae28aeb8ad56e1873bfb3996c5d5ad8367709cd1:packages/valory/skills/trader_abci/skill.yaml:generic-api-key:154 9d8617ddd58c172f8d9dd32a7b74f22aa3d27dfb:packages/valory/skills/mech_interact_abci/skill.yaml:generic-api-key:151 0b4170ce2e876c28a9585081117e6468db17f0b9:packages/valory/services/trader_omen_gnosis/service.yaml:generic-api-key:48 +0c54647f03886d75fca4522420ea72b58d6c3dcc:packages/valory/skills/market_manager_abci/behaviours.py:generic-api-key:59 +ce332275857ab3dabb2c0c57330fb2137b6b77f7:packages/valory/skills/trader_abci/skill.yaml:generic-api-key:263 +518f4ec1e1b80f45420c3626613833a27b0aab5d:packages/valory/skills/trader_abci/skill.yaml:generic-api-key:263 518f4ec1e1b80f45420c3626613833a27b0aab5d:packages/valory/skills/trader_abci/skill.yaml:generic-api-key:263 0c54647f03886d75fca4522420ea72b58d6c3dcc:packages/valory/skills/market_manager_abci/behaviours.py:generic-api-key:59 7cb24a6ba2423f65a946b163268d09308ca00a00:trader_backup/log.txt:generic-api-key:176 diff --git a/Makefile b/Makefile index dedd4ca4a..a8005316b 100644 --- a/Makefile +++ b/Makefile @@ -74,13 +74,13 @@ security: .PHONY: generators generators: clean-cache fix-abci-app-specs tox -e abci-docstrings - tomte format-copyright --author valory --exclude-part abci --exclude-part http_client --exclude-part ipfs --exclude-part ledger --exclude-part p2p_libp2p_client --exclude-part gnosis_safe --exclude-part gnosis_safe_proxy_factory --exclude-part multisend --exclude-part service_registry --exclude-part protocols --exclude-part abstract_abci --exclude-part abstract_round_abci --exclude-part registration_abci --exclude-part reset_pause_abci --exclude-part termination_abci --exclude-part transaction_settlement_abci --exclude-part websocket_client --exclude-part contract_subscription --exclude-part mech --exclude-part mech_interact_abci --exclude-part http_server --exclude-part mech_marketplace + tomte format-copyright --author valory --exclude-part abci --exclude-part http_client --exclude-part http_server --exclude-part ipfs --exclude-part ledger --exclude-part p2p_libp2p_client --exclude-part erc20 --exclude-part gnosis_safe --exclude-part gnosis_safe_proxy_factory --exclude-part mech --exclude-part mech_marketplace --exclude-part multisend --exclude-part service_registry --exclude-part protocols --exclude-part abstract_abci --exclude-part abstract_round_abci --exclude-part mech_interact_abci --exclude-part registration_abci --exclude-part reset_pause_abci --exclude-part termination_abci --exclude-part transaction_settlement_abci --exclude-part websocket_client --exclude-part contract_subscription autonomy packages lock tox -e fix-doc-hashes .PHONY: common-checks-1 common-checks-1: - tomte check-copyright --author valory --exclude-part abci --exclude-part http_client --exclude-part ipfs --exclude-part ledger --exclude-part p2p_libp2p_client --exclude-part gnosis_safe --exclude-part gnosis_safe_proxy_factory --exclude-part multisend --exclude-part service_registry --exclude-part protocols --exclude-part abstract_abci --exclude-part abstract_round_abci --exclude-part registration_abci --exclude-part reset_pause_abci --exclude-part termination_abci --exclude-part transaction_settlement_abci --exclude-part websocket_client --exclude-part contract_subscription --exclude-part mech --exclude-part mech_interact_abci --exclude-part http_server --exclude-part mech_marketplace + tomte check-copyright --author valory --exclude-part abci --exclude-part http_client --exclude-part http_server --exclude-part ipfs --exclude-part ledger --exclude-part p2p_libp2p_client --exclude-part erc20 --exclude-part gnosis_safe --exclude-part gnosis_safe_proxy_factory --exclude-part mech --exclude-part mech_marketplace --exclude-part multisend --exclude-part service_registry --exclude-part protocols --exclude-part abstract_abci --exclude-part abstract_round_abci --exclude-part mech_interact_abci --exclude-part registration_abci --exclude-part reset_pause_abci --exclude-part termination_abci --exclude-part transaction_settlement_abci --exclude-part websocket_client --exclude-part contract_subscription tomte check-doc-links tox -p -e check-hash -e check-packages -e check-doc-hashes -e analyse-service diff --git a/packages/packages.json b/packages/packages.json index 8f6638f64..2aa6d6f11 100644 --- a/packages/packages.json +++ b/packages/packages.json @@ -10,20 +10,20 @@ "contract/valory/realitio_proxy/0.1.0": "bafybeidx37xzjjmapwacedgzhum6grfzhp5vhouz4zu3pvpgdy5pgb2fr4", "contract/valory/conditional_tokens/0.1.0": "bafybeibnzmqmeph4cj5vfh3s622mo2o5627vjjwc6bptrhj4dk65mzgvhe", "contract/valory/agent_registry/0.1.0": "bafybeibboljpn2zevzxnpgflxj6ykxk4bpxegtzjts25ajliaoadz35mca", - "contract/valory/service_staking_token/0.1.0": "bafybeieg664oohr26gpcfn3uied4minlz6dmgd32xboewscnxqnv5kk4zi", + "contract/valory/service_staking_token/0.1.0": "bafybeihlrs7m7qcme22cjfgk4l2fqbyqeqvdbum3xcljmgi4lcbegbwmkq", "contract/valory/transfer_nft_condition/0.1.0": "bafybeicdtigdwlt47jg2tibxltwyyl4apysvlideo53lgiy3muuho3izpa", "contract/valory/mech_activity/0.1.0": "bafybeieadv7vnbguc7beu6xo3rs3mqbgzc7wayc7kvgb2tmitmjtpdcqkq", - "contract/valory/staking_token/0.1.0": "bafybeiaynt6clwbthtbndtocnwul7dp76ctmu4jxinp7fnqks4pxt65yuy", + "contract/valory/staking_token/0.1.0": "bafybeif5m72b7dohrpvcyijqm6xm6gyarg3rp3zlwerxdzfkfmc2imwlga", "contract/valory/relayer/0.1.0": "bafybeicreijhjycqrutdpbdn3vdcpmo233y3p66l3ovmr2goa2y2e6bshy", - "skill/valory/market_manager_abci/0.1.0": "bafybeiayhzwxlpqsevyvaxn6dtnnu745az5vcz7dckmghnmjywxdtvoyly", - "skill/valory/decision_maker_abci/0.1.0": "bafybeibi3b4tnal6c6cypszogn7iskerpolmueddeagnyt3fv65vhvxebq", - "skill/valory/trader_abci/0.1.0": "bafybeias2rt775gh5357uw5tbw3n7acdyp5yhx6x4noa3aqqqluselnkju", - "skill/valory/tx_settlement_multiplexer_abci/0.1.0": "bafybeiharmlmnezdeuyfzbq27ds7jtnbqrvo62k4qj34cdbjstwqe5occu", - "skill/valory/staking_abci/0.1.0": "bafybeiaasclr4lf3u2layekmfwyasckxrslbs2g4me7kvozz5goswlznjq", - "skill/valory/check_stop_trading_abci/0.1.0": "bafybeic3bismocli2yyxmjhjsevjbzpuf3ladsvlkazfx23vq3q6uxrn7m", - "agent/valory/trader/0.1.0": "bafybeib3y24ugudzpcgrqxfpuuhfihj2hzo4yx3am2gnel2zm4xlem237a", - "service/valory/trader/0.1.0": "bafybeigflygpbier6vwn4k7ut77gbwsnw24r5z74o5w2kiewbnvgpxdpea", - "service/valory/trader_pearl/0.1.0": "bafybeid47v5pd3ibsbs4dhie2odz3zldt5euhr4awq5sjq7q4c6p67cxam" + "skill/valory/market_manager_abci/0.1.0": "bafybeihzfnnogprkz2m3vlbzndzrfwjcxk5i35h2vefjhguhf2gfkkcesy", + "skill/valory/decision_maker_abci/0.1.0": "bafybeidq54y34rq7djgxs53rti5crqrcf6kbie22y3tdce7og6mbifo44i", + "skill/valory/trader_abci/0.1.0": "bafybeihworqtm7rjf5q3v4v5l5quiqmyeafn5jdbca6rdbsspiybloreom", + "skill/valory/tx_settlement_multiplexer_abci/0.1.0": "bafybeicovhetzfzrcdnc7rpdyrqwui5mbpk5hltqborjgbg7kc2rg46fjy", + "skill/valory/staking_abci/0.1.0": "bafybeicc4iactsgkdpbbqqih6pm4tedajxf7b2axgmptm4aecisb6gh4ei", + "skill/valory/check_stop_trading_abci/0.1.0": "bafybeicbshzy3nzjs6nw4me66q2m6n2pmdclqvtqlggmaaavk6rtvkdovm", + "agent/valory/trader/0.1.0": "bafybeig7ax6j675ukhjdlobd5lcrujckvjnrnekjsxlchih6onqsjasfze", + "service/valory/trader/0.1.0": "bafybeibgy3t2l3ks2cldjgfdiybxgriituwuou3sfmfvposqtbljrxvvzi", + "service/valory/trader_pearl/0.1.0": "bafybeigtmsu4rox5cssoorkvmq5yknz2ridlt7yzqifxesihwn425t2h4e" }, "third_party": { "protocol/valory/acn_data_share/0.1.0": "bafybeidbvo3jdbt54pqk3foqfso4uim2vaea5abg6jzktomdeklh6sm2rq", diff --git a/packages/valory/agents/trader/aea-config.yaml b/packages/valory/agents/trader/aea-config.yaml index a7217db96..c803960df 100644 --- a/packages/valory/agents/trader/aea-config.yaml +++ b/packages/valory/agents/trader/aea-config.yaml @@ -26,10 +26,10 @@ contracts: - valory/realitio:0.1.0:bafybeietgux6kkhdquspy35qera7gjwwqwrremmoeatjzwwokjb2lzsata - valory/realitio_proxy:0.1.0:bafybeidx37xzjjmapwacedgzhum6grfzhp5vhouz4zu3pvpgdy5pgb2fr4 - valory/agent_registry:0.1.0:bafybeibboljpn2zevzxnpgflxj6ykxk4bpxegtzjts25ajliaoadz35mca -- valory/service_staking_token:0.1.0:bafybeieg664oohr26gpcfn3uied4minlz6dmgd32xboewscnxqnv5kk4zi +- valory/service_staking_token:0.1.0:bafybeihlrs7m7qcme22cjfgk4l2fqbyqeqvdbum3xcljmgi4lcbegbwmkq - valory/transfer_nft_condition:0.1.0:bafybeicdtigdwlt47jg2tibxltwyyl4apysvlideo53lgiy3muuho3izpa - valory/erc20:0.1.0:bafybeientdgpccdi7prtu4x53m5g3yugh5tuh5hnroylfz3wwzyjniqure -- valory/staking_token:0.1.0:bafybeiaynt6clwbthtbndtocnwul7dp76ctmu4jxinp7fnqks4pxt65yuy +- valory/staking_token:0.1.0:bafybeif5m72b7dohrpvcyijqm6xm6gyarg3rp3zlwerxdzfkfmc2imwlga - valory/mech_activity:0.1.0:bafybeieadv7vnbguc7beu6xo3rs3mqbgzc7wayc7kvgb2tmitmjtpdcqkq - valory/mech_marketplace:0.1.0:bafybeicfuigpr65k4l2r5dbazzwh43yc6pfuy5mrkjkagmhmcp6ioktfay - valory/relayer:0.1.0:bafybeicreijhjycqrutdpbdn3vdcpmo233y3p66l3ovmr2goa2y2e6bshy @@ -50,12 +50,12 @@ skills: - valory/reset_pause_abci:0.1.0:bafybeiezfedmmseox3ce5aucxsiszdmvskrwwbtpb2a3vw3sbmc5jt7nri - valory/termination_abci:0.1.0:bafybeiea67epwwgngp7b3wavs6hpkaxv6etyaps6g6325bchfnf354mibq - valory/transaction_settlement_abci:0.1.0:bafybeifmgmwdkx4esemxjacjwzqkqymkuklb5nehkwqkx7v335fllgswcq -- valory/tx_settlement_multiplexer_abci:0.1.0:bafybeiharmlmnezdeuyfzbq27ds7jtnbqrvo62k4qj34cdbjstwqe5occu -- valory/market_manager_abci:0.1.0:bafybeiayhzwxlpqsevyvaxn6dtnnu745az5vcz7dckmghnmjywxdtvoyly -- valory/decision_maker_abci:0.1.0:bafybeibi3b4tnal6c6cypszogn7iskerpolmueddeagnyt3fv65vhvxebq -- valory/trader_abci:0.1.0:bafybeias2rt775gh5357uw5tbw3n7acdyp5yhx6x4noa3aqqqluselnkju -- valory/staking_abci:0.1.0:bafybeiaasclr4lf3u2layekmfwyasckxrslbs2g4me7kvozz5goswlznjq -- valory/check_stop_trading_abci:0.1.0:bafybeic3bismocli2yyxmjhjsevjbzpuf3ladsvlkazfx23vq3q6uxrn7m +- valory/tx_settlement_multiplexer_abci:0.1.0:bafybeicovhetzfzrcdnc7rpdyrqwui5mbpk5hltqborjgbg7kc2rg46fjy +- valory/market_manager_abci:0.1.0:bafybeihzfnnogprkz2m3vlbzndzrfwjcxk5i35h2vefjhguhf2gfkkcesy +- valory/decision_maker_abci:0.1.0:bafybeidq54y34rq7djgxs53rti5crqrcf6kbie22y3tdce7og6mbifo44i +- valory/trader_abci:0.1.0:bafybeihworqtm7rjf5q3v4v5l5quiqmyeafn5jdbca6rdbsspiybloreom +- valory/staking_abci:0.1.0:bafybeicc4iactsgkdpbbqqih6pm4tedajxf7b2axgmptm4aecisb6gh4ei +- valory/check_stop_trading_abci:0.1.0:bafybeicbshzy3nzjs6nw4me66q2m6n2pmdclqvtqlggmaaavk6rtvkdovm - valory/mech_interact_abci:0.1.0:bafybeib4vn6m2yumwoclh5aatcdt5yxcjc5owxmxy5o7t3nfzormgwkr64 customs: - valory/mike_strat:0.1.0:bafybeihjiol7f4ch4piwfikurdtfwzsh6qydkbsztpbwbwb2yrqdqf726m @@ -249,6 +249,8 @@ models: tool_quarantine_duration: ${int:18000} default_chain_id: ${str:gnosis} mech_interact_round_timeout_seconds: ${int:5400} + olas_token_address: ${str:0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f} + http_handler_hostname_regex: ${str:localhost} benchmarking_mode: args: enabled: ${bool:false} diff --git a/packages/valory/contracts/service_staking_token/contract.py b/packages/valory/contracts/service_staking_token/contract.py index 3817febe4..036fa7e5b 100644 --- a/packages/valory/contracts/service_staking_token/contract.py +++ b/packages/valory/contracts/service_staking_token/contract.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # -# Copyright 2023-2024 Valory AG +# Copyright 2023-2025 Valory AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ """This module contains the class to connect to the `ServiceStakingTokenMechUsage` contract.""" from enum import Enum +from typing import Dict from aea.common import JSONLike from aea.configurations.base import PublicId @@ -190,3 +191,16 @@ def get_min_staking_duration( contract = cls.get_instance(ledger_api, contract_address) duration = contract.functions.minStakingDuration().call() return dict(data=duration) + + @classmethod + def get_epoch_end( + cls, + ledger_api: LedgerApi, + contract_address: str, + ) -> Dict: + """Get the epoch end.""" + contract_instance = cls.get_instance(ledger_api, contract_address) + liveness = contract_instance.functions.livenessPeriod().call() + checkpoint_ts = contract_instance.functions.tsCheckpoint().call() + epoch_end = checkpoint_ts + liveness + return dict(epoch_end=epoch_end) \ No newline at end of file diff --git a/packages/valory/contracts/service_staking_token/contract.yaml b/packages/valory/contracts/service_staking_token/contract.yaml index 10d84ad93..d81bdc7f7 100644 --- a/packages/valory/contracts/service_staking_token/contract.yaml +++ b/packages/valory/contracts/service_staking_token/contract.yaml @@ -8,7 +8,7 @@ aea_version: '>=1.0.0, <2.0.0' fingerprint: __init__.py: bafybeid3wfzglolebuo6jrrsopswzu4lk77bm76mvw3euizlsjtnt3wmgu build/ServiceStakingToken.json: bafybeib6frfpqtr4dfyxuylehqmic2iawofydx7u24t7j5zbrsc4m4ijoi - contract.py: bafybeiboxlbmmhnsreimqyrnn3yamzueiucno6v75xjfrvfvbx4pqrnlsy + contract.py: bafybeiecpttavqdocg3vi2eyrsntwxiz5c5corijr2q73rg5bnrkt37ajy fingerprint_ignore_patterns: [] contracts: [] class_name: ServiceStakingTokenContract diff --git a/packages/valory/contracts/staking_token/contract.py b/packages/valory/contracts/staking_token/contract.py index 8bc883783..b3f763c74 100644 --- a/packages/valory/contracts/staking_token/contract.py +++ b/packages/valory/contracts/staking_token/contract.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # -# Copyright 2023-2024 Valory AG +# Copyright 2023-2025 Valory AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ """This module contains the class to connect to the `StakingToken` contract.""" from enum import Enum +from typing import Dict from aea.common import JSONLike from aea.configurations.base import PublicId @@ -190,3 +191,27 @@ def get_min_staking_duration( contract = cls.get_instance(ledger_api, contract_address) duration = contract.functions.minStakingDuration().call() return dict(data=duration) + + @classmethod + def get_metadata_hash( + cls, + ledger_api: LedgerApi, + contract_address: str, + ) -> JSONLike: + """Retrieve the metadata hash.""" + contract = cls.get_instance(ledger_api, contract_address) + metadata_hash = contract.functions.metadataHash().call() + return dict(data=metadata_hash) + + @classmethod + def get_epoch_end( + cls, + ledger_api: LedgerApi, + contract_address: str, + ) -> Dict: + """Get the epoch end.""" + contract_instance = cls.get_instance(ledger_api, contract_address) + liveness = contract_instance.functions.livenessPeriod().call() + checkpoint_ts = contract_instance.functions.tsCheckpoint().call() + epoch_end = checkpoint_ts + liveness + return dict(epoch_end=epoch_end) diff --git a/packages/valory/contracts/staking_token/contract.yaml b/packages/valory/contracts/staking_token/contract.yaml index b2e580c58..69388c2f2 100644 --- a/packages/valory/contracts/staking_token/contract.yaml +++ b/packages/valory/contracts/staking_token/contract.yaml @@ -8,7 +8,7 @@ aea_version: '>=1.0.0, <2.0.0' fingerprint: __init__.py: bafybeicmgkagyhgwn2ktcdjbprijalbdyj26cvza4d3b7uvmehvy4mmr3i build/StakingToken.json: bafybeibhcwyawq377innrpq4ytpw5kotufjqo7cyd2rjhyit34mnbks5b4 - contract.py: bafybeigjt7a2biiorp4vmj4d3qkm3xbbohnawoetywojye5akhavlvkrxm + contract.py: bafybeiatpzwdfoyvms3dr57gt2lgjiv7debw76ael4ulcwkd22q46uofy4 fingerprint_ignore_patterns: [] contracts: [] class_name: StakingTokenContract diff --git a/packages/valory/services/trader/service.yaml b/packages/valory/services/trader/service.yaml index 10e5da8b6..678d3be6c 100644 --- a/packages/valory/services/trader/service.yaml +++ b/packages/valory/services/trader/service.yaml @@ -7,7 +7,7 @@ license: Apache-2.0 fingerprint: README.md: bafybeigtuothskwyvrhfosps2bu6suauycolj67dpuxqvnicdrdu7yhtvq fingerprint_ignore_patterns: [] -agent: valory/trader:0.1.0:bafybeib3y24ugudzpcgrqxfpuuhfihj2hzo4yx3am2gnel2zm4xlem237a +agent: valory/trader:0.1.0:bafybeig7ax6j675ukhjdlobd5lcrujckvjnrnekjsxlchih6onqsjasfze number_of_agents: 4 deployment: agent: @@ -152,7 +152,9 @@ type: skill tool_quarantine_duration: ${TOOL_QUARANTINE_DURATION:int:18000} default_chain_id: ${DEFAULT_CHAIN_ID:str:gnosis} mech_interact_round_timeout_seconds: ${MECH_INTERACT_ROUND_TIMEOUT_SECONDS:int:5400} - benchmark_tool: &id003 + olas_token_address: ${OLAS_TOKEN_ADDRESS:str:0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f} + http_handler_hostname_regex: ${HTTP_HANDLER_HOSTNAME_REGEX:str:localhost} + benchmark_tool: &id012 args: log_dir: ${LOG_DIR:str:/benchmarks} benchmarking_mode: @@ -174,7 +176,7 @@ type: skill bet_amount_field: ${BENCHMARKING_MODE_BET_AMOUNT_FIELD:str:collateral_amount} results_filename: ${BENCHMARKING_MODE_RESULTS_FILENAME:str:benchmarking_results.csv} randomness: ${BENCHMARKING_MODE_RANDOMNESS:str:benchmarking_randomness} - acc_info_fields: &id004 + acc_info_fields: &id003 args: tool: ${ACC_INFO_FIELDS_TOOL:str:tool} requests: ${ACC_INFO_FIELDS_REQUESTS:str:total_requests} @@ -182,7 +184,7 @@ type: skill sep: ${ACC_INFO_FIELDS_SEP:str:,} max: ${ACC_INFO_FIELDS_MAX:str:max} datetime_format: ${ACC_INFO_FIELDS_DATETIME_FORMAT:str:%Y-%m-%d %H:%M:%S} - network_subgraph: &id005 + network_subgraph: &id004 args: headers: ${HEADERS:dict:{"Content-Type":"application/json"}} method: ${NETWORK_SUBGRAPH_METHOD:str:POST} @@ -194,7 +196,7 @@ type: skill error_type: ${NETWORK_SUBGRAPH_ERROR_TYPE:str:dict} retries: ${NETWORK_SUBGRAPH_RETRIES:int:5} url: ${NETWORK_SUBGRAPH_URL:str:https://api.thegraph.com/subgraphs/name/stakewise/ethereum-gnosis} - omen_subgraph: &id006 + omen_subgraph: &id005 args: headers: ${HEADERS:dict:{"Content-Type":"application/json"}} method: ${OMEN_SUBGRAPH_METHOD:str:POST} @@ -205,14 +207,14 @@ type: skill error_type: ${NETWORK_SUBGRAPH_ERROR_TYPE:str:dict} retries: ${OMEN_SUBGRAPH_RETRIES:int:5} url: ${OMEN_SUBGRAPH_URL:str:https://api.thegraph.com/subgraphs/name/protofire/omen-xdai} - randomness_api: &id007 + randomness_api: &id006 args: method: ${RANDOMNESS_API_METHOD:str:GET} response_key: ${RANDOMNESS_API_RESPONSE_KEY:str:null} response_type: ${RANDOMNESS_API_RESPONSE_TYPE:str:dict} retries: ${RANDOMNESS_API_RETRIES:int:5} url: ${RANDOMNESS_API_URL:str:https://drand.cloudflare.com/public/latest} - mech_response: &id008 + mech_response: &id007 args: headers: ${HEADERS:dict:{"Content-Type":"application/json"}} method: ${MECH_RESPONSE_API_METHOD:str:GET} @@ -220,7 +222,7 @@ type: skill response_type: ${MECH_RESPONSE_API_RESPONSE_TYPE:str:str} retries: ${MECH_RESPONSE_API_RETRIES:int:5} url: ${MECH_RESPONSE_API_URL:str:''} - agent_tools: &id009 + agent_tools: &id008 args: headers: ${HEADERS:dict:{"Content-Type":"application/json"}} method: ${AGENT_TOOLS_API_METHOD:str:GET} @@ -228,7 +230,7 @@ type: skill response_type: ${AGENT_TOOLS_API_RESPONSE_TYPE:str:list} retries: ${AGENT_TOOLS_API_RETRIES:int:5} url: ${AGENT_TOOLS_API_URL:str:''} - trades_subgraph: &id010 + trades_subgraph: &id009 args: headers: ${HEADERS:dict:{"Content-Type":"application/json"}} method: ${TRADES_SUBGRAPH_METHOD:str:POST} @@ -239,7 +241,7 @@ type: skill error_type: ${NETWORK_SUBGRAPH_ERROR_TYPE:str:dict} retries: ${TRADES_SUBGRAPH_RETRIES:int:5} url: ${TRADES_SUBGRAPH_URL:str:https://api.thegraph.com/subgraphs/name/protofire/omen-xdai} - conditional_tokens_subgraph: &id011 + conditional_tokens_subgraph: &id010 args: headers: ${HEADERS:dict:{"Content-Type":"application/json"}} method: ${CONDITIONAL_TOKENS_SUBGRAPH_METHOD:str:POST} @@ -250,7 +252,7 @@ type: skill error_type: ${NETWORK_SUBGRAPH_ERROR_TYPE:str:dict} retries: ${CONDITIONAL_TOKENS_SUBGRAPH_RETRIES:int:5} url: ${CONDITIONAL_TOKENS_SUBGRAPH_URL:str:https://api.thegraph.com/subgraphs/name/gnosis/conditional-tokens-gc} - realitio_subgraph: &id012 + realitio_subgraph: &id011 args: headers: ${HEADERS:dict:{"Content-Type":"application/json"}} method: ${REALITIO_SUBGRAPH_METHOD:str:POST} @@ -376,6 +378,8 @@ type: skill tool_quarantine_duration: ${TOOL_QUARANTINE_DURATION:int:18000} default_chain_id: ${DEFAULT_CHAIN_ID:str:gnosis} mech_interact_round_timeout_seconds: ${MECH_INTERACT_ROUND_TIMEOUT_SECONDS:int:5400} + olas_token_address: ${OLAS_TOKEN_ADDRESS:str:0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f} + http_handler_hostname_regex: ${HTTP_HANDLER_HOSTNAME_REGEX:str:localhost} benchmark_tool: *id003 acc_info_fields: *id004 network_subgraph: *id005 @@ -385,7 +389,7 @@ type: skill agent_tools: *id009 trades_subgraph: *id010 conditional_tokens_subgraph: *id011 - realitio_subgraph: *id012 + realitio_subgraph: *id011 2: models: params: @@ -501,16 +505,18 @@ type: skill tool_quarantine_duration: ${TOOL_QUARANTINE_DURATION:int:18000} default_chain_id: ${DEFAULT_CHAIN_ID:str:gnosis} mech_interact_round_timeout_seconds: ${MECH_INTERACT_ROUND_TIMEOUT_SECONDS:int:5400} - benchmark_tool: *id003 - acc_info_fields: *id004 - network_subgraph: *id005 - omen_subgraph: *id006 - randomness_api: *id007 - mech_response: *id008 - agent_tools: *id009 - trades_subgraph: *id010 - conditional_tokens_subgraph: *id011 - realitio_subgraph: *id012 + olas_token_address: ${OLAS_TOKEN_ADDRESS:str:0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f} + http_handler_hostname_regex: ${HTTP_HANDLER_HOSTNAME_REGEX:str:localhost} + benchmark_tool: *id012 + acc_info_fields: *id003 + network_subgraph: *id004 + omen_subgraph: *id005 + randomness_api: *id006 + mech_response: *id007 + agent_tools: *id008 + trades_subgraph: *id009 + conditional_tokens_subgraph: *id010 + realitio_subgraph: *id011 3: models: params: @@ -626,16 +632,18 @@ type: skill tool_quarantine_duration: ${TOOL_QUARANTINE_DURATION:int:18000} default_chain_id: ${DEFAULT_CHAIN_ID:str:gnosis} mech_interact_round_timeout_seconds: ${MECH_INTERACT_ROUND_TIMEOUT_SECONDS:int:5400} - benchmark_tool: *id003 - acc_info_fields: *id004 - network_subgraph: *id005 - omen_subgraph: *id006 - randomness_api: *id007 - mech_response: *id008 - agent_tools: *id009 - trades_subgraph: *id010 - conditional_tokens_subgraph: *id011 - realitio_subgraph: *id012 + olas_token_address: ${OLAS_TOKEN_ADDRESS:str:0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f} + http_handler_hostname_regex: ${HTTP_HANDLER_HOSTNAME_REGEX:str:localhost} + benchmark_tool: *id012 + acc_info_fields: *id003 + network_subgraph: *id004 + omen_subgraph: *id005 + randomness_api: *id006 + mech_response: *id007 + agent_tools: *id008 + trades_subgraph: *id009 + conditional_tokens_subgraph: *id010 + realitio_subgraph: *id011 --- public_id: valory/ledger:0.19.0 type: connection diff --git a/packages/valory/services/trader_pearl/service.yaml b/packages/valory/services/trader_pearl/service.yaml index 5f66e4f83..c21f1c0e5 100644 --- a/packages/valory/services/trader_pearl/service.yaml +++ b/packages/valory/services/trader_pearl/service.yaml @@ -8,7 +8,7 @@ license: Apache-2.0 fingerprint: README.md: bafybeibg7bdqpioh4lmvknw3ygnllfku32oca4eq5pqtvdrdsgw6buko7e fingerprint_ignore_patterns: [] -agent: valory/trader:0.1.0:bafybeib3y24ugudzpcgrqxfpuuhfihj2hzo4yx3am2gnel2zm4xlem237a +agent: valory/trader:0.1.0:bafybeig7ax6j675ukhjdlobd5lcrujckvjnrnekjsxlchih6onqsjasfze number_of_agents: 1 deployment: agent: @@ -155,6 +155,8 @@ models: tool_quarantine_duration: ${TOOL_QUARANTINE_DURATION:int:18000} default_chain_id: ${DEFAULT_CHAIN_ID:str:gnosis} mech_interact_round_timeout_seconds: ${MECH_INTERACT_ROUND_TIMEOUT_SECONDS:int:5400} + olas_token_address: ${OLAS_TOKEN_ADDRESS:str:0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f} + http_handler_hostname_regex: ${HTTP_HANDLER_HOSTNAME_REGEX:str:localhost} benchmark_tool: args: log_dir: ${LOG_DIR:str:/benchmarks} diff --git a/packages/valory/skills/check_stop_trading_abci/behaviours.py b/packages/valory/skills/check_stop_trading_abci/behaviours.py index f1eac99e8..8efe0b5c2 100644 --- a/packages/valory/skills/check_stop_trading_abci/behaviours.py +++ b/packages/valory/skills/check_stop_trading_abci/behaviours.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # -# Copyright 2024 Valory AG +# Copyright 2024-2025 Valory AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -57,6 +57,7 @@ class CheckStopTradingBehaviour(StakingInteractBaseBehaviour): def __init__(self, **kwargs: Any) -> None: """Initialize the behaviour.""" super().__init__(**kwargs) + self._mech_requests_since_last_cp: int = 0 self._mech_request_count: int = 0 @property @@ -69,6 +70,16 @@ def mech_request_count(self, mech_request_count: int) -> None: """Set the liveness period.""" self._mech_request_count = mech_request_count + @property + def mech_requests_since_last_cp(self) -> int: + """Get the mech requests since last checkpoint.""" + return self._mech_requests_since_last_cp + + @mech_requests_since_last_cp.setter + def mech_requests_since_last_cp(self, mech_requests_since_last_cp: int) -> None: + """Set the mech requests since last checkpoint.""" + self._mech_requests_since_last_cp = mech_requests_since_last_cp + def _get_mech_request_count(self) -> WaitableConditionType: """Get the mech request count.""" status = yield from self.contract_interact( @@ -89,6 +100,7 @@ def is_first_period(self) -> bool: @property def params(self) -> CheckStopTradingParams: """Return the params.""" + return cast(CheckStopTradingParams, self.context.params) def is_staking_kpi_met(self) -> Generator[None, None, bool]: @@ -96,6 +108,7 @@ def is_staking_kpi_met(self) -> Generator[None, None, bool]: yield from self.wait_for_condition_with_sleep(self._check_service_staked) self.context.logger.debug(f"{self.service_staking_state=}") if self.service_staking_state != StakingState.STAKED: + self.mech_requests_since_last_cp = 0 return False yield from self.wait_for_condition_with_sleep(self._get_mech_request_count) @@ -118,10 +131,10 @@ def is_staking_kpi_met(self) -> Generator[None, None, bool]: liveness_ratio = self.liveness_ratio self.context.logger.debug(f"{liveness_ratio=}") - mech_requests_since_last_cp = ( + self.mech_requests_since_last_cp = ( mech_request_count - mech_request_count_on_last_checkpoint ) - self.context.logger.debug(f"{mech_requests_since_last_cp=}") + self.context.logger.debug(f"{self.mech_requests_since_last_cp=}") current_timestamp = self.synced_timestamp self.context.logger.debug(f"{current_timestamp=}") @@ -136,7 +149,7 @@ def is_staking_kpi_met(self) -> Generator[None, None, bool]: ) self.context.logger.debug(f"{required_mech_requests=}") - if mech_requests_since_last_cp >= required_mech_requests: + if self.mech_requests_since_last_cp >= required_mech_requests: return True return False @@ -166,10 +179,15 @@ def _compute_stop_trading(self) -> Generator[None, None, bool]: def async_act(self) -> Generator: """Do the action.""" + print("HERE") with self.context.benchmark_tool.measure(self.behaviour_id).local(): stop_trading = yield from self._compute_stop_trading() self.context.logger.info(f"Computed {stop_trading=}") - payload = CheckStopTradingPayload(self.context.agent_address, stop_trading) + payload = CheckStopTradingPayload( + self.context.agent_address, + stop_trading, + self.mech_requests_since_last_cp, + ) with self.context.benchmark_tool.measure(self.behaviour_id).consensus(): yield from self.send_a2a_transaction(payload) diff --git a/packages/valory/skills/check_stop_trading_abci/payloads.py b/packages/valory/skills/check_stop_trading_abci/payloads.py index 5831cff87..4f87a9873 100644 --- a/packages/valory/skills/check_stop_trading_abci/payloads.py +++ b/packages/valory/skills/check_stop_trading_abci/payloads.py @@ -20,6 +20,7 @@ """This module contains the transaction payloads for the check stop trading abci.""" from dataclasses import dataclass +from typing import Optional from packages.valory.skills.abstract_round_abci.base import BaseTxPayload @@ -29,3 +30,4 @@ class CheckStopTradingPayload(BaseTxPayload): """A transaction payload for the check stop trading abci.""" vote: bool + n_mech_requests_this_epoch: Optional[int] diff --git a/packages/valory/skills/check_stop_trading_abci/rounds.py b/packages/valory/skills/check_stop_trading_abci/rounds.py index 21b93e89b..b36bb9fc7 100644 --- a/packages/valory/skills/check_stop_trading_abci/rounds.py +++ b/packages/valory/skills/check_stop_trading_abci/rounds.py @@ -61,6 +61,7 @@ def _get_deserialized(self, key: str) -> DeserializedCollection: serialized = self.db.get_strict(key) return CollectionRound.deserialize_collection(serialized) + @property def is_staking_kpi_met(self) -> bool: """Get the status of the staking kpi.""" return bool(self.db.get("is_staking_kpi_met", False)) @@ -77,6 +78,15 @@ class CheckStopTradingRound(VotingRound): no_majority_event = Event.NO_MAJORITY collection_key = get_name(SynchronizedData.participant_to_votes) + @property + def n_mech_requests_this_epoch(self) -> int: + """Get the mech request count from the payload.""" + payload = self.payloads[0] + + if not hasattr(payload, "n_mech_requests_this_epoch"): + return 0 + return payload.n_mech_requests_this_epoch + def end_block(self) -> Optional[Tuple[BaseSynchronizedData, Enum]]: """Process the end of the block.""" res = super().end_block() @@ -85,7 +95,12 @@ def end_block(self) -> Optional[Tuple[BaseSynchronizedData, Enum]]: return None is_staking_kpi_met = self.positive_vote_threshold_reached - self.synchronized_data.update(is_staking_kpi_met=is_staking_kpi_met) + n_mech_requests_this_epoch = self.n_mech_requests_this_epoch + + self.synchronized_data.update( + is_staking_kpi_met=is_staking_kpi_met, + n_mech_requests_this_epoch=n_mech_requests_this_epoch, + ) return res diff --git a/packages/valory/skills/check_stop_trading_abci/skill.yaml b/packages/valory/skills/check_stop_trading_abci/skill.yaml index 39c227653..fee60b604 100644 --- a/packages/valory/skills/check_stop_trading_abci/skill.yaml +++ b/packages/valory/skills/check_stop_trading_abci/skill.yaml @@ -8,18 +8,18 @@ aea_version: '>=1.0.0, <2.0.0' fingerprint: README.md: bafybeif2pq7fg5upl6vmfgfzpiwsh4nbk4zaeyz6upyucqi5tasrxgq4ee __init__.py: bafybeifc23rlw2hzhplp3wfceixnmwq5ztnixhh7jp4dd5av3crwp3x22a - behaviours.py: bafybeie5mkpxsd6z3vjsoacvswin6zz4q4um5gqp6jhwtn65fepx2kma3m + behaviours.py: bafybeibd4vxdag53jq6ww7pwztzokjwa56cvimccwtobh6arispgjn5zgm dialogues.py: bafybeictrrnwcijiejczy23dfvbx5kujgef3dulzqhs3etl2juvz5spm2e fsm_specification.yaml: bafybeihhau35a5xclncjpxh5lg7qiw34xs4d5qlez7dnjpkf45d3gc57ai handlers.py: bafybeiard64fwxib3rtyp67ymhf222uongcyqhfhdyttpsyqkmyh5ajipu models.py: bafybeigwdhgianx5rizlb7ebmm6pdtkixh4uehbvu5c24ysvyvojs74dfq - payloads.py: bafybeidh5bqywun4chrbsci2xbcrnnzuys5sswxwbxq3yl2ksawi3xsi5q - rounds.py: bafybeift7b2afck4e5so2cpgyoywa76t6el6d4qwfoitvfdjw6kgf4fwie + payloads.py: bafybeifcwsf3pkkm5wk5nbhotubdoaz2ppcx2uxn7gtzqgz63ldjk6gbwm + rounds.py: bafybeib3e4qggah6cmf4oaqcjx3z7jlzgyaklwqtvwt7xebp7ss7kd3pzi tests/__init__.py: bafybeihv2cjk4va5bc5ncqtppqg2xmmxcro34bma36trtvk32gtmhdycxu tests/test_dialogues.py: bafybeia5ac27w7ijx2nyx5dqyrnv4troo4572gjq7nrcxdncexoxucnqti tests/test_handlers.py: bafybeigpmtx2hyunzn6nxk2x4bvvybek7jvuhbk34fqlj7fgfsszcoqhxy - tests/test_payloads.py: bafybeih7q7kdfxsf4ejxxqwjumwglfwwcrbqcjnuy42mkhnfwccxuhiviy - tests/test_rounds.py: bafybeidgbc7mi7r2fpk7ak6xceohuoq2zkpkberkokcb3sb2uzwkxoluae + tests/test_payloads.py: bafybeid5x5zu7bv2hp7z2hh4d43udglqdw6bjfs34sap46qoe4azmyxnry + tests/test_rounds.py: bafybeihglyjeg6pgxzro2eqk27e736l2qwiqe24ema2u2bexxyuopaez4e fingerprint_ignore_patterns: [] connections: [] contracts: @@ -27,7 +27,7 @@ contracts: protocols: [] skills: - valory/abstract_round_abci:0.1.0:bafybeigjddhk7epta7xpnfvv426xedff5abh4xlkwi6cqgp4vkutgkvydm -- valory/staking_abci:0.1.0:bafybeiaasclr4lf3u2layekmfwyasckxrslbs2g4me7kvozz5goswlznjq +- valory/staking_abci:0.1.0:bafybeicc4iactsgkdpbbqqih6pm4tedajxf7b2axgmptm4aecisb6gh4ei behaviours: main: args: {} diff --git a/packages/valory/skills/check_stop_trading_abci/tests/test_payloads.py b/packages/valory/skills/check_stop_trading_abci/tests/test_payloads.py index de53b3995..1f14d036d 100644 --- a/packages/valory/skills/check_stop_trading_abci/tests/test_payloads.py +++ b/packages/valory/skills/check_stop_trading_abci/tests/test_payloads.py @@ -26,8 +26,11 @@ def test_check_stop_trading_payload() -> None: """Test `CheckStopTradingPayload`.""" - payload = CheckStopTradingPayload(sender="sender", vote=True) + payload = CheckStopTradingPayload( + sender="sender", vote=True, n_mech_requests_this_epoch=1 + ) assert payload.vote - assert payload.data == {"vote": True} + assert payload.n_mech_requests_this_epoch + assert payload.data == {"vote": True, "mech_request_count": 1} assert CheckStopTradingPayload.from_json(payload.json) == payload diff --git a/packages/valory/skills/check_stop_trading_abci/tests/test_rounds.py b/packages/valory/skills/check_stop_trading_abci/tests/test_rounds.py index 8967831fa..a849aa810 100644 --- a/packages/valory/skills/check_stop_trading_abci/tests/test_rounds.py +++ b/packages/valory/skills/check_stop_trading_abci/tests/test_rounds.py @@ -84,7 +84,9 @@ def get_participant_to_votes( """participant_to_votes""" return { - participant: CheckStopTradingPayload(sender=participant, vote=vote) + participant: CheckStopTradingPayload( + sender=participant, vote=vote, n_mech_requests_this_epoch=1 + ) for participant in participants } @@ -102,10 +104,11 @@ def get_participant_to_votes_serialized( def get_payloads( payload_cls: Type[CheckStopTradingPayload], data: Optional[str], + mech_request_count: int, ) -> Mapping[str, CheckStopTradingPayload]: """Get payloads.""" return { - participant: payload_cls(participant, data is not None) + participant: payload_cls(participant, data is not None, mech_request_count) for participant in get_participants() } @@ -198,6 +201,7 @@ class TestCheckStopTradingRound(BaseCheckStopTradingRoundTest): payloads=get_payloads( payload_cls=CheckStopTradingPayload, data=get_dummy_check_stop_trading_payload_serialized(), + mech_request_count=1, ), final_data={}, event=Event.SKIP_TRADING, @@ -217,6 +221,7 @@ class TestCheckStopTradingRound(BaseCheckStopTradingRoundTest): payloads=get_payloads( payload_cls=CheckStopTradingPayload, data=get_dummy_check_stop_trading_payload_serialized(), + mech_request_count=1, ), final_data={}, event=Event.NO_MAJORITY, diff --git a/packages/valory/skills/decision_maker_abci/behaviours/bet_placement.py b/packages/valory/skills/decision_maker_abci/behaviours/bet_placement.py index dc4844aba..6615973be 100644 --- a/packages/valory/skills/decision_maker_abci/behaviours/bet_placement.py +++ b/packages/valory/skills/decision_maker_abci/behaviours/bet_placement.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # -# Copyright 2023-2024 Valory AG +# Copyright 2023-2025 Valory AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -234,6 +234,7 @@ def async_act(self) -> Generator: tx_submitter = self.matching_round.auto_round_id() betting_tx_hex = yield from self._prepare_safe_tx() wallet_balance = self.wallet_balance + token_balance = self.token_balance payload = BetPlacementPayload( agent, @@ -241,6 +242,7 @@ def async_act(self) -> Generator: betting_tx_hex, mocking_mode, wallet_balance, + token_balance, ) yield from self.finish_behaviour(payload) diff --git a/packages/valory/skills/decision_maker_abci/behaviours/decision_request.py b/packages/valory/skills/decision_maker_abci/behaviours/decision_request.py index b887fb660..1d0e3271b 100644 --- a/packages/valory/skills/decision_maker_abci/behaviours/decision_request.py +++ b/packages/valory/skills/decision_maker_abci/behaviours/decision_request.py @@ -93,9 +93,11 @@ def async_act(self) -> Generator: with self.context.benchmark_tool.measure(self.behaviour_id).local(): payload_content = None mocking_mode: Optional[bool] = self.benchmarking_mode.enabled + decision_request_timestamp = None if self._metadata and self.n_slots_supported: mech_requests = [self.metadata] payload_content = json.dumps(mech_requests, sort_keys=True) + decision_request_timestamp = self.synced_timestamp if not self.n_slots_supported: mocking_mode = None @@ -106,5 +108,7 @@ def async_act(self) -> Generator: self.shared_state.bet_id_row_manager = bets_mapping agent = self.context.agent_address - payload = DecisionRequestPayload(agent, payload_content, mocking_mode) + payload = DecisionRequestPayload( + agent, payload_content, mocking_mode, decision_request_timestamp + ) yield from self.finish_behaviour(payload) diff --git a/packages/valory/skills/decision_maker_abci/handlers.py b/packages/valory/skills/decision_maker_abci/handlers.py index e6e64a2bb..4f2fe1da9 100644 --- a/packages/valory/skills/decision_maker_abci/handlers.py +++ b/packages/valory/skills/decision_maker_abci/handlers.py @@ -26,7 +26,9 @@ from typing import Any, Callable, Dict, Optional, Tuple, cast from urllib.parse import urlparse +import prometheus_client from aea.protocols.base import Message +from prometheus_client import CollectorRegistry, Gauge, generate_latest from packages.valory.connections.http_server.connection import ( PUBLIC_ID as HTTP_SERVER_PUBLIC_ID, @@ -52,6 +54,9 @@ from packages.valory.skills.abstract_round_abci.handlers import ( TendermintHandler as BaseTendermintHandler, ) +from packages.valory.skills.decision_maker_abci.behaviours.base import ( + DecisionMakerBaseBehaviour, +) from packages.valory.skills.decision_maker_abci.dialogues import ( HttpDialogue, HttpDialogues, @@ -127,6 +132,19 @@ def __init__(self, **kwargs: Any) -> None: self.routes: Dict[tuple, list] = {} self.json_content_header: str = "" self.rounds_info: Dict = {} + self._time_since_last_successful_mech_tx: int = 0 + + @property + def last_successful_mech_tx_ts(self) -> int: + """Get the time since the last successful mech response in seconds.""" + return self._time_since_last_successful_mech_tx + + @last_successful_mech_tx_ts.setter + def last_successful_mech_tx_ts( + self, time_since_last_successful_mech_tx: int + ) -> None: + """Set the time since the last successful mech response in seconds.""" + self._time_since_last_successful_mech_tx = time_since_last_successful_mech_tx def setup(self) -> None: """Implement the setup.""" @@ -141,14 +159,17 @@ def setup(self) -> None: local_ip_regex = r"192\.168(\.\d{1,3}){2}" # Route regexes - hostname_regex = rf".*({config_uri_base_hostname}|{propel_uri_base_hostname}|{local_ip_regex}|localhost|127.0.0.1|0.0.0.0)(:\d+)?" - self.handler_url_regex = rf"{hostname_regex}\/.*" - health_url_regex = rf"{hostname_regex}\/healthcheck" + self.hostname_regex = rf".*({config_uri_base_hostname}|{propel_uri_base_hostname}|{local_ip_regex}|localhost\ + |127.0.0.1|0.0.0.0|{self.context.params.http_handler_hostname_regex})(:\d+)?" + self.handler_url_regex = rf"{self.hostname_regex}\/.*" + health_url_regex = rf"{self.hostname_regex}\/healthcheck" + metrics_url_regex = rf"{self.hostname_regex}\/metrics" # Routes self.routes = { (HttpMethod.GET.value, HttpMethod.HEAD.value): [ (health_url_regex, self._handle_get_health), + (metrics_url_regex, self._handle_get_metrics), ], } @@ -378,3 +399,197 @@ def _check_is_receiving_mech_responses(self) -> bool: < int(datetime.utcnow().timestamp()) - self.context.params.expected_mech_response_time ) + + def _handle_get_metrics( + self, http_msg: HttpMessage, http_dialogue: HttpDialogue + ) -> None: + """Handle the /metrics endpoint.""" + + self.set_metrics() + # Generate the metrics data + metrics_data = generate_latest(REGISTRY) + + # Create a response with the metrics data + http_response = http_dialogue.reply( + performative=HttpMessage.Performative.RESPONSE, + target_message=http_msg, + version=http_msg.version, + status_code=OK_CODE, + status_text="Success", + headers=f"Content-Type: {prometheus_client.CONTENT_TYPE_LATEST}\n{http_msg.headers}", + body=metrics_data, + ) + + # Send response + self.context.logger.info("Responding with metrics data") + self.context.outbox.put_message(message=http_response) + + def set_metrics(self) -> None: + """Set the metrics.""" + + agent_address = self.context.agent_address + safe_address = self.synchronized_data.safe_contract_address + service_id = self.context.params.on_chain_service_id + service_owner_address = self.synchronized_data.service_owner_address + staking_contract_name = self.synchronized_data.staking_contract_name + staking_contract_address = self.context.params.staking_contract_address + metrics_label_values = ( + agent_address, + safe_address, + service_id, + service_owner_address, + ) + + native_balance = DecisionMakerBaseBehaviour.wei_to_native( + self.synchronized_data.wallet_balance + ) + olas_balance = DecisionMakerBaseBehaviour.wei_to_native( + self.synchronized_data.olas_balance + ) + wxdai_balance = self.synchronized_data.token_balance + staking_contract_available_slots = ( + self.synchronized_data.available_staking_slots + ) + staking_state = self.synchronized_data.service_staking_state.value + time_since_last_successful_mech_tx = ( + self.calculate_time_since_last_successful_mech_tx() + ) + time_since_last_mech_tx_attempt = ( + self.calculate_time_since_last_mech_tx_attempt() + ) + n_total_mech_requests = self.synchronized_data.n_mech_requests_this_epoch + n_mech_requests_to_staking_kpi = ( + self.calculate_remaining_mech_calls_to_staking_kpi() + ) + + NATIVE_BALANCE_GAUGE.labels(*metrics_label_values).set(native_balance) + OLAS_BALANCE_GAUGE.labels(*metrics_label_values).set(olas_balance) + WXDAI_BALANCE_GAUGE.labels(*metrics_label_values).set(wxdai_balance) + STAKING_CONTRACT_AVAILABLE_SLOTS_GAUGE.labels( + *metrics_label_values, staking_contract_name, staking_contract_address + ).set(staking_contract_available_slots) + STAKING_STATE_GAUGE.labels(*metrics_label_values).set(staking_state) + TIME_SINCE_LAST_SUCCESSFUL_MECH_TX_GAUGE.labels(*metrics_label_values).set( + time_since_last_successful_mech_tx + ) + TIME_SINCE_LAST_MECH_REQUEST_ATTEMPT_GAUGE.labels(*metrics_label_values).set( + time_since_last_mech_tx_attempt + ) + TOTAL_MECH_REQUESTS_THIS_EPOCH.labels(*metrics_label_values).set( + n_total_mech_requests + ) + REMAINING_MECH_CALLS_FOR_STAKING_KPI.labels(*metrics_label_values).set( + n_mech_requests_to_staking_kpi + ) + EPOCH_TIME_REMAINING.labels(*metrics_label_values).set( + self.calculate_epoch_time_remaining() + ) + + def calculate_time_since_last_successful_mech_tx(self) -> int: + """Calculate the time since the last successful mech transaction (mech response).""" + + mech_tx_ts = self.synchronized_data.decision_receive_timestamp + now = int(datetime.now().timestamp()) + seconds_since_last_successful_mech_tx = 0 + + if mech_tx_ts != 0: + seconds_since_last_successful_mech_tx = now - mech_tx_ts + + return seconds_since_last_successful_mech_tx + + def calculate_time_since_last_mech_tx_attempt(self) -> int: + """Calculate the time since the last attempted mech transaction (mech request).""" + + mech_tx_attempt_ts = self.synchronized_data.decision_request_timestamp + now = int(datetime.now().timestamp()) + + if mech_tx_attempt_ts == 0: + return 0 + + seconds_since_last_mech_tx_attempt = now - mech_tx_attempt_ts + return seconds_since_last_mech_tx_attempt + + def calculate_remaining_mech_calls_to_staking_kpi(self) -> int: + """Calculate the remaining mech calls needed to reach the staking KPI.""" + return ( + N_MECH_CALLS_FOR_STAKING_KPI + - self.synchronized_data.n_mech_requests_this_epoch + ) + + def calculate_epoch_time_remaining(self) -> int: + """Calculate the remaining time in the current epoch.""" + + epoch_end_ts = self.synchronized_data.epoch_end_ts + if epoch_end_ts == 0: + return 0 + return epoch_end_ts - int(datetime.now().timestamp()) + + +REGISTRY = CollectorRegistry() +N_MECH_CALLS_FOR_STAKING_KPI = 60 +PROMETHEUS_METRICS_LABELS = [ + "agent_address", + "safe_address", + "service_id", + "service_owner_address", +] +NATIVE_BALANCE_GAUGE = Gauge( + "olas_agent_native_balance", + "Native token balance in xDai", + PROMETHEUS_METRICS_LABELS, + registry=REGISTRY, +) +OLAS_BALANCE_GAUGE = Gauge( + "olas_agent_olas_balance", + "OLAS token balance", + PROMETHEUS_METRICS_LABELS, + registry=REGISTRY, +) +WXDAI_BALANCE_GAUGE = Gauge( + "olas_agent_wxdai_balance", + "WXDAI token balance", + PROMETHEUS_METRICS_LABELS, + registry=REGISTRY, +) +STAKING_CONTRACT_AVAILABLE_SLOTS_GAUGE = Gauge( + "olas_staking_contract_available_slots", + "Number of available slots in the staking contract", + PROMETHEUS_METRICS_LABELS + ["staking_contract_name", "staking_contract_address"], + registry=REGISTRY, +) +STAKING_STATE_GAUGE = Gauge( + "olas_agent_staked", + "Indicates if an agent is staked (1), not staked (0) or eviceted (2)", + PROMETHEUS_METRICS_LABELS, + registry=REGISTRY, +) +TIME_SINCE_LAST_SUCCESSFUL_MECH_TX_GAUGE = Gauge( + "olas_agent_time_since_last_successful_tx", + "Time in seconds since last successful mech transaction", + PROMETHEUS_METRICS_LABELS, + registry=REGISTRY, +) +TIME_SINCE_LAST_MECH_REQUEST_ATTEMPT_GAUGE = Gauge( + "olas_agent_time_since_last_mech_request_attempt", + "Time in seconds since last request transaction attempt (successful or not)", + PROMETHEUS_METRICS_LABELS, + registry=REGISTRY, +) +TOTAL_MECH_REQUESTS_THIS_EPOCH = Gauge( + "olas_agent_mech_requests", + "Total number of mech requests made by the agent this epoch", + PROMETHEUS_METRICS_LABELS, + registry=REGISTRY, +) +REMAINING_MECH_CALLS_FOR_STAKING_KPI = Gauge( + "olas_agent_remaining_mech_calls_for_staking_kpi", + "Remaining mech calls needed to reach the staking KPI", + PROMETHEUS_METRICS_LABELS, + registry=REGISTRY, +) +EPOCH_TIME_REMAINING = Gauge( + "olas_agent_staking_contract_epoch_time_remaining", + "Time remaining in the current epoch", + PROMETHEUS_METRICS_LABELS, + registry=REGISTRY, +) diff --git a/packages/valory/skills/decision_maker_abci/payloads.py b/packages/valory/skills/decision_maker_abci/payloads.py index b15c02b01..7b1589e44 100644 --- a/packages/valory/skills/decision_maker_abci/payloads.py +++ b/packages/valory/skills/decision_maker_abci/payloads.py @@ -23,11 +23,11 @@ from typing import Optional from packages.valory.skills.abstract_round_abci.base import BaseTxPayload -from packages.valory.skills.market_manager_abci.payloads import UpdateBetsPayload +from packages.valory.skills.market_manager_abci.payloads import BaseUpdateBetsPayload @dataclass(frozen=True) -class DecisionReceivePayload(UpdateBetsPayload): +class DecisionReceivePayload(BaseUpdateBetsPayload): """Represents a transaction payload for the decision-making.""" is_profitable: Optional[bool] @@ -40,7 +40,7 @@ class DecisionReceivePayload(UpdateBetsPayload): @dataclass(frozen=True) -class SamplingPayload(UpdateBetsPayload): +class SamplingPayload(BaseUpdateBetsPayload): """Represents a transaction payload for the sampling of a bet.""" index: Optional[int] @@ -74,6 +74,7 @@ class DecisionRequestPayload(BaseTxPayload): mech_requests: Optional[str] = None mocking_mode: Optional[bool] = None + decision_request_timestamp: Optional[int] = None @dataclass(frozen=True) @@ -99,7 +100,7 @@ class VotingPayload(BaseTxPayload): @dataclass(frozen=True) -class BlacklistingPayload(UpdateBetsPayload): +class BlacklistingPayload(BaseUpdateBetsPayload): """Represents a transaction payload for blacklisting.""" policy: str @@ -120,6 +121,7 @@ class BetPlacementPayload(MultisigTxPayload): """Represents a transaction payload for placing a bet.""" wallet_balance: Optional[int] = None + token_balance: Optional[int] = None @dataclass(frozen=True) diff --git a/packages/valory/skills/decision_maker_abci/rounds.py b/packages/valory/skills/decision_maker_abci/rounds.py index 0c9a9539b..da3596214 100644 --- a/packages/valory/skills/decision_maker_abci/rounds.py +++ b/packages/valory/skills/decision_maker_abci/rounds.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # -# Copyright 2023-2024 Valory AG +# Copyright 2023-2025 Valory AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -338,6 +338,7 @@ class DecisionMakerAbciApp(AbciApp[Event]): get_name(SynchronizedData.mocking_mode), get_name(SynchronizedData.next_mock_data_row), get_name(SynchronizedData.agreement_id), + get_name(SynchronizedData.decision_receive_timestamp), } ) final_states: Set[AppState] = { diff --git a/packages/valory/skills/decision_maker_abci/skill.yaml b/packages/valory/skills/decision_maker_abci/skill.yaml index 7292521c8..8947c545e 100644 --- a/packages/valory/skills/decision_maker_abci/skill.yaml +++ b/packages/valory/skills/decision_maker_abci/skill.yaml @@ -13,12 +13,12 @@ fingerprint: __init__.py: bafybeih563ujnigeci2ldzh7hakbau6a222vsed7leg3b7lq32vcn3nm4a behaviours/__init__.py: bafybeih6ddz2ocvm6x6ytvlbcz6oi4snb5ee5xh5h65nq4w2qf7fd7zfky behaviours/base.py: bafybeibkwfsd2atjzejhsel5p4cptd2wg2shefyly5jbiywgbjvx6k2q4y - behaviours/bet_placement.py: bafybeidyucxomadfzr6eyfywlc4satl4vqb6qhdkyzdp76zkxbfjfyzai4 + behaviours/bet_placement.py: bafybeicx67uvu2epteqent2v5kzgigjmtgadaqfhcpry5nkjbrei4sm5pi behaviours/blacklisting.py: bafybeieuqoup2vrmrtvjfqnr5mzrvkegc7afb2oeujzq2itsbhcsham2se behaviours/check_benchmarking.py: bafybeiao2lyj7apezkqrpgsyzb3dwvrdgsrgtprf6iuhsmlsufvxfl5bci behaviours/claim_subscription.py: bafybeigbqkhc6mb73rbwaks32tfiqx6u2xza43uiy6rvbtrnqd6m4fru3e behaviours/decision_receive.py: bafybeibsthk6fjydkengwzgpgz5v44xpta67nnlyxt6uugx2voitqgojpq - behaviours/decision_request.py: bafybeia22omb7tvocyfe3z2ucn5au5mcas7dg37ha42u7znefzrewjpk7y + behaviours/decision_request.py: bafybeigf7nihxq525oukz5wb2zoks3j7mzbi37w3vpp7dux47on6g3lybq behaviours/handle_failed_tx.py: bafybeiashwlfp6ty3g6ukgmliaghwu6yiunbqpjmyrzheokw3pbcr2ckaq behaviours/order_subscription.py: bafybeihwuhgykptokmeam5sacnfzedk5puvrxbjo7iypaqhsrqg5nk7psm behaviours/randomness.py: bafybeiaoj3awyyg2onhpsdsn3dyczs23gr4smuzqcbw3e5ocljwxswjkce @@ -29,29 +29,29 @@ fingerprint: behaviours/tool_selection.py: bafybeienlxcgjs3ogyofli3d7q3p5rst3mcxxcnwqf7qolqjeefjtixeke dialogues.py: bafybeigpwuzku3we7axmxeamg7vn656maww6emuztau5pg3ebsoquyfdqm fsm_specification.yaml: bafybeifvu7n6sjmrerogkzsftjrw2l6w5ppuq3f43ouj2rwbomdr5glp2e - handlers.py: bafybeieggvt2rh654s2jt2ablqtyzaycb7wlgrrdbsccf5652nptv45hie + handlers.py: bafybeiafresikc3vz4fw4qk77th4xbzensdwsi564lt456wf5snocdryba io_/__init__.py: bafybeifxgmmwjqzezzn3e6keh2bfo4cyo7y5dq2ept3stfmgglbrzfl5rq io_/loader.py: bafybeih3sdsx5dhe4kzhtoafexjgkutsujwqy3zcdrlrkhtdks45bc7exa models.py: bafybeihvmxk5nve54v3yz7uaobekdzg37675ooiqxb7ne5hmeegfhk6c6m - payloads.py: bafybeieygushjlrzwzpnhagjgpbs3goot3pnfheh6yawuwctrk3uoeesfm + payloads.py: bafybeib6tbvhtjnlxd73spnv3ogdjtps3i36c7wgzfuscvhlv26gjqwnsa policy.py: bafybeidofgwvk6sudz75tvuduskuphtn3amtib2irzw5hr3qcfn5pdwuc4 redeem_info.py: bafybeifiiix4gihfo4avraxt34sfw35v6dqq45do2drrssei2shbps63mm - rounds.py: bafybeiftrpaxyyly3d36kvfg2c2m5fmzchm6n4vv5losu3jr2b7s5ces6a + rounds.py: bafybeidhblcwk3secxsxkm3bub6svfvbafjucbxensiumf4hohj6tkya3u rounds_info.py: bafybeihg6i3h7a7ahfxzhow7gcuszcilq5krfpchze2szjdu7dtem2tnwa states/__init__.py: bafybeid23llnyp6j257dluxmrnztugo5llsrog7kua53hllyktz4dqhqoy - states/base.py: bafybeiglqvym3ri6hurx4k7hrnykzbmslxe3vuj23djt6hai4czii4vbqq - states/bet_placement.py: bafybeih5eopyxubczys5u5t3bdxbxpc7mmfdyqrpqsbm2uha5jc2phza4i - states/blacklisting.py: bafybeiapelgjhbjjn4uq4z5gspyirqzwzgccg5anktrp5kxdwamfnfw5mi + states/base.py: bafybeie3xqc6pazplnhwvdmv7a7bwdzvot4b4vqactameui6d5zdvbxwie + states/bet_placement.py: bafybeid6ffwooqeb2uj3f5t2iy7ffoeltoyc76jqzn6x4yjezgvofnsasm + states/blacklisting.py: bafybeidqi356wrxmvj2hqs22hw3kaszh4fg4rs3fo5axk2biosxt6kfbiy states/check_benchmarking.py: bafybeiagrgeopuluwqkvoqbst2hjymte5rju2pecvkoeleklnqca5andeu states/claim_subscription.py: bafybeiefuagbf27cz3ntljmpwr3snkytfh7qht4x5c3a2fivxoijrapd5m - states/decision_receive.py: bafybeiexc26g7z7by6eziawjld52nglljiwlj6oam5ramtbyo6un2tqs5y - states/decision_request.py: bafybeiarv3r5j7cfvxmudki2llbdl2pvf24p5mvsva6bdgrylnwdyag5xy + states/decision_receive.py: bafybeibktcd4clpfyrb3lsoa7bvmpoloyqdhw5eh5fjnno5duaoid55pse + states/decision_request.py: bafybeig6l3l6ulv7alhwfxjlzz53i6g5gw3l5vumivadqstnmqoupwmaea states/final_states.py: bafybeicjrrojo3gmfaxzicwloyorlnqgzl6a2avevo4nvhoh424zwzmbti states/handle_failed_tx.py: bafybeie5gkexscpdpiwz5acflxz2lbfitvs5qd6u3svpmwpgx4viqta3xy states/order_subscription.py: bafybeihl3pwrbccaitiukbigygd5u3weyih34pvzql3c6n5k7gjj47f2be states/randomness.py: bafybeiceoo4nx3t4dofpwczw3v5mclramwmzpwjs6hv7l56arodrjx4l5u states/redeem.py: bafybeica6cn4xg7shea2wjhbqnddgxe5zao2hkmceltze7qknxdhtsoaxe - states/sampling.py: bafybeif2yuwl5swelp7oh5nfuupdf3vg2ijjzapk2xqht7e6i6ggcsl2zy + states/sampling.py: bafybeicrmisa2tze4bhxhi2sxzrlzji2rucyh7qdtoncfk5icbs4sm2pbq states/tool_selection.py: bafybeiak5ihuie4nxh3sguiea6pcdgyxr4k4xyzvq6o2uj5xpf7urocawy tests/__init__.py: bafybeiakpi3k3kc7wrjj7hrluvjcj36lu2gezpmrctwiz5yg2fe7ggnf3i tests/behaviours/__init__.py: bafybeic7icz7lfhfepdkqkase7y7zn3a6pwdw6fx4ah2hajmgejawpolc4 @@ -62,21 +62,21 @@ fingerprint: tests/conftest.py: bafybeidy5hw56kw5mxudnfbhvogofn6k4rqb4ux2bd45baedrrhmgyrude tests/states/test_base.py: bafybeieqy7wz5677jathwnolsgrt7zdifauammly3aeoq6dk4hdsqo5fte tests/states/test_bet_placement.py: bafybeibvc37n2cluep4tasvgmvwxwne2deais6ptirducpogk67v4gj4ga - tests/states/test_blacklising.py: bafybeihm2ex6l7fhorgi3mjj2epztu2r7bqbg56unpgpzfzymghshchqzy + tests/states/test_blacklising.py: bafybeifcsv7v34qi75vx5kw6w72wdjfxyq7vjcfm2oejqgpxitlnk7athy tests/states/test_check_benchmarking.py: bafybeiaauepn46l5z5kx2ifzsqcuravg3zvddecuhdgp4eaeous6vaqyoq tests/states/test_claim_subscription.py: bafybeiclkxjhceb3ehgmg6klt4uywew5drk5b3w6no7mwxetpubxqrejfy - tests/states/test_decision_receive.py: bafybeibkxalkfxuyokb6y5hkyu4pdlg4yfusbpgtkfr66cprygu7cgyd2y + tests/states/test_decision_receive.py: bafybeieshzx2nyjj6rbwkboku3paqismypof64vb6zwt7bhjvspeyu5kae tests/states/test_decision_request.py: bafybeigqbakm2olkwvcngertjplhnmu6on6tp6hxn7lxygi2gf5a5eurbe tests/states/test_final_states.py: bafybeiftfd3ovaqpfe7t5ry7maiziavk74wl66d6zo6ikhgodznormd2nm tests/states/test_handle_failed_tx.py: bafybeigt3kae7werxaleozidsfzpoxrnvmaamp3hidmfci65ulxnbjjleu tests/states/test_order_subscription.py: bafybeidx2tzivsxhpr5xx5e5h2xmpjyewfogt2mujv4sq3hbaeksmcbvhy tests/states/test_randomness.py: bafybeib3eqjv6mhlprzda7d4viddn5alrfqteq6juyg3ccejseoywcsbey tests/states/test_redeem.py: bafybeiezdnfrxukb2xpwffrr357g2anmdkwy7wo3nphvlggipq5xrdzr7a - tests/states/test_sampling.py: bafybeifvbzikke6wtex2p5j7fsnpdbj4qqxl5vh2lm2m2apgvuqdonoyzm + tests/states/test_sampling.py: bafybeigefevj5ncbey2ynoctifqsmw6fa6ybhojoo4ah3derfgcwjqpwoy tests/states/test_tool_selection.py: bafybeib7js3dj7647t33o5ybfqftwytxktwrvhbri5yuyymg6znj6y7xxa tests/test_dialogues.py: bafybeibulo64tgfrq4e5qbcqnmifrlehkqciwuavublints353zaj2mlpa - tests/test_handlers.py: bafybeihpkgtjjm3uegpup6zkznpoaxqpu6kmp3ujiggrzbe73p5fzlq7im - tests/test_payloads.py: bafybeiggbcppj4j54r23qvg423elsnd7dcxl3sfo534ek5sd3g65ua57nq + tests/test_handlers.py: bafybeibpmso4geiw6n72lteqiqwjnkaux7rlkh7z3i2teuo2x4ajt3izye + tests/test_payloads.py: bafybeiejpbnrvcwjed3baqyu6h52ex4cglmlsz75j5jwr5yfarzaosogeu tests/test_rounds.py: bafybeigifftusd4ew42tyvyrr55o2uehhcik2gdq3atkpjwwlqdeskedty utils/__init__.py: bafybeiazrfg3kwfdl5q45azwz6b6mobqxngxpf4hazmrnkhinpk4qhbbf4 utils/nevermined.py: bafybeigallaqxhqopznhjhefr6bukh4ojkz5vdtqyzod5dksshrf24fjgi @@ -102,10 +102,10 @@ protocols: - valory/http:1.0.0:bafybeifugzl63kfdmwrxwphrnrhj7bn6iruxieme3a4ntzejf6kmtuwmae skills: - valory/abstract_round_abci:0.1.0:bafybeigjddhk7epta7xpnfvv426xedff5abh4xlkwi6cqgp4vkutgkvydm -- valory/market_manager_abci:0.1.0:bafybeiayhzwxlpqsevyvaxn6dtnnu745az5vcz7dckmghnmjywxdtvoyly +- valory/market_manager_abci:0.1.0:bafybeihzfnnogprkz2m3vlbzndzrfwjcxk5i35h2vefjhguhf2gfkkcesy - valory/transaction_settlement_abci:0.1.0:bafybeifmgmwdkx4esemxjacjwzqkqymkuklb5nehkwqkx7v335fllgswcq - valory/mech_interact_abci:0.1.0:bafybeib4vn6m2yumwoclh5aatcdt5yxcjc5owxmxy5o7t3nfzormgwkr64 -- valory/staking_abci:0.1.0:bafybeiaasclr4lf3u2layekmfwyasckxrslbs2g4me7kvozz5goswlznjq +- valory/staking_abci:0.1.0:bafybeicc4iactsgkdpbbqqih6pm4tedajxf7b2axgmptm4aecisb6gh4ei behaviours: main: args: {} @@ -425,4 +425,6 @@ dependencies: version: ==4.0.0 pyyaml: version: <=6.0.1,>=3.10 + prometheus_client: + version: ==0.21.1 is_abstract: true diff --git a/packages/valory/skills/decision_maker_abci/states/base.py b/packages/valory/skills/decision_maker_abci/states/base.py index 421eec4c9..8339c9612 100644 --- a/packages/valory/skills/decision_maker_abci/states/base.py +++ b/packages/valory/skills/decision_maker_abci/states/base.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # -# Copyright 2023-2024 Valory AG +# Copyright 2023-2025 Valory AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -39,8 +39,8 @@ MechMetadata, ) from packages.valory.skills.staking_abci.rounds import StakingState -from packages.valory.skills.transaction_settlement_abci.rounds import ( - SynchronizedData as TxSettlementSyncedData, +from packages.valory.skills.staking_abci.rounds import ( + SynchronizedData as StakingSyncedData, ) @@ -70,7 +70,7 @@ class Event(Enum): NEW_SIMULATED_RESAMPLE = "new_simulated_resample" -class SynchronizedData(MarketManagerSyncedData, TxSettlementSyncedData): +class SynchronizedData(MarketManagerSyncedData, StakingSyncedData): """Class to represent the synchronized data. This data is replicated by the tendermint application. @@ -252,14 +252,6 @@ def mech_responses(self) -> List[MechInteractionResponse]: responses = json.loads(serialized) return [MechInteractionResponse(**response_item) for response_item in responses] - @property - def wallet_balance(self) -> int: - """Get the balance of the wallet.""" - wallet_balance = self.db.get("wallet_balance", 0) - if wallet_balance is None: - return 0 - return int(wallet_balance) - @property def decision_receive_timestamp(self) -> int: """Get the timestamp of the mech decision.""" @@ -268,6 +260,14 @@ def decision_receive_timestamp(self) -> int: return 0 return int(decision_receive_timestamp) + @property + def decision_request_timestamp(self) -> int: + """Get the timestamp of the mech request.""" + decision_request_timestamp = self.db.get("decision_request_timestamp", 0) + if decision_request_timestamp is None: + return 0 + return int(decision_request_timestamp) + @property def is_staking_kpi_met(self) -> bool: """Get the status of the staking kpi.""" @@ -278,11 +278,26 @@ def service_staking_state(self) -> StakingState: """Get the service's staking state.""" return StakingState(self.db.get("service_staking_state", 0)) + @property + def n_mech_requests_this_epoch(self) -> int: + """Get the number of mech requests.""" + n_mech_requests_this_epoch = self.db.get("n_mech_requests_this_epoch", 0) + if n_mech_requests_this_epoch is None: + return 0 + return n_mech_requests_this_epoch + @property def after_bet_attempt(self) -> bool: """Get the service's staking state.""" return bool(self.db.get("after_bet_attempt", False)) + @property + def staking_contract_name(self) -> str: + """Get the staking contract name.""" + return str( + self.db.get("staking_contract_name", default="No staking contract name") + ) + class TxPreparationRound(CollectSameUntilThresholdRound): """A round for preparing a transaction.""" diff --git a/packages/valory/skills/decision_maker_abci/states/bet_placement.py b/packages/valory/skills/decision_maker_abci/states/bet_placement.py index 35e7c63f1..1bdcd8b7b 100644 --- a/packages/valory/skills/decision_maker_abci/states/bet_placement.py +++ b/packages/valory/skills/decision_maker_abci/states/bet_placement.py @@ -46,6 +46,9 @@ def end_block(self) -> Optional[Tuple[BaseSynchronizedData, Enum]]: return None sync_data, event = update - wallet_balance = self.most_voted_payload_values[-1] - sync_data = sync_data.update(wallet_balance=wallet_balance) + wallet_balance = self.most_voted_payload_values[-2] + token_balance = self.most_voted_payload_values[-1] + sync_data = sync_data.update( + wallet_balance=wallet_balance, token_balance=token_balance + ) return sync_data, event diff --git a/packages/valory/skills/decision_maker_abci/states/blacklisting.py b/packages/valory/skills/decision_maker_abci/states/blacklisting.py index d2a714583..034671934 100644 --- a/packages/valory/skills/decision_maker_abci/states/blacklisting.py +++ b/packages/valory/skills/decision_maker_abci/states/blacklisting.py @@ -31,19 +31,19 @@ Event, SynchronizedData, ) -from packages.valory.skills.market_manager_abci.payloads import UpdateBetsPayload -from packages.valory.skills.market_manager_abci.rounds import UpdateBetsRound +from packages.valory.skills.market_manager_abci.payloads import BaseUpdateBetsPayload +from packages.valory.skills.market_manager_abci.rounds import BaseUpdateBetsRound -class BlacklistingRound(UpdateBetsRound): +class BlacklistingRound(BaseUpdateBetsRound): """A round for updating the bets after blacklisting the sampled one.""" - payload_class: Type[UpdateBetsPayload] = BlacklistingPayload + payload_class: Type[BaseUpdateBetsPayload] = BlacklistingPayload done_event = Event.DONE none_event = Event.NONE no_majority_event = Event.NO_MAJORITY selection_key: Any = ( - UpdateBetsRound.selection_key, + get_name(SynchronizedData.bets_hash), get_name(SynchronizedData.policy), ) diff --git a/packages/valory/skills/decision_maker_abci/states/decision_receive.py b/packages/valory/skills/decision_maker_abci/states/decision_receive.py index 282e48c08..144d1ec56 100644 --- a/packages/valory/skills/decision_maker_abci/states/decision_receive.py +++ b/packages/valory/skills/decision_maker_abci/states/decision_receive.py @@ -31,7 +31,6 @@ Event, SynchronizedData, ) -from packages.valory.skills.market_manager_abci.rounds import UpdateBetsRound class DecisionReceiveRound(CollectSameUntilThresholdRound): @@ -43,7 +42,7 @@ class DecisionReceiveRound(CollectSameUntilThresholdRound): none_event = Event.MECH_RESPONSE_ERROR no_majority_event = Event.NO_MAJORITY selection_key: Any = ( - UpdateBetsRound.selection_key, + get_name(SynchronizedData.bets_hash), get_name(SynchronizedData.is_profitable), get_name(SynchronizedData.vote), get_name(SynchronizedData.confidence), diff --git a/packages/valory/skills/decision_maker_abci/states/decision_request.py b/packages/valory/skills/decision_maker_abci/states/decision_request.py index 5c4df3e9b..678995853 100644 --- a/packages/valory/skills/decision_maker_abci/states/decision_request.py +++ b/packages/valory/skills/decision_maker_abci/states/decision_request.py @@ -45,6 +45,7 @@ class DecisionRequestRound(CollectSameUntilThresholdRound): selection_key = ( get_name(SynchronizedData.mech_requests), get_name(SynchronizedData.mocking_mode), + get_name(SynchronizedData.decision_request_timestamp), ) none_event = Event.SLOTS_UNSUPPORTED_ERROR diff --git a/packages/valory/skills/decision_maker_abci/states/sampling.py b/packages/valory/skills/decision_maker_abci/states/sampling.py index dbbf08461..3aeba03f0 100644 --- a/packages/valory/skills/decision_maker_abci/states/sampling.py +++ b/packages/valory/skills/decision_maker_abci/states/sampling.py @@ -31,19 +31,19 @@ Event, SynchronizedData, ) -from packages.valory.skills.market_manager_abci.payloads import UpdateBetsPayload -from packages.valory.skills.market_manager_abci.rounds import UpdateBetsRound +from packages.valory.skills.market_manager_abci.payloads import BaseUpdateBetsPayload +from packages.valory.skills.market_manager_abci.rounds import BaseUpdateBetsRound -class SamplingRound(UpdateBetsRound): +class SamplingRound(BaseUpdateBetsRound): """A round for sampling a bet.""" - payload_class: Type[UpdateBetsPayload] = SamplingPayload + payload_class: Type[BaseUpdateBetsPayload] = SamplingPayload done_event = Event.DONE none_event = Event.NONE no_majority_event = Event.NO_MAJORITY selection_key: Any = ( - UpdateBetsRound.selection_key, + get_name(SynchronizedData.bets_hash), get_name(SynchronizedData.sampled_bet_index), get_name(SynchronizedData.benchmarking_finished), get_name(SynchronizedData.simulated_day), diff --git a/packages/valory/skills/decision_maker_abci/tests/states/test_blacklising.py b/packages/valory/skills/decision_maker_abci/tests/states/test_blacklising.py index 02b7dba0f..835684890 100644 --- a/packages/valory/skills/decision_maker_abci/tests/states/test_blacklising.py +++ b/packages/valory/skills/decision_maker_abci/tests/states/test_blacklising.py @@ -30,7 +30,7 @@ from packages.valory.skills.decision_maker_abci.states.blacklisting import ( BlacklistingRound, ) -from packages.valory.skills.market_manager_abci.rounds import UpdateBetsRound +from packages.valory.skills.market_manager_abci.rounds import BaseUpdateBetsRound @pytest.fixture @@ -76,7 +76,7 @@ def test_blacklisting_round_end_block_done_event_no_benchmarking( # Mock the superclass end_block to return DONE event synced_data = MagicMock(spec=SynchronizedData) with patch.object( - UpdateBetsRound, "end_block", return_value=(synced_data, Event.DONE) + BaseUpdateBetsRound, "end_block", return_value=(synced_data, Event.DONE) ) as mock_super_end_block: result = blacklisting_round.end_block() @@ -95,7 +95,7 @@ def test_blacklisting_round_end_block_done_event_with_benchmarking( # Mock the superclass end_block to return DONE event synced_data = MagicMock(spec=SynchronizedData) with patch.object( - UpdateBetsRound, "end_block", return_value=(synced_data, Event.DONE) + BaseUpdateBetsRound, "end_block", return_value=(synced_data, Event.DONE) ) as mock_super_end_block: result = blacklisting_round.end_block() @@ -113,7 +113,7 @@ def test_blacklisting_round_end_block_none_event( """Test end_block when the superclass returns None.""" # Mock the superclass end_block to return None with patch.object( - UpdateBetsRound, "end_block", return_value=None + BaseUpdateBetsRound, "end_block", return_value=None ) as mock_super_end_block: result = blacklisting_round.end_block() diff --git a/packages/valory/skills/decision_maker_abci/tests/states/test_decision_receive.py b/packages/valory/skills/decision_maker_abci/tests/states/test_decision_receive.py index bcf00f1b1..505261429 100644 --- a/packages/valory/skills/decision_maker_abci/tests/states/test_decision_receive.py +++ b/packages/valory/skills/decision_maker_abci/tests/states/test_decision_receive.py @@ -37,7 +37,6 @@ from packages.valory.skills.decision_maker_abci.states.decision_receive import ( DecisionReceiveRound, ) -from packages.valory.skills.market_manager_abci.rounds import UpdateBetsPayload DUMMY_DECISION_HASH = "dummy_decision_hash" @@ -64,7 +63,7 @@ def get_payloads( is_profitable: Optional[bool], bets_hash: str, policy: str, -) -> Mapping[str, UpdateBetsPayload]: +) -> Mapping[str, DecisionReceivePayload]: """Get payloads.""" return { participant: DecisionReceivePayload( @@ -88,7 +87,7 @@ class RoundTestCase: name: str initial_data: Dict[str, Hashable] - payloads: Mapping[str, UpdateBetsPayload] + payloads: Mapping[str, DecisionReceivePayload] final_data: Dict[str, Hashable] event: Event most_voted_payload: Any diff --git a/packages/valory/skills/decision_maker_abci/tests/states/test_sampling.py b/packages/valory/skills/decision_maker_abci/tests/states/test_sampling.py index 50594d9bd..ce1ccbf54 100644 --- a/packages/valory/skills/decision_maker_abci/tests/states/test_sampling.py +++ b/packages/valory/skills/decision_maker_abci/tests/states/test_sampling.py @@ -28,7 +28,7 @@ Event, SynchronizedData, ) -from packages.valory.skills.market_manager_abci.rounds import UpdateBetsRound +from packages.valory.skills.market_manager_abci.rounds import BaseUpdateBetsRound # Mock classes to simulate required attributes @@ -90,9 +90,9 @@ def test_sampling_payload_initialization(self) -> None: assert payload.bets_hash == "mock_bets_hash" assert payload.index == 0 # Check that the index is correctly initialized - def test_sampling_round_inherits_update_bets_round(self) -> None: + def test_sampling_round_inherits_base_update_bets_round(self) -> None: """Test that SamplingRound inherits from UpdateBetsRound.""" - assert issubclass(SamplingRound, UpdateBetsRound) + assert issubclass(SamplingRound, BaseUpdateBetsRound) def test_sampling_round_selection_key( self, setup_sampling_round: SamplingRound @@ -100,7 +100,7 @@ def test_sampling_round_selection_key( """Test the selection key property of SamplingRound.""" sampling_round = setup_sampling_round expected_selection_key = ( - UpdateBetsRound.selection_key, + get_name(SynchronizedData.bets_hash), get_name(SynchronizedData.sampled_bet_index), get_name(SynchronizedData.benchmarking_finished), get_name(SynchronizedData.simulated_day), diff --git a/packages/valory/skills/decision_maker_abci/tests/test_handlers.py b/packages/valory/skills/decision_maker_abci/tests/test_handlers.py index f934bbbe4..96871a2f0 100644 --- a/packages/valory/skills/decision_maker_abci/tests/test_handlers.py +++ b/packages/valory/skills/decision_maker_abci/tests/test_handlers.py @@ -159,10 +159,15 @@ def test_setup(self) -> None: local_ip_regex = r"192\.168(\.\d{1,3}){2}" hostname_regex = rf".*({config_uri_base_hostname}|{propel_uri_base_hostname}|{local_ip_regex}|localhost|127.0.0.1|0.0.0.0)(:\d+)?" health_url_regex = rf"{hostname_regex}\/healthcheck" + metrics_url_regex = rf"{hostname_regex}\/metrics" assert self.handler.handler_url_regex == rf"{hostname_regex}\/.*" assert self.handler.routes == { (HttpMethod.GET.value, HttpMethod.HEAD.value): [ - (health_url_regex, self.handler._handle_get_health), + ( + health_url_regex, + self.handler._handle_get_health, + ), + (metrics_url_regex, self.handler._handle_get_metrics), ], } assert self.handler.json_content_header == "Content-Type: application/json\n" diff --git a/packages/valory/skills/decision_maker_abci/tests/test_payloads.py b/packages/valory/skills/decision_maker_abci/tests/test_payloads.py index f406adc7e..79b02db2a 100644 --- a/packages/valory/skills/decision_maker_abci/tests/test_payloads.py +++ b/packages/valory/skills/decision_maker_abci/tests/test_payloads.py @@ -89,6 +89,7 @@ { "mech_requests": "dummy mech requests", "mocking_mode": True, + "decision_request_timestamp": int(datetime.utcnow().timestamp()), }, ), ( diff --git a/packages/valory/skills/market_manager_abci/behaviours.py b/packages/valory/skills/market_manager_abci/behaviours.py index 999523130..84deb4b55 100644 --- a/packages/valory/skills/market_manager_abci/behaviours.py +++ b/packages/valory/skills/market_manager_abci/behaviours.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # -# Copyright 2023-2024 Valory AG +# Copyright 2023-2025 Valory AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -23,10 +23,13 @@ import os.path from abc import ABC from json import JSONDecodeError -from typing import Any, Dict, Generator, List, Optional, Set, Type +from typing import Any, Dict, Generator, List, Optional, Set, Type, cast from aea.helpers.ipfs.base import IPFSHashOnly +from packages.valory.contracts.erc20.contract import ERC20 +from packages.valory.contracts.service_registry.contract import ServiceRegistryContract +from packages.valory.protocols.contract_api import ContractApiMessage from packages.valory.skills.abstract_round_abci.behaviour_utils import BaseBehaviour from packages.valory.skills.abstract_round_abci.behaviours import AbstractRoundBehaviour from packages.valory.skills.market_manager_abci.bets import ( @@ -39,17 +42,22 @@ MAX_LOG_SIZE, QueryingBehaviour, ) +from packages.valory.skills.market_manager_abci.models import MarketManagerParams from packages.valory.skills.market_manager_abci.payloads import UpdateBetsPayload from packages.valory.skills.market_manager_abci.rounds import ( MarketManagerAbciApp, + SynchronizedData, UpdateBetsRound, ) +WaitableConditionType = Generator[None, None, bool] + BETS_FILENAME = "bets.json" MULTI_BETS_FILENAME = "multi_bets.json" READ_MODE = "r" WRITE_MODE = "w" +WXDAI = "0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d" class BetsManagerBehaviour(BaseBehaviour, ABC): @@ -61,6 +69,37 @@ def __init__(self, **kwargs: Any) -> None: self.bets: List[Bet] = [] self.multi_bets_filepath: str = self.params.store_path / MULTI_BETS_FILENAME self.bets_filepath: str = self.params.store_path / BETS_FILENAME + self.token_balance = 0 + self.wallet_balance = 0 + self.olas_balance = 0 + self.service_owner_address = None + + @property + def params(self) -> MarketManagerParams: + """Return the params.""" + return cast(MarketManagerParams, self.context.params) + + @property + def synchronized_data(self) -> SynchronizedData: + """Return the synchronized data.""" + return SynchronizedData(super().synchronized_data.db) + + @property + def sampled_bet(self) -> Bet: + """Get the sampled bet and reset the bets list.""" + self.read_bets() + bet_index = self.synchronized_data.sampled_bet_index + return self.bets[bet_index] + + @property + def collateral_token(self) -> str: + """Get the contract address of the token that the market maker supports.""" + return self.sampled_bet.collateralToken + + @property + def is_wxdai(self) -> bool: + """Get whether the collateral address is wxDAI.""" + return self.collateral_token.lower() == WXDAI.lower() def store_bets(self) -> None: """Store the bets to the agent's data dir as JSON.""" @@ -113,6 +152,92 @@ def hash_stored_bets(self) -> str: """Get the hash of the stored bets' file.""" return IPFSHashOnly.hash_file(self.multi_bets_filepath) + def get_balance(self) -> WaitableConditionType: + """Get the safe's balance.""" + + print(self.synchronized_data.safe_contract_address) + + response_msg = yield from self.get_contract_api_response( + performative=ContractApiMessage.Performative.GET_RAW_TRANSACTION, # type: ignore + contract_address=self.collateral_token, + contract_id=str(ERC20.contract_id), + contract_callable="check_balance", + account=self.synchronized_data.safe_contract_address, + ) + if response_msg.performative != ContractApiMessage.Performative.RAW_TRANSACTION: + self.context.logger.error( + f"Could not calculate the balance of the safe: {response_msg}" + ) + return False + + token = response_msg.raw_transaction.body.get("token", None) + wallet = response_msg.raw_transaction.body.get("wallet", None) + if token is None or wallet is None: + self.context.logger.error( + f"Something went wrong while trying to get the balance of the safe: {response_msg}" + ) + return False + + self.token_balance = int(token) + self.wallet_balance = int(wallet) + + self.context.logger.info("Balances updated.") + + return True + + def get_olas_balance(self) -> WaitableConditionType: + """Get the safe's olas balance in wei.""" + + print(self.params.olas_token_address) + + response_msg = yield from self.get_contract_api_response( + performative=ContractApiMessage.Performative.GET_RAW_TRANSACTION, # type: ignore + contract_address=self.params.olas_token_address, + contract_id=str(ERC20.contract_id), + contract_callable="check_balance", + account=self.synchronized_data.safe_contract_address, + ) + if response_msg.performative != ContractApiMessage.Performative.RAW_TRANSACTION: + self.context.logger.error( + f"Could not calculate the balance of the safe: {response_msg}" + ) + return False + + token = response_msg.raw_transaction.body.get("token", None) + wallet = response_msg.raw_transaction.body.get("wallet", None) + if token is None or wallet is None: + self.context.logger.error( + f"Something went wrong while trying to get the balance of the safe: {response_msg}" + ) + return False + + self.olas_balance = int(token) + return True + + def _get_service_owner(self) -> WaitableConditionType: + """Method that returns the service owner.""" + print(self.params.service_registry_address) + + response = yield from self.get_contract_api_response( + performative=ContractApiMessage.Performative.GET_STATE, # type: ignore + contract_id=str(ServiceRegistryContract.contract_id), + contract_callable="get_service_owner", + contract_address=self.params.service_registry_address, + service_id=self.params.on_chain_service_id, + chain_id=self.params.default_chain_id, + ) + + if response.performative != ContractApiMessage.Performative.STATE: + self.context.logger.error( + f"Couldn't get the service owner for service with id={self.params.on_chain_service_id}. " + f"Expected response performative {ContractApiMessage.Performative.STATE.value}, " # type: ignore + f"received {response.performative.value}." + ) + return False + + self.service_owner_address = response.state.body.get("service_owner", None) + return True + class UpdateBetsBehaviour(BetsManagerBehaviour, QueryingBehaviour): """Behaviour that fetches and updates the bets.""" @@ -217,8 +342,25 @@ def async_act(self) -> Generator: # Store the bets to the agent's data dir as JSON self.store_bets() + # set the balances + yield from self.get_balance() + yield from self.get_olas_balance() + olas_balance = self.olas_balance + wallet_balance = self.wallet_balance + token_balance = self.token_balance + + yield from self._get_service_owner() + service_owner_address = self.service_owner_address + bets_hash = self.hash_stored_bets() if self.bets else None - payload = UpdateBetsPayload(self.context.agent_address, bets_hash) + payload = UpdateBetsPayload( + self.context.agent_address, + bets_hash, + wallet_balance, + token_balance, + olas_balance, + service_owner_address, + ) with self.context.benchmark_tool.measure(self.behaviour_id).consensus(): yield from self.send_a2a_transaction(payload) diff --git a/packages/valory/skills/market_manager_abci/models.py b/packages/valory/skills/market_manager_abci/models.py index c39c3fc94..a37b75f6d 100644 --- a/packages/valory/skills/market_manager_abci/models.py +++ b/packages/valory/skills/market_manager_abci/models.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # -# Copyright 2023-2024 Valory AG +# Copyright 2023-2025 Valory AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -100,6 +100,10 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.the_graph_payment_required_error: str = self._ensure( "the_graph_payment_required_error", kwargs, str ) + self.olas_token_address: str = self._ensure("olas_token_address", kwargs, str) + self.http_handler_hostname_regex: str = self._ensure( + "http_handler_hostname_regex", kwargs, str + ) super().__init__(*args, **kwargs) @property diff --git a/packages/valory/skills/market_manager_abci/payloads.py b/packages/valory/skills/market_manager_abci/payloads.py index d7247c415..d1e7759fd 100644 --- a/packages/valory/skills/market_manager_abci/payloads.py +++ b/packages/valory/skills/market_manager_abci/payloads.py @@ -26,7 +26,17 @@ @dataclass(frozen=True) -class UpdateBetsPayload(BaseTxPayload): +class BaseUpdateBetsPayload(BaseTxPayload): """A transaction payload for the updated bets.""" bets_hash: Optional[str] + + +@dataclass(frozen=True) +class UpdateBetsPayload(BaseUpdateBetsPayload): + """A transaction payload for the updated bets.""" + + wallet_balance: Optional[int] + token_balance: Optional[int] + olas_balance: Optional[int] + service_owner_address: Optional[str] diff --git a/packages/valory/skills/market_manager_abci/rounds.py b/packages/valory/skills/market_manager_abci/rounds.py index 1ac5dc8be..7c25350d9 100644 --- a/packages/valory/skills/market_manager_abci/rounds.py +++ b/packages/valory/skills/market_manager_abci/rounds.py @@ -21,7 +21,7 @@ from abc import ABC from enum import Enum -from typing import Dict, Set, Tuple, Type, cast +from typing import Any, Dict, Optional, Set, Tuple, Type, cast from packages.valory.skills.abstract_round_abci.base import ( AbciApp, @@ -35,7 +35,10 @@ DeserializedCollection, get_name, ) -from packages.valory.skills.market_manager_abci.payloads import UpdateBetsPayload +from packages.valory.skills.market_manager_abci.payloads import ( + BaseUpdateBetsPayload, + UpdateBetsPayload, +) class Event(Enum): @@ -73,6 +76,46 @@ def is_checkpoint_reached(self) -> bool: """Check if the checkpoint is reached.""" return bool(self.db.get("is_checkpoint_reached", False)) + @property + def wallet_balance(self) -> int: + """Get the wallet balance.""" + wallet_balance = self.db.get("wallet_balance", 0) + if wallet_balance is None: + return 0 + return int(wallet_balance) + + @property + def token_balance(self) -> int: + """Get the wallet balance.""" + token_balance = self.db.get("token_balance", 0) + if token_balance is None: + return 0 + return int(token_balance) + + @property + def olas_balance(self) -> int: + """Get the wallet balance.""" + olas_balance = self.db.get("olas_balance", 0) + if olas_balance is None: + return 0 + return int(olas_balance) + + @property + def sampled_bet_index(self) -> int: + """Get the sampled bet.""" + sampled_bet_index = self.db.get("sampled_bet_index", 0) + if sampled_bet_index is None: + return 0 + return int(sampled_bet_index) + + @property + def service_owner_address(self) -> Optional[str]: + """Get the service owner address.""" + service_owner_address = self.db.get("service_owner_address", None) + if service_owner_address is None: + return None + return str(service_owner_address) + class MarketManagerAbstractRound(AbstractRound[Event], ABC): """Abstract round for the MarketManager skill.""" @@ -91,10 +134,10 @@ def _return_no_majority_event(self) -> Tuple[SynchronizedData, Event]: return self.synchronized_data, Event.NO_MAJORITY -class UpdateBetsRound(CollectSameUntilThresholdRound, MarketManagerAbstractRound): +class BaseUpdateBetsRound(CollectSameUntilThresholdRound, MarketManagerAbstractRound): """A round for the bets fetching & updating.""" - payload_class = UpdateBetsPayload + payload_class = BaseUpdateBetsPayload done_event: Enum = Event.DONE none_event: Enum = Event.FETCH_ERROR no_majority_event: Enum = Event.NO_MAJORITY @@ -103,6 +146,24 @@ class UpdateBetsRound(CollectSameUntilThresholdRound, MarketManagerAbstractRound synchronized_data_class = SynchronizedData +class UpdateBetsRound(BaseUpdateBetsRound): + """A round for the bets fetching & updating.""" + + payload_class: Type[UpdateBetsPayload] = UpdateBetsPayload + done_event: Enum = Event.DONE + none_event: Enum = Event.FETCH_ERROR + no_majority_event: Enum = Event.NO_MAJORITY + selection_key: Any = ( + BaseUpdateBetsRound.selection_key, + get_name(SynchronizedData.wallet_balance), + get_name(SynchronizedData.token_balance), + get_name(SynchronizedData.olas_balance), + get_name(SynchronizedData.service_owner_address), + ) + collection_key = get_name(SynchronizedData.participant_to_bets_hash) + synchronized_data_class = SynchronizedData + + class FinishedMarketManagerRound(DegenerateRound, ABC): """A round that represents MarketManager has finished""" diff --git a/packages/valory/skills/market_manager_abci/skill.yaml b/packages/valory/skills/market_manager_abci/skill.yaml index 0afea7fb0..fdf045438 100644 --- a/packages/valory/skills/market_manager_abci/skill.yaml +++ b/packages/valory/skills/market_manager_abci/skill.yaml @@ -8,7 +8,7 @@ aea_version: '>=1.0.0, <2.0.0' fingerprint: README.md: bafybeie6miwn67uin3bphukmf7qgiifh4xtm42i5v3nuyqxzxtehxsqvcq __init__.py: bafybeigrtedqzlq5mtql2ssjsdriw76ml3666m4e2c3fay6vmyzofl6v6e - behaviours.py: bafybeicfoszavcyrzahe6qaydlaf27mpbwui7a6wgdbstydbzxmdisxhju + behaviours.py: bafybeidr7oeqpraahb3x4wmewzexgx7wcnrphyg3zc47zwc3fwo4hjmqgy bets.py: bafybeibx3x5nasuj6loneeat2lb7fr7kgsmnz7on7f4gxfbpzerzdvawou dialogues.py: bafybeiebofyykseqp3fmif36cqmmyf3k7d2zbocpl6t6wnlpv4szghrxbm fsm_specification.yaml: bafybeic5cvwfbiu5pywyp3h5s2elvu7jqdrcwayay7o3v3ow47vu2jw53q @@ -22,19 +22,22 @@ fingerprint: graph_tooling/requests.py: bafybeibjyb6av33aswnptttekj6t7k7xysgphh2bigoorcgkc54y2j3xkm graph_tooling/utils.py: bafybeig5hxhnqgyfn5ym3poc5nziqwpeozqbd6wa4s6c2hjn6iyedg3t3y handlers.py: bafybeihot2i2yvfkz2gcowvt66wdu6tkjbmv7hsmc4jzt4reqeaiuphbtu - models.py: bafybeibjttnga54y4auz6f33ecfrngyw53b2xzpompm72drjsr4xoytmiy - payloads.py: bafybeicfymvvtdpkcgmkvthfzmb7dqakepkzslqrz6rcs7nxkz7qq3mrzy - rounds.py: bafybeibqqq3vjotaasc67olhlqthka6e6refodguntkmpksgdbqlzme73a + models.py: bafybeif2uk72sqzbkh5cj44uuwwa5utgcv64xrsoykaewerk23mn6qmxge + payloads.py: bafybeib2a2e4fableelw246sr2vougv4vdiyyvdggffy6eohi4c4e3b4se + rounds.py: bafybeic3nwjaf6j7zwuso7d4sfmvlp4urkr4t2z2e75xbeu32wvsacjdem tests/__init__.py: bafybeigaewntxawezvygss345kytjijo56bfwddjtfm6egzxfajsgojam4 tests/test_dialogues.py: bafybeiet646su5nsjmvruahuwg6un4uvwzyj2lnn2jvkye6cxooz22f3ja tests/test_handlers.py: bafybeiaz3idwevvlplcyieaqo5oeikuthlte6e2gi4ajw452ylvimwgiki - tests/test_payloads.py: bafybeidvld43p5c4wpwi7m6rfzontkheqqgxdchjnme5b54wmldojc5dmm - tests/test_rounds.py: bafybeidahkavof43y3o4omnihh6yxdx7gqofio7kzukdydymxbebylempu + tests/test_payloads.py: bafybeia3qstr7ieu2nenznqtmuvipmnequkcp5vwefl7buinckwttx674m + tests/test_rounds.py: bafybeiaxnoswwgkrd6pcvkbyvvmjnt6gawyzf32zurpyi2u45t5hzkkaia fingerprint_ignore_patterns: [] connections: [] -contracts: [] +contracts: +- valory/erc20:0.1.0:bafybeientdgpccdi7prtu4x53m5g3yugh5tuh5hnroylfz3wwzyjniqure +- valory/service_registry:0.1.0:bafybeidipx4cmchxdu5i2v67rno7muie7ckjhmasaj64tv2vtj4fveklxi protocols: - valory/http:1.0.0:bafybeifugzl63kfdmwrxwphrnrhj7bn6iruxieme3a4ntzejf6kmtuwmae +- valory/contract_api:1.0.0:bafybeidgu7o5llh26xp3u3ebq3yluull5lupiyeu6iooi2xyymdrgnzq5i skills: - valory/abstract_round_abci:0.1.0:bafybeigjddhk7epta7xpnfvv426xedff5abh4xlkwi6cqgp4vkutgkvydm behaviours: @@ -150,6 +153,8 @@ models: the_graph_error_message_key: message the_graph_payment_required_error: payment required for subsequent requests for this API key + olas_token_address: '0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f' + http_handler_hostname_regex: localhost class_name: MarketManagerParams network_subgraph: args: diff --git a/packages/valory/skills/market_manager_abci/tests/test_payloads.py b/packages/valory/skills/market_manager_abci/tests/test_payloads.py index 898c80b48..84e78681d 100644 --- a/packages/valory/skills/market_manager_abci/tests/test_payloads.py +++ b/packages/valory/skills/market_manager_abci/tests/test_payloads.py @@ -18,13 +18,13 @@ # ------------------------------------------------------------------------------ """This module contains the transaction payloads for the market manager abci.""" -from packages.valory.skills.market_manager_abci.payloads import UpdateBetsPayload +from packages.valory.skills.market_manager_abci.payloads import BaseUpdateBetsPayload def test_update_bets_payload() -> None: """Test `UpdateBetsPayload`.""" - payload = UpdateBetsPayload(sender="sender", bets_hash="dummy bets hash") + payload = BaseUpdateBetsPayload(sender="sender", bets_hash="dummy bets hash") assert payload.bets_hash == "dummy bets hash" - assert UpdateBetsPayload.from_json(payload.json) == payload + assert BaseUpdateBetsPayload.from_json(payload.json) == payload diff --git a/packages/valory/skills/market_manager_abci/tests/test_rounds.py b/packages/valory/skills/market_manager_abci/tests/test_rounds.py index 33b184c69..19e26ef80 100644 --- a/packages/valory/skills/market_manager_abci/tests/test_rounds.py +++ b/packages/valory/skills/market_manager_abci/tests/test_rounds.py @@ -86,11 +86,22 @@ def get_participants() -> FrozenSet[str]: def get_payloads( - data: Optional[str], + bets_hash: Optional[str], + wallet_balance: Optional[int], + token_balance: Optional[int], + olas_balance: Optional[int], + service_owner_address: Optional[str], ) -> Mapping[str, BaseTxPayload]: """Get payloads.""" return { - participant: UpdateBetsPayload(participant, data) + participant: UpdateBetsPayload( + sender=participant, + bets_hash=bets_hash, + wallet_balance=wallet_balance, + token_balance=token_balance, + olas_balance=olas_balance, + service_owner_address=service_owner_address, + ) for participant in get_participants() } @@ -160,7 +171,11 @@ class TestUpdateBetsRound(BaseMarketManagerRoundTestClass): name="Happy path", initial_data={}, payloads=get_payloads( - data=DUMMY_BETS_HASH, + bets_hash=DUMMY_BETS_HASH, + wallet_balance=1, + token_balance=1, + olas_balance=1, + service_owner_address="dummy_service_owner_address", ), final_data={ "bets_hash": DUMMY_BETS_HASH, @@ -176,7 +191,11 @@ class TestUpdateBetsRound(BaseMarketManagerRoundTestClass): name="Fetch error", initial_data={}, payloads=get_payloads( - data=None, + bets_hash=None, + wallet_balance=None, + token_balance=None, + olas_balance=None, + service_owner_address=None, ), final_data={}, event=Event.FETCH_ERROR, @@ -251,16 +270,16 @@ def test_market_manager_abci_app_initialization(abci_app: MarketManagerAbciApp) # Mock serialized collections for different keys -DUMMY_PARTICIPANT_TO_BETS_HASH = json.dumps( +DUMMY_PARTICIPANT_TO_BETS_HASH_S = json.dumps( { "agent_0": {"sender": "agent_0", "data": "bet_1"}, "agent_1": {"sender": "agent_1", "data": "bet_2"}, } ) -DUMMY_BETS_HASH = json.dumps({"bets_hash": "dummy_bets_hash"}) +DUMMY_BETS_HASH_S = json.dumps({"bets_hash": "dummy_bets_hash"}) -DUMMY_SERIALIZED_PARTICIPANT_TO_BETS_HASH = json.dumps(DUMMY_PARTICIPANT_TO_BETS_HASH) -DUMMY_SERIALIZED_BETS_HASH = json.dumps(DUMMY_BETS_HASH) +DUMMY_SERIALIZED_PARTICIPANT_TO_BETS_HASH = json.dumps(DUMMY_PARTICIPANT_TO_BETS_HASH_S) +DUMMY_SERIALIZED_BETS_HASH = json.dumps(DUMMY_BETS_HASH_S) @pytest.mark.parametrize( @@ -269,13 +288,13 @@ def test_market_manager_abci_app_initialization(abci_app: MarketManagerAbciApp) ( "participant_to_bets_hash", DUMMY_SERIALIZED_PARTICIPANT_TO_BETS_HASH, - json.loads(DUMMY_PARTICIPANT_TO_BETS_HASH), + json.loads(DUMMY_PARTICIPANT_TO_BETS_HASH_S), "participant_to_bets_hash", ), ( "bets_hash", DUMMY_SERIALIZED_BETS_HASH, - json.loads(DUMMY_BETS_HASH), + json.loads(DUMMY_BETS_HASH_S), "bets_hash", ), ], diff --git a/packages/valory/skills/staking_abci/behaviours.py b/packages/valory/skills/staking_abci/behaviours.py index d5676ca58..77220a543 100644 --- a/packages/valory/skills/staking_abci/behaviours.py +++ b/packages/valory/skills/staking_abci/behaviours.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # -# Copyright 2023-2024 Valory AG +# Copyright 2023-2025 Valory AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,11 +18,22 @@ # ------------------------------------------------------------------------------ """This module contains the behaviours for the staking skill.""" - +import json from abc import ABC from datetime import datetime, timedelta from pathlib import Path -from typing import Any, Callable, Generator, Optional, Set, Tuple, Type, Union, cast +from typing import ( + Any, + Callable, + Generator, + List, + Optional, + Set, + Tuple, + Type, + Union, + cast, +) from aea.configurations.data_types import PublicId from aea.contracts.base import Contract @@ -68,6 +79,9 @@ CHECKPOINT_FILENAME = "checkpoint.txt" READ_MODE = "r" WRITE_MODE = "w" +CID_PREFIX = "f01701220" +GET = "GET" +OK_CODE = 200 class StakingInteractBaseBehaviour(BaseBehaviour, ABC): @@ -76,8 +90,13 @@ class StakingInteractBaseBehaviour(BaseBehaviour, ABC): def __init__(self, **kwargs: Any) -> None: """Initialize the behaviour.""" super().__init__(**kwargs) + self._service_ids: List[str] = [] + self._max_num_services: int = 0 self._service_staking_state: StakingState = StakingState.UNSTAKED self._checkpoint_ts = 0 + self._staking_contract_name: str = "" + self._staking_contract_metadata_hash: str = "" + self._epoch_end_ts: int = 0 @property def params(self) -> StakingParams: @@ -99,6 +118,16 @@ def staking_contract_address(self) -> str: """Get the staking contract address.""" return self.params.staking_contract_address + @property + def staking_contract_name(self) -> str: + """Get the staking contract name.""" + return self._staking_contract_name + + @staking_contract_name.setter + def staking_contract_name(self, name: str) -> None: + """Set the staking contract name.""" + self._staking_contract_name = name + @property def mech_activity_checker_contract(self) -> str: """Get the staking contract address.""" @@ -171,6 +200,53 @@ def service_info(self, service_info: Tuple[Any, Any, Tuple[Any, Any]]) -> None: """Set the service info.""" self._service_info = service_info + @property + def max_num_services(self) -> int: + """Get the max number of services.""" + return self._max_num_services + + @max_num_services.setter + def max_num_services(self, max_num_services: int) -> None: + """Set the max number of services.""" + self._max_num_services = max_num_services + + @property + def service_ids(self) -> List[str]: + """Get the service ids staked on the contract.""" + return self._service_ids + + @service_ids.setter + def service_ids(self, service_ids: List[str]) -> None: + """Set the service ids staked on the contract.""" + self._service_ids = service_ids + + @property + def available_staking_slots(self) -> int: + """Get the number of available staking slots""" + if self._service_ids is None or self._max_num_services is None: + return 0 + return self.max_num_services - len(self._service_ids) + + @property + def staking_contract_metadata_hash(self) -> str: + """Get the staking contract metadata hash.""" + return self._staking_contract_metadata_hash + + @staking_contract_metadata_hash.setter + def staking_contract_metadata_hash(self, metadata_hash: str) -> None: + """Set the staking contract metadata hash.""" + self._staking_contract_metadata_hash = metadata_hash + + @property + def epoch_end_ts(self) -> int: + """Get the epoch end timestamp.""" + return self._epoch_end_ts + + @epoch_end_ts.setter + def epoch_end_ts(self, epoch_end_ts: int) -> None: + """Set the epoch end timestamp.""" + self._epoch_end_ts = epoch_end_ts + def wait_for_condition_with_sleep( self, condition_gen: Callable[[], WaitableConditionType], @@ -355,6 +431,58 @@ def _get_service_info(self) -> WaitableConditionType: ) return status + def _get_staking_contract_metadata_hash(self) -> Generator: + """Get the staking contract metadata hash.""" + status = yield from self._staking_contract_interact( + contract_callable="get_metadata_hash", + placeholder=get_name( + CallCheckpointBehaviour.staking_contract_metadata_hash + ), + ) + return status + + def _get_staking_contract_name(self) -> WaitableConditionType: + """Get the staking contract name.""" + self.context.logger.info("Reading staking contract name from IPFS...") + staking_contract_metadata_hash = self.staking_contract_metadata_hash + staking_contract_metadata_hash_formatted = ( + staking_contract_metadata_hash.replace("0x", CID_PREFIX) + ) + staking_contract_link = ( + self.params.ipfs_address + staking_contract_metadata_hash_formatted + ) + response = yield from self.get_http_response( + method=GET, url=staking_contract_link + ) + if response.status_code != OK_CODE: + self.context.logger.error( + f"Could not retrieve data from the url {staking_contract_link}. " + f"Received status code {response.status_code}." + ) + return False + + self.context.logger.info("Parsing staking contract name...") + try: + response_body = response.body.decode() + response_json = json.loads(response_body) + self.staking_contract_name = response_json["name"] + except (ValueError, TypeError) as e: + self.context.logger.error( + f"Could not parse response from ipfs server, " + f"the following error was encountered {type(e).__name__}: {e}" + ) + return False + + return True + + def _get_epoch_end(self) -> WaitableConditionType: + """Get the epoch end.""" + status = yield from self._staking_contract_interact( + contract_callable="get_epoch_end", + placeholder=get_name(CallCheckpointBehaviour.epoch_end_ts), + ) + return status + class CallCheckpointBehaviour( StakingInteractBaseBehaviour @@ -521,6 +649,13 @@ def async_act(self) -> Generator: checkpoint_tx_hex = None if self.service_staking_state == StakingState.STAKED: yield from self.wait_for_condition_with_sleep(self._get_next_checkpoint) + yield from self.wait_for_condition_with_sleep( + self._get_staking_contract_metadata_hash + ) + yield from self.wait_for_condition_with_sleep( + self._get_staking_contract_name + ) + yield from self.wait_for_condition_with_sleep(self._get_epoch_end) if self.is_checkpoint_reached: checkpoint_tx_hex = yield from self._prepare_safe_tx() @@ -536,6 +671,9 @@ def async_act(self) -> Generator: self.service_staking_state.value, self.ts_checkpoint, is_checkpoint_reached, + self.available_staking_slots, + self.staking_contract_name, + self.epoch_end_ts, ) with self.context.benchmark_tool.measure(self.behaviour_id).consensus(): diff --git a/packages/valory/skills/staking_abci/models.py b/packages/valory/skills/staking_abci/models.py index 6b1d91ad9..d633644b3 100644 --- a/packages/valory/skills/staking_abci/models.py +++ b/packages/valory/skills/staking_abci/models.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # -# Copyright 2023-2024 Valory AG +# Copyright 2023-2025 Valory AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,7 +24,6 @@ from pathlib import Path from typing import Any, Optional -from packages.valory.skills.abstract_round_abci.models import BaseParams from packages.valory.skills.abstract_round_abci.models import ( BenchmarkTool as BaseBenchmarkTool, ) @@ -32,6 +31,9 @@ from packages.valory.skills.abstract_round_abci.models import ( SharedState as BaseSharedState, ) +from packages.valory.skills.mech_interact_abci.models import ( + Params as MechInteractParams, +) from packages.valory.skills.staking_abci.rounds import StakingAbciApp @@ -58,7 +60,7 @@ def get_store_path(kwargs: dict) -> Path: return Path(path) -class StakingParams(BaseParams): +class StakingParams(MechInteractParams): """Staking parameters.""" mech_chain_id: Optional[str] diff --git a/packages/valory/skills/staking_abci/payloads.py b/packages/valory/skills/staking_abci/payloads.py index 4b1f47e2c..436ecb81e 100644 --- a/packages/valory/skills/staking_abci/payloads.py +++ b/packages/valory/skills/staking_abci/payloads.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # -# Copyright 2023-2024 Valory AG +# Copyright 2023-2025 Valory AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -40,3 +40,6 @@ class CallCheckpointPayload(MultisigTxPayload): service_staking_state: int ts_checkpoint: int is_checkpoint_reached: bool + available_slot_count: int + staking_contract_name: str + epoch_end_ts: int diff --git a/packages/valory/skills/staking_abci/rounds.py b/packages/valory/skills/staking_abci/rounds.py index 444b97065..42f3783da 100644 --- a/packages/valory/skills/staking_abci/rounds.py +++ b/packages/valory/skills/staking_abci/rounds.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # -# Copyright 2023-2024 Valory AG +# Copyright 2023-2025 Valory AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -100,6 +100,30 @@ def is_checkpoint_reached(self) -> bool: """Check if the checkpoint is reached.""" return bool(self.db.get("is_checkpoint_reached", False)) + @property + def available_staking_slots(self) -> int: + """Get the number of available staking slots for the contract.""" + available_staking_slots = self.db.get("available_staking_slots", None) + if available_staking_slots is None: + return 0 + return int(available_staking_slots) + + @property + def staking_contract_name(self) -> str: + """Get the staking contract name.""" + staking_contract_name = self.db.get("staking_contract_name", None) + if staking_contract_name is None: + return "No staking contract" + return str(staking_contract_name) + + @property + def epoch_end_ts(self) -> int: + """Get the epoch end timestamp.""" + epoch_end_ts = self.db.get("epoch_end_ts", None) + if epoch_end_ts is None: + return 0 + return int(epoch_end_ts) + class CallCheckpointRound(CollectSameUntilThresholdRound): """A round for the checkpoint call preparation.""" @@ -113,6 +137,9 @@ class CallCheckpointRound(CollectSameUntilThresholdRound): get_name(SynchronizedData.service_staking_state), get_name(SynchronizedData.previous_checkpoint), get_name(SynchronizedData.is_checkpoint_reached), + get_name(SynchronizedData.available_staking_slots), + get_name(SynchronizedData.staking_contract_name), + get_name(SynchronizedData.epoch_end_ts), ) collection_key = get_name(SynchronizedData.participant_to_checkpoint) synchronized_data_class = SynchronizedData @@ -205,6 +232,7 @@ class StakingAbciApp(AbciApp[Event]): # pylint: disable=too-few-public-methods get_name(SynchronizedData.service_staking_state), get_name(SynchronizedData.previous_checkpoint), get_name(SynchronizedData.is_checkpoint_reached), + get_name(SynchronizedData.available_staking_slots), } ) final_states: Set[AppState] = { @@ -223,6 +251,7 @@ class StakingAbciApp(AbciApp[Event]): # pylint: disable=too-few-public-methods get_name(SynchronizedData.service_staking_state), get_name(SynchronizedData.previous_checkpoint), get_name(SynchronizedData.is_checkpoint_reached), + get_name(SynchronizedData.available_staking_slots), }, FinishedStakingRound: { get_name(SynchronizedData.service_staking_state), diff --git a/packages/valory/skills/staking_abci/skill.yaml b/packages/valory/skills/staking_abci/skill.yaml index cfc6e54da..25e658796 100644 --- a/packages/valory/skills/staking_abci/skill.yaml +++ b/packages/valory/skills/staking_abci/skill.yaml @@ -8,30 +8,31 @@ aea_version: '>=1.0.0, <2.0.0' fingerprint: README.md: bafybeifrpl36fddmgvniwvghqtxdzc44ry6l2zvqy37vu3y2xvwyd23ugy __init__.py: bafybeiageyes36ujnvvodqd5vlnihgz44rupysrk2ebbhskjkueetj6dai - behaviours.py: bafybeieukrrm34bqjtolm2okiys7llri4led2suclve24mtitbpii3svge + behaviours.py: bafybeif3u6clwsfo6jihfec5ss3kk77yqyr7nh7d2hxrfk3tb7izjszek4 dialogues.py: bafybeiebofyykseqp3fmif36cqmmyf3k7d2zbocpl6t6wnlpv4szghrxbm fsm_specification.yaml: bafybeicuoejmaks3ndwhbflp64kkfdkrdyn74a2fplarg4l3gxlonfmeoq handlers.py: bafybeichsi2y5zvzffupj2vhgagocwvnm7cbzr6jmavp656mfrzsdvkfnu - models.py: bafybeigv4bltwodffe7n5fs6tevx2ysavkj3ynpwniyky653jiigtfzppu - payloads.py: bafybeib7hxsibtabb5vt6gjtco2x2s2yodhs24ojqecgrcexqsiv3pvmuy - rounds.py: bafybeifki4k3x6hjjkudrovsblf2ghrcg42b7nfysds5yts5p357dadl4a + models.py: bafybeifweemtunod7uyqwex7vz4v4g52okk4wjmvgxgeeqg6gw3gcohqvi + payloads.py: bafybeidwrsyz7cjrp34s7o7t57xteq6nntdoagpuar4e4od6qz3bnakkwm + rounds.py: bafybeifas7lcznslnzz67bvoxlkg2glvwqn62bmnso4slk6ddq7m3h6wky tests/__init__.py: bafybeid7m6ynosqeb4mvsss2hqg75aly5o2d47r7yfg2xtgwzkkilv2d2m tests/test_dialogues.py: bafybeidwjk52mufwvkj4cr3xgqycbdzxc6gvosmqyuqdjarnrgwth6wcai tests/test_handers.py: bafybeibnxlwznx3tsdpjpzh62bnp6lq7zdpolyjxfvxeumzz52ljxfzpme - tests/test_payloads.py: bafybeidtkbudsmrx77xs2oooji6cpj7kx7nembf77u32if2sb2kvkavvhq - tests/test_rounds.py: bafybeidge7sp3udobhvjpbdj5kirkukd6w5qycqkcuc6sy3qqiedco2q4i + tests/test_payloads.py: bafybeicgnkiwj6quwfytojjkfoab2yud76khttlqvrtvl4x4eguyceetke + tests/test_rounds.py: bafybeih542rlhhdl5bo7wjviqliicy22a4thekm5fjfdoqxvx55swmv6ge fingerprint_ignore_patterns: [] connections: [] contracts: - valory/gnosis_safe:0.1.0:bafybeihtqcpqthb37msgqabpzcc2xc3l3yzkp5pl2sodeghqyzzzyuevgi -- valory/service_staking_token:0.1.0:bafybeieg664oohr26gpcfn3uied4minlz6dmgd32xboewscnxqnv5kk4zi -- valory/staking_token:0.1.0:bafybeiaynt6clwbthtbndtocnwul7dp76ctmu4jxinp7fnqks4pxt65yuy +- valory/service_staking_token:0.1.0:bafybeihlrs7m7qcme22cjfgk4l2fqbyqeqvdbum3xcljmgi4lcbegbwmkq +- valory/staking_token:0.1.0:bafybeif5m72b7dohrpvcyijqm6xm6gyarg3rp3zlwerxdzfkfmc2imwlga - valory/mech_activity:0.1.0:bafybeieadv7vnbguc7beu6xo3rs3mqbgzc7wayc7kvgb2tmitmjtpdcqkq protocols: - valory/contract_api:1.0.0:bafybeidgu7o5llh26xp3u3ebq3yluull5lupiyeu6iooi2xyymdrgnzq5i skills: - valory/abstract_round_abci:0.1.0:bafybeigjddhk7epta7xpnfvv426xedff5abh4xlkwi6cqgp4vkutgkvydm - valory/transaction_settlement_abci:0.1.0:bafybeifmgmwdkx4esemxjacjwzqkqymkuklb5nehkwqkx7v335fllgswcq +- valory/mech_interact_abci:0.1.0:bafybeib4vn6m2yumwoclh5aatcdt5yxcjc5owxmxy5o7t3nfzormgwkr64 behaviours: main: args: {} diff --git a/packages/valory/skills/staking_abci/tests/test_payloads.py b/packages/valory/skills/staking_abci/tests/test_payloads.py index f9a7b316b..1487bbcc4 100644 --- a/packages/valory/skills/staking_abci/tests/test_payloads.py +++ b/packages/valory/skills/staking_abci/tests/test_payloads.py @@ -43,6 +43,7 @@ "tx_hash": "dummy tx hash", "ts_checkpoint": 1, "is_checkpoint_reached": True, + "available_slot_count": 0, }, ), ], diff --git a/packages/valory/skills/staking_abci/tests/test_rounds.py b/packages/valory/skills/staking_abci/tests/test_rounds.py index 2a0564afd..5602320e3 100644 --- a/packages/valory/skills/staking_abci/tests/test_rounds.py +++ b/packages/valory/skills/staking_abci/tests/test_rounds.py @@ -66,6 +66,7 @@ def abci_app() -> StakingAbciApp: "tx_hash": "dummy_tx_hash", "ts_checkpoint": 0, "is_checkpoint_reached": True, + "available_slot_count": 0, } @@ -152,6 +153,7 @@ class TestCallCheckpointRound(BaseStakingRoundTestClass): "tx_hash": "dummy_tx_hash", "ts_checkpoint": 0, "is_checkpoint_reached": True, + "available_slot_count": 0, } ), final_data={ @@ -160,6 +162,7 @@ class TestCallCheckpointRound(BaseStakingRoundTestClass): "tx_hash": "dummy_tx_hash", "ts_checkpoint": 0, "is_checkpoint_reached": True, + "available_slot_count": 0, }, event=Event.DONE, most_voted_payload=DUMMY_SERVICE_STATE["tx_submitter"], @@ -192,6 +195,7 @@ class TestCallCheckpointRound(BaseStakingRoundTestClass): "tx_hash": "dummy_tx_hash", "ts_checkpoint": 0, "is_checkpoint_reached": True, + "available_slot_count": 0, } ), final_data={}, @@ -212,6 +216,7 @@ class TestCallCheckpointRound(BaseStakingRoundTestClass): "tx_hash": None, "ts_checkpoint": 0, "is_checkpoint_reached": True, + "available_slot_count": 0, } ), final_data={}, @@ -291,6 +296,7 @@ def test_staking_abci_app_initialization(abci_app: StakingAbciApp) -> None: get_name(SynchronizedData.service_staking_state), get_name(SynchronizedData.previous_checkpoint), get_name(SynchronizedData.is_checkpoint_reached), + get_name(SynchronizedData.available_staking_slots), }, FinishedStakingRound: { get_name(SynchronizedData.service_staking_state), diff --git a/packages/valory/skills/trader_abci/skill.yaml b/packages/valory/skills/trader_abci/skill.yaml index 08d0acedf..6d8431836 100644 --- a/packages/valory/skills/trader_abci/skill.yaml +++ b/packages/valory/skills/trader_abci/skill.yaml @@ -26,11 +26,11 @@ skills: - valory/reset_pause_abci:0.1.0:bafybeiezfedmmseox3ce5aucxsiszdmvskrwwbtpb2a3vw3sbmc5jt7nri - valory/transaction_settlement_abci:0.1.0:bafybeifmgmwdkx4esemxjacjwzqkqymkuklb5nehkwqkx7v335fllgswcq - valory/termination_abci:0.1.0:bafybeiea67epwwgngp7b3wavs6hpkaxv6etyaps6g6325bchfnf354mibq -- valory/market_manager_abci:0.1.0:bafybeiayhzwxlpqsevyvaxn6dtnnu745az5vcz7dckmghnmjywxdtvoyly -- valory/decision_maker_abci:0.1.0:bafybeibi3b4tnal6c6cypszogn7iskerpolmueddeagnyt3fv65vhvxebq -- valory/tx_settlement_multiplexer_abci:0.1.0:bafybeiharmlmnezdeuyfzbq27ds7jtnbqrvo62k4qj34cdbjstwqe5occu -- valory/staking_abci:0.1.0:bafybeiaasclr4lf3u2layekmfwyasckxrslbs2g4me7kvozz5goswlznjq -- valory/check_stop_trading_abci:0.1.0:bafybeic3bismocli2yyxmjhjsevjbzpuf3ladsvlkazfx23vq3q6uxrn7m +- valory/market_manager_abci:0.1.0:bafybeihzfnnogprkz2m3vlbzndzrfwjcxk5i35h2vefjhguhf2gfkkcesy +- valory/decision_maker_abci:0.1.0:bafybeidq54y34rq7djgxs53rti5crqrcf6kbie22y3tdce7og6mbifo44i +- valory/tx_settlement_multiplexer_abci:0.1.0:bafybeicovhetzfzrcdnc7rpdyrqwui5mbpk5hltqborjgbg7kc2rg46fjy +- valory/staking_abci:0.1.0:bafybeicc4iactsgkdpbbqqih6pm4tedajxf7b2axgmptm4aecisb6gh4ei +- valory/check_stop_trading_abci:0.1.0:bafybeicbshzy3nzjs6nw4me66q2m6n2pmdclqvtqlggmaaavk6rtvkdovm - valory/mech_interact_abci:0.1.0:bafybeib4vn6m2yumwoclh5aatcdt5yxcjc5owxmxy5o7t3nfzormgwkr64 behaviours: main: @@ -251,8 +251,10 @@ models: mech_invalid_response: Invalid Response mech_consecutive_failures_threshold: 2 tool_quarantine_duration: 18000 + olas_token_address: '0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f' default_chain_id: gnosis mech_interact_round_timeout_seconds: 5400 + http_handler_hostname_regex: localhost class_name: TraderParams benchmarking_mode: args: diff --git a/packages/valory/skills/tx_settlement_multiplexer_abci/skill.yaml b/packages/valory/skills/tx_settlement_multiplexer_abci/skill.yaml index a10ba3203..5e41988b2 100644 --- a/packages/valory/skills/tx_settlement_multiplexer_abci/skill.yaml +++ b/packages/valory/skills/tx_settlement_multiplexer_abci/skill.yaml @@ -23,8 +23,8 @@ protocols: - valory/ledger_api:1.0.0:bafybeihdk6psr4guxmbcrc26jr2cbgzpd5aljkqvpwo64bvaz7tdti2oni skills: - valory/abstract_round_abci:0.1.0:bafybeigjddhk7epta7xpnfvv426xedff5abh4xlkwi6cqgp4vkutgkvydm -- valory/decision_maker_abci:0.1.0:bafybeibi3b4tnal6c6cypszogn7iskerpolmueddeagnyt3fv65vhvxebq -- valory/staking_abci:0.1.0:bafybeiaasclr4lf3u2layekmfwyasckxrslbs2g4me7kvozz5goswlznjq +- valory/decision_maker_abci:0.1.0:bafybeidq54y34rq7djgxs53rti5crqrcf6kbie22y3tdce7og6mbifo44i +- valory/staking_abci:0.1.0:bafybeicc4iactsgkdpbbqqih6pm4tedajxf7b2axgmptm4aecisb6gh4ei - valory/mech_interact_abci:0.1.0:bafybeib4vn6m2yumwoclh5aatcdt5yxcjc5owxmxy5o7t3nfzormgwkr64 behaviours: main: diff --git a/poetry.lock b/poetry.lock index c3e54bd16..73028c9d7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -194,13 +194,13 @@ files = [ [[package]] name = "attrs" -version = "24.3.0" +version = "25.1.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.8" files = [ - {file = "attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"}, - {file = "attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff"}, + {file = "attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a"}, + {file = "attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e"}, ] [package.extras] @@ -1490,13 +1490,13 @@ websockets = ["websockets (>=10,<12)"] [[package]] name = "graphql-core" -version = "3.2.5" +version = "3.2.6" description = "GraphQL implementation for Python, a port of GraphQL.js, the JavaScript reference implementation for GraphQL." optional = false python-versions = "<4,>=3.6" files = [ - {file = "graphql_core-3.2.5-py3-none-any.whl", hash = "sha256:2f150d5096448aa4f8ab26268567bbfeef823769893b39c1a2e1409590939c8a"}, - {file = "graphql_core-3.2.5.tar.gz", hash = "sha256:e671b90ed653c808715645e3998b7ab67d382d55467b7e2978549111bbabf8d5"}, + {file = "graphql_core-3.2.6-py3-none-any.whl", hash = "sha256:78b016718c161a6fb20a7d97bbf107f331cd1afe53e45566c59f776ed7f0b45f"}, + {file = "graphql_core-3.2.6.tar.gz", hash = "sha256:c08eec22f9e40f0bd61d805907e3b3b1b9a320bc606e23dc145eebca07c8fbab"}, ] [package.dependencies] @@ -2414,6 +2414,20 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "prometheus-client" +version = "0.21.1" +description = "Python client for the Prometheus monitoring system." +optional = false +python-versions = ">=3.8" +files = [ + {file = "prometheus_client-0.21.1-py3-none-any.whl", hash = "sha256:594b45c410d6f4f8888940fe80b5cc2521b305a1fafe1c58609ef715a001f301"}, + {file = "prometheus_client-0.21.1.tar.gz", hash = "sha256:252505a722ac04b0456be05c05f75f45d760c2911ffc45f2a06bcaed9f3ae3fb"}, +] + +[package.extras] +twisted = ["twisted"] + [[package]] name = "propcache" version = "0.2.0" @@ -2714,13 +2728,13 @@ hook-testing = ["execnet (>=1.5.0)", "psutil", "pytest (>=2.7.3)"] [[package]] name = "pyinstaller-hooks-contrib" -version = "2024.11" +version = "2025.0" description = "Community maintained hooks for PyInstaller" optional = false python-versions = ">=3.8" files = [ - {file = "pyinstaller_hooks_contrib-2024.11-py3-none-any.whl", hash = "sha256:2781d121a1ee961152ba7287a262c65a1078da30c9ef7621cb8c819326884fd5"}, - {file = "pyinstaller_hooks_contrib-2024.11.tar.gz", hash = "sha256:84399af6b4b902030958063df25f657abbff249d0f329c5344928355c9833ab4"}, + {file = "pyinstaller_hooks_contrib-2025.0-py3-none-any.whl", hash = "sha256:3c0623799c3f81a37293127f485d65894c20fd718f722cb588785a3e52581ad1"}, + {file = "pyinstaller_hooks_contrib-2025.0.tar.gz", hash = "sha256:6dc0b55a1acaab2ffee36ed4a05b073aa0a22e46f25fb5c66a31e217454135ed"}, ] [package.dependencies] @@ -3009,6 +3023,7 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -3016,8 +3031,16 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -3034,6 +3057,7 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -3041,6 +3065,7 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -3472,13 +3497,13 @@ files = [ [[package]] name = "virtualenv" -version = "20.28.1" +version = "20.29.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" files = [ - {file = "virtualenv-20.28.1-py3-none-any.whl", hash = "sha256:412773c85d4dab0409b83ec36f7a6499e72eaf08c80e81e9576bca61831c71cb"}, - {file = "virtualenv-20.28.1.tar.gz", hash = "sha256:5d34ab240fdb5d21549b76f9e8ff3af28252f5499fb6d6f031adac4e5a8c5329"}, + {file = "virtualenv-20.29.1-py3-none-any.whl", hash = "sha256:4e4cb403c0b0da39e13b46b1b2476e505cb0046b25f242bee80f62bf990b2779"}, + {file = "virtualenv-20.29.1.tar.gz", hash = "sha256:b8b8970138d32fb606192cb97f6cd4bb644fa486be9308fb9b63f81091b5dc35"}, ] [package.dependencies] @@ -3829,4 +3854,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "<3.12,>=3.8" -content-hash = "1eb2ddd0e52eaf2c6ed97552b2aac8b485528d43707f06d6e2492b408ed76ab0" +content-hash = "7dc2b6dd43bcf3ffa5e3371d0561234162c9d183860a69d8e22779fb7312cb53" diff --git a/pyproject.toml b/pyproject.toml index 1a383910c..629c003a2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,6 +53,7 @@ jsonschema = "<4.4.0,>=4.3.0" openapi-core = "==0.15.0" openapi-spec-validator = "<0.5.0,>=0.4.0" pyinstaller = "==6.8.0" +prometheus_client = "==0.21.1" [tool.poetry.dependencies.tomte] version = "==0.2.17" diff --git a/tox.ini b/tox.ini index 430676e3a..6dd8544fe 100644 --- a/tox.ini +++ b/tox.ini @@ -75,6 +75,7 @@ deps = {[deps-base]deps} [extra-deps] deps = attrs + prometheus_client==0.21.1 pyinstaller==6.8.0 pyyaml<=6.0.1,>=3.10 werkzeug @@ -407,6 +408,9 @@ ignore_missing_imports = True [mypy-packages.open_aea.*] ignore_errors=True +[mypy-prometheus_client] +ignore_missing_imports = True + [mypy-toml.*] ignore_missing_imports = True