iwa 0.0.1a2__tar.gz → 0.0.1a4__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.1a2/src/iwa.egg-info → iwa-0.0.1a4}/PKG-INFO +6 -3
- {iwa-0.0.1a2 → iwa-0.0.1a4}/README.md +2 -1
- {iwa-0.0.1a2 → iwa-0.0.1a4}/pyproject.toml +15 -3
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/chain/interface.py +51 -61
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/chain/models.py +7 -7
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/chain/rate_limiter.py +21 -10
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/cli.py +27 -2
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/constants.py +6 -5
- iwa-0.0.1a4/src/iwa/core/contracts/abis/erc20.json +930 -0
- iwa-0.0.1a4/src/iwa/core/contracts/abis/multisend.json +24 -0
- iwa-0.0.1a4/src/iwa/core/contracts/abis/multisend_call_only.json +17 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/contracts/contract.py +16 -4
- iwa-0.0.1a4/src/iwa/core/ipfs.py +149 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/keys.py +259 -29
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/mnemonic.py +3 -13
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/models.py +28 -6
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/pricing.py +4 -4
- iwa-0.0.1a4/src/iwa/core/secrets.py +77 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/services/safe.py +3 -3
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/utils.py +6 -1
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/wallet.py +4 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/gnosis/safe.py +2 -2
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/gnosis/tests/test_safe.py +1 -1
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/constants.py +8 -0
- iwa-0.0.1a4/src/iwa/plugins/olas/contracts/abis/activity_checker.json +110 -0
- iwa-0.0.1a4/src/iwa/plugins/olas/contracts/abis/mech.json +740 -0
- iwa-0.0.1a4/src/iwa/plugins/olas/contracts/abis/mech_marketplace.json +1293 -0
- iwa-0.0.1a4/src/iwa/plugins/olas/contracts/abis/mech_new.json +954 -0
- iwa-0.0.1a4/src/iwa/plugins/olas/contracts/abis/service_manager.json +1382 -0
- iwa-0.0.1a4/src/iwa/plugins/olas/contracts/abis/service_registry.json +1909 -0
- iwa-0.0.1a4/src/iwa/plugins/olas/contracts/abis/staking.json +1400 -0
- iwa-0.0.1a4/src/iwa/plugins/olas/contracts/abis/staking_token.json +1274 -0
- iwa-0.0.1a4/src/iwa/plugins/olas/contracts/mech.py +77 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/plugin.py +2 -2
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/test_plugin_full.py +3 -3
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/test_staking_integration.py +2 -2
- iwa-0.0.1a4/src/iwa/tools/__init__.py +1 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/tools/check_profile.py +6 -5
- iwa-0.0.1a4/src/iwa/tools/list_contracts.py +136 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/tools/release.py +9 -3
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/tools/reset_env.py +2 -2
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/tools/reset_tenderly.py +26 -24
- iwa-0.0.1a4/src/iwa/tools/wallet_check.py +150 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/web/dependencies.py +4 -4
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/web/routers/state.py +1 -0
- iwa-0.0.1a4/src/iwa/web/static/app.js +3096 -0
- iwa-0.0.1a4/src/iwa/web/static/index.html +543 -0
- iwa-0.0.1a4/src/iwa/web/static/style.css +1443 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/web/tests/test_web_endpoints.py +3 -2
- iwa-0.0.1a4/src/iwa/web/tests/test_web_swap_coverage.py +156 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4/src/iwa.egg-info}/PKG-INFO +6 -3
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa.egg-info/SOURCES.txt +22 -2
- iwa-0.0.1a4/src/iwa.egg-info/entry_points.txt +6 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa.egg-info/requires.txt +2 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa.egg-info/top_level.txt +0 -1
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_chain.py +1 -1
- iwa-0.0.1a4/src/tests/test_chain_interface_coverage.py +92 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_contract.py +2 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_keys.py +58 -15
- iwa-0.0.1a4/src/tests/test_migration.py +52 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_mnemonic.py +1 -1
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_pricing.py +7 -7
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_safe_coverage.py +1 -1
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_safe_service.py +3 -3
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_staking_router.py +13 -1
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tools/verify_drain.py +1 -1
- iwa-0.0.1a2/src/conftest.py +0 -22
- iwa-0.0.1a2/src/iwa/core/settings.py +0 -95
- iwa-0.0.1a2/src/iwa/plugins/olas/contracts/mech.py +0 -49
- iwa-0.0.1a2/src/iwa.egg-info/entry_points.txt +0 -2
- {iwa-0.0.1a2 → iwa-0.0.1a4}/LICENSE +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/setup.cfg +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/__init__.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/__main__.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/__init__.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/chain/__init__.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/chain/errors.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/chain/manager.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/contracts/__init__.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/contracts/erc20.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/contracts/multisend.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/db.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/monitor.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/plugins.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/services/__init__.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/services/account.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/services/balance.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/services/plugin.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/services/transaction.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/services/transfer/__init__.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/services/transfer/base.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/services/transfer/erc20.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/services/transfer/multisend.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/services/transfer/native.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/services/transfer/swap.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/tables.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/test.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/tests/test_wallet.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/types.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/core/ui.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/__init__.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/gnosis/__init__.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/gnosis/cow/__init__.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/gnosis/cow/quotes.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/gnosis/cow/swap.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/gnosis/cow/types.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/gnosis/cow_utils.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/gnosis/plugin.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/gnosis/tests/test_cow.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/__init__.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/contracts/activity_checker.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/contracts/base.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/contracts/mech_marketplace.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/contracts/service.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/contracts/staking.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/importer.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/mech_reference.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/models.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/scripts/test_full_mech_flow.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/scripts/test_simple_lifecycle.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/service_manager/__init__.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/service_manager/base.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/service_manager/drain.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/service_manager/lifecycle.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/service_manager/mech.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/service_manager/staking.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/conftest.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/test_importer.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/test_importer_error_handling.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/test_mech_contracts.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/test_olas_contracts.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/test_olas_integration.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/test_olas_models.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/test_olas_view.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/test_olas_view_actions.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/test_olas_view_modals.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/test_plugin.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/test_service_lifecycle.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/test_service_manager.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/test_service_manager_errors.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/test_service_manager_flows.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/test_service_manager_mech.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/test_service_manager_rewards.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/test_service_manager_validation.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/test_service_staking.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tests/test_staking_validation.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tui/__init__.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/plugins/olas/tui/olas_view.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/tools/restore_backup.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/tui/__init__.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/tui/app.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/tui/modals/__init__.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/tui/modals/base.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/tui/rpc.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/tui/screens/__init__.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/tui/screens/wallets.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/tui/tests/test_app.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/tui/tests/test_rpc.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/tui/tests/test_wallets_refactor.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/tui/tests/test_widgets.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/tui/widgets/__init__.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/tui/widgets/base.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/tui/workers.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/web/models.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/web/routers/accounts.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/web/routers/olas/__init__.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/web/routers/olas/admin.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/web/routers/olas/funding.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/web/routers/olas/general.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/web/routers/olas/services.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/web/routers/olas/staking.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/web/routers/swap.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/web/routers/transactions.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/web/server.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/web/tests/test_web_olas.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa/web/tests/test_web_swap.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/iwa.egg-info/dependency_links.txt +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/legacy_cow.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/legacy_safe.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/legacy_transaction_retry_logic.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/legacy_tui.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/legacy_wallets_screen.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/legacy_web.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_account_service.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_balance_service.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_chain_interface.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_cli.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_db.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_drain_coverage.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_erc20.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_gnosis_plugin.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_legacy_wallet.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_main.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_modals.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_models.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_monitor.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_multisend.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_plugin_service.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_rate_limiter.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_reset_tenderly.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_rpc_view.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_service_manager_integration.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_service_manager_structure.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_service_transaction.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_staking_simple.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_tables.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_transaction_service.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_transfer_multisend.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_transfer_native.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_transfer_security.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_transfer_structure.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_transfer_swap_unit.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_ui_coverage.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_utils.py +0 -0
- {iwa-0.0.1a2 → iwa-0.0.1a4}/src/tests/test_workers.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: iwa
|
|
3
|
-
Version: 0.0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.0.1a4
|
|
4
|
+
Summary: A secure, modular, and plugin-based framework for crypto agents and ops
|
|
5
5
|
Requires-Python: <4.0,>=3.12
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
7
|
License-File: LICENSE
|
|
@@ -33,6 +33,8 @@ Requires-Dist: nest-asyncio>=1.6.0
|
|
|
33
33
|
Requires-Dist: aiohttp>=3.13.3
|
|
34
34
|
Requires-Dist: pynacl>=1.6.2
|
|
35
35
|
Requires-Dist: urllib3>=2.6.3
|
|
36
|
+
Requires-Dist: multiformats>=0.3.1
|
|
37
|
+
Requires-Dist: requests>=2.32.0
|
|
36
38
|
Dynamic: license-file
|
|
37
39
|
|
|
38
40
|
# Iwa
|
|
@@ -160,7 +162,8 @@ just test
|
|
|
160
162
|
### Security Checks
|
|
161
163
|
|
|
162
164
|
```bash
|
|
163
|
-
just security
|
|
165
|
+
just security # Runs gitleaks, bandit, and pip-audit
|
|
166
|
+
just wallet-check # Verifies password, keys, and mnemonic integrity
|
|
164
167
|
```
|
|
165
168
|
|
|
166
169
|
### Docker
|
|
@@ -123,7 +123,8 @@ just test
|
|
|
123
123
|
### Security Checks
|
|
124
124
|
|
|
125
125
|
```bash
|
|
126
|
-
just security
|
|
126
|
+
just security # Runs gitleaks, bandit, and pip-audit
|
|
127
|
+
just wallet-check # Verifies password, keys, and mnemonic integrity
|
|
127
128
|
```
|
|
128
129
|
|
|
129
130
|
### Docker
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "iwa"
|
|
3
|
-
version = "0.0.
|
|
4
|
-
description = "
|
|
3
|
+
version = "0.0.1a4"
|
|
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"
|
|
7
7
|
dependencies = [
|
|
@@ -33,10 +33,16 @@ dependencies = [
|
|
|
33
33
|
"aiohttp>=3.13.3",
|
|
34
34
|
"pynacl>=1.6.2",
|
|
35
35
|
"urllib3>=2.6.3",
|
|
36
|
+
"multiformats>=0.3.1",
|
|
37
|
+
"requests>=2.32.0",
|
|
36
38
|
]
|
|
37
39
|
|
|
38
40
|
[project.scripts]
|
|
39
41
|
iwa = "iwa.core.cli:iwa_cli"
|
|
42
|
+
iwa-wallet-check = "iwa.tools.wallet_check:check_wallet"
|
|
43
|
+
iwa-reset-tenderly = "iwa.tools.reset_tenderly:main"
|
|
44
|
+
iwa-reset-env = "iwa.tools.reset_env:main"
|
|
45
|
+
iwa-check-profile = "iwa.tools.check_profile:main"
|
|
40
46
|
|
|
41
47
|
[dependency-groups]
|
|
42
48
|
dev = [
|
|
@@ -57,9 +63,15 @@ dev = [
|
|
|
57
63
|
requires = ["setuptools>=61.0", "wheel"]
|
|
58
64
|
build-backend = "setuptools.build_meta"
|
|
59
65
|
|
|
66
|
+
[tool.setuptools.packages.find]
|
|
67
|
+
where = ["src"]
|
|
68
|
+
|
|
69
|
+
[tool.setuptools.package-data]
|
|
70
|
+
"*" = ["*.json", "*.yaml", "*.yml", "*.html", "*.css", "*.js"]
|
|
71
|
+
|
|
60
72
|
[tool.ruff]
|
|
61
73
|
line-length = 100
|
|
62
|
-
target-version = "0.0.
|
|
74
|
+
target-version = "0.0.1a4"
|
|
63
75
|
fix = true
|
|
64
76
|
|
|
65
77
|
[tool.ruff.lint]
|
|
@@ -21,8 +21,8 @@ DEFAULT_RPC_TIMEOUT = 10
|
|
|
21
21
|
class ChainInterface:
|
|
22
22
|
"""ChainInterface with rate limiting, retry logic, and RPC rotation support."""
|
|
23
23
|
|
|
24
|
-
DEFAULT_MAX_RETRIES =
|
|
25
|
-
DEFAULT_RETRY_DELAY = 0
|
|
24
|
+
DEFAULT_MAX_RETRIES = 6 # Allow trying most/all available RPCs on rate limit
|
|
25
|
+
DEFAULT_RETRY_DELAY = 1.0 # Base delay between retries (exponential backoff)
|
|
26
26
|
|
|
27
27
|
chain: SupportedChain
|
|
28
28
|
|
|
@@ -54,42 +54,40 @@ class ChainInterface:
|
|
|
54
54
|
return "tenderly" in rpc.lower() or "virtual" in rpc.lower()
|
|
55
55
|
|
|
56
56
|
def init_block_tracking(self):
|
|
57
|
-
"""Initialize block tracking for limit detection.
|
|
57
|
+
"""Initialize block tracking for limit detection.
|
|
58
|
+
|
|
59
|
+
Only enables block limit warnings if we have a valid tenderly config file
|
|
60
|
+
with initial_block set. Otherwise, leaves _initial_block at 0 which
|
|
61
|
+
disables the warnings (since we can't accurately track usage without
|
|
62
|
+
knowing the fork point).
|
|
63
|
+
"""
|
|
64
|
+
if not self.is_tenderly:
|
|
65
|
+
return # Only track for Tenderly vNets
|
|
66
|
+
|
|
58
67
|
try:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
logger.warning(
|
|
83
|
-
f"Tenderly detected but no initial_block in config. using session start: {self._initial_block}"
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
logger.warning(
|
|
87
|
-
"Monitoring Tenderly vNet block usage (Limit ~50 blocks from vNet start)"
|
|
88
|
-
)
|
|
89
|
-
except Exception as ex:
|
|
90
|
-
logger.warning(f"Failed to load Tenderly config for block tracking: {ex}")
|
|
91
|
-
except Exception as e:
|
|
92
|
-
logger.warning(f"Failed to init block tracking: {e}")
|
|
68
|
+
from iwa.core.constants import get_tenderly_config_path
|
|
69
|
+
from iwa.core.models import TenderlyConfig
|
|
70
|
+
|
|
71
|
+
profile = Config().core.tenderly_profile
|
|
72
|
+
config_path = get_tenderly_config_path(profile)
|
|
73
|
+
|
|
74
|
+
if not config_path.exists():
|
|
75
|
+
logger.debug(f"Tenderly config not found at {config_path}, skipping block tracking")
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
t_config = TenderlyConfig.load(config_path)
|
|
79
|
+
vnet = t_config.vnets.get(self.chain.name)
|
|
80
|
+
if not vnet:
|
|
81
|
+
vnet = t_config.vnets.get(self.chain.name.lower())
|
|
82
|
+
|
|
83
|
+
if vnet and vnet.initial_block > 0:
|
|
84
|
+
self._initial_block = vnet.initial_block
|
|
85
|
+
logger.info(f"Tenderly block tracking enabled (genesis: {self._initial_block})")
|
|
86
|
+
else:
|
|
87
|
+
logger.debug(f"Tenderly config exists but no initial_block for {self.chain.name}")
|
|
88
|
+
|
|
89
|
+
except Exception as ex:
|
|
90
|
+
logger.warning(f"Failed to load Tenderly config for block tracking: {ex}")
|
|
93
91
|
|
|
94
92
|
def check_block_limit(self, show_progress_bar: bool = False):
|
|
95
93
|
"""Check if approaching block limit (heuristic).
|
|
@@ -160,7 +158,13 @@ class ChainInterface:
|
|
|
160
158
|
"""Initialize Web3 with current RPC."""
|
|
161
159
|
rpc_url = self.chain.rpcs[self._current_rpc_index] if self.chain.rpcs else ""
|
|
162
160
|
raw_web3 = Web3(Web3.HTTPProvider(rpc_url, request_kwargs={"timeout": DEFAULT_RPC_TIMEOUT}))
|
|
163
|
-
|
|
161
|
+
|
|
162
|
+
# Use duck typing to check if current web3 is a RateLimitedWeb3 wrapper
|
|
163
|
+
# (isinstance check fails when RateLimitedWeb3 is mocked in tests)
|
|
164
|
+
if hasattr(self, "web3") and hasattr(self.web3, "set_backend"):
|
|
165
|
+
self.web3.set_backend(raw_web3)
|
|
166
|
+
else:
|
|
167
|
+
self.web3 = RateLimitedWeb3(raw_web3, self._rate_limiter, self)
|
|
164
168
|
|
|
165
169
|
def _is_rate_limit_error(self, error: Exception) -> bool:
|
|
166
170
|
"""Check if error is a rate limit (429) error."""
|
|
@@ -268,30 +272,16 @@ class ChainInterface:
|
|
|
268
272
|
if not self.chain.rpcs or len(self.chain.rpcs) <= 1:
|
|
269
273
|
return False
|
|
270
274
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
self._current_rpc_index = (self._current_rpc_index + 1) % len(self.chain.rpcs)
|
|
276
|
-
attempts += 1
|
|
277
|
-
|
|
278
|
-
if self._rpc_failure_counts.get(self._current_rpc_index, 0) >= 5:
|
|
279
|
-
continue
|
|
280
|
-
|
|
281
|
-
logger.info(f"Rotating RPC for {self.chain.name} to index {self._current_rpc_index}")
|
|
282
|
-
self._init_web3()
|
|
283
|
-
|
|
284
|
-
if self.check_rpc_health():
|
|
285
|
-
return True
|
|
286
|
-
else:
|
|
287
|
-
logger.warning(f"RPC at index {self._current_rpc_index} failed health check")
|
|
288
|
-
self._rpc_failure_counts[self._current_rpc_index] = (
|
|
289
|
-
self._rpc_failure_counts.get(self._current_rpc_index, 0) + 1
|
|
290
|
-
)
|
|
291
|
-
|
|
292
|
-
self._current_rpc_index = original_index
|
|
275
|
+
# Simple Round Robin rotation
|
|
276
|
+
# We don't check health here because the health check itself might consume rate limits
|
|
277
|
+
# or fail flakily. Better to just switch and try the operation.
|
|
278
|
+
self._current_rpc_index = (self._current_rpc_index + 1) % len(self.chain.rpcs)
|
|
293
279
|
self._init_web3()
|
|
294
|
-
|
|
280
|
+
|
|
281
|
+
logger.info(
|
|
282
|
+
f"Rotated RPC for {self.chain.name} to index {self._current_rpc_index}: {self.chain.rpcs[self._current_rpc_index]}"
|
|
283
|
+
)
|
|
284
|
+
return True
|
|
295
285
|
|
|
296
286
|
def check_rpc_health(self) -> bool:
|
|
297
287
|
"""Check if the current RPC is healthy."""
|
|
@@ -5,7 +5,7 @@ from typing import Dict, List, Optional
|
|
|
5
5
|
from pydantic import BaseModel
|
|
6
6
|
|
|
7
7
|
from iwa.core.models import EthereumAddress
|
|
8
|
-
from iwa.core.
|
|
8
|
+
from iwa.core.secrets import secrets
|
|
9
9
|
from iwa.core.utils import singleton
|
|
10
10
|
|
|
11
11
|
|
|
@@ -75,8 +75,8 @@ class Gnosis(SupportedChain):
|
|
|
75
75
|
def __init__(self, **data):
|
|
76
76
|
"""Initialize with RPCs from settings (after testing override is applied)."""
|
|
77
77
|
super().__init__(**data)
|
|
78
|
-
if not self.rpcs and
|
|
79
|
-
self.rpcs =
|
|
78
|
+
if not self.rpcs and secrets.gnosis_rpc:
|
|
79
|
+
self.rpcs = secrets.gnosis_rpc.get_secret_value().split(",")
|
|
80
80
|
|
|
81
81
|
|
|
82
82
|
@singleton
|
|
@@ -95,8 +95,8 @@ class Ethereum(SupportedChain):
|
|
|
95
95
|
def __init__(self, **data):
|
|
96
96
|
"""Initialize with RPCs from settings (after testing override is applied)."""
|
|
97
97
|
super().__init__(**data)
|
|
98
|
-
if not self.rpcs and
|
|
99
|
-
self.rpcs =
|
|
98
|
+
if not self.rpcs and secrets.ethereum_rpc:
|
|
99
|
+
self.rpcs = secrets.ethereum_rpc.get_secret_value().split(",")
|
|
100
100
|
|
|
101
101
|
|
|
102
102
|
@singleton
|
|
@@ -115,8 +115,8 @@ class Base(SupportedChain):
|
|
|
115
115
|
def __init__(self, **data):
|
|
116
116
|
"""Initialize with RPCs from settings (after testing override is applied)."""
|
|
117
117
|
super().__init__(**data)
|
|
118
|
-
if not self.rpcs and
|
|
119
|
-
self.rpcs =
|
|
118
|
+
if not self.rpcs and secrets.base_rpc:
|
|
119
|
+
self.rpcs = secrets.base_rpc.get_secret_value().split(",")
|
|
120
120
|
|
|
121
121
|
|
|
122
122
|
@singleton
|
|
@@ -152,17 +152,19 @@ class RateLimitedEth:
|
|
|
152
152
|
delattr(self._eth, name)
|
|
153
153
|
|
|
154
154
|
def _wrap_with_rate_limit(self, method, method_name):
|
|
155
|
-
"""Wrap a method with rate limiting
|
|
155
|
+
"""Wrap a method with rate limiting.
|
|
156
|
+
|
|
157
|
+
Note: Error handling (rotation, retry) is NOT done here.
|
|
158
|
+
It is the responsibility of `ChainInterface.with_retry()` to handle
|
|
159
|
+
errors and rotate RPCs as needed. This wrapper only ensures
|
|
160
|
+
rate limiting.
|
|
161
|
+
"""
|
|
156
162
|
|
|
157
163
|
def wrapper(*args, **kwargs):
|
|
158
164
|
if not self._rate_limiter.acquire(timeout=30.0):
|
|
159
165
|
raise TimeoutError(f"Rate limit timeout waiting for {method_name}")
|
|
160
166
|
|
|
161
|
-
|
|
162
|
-
return method(*args, **kwargs)
|
|
163
|
-
except Exception as e:
|
|
164
|
-
self._chain_interface._handle_rpc_error(e)
|
|
165
|
-
raise
|
|
167
|
+
return method(*args, **kwargs)
|
|
166
168
|
|
|
167
169
|
return wrapper
|
|
168
170
|
|
|
@@ -178,14 +180,23 @@ class RateLimitedWeb3:
|
|
|
178
180
|
self._rate_limiter = rate_limiter
|
|
179
181
|
self._chain_interface = chain_interface
|
|
180
182
|
self._eth_wrapper = None
|
|
183
|
+
# Initialize eth wrapper immediately
|
|
184
|
+
self._update_eth_wrapper()
|
|
185
|
+
|
|
186
|
+
def set_backend(self, new_web3):
|
|
187
|
+
"""Update the underlying Web3 instance (hot-swap)."""
|
|
188
|
+
self._web3 = new_web3
|
|
189
|
+
self._update_eth_wrapper()
|
|
190
|
+
|
|
191
|
+
def _update_eth_wrapper(self):
|
|
192
|
+
"""Update the eth wrapper to point to the current _web3.eth."""
|
|
193
|
+
self._eth_wrapper = RateLimitedEth(
|
|
194
|
+
self._web3.eth, self._rate_limiter, self._chain_interface
|
|
195
|
+
)
|
|
181
196
|
|
|
182
197
|
@property
|
|
183
198
|
def eth(self):
|
|
184
199
|
"""Return rate-limited eth interface."""
|
|
185
|
-
if self._eth_wrapper is None:
|
|
186
|
-
self._eth_wrapper = RateLimitedEth(
|
|
187
|
-
self._web3.eth, self._rate_limiter, self._chain_interface
|
|
188
|
-
)
|
|
189
200
|
return self._eth_wrapper
|
|
190
201
|
|
|
191
202
|
def __getattr__(self, name):
|
|
@@ -67,6 +67,31 @@ def account_list(
|
|
|
67
67
|
)
|
|
68
68
|
|
|
69
69
|
|
|
70
|
+
@wallet_cli.command("mnemonic")
|
|
71
|
+
def show_mnemonic():
|
|
72
|
+
"""Show the master account mnemonic (requires password)"""
|
|
73
|
+
key_storage = KeyStorage()
|
|
74
|
+
try:
|
|
75
|
+
mnemonic = key_storage.decrypt_mnemonic()
|
|
76
|
+
print("\n" + "=" * 60)
|
|
77
|
+
print("📜 MASTER ACCOUNT MNEMONIC (BIP-39)")
|
|
78
|
+
print("=" * 60)
|
|
79
|
+
print("\nWrite down these 24 words and store them in a safe place.")
|
|
80
|
+
print("-" * 60)
|
|
81
|
+
words = mnemonic.split()
|
|
82
|
+
for i in range(0, 24, 4):
|
|
83
|
+
print(
|
|
84
|
+
f" {i + 1:2}. {words[i]:12} {i + 2:2}. {words[i + 1]:12} "
|
|
85
|
+
f"{i + 3:2}. {words[i + 2]:12} {i + 4:2}. {words[i + 3]:12}"
|
|
86
|
+
)
|
|
87
|
+
print("-" * 60)
|
|
88
|
+
print("\n⚠️ Keep this phrase secret! Anyone with it can access your funds.")
|
|
89
|
+
print("=" * 60)
|
|
90
|
+
except Exception as e:
|
|
91
|
+
typer.echo(f"Error: {e}")
|
|
92
|
+
raise typer.Exit(code=1) from e
|
|
93
|
+
|
|
94
|
+
|
|
70
95
|
@wallet_cli.command("send")
|
|
71
96
|
def account_send(
|
|
72
97
|
from_address_or_tag: str = typer.Option(..., "--from", "-f", help="From address or tag"),
|
|
@@ -164,10 +189,10 @@ def web_server(
|
|
|
164
189
|
host: str = typer.Option("127.0.0.1", "--host", "-h", help="Host to listen on"),
|
|
165
190
|
):
|
|
166
191
|
"""Start Web Interface."""
|
|
167
|
-
from iwa.core.
|
|
192
|
+
from iwa.core.models import Config
|
|
168
193
|
from iwa.web.server import run_server
|
|
169
194
|
|
|
170
|
-
server_port = port or
|
|
195
|
+
server_port = port or Config().core.web_port
|
|
171
196
|
typer.echo(f"Starting web server on http://{host}:{server_port}")
|
|
172
197
|
run_server(host=host, port=server_port)
|
|
173
198
|
|
|
@@ -6,14 +6,15 @@ from iwa.core.types import EthereumAddress
|
|
|
6
6
|
|
|
7
7
|
PROJECT_ROOT = Path(__file__).parent.parent.parent.parent
|
|
8
8
|
|
|
9
|
-
# Data directory for
|
|
10
|
-
DATA_DIR =
|
|
9
|
+
# Data directory for runtime files
|
|
10
|
+
DATA_DIR = Path("data")
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
# secrets.env is at project root (NOT in data/)
|
|
13
|
+
SECRETS_PATH = Path("secrets.env")
|
|
13
14
|
CONFIG_PATH = DATA_DIR / "config.yaml"
|
|
14
15
|
WALLET_PATH = DATA_DIR / "wallet.json"
|
|
15
16
|
BACKUP_DIR = DATA_DIR / "backup"
|
|
16
|
-
TENDERLY_CONFIG_PATH =
|
|
17
|
+
TENDERLY_CONFIG_PATH = Path("tenderly.yaml")
|
|
17
18
|
|
|
18
19
|
ABI_PATH = PROJECT_ROOT / "src" / "iwa" / "core" / "contracts" / "abis"
|
|
19
20
|
|
|
@@ -25,4 +26,4 @@ DEFAULT_MECH_CONTRACT_ADDRESS = EthereumAddress("0x77af31De935740567Cf4FF1986D04
|
|
|
25
26
|
|
|
26
27
|
def get_tenderly_config_path(profile: int = 1) -> Path:
|
|
27
28
|
"""Get the path to a profile-specific Tenderly config file."""
|
|
28
|
-
return
|
|
29
|
+
return Path(f"tenderly_{profile}.yaml")
|