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
brawny/db/queries.py
ADDED
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
"""Canonical SQL queries for brawny database operations.
|
|
2
|
+
|
|
3
|
+
All queries use :name placeholder style.
|
|
4
|
+
- SQLite: Supports :name natively with dict params
|
|
5
|
+
- Postgres: Rewritten to %(name)s in postgres.py
|
|
6
|
+
|
|
7
|
+
Dialect-specific queries use dict format: {"postgres": "...", "sqlite": "..."}
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
# =============================================================================
|
|
13
|
+
# Block State
|
|
14
|
+
# =============================================================================
|
|
15
|
+
|
|
16
|
+
GET_BLOCK_STATE = """
|
|
17
|
+
SELECT * FROM block_state WHERE chain_id = :chain_id
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
UPSERT_BLOCK_STATE = """
|
|
21
|
+
INSERT INTO block_state (chain_id, last_processed_block_number, last_processed_block_hash)
|
|
22
|
+
VALUES (:chain_id, :block_number, :block_hash)
|
|
23
|
+
ON CONFLICT(chain_id) DO UPDATE SET
|
|
24
|
+
last_processed_block_number = EXCLUDED.last_processed_block_number,
|
|
25
|
+
last_processed_block_hash = EXCLUDED.last_processed_block_hash,
|
|
26
|
+
updated_at = CURRENT_TIMESTAMP
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
GET_BLOCK_HASH_AT_HEIGHT = """
|
|
30
|
+
SELECT block_hash FROM block_hash_history
|
|
31
|
+
WHERE chain_id = :chain_id AND block_number = :block_number
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
INSERT_BLOCK_HASH = """
|
|
35
|
+
INSERT INTO block_hash_history (chain_id, block_number, block_hash)
|
|
36
|
+
VALUES (:chain_id, :block_number, :block_hash)
|
|
37
|
+
ON CONFLICT(chain_id, block_number) DO UPDATE SET
|
|
38
|
+
block_hash = EXCLUDED.block_hash,
|
|
39
|
+
inserted_at = CURRENT_TIMESTAMP
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
DELETE_BLOCK_HASHES_ABOVE = """
|
|
43
|
+
DELETE FROM block_hash_history
|
|
44
|
+
WHERE chain_id = :chain_id AND block_number > :block_number
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
DELETE_BLOCK_HASH_AT_HEIGHT = """
|
|
48
|
+
DELETE FROM block_hash_history
|
|
49
|
+
WHERE chain_id = :chain_id AND block_number = :block_number
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
GET_MAX_BLOCK_IN_HISTORY = """
|
|
53
|
+
SELECT MAX(block_number) as max_block FROM block_hash_history
|
|
54
|
+
WHERE chain_id = :chain_id
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
DELETE_BLOCK_HASHES_BELOW = """
|
|
58
|
+
DELETE FROM block_hash_history
|
|
59
|
+
WHERE chain_id = :chain_id AND block_number < :cutoff
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
GET_OLDEST_BLOCK_IN_HISTORY = """
|
|
63
|
+
SELECT MIN(block_number) as min_block FROM block_hash_history
|
|
64
|
+
WHERE chain_id = :chain_id
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
# =============================================================================
|
|
68
|
+
# Jobs
|
|
69
|
+
# =============================================================================
|
|
70
|
+
|
|
71
|
+
GET_JOB = "SELECT * FROM jobs WHERE job_id = :job_id"
|
|
72
|
+
|
|
73
|
+
GET_ENABLED_JOBS = "SELECT * FROM jobs WHERE enabled = 1 ORDER BY job_id"
|
|
74
|
+
|
|
75
|
+
LIST_ALL_JOBS = "SELECT * FROM jobs ORDER BY job_id"
|
|
76
|
+
|
|
77
|
+
UPSERT_JOB = """
|
|
78
|
+
INSERT INTO jobs (job_id, job_name, check_interval_blocks, enabled)
|
|
79
|
+
VALUES (:job_id, :job_name, :check_interval_blocks, :enabled)
|
|
80
|
+
ON CONFLICT(job_id) DO UPDATE SET
|
|
81
|
+
job_name = EXCLUDED.job_name,
|
|
82
|
+
check_interval_blocks = EXCLUDED.check_interval_blocks,
|
|
83
|
+
updated_at = CURRENT_TIMESTAMP
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
UPDATE_JOB_ENABLED = """
|
|
87
|
+
UPDATE jobs SET enabled = :enabled, updated_at = CURRENT_TIMESTAMP
|
|
88
|
+
WHERE job_id = :job_id
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
UPDATE_JOB_CHECKED = """
|
|
92
|
+
UPDATE jobs SET
|
|
93
|
+
last_checked_block_number = :block_number,
|
|
94
|
+
updated_at = CURRENT_TIMESTAMP
|
|
95
|
+
WHERE job_id = :job_id
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
UPDATE_JOB_TRIGGERED = """
|
|
99
|
+
UPDATE jobs SET
|
|
100
|
+
last_checked_block_number = :block_number,
|
|
101
|
+
last_triggered_block_number = :block_number,
|
|
102
|
+
updated_at = CURRENT_TIMESTAMP
|
|
103
|
+
WHERE job_id = :job_id
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
DELETE_JOB = "DELETE FROM jobs WHERE job_id = :job_id"
|
|
107
|
+
|
|
108
|
+
# =============================================================================
|
|
109
|
+
# Job KV Store
|
|
110
|
+
# =============================================================================
|
|
111
|
+
|
|
112
|
+
GET_JOB_KV = """
|
|
113
|
+
SELECT value_json FROM job_kv
|
|
114
|
+
WHERE job_id = :job_id AND key = :key
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
UPSERT_JOB_KV = """
|
|
118
|
+
INSERT INTO job_kv (job_id, key, value_json)
|
|
119
|
+
VALUES (:job_id, :key, :value_json)
|
|
120
|
+
ON CONFLICT(job_id, key) DO UPDATE SET
|
|
121
|
+
value_json = EXCLUDED.value_json,
|
|
122
|
+
updated_at = CURRENT_TIMESTAMP
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
DELETE_JOB_KV = """
|
|
126
|
+
DELETE FROM job_kv WHERE job_id = :job_id AND key = :key
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
DELETE_ALL_JOB_KV = """
|
|
130
|
+
DELETE FROM job_kv WHERE job_id = :job_id
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
# =============================================================================
|
|
134
|
+
# Signers / Nonce Management
|
|
135
|
+
# =============================================================================
|
|
136
|
+
|
|
137
|
+
GET_SIGNER = """
|
|
138
|
+
SELECT * FROM signers
|
|
139
|
+
WHERE chain_id = :chain_id AND signer_address = :address
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
LIST_SIGNERS = """
|
|
143
|
+
SELECT * FROM signers WHERE chain_id = :chain_id ORDER BY signer_address
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
UPSERT_SIGNER = """
|
|
147
|
+
INSERT INTO signers (chain_id, signer_address, next_nonce, last_synced_chain_nonce)
|
|
148
|
+
VALUES (:chain_id, :address, :next_nonce, :last_synced_chain_nonce)
|
|
149
|
+
ON CONFLICT(chain_id, signer_address) DO UPDATE SET
|
|
150
|
+
next_nonce = EXCLUDED.next_nonce,
|
|
151
|
+
last_synced_chain_nonce = EXCLUDED.last_synced_chain_nonce,
|
|
152
|
+
updated_at = CURRENT_TIMESTAMP
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
UPDATE_SIGNER_NEXT_NONCE = """
|
|
156
|
+
UPDATE signers SET next_nonce = :next_nonce, updated_at = CURRENT_TIMESTAMP
|
|
157
|
+
WHERE chain_id = :chain_id AND signer_address = :address
|
|
158
|
+
"""
|
|
159
|
+
|
|
160
|
+
UPDATE_SIGNER_CHAIN_NONCE = """
|
|
161
|
+
UPDATE signers SET last_synced_chain_nonce = :chain_nonce, updated_at = CURRENT_TIMESTAMP
|
|
162
|
+
WHERE chain_id = :chain_id AND signer_address = :address
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
SET_GAP_STARTED_AT = """
|
|
166
|
+
UPDATE signers SET gap_started_at = :started_at, updated_at = CURRENT_TIMESTAMP
|
|
167
|
+
WHERE chain_id = :chain_id AND signer_address = :address
|
|
168
|
+
"""
|
|
169
|
+
|
|
170
|
+
CLEAR_GAP_STARTED_AT = """
|
|
171
|
+
UPDATE signers SET gap_started_at = NULL, updated_at = CURRENT_TIMESTAMP
|
|
172
|
+
WHERE chain_id = :chain_id AND signer_address = :address
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
GET_SIGNER_BY_ALIAS = """
|
|
176
|
+
SELECT * FROM signers
|
|
177
|
+
WHERE chain_id = :chain_id AND alias = :alias
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
# =============================================================================
|
|
181
|
+
# Nonce Reservations
|
|
182
|
+
# =============================================================================
|
|
183
|
+
|
|
184
|
+
GET_NONCE_RESERVATION = """
|
|
185
|
+
SELECT * FROM nonce_reservations
|
|
186
|
+
WHERE chain_id = :chain_id AND signer_address = :address AND nonce = :nonce
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
GET_RESERVATIONS_FOR_SIGNER = """
|
|
190
|
+
SELECT * FROM nonce_reservations
|
|
191
|
+
WHERE chain_id = :chain_id AND signer_address = :address
|
|
192
|
+
ORDER BY nonce
|
|
193
|
+
"""
|
|
194
|
+
|
|
195
|
+
GET_RESERVATIONS_FOR_SIGNER_WITH_STATUS = """
|
|
196
|
+
SELECT * FROM nonce_reservations
|
|
197
|
+
WHERE chain_id = :chain_id AND signer_address = :address AND status = :status
|
|
198
|
+
ORDER BY nonce
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
GET_RESERVATIONS_BELOW_NONCE = """
|
|
202
|
+
SELECT * FROM nonce_reservations
|
|
203
|
+
WHERE chain_id = :chain_id AND signer_address = :address AND nonce < :nonce
|
|
204
|
+
ORDER BY nonce
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
GET_NON_RELEASED_RESERVATIONS = """
|
|
208
|
+
SELECT * FROM nonce_reservations
|
|
209
|
+
WHERE chain_id = :chain_id AND signer_address = :address
|
|
210
|
+
AND status != :released_status
|
|
211
|
+
AND nonce >= :base_nonce
|
|
212
|
+
ORDER BY nonce
|
|
213
|
+
"""
|
|
214
|
+
|
|
215
|
+
UPSERT_NONCE_RESERVATION = """
|
|
216
|
+
INSERT INTO nonce_reservations (chain_id, signer_address, nonce, status, intent_id)
|
|
217
|
+
VALUES (:chain_id, :address, :nonce, :status, :intent_id)
|
|
218
|
+
ON CONFLICT(chain_id, signer_address, nonce) DO UPDATE SET
|
|
219
|
+
status = EXCLUDED.status,
|
|
220
|
+
intent_id = EXCLUDED.intent_id,
|
|
221
|
+
updated_at = CURRENT_TIMESTAMP
|
|
222
|
+
"""
|
|
223
|
+
|
|
224
|
+
UPDATE_NONCE_RESERVATION_STATUS = """
|
|
225
|
+
UPDATE nonce_reservations SET status = :status, updated_at = CURRENT_TIMESTAMP
|
|
226
|
+
WHERE chain_id = :chain_id AND signer_address = :address AND nonce = :nonce
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
UPDATE_NONCE_RESERVATION_STATUS_WITH_INTENT = """
|
|
230
|
+
UPDATE nonce_reservations SET status = :status, intent_id = :intent_id, updated_at = CURRENT_TIMESTAMP
|
|
231
|
+
WHERE chain_id = :chain_id AND signer_address = :address AND nonce = :nonce
|
|
232
|
+
"""
|
|
233
|
+
|
|
234
|
+
# Dialect-specific: lock signer for nonce reservation
|
|
235
|
+
# Postgres uses FOR UPDATE, SQLite doesn't need it (uses BEGIN IMMEDIATE)
|
|
236
|
+
LOCK_SIGNER_FOR_UPDATE = {
|
|
237
|
+
"postgres": """
|
|
238
|
+
SELECT * FROM signers
|
|
239
|
+
WHERE chain_id = :chain_id AND signer_address = :address
|
|
240
|
+
FOR UPDATE
|
|
241
|
+
""",
|
|
242
|
+
"sqlite": """
|
|
243
|
+
SELECT * FROM signers
|
|
244
|
+
WHERE chain_id = :chain_id AND signer_address = :address
|
|
245
|
+
""",
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
ENSURE_SIGNER_EXISTS = """
|
|
249
|
+
INSERT INTO signers (chain_id, signer_address, next_nonce, last_synced_chain_nonce)
|
|
250
|
+
VALUES (:chain_id, :address, 0, NULL)
|
|
251
|
+
ON CONFLICT(chain_id, signer_address) DO NOTHING
|
|
252
|
+
"""
|
|
253
|
+
|
|
254
|
+
# Dialect-specific: cleanup orphaned nonces
|
|
255
|
+
CLEANUP_ORPHANED_NONCES = {
|
|
256
|
+
"postgres": """
|
|
257
|
+
DELETE FROM nonce_reservations
|
|
258
|
+
WHERE chain_id = :chain_id
|
|
259
|
+
AND status = 'orphaned'
|
|
260
|
+
AND updated_at < NOW() - make_interval(hours => :hours)
|
|
261
|
+
""",
|
|
262
|
+
"sqlite": """
|
|
263
|
+
DELETE FROM nonce_reservations
|
|
264
|
+
WHERE chain_id = :chain_id
|
|
265
|
+
AND status = 'orphaned'
|
|
266
|
+
AND updated_at < datetime('now', :hours_offset)
|
|
267
|
+
""",
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
# =============================================================================
|
|
271
|
+
# Intents
|
|
272
|
+
# =============================================================================
|
|
273
|
+
|
|
274
|
+
CREATE_INTENT = """
|
|
275
|
+
INSERT INTO tx_intents (
|
|
276
|
+
intent_id, job_id, chain_id, signer_address, idempotency_key,
|
|
277
|
+
to_address, data, value_wei, gas_limit, max_fee_per_gas,
|
|
278
|
+
max_priority_fee_per_gas, min_confirmations, deadline_ts,
|
|
279
|
+
broadcast_group, broadcast_endpoints_json, retry_after, status,
|
|
280
|
+
metadata_json
|
|
281
|
+
) VALUES (
|
|
282
|
+
:intent_id, :job_id, :chain_id, :signer_address, :idempotency_key,
|
|
283
|
+
:to_address, :data, :value_wei, :gas_limit, :max_fee_per_gas,
|
|
284
|
+
:max_priority_fee_per_gas, :min_confirmations, :deadline_ts,
|
|
285
|
+
:broadcast_group, :broadcast_endpoints_json, NULL, 'created',
|
|
286
|
+
:metadata_json
|
|
287
|
+
)
|
|
288
|
+
ON CONFLICT (chain_id, signer_address, idempotency_key) DO NOTHING
|
|
289
|
+
RETURNING *
|
|
290
|
+
"""
|
|
291
|
+
|
|
292
|
+
GET_INTENT = "SELECT * FROM tx_intents WHERE intent_id = :intent_id"
|
|
293
|
+
|
|
294
|
+
GET_INTENT_BY_IDEMPOTENCY_KEY = """
|
|
295
|
+
SELECT * FROM tx_intents
|
|
296
|
+
WHERE chain_id = :chain_id
|
|
297
|
+
AND signer_address = :signer_address
|
|
298
|
+
AND idempotency_key = :idempotency_key
|
|
299
|
+
"""
|
|
300
|
+
|
|
301
|
+
# Dialect-specific: claim next intent (uses FOR UPDATE SKIP LOCKED on Postgres)
|
|
302
|
+
CLAIM_NEXT_INTENT = {
|
|
303
|
+
"postgres": """
|
|
304
|
+
WITH claimed AS (
|
|
305
|
+
SELECT intent_id FROM tx_intents
|
|
306
|
+
WHERE status = 'created'
|
|
307
|
+
AND (deadline_ts IS NULL OR deadline_ts > CURRENT_TIMESTAMP)
|
|
308
|
+
AND (retry_after IS NULL OR retry_after <= CURRENT_TIMESTAMP)
|
|
309
|
+
ORDER BY created_at ASC
|
|
310
|
+
FOR UPDATE SKIP LOCKED
|
|
311
|
+
LIMIT 1
|
|
312
|
+
)
|
|
313
|
+
UPDATE tx_intents
|
|
314
|
+
SET status = 'claimed', claim_token = :claim_token,
|
|
315
|
+
claimed_at = CURRENT_TIMESTAMP, claimed_by = :claimed_by,
|
|
316
|
+
retry_after = NULL,
|
|
317
|
+
updated_at = CURRENT_TIMESTAMP
|
|
318
|
+
WHERE intent_id = (SELECT intent_id FROM claimed)
|
|
319
|
+
RETURNING *
|
|
320
|
+
""",
|
|
321
|
+
"sqlite": """
|
|
322
|
+
UPDATE tx_intents
|
|
323
|
+
SET status = 'claimed', claim_token = :claim_token,
|
|
324
|
+
claimed_at = CURRENT_TIMESTAMP, claimed_by = :claimed_by,
|
|
325
|
+
retry_after = NULL,
|
|
326
|
+
updated_at = CURRENT_TIMESTAMP
|
|
327
|
+
WHERE intent_id = (
|
|
328
|
+
SELECT intent_id FROM tx_intents
|
|
329
|
+
WHERE status = 'created'
|
|
330
|
+
AND (deadline_ts IS NULL OR deadline_ts > CURRENT_TIMESTAMP)
|
|
331
|
+
AND (retry_after IS NULL OR retry_after <= CURRENT_TIMESTAMP)
|
|
332
|
+
ORDER BY created_at ASC
|
|
333
|
+
LIMIT 1
|
|
334
|
+
)
|
|
335
|
+
RETURNING *
|
|
336
|
+
""",
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
UPDATE_INTENT_STATUS = """
|
|
340
|
+
UPDATE tx_intents
|
|
341
|
+
SET status = :status, updated_at = CURRENT_TIMESTAMP
|
|
342
|
+
WHERE intent_id = :intent_id
|
|
343
|
+
"""
|
|
344
|
+
|
|
345
|
+
UPDATE_INTENT_TO_SENDING = """
|
|
346
|
+
UPDATE tx_intents
|
|
347
|
+
SET status = 'sending', updated_at = CURRENT_TIMESTAMP
|
|
348
|
+
WHERE intent_id = :intent_id AND status = 'claimed' AND claim_token = :claim_token
|
|
349
|
+
"""
|
|
350
|
+
|
|
351
|
+
UPDATE_INTENT_TO_PENDING = """
|
|
352
|
+
UPDATE tx_intents
|
|
353
|
+
SET status = 'pending', updated_at = CURRENT_TIMESTAMP
|
|
354
|
+
WHERE intent_id = :intent_id
|
|
355
|
+
"""
|
|
356
|
+
|
|
357
|
+
UPDATE_INTENT_TO_CONFIRMED = """
|
|
358
|
+
UPDATE tx_intents
|
|
359
|
+
SET status = 'confirmed', updated_at = CURRENT_TIMESTAMP
|
|
360
|
+
WHERE intent_id = :intent_id
|
|
361
|
+
"""
|
|
362
|
+
|
|
363
|
+
UPDATE_INTENT_TO_FAILED = """
|
|
364
|
+
UPDATE tx_intents
|
|
365
|
+
SET status = 'failed', updated_at = CURRENT_TIMESTAMP
|
|
366
|
+
WHERE intent_id = :intent_id
|
|
367
|
+
"""
|
|
368
|
+
|
|
369
|
+
UPDATE_INTENT_TO_REVERTED = """
|
|
370
|
+
UPDATE tx_intents
|
|
371
|
+
SET status = 'reverted', updated_at = CURRENT_TIMESTAMP
|
|
372
|
+
WHERE intent_id = :intent_id
|
|
373
|
+
"""
|
|
374
|
+
|
|
375
|
+
UPDATE_INTENT_RETRY_AFTER = """
|
|
376
|
+
UPDATE tx_intents
|
|
377
|
+
SET retry_after = :retry_after, retry_count = retry_count + 1, updated_at = CURRENT_TIMESTAMP
|
|
378
|
+
WHERE intent_id = :intent_id
|
|
379
|
+
"""
|
|
380
|
+
|
|
381
|
+
RELEASE_INTENT_CLAIM = """
|
|
382
|
+
UPDATE tx_intents
|
|
383
|
+
SET status = 'created', claim_token = NULL, claimed_at = NULL,
|
|
384
|
+
retry_after = :retry_after, updated_at = CURRENT_TIMESTAMP
|
|
385
|
+
WHERE intent_id = :intent_id AND claim_token = :claim_token
|
|
386
|
+
"""
|
|
387
|
+
|
|
388
|
+
UPDATE_INTENT_BROADCAST_BINDING = """
|
|
389
|
+
UPDATE tx_intents
|
|
390
|
+
SET broadcast_group = :broadcast_group, broadcast_endpoints_json = :broadcast_endpoints_json,
|
|
391
|
+
updated_at = CURRENT_TIMESTAMP
|
|
392
|
+
WHERE intent_id = :intent_id
|
|
393
|
+
"""
|
|
394
|
+
|
|
395
|
+
GET_INTENT_COUNT_BY_STATUS = """
|
|
396
|
+
SELECT COUNT(*) AS count FROM tx_intents
|
|
397
|
+
WHERE status IN ({placeholders}) AND job_id = :job_id
|
|
398
|
+
"""
|
|
399
|
+
|
|
400
|
+
GET_PENDING_INTENT_COUNT = """
|
|
401
|
+
SELECT COUNT(*) AS count FROM tx_intents
|
|
402
|
+
WHERE status IN ({placeholders})
|
|
403
|
+
"""
|
|
404
|
+
|
|
405
|
+
GET_BACKING_OFF_INTENT_COUNT = """
|
|
406
|
+
SELECT COUNT(*) AS count FROM tx_intents
|
|
407
|
+
WHERE retry_after > CURRENT_TIMESTAMP
|
|
408
|
+
"""
|
|
409
|
+
|
|
410
|
+
# =============================================================================
|
|
411
|
+
# Attempts
|
|
412
|
+
# =============================================================================
|
|
413
|
+
|
|
414
|
+
CREATE_ATTEMPT = """
|
|
415
|
+
INSERT INTO tx_attempts (
|
|
416
|
+
attempt_id, intent_id, nonce, tx_hash, gas_params_json,
|
|
417
|
+
status, broadcast_block, broadcast_at, broadcast_group, endpoint_url
|
|
418
|
+
) VALUES (
|
|
419
|
+
:attempt_id, :intent_id, :nonce, :tx_hash, :gas_params_json,
|
|
420
|
+
:status, :broadcast_block, :broadcast_at, :broadcast_group, :endpoint_url
|
|
421
|
+
)
|
|
422
|
+
RETURNING *
|
|
423
|
+
"""
|
|
424
|
+
|
|
425
|
+
GET_ATTEMPT = "SELECT * FROM tx_attempts WHERE attempt_id = :attempt_id"
|
|
426
|
+
|
|
427
|
+
GET_ATTEMPT_BY_TX_HASH = "SELECT * FROM tx_attempts WHERE tx_hash = :tx_hash"
|
|
428
|
+
|
|
429
|
+
GET_ATTEMPTS_FOR_INTENT = """
|
|
430
|
+
SELECT * FROM tx_attempts WHERE intent_id = :intent_id ORDER BY created_at DESC
|
|
431
|
+
"""
|
|
432
|
+
|
|
433
|
+
GET_LATEST_ATTEMPT_FOR_INTENT = """
|
|
434
|
+
SELECT * FROM tx_attempts
|
|
435
|
+
WHERE intent_id = :intent_id
|
|
436
|
+
ORDER BY created_at DESC
|
|
437
|
+
LIMIT 1
|
|
438
|
+
"""
|
|
439
|
+
|
|
440
|
+
UPDATE_ATTEMPT_STATUS = """
|
|
441
|
+
UPDATE tx_attempts
|
|
442
|
+
SET status = :status, updated_at = CURRENT_TIMESTAMP
|
|
443
|
+
WHERE attempt_id = :attempt_id
|
|
444
|
+
"""
|
|
445
|
+
|
|
446
|
+
UPDATE_ATTEMPT_INCLUDED = """
|
|
447
|
+
UPDATE tx_attempts
|
|
448
|
+
SET status = :status, included_block = :included_block, updated_at = CURRENT_TIMESTAMP
|
|
449
|
+
WHERE attempt_id = :attempt_id
|
|
450
|
+
"""
|
|
451
|
+
|
|
452
|
+
UPDATE_ATTEMPT_ERROR = """
|
|
453
|
+
UPDATE tx_attempts
|
|
454
|
+
SET status = :status, error_code = :error_code, error_detail = :error_detail,
|
|
455
|
+
updated_at = CURRENT_TIMESTAMP
|
|
456
|
+
WHERE attempt_id = :attempt_id
|
|
457
|
+
"""
|
|
458
|
+
|
|
459
|
+
GET_PENDING_ATTEMPTS = """
|
|
460
|
+
SELECT * FROM tx_attempts
|
|
461
|
+
WHERE status = 'pending' AND intent_id IN (
|
|
462
|
+
SELECT intent_id FROM tx_intents WHERE chain_id = :chain_id
|
|
463
|
+
)
|
|
464
|
+
ORDER BY created_at ASC
|
|
465
|
+
"""
|
|
466
|
+
|
|
467
|
+
# =============================================================================
|
|
468
|
+
# ABI Cache
|
|
469
|
+
# =============================================================================
|
|
470
|
+
|
|
471
|
+
GET_ABI_CACHE = """
|
|
472
|
+
SELECT * FROM abi_cache
|
|
473
|
+
WHERE chain_id = :chain_id AND address = :address
|
|
474
|
+
"""
|
|
475
|
+
|
|
476
|
+
UPSERT_ABI_CACHE = """
|
|
477
|
+
INSERT INTO abi_cache (chain_id, address, abi_json, source)
|
|
478
|
+
VALUES (:chain_id, :address, :abi_json, :source)
|
|
479
|
+
ON CONFLICT(chain_id, address) DO UPDATE SET
|
|
480
|
+
abi_json = EXCLUDED.abi_json,
|
|
481
|
+
source = EXCLUDED.source,
|
|
482
|
+
resolved_at = CURRENT_TIMESTAMP
|
|
483
|
+
"""
|
|
484
|
+
|
|
485
|
+
DELETE_ABI_CACHE = """
|
|
486
|
+
DELETE FROM abi_cache WHERE chain_id = :chain_id AND address = :address
|
|
487
|
+
"""
|
|
488
|
+
|
|
489
|
+
# =============================================================================
|
|
490
|
+
# Proxy Cache
|
|
491
|
+
# =============================================================================
|
|
492
|
+
|
|
493
|
+
GET_PROXY_CACHE = """
|
|
494
|
+
SELECT * FROM proxy_cache
|
|
495
|
+
WHERE chain_id = :chain_id AND proxy_address = :proxy_address
|
|
496
|
+
"""
|
|
497
|
+
|
|
498
|
+
UPSERT_PROXY_CACHE = """
|
|
499
|
+
INSERT INTO proxy_cache (chain_id, proxy_address, implementation_address)
|
|
500
|
+
VALUES (:chain_id, :proxy_address, :implementation_address)
|
|
501
|
+
ON CONFLICT(chain_id, proxy_address) DO UPDATE SET
|
|
502
|
+
implementation_address = EXCLUDED.implementation_address,
|
|
503
|
+
resolved_at = CURRENT_TIMESTAMP
|
|
504
|
+
"""
|
|
505
|
+
|
|
506
|
+
DELETE_PROXY_CACHE = """
|
|
507
|
+
DELETE FROM proxy_cache WHERE chain_id = :chain_id AND proxy_address = :proxy_address
|
|
508
|
+
"""
|
|
509
|
+
|
|
510
|
+
# =============================================================================
|
|
511
|
+
# Stuck Transaction Detection
|
|
512
|
+
# =============================================================================
|
|
513
|
+
|
|
514
|
+
GET_STUCK_SENDING_INTENTS = """
|
|
515
|
+
SELECT * FROM tx_intents
|
|
516
|
+
WHERE status = 'sending'
|
|
517
|
+
AND updated_at < :cutoff_time
|
|
518
|
+
"""
|
|
519
|
+
|
|
520
|
+
GET_STUCK_PENDING_INTENTS = """
|
|
521
|
+
SELECT * FROM tx_intents
|
|
522
|
+
WHERE status = 'pending'
|
|
523
|
+
AND updated_at < :cutoff_time
|
|
524
|
+
"""
|
|
525
|
+
|
|
526
|
+
# =============================================================================
|
|
527
|
+
# Cleanup / Maintenance
|
|
528
|
+
# =============================================================================
|
|
529
|
+
|
|
530
|
+
DELETE_OLD_CONFIRMED_INTENTS = """
|
|
531
|
+
DELETE FROM tx_intents
|
|
532
|
+
WHERE status IN ('confirmed', 'failed', 'reverted')
|
|
533
|
+
AND updated_at < :cutoff_time
|
|
534
|
+
"""
|
|
535
|
+
|
|
536
|
+
DELETE_ABANDONED_INTENTS = """
|
|
537
|
+
DELETE FROM tx_intents
|
|
538
|
+
WHERE status = 'created'
|
|
539
|
+
AND created_at < :cutoff_time
|
|
540
|
+
"""
|
|
541
|
+
|
|
542
|
+
# =============================================================================
|
|
543
|
+
# Job Logs
|
|
544
|
+
# =============================================================================
|
|
545
|
+
|
|
546
|
+
INSERT_JOB_LOG = """
|
|
547
|
+
INSERT INTO job_logs (chain_id, job_id, block_number, level, fields_json)
|
|
548
|
+
VALUES (:chain_id, :job_id, :block_number, :level, :fields_json)
|
|
549
|
+
"""
|
|
550
|
+
|
|
551
|
+
LIST_JOB_LOGS = """
|
|
552
|
+
SELECT * FROM job_logs
|
|
553
|
+
WHERE chain_id = :chain_id AND job_id = :job_id
|
|
554
|
+
ORDER BY ts DESC
|
|
555
|
+
LIMIT :limit
|
|
556
|
+
"""
|
|
557
|
+
|
|
558
|
+
LIST_ALL_JOB_LOGS = """
|
|
559
|
+
SELECT * FROM job_logs
|
|
560
|
+
WHERE chain_id = :chain_id
|
|
561
|
+
ORDER BY ts DESC
|
|
562
|
+
LIMIT :limit
|
|
563
|
+
"""
|
|
564
|
+
|
|
565
|
+
LIST_LATEST_JOB_LOGS = {
|
|
566
|
+
"postgres": """
|
|
567
|
+
SELECT DISTINCT ON (job_id) *
|
|
568
|
+
FROM job_logs
|
|
569
|
+
WHERE chain_id = :chain_id
|
|
570
|
+
ORDER BY job_id, ts DESC
|
|
571
|
+
""",
|
|
572
|
+
"sqlite": """
|
|
573
|
+
SELECT * FROM job_logs l1
|
|
574
|
+
WHERE chain_id = :chain_id
|
|
575
|
+
AND ts = (SELECT MAX(ts) FROM job_logs l2
|
|
576
|
+
WHERE l2.job_id = l1.job_id AND l2.chain_id = :chain_id)
|
|
577
|
+
ORDER BY job_id
|
|
578
|
+
""",
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
DELETE_OLD_JOB_LOGS = """
|
|
582
|
+
DELETE FROM job_logs
|
|
583
|
+
WHERE chain_id = :chain_id AND ts < :cutoff
|
|
584
|
+
"""
|