brawny 0.1.13__py3-none-any.whl
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.
- brawny/__init__.py +106 -0
- brawny/_context.py +232 -0
- brawny/_rpc/__init__.py +38 -0
- brawny/_rpc/broadcast.py +172 -0
- brawny/_rpc/clients.py +98 -0
- brawny/_rpc/context.py +49 -0
- brawny/_rpc/errors.py +252 -0
- brawny/_rpc/gas.py +158 -0
- brawny/_rpc/manager.py +982 -0
- brawny/_rpc/selector.py +156 -0
- brawny/accounts.py +534 -0
- brawny/alerts/__init__.py +132 -0
- brawny/alerts/abi_resolver.py +530 -0
- brawny/alerts/base.py +152 -0
- brawny/alerts/context.py +271 -0
- brawny/alerts/contracts.py +635 -0
- brawny/alerts/encoded_call.py +201 -0
- brawny/alerts/errors.py +267 -0
- brawny/alerts/events.py +680 -0
- brawny/alerts/function_caller.py +364 -0
- brawny/alerts/health.py +185 -0
- brawny/alerts/routing.py +118 -0
- brawny/alerts/send.py +364 -0
- brawny/api.py +660 -0
- brawny/chain.py +93 -0
- brawny/cli/__init__.py +16 -0
- brawny/cli/app.py +17 -0
- brawny/cli/bootstrap.py +37 -0
- brawny/cli/commands/__init__.py +41 -0
- brawny/cli/commands/abi.py +93 -0
- brawny/cli/commands/accounts.py +632 -0
- brawny/cli/commands/console.py +495 -0
- brawny/cli/commands/contract.py +139 -0
- brawny/cli/commands/health.py +112 -0
- brawny/cli/commands/init_project.py +86 -0
- brawny/cli/commands/intents.py +130 -0
- brawny/cli/commands/job_dev.py +254 -0
- brawny/cli/commands/jobs.py +308 -0
- brawny/cli/commands/logs.py +87 -0
- brawny/cli/commands/maintenance.py +182 -0
- brawny/cli/commands/migrate.py +51 -0
- brawny/cli/commands/networks.py +253 -0
- brawny/cli/commands/run.py +249 -0
- brawny/cli/commands/script.py +209 -0
- brawny/cli/commands/signer.py +248 -0
- brawny/cli/helpers.py +265 -0
- brawny/cli_templates.py +1445 -0
- brawny/config/__init__.py +74 -0
- brawny/config/models.py +404 -0
- brawny/config/parser.py +633 -0
- brawny/config/routing.py +55 -0
- brawny/config/validation.py +246 -0
- brawny/daemon/__init__.py +14 -0
- brawny/daemon/context.py +69 -0
- brawny/daemon/core.py +702 -0
- brawny/daemon/loops.py +327 -0
- brawny/db/__init__.py +78 -0
- brawny/db/base.py +986 -0
- brawny/db/base_new.py +165 -0
- brawny/db/circuit_breaker.py +97 -0
- brawny/db/global_cache.py +298 -0
- brawny/db/mappers.py +182 -0
- brawny/db/migrate.py +349 -0
- brawny/db/migrations/001_init.sql +186 -0
- brawny/db/migrations/002_add_included_block.sql +7 -0
- brawny/db/migrations/003_add_broadcast_at.sql +10 -0
- brawny/db/migrations/004_broadcast_binding.sql +20 -0
- brawny/db/migrations/005_add_retry_after.sql +9 -0
- brawny/db/migrations/006_add_retry_count_column.sql +11 -0
- brawny/db/migrations/007_add_gap_tracking.sql +18 -0
- brawny/db/migrations/008_add_transactions.sql +72 -0
- brawny/db/migrations/009_add_intent_metadata.sql +5 -0
- brawny/db/migrations/010_add_nonce_gap_index.sql +9 -0
- brawny/db/migrations/011_add_job_logs.sql +24 -0
- brawny/db/migrations/012_add_claimed_by.sql +5 -0
- brawny/db/ops/__init__.py +29 -0
- brawny/db/ops/attempts.py +108 -0
- brawny/db/ops/blocks.py +83 -0
- brawny/db/ops/cache.py +93 -0
- brawny/db/ops/intents.py +296 -0
- brawny/db/ops/jobs.py +110 -0
- brawny/db/ops/logs.py +97 -0
- brawny/db/ops/nonces.py +322 -0
- brawny/db/postgres.py +2535 -0
- brawny/db/postgres_new.py +196 -0
- brawny/db/queries.py +584 -0
- brawny/db/sqlite.py +2733 -0
- brawny/db/sqlite_new.py +191 -0
- brawny/history.py +126 -0
- brawny/interfaces.py +136 -0
- brawny/invariants.py +155 -0
- brawny/jobs/__init__.py +26 -0
- brawny/jobs/base.py +287 -0
- brawny/jobs/discovery.py +233 -0
- brawny/jobs/job_validation.py +111 -0
- brawny/jobs/kv.py +125 -0
- brawny/jobs/registry.py +283 -0
- brawny/keystore.py +484 -0
- brawny/lifecycle.py +551 -0
- brawny/logging.py +290 -0
- brawny/metrics.py +594 -0
- brawny/model/__init__.py +53 -0
- brawny/model/contexts.py +319 -0
- brawny/model/enums.py +70 -0
- brawny/model/errors.py +194 -0
- brawny/model/events.py +93 -0
- brawny/model/startup.py +20 -0
- brawny/model/types.py +483 -0
- brawny/networks/__init__.py +96 -0
- brawny/networks/config.py +269 -0
- brawny/networks/manager.py +423 -0
- brawny/obs/__init__.py +67 -0
- brawny/obs/emit.py +158 -0
- brawny/obs/health.py +175 -0
- brawny/obs/heartbeat.py +133 -0
- brawny/reconciliation.py +108 -0
- brawny/scheduler/__init__.py +19 -0
- brawny/scheduler/poller.py +472 -0
- brawny/scheduler/reorg.py +632 -0
- brawny/scheduler/runner.py +708 -0
- brawny/scheduler/shutdown.py +371 -0
- brawny/script_tx.py +297 -0
- brawny/scripting.py +251 -0
- brawny/startup.py +76 -0
- brawny/telegram.py +393 -0
- brawny/testing.py +108 -0
- brawny/tx/__init__.py +41 -0
- brawny/tx/executor.py +1071 -0
- brawny/tx/fees.py +50 -0
- brawny/tx/intent.py +423 -0
- brawny/tx/monitor.py +628 -0
- brawny/tx/nonce.py +498 -0
- brawny/tx/replacement.py +456 -0
- brawny/tx/utils.py +26 -0
- brawny/utils.py +205 -0
- brawny/validation.py +69 -0
- brawny-0.1.13.dist-info/METADATA +156 -0
- brawny-0.1.13.dist-info/RECORD +141 -0
- brawny-0.1.13.dist-info/WHEEL +5 -0
- brawny-0.1.13.dist-info/entry_points.txt +2 -0
- brawny-0.1.13.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Configuration management for brawny.
|
|
2
|
+
|
|
3
|
+
All configuration is validated at startup. Invalid config prevents startup with clear error messages.
|
|
4
|
+
Environment variables use the BRAWNY_ prefix.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
from brawny.config.models import AdvancedConfig, Config, RPCGroupConfig
|
|
12
|
+
from brawny.config.validation import (
|
|
13
|
+
InvalidEndpointError,
|
|
14
|
+
canonicalize_endpoint,
|
|
15
|
+
dedupe_preserve_order,
|
|
16
|
+
validate_config,
|
|
17
|
+
)
|
|
18
|
+
from brawny.model.errors import ConfigError
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
# Models
|
|
22
|
+
"Config",
|
|
23
|
+
"AdvancedConfig",
|
|
24
|
+
"RPCGroupConfig",
|
|
25
|
+
# Validation
|
|
26
|
+
"InvalidEndpointError",
|
|
27
|
+
"canonicalize_endpoint",
|
|
28
|
+
"dedupe_preserve_order",
|
|
29
|
+
"validate_config",
|
|
30
|
+
# Errors
|
|
31
|
+
"ConfigError",
|
|
32
|
+
# Global instance
|
|
33
|
+
"get_config",
|
|
34
|
+
"set_config",
|
|
35
|
+
"reset_config",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# Global configuration instance (lazy loaded)
|
|
40
|
+
_config: Config | None = None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_config() -> Config:
|
|
44
|
+
"""Get the global configuration instance.
|
|
45
|
+
|
|
46
|
+
Loads from environment on first access.
|
|
47
|
+
"""
|
|
48
|
+
global _config
|
|
49
|
+
if _config is None:
|
|
50
|
+
default_path = os.path.join(os.getcwd(), "config.yaml")
|
|
51
|
+
if os.path.exists(default_path):
|
|
52
|
+
config = Config.from_yaml(default_path)
|
|
53
|
+
_config, _ = config.apply_env_overrides()
|
|
54
|
+
else:
|
|
55
|
+
_config = Config.from_env()
|
|
56
|
+
return _config
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def set_config(config: Config) -> None:
|
|
60
|
+
"""Set the global configuration instance.
|
|
61
|
+
|
|
62
|
+
Useful for testing or programmatic configuration.
|
|
63
|
+
"""
|
|
64
|
+
global _config
|
|
65
|
+
_config = config
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def reset_config() -> None:
|
|
69
|
+
"""Reset the global configuration instance.
|
|
70
|
+
|
|
71
|
+
Forces reload from environment on next access.
|
|
72
|
+
"""
|
|
73
|
+
global _config
|
|
74
|
+
_config = None
|
brawny/config/models.py
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
"""Configuration models for brawny.
|
|
2
|
+
|
|
3
|
+
Defines dataclass models for all configuration sections.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from dataclasses import asdict, dataclass, field
|
|
9
|
+
from brawny.model.enums import KeystoreType
|
|
10
|
+
|
|
11
|
+
DEFAULT_BLOCK_HASH_HISTORY_SIZE = 256
|
|
12
|
+
DEFAULT_JOB_ERROR_BACKOFF_BLOCKS = 1
|
|
13
|
+
DEFAULT_INTENT_RETRY_BACKOFF_SECONDS = 5
|
|
14
|
+
DEFAULT_NONCE_RECONCILE_INTERVAL_SECONDS = 300
|
|
15
|
+
DEFAULT_STUCK_TX_BLOCKS = 50
|
|
16
|
+
DEFAULT_SHUTDOWN_TIMEOUT_SECONDS = 30
|
|
17
|
+
DEFAULT_RPC_RETRY_BACKOFF_BASE = 1.0
|
|
18
|
+
DEFAULT_RPC_CIRCUIT_BREAKER_SECONDS = 300
|
|
19
|
+
DEFAULT_DB_CIRCUIT_BREAKER_FAILURES = 5
|
|
20
|
+
DEFAULT_DB_CIRCUIT_BREAKER_SECONDS = 30
|
|
21
|
+
DEFAULT_GAS_REFRESH_SECONDS = 15
|
|
22
|
+
DEFAULT_FALLBACK_GAS_LIMIT = 500_000
|
|
23
|
+
DEFAULT_TELEGRAM_RATE_LIMIT_PER_MINUTE = 20
|
|
24
|
+
DEFAULT_ABI_CACHE_TTL_SECONDS = 86400 * 7
|
|
25
|
+
DEFAULT_DATABASE_POOL_TIMEOUT_SECONDS = 30.0
|
|
26
|
+
DEFAULT_NONCE_GAP_ALERT_SECONDS = 300
|
|
27
|
+
DEFAULT_MAX_EXECUTOR_RETRIES = 5
|
|
28
|
+
DEFAULT_FINALITY_CONFIRMATIONS = 12
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class TelegramConfig:
|
|
33
|
+
"""Telegram alert configuration.
|
|
34
|
+
|
|
35
|
+
Fields:
|
|
36
|
+
bot_token: Bot token for API calls (None = disabled)
|
|
37
|
+
chats: Named chat targets (e.g., {"ops": "-100...", "dev": "-100..."})
|
|
38
|
+
default: Default targets when job.alert_to not specified
|
|
39
|
+
parse_mode: Default parse mode for telegram messages (None = plain text)
|
|
40
|
+
health_chat: Chat name for daemon health alerts (None = logged only)
|
|
41
|
+
health_cooldown_seconds: Deduplication window for health alerts
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
bot_token: str | None = None
|
|
45
|
+
chats: dict[str, str] = field(default_factory=dict) # name -> chat_id
|
|
46
|
+
default: list[str] = field(default_factory=list) # Always a list internally
|
|
47
|
+
parse_mode: str | None = "Markdown"
|
|
48
|
+
health_chat: str | None = None # e.g. "ops" - where daemon health alerts go
|
|
49
|
+
health_cooldown_seconds: int = 1800 # 30 minutes between identical alerts
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass
|
|
53
|
+
class RPCGroupConfig:
|
|
54
|
+
"""A named collection of RPC endpoints."""
|
|
55
|
+
|
|
56
|
+
endpoints: list[str] = field(default_factory=list) # Canonicalized + deduped at parse time
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass
|
|
60
|
+
class AdvancedConfig:
|
|
61
|
+
"""
|
|
62
|
+
Advanced options.
|
|
63
|
+
|
|
64
|
+
RULE:
|
|
65
|
+
- If this exceeds ~25 fields, something is wrong.
|
|
66
|
+
- New options must justify why they are user-facing at all.
|
|
67
|
+
- AdvancedConfig may only contain tuning parameters, not semantic switches.
|
|
68
|
+
No feature flags or behavior-class booleans (e.g., enable_x/disable_y).
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
# Polling
|
|
72
|
+
poll_interval_seconds: float = 1.0
|
|
73
|
+
reorg_depth: int = 32
|
|
74
|
+
finality_confirmations: int = DEFAULT_FINALITY_CONFIRMATIONS
|
|
75
|
+
|
|
76
|
+
# Execution
|
|
77
|
+
default_deadline_seconds: int = 3600
|
|
78
|
+
stuck_tx_seconds: int = 300
|
|
79
|
+
max_replacement_attempts: int = 5
|
|
80
|
+
|
|
81
|
+
# Gas (gwei)
|
|
82
|
+
gas_limit_multiplier: float = 1.2
|
|
83
|
+
default_priority_fee_gwei: float = 1.5
|
|
84
|
+
max_fee_cap_gwei: float | None = 500.0
|
|
85
|
+
fee_bump_percent: int = 15
|
|
86
|
+
|
|
87
|
+
# RPC
|
|
88
|
+
rpc_timeout_seconds: float = 30.0
|
|
89
|
+
rpc_max_retries: int = 3
|
|
90
|
+
|
|
91
|
+
# Database pool (Postgres only)
|
|
92
|
+
database_pool_size: int = 5
|
|
93
|
+
database_pool_max_overflow: int = 10
|
|
94
|
+
|
|
95
|
+
# Job logs
|
|
96
|
+
log_retention_days: int = 7
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@dataclass
|
|
100
|
+
class Config:
|
|
101
|
+
"""Main configuration for brawny.
|
|
102
|
+
|
|
103
|
+
NOTE: Direct construction does NOT validate. Use Config.from_yaml() or
|
|
104
|
+
Config.from_env() for validated configuration, or call .validate() explicitly.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
# Required fields (no defaults) must come first
|
|
108
|
+
database_url: str
|
|
109
|
+
rpc_endpoints: list[str] # Derived from rpc_groups; not user-facing
|
|
110
|
+
|
|
111
|
+
# RPC Groups (for per-job read/broadcast routing)
|
|
112
|
+
rpc_groups: dict[str, RPCGroupConfig]
|
|
113
|
+
|
|
114
|
+
# Chain (required)
|
|
115
|
+
chain_id: int
|
|
116
|
+
|
|
117
|
+
# RPC default group (optional)
|
|
118
|
+
rpc_default_group: str | None = None
|
|
119
|
+
|
|
120
|
+
# Execution
|
|
121
|
+
worker_count: int = 1
|
|
122
|
+
|
|
123
|
+
# Advanced (rarely changed)
|
|
124
|
+
advanced: AdvancedConfig | None = None
|
|
125
|
+
|
|
126
|
+
# Telegram (canonical form - parsed from telegram: or legacy fields)
|
|
127
|
+
telegram: TelegramConfig = field(default_factory=TelegramConfig)
|
|
128
|
+
|
|
129
|
+
# Metrics
|
|
130
|
+
metrics_port: int = 9091
|
|
131
|
+
|
|
132
|
+
# Keystore (required)
|
|
133
|
+
keystore_type: KeystoreType = KeystoreType.FILE
|
|
134
|
+
keystore_path: str = "~/.brawny/keys"
|
|
135
|
+
|
|
136
|
+
def validate(self) -> None:
|
|
137
|
+
"""Validate configuration.
|
|
138
|
+
|
|
139
|
+
Raises:
|
|
140
|
+
ConfigError: If validation fails
|
|
141
|
+
"""
|
|
142
|
+
from brawny.config.validation import validate_config, validate_advanced_config
|
|
143
|
+
|
|
144
|
+
validate_config(self)
|
|
145
|
+
validate_advanced_config(self._advanced_or_default())
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def is_sqlite(self) -> bool:
|
|
149
|
+
"""Check if using SQLite database."""
|
|
150
|
+
return self.database_url.startswith("sqlite:///")
|
|
151
|
+
|
|
152
|
+
@property
|
|
153
|
+
def is_postgres(self) -> bool:
|
|
154
|
+
"""Check if using PostgreSQL database."""
|
|
155
|
+
return self.database_url.startswith(("postgresql://", "postgres://"))
|
|
156
|
+
|
|
157
|
+
@classmethod
|
|
158
|
+
def from_env(cls) -> "Config":
|
|
159
|
+
"""Load configuration from environment variables."""
|
|
160
|
+
from brawny.config.parser import from_env as _from_env
|
|
161
|
+
return _from_env()
|
|
162
|
+
|
|
163
|
+
@classmethod
|
|
164
|
+
def from_yaml(cls, path: str) -> "Config":
|
|
165
|
+
"""Load configuration from a YAML file.
|
|
166
|
+
|
|
167
|
+
Supports environment variable interpolation using ${VAR}, ${{VAR}}, or ${VAR:-default} syntax.
|
|
168
|
+
"""
|
|
169
|
+
from brawny.config.parser import from_yaml as _from_yaml
|
|
170
|
+
return _from_yaml(path)
|
|
171
|
+
|
|
172
|
+
def apply_env_overrides(self) -> tuple["Config", list[str]]:
|
|
173
|
+
"""Apply environment overrides to the current config."""
|
|
174
|
+
from brawny.config.parser import apply_env_overrides as _apply_env_overrides
|
|
175
|
+
return _apply_env_overrides(self)
|
|
176
|
+
|
|
177
|
+
def to_dict(self) -> dict[str, object]:
|
|
178
|
+
return asdict(self)
|
|
179
|
+
|
|
180
|
+
def redacted_dict(self) -> dict[str, object]:
|
|
181
|
+
from urllib.parse import urlsplit, urlunsplit
|
|
182
|
+
|
|
183
|
+
def _redact_url(value: str) -> str:
|
|
184
|
+
try:
|
|
185
|
+
split = urlsplit(value)
|
|
186
|
+
except Exception:
|
|
187
|
+
return "***"
|
|
188
|
+
netloc = split.netloc
|
|
189
|
+
if "@" in netloc:
|
|
190
|
+
netloc = "***@" + netloc.split("@", 1)[1]
|
|
191
|
+
return urlunsplit((split.scheme, netloc, split.path, "", ""))
|
|
192
|
+
|
|
193
|
+
data = self.to_dict()
|
|
194
|
+
redacted: dict[str, object] = {}
|
|
195
|
+
for key, value in data.items():
|
|
196
|
+
if any(word in key.lower() for word in ("token", "secret", "key", "password")):
|
|
197
|
+
redacted[key] = "***"
|
|
198
|
+
elif key == "rpc_endpoints" and isinstance(value, list):
|
|
199
|
+
redacted[key] = [_redact_url(str(v)) for v in value]
|
|
200
|
+
elif key == "rpc_groups" and isinstance(value, dict):
|
|
201
|
+
redacted_groups: dict[str, object] = {}
|
|
202
|
+
for group_name, group_value in value.items():
|
|
203
|
+
if isinstance(group_value, dict):
|
|
204
|
+
endpoints = group_value.get("endpoints")
|
|
205
|
+
if isinstance(endpoints, list):
|
|
206
|
+
group_value = {
|
|
207
|
+
**group_value,
|
|
208
|
+
"endpoints": [_redact_url(str(v)) for v in endpoints],
|
|
209
|
+
}
|
|
210
|
+
redacted_groups[group_name] = group_value
|
|
211
|
+
redacted[key] = redacted_groups
|
|
212
|
+
elif key == "rpc_rate_limits" and isinstance(value, dict):
|
|
213
|
+
redacted[key] = {
|
|
214
|
+
_redact_url(str(endpoint)): limiter for endpoint, limiter in value.items()
|
|
215
|
+
}
|
|
216
|
+
elif key == "telegram" and isinstance(value, dict):
|
|
217
|
+
# Redact bot_token within telegram config
|
|
218
|
+
redacted[key] = {
|
|
219
|
+
**value,
|
|
220
|
+
"bot_token": "***" if value.get("bot_token") else None,
|
|
221
|
+
}
|
|
222
|
+
elif isinstance(value, str) and key.endswith("url"):
|
|
223
|
+
redacted[key] = _redact_url(value)
|
|
224
|
+
else:
|
|
225
|
+
redacted[key] = value
|
|
226
|
+
return redacted
|
|
227
|
+
|
|
228
|
+
def _advanced_or_default(self) -> AdvancedConfig:
|
|
229
|
+
return self.advanced or AdvancedConfig()
|
|
230
|
+
|
|
231
|
+
def _derive_claim_timeout_seconds(self) -> int:
|
|
232
|
+
deadline = self._advanced_or_default().default_deadline_seconds
|
|
233
|
+
return max(60, int(min(300, deadline / 12)))
|
|
234
|
+
|
|
235
|
+
@property
|
|
236
|
+
def poll_interval_seconds(self) -> float:
|
|
237
|
+
return self._advanced_or_default().poll_interval_seconds
|
|
238
|
+
|
|
239
|
+
@property
|
|
240
|
+
def reorg_depth(self) -> int:
|
|
241
|
+
return self._advanced_or_default().reorg_depth
|
|
242
|
+
|
|
243
|
+
@property
|
|
244
|
+
def finality_confirmations(self) -> int:
|
|
245
|
+
return self._advanced_or_default().finality_confirmations
|
|
246
|
+
|
|
247
|
+
@property
|
|
248
|
+
def default_deadline_seconds(self) -> int:
|
|
249
|
+
return self._advanced_or_default().default_deadline_seconds
|
|
250
|
+
|
|
251
|
+
@property
|
|
252
|
+
def stuck_tx_seconds(self) -> int:
|
|
253
|
+
return self._advanced_or_default().stuck_tx_seconds
|
|
254
|
+
|
|
255
|
+
@property
|
|
256
|
+
def max_replacement_attempts(self) -> int:
|
|
257
|
+
return self._advanced_or_default().max_replacement_attempts
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def gas_limit_multiplier(self) -> float:
|
|
261
|
+
return self._advanced_or_default().gas_limit_multiplier
|
|
262
|
+
|
|
263
|
+
@property
|
|
264
|
+
def default_priority_fee_gwei(self) -> float:
|
|
265
|
+
return self._advanced_or_default().default_priority_fee_gwei
|
|
266
|
+
|
|
267
|
+
@property
|
|
268
|
+
def max_fee_cap_gwei(self) -> float | None:
|
|
269
|
+
return self._advanced_or_default().max_fee_cap_gwei
|
|
270
|
+
|
|
271
|
+
@property
|
|
272
|
+
def fee_bump_percent(self) -> int:
|
|
273
|
+
return self._advanced_or_default().fee_bump_percent
|
|
274
|
+
|
|
275
|
+
@property
|
|
276
|
+
def rpc_timeout_seconds(self) -> float:
|
|
277
|
+
return self._advanced_or_default().rpc_timeout_seconds
|
|
278
|
+
|
|
279
|
+
@property
|
|
280
|
+
def rpc_max_retries(self) -> int:
|
|
281
|
+
return self._advanced_or_default().rpc_max_retries
|
|
282
|
+
|
|
283
|
+
@property
|
|
284
|
+
def database_pool_size(self) -> int:
|
|
285
|
+
return self._advanced_or_default().database_pool_size
|
|
286
|
+
|
|
287
|
+
@property
|
|
288
|
+
def database_pool_max_overflow(self) -> int:
|
|
289
|
+
return self._advanced_or_default().database_pool_max_overflow
|
|
290
|
+
|
|
291
|
+
@property
|
|
292
|
+
def priority_fee(self) -> int:
|
|
293
|
+
return int(self.default_priority_fee_gwei * 1_000_000_000)
|
|
294
|
+
|
|
295
|
+
@property
|
|
296
|
+
def max_fee(self) -> int | None:
|
|
297
|
+
cap = self.max_fee_cap_gwei
|
|
298
|
+
if cap is None:
|
|
299
|
+
return None
|
|
300
|
+
return int(cap * 1_000_000_000)
|
|
301
|
+
|
|
302
|
+
@property
|
|
303
|
+
def job_error_backoff_blocks(self) -> int:
|
|
304
|
+
return DEFAULT_JOB_ERROR_BACKOFF_BLOCKS
|
|
305
|
+
|
|
306
|
+
@property
|
|
307
|
+
def block_hash_history_size(self) -> int:
|
|
308
|
+
return DEFAULT_BLOCK_HASH_HISTORY_SIZE
|
|
309
|
+
|
|
310
|
+
@property
|
|
311
|
+
def deep_reorg_pause(self) -> bool:
|
|
312
|
+
return False
|
|
313
|
+
|
|
314
|
+
@property
|
|
315
|
+
def claim_timeout_seconds(self) -> int:
|
|
316
|
+
return self._derive_claim_timeout_seconds()
|
|
317
|
+
|
|
318
|
+
@property
|
|
319
|
+
def intent_retry_backoff_seconds(self) -> int:
|
|
320
|
+
return DEFAULT_INTENT_RETRY_BACKOFF_SECONDS
|
|
321
|
+
|
|
322
|
+
@property
|
|
323
|
+
def max_executor_retries(self) -> int:
|
|
324
|
+
return DEFAULT_MAX_EXECUTOR_RETRIES
|
|
325
|
+
|
|
326
|
+
@property
|
|
327
|
+
def nonce_reconcile_interval_seconds(self) -> int:
|
|
328
|
+
return DEFAULT_NONCE_RECONCILE_INTERVAL_SECONDS
|
|
329
|
+
|
|
330
|
+
@property
|
|
331
|
+
def stuck_tx_blocks(self) -> int:
|
|
332
|
+
return DEFAULT_STUCK_TX_BLOCKS
|
|
333
|
+
|
|
334
|
+
@property
|
|
335
|
+
def shutdown_timeout_seconds(self) -> int:
|
|
336
|
+
return DEFAULT_SHUTDOWN_TIMEOUT_SECONDS
|
|
337
|
+
|
|
338
|
+
@property
|
|
339
|
+
def shutdown_grace_seconds(self) -> int:
|
|
340
|
+
return DEFAULT_SHUTDOWN_TIMEOUT_SECONDS
|
|
341
|
+
|
|
342
|
+
@property
|
|
343
|
+
def rpc_retry_backoff_base(self) -> float:
|
|
344
|
+
return DEFAULT_RPC_RETRY_BACKOFF_BASE
|
|
345
|
+
|
|
346
|
+
@property
|
|
347
|
+
def rpc_circuit_breaker_seconds(self) -> int:
|
|
348
|
+
return DEFAULT_RPC_CIRCUIT_BREAKER_SECONDS
|
|
349
|
+
|
|
350
|
+
@property
|
|
351
|
+
def db_circuit_breaker_failures(self) -> int:
|
|
352
|
+
return DEFAULT_DB_CIRCUIT_BREAKER_FAILURES
|
|
353
|
+
|
|
354
|
+
@property
|
|
355
|
+
def db_circuit_breaker_seconds(self) -> int:
|
|
356
|
+
return DEFAULT_DB_CIRCUIT_BREAKER_SECONDS
|
|
357
|
+
|
|
358
|
+
@property
|
|
359
|
+
def rpc_rate_limit_per_second(self) -> float | None:
|
|
360
|
+
return None
|
|
361
|
+
|
|
362
|
+
@property
|
|
363
|
+
def rpc_rate_limit_burst(self) -> int | None:
|
|
364
|
+
return None
|
|
365
|
+
|
|
366
|
+
@property
|
|
367
|
+
def rpc_rate_limits(self) -> dict[str, dict[str, float | int]]:
|
|
368
|
+
return {}
|
|
369
|
+
|
|
370
|
+
@property
|
|
371
|
+
def gas_refresh_seconds(self) -> int:
|
|
372
|
+
return DEFAULT_GAS_REFRESH_SECONDS
|
|
373
|
+
|
|
374
|
+
@property
|
|
375
|
+
def fallback_gas_limit(self) -> int:
|
|
376
|
+
return DEFAULT_FALLBACK_GAS_LIMIT
|
|
377
|
+
|
|
378
|
+
@property
|
|
379
|
+
def telegram_rate_limit_per_minute(self) -> int:
|
|
380
|
+
return DEFAULT_TELEGRAM_RATE_LIMIT_PER_MINUTE
|
|
381
|
+
|
|
382
|
+
@property
|
|
383
|
+
def abi_cache_ttl_seconds(self) -> int:
|
|
384
|
+
return DEFAULT_ABI_CACHE_TTL_SECONDS
|
|
385
|
+
|
|
386
|
+
@property
|
|
387
|
+
def database_pool_timeout_seconds(self) -> float:
|
|
388
|
+
return DEFAULT_DATABASE_POOL_TIMEOUT_SECONDS
|
|
389
|
+
|
|
390
|
+
@property
|
|
391
|
+
def allow_unsafe_nonce_reset(self) -> bool:
|
|
392
|
+
return False
|
|
393
|
+
|
|
394
|
+
@property
|
|
395
|
+
def nonce_gap_alert_seconds(self) -> int:
|
|
396
|
+
return DEFAULT_NONCE_GAP_ALERT_SECONDS
|
|
397
|
+
|
|
398
|
+
@property
|
|
399
|
+
def brownie_password_fallback(self) -> bool:
|
|
400
|
+
return False
|
|
401
|
+
|
|
402
|
+
@property
|
|
403
|
+
def log_retention_days(self) -> int:
|
|
404
|
+
return self._advanced_or_default().log_retention_days
|