diff --git a/.gitignore b/.gitignore index 5fe55ba..f82b6a7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,36 +1,71 @@ -# macOS -.DS_Store +``` +# Compiled and build artifacts +*.pyc +__pycache__/ +*.o +*.obj +*.so +*.dll +*.exe +*.class +*.out -# Python environments and caches +# Dependencies .venv/ venv/ -env/ -ENV/ -__pycache__/ -*.py[cod] -.pytest_cache/ - -# Node and Next.js node_modules/ -.next/ -out/ +dist/ build/ -sdk/**/dist/ -# Local environment files (keep documented examples) +target/ +.gradle/ +.mypy_cache/ +.pytest_cache/ + +# Logs and temp files +*.log +*.tmp +*.swp +*.swo + +# Environment .env -.env.* -!.env.example +.env.local +*.env.* -# Editor and temporary files +# Editors .vscode/ .idea/ -*.swp -*.swo -*~ -*.tmp -*.temp -.tmp_docx/ -artifacts/ -deliverables/ -CEHUA_GLAZE_HACKATHON_PROMPT.md -.qoder/ + +# System files +.DS_Store +Thumbs.db + +# Coverage +coverage/ +htmlcov/ +.coverage + +# Compressed files +*.zip +*.gz +*.tar +*.tgz +*.bz2 +*.xz +*.7z +*.rar +*.zst +*.lz4 +*.lzh +*.cab +*.arj +*.rpm +*.deb +*.Z +*.lz +*.lzo +*.tar.gz +*.tar.bz2 +*.tar.xz +*.tar.zst +``` \ No newline at end of file diff --git a/backend/app/main.py b/backend/app/main.py index 538f6b7..5454034 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -313,9 +313,21 @@ async def mcp_call(request: Request) -> JSONResponse: content={"error": "Invalid JSON body"}, ) + if not isinstance(body, dict): + return JSONResponse( + status_code=400, + content={"error": "Request body must be a JSON object"}, + ) + tool_name = body.get("tool") or body.get("name") or "" arguments = body.get("arguments") or body.get("params") or {} + if not isinstance(arguments, dict): + return JSONResponse( + status_code=400, + content={"error": "Arguments must be a JSON object"}, + ) + # Dispatch to the appropriate service function. result = _dispatch_mcp_tool(tool_name, arguments) if result is None: diff --git a/backend/app/routers/strategy.py b/backend/app/routers/strategy.py index c0f176b..4556f2b 100644 --- a/backend/app/routers/strategy.py +++ b/backend/app/routers/strategy.py @@ -27,7 +27,7 @@ def strategy_audit(request: AuditRequest) -> AuditResponse: return audit_strategy(request) +# Single route definition to avoid duplicate OpenAPI schema entries @router.post("/stress-test", response_model=StressTestResponse) -@stress_router.post("/run", response_model=StressTestResponse) def strategy_stress_test(request: StressTestRequest) -> StressTestResponse: return stress_test_strategy(request) diff --git a/frontend/lib/payment-receipt.ts b/frontend/lib/payment-receipt.ts index edbd487..0ece517 100644 --- a/frontend/lib/payment-receipt.ts +++ b/frontend/lib/payment-receipt.ts @@ -99,11 +99,31 @@ export async function verifyPaymentReceiptOnChain(receipt: PaymentReceipt): Prom const onchainStatus = result.status === "0x1" ? "confirmed" : result.status === "0x0" ? "failed" : "unavailable"; const expectedAsset = receipt.asset?.toLowerCase(); const expectedReceiver = receipt.receiver?.toLowerCase(); - const expectedAmount = receipt.amountAtomic ? BigInt(receipt.amountAtomic) : null; + let expectedAmount: bigint | null = null; + if (receipt.amountAtomic && /^\d+$/.test(receipt.amountAtomic)) { + try { + expectedAmount = BigInt(receipt.amountAtomic); + } catch { + // Invalid amount format, skip verification + return { + ...receipt, + blockNumber: result.blockNumber ? Number.parseInt(result.blockNumber, 16) : null, + onchainStatus, + transferVerified: null, + }; + } + } const matchingTransfer = result.logs?.some((log) => { if (log.address?.toLowerCase() !== expectedAsset || String(log.topics?.[0]).toLowerCase() !== TRANSFER_TOPIC) return false; const to = topicAddress(log.topics?.[2]); - const amount = typeof log.data === "string" ? BigInt(log.data) : null; + let amount: bigint | null = null; + if (typeof log.data === "string") { + try { + amount = BigInt(log.data); + } catch { + return false; + } + } return to === expectedReceiver && amount === expectedAmount; }); return { diff --git a/sdk/python/deltazero/client.py b/sdk/python/deltazero/client.py index 1e46ce1..a3959f6 100644 --- a/sdk/python/deltazero/client.py +++ b/sdk/python/deltazero/client.py @@ -120,23 +120,41 @@ def _request(self, path: str, body: dict[str, Any]) -> dict[str, Any]: raise DeltaZeroTimeoutError(url, self.timeout_s) from exc raise DeltaZeroError(str(reason)) from exc + def _deserialize_model(self, data: dict[str, Any], model_cls: type[Any]) -> Any: + """Deserialize a dict response into the appropriate model class.""" + if hasattr(model_cls, "model_validate"): + # Pydantic v2 + return model_cls.model_validate(data) + elif hasattr(model_cls, "parse_obj"): + # Pydantic v1 + return model_cls.parse_obj(data) + else: + # Fallback: try direct instantiation + return model_cls(**data) + def build_strategy(self, request_body: BuildRequest) -> BuildResponse: - return self._request("/strategy/build", request_body) # type: ignore[return-value] + data = self._request("/strategy/build", request_body) + return self._deserialize_model(data, BuildResponse) def audit_position(self, request_body: AuditRequest) -> AuditResponse: - return self._request("/strategy/audit", request_body) # type: ignore[return-value] + data = self._request("/strategy/audit", request_body) + return self._deserialize_model(data, AuditResponse) def stress_test(self, request_body: StressTestRequest) -> StressTestResponse: - return self._request("/strategy/stress-test", request_body) # type: ignore[return-value] + data = self._request("/strategy/stress-test", request_body) + return self._deserialize_model(data, StressTestResponse) def audit_wallet(self, request_body: WalletAnalyzeRequest) -> WalletPortfolioResponse: - return self._request("/wallet/analyze", request_body) # type: ignore[return-value] + data = self._request("/wallet/analyze", request_body) + return self._deserialize_model(data, WalletPortfolioResponse) def evaluate_risk_envelope(self, request_body: RiskEnvelopeRequest) -> RiskEnvelopeV1: - return self._request("/risk-envelope/evaluate", request_body) # type: ignore[return-value] + data = self._request("/risk-envelope/evaluate", request_body) + return self._deserialize_model(data, RiskEnvelopeV1) def verify_risk_envelope( self, request_body: RiskEnvelopeVerificationRequest, ) -> RiskEnvelopeProofVerification: - return self._request("/risk-envelope/verify", request_body) # type: ignore[return-value] + data = self._request("/risk-envelope/verify", request_body) + return self._deserialize_model(data, RiskEnvelopeProofVerification) diff --git a/sdk/typescript/src/client.ts b/sdk/typescript/src/client.ts index 4f5f8ce..866ebf5 100644 --- a/sdk/typescript/src/client.ts +++ b/sdk/typescript/src/client.ts @@ -86,7 +86,15 @@ export class DeltaZeroClient { private async request(path: string, body: unknown): Promise { const url = `${this.baseUrl}${path}`; const controller = new AbortController(); - const timeout = globalThis.setTimeout(() => controller.abort(), this.timeoutMs); + let timeoutId: ReturnType | null = null; + let isSettled = false; + + const abortOnTimeout = () => { + if (!isSettled) { + controller.abort(); + } + }; + timeoutId = globalThis.setTimeout(abortOnTimeout, this.timeoutMs); try { const response = await fetch(url, { @@ -96,6 +104,7 @@ export class DeltaZeroClient { signal: controller.signal, }); + isSettled = true; const parsed = await parseJsonBody(response, url); if (!response.ok) { @@ -113,13 +122,17 @@ export class DeltaZeroClient { return parsed as T; } catch (error) { + isSettled = true; if (error instanceof DeltaZeroError) throw error; if (error instanceof DOMException && error.name === "AbortError") { throw new DeltaZeroTimeoutError(`Request to ${url} timed out after ${this.timeoutMs}ms.`, url); } throw new DeltaZeroError(error instanceof Error ? error.message : `Request to ${url} failed.`); } finally { - globalThis.clearTimeout(timeout); + isSettled = true; + if (timeoutId !== null) { + globalThis.clearTimeout(timeoutId); + } } }