iwa 0.0.10__tar.gz → 0.0.11__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.10/src/iwa.egg-info → iwa-0.0.11}/PKG-INFO +1 -1
- {iwa-0.0.10 → iwa-0.0.11}/pyproject.toml +2 -2
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/chain/interface.py +21 -7
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/services/transaction.py +196 -3
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/wallet.py +2 -1
- iwa-0.0.11/src/iwa/plugins/olas/contracts/abis/service_registry_token_utility.json +926 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/service.py +50 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/service_manager/lifecycle.py +275 -39
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/service_manager/staking.py +152 -63
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_olas_contracts.py +6 -2
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_service_lifecycle.py +1 -4
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_service_manager.py +59 -89
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_service_manager_errors.py +1 -2
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_service_manager_flows.py +5 -15
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/web/routers/olas/services.py +52 -19
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/web/routers/olas/staking.py +8 -2
- {iwa-0.0.10 → iwa-0.0.11/src/iwa.egg-info}/PKG-INFO +1 -1
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa.egg-info/SOURCES.txt +1 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_chain.py +12 -7
- {iwa-0.0.10 → iwa-0.0.11}/LICENSE +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/README.md +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/setup.cfg +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/__init__.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/__main__.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/__init__.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/chain/__init__.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/chain/errors.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/chain/manager.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/chain/models.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/chain/rate_limiter.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/cli.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/constants.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/contracts/__init__.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/contracts/abis/erc20.json +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/contracts/abis/multisend.json +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/contracts/abis/multisend_call_only.json +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/contracts/contract.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/contracts/erc20.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/contracts/multisend.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/db.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/ipfs.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/keys.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/mnemonic.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/models.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/monitor.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/plugins.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/pricing.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/secrets.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/services/__init__.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/services/account.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/services/balance.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/services/plugin.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/services/safe.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/services/transfer/__init__.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/services/transfer/base.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/services/transfer/erc20.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/services/transfer/multisend.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/services/transfer/native.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/services/transfer/swap.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/tables.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/test.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/tests/test_wallet.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/types.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/ui.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/core/utils.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/__init__.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/gnosis/__init__.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/gnosis/cow/__init__.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/gnosis/cow/quotes.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/gnosis/cow/swap.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/gnosis/cow/types.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/gnosis/cow_utils.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/gnosis/plugin.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/gnosis/safe.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/gnosis/tests/test_cow.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/gnosis/tests/test_safe.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/__init__.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/constants.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/abis/activity_checker.json +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/abis/mech.json +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/abis/mech_marketplace.json +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/abis/mech_new.json +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/abis/service_manager.json +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/abis/service_registry.json +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/abis/staking.json +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/abis/staking_token.json +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/activity_checker.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/base.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/mech.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/mech_marketplace.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/staking.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/importer.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/mech_reference.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/models.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/plugin.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/scripts/test_full_mech_flow.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/scripts/test_simple_lifecycle.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/service_manager/__init__.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/service_manager/base.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/service_manager/drain.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/service_manager/mech.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/conftest.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_importer.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_importer_error_handling.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_mech_contracts.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_olas_integration.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_olas_models.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_olas_view.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_olas_view_actions.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_olas_view_modals.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_plugin.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_plugin_full.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_service_manager_mech.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_service_manager_rewards.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_service_manager_validation.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_service_staking.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_staking_integration.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_staking_validation.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tui/__init__.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/plugins/olas/tui/olas_view.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tools/__init__.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tools/check_profile.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tools/list_contracts.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tools/release.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tools/reset_env.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tools/reset_tenderly.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tools/restore_backup.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tools/wallet_check.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tui/__init__.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tui/app.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tui/modals/__init__.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tui/modals/base.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tui/rpc.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tui/screens/__init__.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tui/screens/wallets.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tui/tests/test_app.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tui/tests/test_rpc.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tui/tests/test_wallets_refactor.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tui/tests/test_widgets.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tui/widgets/__init__.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tui/widgets/base.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/tui/workers.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/web/dependencies.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/web/models.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/web/routers/accounts.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/web/routers/olas/__init__.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/web/routers/olas/admin.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/web/routers/olas/funding.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/web/routers/olas/general.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/web/routers/state.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/web/routers/swap.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/web/routers/transactions.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/web/server.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/web/static/app.js +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/web/static/index.html +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/web/static/style.css +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/web/tests/test_web_endpoints.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/web/tests/test_web_olas.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/web/tests/test_web_swap.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa/web/tests/test_web_swap_coverage.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa.egg-info/dependency_links.txt +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa.egg-info/entry_points.txt +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa.egg-info/requires.txt +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/iwa.egg-info/top_level.txt +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/legacy_cow.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/legacy_safe.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/legacy_transaction_retry_logic.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/legacy_tui.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/legacy_wallets_screen.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/legacy_web.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_account_service.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_balance_service.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_chain_interface.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_chain_interface_coverage.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_cli.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_contract.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_db.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_drain_coverage.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_erc20.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_gnosis_plugin.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_keys.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_legacy_wallet.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_main.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_migration.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_mnemonic.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_modals.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_models.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_monitor.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_multisend.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_plugin_service.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_pricing.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_rate_limiter.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_reset_tenderly.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_rpc_view.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_safe_coverage.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_safe_service.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_service_manager_integration.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_service_manager_structure.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_service_transaction.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_staking_router.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_staking_simple.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_tables.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_transaction_service.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_transfer_multisend.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_transfer_native.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_transfer_security.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_transfer_structure.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_transfer_swap_unit.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_ui_coverage.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_utils.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tests/test_workers.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/src/tools/create_and_stake_service.py +0 -0
- {iwa-0.0.10 → iwa-0.0.11}/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.11"
|
|
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.11"
|
|
75
75
|
fix = true
|
|
76
76
|
|
|
77
77
|
[tool.ruff.lint]
|
|
@@ -365,15 +365,29 @@ class ChainInterface:
|
|
|
365
365
|
except Exception:
|
|
366
366
|
return address[:6] + "..." + address[-4:]
|
|
367
367
|
|
|
368
|
-
def get_token_decimals(self, address: EthereumAddress) -> int:
|
|
369
|
-
"""Get token decimals for an address.
|
|
370
|
-
try:
|
|
371
|
-
from iwa.core.contracts.erc20 import ERC20Contract
|
|
368
|
+
def get_token_decimals(self, address: EthereumAddress, fallback_to_18: bool = True) -> Optional[int]:
|
|
369
|
+
"""Get token decimals for an address.
|
|
372
370
|
|
|
373
|
-
|
|
374
|
-
|
|
371
|
+
Args:
|
|
372
|
+
address: Token contract address.
|
|
373
|
+
fallback_to_18: If True, return 18 on error (default).
|
|
374
|
+
If False, return None on error (useful for detecting NFTs).
|
|
375
|
+
|
|
376
|
+
Returns:
|
|
377
|
+
Decimals as int, or None if error and fallback_to_18 is False.
|
|
378
|
+
|
|
379
|
+
"""
|
|
380
|
+
try:
|
|
381
|
+
# Call decimals() directly without with_retry to avoid error logging
|
|
382
|
+
contract = self.web3.eth.contract(
|
|
383
|
+
address=self.web3.to_checksum_address(address),
|
|
384
|
+
abi=[{"constant": True, "inputs": [], "name": "decimals", "outputs": [{"type": "uint8"}], "type": "function"}]
|
|
385
|
+
)
|
|
386
|
+
return contract.functions.decimals().call()
|
|
375
387
|
except Exception:
|
|
376
|
-
|
|
388
|
+
if fallback_to_18:
|
|
389
|
+
return 18
|
|
390
|
+
return None
|
|
377
391
|
|
|
378
392
|
def get_native_balance_wei(self, address: EthereumAddress):
|
|
379
393
|
"""Get the native balance in wei"""
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"""Transaction service module."""
|
|
2
2
|
|
|
3
3
|
import time
|
|
4
|
-
from typing import Dict, List, Optional, Tuple
|
|
4
|
+
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
|
|
5
5
|
|
|
6
6
|
from loguru import logger
|
|
7
|
+
from web3 import Web3
|
|
7
8
|
from web3 import exceptions as web3_exceptions
|
|
8
9
|
|
|
9
10
|
from iwa.core.chain import ChainInterfaces
|
|
@@ -11,6 +12,191 @@ from iwa.core.db import log_transaction
|
|
|
11
12
|
from iwa.core.keys import KeyStorage
|
|
12
13
|
from iwa.core.services.account import AccountService
|
|
13
14
|
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from iwa.core.chain import ChainInterface
|
|
17
|
+
|
|
18
|
+
# ERC20 Transfer event signature: Transfer(address indexed from, address indexed to, uint256 value)
|
|
19
|
+
TRANSFER_EVENT_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TransferLogger:
|
|
23
|
+
"""Parse and log transfer events from transaction receipts."""
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
account_service: AccountService,
|
|
28
|
+
chain_interface: "ChainInterface",
|
|
29
|
+
):
|
|
30
|
+
"""Initialize TransferLogger."""
|
|
31
|
+
self.account_service = account_service
|
|
32
|
+
self.chain_interface = chain_interface
|
|
33
|
+
|
|
34
|
+
def log_transfers(self, receipt: Dict, tx: Dict) -> None:
|
|
35
|
+
"""Log all transfers (ERC20 and native) from a transaction receipt.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
receipt: Transaction receipt containing logs.
|
|
39
|
+
tx: Original transaction dict.
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
# Log native value transfer if present
|
|
43
|
+
native_value = tx.get("value", 0)
|
|
44
|
+
if native_value and int(native_value) > 0:
|
|
45
|
+
self._log_native_transfer(tx, native_value)
|
|
46
|
+
|
|
47
|
+
# Log ERC20 transfers from event logs
|
|
48
|
+
logs = receipt.get("logs", [])
|
|
49
|
+
if hasattr(receipt, "logs"):
|
|
50
|
+
logs = receipt.logs
|
|
51
|
+
|
|
52
|
+
for log in logs:
|
|
53
|
+
self._process_log(log)
|
|
54
|
+
|
|
55
|
+
def _log_native_transfer(self, tx: Dict, value_wei: int) -> None:
|
|
56
|
+
"""Log a native currency transfer."""
|
|
57
|
+
from_addr = tx.get("from", "")
|
|
58
|
+
to_addr = tx.get("to", "")
|
|
59
|
+
|
|
60
|
+
from_label = self._resolve_address_label(from_addr)
|
|
61
|
+
to_label = self._resolve_address_label(to_addr)
|
|
62
|
+
|
|
63
|
+
native_symbol = self.chain_interface.chain.native_currency
|
|
64
|
+
amount_eth = Web3.from_wei(value_wei, "ether")
|
|
65
|
+
|
|
66
|
+
logger.info(f"[TRANSFER] {amount_eth:.6g} {native_symbol}: {from_label} → {to_label}")
|
|
67
|
+
|
|
68
|
+
def _process_log(self, log) -> None:
|
|
69
|
+
"""Process a single log entry for Transfer events."""
|
|
70
|
+
# Get topics - handle both dict and AttributeDict
|
|
71
|
+
topics = log.get("topics", []) if isinstance(log, dict) else getattr(log, "topics", [])
|
|
72
|
+
|
|
73
|
+
if not topics:
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
# Check if this is a Transfer event
|
|
77
|
+
first_topic = topics[0]
|
|
78
|
+
if isinstance(first_topic, bytes):
|
|
79
|
+
first_topic = "0x" + first_topic.hex()
|
|
80
|
+
elif hasattr(first_topic, "hex"):
|
|
81
|
+
first_topic = first_topic.hex()
|
|
82
|
+
if not first_topic.startswith("0x"):
|
|
83
|
+
first_topic = "0x" + first_topic
|
|
84
|
+
|
|
85
|
+
if first_topic.lower() != TRANSFER_EVENT_TOPIC.lower():
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
# Need at least 3 topics for indexed from/to
|
|
89
|
+
if len(topics) < 3:
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
# Extract from/to from indexed topics (last 20 bytes of 32-byte topic)
|
|
94
|
+
from_topic = topics[1]
|
|
95
|
+
to_topic = topics[2]
|
|
96
|
+
|
|
97
|
+
from_addr = self._topic_to_address(from_topic)
|
|
98
|
+
to_addr = self._topic_to_address(to_topic)
|
|
99
|
+
|
|
100
|
+
# Extract amount from data
|
|
101
|
+
data = log.get("data", b"") if isinstance(log, dict) else getattr(log, "data", b"")
|
|
102
|
+
if isinstance(data, str):
|
|
103
|
+
data = bytes.fromhex(data.replace("0x", ""))
|
|
104
|
+
|
|
105
|
+
amount = int.from_bytes(data, "big") if data else 0
|
|
106
|
+
|
|
107
|
+
# Get token address
|
|
108
|
+
token_addr = (
|
|
109
|
+
log.get("address", "") if isinstance(log, dict) else getattr(log, "address", "")
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
self._log_erc20_transfer(token_addr, from_addr, to_addr, amount)
|
|
113
|
+
|
|
114
|
+
except Exception as e:
|
|
115
|
+
logger.debug(f"Failed to parse Transfer event: {e}")
|
|
116
|
+
|
|
117
|
+
def _topic_to_address(self, topic) -> str:
|
|
118
|
+
"""Convert a 32-byte topic to a 20-byte address."""
|
|
119
|
+
if isinstance(topic, bytes):
|
|
120
|
+
# Last 20 bytes
|
|
121
|
+
addr_bytes = topic[-20:]
|
|
122
|
+
return Web3.to_checksum_address("0x" + addr_bytes.hex())
|
|
123
|
+
elif hasattr(topic, "hex"):
|
|
124
|
+
hex_str = topic.hex()
|
|
125
|
+
if not hex_str.startswith("0x"):
|
|
126
|
+
hex_str = "0x" + hex_str
|
|
127
|
+
# Last 40 chars (20 bytes)
|
|
128
|
+
return Web3.to_checksum_address("0x" + hex_str[-40:])
|
|
129
|
+
elif isinstance(topic, str):
|
|
130
|
+
if topic.startswith("0x"):
|
|
131
|
+
topic = topic[2:]
|
|
132
|
+
return Web3.to_checksum_address("0x" + topic[-40:])
|
|
133
|
+
return ""
|
|
134
|
+
|
|
135
|
+
def _log_erc20_transfer(
|
|
136
|
+
self, token_addr: str, from_addr: str, to_addr: str, amount_wei: int
|
|
137
|
+
) -> None:
|
|
138
|
+
"""Log an ERC20 transfer (or NFT transfer if detected)."""
|
|
139
|
+
from_label = self._resolve_address_label(from_addr)
|
|
140
|
+
to_label = self._resolve_address_label(to_addr)
|
|
141
|
+
token_label = self._resolve_token_label(token_addr)
|
|
142
|
+
|
|
143
|
+
# Try to get decimals - if None, it's an NFT (ERC721)
|
|
144
|
+
decimals = self.chain_interface.get_token_decimals(token_addr, fallback_to_18=False)
|
|
145
|
+
|
|
146
|
+
if decimals is not None:
|
|
147
|
+
amount = amount_wei / (10**decimals)
|
|
148
|
+
logger.info(f"[TRANSFER] {amount:.6g} {token_label}: {from_label} → {to_label}")
|
|
149
|
+
else:
|
|
150
|
+
# Likely an NFT (ERC721) - the amount is the token ID
|
|
151
|
+
if amount_wei > 0:
|
|
152
|
+
logger.info(f"[NFT TRANSFER] Token #{amount_wei} {token_label}: {from_label} → {to_label}")
|
|
153
|
+
else:
|
|
154
|
+
logger.debug(f"[NFT TRANSFER] {token_label}: {from_label} → {to_label}")
|
|
155
|
+
|
|
156
|
+
def _resolve_address_label(self, address: str) -> str:
|
|
157
|
+
"""Resolve an address to a human-readable label.
|
|
158
|
+
|
|
159
|
+
Priority:
|
|
160
|
+
1. Known wallet tag (from wallets.json)
|
|
161
|
+
2. Known token name (it's a token contract)
|
|
162
|
+
3. Abbreviated address
|
|
163
|
+
|
|
164
|
+
"""
|
|
165
|
+
if not address:
|
|
166
|
+
return "unknown"
|
|
167
|
+
|
|
168
|
+
# 1. Check known wallets
|
|
169
|
+
tag = self.account_service.get_tag_by_address(address)
|
|
170
|
+
if tag:
|
|
171
|
+
return tag
|
|
172
|
+
|
|
173
|
+
# 2. Check if it's a known token contract
|
|
174
|
+
token_name = self.chain_interface.chain.get_token_name(address)
|
|
175
|
+
if token_name:
|
|
176
|
+
return f"{token_name}_contract"
|
|
177
|
+
|
|
178
|
+
# 3. Fallback to abbreviated address
|
|
179
|
+
return f"{address[:6]}...{address[-4:]}"
|
|
180
|
+
|
|
181
|
+
def _resolve_token_label(self, token_addr: str) -> str:
|
|
182
|
+
"""Resolve a token address to its symbol.
|
|
183
|
+
|
|
184
|
+
Priority:
|
|
185
|
+
1. Known token from chain config
|
|
186
|
+
2. Abbreviated address
|
|
187
|
+
|
|
188
|
+
"""
|
|
189
|
+
if not token_addr:
|
|
190
|
+
return "UNKNOWN"
|
|
191
|
+
|
|
192
|
+
# Check known tokens
|
|
193
|
+
token_name = self.chain_interface.chain.get_token_name(token_addr)
|
|
194
|
+
if token_name:
|
|
195
|
+
return token_name
|
|
196
|
+
|
|
197
|
+
# Fallback to abbreviated address
|
|
198
|
+
return f"{token_addr[:6]}...{token_addr[-4:]}"
|
|
199
|
+
|
|
14
200
|
|
|
15
201
|
class TransactionService:
|
|
16
202
|
"""Manages transaction lifecycle: signing, sending, retrying."""
|
|
@@ -54,7 +240,7 @@ class TransactionService:
|
|
|
54
240
|
logger.info(f"Transaction sent successfully. Tx Hash: {txn_hash.hex()}")
|
|
55
241
|
|
|
56
242
|
self._log_successful_transaction(
|
|
57
|
-
receipt, tx, signer_account, chain_name, txn_hash, tags
|
|
243
|
+
receipt, tx, signer_account, chain_name, txn_hash, tags, chain_interface
|
|
58
244
|
)
|
|
59
245
|
return True, receipt
|
|
60
246
|
|
|
@@ -115,7 +301,9 @@ class TransactionService:
|
|
|
115
301
|
logger.exception(f"Unexpected error sending transaction: {e}")
|
|
116
302
|
return False
|
|
117
303
|
|
|
118
|
-
def _log_successful_transaction(
|
|
304
|
+
def _log_successful_transaction(
|
|
305
|
+
self, receipt, tx, signer_account, chain_name, txn_hash, tags, chain_interface
|
|
306
|
+
):
|
|
119
307
|
try:
|
|
120
308
|
gas_cost_wei, gas_value_eur = self._calculate_gas_cost(receipt, tx, chain_name)
|
|
121
309
|
final_tags = self._determine_tags(tx, tags)
|
|
@@ -132,6 +320,11 @@ class TransactionService:
|
|
|
132
320
|
gas_value_eur=gas_value_eur,
|
|
133
321
|
tags=final_tags if final_tags else None,
|
|
134
322
|
)
|
|
323
|
+
|
|
324
|
+
# Log transfer events (ERC20 and native value)
|
|
325
|
+
transfer_logger = TransferLogger(self.account_service, chain_interface)
|
|
326
|
+
transfer_logger.log_transfers(receipt, tx)
|
|
327
|
+
|
|
135
328
|
except Exception as log_err:
|
|
136
329
|
logger.warning(f"Failed to log transaction: {log_err}")
|
|
137
330
|
|
|
@@ -92,7 +92,8 @@ class Wallet:
|
|
|
92
92
|
return addr, t_name, 0.0
|
|
93
93
|
|
|
94
94
|
# Use ThreadPoolExecutor for parallel balance fetching
|
|
95
|
-
|
|
95
|
+
# Limited to 4 workers to avoid overwhelming RPC endpoints
|
|
96
|
+
with ThreadPoolExecutor(max_workers=4) as executor:
|
|
96
97
|
tasks = []
|
|
97
98
|
for addr in accounts_data.keys():
|
|
98
99
|
for t_name in token_names:
|