Skip to content

Commit b41382d

Browse files
committed
feat: update trade role handling and improve credit award logging
1 parent 1793442 commit b41382d

File tree

4 files changed

+91
-18
lines changed

4 files changed

+91
-18
lines changed

relayer/src/api/rest_api.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,10 +392,16 @@ async fn record_trade(
392392
return Err(StatusCode::BAD_REQUEST);
393393
}
394394

395+
let role = if payload.role.eq_ignore_ascii_case("follower") {
396+
"follower"
397+
} else {
398+
"leader"
399+
};
400+
395401
svc.record_trade_tx(
396402
&payload.bot_pubkey,
397403
payload.follower_pubkey.as_deref(),
398-
&payload.role,
404+
role,
399405
&payload.symbol,
400406
&payload.side,
401407
payload.size,

relayer/src/core/settlement_worker.rs

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,41 +60,78 @@ impl SettlementWorker {
6060
match self.verify_tx_opt(t.tx_hash.as_deref()).await {
6161
Ok(Some(true)) => {
6262
self.svc
63-
.update_trade_settlement(t.tx_hash.as_deref(), t.oid.as_deref(), "confirmed", None, None)
63+
.update_trade_settlement(
64+
t.tx_hash.as_deref(),
65+
t.oid.as_deref(),
66+
"confirmed",
67+
None,
68+
None,
69+
)
6470
.await?;
6571
if let Some(credit) = self.compute_credit(&t) {
6672
let recipient = t.follower_pubkey.as_deref().unwrap_or(&t.bot_pubkey);
67-
self.svc
73+
if let Err(e) = self
74+
.svc
6875
.award_credits(&t.bot_pubkey, recipient, credit)
69-
.await?;
76+
.await
77+
{
78+
log_award_error(&e, &t.bot_pubkey, recipient);
79+
continue;
80+
}
7081
}
71-
info!("settlement: confirmed tx_hash={:?} oid={:?}", t.tx_hash, t.oid);
82+
info!(
83+
"settlement: confirmed tx_hash={:?} oid={:?}",
84+
t.tx_hash, t.oid
85+
);
7286
}
7387
Ok(Some(false)) => {
7488
self.svc
75-
.update_trade_settlement(t.tx_hash.as_deref(), t.oid.as_deref(), "failed", None, None)
89+
.update_trade_settlement(
90+
t.tx_hash.as_deref(),
91+
t.oid.as_deref(),
92+
"failed",
93+
None,
94+
None,
95+
)
7696
.await?;
77-
warn!("settlement: marked failed tx_hash={:?} oid={:?}", t.tx_hash, t.oid);
97+
warn!(
98+
"settlement: marked failed tx_hash={:?} oid={:?}",
99+
t.tx_hash, t.oid
100+
);
78101
}
79102
Ok(None) => {
80103
// If no tx hash, treat pending entry as immediately credit-eligible.
81104
if t.tx_hash.is_none() {
82105
if let Some(credit) = self.compute_credit(&t) {
83106
let recipient = t.follower_pubkey.as_deref().unwrap_or(&t.bot_pubkey);
84-
self.svc
107+
if let Err(e) = self
108+
.svc
85109
.award_credits(&t.bot_pubkey, recipient, credit)
86-
.await?;
110+
.await
111+
{
112+
log_award_error(&e, &t.bot_pubkey, recipient);
113+
continue;
114+
}
87115
}
88116
self.svc
89-
.update_trade_settlement(t.tx_hash.as_deref(), t.oid.as_deref(), "confirmed", None, None)
117+
.update_trade_settlement(
118+
t.tx_hash.as_deref(),
119+
t.oid.as_deref(),
120+
"confirmed",
121+
None,
122+
None,
123+
)
90124
.await?;
91125
info!("settlement: credited pending trade with oid={:?}", t.oid);
92126
} else {
93127
debug!("settlement: tx {:?} not yet found", t.tx_hash);
94128
}
95129
}
96130
Err(e) => {
97-
error!("settlement: verify tx_hash={:?} oid={:?} error: {}", t.tx_hash, t.oid, e);
131+
error!(
132+
"settlement: verify tx_hash={:?} oid={:?} error: {}",
133+
t.tx_hash, t.oid, e
134+
);
98135
}
99136
}
100137
}
@@ -148,3 +185,20 @@ impl SettlementWorker {
148185
}
149186
}
150187
}
188+
189+
fn log_award_error(err: &anyhow::Error, bot_pubkey: &str, follower: &str) {
190+
if let Some(db_err) = err.downcast_ref::<tokio_postgres::Error>() {
191+
if let Some(code) = db_err.code() {
192+
warn!(
193+
"award_credits failed (pg code={:?}): bot={} follower={} err={:?}",
194+
code, bot_pubkey, follower, db_err
195+
);
196+
return;
197+
}
198+
}
199+
200+
warn!(
201+
"award_credits failed: bot={} follower={} err={:?}",
202+
bot_pubkey, follower, err
203+
);
204+
}

