iwa 0.0.17__tar.gz → 0.0.18__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {iwa-0.0.17/src/iwa.egg-info → iwa-0.0.18}/PKG-INFO +1 -1
- {iwa-0.0.17 → iwa-0.0.18}/pyproject.toml +2 -2
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/chain/interface.py +1 -65
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/pricing.py +1 -4
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/services/transaction.py +72 -70
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/services/transfer/erc20.py +8 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/services/transfer/native.py +47 -13
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/activity_checker.py +2 -51
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/service_manager/mech.py +43 -25
- {iwa-0.0.17 → iwa-0.0.18/src/iwa.egg-info}/PKG-INFO +1 -1
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_chain.py +2 -97
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_legacy_wallet.py +6 -6
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_service_transaction.py +23 -8
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_staking_router.py +14 -6
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_transaction_service.py +20 -6
- {iwa-0.0.17 → iwa-0.0.18}/LICENSE +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/README.md +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/setup.cfg +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/__init__.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/__main__.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/__init__.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/chain/__init__.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/chain/errors.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/chain/manager.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/chain/models.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/chain/rate_limiter.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/cli.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/constants.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/contracts/__init__.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/contracts/abis/erc20.json +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/contracts/abis/multisend.json +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/contracts/abis/multisend_call_only.json +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/contracts/contract.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/contracts/erc20.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/contracts/multisend.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/db.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/ipfs.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/keys.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/mnemonic.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/models.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/monitor.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/plugins.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/secrets.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/services/__init__.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/services/account.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/services/balance.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/services/plugin.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/services/safe.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/services/transfer/__init__.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/services/transfer/base.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/services/transfer/multisend.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/services/transfer/swap.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/tables.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/test.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/tests/test_wallet.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/types.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/ui.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/utils.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/core/wallet.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/__init__.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/gnosis/__init__.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/gnosis/cow/__init__.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/gnosis/cow/quotes.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/gnosis/cow/swap.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/gnosis/cow/types.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/gnosis/cow_utils.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/gnosis/plugin.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/gnosis/safe.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/gnosis/tests/test_cow.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/gnosis/tests/test_safe.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/__init__.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/constants.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/abis/activity_checker.json +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/abis/mech.json +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/abis/mech_marketplace.json +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/abis/mech_new.json +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/abis/service_manager.json +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/abis/service_registry.json +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/abis/service_registry_token_utility.json +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/abis/staking.json +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/abis/staking_token.json +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/base.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/mech.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/mech_marketplace.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/service.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/staking.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/importer.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/mech_reference.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/models.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/plugin.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/scripts/test_full_mech_flow.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/scripts/test_simple_lifecycle.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/service_manager/__init__.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/service_manager/base.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/service_manager/drain.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/service_manager/lifecycle.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/service_manager/staking.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/conftest.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_importer.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_importer_error_handling.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_mech_contracts.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_olas_contracts.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_olas_integration.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_olas_models.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_olas_view.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_olas_view_actions.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_olas_view_modals.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_plugin.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_plugin_full.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_service_lifecycle.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_service_manager.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_service_manager_errors.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_service_manager_flows.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_service_manager_mech.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_service_manager_rewards.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_service_manager_validation.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_service_staking.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_staking_integration.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_staking_validation.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tui/__init__.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/plugins/olas/tui/olas_view.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tools/__init__.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tools/check_profile.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tools/list_contracts.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tools/release.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tools/reset_env.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tools/reset_tenderly.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tools/restore_backup.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tools/wallet_check.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tui/__init__.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tui/app.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tui/modals/__init__.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tui/modals/base.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tui/rpc.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tui/screens/__init__.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tui/screens/wallets.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tui/tests/test_app.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tui/tests/test_rpc.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tui/tests/test_wallets_refactor.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tui/tests/test_widgets.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tui/widgets/__init__.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tui/widgets/base.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/tui/workers.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/web/dependencies.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/web/models.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/web/routers/accounts.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/web/routers/olas/__init__.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/web/routers/olas/admin.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/web/routers/olas/funding.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/web/routers/olas/general.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/web/routers/olas/services.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/web/routers/olas/staking.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/web/routers/state.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/web/routers/swap.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/web/routers/transactions.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/web/server.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/web/static/app.js +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/web/static/index.html +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/web/static/style.css +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/web/tests/test_web_endpoints.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/web/tests/test_web_olas.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/web/tests/test_web_swap.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa/web/tests/test_web_swap_coverage.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa.egg-info/SOURCES.txt +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa.egg-info/dependency_links.txt +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa.egg-info/entry_points.txt +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa.egg-info/requires.txt +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/iwa.egg-info/top_level.txt +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/legacy_cow.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/legacy_safe.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/legacy_transaction_retry_logic.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/legacy_tui.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/legacy_wallets_screen.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/legacy_web.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_account_service.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_balance_service.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_chain_interface.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_chain_interface_coverage.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_cli.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_contract.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_db.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_drain_coverage.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_erc20.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_gnosis_plugin.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_keys.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_main.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_migration.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_mnemonic.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_modals.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_models.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_monitor.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_multisend.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_plugin_service.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_pricing.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_rate_limiter.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_reset_tenderly.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_rpc_rotation.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_rpc_view.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_safe_coverage.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_safe_service.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_service_manager_integration.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_service_manager_structure.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_staking_simple.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_tables.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_transfer_multisend.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_transfer_native.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_transfer_security.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_transfer_structure.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_transfer_swap_unit.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_ui_coverage.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_utils.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tests/test_workers.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tools/create_and_stake_service.py +0 -0
- {iwa-0.0.17 → iwa-0.0.18}/src/tools/verify_drain.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "iwa"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.18"
|
|
4
4
|
description = "A secure, modular, and plugin-based framework for crypto agents and ops"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12,<4.0"
|
|
@@ -71,7 +71,7 @@ where = ["src"]
|
|
|
71
71
|
|
|
72
72
|
[tool.ruff]
|
|
73
73
|
line-length = 100
|
|
74
|
-
target-version = "0.0.
|
|
74
|
+
target-version = "0.0.18"
|
|
75
75
|
fix = true
|
|
76
76
|
|
|
77
77
|
[tool.ruff.lint]
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import threading
|
|
4
4
|
import time
|
|
5
|
-
from typing import Callable, Dict, Optional,
|
|
5
|
+
from typing import Callable, Dict, Optional, TypeVar, Union
|
|
6
6
|
|
|
7
|
-
from eth_account.datastructures import SignedTransaction
|
|
8
7
|
from web3 import Web3
|
|
9
8
|
|
|
10
9
|
from iwa.core.chain.errors import TenderlyQuotaExceededError, sanitize_rpc_url
|
|
@@ -449,69 +448,6 @@ class ChainInterface:
|
|
|
449
448
|
|
|
450
449
|
return False
|
|
451
450
|
|
|
452
|
-
def send_native_transfer(
|
|
453
|
-
self,
|
|
454
|
-
from_address: EthereumAddress,
|
|
455
|
-
to_address: EthereumAddress,
|
|
456
|
-
value_wei: int,
|
|
457
|
-
sign_callback: Callable[[dict], SignedTransaction],
|
|
458
|
-
) -> Tuple[bool, Optional[str]]:
|
|
459
|
-
"""Send native currency transaction with retry logic."""
|
|
460
|
-
|
|
461
|
-
def _do_transfer() -> Tuple[bool, Optional[str]]:
|
|
462
|
-
tx = {
|
|
463
|
-
"from": from_address,
|
|
464
|
-
"to": to_address,
|
|
465
|
-
"value": value_wei,
|
|
466
|
-
"nonce": self.web3.eth.get_transaction_count(from_address),
|
|
467
|
-
"chainId": self.chain.chain_id,
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
balance_wei = self.get_native_balance_wei(from_address)
|
|
471
|
-
gas_price = self.web3.eth.gas_price
|
|
472
|
-
gas_estimate = self.web3.eth.estimate_gas(tx)
|
|
473
|
-
required_wei = value_wei + (gas_estimate * gas_price)
|
|
474
|
-
|
|
475
|
-
if balance_wei < required_wei:
|
|
476
|
-
logger.error(
|
|
477
|
-
f"Insufficient balance. "
|
|
478
|
-
f"Balance: {self.web3.from_wei(balance_wei, 'ether'):.4f} "
|
|
479
|
-
f"{self.chain.native_currency}, "
|
|
480
|
-
f"Required: {self.web3.from_wei(required_wei, 'ether'):.4f} "
|
|
481
|
-
f"{self.chain.native_currency}"
|
|
482
|
-
)
|
|
483
|
-
return False, None
|
|
484
|
-
|
|
485
|
-
tx["gas"] = gas_estimate
|
|
486
|
-
tx["gasPrice"] = gas_price
|
|
487
|
-
|
|
488
|
-
signed_tx = sign_callback(tx)
|
|
489
|
-
txn_hash = self.web3.eth.send_raw_transaction(signed_tx.raw_transaction)
|
|
490
|
-
receipt = self.web3.eth.wait_for_transaction_receipt(txn_hash)
|
|
491
|
-
|
|
492
|
-
status = getattr(receipt, "status", None)
|
|
493
|
-
if status is None and isinstance(receipt, dict):
|
|
494
|
-
status = receipt.get("status")
|
|
495
|
-
|
|
496
|
-
if receipt and status == 1:
|
|
497
|
-
self.wait_for_no_pending_tx(from_address)
|
|
498
|
-
logger.info(f"Transaction sent successfully. Tx Hash: {txn_hash.hex()}")
|
|
499
|
-
# Check Tenderly block limit after each successful transaction
|
|
500
|
-
self.check_block_limit()
|
|
501
|
-
return True, receipt["transactionHash"].hex()
|
|
502
|
-
|
|
503
|
-
logger.error("Transaction failed (status != 1)")
|
|
504
|
-
return False, None
|
|
505
|
-
|
|
506
|
-
try:
|
|
507
|
-
return self.with_retry(
|
|
508
|
-
_do_transfer,
|
|
509
|
-
operation_name=f"native_transfer to {str(to_address)[:10]}...",
|
|
510
|
-
)
|
|
511
|
-
except Exception as e:
|
|
512
|
-
logger.exception(f"Native transfer failed: {e}")
|
|
513
|
-
return False, None
|
|
514
|
-
|
|
515
451
|
def get_token_address(self, token_name: str) -> Optional[EthereumAddress]:
|
|
516
452
|
"""Get token address by name"""
|
|
517
453
|
return self.chain.get_token_address(token_name)
|
|
@@ -19,7 +19,6 @@ class PriceService:
|
|
|
19
19
|
"""Service to fetch token prices from CoinGecko."""
|
|
20
20
|
|
|
21
21
|
BASE_URL = "https://api.coingecko.com/api/v3"
|
|
22
|
-
DEMO_URL = "https://demo-api.coingecko.com/api/v3"
|
|
23
22
|
|
|
24
23
|
def __init__(self):
|
|
25
24
|
"""Initialize PriceService."""
|
|
@@ -60,9 +59,7 @@ class PriceService:
|
|
|
60
59
|
max_retries = 2
|
|
61
60
|
for attempt in range(max_retries + 1):
|
|
62
61
|
try:
|
|
63
|
-
|
|
64
|
-
# NOTE: Demo URL is significantly more reliable for demo keys
|
|
65
|
-
base_url = self.DEMO_URL if self.api_key else self.BASE_URL
|
|
62
|
+
base_url = self.BASE_URL
|
|
66
63
|
url = f"{base_url}/simple/price"
|
|
67
64
|
params = {"ids": token_id, "vs_currencies": vs_currency}
|
|
68
65
|
headers = {}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"""Transaction service module."""
|
|
2
2
|
|
|
3
|
-
import time
|
|
4
3
|
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
|
|
5
4
|
|
|
6
5
|
from loguru import logger
|
|
@@ -31,32 +30,37 @@ class TransferLogger:
|
|
|
31
30
|
self.account_service = account_service
|
|
32
31
|
self.chain_interface = chain_interface
|
|
33
32
|
|
|
34
|
-
def log_transfers(self, receipt: Dict
|
|
33
|
+
def log_transfers(self, receipt: Dict) -> None:
|
|
35
34
|
"""Log all transfers (ERC20 and native) from a transaction receipt.
|
|
36
35
|
|
|
37
36
|
Args:
|
|
38
37
|
receipt: Transaction receipt containing logs.
|
|
39
|
-
tx: Original transaction dict.
|
|
40
38
|
|
|
41
39
|
"""
|
|
42
|
-
#
|
|
43
|
-
|
|
44
|
-
if
|
|
45
|
-
|
|
40
|
+
# Get the original transaction to check for native value transfer
|
|
41
|
+
tx_hash = receipt.get("transactionHash") or getattr(receipt, "transactionHash", None)
|
|
42
|
+
if tx_hash:
|
|
43
|
+
try:
|
|
44
|
+
tx = self.chain_interface.web3.eth.get_transaction(tx_hash)
|
|
45
|
+
native_value = getattr(tx, "value", 0) or tx.get("value", 0) if isinstance(tx, dict) else getattr(tx, "value", 0)
|
|
46
|
+
if native_value and int(native_value) > 0:
|
|
47
|
+
from_addr = getattr(tx, "from", "") if hasattr(tx, "from") else tx.get("from", "")
|
|
48
|
+
# Handle AttributeDict's special 'from' attribute
|
|
49
|
+
if not from_addr and hasattr(tx, "__getitem__"):
|
|
50
|
+
from_addr = tx["from"]
|
|
51
|
+
to_addr = getattr(tx, "to", "") or (tx.get("to", "") if isinstance(tx, dict) else "")
|
|
52
|
+
self._log_native_transfer(from_addr, to_addr, native_value)
|
|
53
|
+
except Exception as e:
|
|
54
|
+
logger.debug(f"Could not get tx for native transfer logging: {e}")
|
|
46
55
|
|
|
47
56
|
# Log ERC20 transfers from event logs
|
|
48
|
-
logs = receipt.get("logs", [])
|
|
49
|
-
if hasattr(receipt, "logs"):
|
|
50
|
-
logs = receipt.logs
|
|
57
|
+
logs = receipt.get("logs", []) if isinstance(receipt, dict) else getattr(receipt, "logs", [])
|
|
51
58
|
|
|
52
59
|
for log in logs:
|
|
53
60
|
self._process_log(log)
|
|
54
61
|
|
|
55
|
-
def _log_native_transfer(self,
|
|
62
|
+
def _log_native_transfer(self, from_addr: str, to_addr: str, value_wei: int) -> None:
|
|
56
63
|
"""Log a native currency transfer."""
|
|
57
|
-
from_addr = tx.get("from", "")
|
|
58
|
-
to_addr = tx.get("to", "")
|
|
59
|
-
|
|
60
64
|
from_label = self._resolve_address_label(from_addr)
|
|
61
65
|
to_label = self._resolve_address_label(to_addr)
|
|
62
66
|
|
|
@@ -213,53 +217,67 @@ class TransactionService:
|
|
|
213
217
|
chain_name: str = "gnosis",
|
|
214
218
|
tags: Optional[List[str]] = None,
|
|
215
219
|
) -> Tuple[bool, Dict]:
|
|
216
|
-
"""Sign and send a transaction
|
|
220
|
+
"""Sign and send a transaction using unified retry mechanism.
|
|
221
|
+
|
|
222
|
+
Uses ChainInterface.with_retry() for consistent RPC rotation and retry logic.
|
|
223
|
+
Gas errors are handled by increasing gas and retrying within the same mechanism.
|
|
224
|
+
"""
|
|
217
225
|
chain_interface = ChainInterfaces().get(chain_name)
|
|
218
226
|
tx = dict(transaction)
|
|
219
|
-
max_retries = 10
|
|
220
227
|
|
|
221
228
|
if not self._prepare_transaction(tx, signer_address_or_tag, chain_interface):
|
|
222
229
|
return False, {}
|
|
223
230
|
|
|
224
|
-
|
|
231
|
+
# Mutable state for retry attempts
|
|
232
|
+
state = {"gas_retries": 0, "max_gas_retries": 5}
|
|
233
|
+
|
|
234
|
+
def _do_sign_send_wait() -> Tuple[bool, Dict, bytes]:
|
|
235
|
+
"""Inner operation wrapped by with_retry."""
|
|
225
236
|
try:
|
|
226
237
|
signed_txn = self.key_storage.sign_transaction(tx, signer_address_or_tag)
|
|
227
|
-
txn_hash = chain_interface.web3.eth.send_raw_transaction(
|
|
228
|
-
|
|
229
|
-
# Use chain_interface.with_retry for waiting for receipt to handle timeouts/RPC errors
|
|
230
|
-
def wait_for_receipt(tx_h=txn_hash):
|
|
231
|
-
return chain_interface.web3.eth.wait_for_transaction_receipt(tx_h)
|
|
232
|
-
|
|
233
|
-
receipt = chain_interface.with_retry(
|
|
234
|
-
wait_for_receipt, operation_name="wait_for_receipt"
|
|
238
|
+
txn_hash = chain_interface.web3.eth.send_raw_transaction(
|
|
239
|
+
signed_txn.raw_transaction
|
|
235
240
|
)
|
|
241
|
+
receipt = chain_interface.web3.eth.wait_for_transaction_receipt(txn_hash)
|
|
236
242
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
logger.info(f"Transaction sent successfully. Tx Hash: {txn_hash.hex()}")
|
|
241
|
-
|
|
242
|
-
self._log_successful_transaction(
|
|
243
|
-
receipt, tx, signer_account, chain_name, txn_hash, tags, chain_interface
|
|
244
|
-
)
|
|
245
|
-
return True, receipt
|
|
243
|
+
status = getattr(receipt, "status", None)
|
|
244
|
+
if status is None and isinstance(receipt, dict):
|
|
245
|
+
status = receipt.get("status")
|
|
246
246
|
|
|
247
|
-
|
|
247
|
+
if receipt and status == 1:
|
|
248
|
+
return True, receipt, txn_hash
|
|
249
|
+
# Transaction mined but reverted - don't retry
|
|
248
250
|
logger.error("Transaction failed (status 0).")
|
|
249
|
-
|
|
251
|
+
raise ValueError("Transaction reverted")
|
|
250
252
|
|
|
251
253
|
except web3_exceptions.Web3RPCError as e:
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
254
|
+
# Handle gas errors by increasing gas and re-raising
|
|
255
|
+
self._handle_gas_retry(e, tx, state)
|
|
256
|
+
raise # Re-raise to trigger with_retry's retry mechanism
|
|
255
257
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
258
|
+
try:
|
|
259
|
+
success, receipt, txn_hash = chain_interface.with_retry(
|
|
260
|
+
_do_sign_send_wait,
|
|
261
|
+
operation_name=f"sign_and_send to {tx.get('to', 'unknown')[:10]}...",
|
|
262
|
+
)
|
|
263
|
+
if success:
|
|
264
|
+
signer_account = self.account_service.resolve_account(signer_address_or_tag)
|
|
265
|
+
chain_interface.wait_for_no_pending_tx(signer_account.address)
|
|
266
|
+
logger.info(f"Transaction sent successfully. Tx Hash: {txn_hash.hex()}")
|
|
267
|
+
self._log_successful_transaction(
|
|
268
|
+
receipt, tx, signer_account, chain_name, txn_hash, tags, chain_interface
|
|
269
|
+
)
|
|
270
|
+
return True, receipt
|
|
271
|
+
return False, {}
|
|
272
|
+
except ValueError as e:
|
|
273
|
+
# Transaction reverted - already logged
|
|
274
|
+
if "reverted" in str(e).lower():
|
|
260
275
|
return False, {}
|
|
261
|
-
|
|
262
|
-
|
|
276
|
+
logger.exception(f"Transaction failed: {e}")
|
|
277
|
+
return False, {}
|
|
278
|
+
except Exception as e:
|
|
279
|
+
logger.exception(f"Transaction failed after retries: {e}")
|
|
280
|
+
return False, {}
|
|
263
281
|
|
|
264
282
|
def _prepare_transaction(self, tx: dict, signer_tag: str, chain_interface) -> bool:
|
|
265
283
|
"""Ensure nonce and chainId are set."""
|
|
@@ -274,32 +292,16 @@ class TransactionService:
|
|
|
274
292
|
tx["chainId"] = chain_interface.chain.chain_id
|
|
275
293
|
return True
|
|
276
294
|
|
|
277
|
-
def
|
|
278
|
-
|
|
279
|
-
if self._is_gas_too_low_error(
|
|
280
|
-
logger.warning(
|
|
281
|
-
f"Gas too low error detected. Retrying with increased gas (Attempt {attempt}/{max_retries})..."
|
|
282
|
-
)
|
|
295
|
+
def _handle_gas_retry(self, e: Exception, tx: dict, state: dict) -> None:
|
|
296
|
+
"""Increase gas if error is gas-related and retries remaining."""
|
|
297
|
+
if self._is_gas_too_low_error(str(e)) and state["gas_retries"] < state["max_gas_retries"]:
|
|
283
298
|
current_gas = int(tx.get("gas", 30_000))
|
|
284
299
|
tx["gas"] = int(current_gas * 1.5)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
return False
|
|
291
|
-
|
|
292
|
-
def _handle_generic_error(self, e, chain_interface, attempt, max_retries) -> bool:
|
|
293
|
-
if attempt < max_retries:
|
|
294
|
-
logger.warning(f"Error encountered: {e}. Attempting to rotate RPC...")
|
|
295
|
-
|
|
296
|
-
if chain_interface.rotate_rpc():
|
|
297
|
-
logger.info("Retrying with new RPC...")
|
|
298
|
-
# Exponential backoff
|
|
299
|
-
time.sleep(min(2**attempt, 30))
|
|
300
|
-
return True
|
|
301
|
-
logger.exception(f"Unexpected error sending transaction: {e}")
|
|
302
|
-
return False
|
|
300
|
+
state["gas_retries"] += 1
|
|
301
|
+
logger.warning(
|
|
302
|
+
f"Gas too low, increasing to {tx['gas']} "
|
|
303
|
+
f"(attempt {state['gas_retries']}/{state['max_gas_retries']})"
|
|
304
|
+
)
|
|
303
305
|
|
|
304
306
|
def _log_successful_transaction(
|
|
305
307
|
self, receipt, tx, signer_account, chain_name, txn_hash, tags, chain_interface
|
|
@@ -323,7 +325,7 @@ class TransactionService:
|
|
|
323
325
|
|
|
324
326
|
# Log transfer events (ERC20 and native value)
|
|
325
327
|
transfer_logger = TransferLogger(self.account_service, chain_interface)
|
|
326
|
-
transfer_logger.log_transfers(receipt
|
|
328
|
+
transfer_logger.log_transfers(receipt)
|
|
327
329
|
|
|
328
330
|
except Exception as log_err:
|
|
329
331
|
logger.warning(f"Failed to log transaction: {log_err}")
|
|
@@ -64,6 +64,14 @@ class ERC20TransferMixin:
|
|
|
64
64
|
value_eur=v_eur,
|
|
65
65
|
tags=["erc20-transfer", "safe-transaction"],
|
|
66
66
|
)
|
|
67
|
+
|
|
68
|
+
# Log transfers extracted from receipt events
|
|
69
|
+
if receipt:
|
|
70
|
+
from iwa.core.services.transaction import TransferLogger
|
|
71
|
+
|
|
72
|
+
transfer_logger = TransferLogger(self.account_service, interface)
|
|
73
|
+
transfer_logger.log_transfers(receipt)
|
|
74
|
+
|
|
67
75
|
return tx_hash
|
|
68
76
|
|
|
69
77
|
def _send_erc20_via_eoa(
|
|
@@ -61,6 +61,15 @@ class NativeTransferMixin:
|
|
|
61
61
|
value_eur=v_eur,
|
|
62
62
|
tags=["native-transfer", "safe-transaction"],
|
|
63
63
|
)
|
|
64
|
+
|
|
65
|
+
# Log transfers extracted from receipt events
|
|
66
|
+
if receipt:
|
|
67
|
+
from iwa.core.services.transaction import TransferLogger
|
|
68
|
+
|
|
69
|
+
interface = ChainInterfaces().get(chain_name)
|
|
70
|
+
transfer_logger = TransferLogger(self.account_service, interface)
|
|
71
|
+
transfer_logger.log_transfers(receipt)
|
|
72
|
+
|
|
64
73
|
return tx_hash
|
|
65
74
|
|
|
66
75
|
def _send_native_via_eoa(
|
|
@@ -74,20 +83,38 @@ class NativeTransferMixin:
|
|
|
74
83
|
to_tag: Optional[str],
|
|
75
84
|
token_symbol: str,
|
|
76
85
|
) -> Optional[str]:
|
|
77
|
-
"""Send native currency via EOA
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
86
|
+
"""Send native currency via EOA using unified TransactionService."""
|
|
87
|
+
# Build transaction dict
|
|
88
|
+
try:
|
|
89
|
+
gas_price = chain_interface.web3.eth.gas_price
|
|
90
|
+
gas_estimate = chain_interface.web3.eth.estimate_gas({
|
|
91
|
+
"from": from_account.address,
|
|
92
|
+
"to": to_address,
|
|
93
|
+
"value": amount_wei,
|
|
94
|
+
})
|
|
95
|
+
except Exception as e:
|
|
96
|
+
logger.error(f"Failed to estimate gas for native transfer: {e}")
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
tx = {
|
|
100
|
+
"from": from_account.address,
|
|
101
|
+
"to": to_address,
|
|
102
|
+
"value": amount_wei,
|
|
103
|
+
"gas": gas_estimate,
|
|
104
|
+
"gasPrice": gas_price,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
# Use unified TransactionService
|
|
108
|
+
success, receipt = self.transaction_service.sign_and_send(
|
|
109
|
+
tx, from_account.address, chain_name, tags=["native-transfer"]
|
|
83
110
|
)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
111
|
+
|
|
112
|
+
if success and receipt:
|
|
113
|
+
tx_hash = receipt.get("transactionHash", b"")
|
|
114
|
+
if hasattr(tx_hash, "hex"):
|
|
115
|
+
tx_hash = tx_hash.hex()
|
|
116
|
+
elif isinstance(tx_hash, bytes):
|
|
117
|
+
tx_hash = tx_hash.hex()
|
|
91
118
|
|
|
92
119
|
gas_cost, gas_value_eur = self._calculate_gas_info(receipt, chain_name)
|
|
93
120
|
p_eur, v_eur = self._get_token_price_info(token_symbol, amount_wei, chain_name)
|
|
@@ -106,6 +133,13 @@ class NativeTransferMixin:
|
|
|
106
133
|
value_eur=v_eur,
|
|
107
134
|
tags=["native-transfer"],
|
|
108
135
|
)
|
|
136
|
+
|
|
137
|
+
# Log transfers extracted from receipt events
|
|
138
|
+
from iwa.core.services.transaction import TransferLogger
|
|
139
|
+
|
|
140
|
+
transfer_logger = TransferLogger(self.account_service, chain_interface)
|
|
141
|
+
transfer_logger.log_transfers(receipt)
|
|
142
|
+
|
|
109
143
|
return tx_hash
|
|
110
144
|
return None
|
|
111
145
|
|
|
@@ -10,8 +10,6 @@ requests relative to the time elapsed since the last checkpoint.
|
|
|
10
10
|
|
|
11
11
|
from typing import Tuple
|
|
12
12
|
|
|
13
|
-
from web3 import Web3
|
|
14
|
-
|
|
15
13
|
from iwa.core.constants import DEFAULT_MECH_CONTRACT_ADDRESS
|
|
16
14
|
from iwa.core.types import EthereumAddress
|
|
17
15
|
from iwa.plugins.olas.contracts.base import OLAS_ABI_PATH, ContractInstance
|
|
@@ -70,9 +68,6 @@ class ActivityCheckerContract(ContractInstance):
|
|
|
70
68
|
def get_multisig_nonces(self, multisig: EthereumAddress) -> Tuple[int, int]:
|
|
71
69
|
"""Get the nonces for a multisig address.
|
|
72
70
|
|
|
73
|
-
This method reads directly from the source contracts to ensure fresh data
|
|
74
|
-
and compatibility with Legacy/MM contracts.
|
|
75
|
-
|
|
76
71
|
Args:
|
|
77
72
|
multisig: The multisig address to check.
|
|
78
73
|
|
|
@@ -82,52 +77,8 @@ class ActivityCheckerContract(ContractInstance):
|
|
|
82
77
|
- mech_requests_count: Total mech requests made
|
|
83
78
|
|
|
84
79
|
"""
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
try:
|
|
88
|
-
# Minimal ABI for Safe nonce
|
|
89
|
-
safe_abi = [{"name": "nonce", "type": "function", "inputs": [], "outputs": [{"type": "uint256"}]}]
|
|
90
|
-
safe_contract = self.chain_interface.web3._web3.eth.contract(
|
|
91
|
-
address=Web3.to_checksum_address(multisig), abi=safe_abi
|
|
92
|
-
)
|
|
93
|
-
safe_nonce = safe_contract.functions.nonce().call()
|
|
94
|
-
except Exception as e:
|
|
95
|
-
# Fallback or log error? If safe read fails, something is very wrong.
|
|
96
|
-
# But we don't want to crash the whole status check if possible.
|
|
97
|
-
# For now, let's log and keep 0 or re-raise if critical.
|
|
98
|
-
# safe_nonce is critical for liveness check (diffNonces).
|
|
99
|
-
from loguru import logger
|
|
100
|
-
logger.warning(f"Failed to read Safe nonce for {multisig}: {e}")
|
|
101
|
-
|
|
102
|
-
# 2. Get Mech Requests Count
|
|
103
|
-
mech_requests = 0
|
|
104
|
-
|
|
105
|
-
if self.mech_marketplace:
|
|
106
|
-
# Case A: Marketplace (MM)
|
|
107
|
-
try:
|
|
108
|
-
# Minimal ABI for Marketplace mapRequestCounts
|
|
109
|
-
mp_abi = [{"name": "mapRequestCounts", "type": "function", "inputs": [{"type": "address"}], "outputs": [{"type": "uint256"}]}]
|
|
110
|
-
mp_contract = self.chain_interface.web3._web3.eth.contract(
|
|
111
|
-
address=Web3.to_checksum_address(self.mech_marketplace), abi=mp_abi
|
|
112
|
-
)
|
|
113
|
-
mech_requests = mp_contract.functions.mapRequestCounts(multisig).call()
|
|
114
|
-
except Exception as e:
|
|
115
|
-
from loguru import logger
|
|
116
|
-
logger.warning(f"Failed to read Marketplace requests for {multisig}: {e}")
|
|
117
|
-
else:
|
|
118
|
-
# Case B: Legacy (AgentMech)
|
|
119
|
-
try:
|
|
120
|
-
# Minimal ABI for AgentMech getRequestsCount
|
|
121
|
-
mech_abi = [{"name": "getRequestsCount", "type": "function", "inputs": [{"type": "address"}], "outputs": [{"type": "uint256"}]}]
|
|
122
|
-
mech_contract = self.chain_interface.web3._web3.eth.contract(
|
|
123
|
-
address=Web3.to_checksum_address(self.agent_mech), abi=mech_abi
|
|
124
|
-
)
|
|
125
|
-
mech_requests = mech_contract.functions.getRequestsCount(multisig).call()
|
|
126
|
-
except Exception as e:
|
|
127
|
-
from loguru import logger
|
|
128
|
-
logger.warning(f"Failed to read AgentMech requests for {multisig}: {e}")
|
|
129
|
-
|
|
130
|
-
return (safe_nonce, mech_requests)
|
|
80
|
+
nonces = self.contract.functions.getMultisigNonces(multisig).call()
|
|
81
|
+
return (nonces[0], nonces[1])
|
|
131
82
|
|
|
132
83
|
def is_ratio_pass(
|
|
133
84
|
self,
|
|
@@ -140,11 +140,13 @@ class MechManagerMixin:
|
|
|
140
140
|
)
|
|
141
141
|
if use_marketplace:
|
|
142
142
|
priority_mech = priority_mech or detected_priority_mech
|
|
143
|
+
mech_address = mech_address or detected_marketplace
|
|
143
144
|
|
|
144
145
|
if use_marketplace:
|
|
145
146
|
return self._send_marketplace_mech_request(
|
|
146
147
|
data=data,
|
|
147
148
|
value=value,
|
|
149
|
+
marketplace_address=mech_address,
|
|
148
150
|
priority_mech=priority_mech,
|
|
149
151
|
max_delivery_rate=max_delivery_rate,
|
|
150
152
|
payment_type=payment_type,
|
|
@@ -260,10 +262,39 @@ class MechManagerMixin:
|
|
|
260
262
|
|
|
261
263
|
return True
|
|
262
264
|
|
|
265
|
+
def _resolve_marketplace_config(
|
|
266
|
+
self, marketplace_addr: Optional[str], priority_addr: Optional[str]
|
|
267
|
+
) -> tuple[str, str]:
|
|
268
|
+
"""Resolve marketplace and priority mech addresses. Returns (marketplace, priority)."""
|
|
269
|
+
chain_name = self.chain_name if self.service else getattr(self, "chain_name", "gnosis")
|
|
270
|
+
protocol_contracts = OLAS_CONTRACTS.get(chain_name, {})
|
|
271
|
+
|
|
272
|
+
resolved_mp = marketplace_addr or protocol_contracts.get("OLAS_MECH_MARKETPLACE")
|
|
273
|
+
if not resolved_mp:
|
|
274
|
+
raise ValueError(f"Mech Marketplace address not found for chain {chain_name}")
|
|
275
|
+
|
|
276
|
+
if not priority_addr:
|
|
277
|
+
raise ValueError("priority_mech is required for marketplace requests")
|
|
278
|
+
|
|
279
|
+
return str(resolved_mp), Web3.to_checksum_address(priority_addr)
|
|
280
|
+
|
|
281
|
+
def _prepare_marketplace_params(
|
|
282
|
+
self,
|
|
283
|
+
value: Optional[int],
|
|
284
|
+
max_delivery_rate: Optional[int],
|
|
285
|
+
payment_type: Optional[bytes],
|
|
286
|
+
) -> tuple[int, int, bytes]:
|
|
287
|
+
"""Prepare default values for marketplace parameters."""
|
|
288
|
+
p_type = payment_type or bytes.fromhex(PAYMENT_TYPE_NATIVE)
|
|
289
|
+
val = value if value is not None else 10_000_000_000_000_000
|
|
290
|
+
rate = max_delivery_rate if max_delivery_rate is not None else val
|
|
291
|
+
return val, rate, p_type
|
|
292
|
+
|
|
263
293
|
def _send_marketplace_mech_request(
|
|
264
294
|
self,
|
|
265
295
|
data: bytes,
|
|
266
296
|
value: Optional[int] = None,
|
|
297
|
+
marketplace_address: Optional[str] = None,
|
|
267
298
|
priority_mech: Optional[str] = None,
|
|
268
299
|
max_delivery_rate: Optional[int] = None,
|
|
269
300
|
payment_type: Optional[bytes] = None,
|
|
@@ -275,43 +306,30 @@ class MechManagerMixin:
|
|
|
275
306
|
logger.error("No active service")
|
|
276
307
|
return None
|
|
277
308
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
logger.error(f"Mech Marketplace address not found for chain {chain_name}")
|
|
285
|
-
return None
|
|
286
|
-
|
|
287
|
-
if not priority_mech:
|
|
288
|
-
logger.error("priority_mech is required for marketplace requests")
|
|
309
|
+
try:
|
|
310
|
+
marketplace_address, priority_mech = self._resolve_marketplace_config(
|
|
311
|
+
marketplace_address, priority_mech
|
|
312
|
+
)
|
|
313
|
+
except ValueError as e:
|
|
314
|
+
logger.error(e)
|
|
289
315
|
return None
|
|
290
316
|
|
|
291
|
-
|
|
292
|
-
marketplace = MechMarketplaceContract(str(marketplace_address), chain_name=chain_name)
|
|
317
|
+
marketplace = MechMarketplaceContract(marketplace_address, chain_name=self.chain_name)
|
|
293
318
|
|
|
294
319
|
if not self._validate_priority_mech(marketplace, priority_mech):
|
|
295
320
|
return None
|
|
296
321
|
|
|
297
|
-
# Set defaults for payment
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
if value is None:
|
|
302
|
-
value = 10_000_000_000_000_000
|
|
303
|
-
logger.info(f"Using default value: {value} wei (0.01 xDAI)")
|
|
304
|
-
|
|
305
|
-
if max_delivery_rate is None:
|
|
306
|
-
max_delivery_rate = value
|
|
307
|
-
logger.info(f"Using value as max_delivery_rate: {max_delivery_rate}")
|
|
322
|
+
# Set defaults for payment and delivery
|
|
323
|
+
value, max_delivery_rate, payment_type = self._prepare_marketplace_params(
|
|
324
|
+
value, max_delivery_rate, payment_type
|
|
325
|
+
)
|
|
308
326
|
|
|
309
327
|
if not self._validate_marketplace_params(marketplace, response_timeout, payment_type):
|
|
310
328
|
return None
|
|
311
329
|
|
|
312
330
|
# Prepare transaction
|
|
313
331
|
tx_data = marketplace.prepare_request_tx(
|
|
314
|
-
from_address=multisig_address,
|
|
332
|
+
from_address=self.service.multisig_address,
|
|
315
333
|
request_data=data,
|
|
316
334
|
priority_mech=priority_mech,
|
|
317
335
|
response_timeout=response_timeout,
|