razvan commited on
Commit
7a2cfcb
Β·
verified Β·
1 Parent(s): eff7121

Upload builderbrain/arc_bridge.py

Browse files
Files changed (1) hide show
  1. builderbrain/arc_bridge.py +347 -0
builderbrain/arc_bridge.py ADDED
@@ -0,0 +1,347 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Arc/Circle Integration Bridge
3
+ =============================
4
+
5
+ Handles:
6
+ - Gateway + CCTP for cross-chain USDC movement
7
+ - Nanopayments for per-trade micropayments
8
+ - Wallets (embedded + strategy wallets)
9
+ - Paymaster for gas abstraction
10
+ - USYC yield on idle capital
11
+ - Contract interactions for on-chain tracking
12
+
13
+ This is the settlement layer for BuilderBrain.
14
+ """
15
+
16
+ import json
17
+ import hashlib
18
+ from dataclasses import dataclass, asdict
19
+ from typing import Dict, Optional, List, Any
20
+ from datetime import datetime
21
+
22
+
23
+ @dataclass
24
+ class NanopaymentConfig:
25
+ """Configuration for Arc Nanopayments."""
26
+ enabled: bool = True
27
+ fee_per_trade_bps: float = 5.0 # 5 bps per trade
28
+ fee_per_insight_usd: float = 0.01 # 1c per insight
29
+ min_payment_usd: float = 0.001 # 0.1c minimum
30
+ max_payment_usd: float = 10.0
31
+ settlement_interval_sec: int = 3600 # hourly batching
32
+
33
+
34
+ @dataclass
35
+ class WalletConfig:
36
+ """Embedded wallet configuration."""
37
+ wallet_type: str = "embedded" # 'embedded', 'eoa', 'smart_contract'
38
+ chain_id: int = 42161 # Arbitrum (Arc default)
39
+ gas_abstraction: bool = True # Paymaster enabled
40
+ daily_limit_usd: float = 1000.0
41
+ max_single_trade_usd: float = 500.0
42
+
43
+
44
+ @dataclass
45
+ class USYCConfig:
46
+ """USYC yield configuration for idle capital."""
47
+ enabled: bool = True
48
+ apy: float = 0.043 # 4.3% current
49
+ auto_rotate_threshold: float = 0.70 # rotate 70% to USYC when risk-off
50
+ risk_off_signals: List[str] = None
51
+
52
+ def __post_init__(self):
53
+ if self.risk_off_signals is None:
54
+ self.risk_off_signals = [
55
+ "vix_spike",
56
+ "correlation_breakdown",
57
+ "drawdown_15pct",
58
+ ]
59
+
60
+
61
+ @dataclass
62
+ class GatewayRoute:
63
+ """A cross-chain USDC route via Gateway + CCTP."""
64
+ from_chain: str
65
+ to_chain: str
66
+ amount_usd: float
67
+ estimated_time_sec: int
68
+ fee_usd: float
69
+ cctp_burn_tx: Optional[str] = None
70
+ cctp_mint_tx: Optional[str] = None
71
+
72
+
73
+ class ArcBridge:
74
+ """
75
+ Bridge to Arc/Circle infrastructure.
76
+
77
+ In production, this would:
78
+ - Call Arc Gateway API for CCTP transfers
79
+ - Submit Nanopayment batches
80
+ - Interact with USYC contract for yield
81
+ - Use Paymaster for gas abstraction
82
+
83
+ For hackathon, we simulate the integration patterns
84
+ with structured logging and mock transactions.
85
+ """
86
+
87
+ SUPPORTED_CHAINS = [
88
+ "arbitrum", "base", "optimism", "ethereum", "polygon"
89
+ ]
90
+
91
+ def __init__(
92
+ self,
93
+ nanopayment: Optional[NanopaymentConfig] = None,
94
+ wallet: Optional[WalletConfig] = None,
95
+ usyc: Optional[USYCConfig] = None,
96
+ ):
97
+ self.nanopayment = nanopayment or NanopaymentConfig()
98
+ self.wallet = wallet or WalletConfig()
99
+ self.usyc = usyc or USYCConfig()
100
+
101
+ # Simulated state
102
+ self.balances: Dict[str, float] = {"usdc": 0.0, "usyc": 0.0}
103
+ self.pending_payments: List[Dict] = []
104
+ self.gateway_routes: List[GatewayRoute] = []
105
+ self.paymaster_ops: List[Dict] = []
106
+ self.contract_calls: List[Dict] = []
107
+
108
+ # ────────────────────────────── Gateway + CCTP ──────────────────────────────
109
+
110
+ def route_usdc(
111
+ self,
112
+ from_chain: str,
113
+ to_chain: str,
114
+ amount_usd: float,
115
+ purpose: str = "trading",
116
+ ) -> GatewayRoute:
117
+ """
118
+ Route USDC between chains via Gateway + CCTP.
119
+
120
+ In production: call Arc Gateway API, initiate CCTP burn,
121
+ wait for attestation, mint on destination.
122
+ """
123
+ if from_chain not in self.SUPPORTED_CHAINS:
124
+ raise ValueError(f"Unsupported chain: {from_chain}")
125
+ if to_chain not in self.SUPPORTED_CHAINS:
126
+ raise ValueError(f"Unsupported chain: {to_chain}")
127
+
128
+ # Simulate CCTP timing and fees
129
+ route = GatewayRoute(
130
+ from_chain=from_chain,
131
+ to_chain=to_chain,
132
+ amount_usd=amount_usd,
133
+ estimated_time_sec=1800 if from_chain == "ethereum" else 600,
134
+ fee_usd=amount_usd * 0.0005, # 5 bps CCTP fee
135
+ )
136
+
137
+ # Simulate burn
138
+ route.cctp_burn_tx = f"0x{hashlib.sha256(f'burn_{amount_usd}'.encode()).hexdigest()[:40]}"
139
+
140
+ self.gateway_routes.append(route)
141
+
142
+ # Update balances
143
+ self.balances["usdc"] -= amount_usd + route.fee_usd
144
+
145
+ return route
146
+
147
+ def get_balance(self, chain: str = "arbitrum") -> Dict:
148
+ """Get USDC balance on a chain."""
149
+ return {
150
+ "chain": chain,
151
+ "usdc": self.balances.get("usdc", 0),
152
+ "usyc": self.balances.get("usyc", 0),
153
+ "total_usd": self.balances.get("usdc", 0) + self.balances.get("usyc", 0),
154
+ }
155
+
156
+ # ──────────────────��─────────── Nanopayments ──────────────────────────────
157
+
158
+ def charge_insight_fee(
159
+ self,
160
+ user_id: str,
161
+ trace_id: str,
162
+ amount_usd: Optional[float] = None,
163
+ ) -> Dict:
164
+ """Charge per-insight fee via Nanopayments."""
165
+ if not self.nanopayment.enabled:
166
+ return {"status": "skipped", "reason": "nanopayments_disabled"}
167
+
168
+ amount = amount_usd or self.nanopayment.fee_per_insight_usd
169
+ amount = max(amount, self.nanopayment.min_payment_usd)
170
+
171
+ payment = {
172
+ "type": "insight",
173
+ "user_id": user_id,
174
+ "trace_id": trace_id,
175
+ "amount_usd": amount,
176
+ "timestamp": datetime.utcnow().isoformat(),
177
+ "status": "pending_batch",
178
+ }
179
+
180
+ self.pending_payments.append(payment)
181
+ return payment
182
+
183
+ def charge_trade_fee(
184
+ self,
185
+ user_id: str,
186
+ trade_id: str,
187
+ notional_usd: float,
188
+ ) -> Dict:
189
+ """Charge per-trade fee via Nanopayments."""
190
+ if not self.nanopayment.enabled:
191
+ return {"status": "skipped", "reason": "nanopayments_disabled"}
192
+
193
+ amount = notional_usd * self.nanopayment.fee_per_trade_bps / 10000
194
+ amount = max(amount, self.nanopayment.min_payment_usd)
195
+
196
+ payment = {
197
+ "type": "trade",
198
+ "user_id": user_id,
199
+ "trade_id": trade_id,
200
+ "amount_usd": amount,
201
+ "notional_usd": notional_usd,
202
+ "timestamp": datetime.utcnow().isoformat(),
203
+ "status": "pending_batch",
204
+ }
205
+
206
+ self.pending_payments.append(payment)
207
+ return payment
208
+
209
+ def batch_settle(self) -> Dict:
210
+ """Batch settle pending nanopayments."""
211
+ if not self.pending_payments:
212
+ return {"settled": 0, "total_usd": 0}
213
+
214
+ total = sum(p["amount_usd"] for p in self.pending_payments)
215
+ count = len(self.pending_payments)
216
+
217
+ # Simulate batch settlement
218
+ batch_tx = f"0x{hashlib.sha256(f'batch_{count}'.encode()).hexdigest()[:40]}"
219
+
220
+ for p in self.pending_payments:
221
+ p["status"] = "settled"
222
+ p["batch_tx"] = batch_tx
223
+
224
+ self.pending_payments = []
225
+
226
+ return {
227
+ "settled": count,
228
+ "total_usd": total,
229
+ "batch_tx": batch_tx,
230
+ "avg_fee_usd": total / count if count > 0 else 0,
231
+ }
232
+
233
+ # ────────────────────────────── Paymaster ──────────────────────────────
234
+
235
+ def sponsor_transaction(
236
+ self,
237
+ user_id: str,
238
+ tx_data: Dict,
239
+ ) -> Dict:
240
+ """
241
+ Sponsor gas for a user transaction via Paymaster.
242
+
243
+ All UX-facing actions are gas-abstracted and USDC-denominated.
244
+ """
245
+ if not self.wallet.gas_abstraction:
246
+ return {"status": "rejected", "reason": "gas_abstraction_disabled"}
247
+
248
+ # Estimate gas cost in USDC
249
+ gas_estimate_usd = tx_data.get("gas_estimate", 0.50)
250
+
251
+ op = {
252
+ "user_id": user_id,
253
+ "tx_data": tx_data,
254
+ "gas_sponsored_usd": gas_estimate_usd,
255
+ "timestamp": datetime.utcnow().isoformat(),
256
+ "status": "sponsored",
257
+ "paymaster_tx": f"0x{hashlib.sha256(json.dumps(tx_data).encode()).hexdigest()[:40]}",
258
+ }
259
+
260
+ self.paymaster_ops.append(op)
261
+ return op
262
+
263
+ # ────────────────────────────── USYC ──────────────────────────────
264
+
265
+ def rotate_to_usyc(self, amount_usd: float) -> Dict:
266
+ """Rotate idle USDC to USYC for yield."""
267
+ if not self.usyc.enabled:
268
+ return {"status": "skipped", "reason": "usyc_disabled"}
269
+
270
+ if amount_usd > self.balances.get("usdc", 0):
271
+ amount_usd = self.balances.get("usdc", 0)
272
+
273
+ self.balances["usdc"] -= amount_usd
274
+ self.balances["usyc"] += amount_usd
275
+
276
+ return {
277
+ "status": "rotated",
278
+ "amount_usd": amount_usd,
279
+ "estimated_apy": self.usyc.apy,
280
+ "projected_annual_yield": amount_usd * self.usyc.apy,
281
+ }
282
+
283
+ def rotate_from_usyc(self, amount_usd: float) -> Dict:
284
+ """Rotate USYC back to USDC for trading."""
285
+ if amount_usd > self.balances.get("usyc", 0):
286
+ amount_usd = self.balances.get("usyc", 0)
287
+
288
+ self.balances["usyc"] -= amount_usd
289
+ self.balances["usdc"] += amount_usd
290
+
291
+ return {
292
+ "status": "rotated",
293
+ "amount_usd": amount_usd,
294
+ }
295
+
296
+ def auto_risk_off_rotation(self, risk_signal: str) -> Dict:
297
+ """Auto-rotate to USYC when risk-off signals trigger."""
298
+ if risk_signal not in self.usyc.risk_off_signals:
299
+ return {"status": "no_action", "reason": "signal_not_configured"}
300
+
301
+ usdc_balance = self.balances.get("usdc", 0)
302
+ rotate_amount = usdc_balance * self.usyc.auto_rotate_threshold
303
+
304
+ return self.rotate_to_usyc(rotate_amount)
305
+
306
+ # ────────────────────────────── Contract Tracking ──────────────────────────────
307
+
308
+ def record_strategy_metrics(
309
+ self,
310
+ strategy_id: str,
311
+ sharpe: float,
312
+ drawdown: float,
313
+ win_rate: float,
314
+ pnl_usd: float,
315
+ ) -> Dict:
316
+ """
317
+ Record on-chain strategy performance metrics.
318
+
319
+ In production: call Arc contract to update strategy leaderboard.
320
+ """
321
+ metrics = {
322
+ "strategy_id": strategy_id,
323
+ "sharpe": sharpe,
324
+ "drawdown": drawdown,
325
+ "win_rate": win_rate,
326
+ "pnl_usd": pnl_usd,
327
+ "timestamp": datetime.utcnow().isoformat(),
328
+ "contract_tx": f"0x{hashlib.sha256(f'{strategy_id}_{pnl_usd}'.encode()).hexdigest()[:40]}",
329
+ }
330
+
331
+ self.contract_calls.append(metrics)
332
+ return metrics
333
+
334
+ # ────────────────────────────── Stats ──────────────────────────────
335
+
336
+ def stats(self) -> Dict:
337
+ """Bridge usage statistics."""
338
+ return {
339
+ "balances": self.balances,
340
+ "total_gateway_routes": len(self.gateway_routes),
341
+ "total_pending_payments": len(self.pending_payments),
342
+ "total_paymaster_ops": len(self.paymaster_ops),
343
+ "total_contract_calls": len(self.contract_calls),
344
+ "nanopayment_config": asdict(self.nanopayment),
345
+ "wallet_config": asdict(self.wallet),
346
+ "usyc_config": asdict(self.usyc),
347
+ }