relayer/src/core/subscription.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ impl SubscriptionService {
166166
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
167167
PRIMARY KEY (bot_pubkey, follower_pubkey)
168168
);
169+
ALTER TABLE credits ALTER COLUMN credits TYPE DOUBLE PRECISION USING credits::double precision;
170+
ALTER TABLE credits ALTER COLUMN credits SET DEFAULT 0.0;
169171
CREATE TABLE IF NOT EXISTS signals (
170172
id BIGSERIAL PRIMARY KEY,
171173
event_id TEXT NOT NULL UNIQUE,
@@ -528,10 +530,10 @@ impl SubscriptionService {
528530
let client = self.pool.get().await.context("Failed to get PG client")?;
529531
client
530532
.execute(
531-
"INSERT INTO credits (bot_pubkey, follower_pubkey, credits)
533+
"INSERT INTO credits AS c (bot_pubkey, follower_pubkey, credits)
532534
VALUES ($1, $2, $3)
533535
ON CONFLICT (bot_pubkey, follower_pubkey)
534-
DO UPDATE SET credits = credits + EXCLUDED.credits, updated_at = now()",
536+
DO UPDATE SET credits = c.credits + EXCLUDED.credits, updated_at = now()",
535537
&[&bot_pubkey, &follower_pubkey, &delta],
536538
)
537539
.await

trader/nostr/signal_service.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def __init__(self, config: Dict[str, Any]):
4242
self.sid = nostr_cfg.get("sid", "bot-main")
4343
self.role = nostr_cfg.get("role", "bot")
4444
self.relays = nostr_cfg.get("relays", [])
45-
self.relayer_api = nostr_cfg.get("relayer_api")
45+
self.relayer_api = config.get("relayer_api")
4646
self.settlement_token = nostr_cfg.get("settlement_token")
4747

4848
if not self.enabled:
@@ -220,6 +220,7 @@ def send_execution_report(
220220
account: Optional[str] = None,
221221
note: Optional[str] = None,
222222
oid: Optional[str] = None,
223+
follower_pubkey: Optional[str] = None,
223224
) -> bool:
224225
if not self.enabled:
225226
return False
@@ -251,14 +252,17 @@ def send_execution_report(
251252
)
252253
published = self._publish(event)
253254

254-
if tx_hash and self.relayer_api and self.publisher is not None:
255+
if self.relayer_api and self.publisher is not None:
255256
self._report_trade_tx(
257+
account=account,
256258
tx_hash=tx_hash,
257259
symbol=symbol,
258260
side=side,
259261
size=size,
260262
price=price,
261263
role=self.role,
264+
follower_pubkey=follower_pubkey,
265+
oid=oid,
262266
)
263267

264268
return published
@@ -287,28 +291,35 @@ def send_agent_register(
287291
def _report_trade_tx(
288292
self,
289293
*,
290-
tx_hash: str,
294+
account: Optional[str],
295+
tx_hash: Optional[str],
291296
symbol: str,
292297
side: str,
293298
size: float,
294299
price: float,
295300
role: str,
296301
follower_pubkey: Optional[str] = None,
302+
oid: Optional[str] = None,
297303
) -> None:
304+
if not account:
305+
logger.warning("Failed to report trade tx: missing account/wallet for bot_pubkey")
306+
return
307+
db_role = "leader" if role.lower() in ("bot", "leader") else "follower"
298308
url = f"{self.relayer_api.rstrip('/')}/api/trades/record"
299309
headers = {"Content-Type": "application/json"}
300310
if self.settlement_token:
301311
headers["X-Settlement-Token"] = self.settlement_token
302312

303313
payload = {
304-
"bot_pubkey": getattr(self.publisher, "public_key", None),
314+
"bot_pubkey": account,
305315
"follower_pubkey": follower_pubkey,
306-
"role": role,
316+
"role": db_role,
307317
"symbol": symbol,
308318
"side": side,
309319
"size": size,
310320
"price": price,
311321
"tx_hash": tx_hash,
322+
"oid": oid,
312323
}
313324

314325
try:

0 commit comments

Comments
 (0)