diff --git a/.env.example b/.env.example index c2e2c22..609ceb5 100644 --- a/.env.example +++ b/.env.example @@ -1,47 +1,146 @@ # DJD Agent Score — Environment Variables -# Copy to .env and fill in values before running. +# Copy to .env and fill in the values you actually need. +# +# There are two env namespaces in this repo: +# - Runtime/app envs: ADMIN_KEY, PAY_TO, BASE_RPC_URL, etc. +# - Evaluator contract ops envs: DJD_*, used by scripts/ for stage/deploy/verify/smoke/promote. -# ── Server ──────────────────────────────────────────────────────── +# ── Runtime / server ─────────────────────────────────────────────────────── PORT=3000 +# NODE_ENV=development +# LOG_LEVEL=debug -# ── Required ───────────────────────────────────────────────────── -# Admin API key — must be >= 32 characters in production +# Runtime metadata surfaced by /health and deploy checks. +# DJD_RUNTIME_MODE=combined +# DJD_RELEASE_SHA= +# DJD_BUILD_TIMESTAMP=2026-03-17T00:00:00.000Z + +# ── Admin / public metadata ──────────────────────────────────────────────── +# Admin API key. Required in production and reused by some post-deploy checks. ADMIN_KEY= -# Comma-separated allowed CORS origins (required in production) +# Optional alias for deploy/smoke scripts. Use the same value as ADMIN_KEY when needed. +# DJD_ADMIN_KEY= + +# Comma-separated allowed CORS origins. Required in production. # CORS_ORIGINS=https://djdagentscore.dev,https://yourfrontend.com -# Canonical public site URL used in billing links, metadata, and published docs. +# Canonical public site URL used in docs, legal pages, and generated links. # PUBLIC_BASE_URL=https://djdagentscore.dev +# Optional fallback used by some billing/public helpers if PUBLIC_BASE_URL is unset. +# BILLING_BASE_URL=https://djdagentscore.dev + # Public support email surfaced in OpenAPI and legal pages. # PUBLIC_SUPPORT_EMAIL=feedback@djdagentscore.dev -# ── Base L2 RPC ────────────────────────────────────────────────── -# Primary RPC endpoint for Base mainnet. Falls back to BASE_RPC_FALLBACK_URL on failure. +# ── Runtime chain access ──────────────────────────────────────────────────── +# Base mainnet RPC used by the scoring runtime, indexers, and publisher jobs. BASE_RPC_URL=https://base-mainnet.public.blastapi.io BASE_RPC_FALLBACK_URL=https://mainnet.base.org -# ── x402 Payments ──────────────────────────────────────────────── -# Wallet address that receives x402 USDC micropayments (your revenue address). +# Optional ERC-8004 identity registry contract address. When unset, ERC-8004 registration +# remains informative/document-shaped but not backed by a configured registry contract. +# ERC8004_IDENTITY_REGISTRY=0x0000000000000000000000000000000000000000 + +# ── x402 payments ─────────────────────────────────────────────────────────── +# Wallet that receives x402 USDC micropayments and staking fee transfers. PAY_TO=0xYOUR_WALLET_ADDRESS_HERE -# x402 facilitator service URL (default: https://x402.org/facilitator) +# x402 facilitator service URL and contract address. FACILITATOR_URL=https://x402.org/facilitator - -# Facilitator contract address on Base (default: Coinbase facilitator) FACILITATOR_ADDRESS=0x97316FA4730BC7d3B295234F8e4D04a0a4C093e8 -# ── Stripe Billing (optional) ────────────────────────────────────── -# When STRIPE_SECRET_KEY is set, /billing/* and /stripe/webhook routes are enabled. -# All STRIPE_PRICE_* vars are required when Stripe is enabled. +# ── Optional integrations ─────────────────────────────────────────────────── +# Stripe enables /billing/* and /stripe/webhook routes. # STRIPE_SECRET_KEY=sk_live_xxxxxxxxxxxxxxxxxxxxx # STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxx # STRIPE_PRICE_STARTER=price_xxxxxxxxxxxxxxxxxxxxx # STRIPE_PRICE_GROWTH=price_xxxxxxxxxxxxxxxxxxxxx # STRIPE_PRICE_SCALE=price_xxxxxxxxxxxxxxxxxxxxx -# ── GitHub Integration (optional) ──────────────────────────────── -# Personal access token for GitHub API. Without this, rate limits are 60 req/hr. -# With a token, rate limits are 5000 req/hr. Used for agent GitHub URL verification. +# GitHub API token used for agent registration / verification flows. # GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx + +# Insumer integration for additional identity context. +# INSUMER_API_URL=https://api.insumermodel.com +# INSUMER_API_KEY= + +# ── Evaluator signing / publication ──────────────────────────────────────── +# Signed oracle verdicts require ORACLE_SIGNER_PRIVATE_KEY or fallback PUBLISHER_PRIVATE_KEY. +# ORACLE_SIGNER_PRIVATE_KEY=0xyour64byteprivatekey +# PUBLISHER_PRIVATE_KEY=0xyour64byteprivatekey + +# ── Evaluator contract ops (scripts/) ────────────────────────────────────── +# Supported networks today: base, base-sepolia +# DJD_NETWORK=base-sepolia + +# Optional global RPC override for scripts. If unset, scripts look for the per-network vars below. +# DJD_RPC_URL= + +# Preferred per-network RPC vars for deploy/verify/smoke/stage. +# DJD_BASE_RPC_URL=https://mainnet.base.org +# DJD_BASE_SEPOLIA_RPC_URL=https://sepolia.base.org + +# Private key used by contracts:deploy and contracts:stage. +# DJD_DEPLOYER_PRIVATE_KEY=0xyour64byteprivatekey + +# API-driven bundle resolution: use these when fetching the deploy bundle from the live API. +# DJD_API_BASE_URL=https://djdagentscore.dev +# DJD_VERDICT_ID=verdict_123e4567-e89b-42d3-a456-426614174000 + +# Optional explicit verifier override when building a deployment bundle. +# DJD_VERIFIER_CONTRACT=0x0000000000000000000000000000000000000000 + +# File-driven bundle/result resolution. Use these when running scripts against local artifacts. +# DJD_DEPLOY_BUNDLE_PATH=.tmp/djd-evaluator-deploy-bundle.json +# DJD_DEPLOY_BUNDLE_URL=https://djdagentscore.dev/v1/score/evaluator/deploy/bundle?id=verdict_... +# DJD_DEPLOY_RESULT_PATH=.tmp/djd-evaluator-deploy-result.json + +# Published deployment registry location. Local scripts default to data/evaluator-deployments.json. +# DJD_EVALUATOR_DEPLOYMENTS_PATH=data/evaluator-deployments.json +# DJD_DEPLOYMENTS_URL=https://djdagentscore.dev/v1/score/evaluator/deployments?network=base-sepolia + +# ── Stage / preflight / smoke ────────────────────────────────────────────── +# Stage report file written by contracts:stage. +# DJD_STAGE_REPORT_PATH=.tmp/djd-evaluator-stage-report.json + +# Optional env bootstrap output written by contracts:bootstrap-env. +# DJD_ENV_BOOTSTRAP_FORMAT=dotenv +# DJD_ENV_BOOTSTRAP_OUTPUT_PATH=.tmp/djd-evaluator-bootstrap.env + +# Enable post-deploy health verification inside contracts:stage. +# DJD_STAGE_RUN_HEALTH=true +# DJD_HEALTHCHECK_URL=https://djdagentscore.dev/health + +# Expected runtime metadata for post-deploy smoke. +# DJD_EXPECT_RUNTIME_MODE=combined +# DJD_EXPECT_RELEASE_SHA= + +# Timeouts for post-deploy smoke. +# DJD_DEPLOY_SMOKE_TIMEOUT_MS=180000 +# DJD_DEPLOY_SMOKE_INTERVAL_MS=5000 + +# Publishing and promotion toggles for contracts:stage. +# DJD_STAGE_PUBLISH_REGISTRY=true +# DJD_STAGE_PROMOTE=true + +# Allow publishing a deployment registry entry even if preflight/verify/smoke did not all pass. +# DJD_PUBLISH_ALLOW_PARTIAL=false + +# ── Promotion outputs ─────────────────────────────────────────────────────── +# Generic output paths used by contracts:promote. +# DJD_PROMOTION_OUTPUT_PATH=.tmp/djd-evaluator-promotion.json +# DJD_PROMOTION_DOTENV_PATH=.tmp/djd-evaluator-promotion.env +# DJD_PROMOTION_SHELL_PATH=.tmp/djd-evaluator-promotion.sh +# DJD_PROMOTION_GITHUB_OUTPUT_PATH=.tmp/djd-evaluator-github-output.txt + +# Optional stage-specific overrides. If unset, contracts:stage falls back to the generic +# DJD_PROMOTION_* paths above. +# DJD_STAGE_PROMOTION_OUTPUT_PATH=.tmp/djd-stage-promotion.json +# DJD_STAGE_PROMOTION_DOTENV_PATH=.tmp/djd-stage-promotion.env +# DJD_STAGE_PROMOTION_SHELL_PATH=.tmp/djd-stage-promotion.sh +# DJD_STAGE_PROMOTION_GITHUB_OUTPUT_PATH=.tmp/djd-stage-github-output.txt + +# In GitHub Actions, the runner usually provides this automatically. +# GITHUB_OUTPUT= diff --git a/.gitignore b/.gitignore index 4fed8ef..556e825 100644 --- a/.gitignore +++ b/.gitignore @@ -10,9 +10,9 @@ data/ __pycache__/ .serena/ .claude/ +.worktrees/ x402/ server.json -docs/plans/ DEPLOY.md evaluate.sh program.md diff --git a/README.md b/README.md index ce19e41..fae0f56 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,82 @@ DJD Agent Score is the trust and governance layer for agent wallets on Base. Tod --- +## Local and operator setup + +Copy [`.env.example`](./.env.example) to `.env`. The repo now uses two env groups: + +- **Runtime envs** power the API itself: `ADMIN_KEY`, `PAY_TO`, `BASE_RPC_URL`, `PUBLIC_BASE_URL`, Stripe, GitHub, and signing keys. +- **`DJD_*` contract-ops envs** power the deploy pipeline in [`scripts/`](./scripts): preflight, deploy, verify, smoke, stage, publish, and promote. + +For normal local API work, the minimum is usually: + +```bash +cp .env.example .env +# then set at least: +# ADMIN_KEY=... +# PAY_TO=0x... +# BASE_RPC_URL=https://... +``` + +Start the combined runtime: + +```bash +npm install +npm run dev +``` + +### Evaluator contract staging + +The evaluator/onchain pipeline is file- and API-driven. The cleanest Base Sepolia path is: + +```bash +# required for a real public stage run +export DJD_NETWORK=base-sepolia +export DJD_BASE_SEPOLIA_RPC_URL=https://... +export DJD_DEPLOYER_PRIVATE_KEY=0x... +export DJD_API_BASE_URL=https://your-runtime.example +export DJD_VERDICT_ID=verdict_... +``` + +The `contracts:*` npm commands now auto-load `.env` if it exists, so you can either export these in your shell or keep them in the repo-local `.env`. + +Optional but commonly needed: + +```bash +export ORACLE_SIGNER_PRIVATE_KEY=0x... +export DJD_EVALUATOR_DEPLOYMENTS_PATH=data/evaluator-deployments.json +export DJD_STAGE_PUBLISH_REGISTRY=true +export DJD_STAGE_PROMOTE=true +export DJD_STAGE_REPORT_PATH=.tmp/djd-stage-report.json +export DJD_PROMOTION_OUTPUT_PATH=.tmp/djd-promotion.json +export DJD_PROMOTION_DOTENV_PATH=.tmp/djd-promotion.env +export DJD_PROMOTION_SHELL_PATH=.tmp/djd-promotion.sh +``` + +Then run: + +```bash +npm run contracts:bootstrap-env +npm run contracts:preflight +npm run contracts:stage +``` + +`contracts:bootstrap-env` is the quickest way to generate a ready-to-fill network-specific env snippet before you try a real stage run. + +If you only want the env/export bundle for the active published deployment: + +```bash +npm run contracts:promote +``` + +Important env naming detail: + +- `BASE_RPC_URL` is used by the runtime/indexer side of the app. +- `DJD_BASE_RPC_URL` and `DJD_BASE_SEPOLIA_RPC_URL` are used by the contract deployment scripts. +- `ADMIN_KEY` secures the app; `DJD_ADMIN_KEY` is an optional alias for post-deploy health checks. + +--- + ## Start here: gate an x402 route If you run a paid Hono endpoint, this is the best first integration. @@ -264,6 +340,8 @@ Paid endpoints return `402 Payment Required` without a valid payment proof or AP If you want to use DJD Agent Score in production and want help choosing a starting score threshold or rollout policy, reach out at [drewjacobs32@gmail.com](mailto:drewjacobs32@gmail.com). +Design partners can start with the [Pilot Integration Guide](./docs/pilot-integration-guide.md). + --- ## Report fraud diff --git a/artifacts/contracts/DJDEvaluatorEscrowSettlementExample.json b/artifacts/contracts/DJDEvaluatorEscrowSettlementExample.json new file mode 100644 index 0000000..1a884e9 --- /dev/null +++ b/artifacts/contracts/DJDEvaluatorEscrowSettlementExample.json @@ -0,0 +1,766 @@ +{ + "contract": "DJDEvaluatorEscrowSettlementExample", + "artifact_kind": "contract", + "source_path": "contracts/DJDEvaluatorEscrowSettlementExample.sol", + "source_sha256": "ce744e5e3b43b266ac50e5176279fb44cb12f6f14ddbaca028fe39d31ea00fbb", + "compiler": { + "name": "solc", + "version": "0.8.34+commit.80d5c536.Emscripten.clang", + "optimizer": { + "enabled": true, + "runs": 200 + }, + "via_ir": true + }, + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "verifier_", + "type": "address" + }, + { + "internalType": "address", + "name": "provider_", + "type": "address" + }, + { + "internalType": "address", + "name": "counterparty_", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "escrowIdHash_", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AlreadySettled", + "type": "error" + }, + { + "inputs": [], + "name": "CounterpartyMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "EscrowIdMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidProvider", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidVerdictSignature", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidVerifier", + "type": "error" + }, + { + "inputs": [], + "name": "ProviderMismatch", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "verdictDigest", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "packetHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "enum DJDEvaluatorEscrowSettlementExample.SettlementOutcome", + "name": "outcome", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + }, + { + "indexed": false, + "internalType": "address", + "name": "provider", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "counterparty", + "type": "address" + } + ], + "name": "VerdictSettled", + "type": "event" + }, + { + "inputs": [], + "name": "counterparty", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "escrowIdHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastPacketHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastVerdictDigest", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "outcome", + "outputs": [ + { + "internalType": "enum DJDEvaluatorEscrowSettlementExample.SettlementOutcome", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "provider", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "releaseAuthorized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "string", + "name": "verdictId", + "type": "string" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "address", + "name": "counterpartyWallet", + "type": "address" + }, + { + "internalType": "string", + "name": "escrowId", + "type": "string" + }, + { + "internalType": "string", + "name": "decision", + "type": "string" + }, + { + "internalType": "string", + "name": "recommendation", + "type": "string" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "confidence", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "agentScoreProvider", + "type": "uint16" + }, + { + "internalType": "string", + "name": "scoreModelVersion", + "type": "string" + }, + { + "internalType": "bool", + "name": "certificationValid", + "type": "bool" + }, + { + "internalType": "string", + "name": "certificationTier", + "type": "string" + }, + { + "internalType": "string", + "name": "riskLevel", + "type": "string" + }, + { + "internalType": "uint16", + "name": "riskScore", + "type": "uint16" + }, + { + "internalType": "string", + "name": "forensicTraceId", + "type": "string" + }, + { + "internalType": "bytes32", + "name": "packetHash", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "generatedAt", + "type": "string" + } + ], + "internalType": "struct IDJDEvaluatorVerdictVerifier.EvaluatorVerdict", + "name": "verdict", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "settleWithDJDVerdict", + "outputs": [ + { + "internalType": "enum DJDEvaluatorEscrowSettlementExample.SettlementOutcome", + "name": "resolvedOutcome", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "settled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "verifier", + "outputs": [ + { + "internalType": "contract IDJDEvaluatorVerdictVerifier", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x610100346100fe57601f610b5438819003918201601f19168301916001600160401b03831184841017610102578084926080946040528339810103126100fe5761004881610116565b9061005560208201610116565b606061006360408401610116565b920151926001600160a01b031680156100ef576001600160a01b038216156100e05760805260a05260c05260e052604051610a29908161012b82396080518181816102070152610619015260a051818181610167015261069e015260c051818181610191015261055d015260e0518181816101ca01526105a30152f35b633b136dc160e11b5f5260045ffd5b63baa3de5f60e01b5f5260045ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036100fe5756fe6080806040526004361015610012575f80fd5b5f3560e01c908163085d48831461068c575080630c2280471461066f57806327793f87146106485780632b7ac3f31461060457806373c3c702146105e75780638f775839146105c6578063c08e7a7e1461058c578063c3df654614610548578063df5b527a146100d15763f79106361461008a575f80fd5b346100cd575f3660031901126100cd5760ff5f5460081c1660058110156100b957602090600160405191148152f35b634e487b7160e01b5f52602160045260245ffd5b5f80fd5b346100cd5760403660031901126100cd5760043567ffffffffffffffff81116100cd578060040161022060031983360301126100cd5760243567ffffffffffffffff81116100cd57366023820112156100cd57806004013567ffffffffffffffff81116100cd5736602482840101116100cd575f549160ff831661053a57602485019061015d826106da565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811691160361052b577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316801515908161050c575b506104fd577f000000000000000000000000000000000000000000000000000000000000000080151590816104dc575b506104cd5760206102629160018060a01b037f0000000000000000000000000000000000000000000000000000000000000000169460405193849283926301f976eb60e51b8452604060048501526024610250604486018d61081f565b858103600319018287015292016107cf565b0381865afa9081156103d6575f91610492575b50156104835761029261028b60a48701866106ee565b3691610757565b602081519101209260c48601936102a8856109e6565b80610457575b156103e157506001945b60058610156100b9576102fe93600160209361ff008960081b169061ffff191617175f55604051808096819463c872964960e01b8352866004840152602483019061081f565b03915afa9182156103d6575f9261039f575b5060807f5afa00b213e4bd9e5433522c786b8e106c2868dca359646ff98deb1288fcb6c89160209684600155610364604461035d6103576101e4850135998a6002556109e6565b946106da565b92016106da565b9060405192610373848a6106cd565b1515838a01526001600160a01b039081166040840152166060820152a361039d60405180926106cd565bf35b9491506020853d6020116103ce575b816103bb60209383610721565b810103126100cd57935190936080610310565b3d91506103ae565b6040513d5f823e3d90fd5b6040600d602082516103f38482610721565b828152016c6d616e75616c5f72657669657760981b81522082145f1461041d5750506002946102b8565b602060079161042e81519182610721565b82815201666469737075746560c81b815220145f1461044f576003946102b8565b6004946102b8565b506007602060405161046a604082610721565b828152016672656c6561736560c81b81522081146102ae565b6338f16e3360e11b5f5260045ffd5b90506020813d6020116104c5575b816104ad60209383610721565b810103126100cd575180151581036100cd5786610275565b3d91506104a0565b63afdffb6560e01b5f5260045ffd5b90506104ee61028b60648901886106ee565b602081519101201415876101f3565b6305a7febd60e31b5f5260045ffd5b90506001600160a01b03610522604489016106da565b161415876101c3565b633943fc0360e11b5f5260045ffd5b62560ff960e81b5f5260045ffd5b346100cd575f3660031901126100cd576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100cd575f3660031901126100cd5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100cd575f3660031901126100cd57602060ff5f54166040519015158152f35b346100cd575f3660031901126100cd576020600254604051908152f35b346100cd575f3660031901126100cd576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100cd575f3660031901126100cd57602060ff5f5460081c1661039d60405180926106cd565b346100cd575f3660031901126100cd576020600154604051908152f35b346100cd575f3660031901126100cd577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b9060058210156100b95752565b356001600160a01b03811681036100cd5790565b903590601e19813603018212156100cd570180359067ffffffffffffffff82116100cd576020019181360383136100cd57565b90601f8019910116810190811067ffffffffffffffff82111761074357604052565b634e487b7160e01b5f52604160045260245ffd5b92919267ffffffffffffffff82116107435760405191610781601f8201601f191660200184610721565b8294818452818301116100cd578281602093845f960137010152565b9035601e19823603018112156100cd57016020813591019167ffffffffffffffff82116100cd5781360383136100cd57565b908060209392818452848401375f828201840152601f01601f1916010190565b35906001600160a01b03821682036100cd57565b359081151582036100cd57565b359061ffff821682036100cd57565b6109e3916109d46109bc61098861096b6109396108dd6108c26108a76108586108488b8061079d565b6102208c526102208c01916107cf565b6001600160a01b0361086c60208d016107ef565b1660208b01526001600160a01b0361088660408d016107ef565b1660408b015261089960608c018c61079d565b908b830360608d01526107cf565b6108b460808b018b61079d565b908a830360808c01526107cf565b6108cf60a08a018a61079d565b9089830360a08b01526107cf565b6108e960c08901610803565b151560c088015261ffff6108ff60e08a01610810565b1660e088015261ffff6109156101008a01610810565b1661010088015261092a61012089018961079d565b908883036101208a01526107cf565b6109466101408801610803565b151561014087015261095c61016088018861079d565b908783036101608901526107cf565b61097961018087018761079d565b908683036101808801526107cf565b61ffff6109986101a08701610810565b166101a08501526109ad6101c086018661079d565b908583036101c08701526107cf565b926101e08101356101e084015261020081019061079d565b916102008185039101526107cf565b90565b3580151581036100cd579056fea264697066735822122083886c7bf401a3d682ef75d66229e5e42dfee202f878320290266da974d48ed364736f6c63430008220033", + "deployed_bytecode": "0x6080806040526004361015610012575f80fd5b5f3560e01c908163085d48831461068c575080630c2280471461066f57806327793f87146106485780632b7ac3f31461060457806373c3c702146105e75780638f775839146105c6578063c08e7a7e1461058c578063c3df654614610548578063df5b527a146100d15763f79106361461008a575f80fd5b346100cd575f3660031901126100cd5760ff5f5460081c1660058110156100b957602090600160405191148152f35b634e487b7160e01b5f52602160045260245ffd5b5f80fd5b346100cd5760403660031901126100cd5760043567ffffffffffffffff81116100cd578060040161022060031983360301126100cd5760243567ffffffffffffffff81116100cd57366023820112156100cd57806004013567ffffffffffffffff81116100cd5736602482840101116100cd575f549160ff831661053a57602485019061015d826106da565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811691160361052b577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316801515908161050c575b506104fd577f000000000000000000000000000000000000000000000000000000000000000080151590816104dc575b506104cd5760206102629160018060a01b037f0000000000000000000000000000000000000000000000000000000000000000169460405193849283926301f976eb60e51b8452604060048501526024610250604486018d61081f565b858103600319018287015292016107cf565b0381865afa9081156103d6575f91610492575b50156104835761029261028b60a48701866106ee565b3691610757565b602081519101209260c48601936102a8856109e6565b80610457575b156103e157506001945b60058610156100b9576102fe93600160209361ff008960081b169061ffff191617175f55604051808096819463c872964960e01b8352866004840152602483019061081f565b03915afa9182156103d6575f9261039f575b5060807f5afa00b213e4bd9e5433522c786b8e106c2868dca359646ff98deb1288fcb6c89160209684600155610364604461035d6103576101e4850135998a6002556109e6565b946106da565b92016106da565b9060405192610373848a6106cd565b1515838a01526001600160a01b039081166040840152166060820152a361039d60405180926106cd565bf35b9491506020853d6020116103ce575b816103bb60209383610721565b810103126100cd57935190936080610310565b3d91506103ae565b6040513d5f823e3d90fd5b6040600d602082516103f38482610721565b828152016c6d616e75616c5f72657669657760981b81522082145f1461041d5750506002946102b8565b602060079161042e81519182610721565b82815201666469737075746560c81b815220145f1461044f576003946102b8565b6004946102b8565b506007602060405161046a604082610721565b828152016672656c6561736560c81b81522081146102ae565b6338f16e3360e11b5f5260045ffd5b90506020813d6020116104c5575b816104ad60209383610721565b810103126100cd575180151581036100cd5786610275565b3d91506104a0565b63afdffb6560e01b5f5260045ffd5b90506104ee61028b60648901886106ee565b602081519101201415876101f3565b6305a7febd60e31b5f5260045ffd5b90506001600160a01b03610522604489016106da565b161415876101c3565b633943fc0360e11b5f5260045ffd5b62560ff960e81b5f5260045ffd5b346100cd575f3660031901126100cd576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100cd575f3660031901126100cd5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100cd575f3660031901126100cd57602060ff5f54166040519015158152f35b346100cd575f3660031901126100cd576020600254604051908152f35b346100cd575f3660031901126100cd576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100cd575f3660031901126100cd57602060ff5f5460081c1661039d60405180926106cd565b346100cd575f3660031901126100cd576020600154604051908152f35b346100cd575f3660031901126100cd577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b9060058210156100b95752565b356001600160a01b03811681036100cd5790565b903590601e19813603018212156100cd570180359067ffffffffffffffff82116100cd576020019181360383136100cd57565b90601f8019910116810190811067ffffffffffffffff82111761074357604052565b634e487b7160e01b5f52604160045260245ffd5b92919267ffffffffffffffff82116107435760405191610781601f8201601f191660200184610721565b8294818452818301116100cd578281602093845f960137010152565b9035601e19823603018112156100cd57016020813591019167ffffffffffffffff82116100cd5781360383136100cd57565b908060209392818452848401375f828201840152601f01601f1916010190565b35906001600160a01b03821682036100cd57565b359081151582036100cd57565b359061ffff821682036100cd57565b6109e3916109d46109bc61098861096b6109396108dd6108c26108a76108586108488b8061079d565b6102208c526102208c01916107cf565b6001600160a01b0361086c60208d016107ef565b1660208b01526001600160a01b0361088660408d016107ef565b1660408b015261089960608c018c61079d565b908b830360608d01526107cf565b6108b460808b018b61079d565b908a830360808c01526107cf565b6108cf60a08a018a61079d565b9089830360a08b01526107cf565b6108e960c08901610803565b151560c088015261ffff6108ff60e08a01610810565b1660e088015261ffff6109156101008a01610810565b1661010088015261092a61012089018961079d565b908883036101208a01526107cf565b6109466101408801610803565b151561014087015261095c61016088018861079d565b908783036101608901526107cf565b61097961018087018761079d565b908683036101808801526107cf565b61ffff6109986101a08701610810565b166101a08501526109ad6101c086018661079d565b908583036101c08701526107cf565b926101e08101356101e084015261020081019061079d565b916102008185039101526107cf565b90565b3580151581036100cd579056fea264697066735822122083886c7bf401a3d682ef75d66229e5e42dfee202f878320290266da974d48ed364736f6c63430008220033", + "method_identifiers": { + "counterparty()": "c3df6546", + "escrowIdHash()": "c08e7a7e", + "lastPacketHash()": "73c3c702", + "lastVerdictDigest()": "0c228047", + "outcome()": "27793f87", + "provider()": "085d4883", + "releaseAuthorized()": "f7910636", + "settleWithDJDVerdict((string,address,address,string,string,string,bool,uint16,uint16,string,bool,string,string,uint16,string,bytes32,string),bytes)": "df5b527a", + "settled()": "8f775839", + "verifier()": "2b7ac3f3" + }, + "constructor": { + "inputs": [ + { + "internalType": "address", + "name": "verifier_", + "type": "address" + }, + { + "internalType": "address", + "name": "provider_", + "type": "address" + }, + { + "internalType": "address", + "name": "counterparty_", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "escrowIdHash_", + "type": "bytes32" + } + ] + }, + "metadata": { + "compiler": { + "version": "0.8.34+commit.80d5c536" + }, + "language": "Solidity", + "output": { + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "verifier_", + "type": "address" + }, + { + "internalType": "address", + "name": "provider_", + "type": "address" + }, + { + "internalType": "address", + "name": "counterparty_", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "escrowIdHash_", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AlreadySettled", + "type": "error" + }, + { + "inputs": [], + "name": "CounterpartyMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "EscrowIdMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidProvider", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidVerdictSignature", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidVerifier", + "type": "error" + }, + { + "inputs": [], + "name": "ProviderMismatch", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "verdictDigest", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "packetHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "enum DJDEvaluatorEscrowSettlementExample.SettlementOutcome", + "name": "outcome", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + }, + { + "indexed": false, + "internalType": "address", + "name": "provider", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "counterparty", + "type": "address" + } + ], + "name": "VerdictSettled", + "type": "event" + }, + { + "inputs": [], + "name": "counterparty", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "escrowIdHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastPacketHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastVerdictDigest", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "outcome", + "outputs": [ + { + "internalType": "enum DJDEvaluatorEscrowSettlementExample.SettlementOutcome", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "provider", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "releaseAuthorized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "string", + "name": "verdictId", + "type": "string" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "address", + "name": "counterpartyWallet", + "type": "address" + }, + { + "internalType": "string", + "name": "escrowId", + "type": "string" + }, + { + "internalType": "string", + "name": "decision", + "type": "string" + }, + { + "internalType": "string", + "name": "recommendation", + "type": "string" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "confidence", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "agentScoreProvider", + "type": "uint16" + }, + { + "internalType": "string", + "name": "scoreModelVersion", + "type": "string" + }, + { + "internalType": "bool", + "name": "certificationValid", + "type": "bool" + }, + { + "internalType": "string", + "name": "certificationTier", + "type": "string" + }, + { + "internalType": "string", + "name": "riskLevel", + "type": "string" + }, + { + "internalType": "uint16", + "name": "riskScore", + "type": "uint16" + }, + { + "internalType": "string", + "name": "forensicTraceId", + "type": "string" + }, + { + "internalType": "bytes32", + "name": "packetHash", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "generatedAt", + "type": "string" + } + ], + "internalType": "struct IDJDEvaluatorVerdictVerifier.EvaluatorVerdict", + "name": "verdict", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "settleWithDJDVerdict", + "outputs": [ + { + "internalType": "enum DJDEvaluatorEscrowSettlementExample.SettlementOutcome", + "name": "resolvedOutcome", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "settled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "verifier", + "outputs": [ + { + "internalType": "contract IDJDEvaluatorVerdictVerifier", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "devdoc": { + "kind": "dev", + "methods": {}, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": {}, + "version": 1 + } + }, + "settings": { + "compilationTarget": { + "DJDEvaluatorEscrowSettlementExample.sol": "DJDEvaluatorEscrowSettlementExample" + }, + "evmVersion": "osaka", + "libraries": {}, + "metadata": { + "bytecodeHash": "ipfs" + }, + "optimizer": { + "enabled": true, + "runs": 200 + }, + "remappings": [], + "viaIR": true + }, + "sources": { + "DJDEvaluatorEscrowSettlementExample.sol": { + "keccak256": "0x98006613eae5a6a381694b1239012fb612570fabdb3f7c65e47ab97db423773c", + "license": "MIT", + "urls": [ + "bzz-raw://9d340946ca258e8dafd7d38b0cf7a11b890e8c2a8f990ffdb0650f3c5a091268", + "dweb:/ipfs/QmUAauzgKMyA8X5i5MRAga5V263AP5aaf4doAXHWct38G5" + ] + } + }, + "version": 1 + } +} diff --git a/artifacts/contracts/DJDEvaluatorVerdictVerifier.json b/artifacts/contracts/DJDEvaluatorVerdictVerifier.json new file mode 100644 index 0000000..f9b317c --- /dev/null +++ b/artifacts/contracts/DJDEvaluatorVerdictVerifier.json @@ -0,0 +1,869 @@ +{ + "contract": "DJDEvaluatorVerdictVerifier", + "artifact_kind": "contract", + "source_path": "contracts/DJDEvaluatorVerdictVerifier.sol", + "source_sha256": "fc870e0df4939fc1072369814d9357c2385973af55e9162b907114828a512eb5", + "compiler": { + "name": "solc", + "version": "0.8.34+commit.80d5c536.Emscripten.clang", + "optimizer": { + "enabled": true, + "runs": 200 + }, + "via_ir": true + }, + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "initialSigner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "InvalidOwner", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidSigner", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousSigner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newSigner", + "type": "address" + } + ], + "name": "OracleSignerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "EIP712_DOMAIN_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "EVALUATOR_VERDICT_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "domainSeparator", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "string", + "name": "verdictId", + "type": "string" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "address", + "name": "counterpartyWallet", + "type": "address" + }, + { + "internalType": "string", + "name": "escrowId", + "type": "string" + }, + { + "internalType": "string", + "name": "decision", + "type": "string" + }, + { + "internalType": "string", + "name": "recommendation", + "type": "string" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "confidence", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "agentScoreProvider", + "type": "uint16" + }, + { + "internalType": "string", + "name": "scoreModelVersion", + "type": "string" + }, + { + "internalType": "bool", + "name": "certificationValid", + "type": "bool" + }, + { + "internalType": "string", + "name": "certificationTier", + "type": "string" + }, + { + "internalType": "string", + "name": "riskLevel", + "type": "string" + }, + { + "internalType": "uint16", + "name": "riskScore", + "type": "uint16" + }, + { + "internalType": "string", + "name": "forensicTraceId", + "type": "string" + }, + { + "internalType": "bytes32", + "name": "packetHash", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "generatedAt", + "type": "string" + } + ], + "internalType": "struct DJDEvaluatorVerdictVerifier.EvaluatorVerdict", + "name": "verdict", + "type": "tuple" + } + ], + "name": "hashVerdict", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "oracleSigner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newSigner", + "type": "address" + } + ], + "name": "setOracleSigner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "digest", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "verifyDigest", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "string", + "name": "verdictId", + "type": "string" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "address", + "name": "counterpartyWallet", + "type": "address" + }, + { + "internalType": "string", + "name": "escrowId", + "type": "string" + }, + { + "internalType": "string", + "name": "decision", + "type": "string" + }, + { + "internalType": "string", + "name": "recommendation", + "type": "string" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "confidence", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "agentScoreProvider", + "type": "uint16" + }, + { + "internalType": "string", + "name": "scoreModelVersion", + "type": "string" + }, + { + "internalType": "bool", + "name": "certificationValid", + "type": "bool" + }, + { + "internalType": "string", + "name": "certificationTier", + "type": "string" + }, + { + "internalType": "string", + "name": "riskLevel", + "type": "string" + }, + { + "internalType": "uint16", + "name": "riskScore", + "type": "uint16" + }, + { + "internalType": "string", + "name": "forensicTraceId", + "type": "string" + }, + { + "internalType": "bytes32", + "name": "packetHash", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "generatedAt", + "type": "string" + } + ], + "internalType": "struct DJDEvaluatorVerdictVerifier.EvaluatorVerdict", + "name": "verdict", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "verifyVerdict", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x60a080604052346101a857602081610a69803803809161001f82856101ac565b8339810103126101a857516001600160a01b038116908190036101a8578015610199575f80546001600160a01b031990811633179091556001805490911682179055604080519061007090826101ac565b601581527f444a44204576616c7561746f7220566572646963740000000000000000000000602090910152604080517f055a119ad2a5c0700f8493076ff9de198d1c63b90d350b68795c7a8477790cb7916100cb90826101ac565b600181526020810190603160f81b82525190206040519060208201927fc2f8787176b8ac6bf7215b4adcc1e069bf4ab82d9ab1df05a57a91d425935b6e8452604083015260608201524660808201526080815261012960a0826101ac565b51902060805260405190335f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a35f7f53484eb5c3028cc71fda017d41e647dea2740218d0f723d84cf7e98476459a8e8180a361088590816101e4823960805181818160a101526106950152f35b632057875960e21b5f5260045ffd5b5f80fd5b601f909101601f19168101906001600160401b038211908210176101cf57604052565b634e487b7160e01b5f52604160045260245ffdfe60e0806040526004361015610012575f80fd5b5f3560e01c9081631709a61b146103755750806328bff9db146102e65780633f2edd601461027a5780638da5cb5b14610253578063c3b5b74d14610219578063c7977be7146101df578063c872964914610197578063e8a32a8c1461014e578063f2fde38b146100c85763f698da251461008a575f80fd5b346100c4575f3660031901126100c45760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b5f80fd5b346100c45760203660031901126100c4576004356001600160a01b038116908190036100c4575f546001600160a01b038116903382900361013f57821561013f576001600160a01b03191682175f9081557f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3005b6349e27cff60e01b5f5260045ffd5b346100c45760403660031901126100c45760243567ffffffffffffffff81116100c45761018d6101846020923690600401610398565b906004356106d3565b6040519015158152f35b346100c45760203660031901126100c45760043567ffffffffffffffff81116100c45761022060031982360301126100c4576101d76020916004016103fc565b604051908152f35b346100c4575f3660031901126100c45760206040517fc2f8787176b8ac6bf7215b4adcc1e069bf4ab82d9ab1df05a57a91d425935b6e8152f35b346100c4575f3660031901126100c45760206040517fc2651a786e263353da90b55db746aaacb23c82c82b850d9a77aae53acf8e6e018152f35b346100c4575f3660031901126100c4575f546040516001600160a01b039091168152602090f35b346100c45760403660031901126100c45760043567ffffffffffffffff81116100c45761022060031982360301126100c4576024359067ffffffffffffffff82116100c4576102e161018d916102d66020943690600401610398565b9290916004016103fc565b6106d3565b346100c45760203660031901126100c4576004356001600160a01b038116908190036100c4575f546001600160a01b0316330361013f57801561036657600180546001600160a01b0319811683179091556001600160a01b03167f53484eb5c3028cc71fda017d41e647dea2740218d0f723d84cf7e98476459a8e5f80a3005b632057875960e21b5f5260045ffd5b346100c4575f3660031901126100c4576001546001600160a01b03168152602090f35b9181601f840112156100c45782359167ffffffffffffffff83116100c457602083818601950101116100c457565b90601f8019910116810190811067ffffffffffffffff8211176103e857604052565b634e487b7160e01b5f52604160045260245ffd5b806104078180610707565b36906104129261073a565b805190602001206020820161042690610780565b60c05261043560408301610780565b6104426060840184610707565b369061044d9261073a565b80519060200120608084016104629085610707565b369061046d9261073a565b8051906020012060a085016104829086610707565b369061048d9261073a565b8051906020012060c086016104a190610794565b6104ad60e088016107a1565b6104ba61010089016107a1565b916104c96101208a018a610707565b36906104d49261073a565b80519060200120936101408a016104ea90610794565b956104f96101608c018c610707565b36906105049261073a565b80519060200120976101808c0161051b908d610707565b36906105269261073a565b80519060200120996101a08d0161053c906107a1565b9b61054b6101c08f018f610707565b36906105569261073a565b805190602001209d610200810161056c91610707565b36906105779261073a565b8051906020012060805260405160a05260a0516020017fc2651a786e263353da90b55db746aaacb23c82c82b850d9a77aae53acf8e6e01905260a05160400152600160a01b6001900360c0511660a05160600152600160a01b600190031660a0516080015260a05160a0015260a05160c0015260a05160e00152151560a051610100015261ffff1660a051610120015261ffff1660a051610140015260a0516101600152151560a051610180015260a0516101a0015260a0516101c0015261ffff1660a0516101e0015260a05161020001526101e0013560a051610220015260805160a051610240015260a051610240905260a051610260610678916103c6565b60a0515160a05160200120604051602081019161190160f01b83527f000000000000000000000000000000000000000000000000000000000000000060228301526042820152604281526106cd6062826103c6565b51902090565b6001600160a01b03926106e79290916107b0565b1680151590816106f5575090565b6001546001600160a01b031614919050565b903590601e19813603018212156100c4570180359067ffffffffffffffff82116100c4576020019181360383136100c457565b92919267ffffffffffffffff82116103e85760405191610764601f8201601f1916602001846103c6565b8294818452818301116100c4578281602093845f960137010152565b356001600160a01b03811681036100c45790565b3580151581036100c45790565b3561ffff811681036100c45790565b91604019016108495760208101359160408201355f1a907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841161083657601b8214158061083e575b610836576020935f9360809360405193845286840152356040830152606082015282805260015afa1561082b575f5190565b6040513d5f823e3d90fd5b505050505f90565b50601c8214156107f9565b50505f9056fea2646970667358221220da3bde2da8d5d0963c879de6b1ec00b42e08c217daf0a52cb2610ebd6d88ff7864736f6c63430008220033", + "deployed_bytecode": "0x60e0806040526004361015610012575f80fd5b5f3560e01c9081631709a61b146103755750806328bff9db146102e65780633f2edd601461027a5780638da5cb5b14610253578063c3b5b74d14610219578063c7977be7146101df578063c872964914610197578063e8a32a8c1461014e578063f2fde38b146100c85763f698da251461008a575f80fd5b346100c4575f3660031901126100c45760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b5f80fd5b346100c45760203660031901126100c4576004356001600160a01b038116908190036100c4575f546001600160a01b038116903382900361013f57821561013f576001600160a01b03191682175f9081557f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3005b6349e27cff60e01b5f5260045ffd5b346100c45760403660031901126100c45760243567ffffffffffffffff81116100c45761018d6101846020923690600401610398565b906004356106d3565b6040519015158152f35b346100c45760203660031901126100c45760043567ffffffffffffffff81116100c45761022060031982360301126100c4576101d76020916004016103fc565b604051908152f35b346100c4575f3660031901126100c45760206040517fc2f8787176b8ac6bf7215b4adcc1e069bf4ab82d9ab1df05a57a91d425935b6e8152f35b346100c4575f3660031901126100c45760206040517fc2651a786e263353da90b55db746aaacb23c82c82b850d9a77aae53acf8e6e018152f35b346100c4575f3660031901126100c4575f546040516001600160a01b039091168152602090f35b346100c45760403660031901126100c45760043567ffffffffffffffff81116100c45761022060031982360301126100c4576024359067ffffffffffffffff82116100c4576102e161018d916102d66020943690600401610398565b9290916004016103fc565b6106d3565b346100c45760203660031901126100c4576004356001600160a01b038116908190036100c4575f546001600160a01b0316330361013f57801561036657600180546001600160a01b0319811683179091556001600160a01b03167f53484eb5c3028cc71fda017d41e647dea2740218d0f723d84cf7e98476459a8e5f80a3005b632057875960e21b5f5260045ffd5b346100c4575f3660031901126100c4576001546001600160a01b03168152602090f35b9181601f840112156100c45782359167ffffffffffffffff83116100c457602083818601950101116100c457565b90601f8019910116810190811067ffffffffffffffff8211176103e857604052565b634e487b7160e01b5f52604160045260245ffd5b806104078180610707565b36906104129261073a565b805190602001206020820161042690610780565b60c05261043560408301610780565b6104426060840184610707565b369061044d9261073a565b80519060200120608084016104629085610707565b369061046d9261073a565b8051906020012060a085016104829086610707565b369061048d9261073a565b8051906020012060c086016104a190610794565b6104ad60e088016107a1565b6104ba61010089016107a1565b916104c96101208a018a610707565b36906104d49261073a565b80519060200120936101408a016104ea90610794565b956104f96101608c018c610707565b36906105049261073a565b80519060200120976101808c0161051b908d610707565b36906105269261073a565b80519060200120996101a08d0161053c906107a1565b9b61054b6101c08f018f610707565b36906105569261073a565b805190602001209d610200810161056c91610707565b36906105779261073a565b8051906020012060805260405160a05260a0516020017fc2651a786e263353da90b55db746aaacb23c82c82b850d9a77aae53acf8e6e01905260a05160400152600160a01b6001900360c0511660a05160600152600160a01b600190031660a0516080015260a05160a0015260a05160c0015260a05160e00152151560a051610100015261ffff1660a051610120015261ffff1660a051610140015260a0516101600152151560a051610180015260a0516101a0015260a0516101c0015261ffff1660a0516101e0015260a05161020001526101e0013560a051610220015260805160a051610240015260a051610240905260a051610260610678916103c6565b60a0515160a05160200120604051602081019161190160f01b83527f000000000000000000000000000000000000000000000000000000000000000060228301526042820152604281526106cd6062826103c6565b51902090565b6001600160a01b03926106e79290916107b0565b1680151590816106f5575090565b6001546001600160a01b031614919050565b903590601e19813603018212156100c4570180359067ffffffffffffffff82116100c4576020019181360383136100c457565b92919267ffffffffffffffff82116103e85760405191610764601f8201601f1916602001846103c6565b8294818452818301116100c4578281602093845f960137010152565b356001600160a01b03811681036100c45790565b3580151581036100c45790565b3561ffff811681036100c45790565b91604019016108495760208101359160408201355f1a907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841161083657601b8214158061083e575b610836576020935f9360809360405193845286840152356040830152606082015282805260015afa1561082b575f5190565b6040513d5f823e3d90fd5b505050505f90565b50601c8214156107f9565b50505f9056fea2646970667358221220da3bde2da8d5d0963c879de6b1ec00b42e08c217daf0a52cb2610ebd6d88ff7864736f6c63430008220033", + "method_identifiers": { + "EIP712_DOMAIN_TYPEHASH()": "c7977be7", + "EVALUATOR_VERDICT_TYPEHASH()": "c3b5b74d", + "domainSeparator()": "f698da25", + "hashVerdict((string,address,address,string,string,string,bool,uint16,uint16,string,bool,string,string,uint16,string,bytes32,string))": "c8729649", + "oracleSigner()": "1709a61b", + "owner()": "8da5cb5b", + "setOracleSigner(address)": "28bff9db", + "transferOwnership(address)": "f2fde38b", + "verifyDigest(bytes32,bytes)": "e8a32a8c", + "verifyVerdict((string,address,address,string,string,string,bool,uint16,uint16,string,bool,string,string,uint16,string,bytes32,string),bytes)": "3f2edd60" + }, + "constructor": { + "inputs": [ + { + "internalType": "address", + "name": "initialSigner", + "type": "address" + } + ] + }, + "metadata": { + "compiler": { + "version": "0.8.34+commit.80d5c536" + }, + "language": "Solidity", + "output": { + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "initialSigner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "InvalidOwner", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidSigner", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousSigner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newSigner", + "type": "address" + } + ], + "name": "OracleSignerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "EIP712_DOMAIN_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "EVALUATOR_VERDICT_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "domainSeparator", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "string", + "name": "verdictId", + "type": "string" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "address", + "name": "counterpartyWallet", + "type": "address" + }, + { + "internalType": "string", + "name": "escrowId", + "type": "string" + }, + { + "internalType": "string", + "name": "decision", + "type": "string" + }, + { + "internalType": "string", + "name": "recommendation", + "type": "string" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "confidence", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "agentScoreProvider", + "type": "uint16" + }, + { + "internalType": "string", + "name": "scoreModelVersion", + "type": "string" + }, + { + "internalType": "bool", + "name": "certificationValid", + "type": "bool" + }, + { + "internalType": "string", + "name": "certificationTier", + "type": "string" + }, + { + "internalType": "string", + "name": "riskLevel", + "type": "string" + }, + { + "internalType": "uint16", + "name": "riskScore", + "type": "uint16" + }, + { + "internalType": "string", + "name": "forensicTraceId", + "type": "string" + }, + { + "internalType": "bytes32", + "name": "packetHash", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "generatedAt", + "type": "string" + } + ], + "internalType": "struct DJDEvaluatorVerdictVerifier.EvaluatorVerdict", + "name": "verdict", + "type": "tuple" + } + ], + "name": "hashVerdict", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "oracleSigner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newSigner", + "type": "address" + } + ], + "name": "setOracleSigner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "digest", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "verifyDigest", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "string", + "name": "verdictId", + "type": "string" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "address", + "name": "counterpartyWallet", + "type": "address" + }, + { + "internalType": "string", + "name": "escrowId", + "type": "string" + }, + { + "internalType": "string", + "name": "decision", + "type": "string" + }, + { + "internalType": "string", + "name": "recommendation", + "type": "string" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "confidence", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "agentScoreProvider", + "type": "uint16" + }, + { + "internalType": "string", + "name": "scoreModelVersion", + "type": "string" + }, + { + "internalType": "bool", + "name": "certificationValid", + "type": "bool" + }, + { + "internalType": "string", + "name": "certificationTier", + "type": "string" + }, + { + "internalType": "string", + "name": "riskLevel", + "type": "string" + }, + { + "internalType": "uint16", + "name": "riskScore", + "type": "uint16" + }, + { + "internalType": "string", + "name": "forensicTraceId", + "type": "string" + }, + { + "internalType": "bytes32", + "name": "packetHash", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "generatedAt", + "type": "string" + } + ], + "internalType": "struct DJDEvaluatorVerdictVerifier.EvaluatorVerdict", + "name": "verdict", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "verifyVerdict", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "devdoc": { + "kind": "dev", + "methods": {}, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": {}, + "version": 1 + } + }, + "settings": { + "compilationTarget": { + "DJDEvaluatorVerdictVerifier.sol": "DJDEvaluatorVerdictVerifier" + }, + "evmVersion": "osaka", + "libraries": {}, + "metadata": { + "bytecodeHash": "ipfs" + }, + "optimizer": { + "enabled": true, + "runs": 200 + }, + "remappings": [], + "viaIR": true + }, + "sources": { + "DJDEvaluatorVerdictVerifier.sol": { + "keccak256": "0x4c0cab75a23b9cab397196fd7775de46c86d89c4a11d83cd94cbbd5012854ed2", + "license": "MIT", + "urls": [ + "bzz-raw://bf1c87dbccc047c8753590b93bdc42d6a69cb154fa3de5703d054e81b738351e", + "dweb:/ipfs/QmUoLwZBrizCN79niUs9gSiBLCFwTrxAdJ76SfxwpPDUyG" + ] + } + }, + "version": 1 + } +} diff --git a/artifacts/contracts/IDJDEvaluatorOracleCallback.json b/artifacts/contracts/IDJDEvaluatorOracleCallback.json new file mode 100644 index 0000000..92d70f6 --- /dev/null +++ b/artifacts/contracts/IDJDEvaluatorOracleCallback.json @@ -0,0 +1,216 @@ +{ + "contract": "IDJDEvaluatorOracleCallback", + "artifact_kind": "interface", + "source_path": "contracts/IDJDEvaluatorOracleCallback.sol", + "source_sha256": "57633f63db8ec8c9ae01227abe5b84c6434137ebbf6c75b2cd92ac8d5ef36230", + "compiler": { + "name": "solc", + "version": "0.8.34+commit.80d5c536.Emscripten.clang", + "optimizer": { + "enabled": true, + "runs": 200 + }, + "via_ir": true + }, + "abi": [ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "escrowIdHash", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "provider", + "type": "address" + }, + { + "internalType": "address", + "name": "counterparty", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decisionCode", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "recommendationCode", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "confidence", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "agentScoreProvider", + "type": "uint16" + }, + { + "internalType": "bool", + "name": "certificationValid", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "riskScore", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "packetHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "attestationDigest", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "attestationSignature", + "type": "bytes" + } + ], + "name": "receiveVerdict", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x", + "deployed_bytecode": "0x", + "method_identifiers": { + "receiveVerdict(bytes32,address,address,uint8,uint8,bool,uint16,uint16,bool,uint16,bytes32,bytes32,bytes)": "807f6017" + }, + "constructor": null, + "metadata": { + "compiler": { + "version": "0.8.34+commit.80d5c536" + }, + "language": "Solidity", + "output": { + "abi": [ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "escrowIdHash", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "provider", + "type": "address" + }, + { + "internalType": "address", + "name": "counterparty", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decisionCode", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "recommendationCode", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "confidence", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "agentScoreProvider", + "type": "uint16" + }, + { + "internalType": "bool", + "name": "certificationValid", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "riskScore", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "packetHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "attestationDigest", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "attestationSignature", + "type": "bytes" + } + ], + "name": "receiveVerdict", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "devdoc": { + "kind": "dev", + "methods": {}, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": {}, + "version": 1 + } + }, + "settings": { + "compilationTarget": { + "IDJDEvaluatorOracleCallback.sol": "IDJDEvaluatorOracleCallback" + }, + "evmVersion": "osaka", + "libraries": {}, + "metadata": { + "bytecodeHash": "ipfs" + }, + "optimizer": { + "enabled": true, + "runs": 200 + }, + "remappings": [], + "viaIR": true + }, + "sources": { + "IDJDEvaluatorOracleCallback.sol": { + "keccak256": "0xf2d2144fec0bf5f2e5e918a8b8d34e7456d55fe90ae0a22899665b560fbdef91", + "license": "MIT", + "urls": [ + "bzz-raw://904a62dba021a5a7abc4015cd744e3ce5c4cd66897e3a76c066491ba7e70047c", + "dweb:/ipfs/QmdEtYVFRaSWsmn52eJQnLMmgUDA816G4znexoYmBNtK5a" + ] + } + }, + "version": 1 + } +} diff --git a/artifacts/contracts/IDJDEvaluatorVerdictVerifier.json b/artifacts/contracts/IDJDEvaluatorVerdictVerifier.json new file mode 100644 index 0000000..c0be532 --- /dev/null +++ b/artifacts/contracts/IDJDEvaluatorVerdictVerifier.json @@ -0,0 +1,505 @@ +{ + "contract": "IDJDEvaluatorVerdictVerifier", + "artifact_kind": "interface", + "source_path": "contracts/DJDEvaluatorEscrowSettlementExample.sol", + "source_sha256": "ce744e5e3b43b266ac50e5176279fb44cb12f6f14ddbaca028fe39d31ea00fbb", + "compiler": { + "name": "solc", + "version": "0.8.34+commit.80d5c536.Emscripten.clang", + "optimizer": { + "enabled": true, + "runs": 200 + }, + "via_ir": true + }, + "abi": [ + { + "inputs": [ + { + "components": [ + { + "internalType": "string", + "name": "verdictId", + "type": "string" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "address", + "name": "counterpartyWallet", + "type": "address" + }, + { + "internalType": "string", + "name": "escrowId", + "type": "string" + }, + { + "internalType": "string", + "name": "decision", + "type": "string" + }, + { + "internalType": "string", + "name": "recommendation", + "type": "string" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "confidence", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "agentScoreProvider", + "type": "uint16" + }, + { + "internalType": "string", + "name": "scoreModelVersion", + "type": "string" + }, + { + "internalType": "bool", + "name": "certificationValid", + "type": "bool" + }, + { + "internalType": "string", + "name": "certificationTier", + "type": "string" + }, + { + "internalType": "string", + "name": "riskLevel", + "type": "string" + }, + { + "internalType": "uint16", + "name": "riskScore", + "type": "uint16" + }, + { + "internalType": "string", + "name": "forensicTraceId", + "type": "string" + }, + { + "internalType": "bytes32", + "name": "packetHash", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "generatedAt", + "type": "string" + } + ], + "internalType": "struct IDJDEvaluatorVerdictVerifier.EvaluatorVerdict", + "name": "verdict", + "type": "tuple" + } + ], + "name": "hashVerdict", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "string", + "name": "verdictId", + "type": "string" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "address", + "name": "counterpartyWallet", + "type": "address" + }, + { + "internalType": "string", + "name": "escrowId", + "type": "string" + }, + { + "internalType": "string", + "name": "decision", + "type": "string" + }, + { + "internalType": "string", + "name": "recommendation", + "type": "string" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "confidence", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "agentScoreProvider", + "type": "uint16" + }, + { + "internalType": "string", + "name": "scoreModelVersion", + "type": "string" + }, + { + "internalType": "bool", + "name": "certificationValid", + "type": "bool" + }, + { + "internalType": "string", + "name": "certificationTier", + "type": "string" + }, + { + "internalType": "string", + "name": "riskLevel", + "type": "string" + }, + { + "internalType": "uint16", + "name": "riskScore", + "type": "uint16" + }, + { + "internalType": "string", + "name": "forensicTraceId", + "type": "string" + }, + { + "internalType": "bytes32", + "name": "packetHash", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "generatedAt", + "type": "string" + } + ], + "internalType": "struct IDJDEvaluatorVerdictVerifier.EvaluatorVerdict", + "name": "verdict", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "verifyVerdict", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x", + "deployed_bytecode": "0x", + "method_identifiers": { + "hashVerdict((string,address,address,string,string,string,bool,uint16,uint16,string,bool,string,string,uint16,string,bytes32,string))": "c8729649", + "verifyVerdict((string,address,address,string,string,string,bool,uint16,uint16,string,bool,string,string,uint16,string,bytes32,string),bytes)": "3f2edd60" + }, + "constructor": null, + "metadata": { + "compiler": { + "version": "0.8.34+commit.80d5c536" + }, + "language": "Solidity", + "output": { + "abi": [ + { + "inputs": [ + { + "components": [ + { + "internalType": "string", + "name": "verdictId", + "type": "string" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "address", + "name": "counterpartyWallet", + "type": "address" + }, + { + "internalType": "string", + "name": "escrowId", + "type": "string" + }, + { + "internalType": "string", + "name": "decision", + "type": "string" + }, + { + "internalType": "string", + "name": "recommendation", + "type": "string" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "confidence", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "agentScoreProvider", + "type": "uint16" + }, + { + "internalType": "string", + "name": "scoreModelVersion", + "type": "string" + }, + { + "internalType": "bool", + "name": "certificationValid", + "type": "bool" + }, + { + "internalType": "string", + "name": "certificationTier", + "type": "string" + }, + { + "internalType": "string", + "name": "riskLevel", + "type": "string" + }, + { + "internalType": "uint16", + "name": "riskScore", + "type": "uint16" + }, + { + "internalType": "string", + "name": "forensicTraceId", + "type": "string" + }, + { + "internalType": "bytes32", + "name": "packetHash", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "generatedAt", + "type": "string" + } + ], + "internalType": "struct IDJDEvaluatorVerdictVerifier.EvaluatorVerdict", + "name": "verdict", + "type": "tuple" + } + ], + "name": "hashVerdict", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "string", + "name": "verdictId", + "type": "string" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "address", + "name": "counterpartyWallet", + "type": "address" + }, + { + "internalType": "string", + "name": "escrowId", + "type": "string" + }, + { + "internalType": "string", + "name": "decision", + "type": "string" + }, + { + "internalType": "string", + "name": "recommendation", + "type": "string" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "confidence", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "agentScoreProvider", + "type": "uint16" + }, + { + "internalType": "string", + "name": "scoreModelVersion", + "type": "string" + }, + { + "internalType": "bool", + "name": "certificationValid", + "type": "bool" + }, + { + "internalType": "string", + "name": "certificationTier", + "type": "string" + }, + { + "internalType": "string", + "name": "riskLevel", + "type": "string" + }, + { + "internalType": "uint16", + "name": "riskScore", + "type": "uint16" + }, + { + "internalType": "string", + "name": "forensicTraceId", + "type": "string" + }, + { + "internalType": "bytes32", + "name": "packetHash", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "generatedAt", + "type": "string" + } + ], + "internalType": "struct IDJDEvaluatorVerdictVerifier.EvaluatorVerdict", + "name": "verdict", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "verifyVerdict", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "devdoc": { + "kind": "dev", + "methods": {}, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": {}, + "version": 1 + } + }, + "settings": { + "compilationTarget": { + "DJDEvaluatorEscrowSettlementExample.sol": "IDJDEvaluatorVerdictVerifier" + }, + "evmVersion": "osaka", + "libraries": {}, + "metadata": { + "bytecodeHash": "ipfs" + }, + "optimizer": { + "enabled": true, + "runs": 200 + }, + "remappings": [], + "viaIR": true + }, + "sources": { + "DJDEvaluatorEscrowSettlementExample.sol": { + "keccak256": "0x98006613eae5a6a381694b1239012fb612570fabdb3f7c65e47ab97db423773c", + "license": "MIT", + "urls": [ + "bzz-raw://9d340946ca258e8dafd7d38b0cf7a11b890e8c2a8f990ffdb0650f3c5a091268", + "dweb:/ipfs/QmUAauzgKMyA8X5i5MRAga5V263AP5aaf4doAXHWct38G5" + ] + } + }, + "version": 1 + } +} diff --git a/artifacts/contracts/manifest.json b/artifacts/contracts/manifest.json new file mode 100644 index 0000000..6a4773d --- /dev/null +++ b/artifacts/contracts/manifest.json @@ -0,0 +1,46 @@ +{ + "standard": "djd-solidity-artifacts-v1", + "compiler": { + "name": "solc", + "version": "0.8.34+commit.80d5c536.Emscripten.clang", + "via_ir": true + }, + "contracts": [ + { + "contract": "DJDEvaluatorEscrowSettlementExample", + "artifact_kind": "contract", + "artifact_path": "artifacts/contracts/DJDEvaluatorEscrowSettlementExample.json", + "source_path": "contracts/DJDEvaluatorEscrowSettlementExample.sol", + "source_sha256": "ce744e5e3b43b266ac50e5176279fb44cb12f6f14ddbaca028fe39d31ea00fbb", + "bytecode_sha256": "195a9767a5da360483f30bdb8fbb05c5648059b1a3a64f2ef7d7ada3c5abfe70", + "deployed_bytecode_sha256": "1d1c4b6c7cc137a8220a15f2e92715f0cde19c4c60e2bd9bbd552eed908cc414" + }, + { + "contract": "DJDEvaluatorVerdictVerifier", + "artifact_kind": "contract", + "artifact_path": "artifacts/contracts/DJDEvaluatorVerdictVerifier.json", + "source_path": "contracts/DJDEvaluatorVerdictVerifier.sol", + "source_sha256": "fc870e0df4939fc1072369814d9357c2385973af55e9162b907114828a512eb5", + "bytecode_sha256": "e55802cb4050bbfbfa9729a147c25712361e801ef46d1c10488f199b706f1f1f", + "deployed_bytecode_sha256": "47ede7828ff250ebffc01df20733af8801c058210e9d40fb2ee50953eee0f0fb" + }, + { + "contract": "IDJDEvaluatorOracleCallback", + "artifact_kind": "interface", + "artifact_path": "artifacts/contracts/IDJDEvaluatorOracleCallback.json", + "source_path": "contracts/IDJDEvaluatorOracleCallback.sol", + "source_sha256": "57633f63db8ec8c9ae01227abe5b84c6434137ebbf6c75b2cd92ac8d5ef36230", + "bytecode_sha256": "a54942c8e365f3784f38b8d437f9d708290db60738b00cdcfb934c32d1be97f3", + "deployed_bytecode_sha256": "a54942c8e365f3784f38b8d437f9d708290db60738b00cdcfb934c32d1be97f3" + }, + { + "contract": "IDJDEvaluatorVerdictVerifier", + "artifact_kind": "interface", + "artifact_path": "artifacts/contracts/IDJDEvaluatorVerdictVerifier.json", + "source_path": "contracts/DJDEvaluatorEscrowSettlementExample.sol", + "source_sha256": "ce744e5e3b43b266ac50e5176279fb44cb12f6f14ddbaca028fe39d31ea00fbb", + "bytecode_sha256": "a54942c8e365f3784f38b8d437f9d708290db60738b00cdcfb934c32d1be97f3", + "deployed_bytecode_sha256": "a54942c8e365f3784f38b8d437f9d708290db60738b00cdcfb934c32d1be97f3" + } + ] +} diff --git a/biome.json b/biome.json index 8a7acda..8dca632 100644 --- a/biome.json +++ b/biome.json @@ -41,6 +41,7 @@ "overrides": [ { "includes": [ + "src/scoring/**", "src/middleware/apiKeyAuth.ts", "src/middleware/paidRateLimit.ts", "src/routes/report.ts", diff --git a/contracts/DJDEvaluatorEscrowSettlementExample.sol b/contracts/DJDEvaluatorEscrowSettlementExample.sol new file mode 100644 index 0000000..f4e14bb --- /dev/null +++ b/contracts/DJDEvaluatorEscrowSettlementExample.sol @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +interface IDJDEvaluatorVerdictVerifier { + struct EvaluatorVerdict { + string verdictId; + address wallet; + address counterpartyWallet; + string escrowId; + string decision; + string recommendation; + bool approved; + uint16 confidence; + uint16 agentScoreProvider; + string scoreModelVersion; + bool certificationValid; + string certificationTier; + string riskLevel; + uint16 riskScore; + string forensicTraceId; + bytes32 packetHash; + string generatedAt; + } + + function hashVerdict( + EvaluatorVerdict calldata verdict + ) external view returns (bytes32); + + function verifyVerdict( + EvaluatorVerdict calldata verdict, + bytes calldata signature + ) external view returns (bool); +} + +contract DJDEvaluatorEscrowSettlementExample { + enum SettlementOutcome { + None, + Release, + ManualReview, + Dispute, + Reject + } + + bytes32 internal constant RELEASE_RECOMMENDATION_HASH = + keccak256(bytes("release")); + bytes32 internal constant MANUAL_REVIEW_RECOMMENDATION_HASH = + keccak256(bytes("manual_review")); + bytes32 internal constant DISPUTE_RECOMMENDATION_HASH = + keccak256(bytes("dispute")); + + IDJDEvaluatorVerdictVerifier public immutable verifier; + address public immutable provider; + address public immutable counterparty; + bytes32 public immutable escrowIdHash; + + bool public settled; + SettlementOutcome public outcome; + bytes32 public lastVerdictDigest; + bytes32 public lastPacketHash; + + event VerdictSettled( + bytes32 indexed verdictDigest, + bytes32 indexed packetHash, + SettlementOutcome outcome, + bool approved, + address provider, + address counterparty + ); + + error AlreadySettled(); + error InvalidVerifier(); + error InvalidProvider(); + error ProviderMismatch(); + error CounterpartyMismatch(); + error EscrowIdMismatch(); + error InvalidVerdictSignature(); + + constructor( + address verifier_, + address provider_, + address counterparty_, + bytes32 escrowIdHash_ + ) { + if (verifier_ == address(0)) revert InvalidVerifier(); + if (provider_ == address(0)) revert InvalidProvider(); + + verifier = IDJDEvaluatorVerdictVerifier(verifier_); + provider = provider_; + counterparty = counterparty_; + escrowIdHash = escrowIdHash_; + } + + function settleWithDJDVerdict( + IDJDEvaluatorVerdictVerifier.EvaluatorVerdict calldata verdict, + bytes calldata signature + ) external returns (SettlementOutcome resolvedOutcome) { + if (settled) revert AlreadySettled(); + if (verdict.wallet != provider) revert ProviderMismatch(); + if ( + counterparty != address(0) && + verdict.counterpartyWallet != counterparty + ) revert CounterpartyMismatch(); + if ( + escrowIdHash != bytes32(0) && + keccak256(bytes(verdict.escrowId)) != escrowIdHash + ) revert EscrowIdMismatch(); + if (!verifier.verifyVerdict(verdict, signature)) { + revert InvalidVerdictSignature(); + } + + bytes32 recommendationHash = keccak256(bytes(verdict.recommendation)); + if (verdict.approved && recommendationHash == RELEASE_RECOMMENDATION_HASH) { + resolvedOutcome = SettlementOutcome.Release; + } else if (recommendationHash == MANUAL_REVIEW_RECOMMENDATION_HASH) { + resolvedOutcome = SettlementOutcome.ManualReview; + } else if (recommendationHash == DISPUTE_RECOMMENDATION_HASH) { + resolvedOutcome = SettlementOutcome.Dispute; + } else { + resolvedOutcome = SettlementOutcome.Reject; + } + + settled = true; + outcome = resolvedOutcome; + lastVerdictDigest = verifier.hashVerdict(verdict); + lastPacketHash = verdict.packetHash; + + emit VerdictSettled( + lastVerdictDigest, + verdict.packetHash, + resolvedOutcome, + verdict.approved, + verdict.wallet, + verdict.counterpartyWallet + ); + } + + function releaseAuthorized() external view returns (bool) { + return outcome == SettlementOutcome.Release; + } +} diff --git a/contracts/DJDEvaluatorVerdictVerifier.sol b/contracts/DJDEvaluatorVerdictVerifier.sol new file mode 100644 index 0000000..415e100 --- /dev/null +++ b/contracts/DJDEvaluatorVerdictVerifier.sol @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +contract DJDEvaluatorVerdictVerifier { + bytes32 public constant EIP712_DOMAIN_TYPEHASH = + keccak256("EIP712Domain(string name,string version,uint256 chainId)"); + bytes32 public constant EVALUATOR_VERDICT_TYPEHASH = + keccak256( + "EvaluatorVerdict(string verdictId,address wallet,address counterpartyWallet,string escrowId,string decision,string recommendation,bool approved,uint16 confidence,uint16 agentScoreProvider,string scoreModelVersion,bool certificationValid,string certificationTier,string riskLevel,uint16 riskScore,string forensicTraceId,bytes32 packetHash,string generatedAt)" + ); + bytes32 internal constant SECP256K1N_HALVED = + 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0; + + struct EvaluatorVerdict { + string verdictId; + address wallet; + address counterpartyWallet; + string escrowId; + string decision; + string recommendation; + bool approved; + uint16 confidence; + uint16 agentScoreProvider; + string scoreModelVersion; + bool certificationValid; + string certificationTier; + string riskLevel; + uint16 riskScore; + string forensicTraceId; + bytes32 packetHash; + string generatedAt; + } + + address public owner; + address public oracleSigner; + bytes32 public immutable domainSeparator; + + event OracleSignerUpdated(address indexed previousSigner, address indexed newSigner); + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + error InvalidOwner(); + error InvalidSigner(); + + modifier onlyOwner() { + if (msg.sender != owner) revert InvalidOwner(); + _; + } + + constructor(address initialSigner) { + if (initialSigner == address(0)) revert InvalidSigner(); + + owner = msg.sender; + oracleSigner = initialSigner; + domainSeparator = keccak256( + abi.encode( + EIP712_DOMAIN_TYPEHASH, + keccak256(bytes("DJD Evaluator Verdict")), + keccak256(bytes("1")), + block.chainid + ) + ); + + emit OwnershipTransferred(address(0), msg.sender); + emit OracleSignerUpdated(address(0), initialSigner); + } + + function transferOwnership(address newOwner) external onlyOwner { + if (newOwner == address(0)) revert InvalidOwner(); + + address previousOwner = owner; + owner = newOwner; + emit OwnershipTransferred(previousOwner, newOwner); + } + + function setOracleSigner(address newSigner) external onlyOwner { + if (newSigner == address(0)) revert InvalidSigner(); + + address previousSigner = oracleSigner; + oracleSigner = newSigner; + emit OracleSignerUpdated(previousSigner, newSigner); + } + + function hashVerdict(EvaluatorVerdict calldata verdict) public view returns (bytes32) { + return keccak256(abi.encodePacked("\x19\x01", domainSeparator, _hashStruct(verdict))); + } + + function verifyVerdict( + EvaluatorVerdict calldata verdict, + bytes calldata signature + ) external view returns (bool) { + return verifyDigest(hashVerdict(verdict), signature); + } + + function verifyDigest(bytes32 digest, bytes calldata signature) public view returns (bool) { + address recovered = _recoverSigner(digest, signature); + return recovered != address(0) && recovered == oracleSigner; + } + + function _hashStruct(EvaluatorVerdict calldata verdict) internal pure returns (bytes32) { + return keccak256( + abi.encode( + EVALUATOR_VERDICT_TYPEHASH, + keccak256(bytes(verdict.verdictId)), + verdict.wallet, + verdict.counterpartyWallet, + keccak256(bytes(verdict.escrowId)), + keccak256(bytes(verdict.decision)), + keccak256(bytes(verdict.recommendation)), + verdict.approved, + verdict.confidence, + verdict.agentScoreProvider, + keccak256(bytes(verdict.scoreModelVersion)), + verdict.certificationValid, + keccak256(bytes(verdict.certificationTier)), + keccak256(bytes(verdict.riskLevel)), + verdict.riskScore, + keccak256(bytes(verdict.forensicTraceId)), + verdict.packetHash, + keccak256(bytes(verdict.generatedAt)) + ) + ); + } + + function _recoverSigner( + bytes32 digest, + bytes calldata signature + ) internal pure returns (address recovered) { + if (signature.length != 65) { + return address(0); + } + + bytes32 r; + bytes32 s; + uint8 v; + + assembly { + r := calldataload(signature.offset) + s := calldataload(add(signature.offset, 32)) + v := byte(0, calldataload(add(signature.offset, 64))) + } + + if (uint256(s) > uint256(SECP256K1N_HALVED)) { + return address(0); + } + + if (v != 27 && v != 28) { + return address(0); + } + + recovered = ecrecover(digest, v, r, s); + } +} diff --git a/contracts/IDJDEvaluatorOracleCallback.sol b/contracts/IDJDEvaluatorOracleCallback.sol new file mode 100644 index 0000000..8716a83 --- /dev/null +++ b/contracts/IDJDEvaluatorOracleCallback.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +interface IDJDEvaluatorOracleCallback { + function receiveVerdict( + bytes32 escrowIdHash, + address provider, + address counterparty, + uint8 decisionCode, + uint8 recommendationCode, + bool approved, + uint16 confidence, + uint16 agentScoreProvider, + bool certificationValid, + uint16 riskScore, + bytes32 packetHash, + bytes32 attestationDigest, + bytes calldata attestationSignature + ) external; +} diff --git a/docs/pilot-integration-guide.md b/docs/pilot-integration-guide.md new file mode 100644 index 0000000..93be005 --- /dev/null +++ b/docs/pilot-integration-guide.md @@ -0,0 +1,304 @@ +# DJD Agent Score — Pilot Integration Guide + +**Audience:** x402-enabled agent frameworks and wallet providers onboarding as design partners. +**API Base:** `https://djdagentscore.dev` +**Version:** v1 (March 2026) + +--- + +## Part 1: Auth Setup + +### Requesting a Pilot API Key + +Contact Drew Jacobs (`drewjacobs32@gmail.com`) to receive a pilot API key. Keys are issued per agent or service and have the format: + +``` +djd_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +``` + +### Using Your API Key + +Pass the key as a Bearer token on every request: + +```http +Authorization: Bearer djd_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +``` + +**Without an API key**, requests fall through to x402 micropayment auth. Paid endpoints charge a small per-call fee via the x402 protocol (your agent needs a funded Base L2 wallet). During the pilot, API key auth bypasses x402 and usage is tracked against your monthly quota. + +### Rate Limits + +Responses include usage headers: + +``` +X-RateLimit-Limit: 1000 +X-RateLimit-Remaining: 987 +X-RateLimit-Reset: 2026-04-01T00:00:00.000Z +``` + +Quota resets monthly. Contact us to raise limits during your pilot. + +### Registering Your Agent (Recommended) + +Register your agent's wallet to improve scoring accuracy and unlock identity-linked evidence: + +```http +POST /v1/agent/register +Authorization: Bearer djd_live_... +Content-Type: application/json + +{ + "wallet": "0xYourAgentWallet", + "name": "My Agent v1", + "description": "An x402-enabled autonomous agent", + "website_url": "https://your-project.dev" +} +``` + +**Response:** + +```json +{ + "wallet": "0xYourAgentWallet", + "name": "My Agent v1", + "registered": true, + "registered_at": "2026-03-17T10:00:00.000Z" +} +``` + +Registration is free and improves the quality of evaluator decisions over time. + +--- + +## Part 2: Calling the Evaluator + +The evaluator answers one question before every x402 transaction: **"Should I trust this counterparty enough to proceed?"** + +### Endpoint + +``` +GET /v1/score/evaluator?wallet={counterpartyWallet} +Authorization: Bearer djd_live_... +``` + +Replace `{counterpartyWallet}` with the wallet address of the agent or party you are about to transact with. + +### Request Example + +```http +GET /v1/score/evaluator?wallet=0xAbCd1234... +Authorization: Bearer djd_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +``` + +### Response Example + +```json +{ + "standard": "erc-8183-evaluator-prototype", + "wallet": "0xabcd1234...", + "decision": "approve", + "confidence": 0.84, + "rationale": "Wallet has a strong transaction history, no active fraud reports, and a verified identity profile.", + "score": { + "current_score": 78, + "current_tier": "established", + "score_confidence": 0.84, + "score_recommendation": "transact", + "score_model_version": "2.0.0", + "last_scored_at": "2026-03-17T08:45:00.000Z" + }, + "checks": [ + { "key": "score_threshold", "label": "Score threshold", "status": "pass", "details": {} }, + { "key": "fraud_reports", "label": "Fraud reports", "status": "pass", "details": { "count": 0 } }, + { "key": "blacklist", "label": "Blacklist check", "status": "pass", "details": {} }, + { "key": "identity", "label": "Identity verification", "status": "pass", "details": { "registered": true } } + ], + "risk": { + "risk_level": "low", + "flags": [] + }, + "certification": { + "active": false, + "tier": null + }, + "links": { + "full_score": "https://djdagentscore.dev/v1/score/full?wallet=0xabcd1234...", + "evidence_packet": "https://djdagentscore.dev/v1/score/evaluator/evidence?wallet=0xabcd1234...", + "forensics_summary": "https://djdagentscore.dev/v1/score/risk?wallet=0xabcd1234..." + } +} +``` + +### Decision Values + +| `decision` | Meaning | Recommended action | +|---|---|---| +| `approve` | Wallet passes all trust checks | Proceed with transaction | +| `review` | Mixed signals; not clearly safe or unsafe | Queue for human review before proceeding | +| `reject` | Wallet fails one or more trust checks | Abort transaction; log the decision | + +--- + +## Part 3: Handling Decisions + +### Integration Pattern + +Add an evaluator call to your transaction pre-flight: + +```typescript +async function shouldTransact(counterpartyWallet: string): Promise { + const res = await fetch( + `https://djdagentscore.dev/v1/score/evaluator?wallet=${counterpartyWallet}`, + { + headers: { + Authorization: `Bearer ${process.env.DJD_API_KEY}`, + }, + } + ) + + if (!res.ok) { + // On API error, fall back to your default policy (allow or deny) + console.error('DJD evaluator error', res.status) + return false // conservative fallback + } + + const result = await res.json() + + switch (result.decision) { + case 'approve': + return true + + case 'review': + await queueForHumanReview(counterpartyWallet, result) + return false // block until human clears + + case 'reject': + await logRejection(counterpartyWallet, result.rationale) + return false + + default: + return false + } +} +``` + +### Handling the `review` Case + +When `decision` is `review`, do not silently allow or deny — route to a human. Log the full result for your operator's review queue: + +```typescript +async function queueForHumanReview(wallet: string, evaluation: any) { + await yourReviewQueue.push({ + wallet, + decision: evaluation.decision, + confidence: evaluation.confidence, + rationale: evaluation.rationale, + checks: evaluation.checks, + evidence_url: evaluation.links.evidence_packet, + queued_at: new Date().toISOString(), + }) +} +``` + +### Error Handling + +| HTTP Status | Meaning | Action | +|---|---|---| +| `400` | Invalid wallet address | Fix request; log error | +| `402` | Payment required (no API key / quota exceeded) | Check API key or contact DJD | +| `404` | Wallet has no score data yet | Treat as `review` by default | +| `429` | Rate limit exceeded | Back off and retry after `X-RateLimit-Reset` | +| `5xx` | Server error | Fail open or closed per your policy; retry with backoff | + +--- + +## Part 4: Retrieving Evidence Bundles + +Every evaluator call can be paired with a full evidence packet — a structured record of exactly why a decision was made, suitable for audit, dispute, or export. + +### Endpoint + +``` +GET /v1/score/evaluator/evidence?wallet={wallet} +Authorization: Bearer djd_live_... +``` + +### Response Structure + +```json +{ + "standard": "erc-8183-evaluator-evidence-prototype", + "packet_id": "evidence_7f3a4d1c93a2bc10", + "wallet": "0xabcd1234...", + "generated_at": "2026-03-17T10:01:00.000Z", + "decision": "approve", + "confidence": 0.84, + "packet_hash": "sha256:a1b2c3d4...", + "baseline_profile": "djd-transactional-settlement-v1", + "checks": [...], + "recent_reports": [], + "report_count": 0, + "open_disputes": 0, + "artifacts": [ + { + "key": "full_score", + "label": "Full score breakdown", + "category": "score", + "status": "included", + "href": "https://djdagentscore.dev/v1/score/full?wallet=0xabcd1234...", + "summary": "Current score 78/100 with 84% confidence." + }, + { + "key": "certification_status", + "label": "Certification status", + "category": "certification", + "status": "recommended", + "href": "https://djdagentscore.dev/v1/certification?wallet=0xabcd1234...", + "summary": "No active certification on file; Transactional Settlement certification is the preferred baseline." + }, + { + "key": "forensics_summary", + "label": "Forensics summary", + "category": "forensics", + "status": "included", + "href": "https://djdagentscore.dev/v1/score/risk?wallet=0xabcd1234...", + "summary": "0 reports, 0 open disputes, risk level low." + } + ] +} +``` + +### When to Fetch Evidence + +- **Always** for `reject` decisions — log the bundle ID as immutable proof of why the transaction was blocked. +- **Always** for `review` decisions — attach to the human review queue item. +- **Selectively** for `approve` decisions — fetch and store when the transaction value exceeds your risk threshold. + +### Content-Addressed IDs + +The `packet_id` and `packet_hash` uniquely identify this evidence snapshot. Store them with your transaction record. If a dispute arises, you can reference the bundle to demonstrate the trust evaluation that was in effect at transaction time. + +--- + +## Quick Reference + +| Endpoint | Method | Auth | Cost | +|---|---|---|---| +| `GET /v1/score/basic?wallet=` | GET | API key or x402 | Free | +| `GET /v1/score/evaluator?wallet=` | GET | API key or x402 | $0.35 | +| `GET /v1/score/evaluator/evidence?wallet=` | GET | API key or x402 | $0.45 | +| `GET /v1/score/risk?wallet=` | GET | API key or x402 | $0.50 | +| `POST /v1/agent/register` | POST | API key | Free | +| `GET /health` | GET | None | Free | + +**Pilot keys bypass per-call charges** — you pay nothing during the pilot period. + +--- + +## Getting Help + +- **Email:** drewjacobs32@gmail.com +- **Docs:** https://djdagentscore.dev/docs +- **Status / Health:** `GET https://djdagentscore.dev/health` + +During the pilot, response times for support questions are same-day. If you hit an edge case, the evaluator returns `review` by default — you will never get a silent failure. diff --git a/docs/plans/2026-04-10-audit-remediation-design.md b/docs/plans/2026-04-10-audit-remediation-design.md new file mode 100644 index 0000000..647c72e --- /dev/null +++ b/docs/plans/2026-04-10-audit-remediation-design.md @@ -0,0 +1,96 @@ +# DJD AgentScore Audit Remediation Design + +## Goal + +Eliminate the highest-risk issues from the April 10, 2026 audit of `djdagentscore.dev`, starting with production parity and public-exposure risks, then repairing crawl/discovery surfaces, docs/spec consistency, and public product-surface coherence. + +## Source Of Truth + +- GitHub repo: `jacobsd32-cpu/djdagentscore` +- Working copy: `/Users/drewjacobs32/.config/superpowers/worktrees/djd-agent-score-runtime-phase2-integration/codex-audit-remediation` +- Live app target: `djd-agent-score` on Fly, serving `https://djdagentscore.dev` + +The audit showed clear repo-to-production drift. The remediation therefore starts by treating deployment parity as a first-class problem, not a side note. + +## Baseline On Entry + +- `npm install`: completed successfully in the remediation worktree. +- `npm run lint`: failing with broad pre-existing Biome formatting/import issues across the repo. +- `npm test`: failing in multiple route suites, including `tests/routes/admin.test.ts` and `tests/routes/score.test.ts`. +- Live-site observations still matter because the repo does not appear to match production exactly. + +## Remediation Strategy + +### Phase 1: Production Parity + +Establish what branch/commit/config most likely backs `djdagentscore.dev`, and compare it with the checked-out runtime repo. The purpose is to prevent shipping fixes into a branch that does not control production. + +### Phase 2: High-Risk Public Surface Fixes + +Address the issues that create the largest immediate risk: + +- lock down public operational telemetry surfaces +- restore intended behavior for health/detail exposure +- restore crawl and machine-discovery routes that are missing in production + +This phase is prioritized ahead of broader polish because public telemetry and discovery-route failures have immediate security, SEO, and operational cost. + +### Phase 3: Discovery, Spec, And Docs Consistency + +Normalize the machine-readable and developer-facing surfaces: + +- `openapi.json` +- `/docs` +- `/.well-known/x402` +- examples and model/version strings +- metadata emitted by shared public page helpers + +The objective is to ensure developers see one coherent story across the live API, docs, discovery documents, and public examples. + +### Phase 4: Public Product Surface Cleanup + +Clean up the homepage/docs/pricing/blog/status experience so the product consistently leads with the screening wedge and no longer mixes incompatible positioning or inconsistent UI/navigation patterns. + +## Parallelization Plan + +The work splits cleanly into parallel domains with mostly disjoint write sets: + +1. Production parity and ops exposure + - likely files: `src/routes/metrics.ts`, `src/routes/health.ts`, `src/services/opsService.ts`, deployment config, route registration + +2. Crawl/discovery and metadata + - likely files: `src/routes/legal.ts`, `src/routes/wellKnown.ts`, `src/templates/publicPage.ts`, possible sitemap implementation + +3. Docs/spec/example consistency + - likely files: `openapi.json`, `src/services/discoveryService.ts`, `index.html`, route docs/tests + +4. Public copy/nav coherence + - likely files: `index.html`, `src/routes/blog.ts`, pricing/status/docs templates, shared nav/footer helpers + +I will keep integration and final conflict resolution local, while subagents own their specific slices. + +## Verification Plan + +Verification will happen in layers: + +1. Repo verification + - targeted tests for touched routes/services + - lint/typecheck only where meaningful, with explicit reporting of remaining pre-existing failures + +2. Local smoke checks + - static route/output inspection + - direct command-line checks of generated responses where possible + +3. Live smoke checks + - confirm the target routes and headers behave correctly after the relevant fixes are in place + +## Success Criteria + +- public `/metrics` is no longer exposed without authorization +- `/health` only exposes detailed internals when intended +- `robots.txt` and agent discovery routes exist and return the intended content +- sitemap behavior is valid and no longer points crawlers at `openapi.json` +- OpenAPI/docs/discovery examples match current runtime behavior and versioning +- shared metadata includes the missing canonical/social essentials +- public positioning is more consistent around the screening-first wedge +- we finish with a clear residual-risk list if any repo-wide lint/test debt remains diff --git a/docs/plans/2026-04-10-audit-remediation.md b/docs/plans/2026-04-10-audit-remediation.md new file mode 100644 index 0000000..93dfbfd --- /dev/null +++ b/docs/plans/2026-04-10-audit-remediation.md @@ -0,0 +1,312 @@ +# Audit Remediation Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Remove the highest-priority audit issues from DJD AgentScore, starting with production parity and public-surface risks, then repair discovery/docs consistency and public positioning. + +**Architecture:** Use the runtime repo as the implementation base, but treat production parity as a gating task because the live site appears to differ from the checked-in code. Split the remediation into parallel domains with disjoint write sets, then integrate and verify centrally. + +**Tech Stack:** Node.js, TypeScript, Hono, Fly.io, Biome, Vitest + +--- + +### Task 1: Capture Production Parity And Baseline + +**Files:** +- Create: `docs/plans/2026-04-10-audit-remediation-design.md` +- Create: `docs/plans/2026-04-10-audit-remediation.md` +- Modify: `README.md` only if deployment-source notes belong there after investigation + +**Step 1: Record the baseline state** + +Run: + +```bash +npm install +npm run lint +npm test +``` + +Expected: +- `npm install` succeeds +- `npm run lint` fails with pre-existing Biome issues +- `npm test` fails in pre-existing route suites + +**Step 2: Confirm the repo-to-production mapping** + +Run: + +```bash +git remote -v +sed -n '1,120p' fly.toml +curl -I -L https://djdagentscore.dev/ +curl -i https://djdagentscore.dev/metrics | sed -n '1,40p' +curl -i https://djdagentscore.dev/robots.txt | sed -n '1,40p' +``` + +Expected: +- repo points to `jacobsd32-cpu/djdagentscore` +- Fly config references `djd-agent-score` +- live responses expose the mismatch being remediated + +**Step 3: Document the findings** + +Write the confirmed baseline and parity notes into: + +- `docs/plans/2026-04-10-audit-remediation-design.md` + +**Step 4: Commit planning artifacts** + +```bash +git add docs/plans/2026-04-10-audit-remediation-design.md docs/plans/2026-04-10-audit-remediation.md +git commit -m "docs: add audit remediation design and plan" +``` + +### Task 2: Lock Down Public Ops Exposure + +**Files:** +- Modify: `src/routes/metrics.ts` +- Modify: `src/routes/health.ts` +- Modify: `src/services/opsService.ts` +- Modify: `src/app.ts` +- Test: `tests/routes/admin.test.ts` +- Test: add targeted tests if missing around metrics/health authorization behavior + +**Step 1: Write or extend failing tests** + +Cover: +- unauthenticated `/metrics` must not expose Prometheus output +- `/health` public response must be minimal +- admin-authenticated `/health` may expose detailed payload + +**Step 2: Run the targeted tests** + +Example: + +```bash +npm test -- tests/routes/admin.test.ts +``` + +Expected: +- current behavior fails or is missing coverage + +**Step 3: Implement the minimal route/auth fixes** + +Ensure: +- `/metrics` is guarded as intended +- `/health` cannot leak detailed internals without valid admin authorization +- route registration does not accidentally bypass route-level auth + +**Step 4: Re-run the targeted tests** + +Expected: +- metrics/health authorization tests pass + +**Step 5: Smoke-check locally** + +Run: + +```bash +curl -i http://localhost:3000/metrics +curl -i http://localhost:3000/health +``` + +Expected: +- unauthorized metrics blocked +- public health trimmed + +### Task 3: Restore Crawl And Machine Discovery Surfaces + +**Files:** +- Modify: `src/routes/legal.ts` +- Modify: `src/routes/wellKnown.ts` +- Modify: `src/app.ts` +- Create or Modify: sitemap route implementation file if absent +- Test: route tests covering `robots.txt`, agent discovery, and sitemap behavior + +**Step 1: Write failing route tests** + +Cover: +- `GET /robots.txt` +- `GET /.well-known/agent.json` +- valid sitemap endpoint +- correct route registration through `app.ts` + +**Step 2: Run the focused tests** + +Expected: +- current route behavior or registration gaps fail + +**Step 3: Implement the route fixes** + +Ensure: +- `robots.txt` returns `200` +- sitemap reference points to an actual XML sitemap, not `openapi.json` +- `/.well-known/agent.json` returns intended machine-readable metadata + +**Step 4: Re-run the focused tests and CLI smoke checks** + +Run: + +```bash +curl -i http://localhost:3000/robots.txt +curl -i http://localhost:3000/.well-known/agent.json +curl -i http://localhost:3000/sitemap.xml +``` + +Expected: +- all three return `200` with valid content types + +### Task 4: Normalize OpenAPI, Docs, And Discovery Examples + +**Files:** +- Modify: `openapi.json` +- Modify: `src/services/discoveryService.ts` +- Modify: `index.html` +- Test: `tests/routes/openapi.test.ts` +- Test: add targeted docs/discovery example assertions if missing + +**Step 1: Write failing assertions for version/example mismatches** + +Cover: +- current service version strings +- basic score example payload shape +- model version/example freshness fields +- consistency between docs/openapi/x402 discovery output + +**Step 2: Run the focused tests** + +```bash +npm test -- tests/routes/openapi.test.ts +``` + +**Step 3: Update the machine-readable surfaces** + +Fix: +- stale `info.version` +- stale example `modelVersion` +- any missing fields that current live/basic responses include +- x402 discovery examples that still reflect outdated semantics + +**Step 4: Re-run focused tests and inspect rendered outputs** + +Run: + +```bash +curl -i http://localhost:3000/openapi.json | sed -n '1,80p' +curl -i http://localhost:3000/.well-known/x402 | sed -n '1,120p' +``` + +Expected: +- values line up with current runtime behavior + +### Task 5: Repair Shared Metadata And Public SEO Essentials + +**Files:** +- Modify: `src/templates/publicPage.ts` +- Modify: public route/template files that need explicit overrides +- Test: add targeted assertions for shared head output if missing + +**Step 1: Write failing assertions or snapshot checks** + +Cover: +- canonical link +- `og:image` and `twitter:image` support +- retained title/description/og url behavior + +**Step 2: Implement the shared head improvements** + +Add: +- canonical output +- image metadata plumbing +- safe defaults that do not require every page to hand-roll metadata + +**Step 3: Re-run tests and inspect sample HTML** + +Run: + +```bash +curl -s http://localhost:3000/ | sed -n '1,40p' +curl -s http://localhost:3000/pricing | sed -n '1,40p' +``` + +Expected: +- canonical and social image metadata appear where intended + +### Task 6: Unify Public Positioning And Navigation + +**Files:** +- Modify: `index.html` +- Modify: `src/routes/blog.ts` +- Modify: pricing/docs/status/public templates as needed +- Test: add or update route/content assertions where practical + +**Step 1: Audit the public copy against the screening-first wedge** + +Focus on: +- homepage hero and CTA +- docs hero and quickstart framing +- pricing surface +- blog listing/nav +- status page linkage + +**Step 2: Implement copy and nav consistency updates** + +Keep: +- “screen wallets before payout or paid route execution” as the primary wedge + +Reduce: +- mixed governance/trust-infrastructure messaging where it obscures the primary product +- inconsistent nav menus across major public pages + +**Step 3: Re-run targeted route tests and CLI HTML checks** + +Expected: +- primary public surfaces tell a coherent story + +### Task 7: Integrate, Verify, And Prepare Live Smoke Checks + +**Files:** +- Modify: any touched tests or docs from prior tasks + +**Step 1: Run focused verification for every touched area** + +Examples: + +```bash +npm test -- tests/routes/admin.test.ts tests/routes/openapi.test.ts +``` + +**Step 2: Run broader repo verification** + +Run: + +```bash +npm run lint +npm test +``` + +Expected: +- either improved results or explicitly documented remaining unrelated failures + +**Step 3: Start the app locally and smoke-check key routes** + +Run: + +```bash +npm run dev +curl -i http://localhost:3000/metrics +curl -i http://localhost:3000/health +curl -i http://localhost:3000/robots.txt +curl -i http://localhost:3000/.well-known/agent.json +curl -i http://localhost:3000/openapi.json +``` + +**Step 4: Commit the remediation** + +```bash +git add . +git commit -m "fix: remediate audit findings across ops, discovery, and docs" +``` + +Plan complete and saved to `docs/plans/2026-04-10-audit-remediation.md`. The user already selected the subagent-driven execution approach in this session, so proceed with parallel subagents on disjoint write scopes and integrate centrally. diff --git a/index.html b/index.html index 7c09ff1..fd8d2f9 100644 --- a/index.html +++ b/index.html @@ -3,16 +3,21 @@ -DJD Agent Score — Trust Infrastructure for the Agent Economy - +DJD Agent Score — Screen wallets before payout + + - - + + + + - - + + + + @@ -327,13 +332,12 @@ @@ -343,23 +347,23 @@
-
Live on Base · Wallet trust for apps, marketplaces, and agents
-

Know who an agent wallet is before your app sends money.

-

DJD turns on-chain behavior into a trust layer. For humans, that means profiles, certification, and a public directory. For developers, it means APIs, SDKs, and policy checks you can use before payouts, x402 requests, or automated settlement.

+
Live on Base · Screen wallets before payout
+

Know who a wallet is before your app pays it.

+

DJD turns on-chain behavior into a screening layer. For humans, that means profiles, certification, and a public directory. For developers, it means APIs, SDKs, and policy checks you can use before payouts, paid routes, or automated settlement.

+ Screen wallets before payouts Free basic wallet lookup API keys and SDKs for developers - Card billing for teams x402 support for autonomous agents
@@ -548,7 +552,7 @@

Ask for an evaluator decision

Built For
-
Use DJD when your product has to trust a wallet
+
Use DJD when your product has to screen a wallet before money moves
The first buyers are usually developers shipping payouts, marketplaces, or paid agent workflows. DJD sits between a wallet address and a money-moving decision.
@@ -715,7 +719,7 @@

Integrity Systems

Integration
-
Start with the x402 gate
+
Start with the screening gate
If you are a developer, this is the fastest production wedge: check the payer wallet before your paid route does any work. Human teams can use API keys and card billing; autonomous agents can still pay per request with x402.
Hono + x402
import { agentScoreGate } from 'x402-agent-score'
 
@@ -925,8 +929,8 @@ 

Need a trust plan for your product?

Disclaimer: DJD Agent Score is experimental. All scores are algorithmically generated from publicly available blockchain data and unverified third-party submissions. Scores are not financial advice, credit assessments, investment recommendations, or guarantees. The scoring model is unvalidated and may produce inaccurate results. This service is not a "consumer report" as defined by the FCRA. By using this service you agree to the Terms of Service and acknowledge the Privacy Policy.
-
© 2026 DJD Agent Score LLC · Trust infrastructure for agent payments and marketplaces on Base · Pricing · Identity attestation by Insumer Model
- +
© 2026 DJD Agent Score LLC · Wallet screening infrastructure for payouts and paid routes on Base · Pricing · Identity attestation by Insumer Model
+
diff --git a/openapi.json b/openapi.json index 50972ec..16b2e4f 100644 --- a/openapi.json +++ b/openapi.json @@ -2,253 +2,442 @@ "openapi": "3.1.0", "info": { "title": "DJD Agent Score API", - "description": "Trust infrastructure for AI agent wallets on Base. DJD helps apps and agent operators score wallets, publish public trust surfaces, and enforce payout or settlement policy before money moves. Use it for agent marketplaces, payout and escrow flows, paid tools, and automated settlement decisions. Paid endpoints support x402 on Base or API-key access for developer teams; free endpoints require no authentication. Interactive docs available at /docs.", - "version": "2.0.0", - "contact": { "email": "drewjacobs32@gmail.com" }, - "license": { "name": "MIT", "url": "https://github.com/jacobsd32-cpu/djdagentscore/blob/main/LICENSE" } + "description": "Wallet screening and trust signals for AI agent wallets on Base. DJD helps apps and agent operators score wallets, publish public trust surfaces, and enforce payout or settlement policy before money moves. Use it for payout and escrow flows, paid tools, agent marketplaces, and automated settlement decisions. Paid endpoints support x402 on Base or API-key access for developer teams; free endpoints require no authentication. Interactive docs available at /docs.", + "version": "2.5.0", + "contact": { + "email": "drewjacobs32@gmail.com" + }, + "license": { + "name": "MIT", + "url": "https://github.com/jacobsd32-cpu/djdagentscore/blob/main/LICENSE" + } }, "servers": [ - { "url": "https://djdagentscore.dev", "description": "Production" } + { + "url": "https://djdagentscore.dev", + "description": "Production" + } ], "tags": [ - { "name": "score", "description": "Score lookup and computation" }, - { "name": "register", "description": "Agent self-registration" }, - { "name": "report", "description": "Fraud reporting" }, - { "name": "ratings", "description": "Transaction-backed mutual counterparty ratings" }, - { "name": "data", "description": "Aggregated data endpoints" }, - { "name": "system", "description": "Health and metadata" }, - { "name": "api-keys", "description": "API key management (admin)" }, - { "name": "webhooks", "description": "Webhook subscriptions (admin and public)" }, - { "name": "monitoring", "description": "Managed score and DJD Forensics monitoring subscriptions" }, - { "name": "history", "description": "Historical score data (paid)" }, - { "name": "forensics", "description": "DJD Forensics wallet overview and incident timelines (paid)" }, - { "name": "certification", "description": "Certified Agent Badge (paid, admin, and public)" } + { + "name": "score", + "description": "Score lookup and computation" + }, + { + "name": "register", + "description": "Agent self-registration" + }, + { + "name": "report", + "description": "Fraud reporting" + }, + { + "name": "ratings", + "description": "Transaction-backed mutual counterparty ratings" + }, + { + "name": "data", + "description": "Aggregated data endpoints" + }, + { + "name": "system", + "description": "Health and metadata" + }, + { + "name": "api-keys", + "description": "API key management (admin)" + }, + { + "name": "webhooks", + "description": "Webhook subscriptions (admin and public)" + }, + { + "name": "monitoring", + "description": "Managed score and DJD Forensics monitoring subscriptions" + }, + { + "name": "history", + "description": "Historical score data (paid)" + }, + { + "name": "forensics", + "description": "DJD Forensics wallet overview and incident timelines (paid)" + }, + { + "name": "certification", + "description": "Certified Agent Badge (paid, admin, and public)" + } ], "paths": { "/v1/score/basic": { "get": { - "tags": ["score"], + "tags": [ + "score" + ], "summary": "Basic score lookup", "description": "Returns composite score, tier, confidence, recommendation, and model version. Free tier: 10 requests/day per IP. No payment required.", "operationId": "getBasicScore", "parameters": [ - { "$ref": "#/components/parameters/wallet" } + { + "$ref": "#/components/parameters/wallet" + } ], "responses": { "200": { "description": "Score returned (may include stale:true if RPC was unavailable)", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/BasicScoreResponse" }, + "schema": { + "$ref": "#/components/schemas/BasicScoreResponse" + }, "example": { "wallet": "0x3e4ef1f774857c69e33dddc471e110c7ac7bb528", - "score": 13, - "tier": "Unverified", - "confidence": 0.1, - "recommendation": "insufficient_history", - "modelVersion": "1.0.0", - "lastUpdated": "2026-02-21T20:00:00.000Z", - "computedAt": "2026-02-21T20:00:00.000Z", - "scoreFreshness": 0.85 + "score": 78, + "tier": "Trusted", + "confidence": 0.85, + "recommendation": "proceed", + "modelVersion": "2.5.0", + "lastUpdated": "2026-04-10T00:00:00.000Z", + "computedAt": "2026-04-10T00:00:00.000Z", + "scoreFreshness": 1 } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "429": { "$ref": "#/components/responses/FreeTierExhausted" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "429": { + "$ref": "#/components/responses/FreeTierExhausted" + } } } }, "/v1/score/erc8004": { "get": { - "tags": ["score"], + "tags": [ + "score" + ], "summary": "ERC-8004-compatible reputation document", "description": "Returns a free ERC-8004-compatible reputation document for a wallet, bundling the latest DJD score with registration, certification, and on-chain publication status.", "operationId": "getErc8004CompatibleScore", "parameters": [ - { "$ref": "#/components/parameters/wallet" } + { + "$ref": "#/components/parameters/wallet" + } ], "responses": { "200": { "description": "Compatibility document returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ERC8004CompatibleScoreResponse" } + "schema": { + "$ref": "#/components/schemas/ERC8004CompatibleScoreResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "429": { "$ref": "#/components/responses/RateLimited" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } } } }, "/v1/score/full": { "get": { - "tags": ["score"], + "tags": [ + "score" + ], "summary": "Full score with dimension breakdown", "description": "Returns the composite score plus all four dimension scores (reliability, viability, identity, capability), integrity flags, data availability assessment, and improvement path. **Requires x402 payment of $0.10 USDC on Base.**", "operationId": "getFullScore", "parameters": [ - { "$ref": "#/components/parameters/wallet" } + { + "$ref": "#/components/parameters/wallet" + } ], - "security": [{ "x402": [] }], - "x-x402": { "price": "$0.10", "network": "base" }, + "security": [ + { + "x402": [] + } + ], + "x-x402": { + "price": "$0.10", + "network": "base" + }, "responses": { "200": { "description": "Full score returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/FullScoreResponse" } + "schema": { + "$ref": "#/components/schemas/FullScoreResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "429": { "$ref": "#/components/responses/RateLimited" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } } } }, "/v1/score/evaluator": { "get": { - "tags": ["score"], + "tags": [ + "score" + ], "summary": "ERC-8183 evaluator prototype", - "description": "Returns a settlement-readiness decision using DJD score, certification, risk, ratings, and creator-stake data. **Requires x402 payment of $0.35 USDC on Base or Bearer API key.**", + "description": "ERC-8183 evaluator prototype that returns an approve, review, or reject recommendation for settlement readiness, plus links into standards and forensics surfaces.", "operationId": "getEvaluatorPreview", "parameters": [ - { "$ref": "#/components/parameters/wallet" } + { + "$ref": "#/components/parameters/wallet" + } + ], + "security": [ + { + "x402": [] + } ], - "security": [{ "x402": [] }], - "x-x402": { "price": "$0.35", "network": "base" }, + "x-x402": { + "price": "$0.35", + "network": "base" + }, "responses": { "200": { "description": "Evaluator preview returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/EvaluatorPreviewResponse" } + "schema": { + "$ref": "#/components/schemas/EvaluatorPreviewResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "429": { "$ref": "#/components/responses/RateLimited" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } } } }, "/v1/score/risk": { "get": { - "tags": ["score"], + "tags": [ + "score" + ], "summary": "Risk prediction overlay", "description": "Returns a risk prediction view for a wallet using fraud pressure, fraud-pattern matches, sybil and gaming signals, counterparty ratings, and transaction-intent outcomes. **Requires x402 payment of $0.50 USDC on Base or Bearer API key.**", "operationId": "getRiskScore", "parameters": [ - { "$ref": "#/components/parameters/wallet" } + { + "$ref": "#/components/parameters/wallet" + } + ], + "security": [ + { + "x402": [] + } ], - "security": [{ "x402": [] }], - "x-x402": { "price": "$0.50", "network": "base" }, + "x-x402": { + "price": "$0.50", + "network": "base" + }, "responses": { "200": { "description": "Risk profile returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/RiskScoreResponse" } + "schema": { + "$ref": "#/components/schemas/RiskScoreResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "429": { "$ref": "#/components/responses/RateLimited" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } } } }, "/v1/cluster": { "get": { - "tags": ["data"], + "tags": [ + "data" + ], "summary": "Cluster analysis", "description": "Returns a cluster view for a wallet using relationship-graph structure, risk signals, and persisted cluster assignments. **Requires x402 payment of $0.15 USDC on Base or Bearer API key.**", "operationId": "getCluster", "parameters": [ - { "$ref": "#/components/parameters/wallet" }, + { + "$ref": "#/components/parameters/wallet" + }, { "name": "limit", "in": "query", "required": false, "description": "Max cluster peers and linked wallets to return (default 10, max 25)", - "schema": { "type": "integer", "minimum": 1, "maximum": 25, "default": 10 } + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 25, + "default": 10 + } + } + ], + "security": [ + { + "x402": [] } ], - "security": [{ "x402": [] }], - "x-x402": { "price": "$0.15", "network": "base" }, + "x-x402": { + "price": "$0.15", + "network": "base" + }, "responses": { "200": { "description": "Cluster analysis returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ClusterResponse" } + "schema": { + "$ref": "#/components/schemas/ClusterResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "429": { "$ref": "#/components/responses/RateLimited" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } } } }, "/v1/score/refresh": { "post": { - "tags": ["score"], + "tags": [ + "score" + ], "summary": "Force live recalculation", "description": "Bypasses the cache and triggers a fresh on-chain scan. Returns the full score response. **Requires x402 payment of $0.25 USDC on Base.** Prefer POST; GET is accepted for backward compatibility but deprecated.", "operationId": "refreshScore", "parameters": [ - { "$ref": "#/components/parameters/wallet" } + { + "$ref": "#/components/parameters/wallet" + } + ], + "security": [ + { + "x402": [] + } ], - "security": [{ "x402": [] }], - "x-x402": { "price": "$0.25", "network": "base" }, + "x-x402": { + "price": "$0.25", + "network": "base" + }, "responses": { "200": { "description": "Freshly computed full score", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/FullScoreResponse" } + "schema": { + "$ref": "#/components/schemas/FullScoreResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "429": { "$ref": "#/components/responses/RateLimited" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } } }, "get": { - "tags": ["score"], + "tags": [ + "score" + ], "summary": "Force live recalculation (deprecated — use POST)", "description": "**Deprecated.** Use `POST /v1/score/refresh` instead. This GET alias is kept for backward compatibility only.", "operationId": "refreshScoreDeprecated", "deprecated": true, "parameters": [ - { "$ref": "#/components/parameters/wallet" } + { + "$ref": "#/components/parameters/wallet" + } + ], + "security": [ + { + "x402": [] + } ], - "security": [{ "x402": [] }], - "x-x402": { "price": "$0.25", "network": "base" }, + "x-x402": { + "price": "$0.25", + "network": "base" + }, "responses": { "200": { "description": "Freshly computed full score", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/FullScoreResponse" } + "schema": { + "$ref": "#/components/schemas/FullScoreResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "429": { "$ref": "#/components/responses/RateLimited" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } } } }, "/v1/score/compute": { "post": { - "tags": ["score"], + "tags": [ + "score" + ], "summary": "Queue async score computation", "description": "Submits a background full-scan scoring job and returns a `jobId` immediately (HTTP 202). Use `GET /v1/score/job/{jobId}` to poll for completion. Free — no payment required. Useful when the caller cannot wait 20-150s for the synchronous endpoints.", "operationId": "computeScore", @@ -259,7 +448,10 @@ "required": false, "description": "Wallet address (deprecated — prefer JSON body)", "deprecated": true, - "schema": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$" } + "schema": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{40}$" + } } ], "requestBody": { @@ -269,12 +461,20 @@ "application/json": { "schema": { "type": "object", - "required": ["wallet"], + "required": [ + "wallet" + ], "properties": { - "wallet": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$", "description": "Base wallet address" } + "wallet": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{40}$", + "description": "Base wallet address" + } } }, - "example": { "wallet": "0x3e4ef1f774857c69e33dddc471e110c7ac7bb528" } + "example": { + "wallet": "0x3e4ef1f774857c69e33dddc471e110c7ac7bb528" + } } } }, @@ -286,10 +486,23 @@ "schema": { "type": "object", "properties": { - "jobId": { "type": "string", "format": "uuid" }, - "status": { "type": "string", "enum": ["pending"] }, - "wallet": { "type": "string" }, - "pollUrl": { "type": "string", "description": "Relative URL to poll for job status" } + "jobId": { + "type": "string", + "format": "uuid" + }, + "status": { + "type": "string", + "enum": [ + "pending" + ] + }, + "wallet": { + "type": "string" + }, + "pollUrl": { + "type": "string", + "description": "Relative URL to poll for job status" + } } }, "example": { @@ -301,13 +514,17 @@ } } }, - "400": { "$ref": "#/components/responses/BadRequest" } + "400": { + "$ref": "#/components/responses/BadRequest" + } } } }, "/v1/score/job/{jobId}": { "get": { - "tags": ["score"], + "tags": [ + "score" + ], "summary": "Poll async scoring job", "description": "Returns the current status of a scoring job submitted via `POST /v1/score/compute`. Jobs expire after 10 minutes.", "operationId": "getJobStatus", @@ -316,7 +533,10 @@ "name": "jobId", "in": "path", "required": true, - "schema": { "type": "string", "format": "uuid" } + "schema": { + "type": "string", + "format": "uuid" + } } ], "responses": { @@ -327,11 +547,28 @@ "schema": { "type": "object", "properties": { - "jobId": { "type": "string" }, - "status": { "type": "string", "enum": ["pending", "complete", "error"] }, - "wallet": { "type": "string" }, - "result": { "$ref": "#/components/schemas/FullScoreResponse", "description": "Present when status=complete" }, - "error": { "type": "string", "description": "Present when status=error" } + "jobId": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pending", + "complete", + "error" + ] + }, + "wallet": { + "type": "string" + }, + "result": { + "$ref": "#/components/schemas/FullScoreResponse", + "description": "Present when status=complete" + }, + "error": { + "type": "string", + "description": "Present when status=error" + } } } } @@ -339,14 +576,22 @@ }, "404": { "description": "Job not found or expired", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } } } } }, "/v1/agent/register": { "post": { - "tags": ["register"], + "tags": [ + "register" + ], "summary": "Register an agent wallet", "description": "Voluntarily register a wallet with optional metadata. Adds +15 identity points to the score and marks the wallet as registered on the leaderboard. GitHub URL is asynchronously verified against the GitHub API. Free — no payment required.", "operationId": "registerAgent", @@ -354,7 +599,9 @@ "required": true, "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/RegisterBody" }, + "schema": { + "$ref": "#/components/schemas/RegisterBody" + }, "example": { "wallet": "0x3e4ef1f774857c69e33dddc471e110c7ac7bb528", "name": "DJD Score API", @@ -366,55 +613,126 @@ } }, "responses": { - "201": { - "description": "First-time registration", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RegisterResponse" } } } - }, "200": { "description": "Updated existing registration", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RegisterResponse" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegisterResponse" + } + } + } + }, + "201": { + "description": "First-time registration", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegisterResponse" + } + } + } }, - "400": { "$ref": "#/components/responses/BadRequest" } + "400": { + "$ref": "#/components/responses/BadRequest" + } } }, "get": { - "tags": ["register"], + "tags": [ + "register" + ], "summary": "Look up agent registration", "description": "Returns registration metadata for a wallet. Free — no payment required.", "operationId": "getRegistration", "parameters": [ - { "$ref": "#/components/parameters/wallet" } + { + "$ref": "#/components/parameters/wallet" + } ], "responses": { "200": { "description": "Registration found", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RegisterResponse" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegisterResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "404": { "description": "Wallet not registered", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } + "404": { + "description": "Wallet not registered", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } } } }, "/v1/report": { "post": { - "tags": ["report"], + "tags": [ + "report" + ], "summary": "Submit a fraud report", "description": "Submit a fraud or misconduct report against a wallet. Verified reports reduce the target wallet's composite score by up to 25 points. **Requires x402 payment of $0.02 USDC on Base.**", "operationId": "submitReport", - "security": [{ "x402": [] }], - "x-x402": { "price": "$0.02", "network": "base" }, + "security": [ + { + "x402": [] + } + ], + "x-x402": { + "price": "$0.02", + "network": "base" + }, "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", - "required": ["target", "reporter", "reason", "details"], + "required": [ + "target", + "reporter", + "reason", + "details" + ], "properties": { - "target": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$", "description": "Wallet being reported" }, - "reporter": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$", "description": "Wallet submitting the report (must differ from target)" }, - "reason": { "type": "string", "enum": ["failed_delivery", "payment_fraud", "impersonation", "malicious_behavior", "other"], "description": "Category of misconduct" }, - "details": { "type": "string", "maxLength": 1000, "description": "Supporting details and evidence (required, max 1000 chars)" } + "target": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{40}$", + "description": "Wallet being reported" + }, + "reporter": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{40}$", + "description": "Wallet submitting the report (must differ from target)" + }, + "reason": { + "type": "string", + "enum": [ + "failed_delivery", + "payment_fraud", + "impersonation", + "malicious_behavior", + "other" + ], + "description": "Category of misconduct" + }, + "details": { + "type": "string", + "maxLength": 1000, + "description": "Supporting details and evidence (required, max 1000 chars)" + } } } } @@ -425,29 +743,48 @@ "description": "Report submitted", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ReportResponse" } + "schema": { + "$ref": "#/components/schemas/ReportResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "429": { "$ref": "#/components/responses/RateLimited" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } } } }, "/v1/rate": { "post": { - "tags": ["ratings"], + "tags": [ + "ratings" + ], "summary": "Submit a mutual counterparty rating", "description": "Submit a transaction-backed 1-5 star rating for a counterparty wallet. The payer wallet becomes the rater, and the `tx_hash` must reference an indexed transaction worth at least $0.10 USDC between the two wallets. **Requires x402 payment of $0.01 USDC on Base.**", "operationId": "submitRating", - "security": [{ "x402": [] }], - "x-x402": { "price": "$0.01", "network": "base" }, + "security": [ + { + "x402": [] + } + ], + "x-x402": { + "price": "$0.01", + "network": "base" + }, "requestBody": { "required": true, "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/RatingRequest" } + "schema": { + "$ref": "#/components/schemas/RatingRequest" + } } } }, @@ -456,20 +793,39 @@ "description": "Rating submitted", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/RatingResponse" } + "schema": { + "$ref": "#/components/schemas/RatingResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "409": { + "description": "Duplicate rating", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "409": { "description": "Duplicate rating", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, - "429": { "$ref": "#/components/responses/RateLimited" } + "429": { + "$ref": "#/components/responses/RateLimited" + } } } }, "/v1/stake": { "post": { - "tags": ["staking"], + "tags": [ + "staking" + ], "summary": "Register a creator stake", "description": "Registers a creator-to-agent USDC stake after validating two indexed transfers: the creator's stake transfer to the agent wallet and a separate 1% DJD fee transfer to `PAY_TO`. This route does not use x402 pricing; the fee is validated directly from on-chain USDC transfers.", "operationId": "submitCreatorStake", @@ -477,7 +833,9 @@ "required": true, "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/StakeRequest" } + "schema": { + "$ref": "#/components/schemas/StakeRequest" + } } } }, @@ -486,16 +844,22 @@ "description": "Creator stake registered", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/StakeResponse" } + "schema": { + "$ref": "#/components/schemas/StakeResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, "409": { "description": "Duplicate or blocked stake", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/Error" } + "schema": { + "$ref": "#/components/schemas/Error" + } } } } @@ -504,183 +868,314 @@ }, "/v1/data/fraud/blacklist": { "get": { - "tags": ["data"], + "tags": [ + "data" + ], "summary": "Fraud report check", "description": "Check whether a wallet has active fraud reports filed against it. Returns the count and most recent reports. **Requires x402 payment of $0.05 USDC on Base.**", "operationId": "getBlacklist", "parameters": [ - { "$ref": "#/components/parameters/wallet" } + { + "$ref": "#/components/parameters/wallet" + } + ], + "security": [ + { + "x402": [] + } ], - "security": [{ "x402": [] }], - "x-x402": { "price": "$0.05", "network": "base" }, + "x-x402": { + "price": "$0.05", + "network": "base" + }, "responses": { "200": { "description": "Blacklist status returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/BlacklistResponse" } + "schema": { + "$ref": "#/components/schemas/BlacklistResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "429": { "$ref": "#/components/responses/RateLimited" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } } } }, "/v1/data/ratings": { "get": { - "tags": ["data"], + "tags": [ + "data" + ], "summary": "Counterparty ratings", "description": "Returns aggregate mutual-rating sentiment and recent transaction-backed ratings for a wallet. **Requires x402 payment of $0.10 USDC on Base.**", "operationId": "getRatings", "parameters": [ - { "$ref": "#/components/parameters/wallet" }, + { + "$ref": "#/components/parameters/wallet" + }, { "name": "limit", "in": "query", "required": false, "description": "Maximum ratings to return (default: 25, max: 100)", - "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 25 + } } ], - "security": [{ "x402": [] }], - "x-x402": { "price": "$0.10", "network": "base" }, + "security": [ + { + "x402": [] + } + ], + "x-x402": { + "price": "$0.10", + "network": "base" + }, "responses": { "200": { "description": "Ratings data returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/DataRatingsResponse" } + "schema": { + "$ref": "#/components/schemas/DataRatingsResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "404": { "$ref": "#/components/responses/NotFound" }, - "429": { "$ref": "#/components/responses/RateLimited" } - } - } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } + } + } }, "/v1/data/intent": { "get": { - "tags": ["data"], + "tags": [ + "data" + ], "summary": "Transaction intent conversion data", "description": "Returns intent-conversion data for a wallet based on paid lookups and whether they were followed by on-chain deals. **Requires x402 payment of $0.25 USDC on Base.**", "operationId": "getIntentSignals", "parameters": [ - { "$ref": "#/components/parameters/wallet" }, + { + "$ref": "#/components/parameters/wallet" + }, { "name": "limit", "in": "query", "required": false, "description": "Maximum intent records to return (default: 25, max: 100)", - "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 25 + } + } + ], + "security": [ + { + "x402": [] } ], - "security": [{ "x402": [] }], - "x-x402": { "price": "$0.25", "network": "base" }, + "x-x402": { + "price": "$0.25", + "network": "base" + }, "responses": { "200": { "description": "Intent conversion data returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/DataIntentResponse" } + "schema": { + "$ref": "#/components/schemas/DataIntentResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "404": { "$ref": "#/components/responses/NotFound" }, - "429": { "$ref": "#/components/responses/RateLimited" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } } } }, "/v1/data/decay": { "get": { - "tags": ["data"], + "tags": [ + "data" + ], "summary": "Score decay curve", "description": "Returns the historical score decay curve for a wallet using hourly score snapshots, plus trend and trajectory analysis. **Requires x402 payment of $0.15 USDC on Base.**", "operationId": "getScoreDecay", "parameters": [ - { "$ref": "#/components/parameters/wallet" }, + { + "$ref": "#/components/parameters/wallet" + }, { "name": "limit", "in": "query", "required": false, "description": "Maximum decay points to return (default: 50, max: 100)", - "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 } + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 50 + } }, { "name": "after", "in": "query", "required": false, "description": "Inclusive ISO 8601 lower bound for `recorded_at`.", - "schema": { "type": "string", "format": "date-time" } + "schema": { + "type": "string", + "format": "date-time" + } }, { "name": "before", "in": "query", "required": false, "description": "Inclusive ISO 8601 upper bound for `recorded_at`.", - "schema": { "type": "string", "format": "date-time" } + "schema": { + "type": "string", + "format": "date-time" + } } ], - "security": [{ "x402": [] }], - "x-x402": { "price": "$0.15", "network": "base" }, + "security": [ + { + "x402": [] + } + ], + "x-x402": { + "price": "$0.15", + "network": "base" + }, "responses": { "200": { "description": "Score decay data returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/DataDecayResponse" } + "schema": { + "$ref": "#/components/schemas/DataDecayResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "404": { "$ref": "#/components/responses/NotFound" }, - "429": { "$ref": "#/components/responses/RateLimited" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } } } }, "/v1/data/graph": { "get": { - "tags": ["data"], + "tags": [ + "data" + ], "summary": "Relationship graph", "description": "Returns directional relationship-graph intelligence for a wallet, including top counterparties and aggregate flow totals. **Requires x402 payment of $0.20 USDC on Base.**", "operationId": "getRelationshipGraph", "parameters": [ - { "$ref": "#/components/parameters/wallet" }, + { + "$ref": "#/components/parameters/wallet" + }, { "name": "limit", "in": "query", "required": false, "description": "Maximum counterparties to return (default: 25, max: 100)", - "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 25 + } + } + ], + "security": [ + { + "x402": [] } ], - "security": [{ "x402": [] }], - "x-x402": { "price": "$0.20", "network": "base" }, + "x-x402": { + "price": "$0.20", + "network": "base" + }, "responses": { "200": { "description": "Relationship graph data returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/DataGraphResponse" } + "schema": { + "$ref": "#/components/schemas/DataGraphResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "404": { "$ref": "#/components/responses/NotFound" }, - "429": { "$ref": "#/components/responses/RateLimited" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } } } }, "/v1/leaderboard": { "get": { - "tags": ["data"], + "tags": [ + "data" + ], "summary": "Leaderboard", "description": "Returns the top 50 wallets by composite score. Registered wallets are sorted first. Free — no payment required.", "operationId": "getLeaderboard", @@ -689,7 +1184,9 @@ "description": "Leaderboard returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/LeaderboardResponse" } + "schema": { + "$ref": "#/components/schemas/LeaderboardResponse" + } } } } @@ -698,7 +1195,9 @@ }, "/v1/badge/{wallet}.svg": { "get": { - "tags": ["data"], + "tags": [ + "data" + ], "summary": "SVG score badge", "description": "Returns an embeddable shields.io-style SVG badge showing the wallet's current score and tier. Cached for 10 minutes. Free — no payment required.", "operationId": "getBadge", @@ -708,43 +1207,71 @@ "in": "path", "required": true, "description": "Full 40-character hex wallet address (without .svg extension)", - "schema": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$" } + "schema": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{40}$" + } } ], "responses": { "200": { "description": "SVG badge image", - "content": { "image/svg+xml": { "schema": { "type": "string" } } } + "content": { + "image/svg+xml": { + "schema": { + "type": "string" + } + } + } }, - "400": { "$ref": "#/components/responses/BadRequest" } + "400": { + "$ref": "#/components/responses/BadRequest" + } } } }, "/health": { "get": { - "tags": ["system"], + "tags": [ + "system" + ], "summary": "Health check", "description": "Returns public service health for any caller, and includes runtime/database/indexer/job detail when called with a valid admin key. Release metadata is exposed when the deploy image is stamped with build identifiers.", "operationId": "getHealth", "responses": { - "200": { "description": "System healthy" } + "200": { + "description": "System healthy" + } } } }, "/docs": { "get": { - "tags": ["system"], + "tags": [ + "system" + ], "summary": "Interactive API documentation", "description": "Serves a Swagger UI interface for exploring and testing the API interactively. Free — no payment required.", "operationId": "getDocs", "responses": { - "200": { "description": "Swagger UI HTML page", "content": { "text/html": { "schema": { "type": "string" } } } } + "200": { + "description": "Swagger UI HTML page", + "content": { + "text/html": { + "schema": { + "type": "string" + } + } + } + } } } }, "/v1/data/economy": { "get": { - "tags": ["data"], + "tags": [ + "data" + ], "summary": "Ecosystem economy metrics", "description": "Returns aggregated ecosystem health metrics: unique wallets scored, total queries, revenue, average scores, and more. Free — no payment required.", "operationId": "getEconomyMetrics", @@ -754,14 +1281,27 @@ "in": "query", "required": false, "description": "Aggregation period (default: daily)", - "schema": { "type": "string", "enum": ["daily", "weekly", "monthly"], "default": "daily" } + "schema": { + "type": "string", + "enum": [ + "daily", + "weekly", + "monthly" + ], + "default": "daily" + } }, { "name": "limit", "in": "query", "required": false, "description": "Number of periods to return (max 90, default 30)", - "schema": { "type": "integer", "minimum": 1, "maximum": 90, "default": 30 } + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 90, + "default": 30 + } } ], "responses": { @@ -772,21 +1312,34 @@ "schema": { "type": "object", "properties": { - "period": { "type": "string" }, - "count": { "type": "integer" }, - "metrics": { "type": "array", "items": { "type": "object" } } + "period": { + "type": "string" + }, + "count": { + "type": "integer" + }, + "metrics": { + "type": "array", + "items": { + "type": "object" + } + } } } } } }, - "400": { "$ref": "#/components/responses/BadRequest" } + "400": { + "$ref": "#/components/responses/BadRequest" + } } } }, "/v1/data/economy/survival": { "get": { - "tags": ["data"], + "tags": [ + "data" + ], "summary": "Economy survival analytics", "description": "Returns cohort survival, 7/30/90-day retention, tier-level survival, and an at-risk wallet board derived from wallet activity and recent score decay. **Requires x402 payment of $0.15 USDC on Base or Bearer API key.**", "operationId": "getEconomySurvival", @@ -796,28 +1349,48 @@ "in": "query", "required": false, "description": "Max at-risk wallets to return (default 20, max 100)", - "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 20 + } + } + ], + "security": [ + { + "x402": [] } ], - "security": [{ "x402": [] }], - "x-x402": { "price": "$0.15", "network": "base" }, + "x-x402": { + "price": "$0.15", + "network": "base" + }, "responses": { "200": { "description": "Economy survival returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/EconomySurvivalResponse" } + "schema": { + "$ref": "#/components/schemas/EconomySurvivalResponse" + } } } }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "429": { "$ref": "#/components/responses/RateLimited" } + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } } } }, "/v1/data/economy/summary": { "get": { - "tags": ["data"], + "tags": [ + "data" + ], "summary": "Economy summary by period", "description": "Returns aggregated ecosystem summary metrics by period from `economy_metrics`. **Requires x402 payment of $0.10 USDC on Base or Bearer API key.**", "operationId": "getEconomySummary", @@ -827,36 +1400,66 @@ "in": "query", "required": false, "description": "Aggregation period (default: daily)", - "schema": { "type": "string", "enum": ["daily", "weekly", "monthly"], "default": "daily" } + "schema": { + "type": "string", + "enum": [ + "daily", + "weekly", + "monthly" + ], + "default": "daily" + } }, { "name": "limit", "in": "query", "required": false, "description": "Number of periods to return (max 90, default 30)", - "schema": { "type": "integer", "minimum": 1, "maximum": 90, "default": 30 } + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 90, + "default": 30 + } + } + ], + "security": [ + { + "x402": [] } ], - "security": [{ "x402": [] }], - "x-x402": { "price": "$0.10", "network": "base" }, + "x-x402": { + "price": "$0.10", + "network": "base" + }, "responses": { "200": { "description": "Economy summary returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/EconomySummaryResponse" } + "schema": { + "$ref": "#/components/schemas/EconomySummaryResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "429": { "$ref": "#/components/responses/RateLimited" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } } } }, "/v1/data/economy/volume": { "get": { - "tags": ["data"], + "tags": [ + "data" + ], "summary": "Economy volume by period", "description": "Returns transaction count and USDC flow time series by period from `economy_metrics`. **Requires x402 payment of $0.10 USDC on Base or Bearer API key.**", "operationId": "getEconomyVolume", @@ -866,52 +1469,94 @@ "in": "query", "required": false, "description": "Aggregation period (default: daily)", - "schema": { "type": "string", "enum": ["daily", "weekly", "monthly"], "default": "daily" } + "schema": { + "type": "string", + "enum": [ + "daily", + "weekly", + "monthly" + ], + "default": "daily" + } }, { "name": "limit", "in": "query", "required": false, "description": "Number of periods to return (max 90, default 30)", - "schema": { "type": "integer", "minimum": 1, "maximum": 90, "default": 30 } + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 90, + "default": 30 + } + } + ], + "security": [ + { + "x402": [] } ], - "security": [{ "x402": [] }], - "x-x402": { "price": "$0.10", "network": "base" }, + "x-x402": { + "price": "$0.10", + "network": "base" + }, "responses": { "200": { "description": "Economy volume returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/EconomyVolumeResponse" } + "schema": { + "$ref": "#/components/schemas/EconomyVolumeResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "429": { "$ref": "#/components/responses/RateLimited" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } } } }, "/v1/score/batch": { "post": { - "tags": ["score"], + "tags": [ + "score" + ], "summary": "Batch score lookup", "description": "Score up to 20 wallets in a single request. Returns basic score data for each wallet. **Requires x402 payment of $0.50 USDC on Base.**", "operationId": "batchScore", - "security": [{ "x402": [] }], - "x-x402": { "price": "$0.50", "network": "base" }, + "security": [ + { + "x402": [] + } + ], + "x-x402": { + "price": "$0.50", + "network": "base" + }, "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", - "required": ["wallets"], + "required": [ + "wallets" + ], "properties": { "wallets": { "type": "array", - "items": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$" }, + "items": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{40}$" + }, "minItems": 2, "maxItems": 20, "description": "Array of wallet addresses to score" @@ -935,53 +1580,101 @@ "schema": { "type": "object", "properties": { - "results": { "type": "array", "items": { "$ref": "#/components/schemas/BasicScoreResponse" } }, - "count": { "type": "integer" } + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BasicScoreResponse" + } + }, + "count": { + "type": "integer" + } } } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "429": { "$ref": "#/components/responses/RateLimited" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } } } }, "/metrics": { "get": { - "tags": ["system"], + "tags": [ + "system" + ], "summary": "Prometheus metrics", "description": "Returns Prometheus-compatible metrics in text exposition format. Includes HTTP request counters, database gauges, and process metrics. Free — no payment required.", "operationId": "getMetrics", "responses": { "200": { "description": "Prometheus text metrics", - "content": { "text/plain": { "schema": { "type": "string" } } } + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } } } } }, - "/admin/api-keys": { "post": { - "tags": ["api-keys"], + "tags": [ + "api-keys" + ], "summary": "Create an API key", "description": "Creates a new API key for a wallet. The raw key is returned only once on creation — store it securely. **Requires admin authentication (X-ADMIN-KEY header).**", "operationId": "createApiKey", - "security": [{ "adminKey": [] }], + "security": [ + { + "adminKey": [] + } + ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", - "required": ["wallet"], + "required": [ + "wallet" + ], "properties": { - "wallet": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$", "description": "Wallet address to associate with the key" }, - "name": { "type": "string", "nullable": true, "description": "Optional human-readable name for the key" }, - "tier": { "type": "string", "enum": ["standard", "premium"], "default": "standard", "description": "API key tier" }, - "monthly_limit": { "type": "integer", "default": 10000, "description": "Monthly request limit" } + "wallet": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{40}$", + "description": "Wallet address to associate with the key" + }, + "name": { + "type": "string", + "nullable": true, + "description": "Optional human-readable name for the key" + }, + "tier": { + "type": "string", + "enum": [ + "standard", + "premium" + ], + "default": "standard", + "description": "API key tier" + }, + "monthly_limit": { + "type": "integer", + "default": 10000, + "description": "Monthly request limit" + } } }, "example": { @@ -998,20 +1691,32 @@ "description": "API key created", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ApiKeyCreatedResponse" } + "schema": { + "$ref": "#/components/schemas/ApiKeyCreatedResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "401": { "$ref": "#/components/responses/Unauthorized" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + } } }, "get": { - "tags": ["api-keys"], + "tags": [ + "api-keys" + ], "summary": "List all API keys", "description": "Returns all API keys with usage stats. Raw keys are never returned — only prefixes. **Requires admin authentication (X-ADMIN-KEY header).**", "operationId": "listApiKeys", - "security": [{ "adminKey": [] }], + "security": [ + { + "adminKey": [] + } + ], "responses": { "200": { "description": "List of API keys", @@ -1022,32 +1727,46 @@ "properties": { "keys": { "type": "array", - "items": { "$ref": "#/components/schemas/ApiKeyListItem" } + "items": { + "$ref": "#/components/schemas/ApiKeyListItem" + } }, - "count": { "type": "integer" } + "count": { + "type": "integer" + } } } } } }, - "401": { "$ref": "#/components/responses/Unauthorized" } + "401": { + "$ref": "#/components/responses/Unauthorized" + } } } }, "/admin/api-keys/{id}": { "delete": { - "tags": ["api-keys"], + "tags": [ + "api-keys" + ], "summary": "Revoke an API key", "description": "Soft-deletes an API key by marking it as revoked. The key will no longer authenticate requests. **Requires admin authentication (X-ADMIN-KEY header).**", "operationId": "revokeApiKey", - "security": [{ "adminKey": [] }], + "security": [ + { + "adminKey": [] + } + ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "API key ID", - "schema": { "type": "integer" } + "schema": { + "type": "integer" + } } ], "responses": { @@ -1058,50 +1777,119 @@ "schema": { "type": "object", "properties": { - "success": { "type": "boolean", "example": true }, - "message": { "type": "string", "example": "API key revoked" } + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "API key revoked" + } } } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "401": { "$ref": "#/components/responses/Unauthorized" }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, "404": { "description": "API key not found or already revoked", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } } } } }, - "/admin/webhooks": { "post": { - "tags": ["webhooks"], + "tags": [ + "webhooks" + ], "summary": "Create webhook (admin)", "description": "Registers a new wallet-scoped webhook subscription. Provide either `events`, `preset`, or `presets`. If `score.threshold` is selected without an explicit `threshold_score`, the service defaults to 60. Use `forensics_filter` to restrict DJD Forensics alerts by minimum resulting risk level and/or report reasons. Managed presets also include anomaly-driven monitoring for score drops, balance freefalls, and new Sybil flags. The webhook secret is returned only once, so store it securely to verify signatures. **Requires admin authentication (X-ADMIN-KEY header).**", "operationId": "createAdminWebhook", - "security": [{ "adminKey": [] }], + "security": [ + { + "adminKey": [] + } + ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", - "required": ["wallet", "url"], + "required": [ + "wallet", + "url" + ], "properties": { - "wallet": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$", "description": "Wallet to monitor" }, - "url": { "type": "string", "format": "uri", "description": "HTTPS endpoint to receive webhook payloads" }, + "wallet": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{40}$", + "description": "Wallet to monitor" + }, + "url": { + "type": "string", + "format": "uri", + "description": "HTTPS endpoint to receive webhook payloads" + }, "events": { "type": "array", - "items": { "type": "string", "enum": ["score.updated", "score.expired", "fraud.reported", "fraud.disputed", "fraud.dispute.resolved", "forensics.risk.changed", "forensics.watchlist.entered", "forensics.watchlist.cleared", "anomaly.score_drop", "anomaly.score_spike", "anomaly.balance_freefall", "anomaly.sybil_flagged", "agent.registered", "score.threshold"] }, + "items": { + "type": "string", + "enum": [ + "score.updated", + "score.expired", + "fraud.reported", + "fraud.disputed", + "fraud.dispute.resolved", + "forensics.risk.changed", + "forensics.watchlist.entered", + "forensics.watchlist.cleared", + "anomaly.score_drop", + "anomaly.score_spike", + "anomaly.balance_freefall", + "anomaly.sybil_flagged", + "agent.registered", + "score.threshold" + ] + }, "minItems": 1, "description": "Explicit events to subscribe to. May be combined with presets." }, - "preset": { "type": "string", "enum": ["score_monitoring", "forensics_monitoring", "forensics_disputes", "forensics_watchlist", "anomaly_monitoring"], "description": "Optional preset that expands to a managed event set" }, + "preset": { + "type": "string", + "enum": [ + "score_monitoring", + "forensics_monitoring", + "forensics_disputes", + "forensics_watchlist", + "anomaly_monitoring" + ], + "description": "Optional preset that expands to a managed event set" + }, "presets": { "type": "array", - "items": { "type": "string", "enum": ["score_monitoring", "forensics_monitoring", "forensics_disputes", "forensics_watchlist", "anomaly_monitoring"] }, + "items": { + "type": "string", + "enum": [ + "score_monitoring", + "forensics_monitoring", + "forensics_disputes", + "forensics_watchlist", + "anomaly_monitoring" + ] + }, "minItems": 1, "description": "Optional preset bundle list. May be combined with explicit events." }, @@ -1114,7 +1902,15 @@ "forensics_filter": { "$ref": "#/components/schemas/WebhookForensicsFilter" }, - "tier": { "type": "string", "enum": ["basic", "premium"], "default": "basic", "description": "Webhook tier" } + "tier": { + "type": "string", + "enum": [ + "basic", + "premium" + ], + "default": "basic", + "description": "Webhook tier" + } } }, "example": { @@ -1123,7 +1919,9 @@ "preset": "forensics_monitoring", "forensics_filter": { "minimum_risk_level": "elevated", - "reasons": ["payment_fraud"] + "reasons": [ + "payment_fraud" + ] }, "tier": "basic" } @@ -1135,20 +1933,32 @@ "description": "Webhook created", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/WebhookCreatedResponse" } + "schema": { + "$ref": "#/components/schemas/WebhookCreatedResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "401": { "$ref": "#/components/responses/Unauthorized" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + } } }, "get": { - "tags": ["webhooks"], + "tags": [ + "webhooks" + ], "summary": "List webhooks (admin)", "description": "Returns all registered webhooks with delivery stats. **Requires admin authentication (X-ADMIN-KEY header).**", "operationId": "listAdminWebhooks", - "security": [{ "adminKey": [] }], + "security": [ + { + "adminKey": [] + } + ], "responses": { "200": { "description": "List of webhooks", @@ -1159,32 +1969,46 @@ "properties": { "webhooks": { "type": "array", - "items": { "$ref": "#/components/schemas/WebhookListItem" } + "items": { + "$ref": "#/components/schemas/WebhookListItem" + } }, - "count": { "type": "integer" } + "count": { + "type": "integer" + } } } } } }, - "401": { "$ref": "#/components/responses/Unauthorized" } + "401": { + "$ref": "#/components/responses/Unauthorized" + } } } }, "/admin/webhooks/{id}": { "get": { - "tags": ["webhooks"], + "tags": [ + "webhooks" + ], "summary": "Get webhook detail (admin)", "description": "Returns one webhook plus recent delivery attempts. **Requires admin authentication (X-ADMIN-KEY header).**", "operationId": "getAdminWebhook", - "security": [{ "adminKey": [] }], + "security": [ + { + "adminKey": [] + } + ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Webhook ID", - "schema": { "type": "integer" } + "schema": { + "type": "integer" + } } ], "responses": { @@ -1192,30 +2016,48 @@ "description": "Webhook detail", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/WebhookDetailResponse" } + "schema": { + "$ref": "#/components/schemas/WebhookDetailResponse" + } } } }, - "401": { "$ref": "#/components/responses/Unauthorized" }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, "404": { "description": "Webhook not found", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } } } }, "delete": { - "tags": ["webhooks"], + "tags": [ + "webhooks" + ], "summary": "Delete webhook (admin)", "description": "Deactivates a webhook subscription. **Requires admin authentication (X-ADMIN-KEY header).**", "operationId": "deleteAdminWebhook", - "security": [{ "adminKey": [] }], + "security": [ + { + "adminKey": [] + } + ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Webhook ID", - "schema": { "type": "integer" } + "schema": { + "type": "integer" + } } ], "responses": { @@ -1226,35 +2068,57 @@ "schema": { "type": "object", "properties": { - "success": { "type": "boolean", "example": true }, - "message": { "type": "string", "example": "Webhook deactivated" } + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "Webhook deactivated" + } } } } } }, - "401": { "$ref": "#/components/responses/Unauthorized" }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, "404": { "description": "Webhook not found or already disabled", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } } } } }, "/admin/webhooks/{id}/test": { "post": { - "tags": ["webhooks"], + "tags": [ + "webhooks" + ], "summary": "Test webhook (admin)", "description": "Sends a test event payload to the webhook endpoint to verify connectivity. **Requires admin authentication (X-ADMIN-KEY header).**", "operationId": "testAdminWebhook", - "security": [{ "adminKey": [] }], - "parameters": [ + "security": [ + { + "adminKey": [] + } + ], + "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Webhook ID", - "schema": { "type": "integer" } + "schema": { + "type": "integer" + } } ], "responses": { @@ -1265,27 +2129,47 @@ "schema": { "type": "object", "properties": { - "success": { "type": "boolean", "description": "Whether the test delivery returned a 2xx status" }, - "status_code": { "type": "integer", "nullable": true, "description": "HTTP status code from the webhook endpoint" }, - "message": { "type": "string" } + "success": { + "type": "boolean", + "description": "Whether the test delivery returned a 2xx status" + }, + "status_code": { + "type": "integer", + "nullable": true, + "description": "HTTP status code from the webhook endpoint" + }, + "message": { + "type": "string" + } } } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "401": { "$ref": "#/components/responses/Unauthorized" }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, "404": { "description": "Webhook not found", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } } } } }, - "/v1/webhooks/presets": { "get": { - "tags": ["webhooks"], + "tags": [ + "webhooks" + ], "summary": "List webhook presets", "description": "Returns the preset catalog for managed score and DJD Forensics monitoring bundles.", "operationId": "listWebhookPresets", @@ -1299,9 +2183,13 @@ "properties": { "presets": { "type": "array", - "items": { "$ref": "#/components/schemas/WebhookPreset" } + "items": { + "$ref": "#/components/schemas/WebhookPreset" + } }, - "count": { "type": "integer" } + "count": { + "type": "integer" + } } } } @@ -1312,30 +2200,79 @@ }, "/v1/webhooks": { "post": { - "tags": ["webhooks"], + "tags": [ + "webhooks" + ], "summary": "Create public webhook", "description": "Self-service wallet-scoped webhook registration for API key holders. Provide either `events`, `preset`, or `presets`. If `score.threshold` is selected without an explicit `threshold_score`, the service defaults to 60. Use `forensics_filter` to restrict DJD Forensics alerts by minimum resulting risk level and/or report reasons. Managed presets also include anomaly-driven monitoring for score drops, balance freefalls, and new Sybil flags. Limited to 10 active webhooks per wallet. **Requires Bearer API key authentication.**", "operationId": "createPublicWebhook", - "security": [{ "bearerApiKey": [] }], + "security": [ + { + "bearerApiKey": [] + } + ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", - "required": ["url"], + "required": [ + "url" + ], "properties": { - "url": { "type": "string", "format": "uri", "description": "HTTPS endpoint to receive webhook payloads" }, + "url": { + "type": "string", + "format": "uri", + "description": "HTTPS endpoint to receive webhook payloads" + }, "events": { "type": "array", - "items": { "type": "string", "enum": ["score.updated", "score.expired", "fraud.reported", "fraud.disputed", "fraud.dispute.resolved", "forensics.risk.changed", "forensics.watchlist.entered", "forensics.watchlist.cleared", "anomaly.score_drop", "anomaly.score_spike", "anomaly.balance_freefall", "anomaly.sybil_flagged", "agent.registered", "score.threshold"] }, + "items": { + "type": "string", + "enum": [ + "score.updated", + "score.expired", + "fraud.reported", + "fraud.disputed", + "fraud.dispute.resolved", + "forensics.risk.changed", + "forensics.watchlist.entered", + "forensics.watchlist.cleared", + "anomaly.score_drop", + "anomaly.score_spike", + "anomaly.balance_freefall", + "anomaly.sybil_flagged", + "agent.registered", + "score.threshold" + ] + }, "minItems": 1, "description": "Explicit events to subscribe to. May be combined with presets." }, - "preset": { "type": "string", "enum": ["score_monitoring", "forensics_monitoring", "forensics_disputes", "forensics_watchlist", "anomaly_monitoring"], "description": "Optional preset that expands to a managed event set" }, + "preset": { + "type": "string", + "enum": [ + "score_monitoring", + "forensics_monitoring", + "forensics_disputes", + "forensics_watchlist", + "anomaly_monitoring" + ], + "description": "Optional preset that expands to a managed event set" + }, "presets": { "type": "array", - "items": { "type": "string", "enum": ["score_monitoring", "forensics_monitoring", "forensics_disputes", "forensics_watchlist", "anomaly_monitoring"] }, + "items": { + "type": "string", + "enum": [ + "score_monitoring", + "forensics_monitoring", + "forensics_disputes", + "forensics_watchlist", + "anomaly_monitoring" + ] + }, "minItems": 1, "description": "Optional preset bundle list. May be combined with explicit events." }, @@ -1355,7 +2292,10 @@ "preset": "forensics_monitoring", "forensics_filter": { "minimum_risk_level": "watch", - "reasons": ["payment_fraud", "impersonation"] + "reasons": [ + "payment_fraud", + "impersonation" + ] } } } @@ -1366,24 +2306,42 @@ "description": "Webhook created", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/PublicWebhookCreatedResponse" } + "schema": { + "$ref": "#/components/schemas/PublicWebhookCreatedResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "401": { "$ref": "#/components/responses/Unauthorized" }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, "429": { "description": "Maximum webhooks per wallet exceeded", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } } } }, "get": { - "tags": ["webhooks"], + "tags": [ + "webhooks" + ], "summary": "List public webhooks", "description": "Lists all webhooks belonging to the authenticated wallet. **Requires Bearer API key authentication.**", "operationId": "listPublicWebhooks", - "security": [{ "bearerApiKey": [] }], + "security": [ + { + "bearerApiKey": [] + } + ], "responses": { "200": { "description": "Webhook list for the authenticated wallet", @@ -1394,32 +2352,46 @@ "properties": { "webhooks": { "type": "array", - "items": { "$ref": "#/components/schemas/PublicWebhookListItem" } + "items": { + "$ref": "#/components/schemas/PublicWebhookListItem" + } }, - "count": { "type": "integer" } + "count": { + "type": "integer" + } } } } } }, - "401": { "$ref": "#/components/responses/Unauthorized" } + "401": { + "$ref": "#/components/responses/Unauthorized" + } } } }, "/v1/webhooks/{id}": { "delete": { - "tags": ["webhooks"], + "tags": [ + "webhooks" + ], "summary": "Delete public webhook", "description": "Deactivates one webhook belonging to the authenticated wallet. **Requires Bearer API key authentication.**", "operationId": "deletePublicWebhook", - "security": [{ "bearerApiKey": [] }], + "security": [ + { + "bearerApiKey": [] + } + ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Webhook ID", - "schema": { "type": "integer" } + "schema": { + "type": "integer" + } } ], "responses": { @@ -1430,23 +2402,36 @@ "schema": { "type": "object", "properties": { - "success": { "type": "boolean", "example": true } + "success": { + "type": "boolean", + "example": true + } } } } } }, - "401": { "$ref": "#/components/responses/Unauthorized" }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, "404": { "description": "Webhook not found or already disabled", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } } } } }, "/v1/monitor/presets": { "get": { - "tags": ["monitoring"], + "tags": [ + "monitoring" + ], "summary": "List monitoring policy presets", "description": "Returns the managed score and DJD Forensics monitoring policies available for `/v1/monitor` subscriptions.", "operationId": "listMonitoringPresets", @@ -1460,9 +2445,13 @@ "properties": { "presets": { "type": "array", - "items": { "$ref": "#/components/schemas/MonitoringPreset" } + "items": { + "$ref": "#/components/schemas/MonitoringPreset" + } }, - "count": { "type": "integer" } + "count": { + "type": "integer" + } } } } @@ -1473,18 +2462,27 @@ }, "/v1/monitor": { "post": { - "tags": ["monitoring"], + "tags": [ + "monitoring" + ], "summary": "Create monitoring subscription", "description": "Creates a managed monitoring subscription for a target wallet and provisions the underlying webhook delivery automatically. Policies can target any wallet; delivery is sent to your HTTPS endpoint. `score_monitoring` supports `threshold_score`. Forensics policies support `forensics_filter`. `anomaly_monitoring` delivers score-drop, score-spike, balance-freefall, and sybil-flag alerts. **Requires Bearer API key authentication.**", "operationId": "createMonitoringSubscription", - "security": [{ "bearerApiKey": [] }], + "security": [ + { + "bearerApiKey": [] + } + ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", - "required": ["url", "policy_type"], + "required": [ + "url", + "policy_type" + ], "properties": { "target_wallet": { "type": "string", @@ -1493,7 +2491,13 @@ }, "policy_type": { "type": "string", - "enum": ["score_monitoring", "forensics_monitoring", "forensics_disputes", "forensics_watchlist", "anomaly_monitoring"], + "enum": [ + "score_monitoring", + "forensics_monitoring", + "forensics_disputes", + "forensics_watchlist", + "anomaly_monitoring" + ], "description": "Managed monitoring policy to provision." }, "url": { @@ -1518,7 +2522,9 @@ "url": "https://example.com/monitoring/djd", "forensics_filter": { "minimum_risk_level": "elevated", - "reasons": ["payment_fraud"] + "reasons": [ + "payment_fraud" + ] } } } @@ -1529,24 +2535,42 @@ "description": "Monitoring subscription created", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/MonitoringSubscriptionCreatedResponse" } + "schema": { + "$ref": "#/components/schemas/MonitoringSubscriptionCreatedResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "401": { "$ref": "#/components/responses/Unauthorized" }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, "429": { "description": "Maximum monitoring subscriptions per subscriber exceeded", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } } } }, "get": { - "tags": ["monitoring"], + "tags": [ + "monitoring" + ], "summary": "List monitoring subscriptions", "description": "Lists all managed monitoring subscriptions owned by the authenticated API-key subscriber. **Requires Bearer API key authentication.**", "operationId": "listMonitoringSubscriptions", - "security": [{ "bearerApiKey": [] }], + "security": [ + { + "bearerApiKey": [] + } + ], "responses": { "200": { "description": "Monitoring subscription list", @@ -1557,32 +2581,47 @@ "properties": { "subscriptions": { "type": "array", - "items": { "$ref": "#/components/schemas/MonitoringSubscription" } + "items": { + "$ref": "#/components/schemas/MonitoringSubscription" + } }, - "count": { "type": "integer" } + "count": { + "type": "integer" + } } } } } }, - "401": { "$ref": "#/components/responses/Unauthorized" } + "401": { + "$ref": "#/components/responses/Unauthorized" + } } } }, "/v1/monitor/{id}": { "delete": { - "tags": ["monitoring"], + "tags": [ + "monitoring" + ], "summary": "Delete monitoring subscription", "description": "Deactivates a managed monitoring subscription and its underlying webhook delivery configuration. **Requires Bearer API key authentication.**", "operationId": "deleteMonitoringSubscription", - "security": [{ "bearerApiKey": [] }], + "security": [ + { + "bearerApiKey": [] + } + ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Monitoring subscription ID", - "schema": { "type": "string", "format": "uuid" } + "schema": { + "type": "string", + "format": "uuid" + } } ], "responses": { @@ -1593,51 +2632,86 @@ "schema": { "type": "object", "properties": { - "success": { "type": "boolean", "example": true } + "success": { + "type": "boolean", + "example": true + } } } } } }, - "401": { "$ref": "#/components/responses/Unauthorized" }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, "404": { "description": "Monitoring subscription not found or already disabled", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } } } } }, - "/v1/score/history": { "get": { - "tags": ["history"], + "tags": [ + "history" + ], "summary": "Historical score data", "description": "Returns paginated score history with trend analysis for a wallet. Supports date-range filtering and configurable result limits. **Requires x402 payment of $0.15 USDC on Base or Bearer API key.**", "operationId": "getScoreHistory", - "security": [{ "x402": [] }, { "bearerApiKey": [] }], - "x-x402": { "price": "$0.15", "network": "base" }, + "security": [ + { + "x402": [] + }, + { + "bearerApiKey": [] + } + ], + "x-x402": { + "price": "$0.15", + "network": "base" + }, "parameters": [ - { "$ref": "#/components/parameters/wallet" }, + { + "$ref": "#/components/parameters/wallet" + }, { "name": "limit", "in": "query", "required": false, "description": "Number of records to return (1-100, default 50)", - "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 } + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 50 + } }, { "name": "after", "in": "query", "required": false, "description": "Return records after this ISO 8601 date (YYYY-MM-DD)", - "schema": { "type": "string", "format": "date" } + "schema": { + "type": "string", + "format": "date" + } }, { "name": "before", "in": "query", "required": false, "description": "Return records before this ISO 8601 date (YYYY-MM-DD)", - "schema": { "type": "string", "format": "date" } + "schema": { + "type": "string", + "format": "date" + } } ], "responses": { @@ -1645,60 +2719,106 @@ "description": "Score history returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ScoreHistoryResponse" } + "schema": { + "$ref": "#/components/schemas/ScoreHistoryResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, "404": { "description": "No score history found for this wallet", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } } } } }, - "/v1/forensics/summary": { "get": { - "tags": ["forensics"], + "tags": [ + "forensics" + ], "summary": "DJD Forensics overview", "description": "Returns a wallet-level forensics summary: active fraud report counts, total penalties, reporter breadth, dispute status, and the most recent incidents. **Requires x402 payment of $0.10 USDC on Base or Bearer API key.**", "operationId": "getForensicsOverview", - "security": [{ "x402": [] }, { "bearerApiKey": [] }], - "x-x402": { "price": "$0.10", "network": "base" }, + "security": [ + { + "x402": [] + }, + { + "bearerApiKey": [] + } + ], + "x-x402": { + "price": "$0.10", + "network": "base" + }, "parameters": [ - { "$ref": "#/components/parameters/wallet" } + { + "$ref": "#/components/parameters/wallet" + } ], "responses": { "200": { "description": "Forensics overview returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ForensicsOverviewResponse" } + "schema": { + "$ref": "#/components/schemas/ForensicsOverviewResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "429": { "$ref": "#/components/responses/RateLimited" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } } } }, - "/v1/forensics/dispute": { "post": { - "tags": ["forensics"], + "tags": [ + "forensics" + ], "summary": "Open a fraud-report dispute", "description": "Allows the reported wallet to open a dispute against a specific fraud report. The disputing wallet is derived from the x402 payment or API key identity and must match the report target wallet. **Requires x402 payment of $0.05 USDC on Base or Bearer API key.**", "operationId": "createForensicsDispute", - "security": [{ "x402": [] }, { "bearerApiKey": [] }], - "x-x402": { "price": "$0.05", "network": "base" }, + "security": [ + { + "x402": [] + }, + { + "bearerApiKey": [] + } + ], + "x-x402": { + "price": "$0.05", + "network": "base" + }, "requestBody": { "required": true, "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/FraudDisputeRequest" } + "schema": { + "$ref": "#/components/schemas/FraudDisputeRequest" + } } } }, @@ -1707,61 +2827,112 @@ "description": "Fraud dispute opened", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/FraudDisputeResponse" } + "schema": { + "$ref": "#/components/schemas/FraudDisputeResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, "404": { "description": "Fraud report not found", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } }, "409": { "description": "Dispute already exists or wallet is not authorized to dispute this report", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } }, - "429": { "$ref": "#/components/responses/RateLimited" } + "429": { + "$ref": "#/components/responses/RateLimited" + } } } }, - "/v1/forensics/feed": { "get": { - "tags": ["forensics"], + "tags": [ + "forensics" + ], "summary": "DJD Forensics incident feed", "description": "Returns recent fraud-report incidents across the corpus. Supports reason and date-range filtering plus configurable result limits. **Requires x402 payment of $0.30 USDC on Base or Bearer API key.**", "operationId": "getForensicsFeed", - "security": [{ "x402": [] }, { "bearerApiKey": [] }], - "x-x402": { "price": "$0.30", "network": "base" }, + "security": [ + { + "x402": [] + }, + { + "bearerApiKey": [] + } + ], + "x-x402": { + "price": "$0.30", + "network": "base" + }, "parameters": [ { "name": "reason", "in": "query", "required": false, "description": "Optional report reason filter", - "schema": { "type": "string", "enum": ["failed_delivery", "payment_fraud", "impersonation", "malicious_behavior", "other"] } + "schema": { + "type": "string", + "enum": [ + "failed_delivery", + "payment_fraud", + "impersonation", + "malicious_behavior", + "other" + ] + } }, { "name": "limit", "in": "query", "required": false, "description": "Number of incidents to return (1-100, default 50)", - "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 } + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 50 + } }, { "name": "after", "in": "query", "required": false, "description": "Return incidents after this ISO 8601 date (YYYY-MM-DD)", - "schema": { "type": "string", "format": "date" } + "schema": { + "type": "string", + "format": "date" + } }, { "name": "before", "in": "query", "required": false, "description": "Return incidents before this ISO 8601 date (YYYY-MM-DD)", - "schema": { "type": "string", "format": "date" } + "schema": { + "type": "string", + "format": "date" + } } ], "responses": { @@ -1769,46 +2940,76 @@ "description": "Forensics incident feed returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ForensicsFeedResponse" } + "schema": { + "$ref": "#/components/schemas/ForensicsFeedResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "429": { "$ref": "#/components/responses/RateLimited" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } } } }, - "/v1/forensics/watchlist": { "get": { - "tags": ["forensics"], + "tags": [ + "forensics" + ], "summary": "DJD Forensics watchlist", "description": "Returns a ranked watchlist of the most-reported wallets across the corpus. Supports date-range filtering and configurable result limits. **Requires x402 payment of $0.25 USDC on Base or Bearer API key.**", "operationId": "getForensicsWatchlist", - "security": [{ "x402": [] }, { "bearerApiKey": [] }], - "x-x402": { "price": "$0.25", "network": "base" }, + "security": [ + { + "x402": [] + }, + { + "bearerApiKey": [] + } + ], + "x-x402": { + "price": "$0.25", + "network": "base" + }, "parameters": [ { "name": "limit", "in": "query", "required": false, "description": "Number of wallets to return (1-100, default 50)", - "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 } + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 50 + } }, { "name": "after", "in": "query", "required": false, "description": "Return incidents after this ISO 8601 date (YYYY-MM-DD)", - "schema": { "type": "string", "format": "date" } + "schema": { + "type": "string", + "format": "date" + } }, { "name": "before", "in": "query", "required": false, "description": "Return incidents before this ISO 8601 date (YYYY-MM-DD)", - "schema": { "type": "string", "format": "date" } + "schema": { + "type": "string", + "format": "date" + } } ], "responses": { @@ -1816,47 +3017,79 @@ "description": "Forensics watchlist returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ForensicsWatchlistResponse" } + "schema": { + "$ref": "#/components/schemas/ForensicsWatchlistResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "429": { "$ref": "#/components/responses/RateLimited" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } } } }, - "/v1/forensics/reports": { "get": { - "tags": ["forensics"], + "tags": [ + "forensics" + ], "summary": "DJD Forensics incident feed", "description": "Returns fraud-report incidents for a wallet with full report details. Supports date-range filtering and configurable result limits. **Requires x402 payment of $0.15 USDC on Base or Bearer API key.**", "operationId": "getForensicsReports", - "security": [{ "x402": [] }, { "bearerApiKey": [] }], - "x-x402": { "price": "$0.15", "network": "base" }, + "security": [ + { + "x402": [] + }, + { + "bearerApiKey": [] + } + ], + "x-x402": { + "price": "$0.15", + "network": "base" + }, "parameters": [ - { "$ref": "#/components/parameters/wallet" }, + { + "$ref": "#/components/parameters/wallet" + }, { "name": "limit", "in": "query", "required": false, "description": "Number of reports to return (1-100, default 50)", - "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 } + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 50 + } }, { "name": "after", "in": "query", "required": false, "description": "Return reports after this ISO 8601 date (YYYY-MM-DD)", - "schema": { "type": "string", "format": "date" } + "schema": { + "type": "string", + "format": "date" + } }, { "name": "before", "in": "query", "required": false, "description": "Return reports before this ISO 8601 date (YYYY-MM-DD)", - "schema": { "type": "string", "format": "date" } + "schema": { + "type": "string", + "format": "date" + } } ], "responses": { @@ -1864,47 +3097,79 @@ "description": "Forensics report feed returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ForensicsReportsResponse" } + "schema": { + "$ref": "#/components/schemas/ForensicsReportsResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, - "429": { "$ref": "#/components/responses/RateLimited" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } } } }, - "/v1/forensics/timeline": { "get": { - "tags": ["forensics"], + "tags": [ + "forensics" + ], "summary": "DJD Forensics merged timeline", "description": "Returns a merged timeline of score snapshots and fraud incidents for a wallet. Supports date-range filtering and configurable result limits. **Requires x402 payment of $0.20 USDC on Base or Bearer API key.**", "operationId": "getForensicsTimeline", - "security": [{ "x402": [] }, { "bearerApiKey": [] }], - "x-x402": { "price": "$0.20", "network": "base" }, + "security": [ + { + "x402": [] + }, + { + "bearerApiKey": [] + } + ], + "x-x402": { + "price": "$0.20", + "network": "base" + }, "parameters": [ - { "$ref": "#/components/parameters/wallet" }, + { + "$ref": "#/components/parameters/wallet" + }, { "name": "limit", "in": "query", "required": false, "description": "Number of combined events to return (1-100, default 50)", - "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 } + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 50 + } }, { "name": "after", "in": "query", "required": false, "description": "Return events after this ISO 8601 date (YYYY-MM-DD)", - "schema": { "type": "string", "format": "date" } + "schema": { + "type": "string", + "format": "date" + } }, { "name": "before", "in": "query", "required": false, "description": "Return events before this ISO 8601 date (YYYY-MM-DD)", - "schema": { "type": "string", "format": "date" } + "schema": { + "type": "string", + "format": "date" + } } ], "responses": { @@ -1912,51 +3177,85 @@ "description": "Merged forensics timeline returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ForensicsTimelineResponse" } + "schema": { + "$ref": "#/components/schemas/ForensicsTimelineResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, "404": { "description": "No forensics data found for this wallet", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } } } } }, - "/v1/certification/apply": { "post": { - "tags": ["certification"], - "summary": "Apply for certification", - "description": "Apply for Certified Agent Badge. Requirements: active (non-expired) score, composite score >= 75, and a registered agent. Certification is valid for 1 year. **Requires x402 payment of $99 USDC on Base or Bearer API key.**", - "operationId": "applyCertification", - "security": [{ "x402": [] }, { "bearerApiKey": [] }], - "x-x402": { "price": "$99.00", "network": "base" }, + "tags": [ + "certification" + ], + "summary": "Apply for transactional certification", + "description": "Legacy DJD Certify purchase path for the default transactional tier. Requirements: active (non-expired) score, composite score >= 75, and a registered agent. Certification is valid for 1 year and requires x402 payment of $200 USDC on Base.", + "operationId": "applyCertificationTransactionalLegacy", + "security": [ + { + "x402": [] + } + ], + "x-x402": { + "price": "$200.00", + "network": "base" + }, "responses": { "201": { "description": "Certification granted", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/CertificationGrantedResponse" } + "schema": { + "$ref": "#/components/schemas/CertificationGrantedResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "402": { "$ref": "#/components/responses/PaymentRequired" }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, "409": { "description": "Wallet already has an active certification", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } } } } }, "/v1/certification/readiness": { "get": { - "tags": ["certification"], + "tags": [ + "certification" + ], "summary": "Check certification readiness", - "description": "Returns whether a wallet can apply for DJD certification right now, which requirements are met, what is blocking it, and the next recommended actions.", + "description": "Returns whether a wallet can apply for a requested DJD Certify tier right now, which requirements are met, what is blocking it, and the next recommended actions.", "operationId": "getCertificationReadiness", "parameters": [ { @@ -1964,7 +3263,24 @@ "in": "query", "required": true, "description": "Ethereum wallet address", - "schema": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$" } + "schema": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{40}$" + } + }, + { + "name": "tier", + "in": "query", + "required": false, + "description": "Requested certification tier", + "schema": { + "type": "string", + "enum": [ + "operational", + "transactional", + "autonomous" + ] + } } ], "responses": { @@ -1972,17 +3288,23 @@ "description": "Certification readiness returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/CertificationReadinessResponse" } + "schema": { + "$ref": "#/components/schemas/CertificationReadinessResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" } + "400": { + "$ref": "#/components/responses/BadRequest" + } } } }, "/v1/certification/review": { "get": { - "tags": ["certification"], + "tags": [ + "certification" + ], "summary": "Get certification review status", "description": "Returns the latest certification review request and reviewer status for a wallet.", "operationId": "getCertificationReview", @@ -1992,7 +3314,10 @@ "in": "query", "required": true, "description": "Ethereum wallet address", - "schema": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$" } + "schema": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{40}$" + } } ], "responses": { @@ -2000,24 +3325,34 @@ "description": "Certification review returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/CertificationReviewResponse" } + "schema": { + "$ref": "#/components/schemas/CertificationReviewResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "404": { "$ref": "#/components/responses/NotFound" } + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } } }, "post": { - "tags": ["certification"], + "tags": [ + "certification" + ], "summary": "Submit certification review request", - "description": "Creates a certification review request for an eligible wallet before the final certification purchase.", + "description": "Creates a certification review request for an eligible wallet before the final certification purchase. Optionally specify a requested tier.", "operationId": "submitCertificationReview", "requestBody": { "required": true, "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/CertificationReviewRequest" } + "schema": { + "$ref": "#/components/schemas/CertificationReviewRequest" + } } } }, @@ -2026,7 +3361,9 @@ "description": "Existing pending certification review returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/CertificationReviewResponse" } + "schema": { + "$ref": "#/components/schemas/CertificationReviewResponse" + } } } }, @@ -2034,21 +3371,33 @@ "description": "Certification review request created", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/CertificationReviewResponse" } + "schema": { + "$ref": "#/components/schemas/CertificationReviewResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, "409": { "description": "Wallet already has an active certification", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } } } } }, "/v1/certification/directory": { "get": { - "tags": ["certification"], + "tags": [ + "certification" + ], "summary": "Certified directory", "description": "Returns a public directory of active DJD certifications with current score context, profile metadata, and direct links into standards and evaluator surfaces.", "operationId": "getCertificationDirectory", @@ -2058,21 +3407,30 @@ "in": "query", "required": false, "description": "Max number of certifications to return (default 25, max 100)", - "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 25 + } }, { "name": "tier", "in": "query", "required": false, "description": "Filter to a specific certification tier", - "schema": { "type": "string" } + "schema": { + "type": "string" + } }, { "name": "search", "in": "query", "required": false, "description": "Filter by wallet, profile name, description, GitHub URL, or website URL", - "schema": { "type": "string" } + "schema": { + "type": "string" + } }, { "name": "sort", @@ -2081,7 +3439,12 @@ "description": "Sort the directory by score, confidence, recency, or name", "schema": { "type": "string", - "enum": ["score", "confidence", "recent", "name"], + "enum": [ + "score", + "confidence", + "recent", + "name" + ], "default": "score" } } @@ -2091,17 +3454,23 @@ "description": "Certified directory returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/CertificationDirectoryResponse" } + "schema": { + "$ref": "#/components/schemas/CertificationDirectoryResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" } + "400": { + "$ref": "#/components/responses/BadRequest" + } } } }, "/v1/certification/{wallet}": { "get": { - "tags": ["certification"], + "tags": [ + "certification" + ], "summary": "Check certification status", "description": "Returns the active certification details for a wallet. Free — no payment required.", "operationId": "getCertificationStatus", @@ -2111,7 +3480,10 @@ "in": "path", "required": true, "description": "Ethereum wallet address", - "schema": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$" } + "schema": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{40}$" + } } ], "responses": { @@ -2119,21 +3491,33 @@ "description": "Active certification found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/CertificationStatusResponse" } + "schema": { + "$ref": "#/components/schemas/CertificationStatusResponse" + } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, "404": { "description": "No active certification found for this wallet", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } } } } }, "/v1/certification/badge/{wallet}": { "get": { - "tags": ["certification"], + "tags": [ + "certification" + ], "summary": "SVG certification badge", "description": "Returns an embeddable shields.io-style SVG badge showing the wallet's certification status. Green if certified, gray if not. Cached for 1 hour. Free — no payment required.", "operationId": "getCertificationBadge", @@ -2143,25 +3527,42 @@ "in": "path", "required": true, "description": "Ethereum wallet address", - "schema": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$" } + "schema": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{40}$" + } } ], "responses": { "200": { "description": "SVG badge image", - "content": { "image/svg+xml": { "schema": { "type": "string" } } } + "content": { + "image/svg+xml": { + "schema": { + "type": "string" + } + } + } }, - "400": { "$ref": "#/components/responses/BadRequest" } + "400": { + "$ref": "#/components/responses/BadRequest" + } } } }, "/v1/certification/admin/all": { "get": { - "tags": ["certification"], + "tags": [ + "certification" + ], "summary": "List all certifications (admin)", "description": "Returns all certifications including revoked ones. **Requires admin authentication (X-ADMIN-KEY header).**", "operationId": "listAllCertifications", - "security": [{ "adminKey": [] }], + "security": [ + { + "adminKey": [] + } + ], "responses": { "200": { "description": "All certifications returned", @@ -2172,32 +3573,46 @@ "properties": { "certifications": { "type": "array", - "items": { "$ref": "#/components/schemas/CertificationAdminItem" } + "items": { + "$ref": "#/components/schemas/CertificationAdminItem" + } }, - "count": { "type": "integer" } + "count": { + "type": "integer" + } } } } } }, - "401": { "$ref": "#/components/responses/Unauthorized" } + "401": { + "$ref": "#/components/responses/Unauthorized" + } } } }, "/v1/certification/admin/{id}/revoke": { "post": { - "tags": ["certification"], + "tags": [ + "certification" + ], "summary": "Revoke certification (admin)", "description": "Revokes an active certification. Optionally provide a reason. **Requires admin authentication (X-ADMIN-KEY header).**", "operationId": "revokeCertification", - "security": [{ "adminKey": [] }], + "security": [ + { + "adminKey": [] + } + ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Certification ID", - "schema": { "type": "integer" } + "schema": { + "type": "integer" + } } ], "requestBody": { @@ -2207,10 +3622,15 @@ "schema": { "type": "object", "properties": { - "reason": { "type": "string", "description": "Reason for revocation (defaults to 'Administrative revocation')" } + "reason": { + "type": "string", + "description": "Reason for revocation (defaults to 'Administrative revocation')" + } } }, - "example": { "reason": "Score dropped below threshold" } + "example": { + "reason": "Score dropped below threshold" + } } } }, @@ -2222,1660 +3642,9246 @@ "schema": { "type": "object", "properties": { - "success": { "type": "boolean", "example": true }, - "message": { "type": "string", "example": "Certification revoked" }, - "id": { "type": "integer" }, - "reason": { "type": "string" } + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "Certification revoked" + }, + "id": { + "type": "integer" + }, + "reason": { + "type": "string" + } } } } } }, - "400": { "$ref": "#/components/responses/BadRequest" }, - "401": { "$ref": "#/components/responses/Unauthorized" }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, "404": { "description": "Certification not found or already revoked", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } } } } }, "/v1/certification/admin/revenue": { "get": { - "tags": ["certification"], + "tags": [ + "certification" + ], "summary": "Revenue summary (admin)", "description": "Returns certification revenue summary including total, active, and revoked counts, gross and net revenue, and monthly breakdown. **Requires admin authentication (X-ADMIN-KEY header).**", "operationId": "getCertificationRevenue", - "security": [{ "adminKey": [] }], + "security": [ + { + "adminKey": [] + } + ], "responses": { "200": { "description": "Revenue summary returned", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/CertificationRevenueResponse" } + "schema": { + "$ref": "#/components/schemas/CertificationRevenueResponse" + } } } }, - "401": { "$ref": "#/components/responses/Unauthorized" } + "401": { + "$ref": "#/components/responses/Unauthorized" + } } } - } - }, - "components": { - "parameters": { - "wallet": { - "name": "wallet", - "in": "query", - "required": true, - "description": "Base wallet address (EIP-55 checksummed or lowercase)", - "schema": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$" }, - "example": "0x3e4ef1f774857c69e33dddc471e110c7ac7bb528" - }, - "X-Request-ID": { - "name": "X-Request-ID", - "in": "header", - "required": false, - "description": "Client-provided request ID for tracing. If omitted, the server generates a UUID v4. Returned in the X-Request-ID response header.", - "schema": { "type": "string", "format": "uuid" } - } }, - "schemas": { - "Error": { - "type": "object", - "properties": { - "error": { - "type": "object", - "properties": { - "code": { "type": "string", "description": "Machine-readable error code" }, - "message": { "type": "string", "description": "Human-readable error message" }, - "details": { "type": "object", "description": "Optional additional context", "additionalProperties": true } - }, - "required": ["code", "message"] + "/v1/certification/tiers": { + "get": { + "tags": [ + "certification" + ], + "summary": "List certification tiers", + "description": "Returns the DJD Certify tier ladder, including score floors, prices, controls, and the default tier.", + "operationId": "getCertificationTiers", + "responses": { + "200": { + "description": "Certification tier catalog returned", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CertificationTierCatalogResponse" + } + } + } } } - }, - "BasicScoreResponse": { - "type": "object", - "properties": { - "wallet": { "type": "string" }, - "score": { "type": "integer", "minimum": 0, "maximum": 100 }, - "tier": { "type": "string", "enum": ["Unverified", "Emerging", "Established", "Trusted", "Elite"] }, - "confidence": { "type": "number", "minimum": 0, "maximum": 1 }, - "recommendation": { "type": "string", "enum": ["proceed", "proceed_with_caution", "insufficient_history", "high_risk", "flagged_for_review", "rpc_unavailable"] }, - "modelVersion": { "type": "string" }, - "lastUpdated": { "type": "string", "format": "date-time" }, - "computedAt": { "type": "string", "format": "date-time", "description": "ISO-8601 timestamp of when the score was computed (may differ from lastUpdated when served from cache)" }, - "scoreFreshness": { "type": "number", "minimum": 0, "maximum": 1, "description": "Freshness factor: 1 = just computed, decays linearly toward 0 at cache expiry. Consumers can use this to weight trust." }, - "stale": { "type": "boolean", "description": "True when RPC was unavailable and a cached result was returned" } - } - }, - "ERC8004CompatibleScoreResponse": { - "type": "object", - "properties": { - "standard": { "type": "string", "enum": ["erc-8004-compatible"] }, - "wallet": { "type": "string" }, - "agent_id": { "type": "string", "description": "Decimal string form of uint256(uint160(wallet))" }, - "provider": { - "type": "object", - "properties": { - "name": { "type": "string" }, - "reputation_tag": { "type": "string" }, - "model_version": { "type": "string" }, - "document_url": { "type": "string", "format": "uri" } - } - }, - "reputation": { - "type": "object", - "properties": { - "composite_score": { "type": "integer", "minimum": 0, "maximum": 100 }, - "tier": { "type": "string", "enum": ["Unverified", "Emerging", "Established", "Trusted", "Elite"] }, - "confidence": { "type": "number", "minimum": 0, "maximum": 1 }, - "recommendation": { "type": "string" }, - "computed_at": { "type": "string", "format": "date-time" }, - "last_updated": { "type": "string", "format": "date-time" }, - "score_freshness": { "type": "number", "minimum": 0, "maximum": 1 }, - "data_source": { "type": "string", "enum": ["live", "cached", "unavailable"], "nullable": true } - } - }, - "identity": { - "type": "object", - "properties": { - "registered": { "type": "boolean" }, - "name": { "type": "string", "nullable": true }, - "description": { "type": "string", "nullable": true }, - "github_url": { "type": "string", "format": "uri", "nullable": true }, - "website_url": { "type": "string", "format": "uri", "nullable": true }, - "github_verified": { "type": "boolean" }, - "registered_at": { "type": "string", "format": "date-time", "nullable": true }, - "updated_at": { "type": "string", "format": "date-time", "nullable": true } + } + }, + "/v1/certification/apply/operational": { + "post": { + "tags": [ + "certification" + ], + "summary": "Apply for operational certification", + "description": "Purchase Tier 1 Operational certification. Requirements: active (non-expired) score, composite score >= 60, and a registered agent. Certification is valid for 1 year and requires x402 payment of $50 USDC on Base.", + "operationId": "applyCertificationOperational", + "security": [ + { + "x402": [] + } + ], + "x-x402": { + "price": "$50.00", + "network": "base" + }, + "responses": { + "201": { + "description": "Certification granted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CertificationGrantedResponse" + } + } } }, - "certification": { - "type": "object", - "properties": { - "active": { "type": "boolean" }, - "tier": { "type": "string", "nullable": true }, - "score_at_certification": { "type": "integer", "minimum": 0, "maximum": 100, "nullable": true }, - "granted_at": { "type": "string", "format": "date-time", "nullable": true }, - "expires_at": { "type": "string", "format": "date-time", "nullable": true }, - "tx_hash": { "type": "string", "nullable": true } - } + "400": { + "$ref": "#/components/responses/BadRequest" }, - "publication": { - "type": "object", - "properties": { - "published": { "type": "boolean" }, - "registry": { "type": "string", "enum": ["erc-8004"] }, - "endpoint": { "type": "string", "format": "uri" }, - "tx_hash": { "type": "string", "nullable": true }, - "published_at": { "type": "string", "format": "date-time", "nullable": true }, - "score_at_publication": { "type": "integer", "minimum": 0, "maximum": 100, "nullable": true }, - "model_version": { "type": "string", "nullable": true } - } + "402": { + "$ref": "#/components/responses/PaymentRequired" }, - "links": { - "type": "object", - "properties": { - "basic_score": { "type": "string", "format": "uri" }, - "full_score": { "type": "string", "format": "uri" }, - "certification_status": { "type": "string", "format": "uri" }, - "certification_badge": { "type": "string", "format": "uri" }, - "score_badge": { "type": "string", "format": "uri" }, - "agent_profile": { "type": "string", "format": "uri" } + "409": { + "description": "Wallet already has an active certification", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } } } } - }, - "EvaluatorPreviewResponse": { - "type": "object", - "properties": { - "standard": { "type": "string", "enum": ["erc-8183-evaluator-prototype"] }, - "wallet": { "type": "string" }, - "decision": { "type": "string", "enum": ["approve", "review", "reject"] }, - "confidence": { "type": "number", "minimum": 0, "maximum": 1 }, - "rationale": { "type": "string" }, - "score": { - "type": "object", - "properties": { - "current_score": { "type": "integer", "minimum": 0, "maximum": 100 }, - "current_tier": { "type": "string" }, - "score_confidence": { "type": "number", "minimum": 0, "maximum": 1 }, - "score_recommendation": { "type": "string" }, - "score_model_version": { "type": "string" }, - "last_scored_at": { "type": "string", "format": "date-time" } + } + }, + "/v1/certification/apply/transactional": { + "post": { + "tags": [ + "certification" + ], + "summary": "Apply for transactional certification", + "description": "Purchase Tier 2 Transactional certification. Requirements: active (non-expired) score, composite score >= 75, and a registered agent. Certification is valid for 1 year and requires x402 payment of $200 USDC on Base.", + "operationId": "applyCertificationTransactional", + "security": [ + { + "x402": [] + } + ], + "x-x402": { + "price": "$200.00", + "network": "base" + }, + "responses": { + "201": { + "description": "Certification granted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CertificationGrantedResponse" + } + } } }, - "certification": { - "type": "object", - "properties": { - "active": { "type": "boolean" }, - "tier": { "type": "string", "nullable": true }, - "granted_at": { "type": "string", "format": "date-time", "nullable": true }, - "expires_at": { "type": "string", "format": "date-time", "nullable": true } - } + "400": { + "$ref": "#/components/responses/BadRequest" }, - "risk": { - "type": "object", - "properties": { - "risk_score": { "type": "integer", "minimum": 0, "maximum": 100 }, - "risk_level": { "type": "string", "enum": ["clear", "watch", "elevated", "critical"] }, - "action": { "type": "string", "enum": ["allow", "monitor", "review", "block"] } - } + "402": { + "$ref": "#/components/responses/PaymentRequired" }, - "market_signals": { - "type": "object", - "properties": { - "rating_count": { "type": "integer" }, - "average_rating": { "type": "number", "nullable": true }, - "unique_raters": { "type": "integer" }, - "intent_count": { "type": "integer" }, - "conversion_rate": { "type": "number" }, - "active_creator_stakes": { "type": "integer" }, - "active_score_boost": { "type": "integer" } + "409": { + "description": "Wallet already has an active certification", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } } - }, - "checks": { - "type": "array", - "items": { + } + } + } + }, + "/v1/certification/apply/autonomous": { + "post": { + "tags": [ + "certification" + ], + "summary": "Apply for autonomous certification", + "description": "Purchase Tier 3 Autonomous certification. Requirements: active (non-expired) score, composite score >= 90, and a registered agent. Certification is valid for 1 year and requires x402 payment of $500 USDC on Base.", + "operationId": "applyCertificationAutonomous", + "security": [ + { + "x402": [] + } + ], + "x-x402": { + "price": "$500.00", + "network": "base" + }, + "responses": { + "201": { + "description": "Certification granted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CertificationGrantedResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "409": { + "description": "Wallet already has an active certification", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/v1/score/evaluator/evidence": { + "get": { + "tags": [ + "score" + ], + "summary": "ERC-8183 evaluator evidence packet", + "description": "Returns a settlement evidence packet that packages the evaluator verdict with certification baseline, forensics summary, and traceable artifact links.", + "operationId": "getEvaluatorEvidencePacket", + "parameters": [ + { + "name": "wallet", + "in": "query", + "required": true, + "description": "Ethereum wallet address", + "schema": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{40}$" + } + } + ], + "security": [ + { + "x402": [] + }, + { + "bearerApiKey": [] + } + ], + "x-x402": { + "price": "$0.45", + "network": "base" + }, + "responses": { + "200": { + "description": "Evaluator evidence packet returned", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EvaluatorEvidencePacketResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + } + } + } + }, + "/v1/score/evaluator/oracle": { + "get": { + "tags": [ + "score" + ], + "summary": "ERC-8183 evaluator oracle verdict", + "description": "ERC-8183 oracle-style verdict endpoint that persists a compact settlement decision with recommendation, forensic trace id, and a signed EIP-712 attestation when a signer is configured.", + "operationId": "getEvaluatorOracleVerdict", + "parameters": [ + { + "$ref": "#/components/parameters/wallet" + }, + { + "name": "counterparty_wallet", + "in": "query", + "description": "Optional client or counterparty wallet", + "schema": { + "type": "string" + } + }, + { + "name": "escrow_id", + "in": "query", + "description": "Optional escrow or settlement reference id", + "schema": { + "type": "string" + } + }, + { + "name": "network", + "in": "query", + "required": false, + "description": "Target evaluator network. Use `base` for Base mainnet or `base-sepolia` for Base Sepolia.", + "schema": { + "type": "string", + "enum": [ + "base", + "base-sepolia" + ] + } + } + ], + "security": [ + { + "x402": [] + } + ], + "x-x402": { + "price": "$0.60", + "network": "base" + }, + "responses": { + "200": { + "description": "Evaluator oracle verdict returned", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EvaluatorOracleResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } + } + } + }, + "/v1/score/evaluator/verdict": { + "get": { + "tags": [ + "score" + ], + "summary": "Stored evaluator verdict record", + "description": "Fetch a previously issued evaluator oracle verdict record by id, including its attestation metadata.", + "operationId": "getEvaluatorVerdictRecord", + "parameters": [ + { + "name": "id", + "in": "query", + "required": true, + "description": "Stored evaluator verdict id returned by the oracle endpoint", + "schema": { + "type": "string" + } + } + ], + "security": [ + { + "x402": [] + } + ], + "x-x402": { + "price": "$0.12", + "network": "base" + }, + "responses": { + "200": { + "description": "Stored evaluator verdict returned", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EvaluatorVerdictRecordResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } + } + } + }, + "/v1/score/evaluator/verdicts": { + "get": { + "tags": [ + "score" + ], + "summary": "Evaluator verdict history", + "description": "List recent persisted evaluator verdicts for a wallet, including approval and dispute mix.", + "operationId": "listEvaluatorVerdictHistory", + "parameters": [ + { + "$ref": "#/components/parameters/wallet" + }, + { + "name": "limit", + "in": "query", + "description": "Max verdicts to return (default 10, max 50)", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 50 + } + } + ], + "security": [ + { + "x402": [] + } + ], + "x-x402": { + "price": "$0.30", + "network": "base" + }, + "responses": { + "200": { + "description": "Evaluator verdict history returned", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EvaluatorVerdictHistoryResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } + } + } + }, + "/v1/score/evaluator/callback": { + "get": { + "tags": [ + "score" + ], + "summary": "Evaluator callback calldata", + "description": "Convert a stored evaluator verdict into ABI-backed callback calldata for a relayer or escrow contract. Returns a ready=false envelope when the verdict attestation is unsigned.", + "operationId": "getEvaluatorCallback", + "parameters": [ + { + "name": "id", + "in": "query", + "required": true, + "description": "Stored evaluator verdict id to convert into callback calldata", + "schema": { + "type": "string" + } + }, + { + "name": "target_contract", + "in": "query", + "description": "Optional target contract address to place into the returned transaction envelope", + "schema": { + "type": "string" + } + }, + { + "name": "network", + "in": "query", + "required": false, + "description": "Target evaluator network. Use `base` for Base mainnet or `base-sepolia` for Base Sepolia.", + "schema": { + "type": "string", + "enum": [ + "base", + "base-sepolia" + ] + } + } + ], + "security": [ + { + "x402": [] + } + ], + "x-x402": { + "price": "$0.20", + "network": "base" + }, + "responses": { + "200": { + "description": "Evaluator callback payload returned", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EvaluatorCallbackResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + } + } + } + }, + "/v1/score/evaluator/verifier": { + "get": { + "tags": [ + "score" + ], + "summary": "Evaluator verifier package", + "description": "Returns the free onchain integration package for DJD evaluator verdicts, including the callback ABI, Solidity verifier ABI, EIP-712 typed-data schema, and Solidity sources for Base-side verification.", + "operationId": "getEvaluatorVerifierPackage", + "parameters": [ + { + "name": "network", + "in": "query", + "required": false, + "description": "Target evaluator network. Use `base` for Base mainnet or `base-sepolia` for Base Sepolia.", + "schema": { + "type": "string", + "enum": [ + "base", + "base-sepolia" + ] + } + } + ], + "responses": { + "200": { + "description": "Verifier package returned", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EvaluatorVerifierPackageResponse" + }, + "example": { + "standard": "djd-evaluator-verifier-package-v1", + "network": { + "chain_id": 8453, + "chain_name": "Base" + }, + "signing": { + "scheme": "eip712", + "primary_type": "EvaluatorVerdict", + "domain": { + "name": "DJD Evaluator Verdict", + "version": "1", + "chainId": 8453 + } + }, + "contracts": { + "callback_interface": { + "contract": "IDJDEvaluatorOracleCallback", + "function": "receiveVerdict", + "selector": "0x807f6017" + }, + "verifier": { + "contract": "DJDEvaluatorVerdictVerifier", + "constructor": { + "initial_signer": "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65" + } + }, + "settlement_example": { + "contract": "DJDEvaluatorEscrowSettlementExample", + "function": "settleWithDJDVerdict", + "selector": "0xdf5b527a" + } + }, + "endpoints": { + "oracle_verdict": "https://api.example.test/v1/score/evaluator/oracle?wallet=0x...", + "verdict_record": "https://api.example.test/v1/score/evaluator/verdict?id=verdict_...", + "callback_calldata": "https://api.example.test/v1/score/evaluator/callback?id=verdict_...", + "docs": "https://api.example.test/docs", + "verifier_proof": "https://api.example.test/v1/score/evaluator/proof?id=verdict_...", + "escrow_settlement": "https://api.example.test/v1/score/evaluator/escrow?id=verdict_...", + "deploy_plan": "https://api.example.test/v1/score/evaluator/deploy?id=verdict_...", + "artifact_package": "https://api.example.test/v1/score/evaluator/artifacts", + "deployment_registry": "https://api.example.test/v1/score/evaluator/deployments?network=base" + } + } + } + } + } + } + } + }, + "/v1/score/evaluator/proof": { + "get": { + "tags": [ + "score" + ], + "summary": "Evaluator verifier proof envelope", + "description": "Returns the contract-ready verifyVerdict calldata for a stored evaluator verdict when the verdict attestation is signed. If target_contract is omitted and a published verifier deployment exists for the requested network, the returned transaction envelope targets that live verifier automatically. Returns ready=false when the verdict is unsigned.", + "operationId": "getEvaluatorVerifierProof", + "parameters": [ + { + "name": "id", + "in": "query", + "required": true, + "schema": { + "type": "string" + }, + "description": "Stored evaluator verdict id to convert into verifier calldata" + }, + { + "name": "target_contract", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "Optional verifier contract address to place into the returned transaction envelope" + }, + { + "name": "network", + "in": "query", + "required": false, + "description": "Target evaluator network. Use `base` for Base mainnet or `base-sepolia` for Base Sepolia.", + "schema": { + "type": "string", + "enum": [ + "base", + "base-sepolia" + ] + } + } + ], + "responses": { + "200": { + "description": "Verifier proof envelope returned", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EvaluatorVerifierProofResponse" + }, + "example": { + "standard": "djd-evaluator-verifier-proof-v1", + "ready": true, + "verdict_id": "verdict_123e4567-e89b-42d3-a456-426614174000", + "verifier": { + "contract": "DJDEvaluatorVerdictVerifier", + "function": "verifyVerdict", + "selector": "0x6f5d7d89", + "chain_id": 8453 + }, + "attestation": { + "status": "signed", + "signer": "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", + "digest": "0xabc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abcd", + "signature": "0xabcdef", + "scheme": "eip712" + }, + "transaction": { + "to": "0x1111111111111111111111111111111111111111", + "data": "0x1234", + "value": "0" + }, + "reason": null, + "call": { + "selector": "0x6f5d7d89", + "calldata": "0x1234", + "args": { + "verdict": { + "verdictId": "verdict_123e4567-e89b-42d3-a456-426614174000" + }, + "signature": "0xabcdef" + } + }, + "resolution": { + "source": "published_registry", + "contract_address": "0x1111111111111111111111111111111111111111", + "registry_updated_at": "2026-03-16T02:00:00.000Z", + "published_deployment": { + "published_at": "2026-03-16T02:00:00.000Z", + "network": { + "key": "base", + "chain_id": 8453, + "chain_name": "Base", + "caip2": "eip155:8453", + "environment": "mainnet" + }, + "verdict_id": "verdict_live_stage_1", + "deployer": "0x1234567890abcdef1234567890abcdef12345678", + "contracts": { + "verifier": { + "contract": "DJDEvaluatorVerdictVerifier", + "address": "0x1111111111111111111111111111111111111111", + "tx_hash": "0x1111111111111111111111111111111111111111111111111111111111111111", + "action": "deploy" + }, + "escrow": { + "contract": "DJDEvaluatorEscrowSettlementExample", + "address": "0x2222222222222222222222222222222222222222", + "tx_hash": "0x2222222222222222222222222222222222222222222222222222222222222222", + "action": "deploy" + } + }, + "verification": { + "oracle_signer": "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", + "escrow_verifier": "0x1111111111111111111111111111111111111111", + "escrow_provider": "0x3333333333333333333333333333333333333333", + "escrow_counterparty": "0x4444444444444444444444444444444444444444", + "escrow_id_hash": "0x3333333333333333333333333333333333333333333333333333333333333333" + }, + "inputs": { + "provider": "0x3333333333333333333333333333333333333333", + "counterparty": "0x4444444444444444444444444444444444444444", + "escrow_id": "escrow-live-123" + }, + "checks": { + "preflight": true, + "verified": true, + "smoked": true, + "health": null, + "staged": true + }, + "explorer": null, + "links": { + "verifier_package": "https://api.example.test/v1/score/evaluator/verifier?network=base", + "deployment_registry": "https://api.example.test/v1/score/evaluator/deployments?network=base", + "verifier_proof": "https://api.example.test/v1/score/evaluator/proof?id=verdict_live_stage_1&network=base", + "escrow_settlement": "https://api.example.test/v1/score/evaluator/escrow?id=verdict_live_stage_1&network=base", + "deploy_bundle": "https://api.example.test/v1/score/evaluator/deploy/bundle?id=verdict_live_stage_1&network=base" + } + } + }, + "links": { + "verdict_record": "https://api.example.test/v1/score/evaluator/verdict?id=verdict_123e4567-e89b-42d3-a456-426614174000", + "verifier_package": "https://api.example.test/v1/score/evaluator/verifier?network=base", + "deployment_registry": "https://api.example.test/v1/score/evaluator/deployments?network=base" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } + } + } + }, + "/v1/score/evaluator/escrow": { + "get": { + "tags": [ + "score" + ], + "summary": "Evaluator escrow settlement envelope", + "description": "Returns the contract-ready settleWithDJDVerdict calldata for the reference DJD escrow consumer contract when the stored verdict attestation is signed. If escrow_contract is omitted and a published escrow deployment exists for the requested network, the returned transaction envelope targets that live escrow automatically. Returns ready=false when the verdict is unsigned.", + "operationId": "getEvaluatorEscrowSettlement", + "parameters": [ + { + "name": "id", + "in": "query", + "required": true, + "schema": { + "type": "string" + }, + "description": "Stored evaluator verdict id to convert into escrow settlement calldata" + }, + { + "name": "escrow_contract", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "Optional deployed escrow consumer contract address to place into the returned transaction envelope" + }, + { + "name": "network", + "in": "query", + "required": false, + "description": "Target evaluator network. Use `base` for Base mainnet or `base-sepolia` for Base Sepolia.", + "schema": { + "type": "string", + "enum": [ + "base", + "base-sepolia" + ] + } + } + ], + "responses": { + "200": { + "description": "Escrow settlement envelope returned", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EvaluatorEscrowSettlementResponse" + }, + "example": { + "standard": "djd-evaluator-escrow-settlement-v1", + "ready": true, + "verdict_id": "verdict_123e4567-e89b-42d3-a456-426614174000", + "escrow": { + "contract": "DJDEvaluatorEscrowSettlementExample", + "function": "settleWithDJDVerdict", + "selector": "0xdf5b527a", + "chain_id": 8453 + }, + "settlement": { + "recommendation": "release", + "approved": true, + "outcome": "release", + "release_authorized": true + }, + "transaction": { + "to": "0x2222222222222222222222222222222222222222", + "data": "0x1234", + "value": "0" + }, + "reason": null, + "verifier": { + "contract": "DJDEvaluatorVerdictVerifier", + "function": "verifyVerdict" + }, + "attestation": { + "status": "signed", + "signer": "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", + "digest": "0xabc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abcd", + "signature": "0xabcdef", + "scheme": "eip712" + }, + "verdict": { + "verdictId": "verdict_123e4567-e89b-42d3-a456-426614174000" + }, + "call": { + "selector": "0xdf5b527a", + "calldata": "0x1234", + "args": { + "verdict": { + "verdictId": "verdict_123e4567-e89b-42d3-a456-426614174000" + }, + "signature": "0xabcdef" + } + }, + "resolution": { + "source": "published_registry", + "contract_address": "0x2222222222222222222222222222222222222222", + "registry_updated_at": "2026-03-16T02:00:00.000Z", + "published_deployment": { + "published_at": "2026-03-16T02:00:00.000Z", + "network": { + "key": "base", + "chain_id": 8453, + "chain_name": "Base", + "caip2": "eip155:8453", + "environment": "mainnet" + }, + "verdict_id": "verdict_live_stage_1", + "deployer": "0x1234567890abcdef1234567890abcdef12345678", + "contracts": { + "verifier": { + "contract": "DJDEvaluatorVerdictVerifier", + "address": "0x1111111111111111111111111111111111111111", + "tx_hash": "0x1111111111111111111111111111111111111111111111111111111111111111", + "action": "deploy" + }, + "escrow": { + "contract": "DJDEvaluatorEscrowSettlementExample", + "address": "0x2222222222222222222222222222222222222222", + "tx_hash": "0x2222222222222222222222222222222222222222222222222222222222222222", + "action": "deploy" + } + }, + "verification": { + "oracle_signer": "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", + "escrow_verifier": "0x1111111111111111111111111111111111111111", + "escrow_provider": "0x3333333333333333333333333333333333333333", + "escrow_counterparty": "0x4444444444444444444444444444444444444444", + "escrow_id_hash": "0x3333333333333333333333333333333333333333333333333333333333333333" + }, + "inputs": { + "provider": "0x3333333333333333333333333333333333333333", + "counterparty": "0x4444444444444444444444444444444444444444", + "escrow_id": "escrow-live-123" + }, + "checks": { + "preflight": true, + "verified": true, + "smoked": true, + "health": null, + "staged": true + }, + "explorer": null, + "links": { + "verifier_package": "https://api.example.test/v1/score/evaluator/verifier?network=base", + "deployment_registry": "https://api.example.test/v1/score/evaluator/deployments?network=base", + "verifier_proof": "https://api.example.test/v1/score/evaluator/proof?id=verdict_live_stage_1&network=base", + "escrow_settlement": "https://api.example.test/v1/score/evaluator/escrow?id=verdict_live_stage_1&network=base", + "deploy_bundle": "https://api.example.test/v1/score/evaluator/deploy/bundle?id=verdict_live_stage_1&network=base" + } + } + }, + "links": { + "verdict_record": "https://api.example.test/v1/score/evaluator/verdict?id=verdict_123e4567-e89b-42d3-a456-426614174000", + "verifier_package": "https://api.example.test/v1/score/evaluator/verifier?network=base", + "verifier_proof": "https://api.example.test/v1/score/evaluator/proof?id=verdict_123e4567-e89b-42d3-a456-426614174000&network=base", + "deployment_registry": "https://api.example.test/v1/score/evaluator/deployments?network=base" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } + } + } + }, + "/v1/score/evaluator/deploy": { + "get": { + "tags": [ + "score" + ], + "summary": "Evaluator contract deployment plan", + "description": "Returns constructor args and readiness metadata for deploying the DJD verdict verifier and the reference escrow consumer contract from a stored evaluator verdict. If verifier_contract is omitted and a published verifier deployment exists for the requested network, the plan reuses that live verifier automatically.", + "operationId": "getEvaluatorDeploymentPlan", + "parameters": [ + { + "name": "id", + "in": "query", + "required": true, + "schema": { + "type": "string" + }, + "description": "Stored evaluator verdict id to use for constructor args" + }, + { + "name": "verifier_contract", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "Optional already-deployed verifier contract address to wire into the escrow consumer constructor" + }, + { + "name": "network", + "in": "query", + "required": false, + "description": "Target evaluator network. Use `base` for Base mainnet or `base-sepolia` for Base Sepolia.", + "schema": { + "type": "string", + "enum": [ + "base", + "base-sepolia" + ] + } + } + ], + "responses": { + "200": { + "description": "Deployment plan returned", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EvaluatorDeploymentPlanResponse" + }, + "example": { + "standard": "djd-evaluator-deploy-plan-v1", + "network": { + "chain_id": 8453, + "chain_name": "Base", + "key": "base", + "caip2": "eip155:8453", + "environment": "mainnet" + }, + "verdict_id": "verdict_123e4567-e89b-42d3-a456-426614174000", + "verifier": { + "contract": "DJDEvaluatorVerdictVerifier", + "constructor": { + "initial_signer": "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65" + }, + "deployment_ready": true, + "reason": null + }, + "escrow": { + "contract": "DJDEvaluatorEscrowSettlementExample", + "constructor": { + "verifier": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd", + "provider": "0x1234567890abcdef1234567890abcdef12345678", + "counterparty": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd", + "escrow_id_hash": "0xabc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abcd", + "escrow_id": "escrow-live-123", + "verifier_source": "published_registry" + }, + "deployment_ready": true, + "reason": null + }, + "links": { + "verifier_package": "https://api.example.test/v1/score/evaluator/verifier?network=base", + "verifier_proof": "https://api.example.test/v1/score/evaluator/proof?id=verdict_123e4567-e89b-42d3-a456-426614174000&network=base", + "escrow_settlement": "https://api.example.test/v1/score/evaluator/escrow?id=verdict_123e4567-e89b-42d3-a456-426614174000&network=base", + "deployment_registry": "https://api.example.test/v1/score/evaluator/deployments?network=base" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } + } + } + }, + "/v1/score/evaluator/artifacts": { + "get": { + "tags": [ + "score" + ], + "summary": "Compiled evaluator contract artifacts", + "description": "Returns the free compiled Solidity artifact package for DJD onchain integrations, including ABI, bytecode, constructor metadata, and compiler settings.", + "operationId": "getEvaluatorArtifactPackage", + "parameters": [], + "responses": { + "200": { + "description": "Artifact package returned", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EvaluatorArtifactPackageResponse" + }, + "example": { + "standard": "djd-evaluator-artifact-package-v1", + "available": true, + "compiler": { + "name": "solc", + "version": "0.8.34+commit.80d5c536.Emscripten.clang", + "via_ir": true + }, + "summary": { + "total": 4, + "deployable": 2, + "interfaces": 2 + }, + "contracts": [ + { + "contract": "DJDEvaluatorVerdictVerifier", + "artifact_kind": "contract", + "deployable": true, + "artifact_path": "artifacts/contracts/DJDEvaluatorVerdictVerifier.json", + "source_path": "contracts/DJDEvaluatorVerdictVerifier.sol", + "bytecode": "0x6080" + } + ], + "links": { + "verifier_package": "https://api.example.test/v1/score/evaluator/verifier", + "deploy_plan": "https://api.example.test/v1/score/evaluator/deploy?id=verdict_...", + "docs": "https://api.example.test/docs" + } + } + } + } + } + } + } + }, + "/v1/score/evaluator/deploy/bundle": { + "get": { + "tags": [ + "score" + ], + "summary": "Evaluator deployment bundle", + "description": "Returns a one-shot deployment bundle that combines verdict-driven constructor args with the compiled verifier and escrow artifacts for operator tooling.", + "operationId": "getEvaluatorDeploymentBundle", + "parameters": [ + { + "name": "id", + "in": "query", + "required": true, + "schema": { + "type": "string" + }, + "description": "Stored evaluator verdict id to use for deployment planning" + }, + { + "name": "verifier_contract", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "Optional existing verifier contract address to reuse instead of deploying a new verifier" + }, + { + "name": "network", + "in": "query", + "required": false, + "description": "Target evaluator network. Use `base` for Base mainnet or `base-sepolia` for Base Sepolia.", + "schema": { + "type": "string", + "enum": [ + "base", + "base-sepolia" + ] + } + } + ], + "responses": { + "200": { + "description": "Deployment bundle returned", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EvaluatorDeploymentBundleResponse" + }, + "example": { + "standard": "djd-evaluator-deploy-bundle-v1", + "network": { + "chain_id": 8453, + "chain_name": "Base" + }, + "verdict_id": "verdict_123e4567-e89b-42d3-a456-426614174000", + "artifacts": { + "available": true, + "verifier": { + "contract": "DJDEvaluatorVerdictVerifier", + "artifact_kind": "contract" + }, + "escrow": { + "contract": "DJDEvaluatorEscrowSettlementExample", + "artifact_kind": "contract" + } + }, + "deployment": { + "order": [ + "verifier", + "escrow" + ], + "verifier": { + "action": "deploy", + "contract": "DJDEvaluatorVerdictVerifier", + "current_address": null + }, + "escrow": { + "action": "deploy", + "contract": "DJDEvaluatorEscrowSettlementExample" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } + } + } + }, + "/v1/score/evaluator/networks": { + "get": { + "tags": [ + "Score" + ], + "summary": "List supported evaluator deployment networks", + "description": "Free catalog of supported evaluator/onchain networks, including Base mainnet and Base Sepolia deployment metadata.", + "responses": { + "200": { + "description": "Supported evaluator networks and deployment metadata.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EvaluatorNetworkCatalogResponse" + }, + "example": { + "standard": "djd-evaluator-network-catalog-v1", + "default_network": { + "key": "base", + "chain_id": 8453, + "chain_name": "Base", + "caip2": "eip155:8453", + "environment": "mainnet" + }, + "supported_networks": [ + { + "key": "base", + "chain_id": 8453, + "chain_name": "Base", + "caip2": "eip155:8453", + "environment": "mainnet", + "explorer": { + "name": "BaseScan", + "base_url": "https://basescan.org" + }, + "deployment": { + "rpc_env_var": "DJD_BASE_RPC_URL", + "bundle_param": "base", + "verifier_package": "https://api.example.test/v1/score/evaluator/verifier?network=base", + "deployment_registry": "https://api.example.test/v1/score/evaluator/deployments?network=base" + } + }, + { + "key": "base-sepolia", + "chain_id": 84532, + "chain_name": "Base Sepolia", + "caip2": "eip155:84532", + "environment": "testnet", + "explorer": { + "name": "BaseScan", + "base_url": "https://sepolia.basescan.org" + }, + "deployment": { + "rpc_env_var": "DJD_BASE_SEPOLIA_RPC_URL", + "bundle_param": "base-sepolia", + "verifier_package": "https://api.example.test/v1/score/evaluator/verifier?network=base-sepolia", + "deployment_registry": "https://api.example.test/v1/score/evaluator/deployments?network=base-sepolia" + } + } + ], + "signing": { + "active_signer": { + "configured": true, + "source": "oracle_signer", + "address": "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", + "reason": null + } + }, + "artifacts": { + "available": true + } + } + } + } + } + } + } + }, + "/v1/score/evaluator/deployments": { + "get": { + "tags": [ + "Score" + ], + "summary": "List published evaluator contract deployments", + "description": "Free registry of currently published DJD evaluator verifier and escrow contract addresses by supported network.", + "parameters": [ + { + "name": "network", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + "base", + "base-sepolia" + ] + }, + "description": "Optional network filter. Defaults to returning all supported networks." + } + ], + "responses": { + "200": { + "description": "Published evaluator deployments keyed by network.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EvaluatorDeploymentRegistryResponse" + }, + "example": { + "standard": "djd-evaluator-deployments-v1", + "registry": { + "available": true, + "updated_at": "2026-03-16T02:00:00.000Z", + "deployment_count": 1, + "error": null + }, + "filter": { + "network": "base-sepolia" + }, + "networks": [ + { + "key": "base-sepolia", + "chain_id": 84532, + "chain_name": "Base Sepolia", + "caip2": "eip155:84532", + "environment": "testnet", + "explorer": { + "name": "BaseScan", + "base_url": "https://sepolia.basescan.org" + }, + "rpc_env_var": "DJD_BASE_SEPOLIA_RPC_URL", + "deployed": true, + "deployment": { + "published_at": "2026-03-16T02:00:00.000Z", + "verdict_id": "verdict_live_stage_1", + "deployer": "0x1234567890abcdef1234567890abcdef12345678", + "contracts": { + "verifier": { + "contract": "DJDEvaluatorVerdictVerifier", + "address": "0x1111111111111111111111111111111111111111", + "tx_hash": "0x1111111111111111111111111111111111111111111111111111111111111111", + "action": "deploy" + }, + "escrow": { + "contract": "DJDEvaluatorEscrowSettlementExample", + "address": "0x2222222222222222222222222222222222222222", + "tx_hash": "0x2222222222222222222222222222222222222222222222222222222222222222", + "action": "deploy" + } + }, + "verification": { + "oracle_signer": "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", + "escrow_verifier": "0x1111111111111111111111111111111111111111", + "escrow_provider": "0x3333333333333333333333333333333333333333", + "escrow_counterparty": "0x4444444444444444444444444444444444444444", + "escrow_id_hash": "0x3333333333333333333333333333333333333333333333333333333333333333" + }, + "inputs": { + "provider": "0x3333333333333333333333333333333333333333", + "counterparty": "0x4444444444444444444444444444444444444444", + "escrow_id": "escrow-live-123" + }, + "checks": { + "preflight": true, + "verified": true, + "smoked": true, + "health": null, + "staged": true + }, + "explorer": { + "verifier_address": "https://sepolia.basescan.org/address/0x1111111111111111111111111111111111111111", + "verifier_transaction": "https://sepolia.basescan.org/tx/0x1111111111111111111111111111111111111111111111111111111111111111", + "escrow_address": "https://sepolia.basescan.org/address/0x2222222222222222222222222222222222222222", + "escrow_transaction": "https://sepolia.basescan.org/tx/0x2222222222222222222222222222222222222222222222222222222222222222" + }, + "links": { + "verifier_package": "https://api.example.test/v1/score/evaluator/verifier?network=base-sepolia", + "deployment_registry": "https://api.example.test/v1/score/evaluator/deployments?network=base-sepolia", + "verifier_proof": "https://api.example.test/v1/score/evaluator/proof?id=verdict_live_stage_1&network=base-sepolia", + "escrow_settlement": "https://api.example.test/v1/score/evaluator/escrow?id=verdict_live_stage_1&network=base-sepolia", + "deploy_bundle": "https://api.example.test/v1/score/evaluator/deploy/bundle?id=verdict_live_stage_1&network=base-sepolia" + } + } + } + ] + } + } + } + } + } + } + }, + "/v1/score/evaluator/promotion": { + "get": { + "tags": [ + "Score" + ], + "summary": "Fetch env bundle for the active published evaluator deployment", + "description": "Free promotion bundle that turns the currently published DJD evaluator verifier and escrow deployment into ready-to-consume environment variables and export formats for CI or downstream consumers.", + "parameters": [ + { + "name": "network", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + "base", + "base-sepolia" + ] + }, + "description": "Optional network filter. Defaults to the primary DJD evaluator network." + } + ], + "responses": { + "200": { + "description": "Published deployment env bundle for the requested network.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EvaluatorDeploymentPromotionResponse" + }, + "example": { + "standard": "djd-evaluator-promotion-bundle-v1", + "ready": true, + "reason": null, + "source": "published_registry", + "registry": { + "available": true, + "updated_at": "2026-03-16T10:00:00.000Z", + "error": null + }, + "network": { + "key": "base-sepolia", + "chain_id": 84532, + "chain_name": "Base Sepolia", + "caip2": "eip155:84532", + "environment": "testnet", + "rpc_env_var": "DJD_BASE_SEPOLIA_RPC_URL", + "explorer": { + "name": "BaseScan", + "base_url": "https://sepolia.basescan.org" + } + }, + "deployment": { + "published_at": "2026-03-16T10:00:00.000Z", + "verdict_id": "verdict_live_promote_1", + "deployer": "0x1234567890abcdef1234567890abcdef12345678", + "contracts": { + "verifier": { + "contract": "DJDEvaluatorVerdictVerifier", + "address": "0x1111111111111111111111111111111111111111", + "tx_hash": "0x1111111111111111111111111111111111111111111111111111111111111111", + "action": "deploy" + }, + "escrow": { + "contract": "DJDEvaluatorEscrowSettlementExample", + "address": "0x2222222222222222222222222222222222222222", + "tx_hash": "0x2222222222222222222222222222222222222222222222222222222222222222", + "action": "deploy" + } + }, + "verification": { + "oracle_signer": "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", + "escrow_verifier": "0x1111111111111111111111111111111111111111", + "escrow_provider": "0x3333333333333333333333333333333333333333", + "escrow_counterparty": "0x4444444444444444444444444444444444444444", + "escrow_id_hash": "0x3333333333333333333333333333333333333333333333333333333333333333" + }, + "inputs": { + "provider": "0x3333333333333333333333333333333333333333", + "counterparty": "0x4444444444444444444444444444444444444444", + "escrow_id": "escrow-live-123" + }, + "checks": { + "preflight": true, + "verified": true, + "smoked": true, + "health": true, + "staged": true + }, + "explorer": { + "verifier_address": "https://sepolia.basescan.org/address/0x1111111111111111111111111111111111111111", + "verifier_transaction": "https://sepolia.basescan.org/tx/0x1111111111111111111111111111111111111111111111111111111111111111", + "escrow_address": "https://sepolia.basescan.org/address/0x2222222222222222222222222222222222222222", + "escrow_transaction": "https://sepolia.basescan.org/tx/0x2222222222222222222222222222222222222222222222222222222222222222" + }, + "links": { + "verifier_package": "https://api.example.test/v1/score/evaluator/verifier?network=base-sepolia", + "deployment_registry": "https://api.example.test/v1/score/evaluator/deployments?network=base-sepolia", + "verifier_proof": "https://api.example.test/v1/score/evaluator/proof?id=verdict_live_promote_1&network=base-sepolia", + "escrow_settlement": "https://api.example.test/v1/score/evaluator/escrow?id=verdict_live_promote_1&network=base-sepolia", + "deploy_bundle": "https://api.example.test/v1/score/evaluator/deploy/bundle?id=verdict_live_promote_1&network=base-sepolia" + } + }, + "outputs": { + "variables": { + "DJD_NETWORK": "base-sepolia", + "DJD_VERDICT_ID": "verdict_live_promote_1", + "DJD_VERIFIER_CONTRACT": "0x1111111111111111111111111111111111111111", + "DJD_ESCROW_CONTRACT": "0x2222222222222222222222222222222222222222", + "DJD_BASE_SEPOLIA_VERIFIER_CONTRACT": "0x1111111111111111111111111111111111111111" + }, + "generic": { + "DJD_NETWORK": "base-sepolia", + "DJD_VERIFIER_CONTRACT": "0x1111111111111111111111111111111111111111" + }, + "network_scoped": { + "DJD_BASE_SEPOLIA_VERIFIER_CONTRACT": "0x1111111111111111111111111111111111111111" + }, + "dotenv": "DJD_NETWORK=base-sepolia\nDJD_VERIFIER_CONTRACT=0x1111111111111111111111111111111111111111\n", + "shell": "export DJD_NETWORK='base-sepolia'\nexport DJD_VERIFIER_CONTRACT='0x1111111111111111111111111111111111111111'\n", + "github_output": "DJD_NETWORK=base-sepolia\nDJD_VERIFIER_CONTRACT=0x1111111111111111111111111111111111111111\n" + } + } + } + } + } + } + } + } + }, + "components": { + "parameters": { + "wallet": { + "name": "wallet", + "in": "query", + "required": true, + "description": "Base wallet address (EIP-55 checksummed or lowercase)", + "schema": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{40}$" + }, + "example": "0x3e4ef1f774857c69e33dddc471e110c7ac7bb528" + }, + "X-Request-ID": { + "name": "X-Request-ID", + "in": "header", + "required": false, + "description": "Client-provided request ID for tracing. If omitted, the server generates a UUID v4. Returned in the X-Request-ID response header.", + "schema": { + "type": "string", + "format": "uuid" + } + } + }, + "schemas": { + "Error": { + "type": "object", + "properties": { + "error": { + "type": "object", + "properties": { + "code": { + "type": "string", + "description": "Machine-readable error code" + }, + "message": { + "type": "string", + "description": "Human-readable error message" + }, + "details": { + "type": "object", + "description": "Optional additional context", + "additionalProperties": true + } + }, + "required": [ + "code", + "message" + ] + } + } + }, + "BasicScoreResponse": { + "type": "object", + "properties": { + "wallet": { + "type": "string" + }, + "score": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "tier": { + "type": "string", + "enum": [ + "Unverified", + "Emerging", + "Established", + "Trusted", + "Elite" + ] + }, + "confidence": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "recommendation": { + "type": "string", + "enum": [ + "proceed", + "proceed_with_caution", + "insufficient_history", + "high_risk", + "flagged_for_review", + "rpc_unavailable" + ] + }, + "modelVersion": { + "type": "string" + }, + "lastUpdated": { + "type": "string", + "format": "date-time" + }, + "computedAt": { + "type": "string", + "format": "date-time", + "description": "ISO-8601 timestamp of when the score was computed (may differ from lastUpdated when served from cache)" + }, + "scoreFreshness": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Freshness factor: 1 = just computed, decays linearly toward 0 at cache expiry. Consumers can use this to weight trust." + }, + "stale": { + "type": "boolean", + "description": "True when RPC was unavailable and a cached result was returned" + } + } + }, + "ERC8004CompatibleScoreResponse": { + "type": "object", + "properties": { + "standard": { + "type": "string", + "enum": [ + "erc-8004-compatible" + ] + }, + "wallet": { + "type": "string" + }, + "agent_id": { + "type": "string", + "description": "Decimal string form of uint256(uint160(wallet))" + }, + "provider": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "reputation_tag": { + "type": "string" + }, + "model_version": { + "type": "string" + }, + "document_url": { + "type": "string", + "format": "uri" + } + } + }, + "reputation": { + "type": "object", + "properties": { + "composite_score": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "tier": { + "type": "string", + "enum": [ + "Unverified", + "Emerging", + "Established", + "Trusted", + "Elite" + ] + }, + "confidence": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "recommendation": { + "type": "string" + }, + "computed_at": { + "type": "string", + "format": "date-time" + }, + "last_updated": { + "type": "string", + "format": "date-time" + }, + "score_freshness": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "data_source": { + "type": "string", + "enum": [ + "live", + "cached", + "unavailable" + ], + "nullable": true + } + } + }, + "identity": { + "type": "object", + "properties": { + "registered": { + "type": "boolean" + }, + "name": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "github_url": { + "type": "string", + "format": "uri", + "nullable": true + }, + "website_url": { + "type": "string", + "format": "uri", + "nullable": true + }, + "github_verified": { + "type": "boolean" + }, + "registered_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "erc8004_registered": { + "type": "boolean" + }, + "erc8004_registry_configured": { + "type": "boolean" + }, + "erc8004_registry_contract": { + "type": "string", + "nullable": true + } + } + }, + "certification": { + "type": "object", + "properties": { + "active": { + "type": "boolean" + }, + "tier": { + "type": "string", + "nullable": true + }, + "score_at_certification": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "nullable": true + }, + "granted_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "expires_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "tx_hash": { + "type": "string", + "nullable": true + } + } + }, + "publication": { + "type": "object", + "properties": { + "published": { + "type": "boolean" + }, + "registry": { + "type": "string", + "enum": [ + "erc-8004" + ] + }, + "endpoint": { + "type": "string", + "format": "uri" + }, + "tx_hash": { + "type": "string", + "nullable": true + }, + "published_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "score_at_publication": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "nullable": true + }, + "model_version": { + "type": "string", + "nullable": true + }, + "network": { + "type": "string", + "enum": [ + "base" + ] + }, + "chain_id": { + "type": "integer", + "example": 8453 + }, + "registry_contract": { + "type": "string" + }, + "feedback_hash": { + "type": "string", + "nullable": true + }, + "eligible_now": { + "type": "boolean" + }, + "eligibility_reasons": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "links": { + "type": "object", + "properties": { + "basic_score": { + "type": "string", + "format": "uri" + }, + "full_score": { + "type": "string", + "format": "uri" + }, + "certification_status": { + "type": "string", + "format": "uri" + }, + "certification_badge": { + "type": "string", + "format": "uri" + }, + "score_badge": { + "type": "string", + "format": "uri" + }, + "agent_profile": { + "type": "string", + "format": "uri" + } + } + } + } + }, + "EvaluatorPreviewResponse": { + "type": "object", + "properties": { + "standard": { + "type": "string", + "enum": [ + "erc-8183-evaluator-prototype" + ] + }, + "wallet": { + "type": "string" + }, + "decision": { + "type": "string", + "enum": [ + "approve", + "review", + "reject" + ] + }, + "confidence": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "rationale": { + "type": "string" + }, + "score": { + "type": "object", + "properties": { + "current_score": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "current_tier": { + "type": "string" + }, + "score_confidence": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "score_recommendation": { + "type": "string" + }, + "score_model_version": { + "type": "string" + }, + "last_scored_at": { + "type": "string", + "format": "date-time" + } + } + }, + "certification": { + "type": "object", + "properties": { + "active": { + "type": "boolean" + }, + "tier": { + "type": "string", + "nullable": true + }, + "granted_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "expires_at": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + }, + "risk": { + "type": "object", + "properties": { + "risk_score": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "risk_level": { + "type": "string", + "enum": [ + "clear", + "watch", + "elevated", + "critical" + ] + }, + "action": { + "type": "string", + "enum": [ + "allow", + "monitor", + "review", + "block" + ] + } + } + }, + "market_signals": { + "type": "object", + "properties": { + "rating_count": { + "type": "integer" + }, + "average_rating": { + "type": "number", + "nullable": true + }, + "unique_raters": { + "type": "integer" + }, + "intent_count": { + "type": "integer" + }, + "conversion_rate": { + "type": "number" + }, + "active_creator_stakes": { + "type": "integer" + }, + "active_score_boost": { + "type": "integer" + } + } + }, + "checks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "label": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pass", + "review", + "fail" + ] + }, + "details": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "links": { + "type": "object", + "properties": { + "full_score": { + "type": "string", + "format": "uri" + }, + "risk": { + "type": "string", + "format": "uri" + }, + "standards_document": { + "type": "string", + "format": "uri" + }, + "certification_status": { + "type": "string", + "format": "uri" + }, + "evaluator_preview": { + "type": "string", + "format": "uri" + }, + "forensics_summary": { + "type": "string", + "format": "uri" + }, + "forensics_timeline": { + "type": "string", + "format": "uri" + }, + "forensics_reports": { + "type": "string", + "format": "uri" + }, + "evidence_packet": { + "type": "string", + "format": "uri" + } + } + } + } + }, + "FullScoreResponse": { + "allOf": [ + { + "$ref": "#/components/schemas/BasicScoreResponse" + }, + { + "type": "object", + "properties": { + "sybilFlag": { + "type": "boolean" + }, + "gamingIndicators": { + "type": "array", + "items": { + "type": "string" + } + }, + "dimensions": { + "type": "object", + "properties": { + "reliability": { + "$ref": "#/components/schemas/Dimension" + }, + "viability": { + "$ref": "#/components/schemas/Dimension" + }, + "identity": { + "$ref": "#/components/schemas/Dimension" + }, + "capability": { + "$ref": "#/components/schemas/Dimension" + } + } + }, + "dataAvailability": { + "type": "object", + "properties": { + "transactionHistory": { + "type": "string" + }, + "walletAge": { + "type": "string" + }, + "economicData": { + "type": "string" + }, + "identityData": { + "type": "string" + }, + "communityData": { + "type": "string" + } + } + }, + "improvementPath": { + "type": "array", + "items": { + "type": "string" + } + }, + "scoreHistory": { + "type": "array", + "items": { + "type": "object", + "properties": { + "score": { + "type": "integer" + }, + "calculatedAt": { + "type": "string", + "format": "date-time" + }, + "modelVersion": { + "type": "string" + } + } + } + } + } + } + ] + }, + "RiskFactor": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "label": { + "type": "string" + }, + "severity": { + "type": "string", + "enum": [ + "low", + "medium", + "high", + "critical" + ] + }, + "contribution": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "details": { + "type": "object", + "additionalProperties": true + } + } + }, + "RiskPatternMatch": { + "type": "object", + "properties": { + "pattern_name": { + "type": "string" + }, + "risk_weight": { + "type": "number" + }, + "occurrences": { + "type": "integer" + }, + "first_detected": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "last_detected": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + }, + "RiskScoreResponse": { + "type": "object", + "properties": { + "wallet": { + "type": "string" + }, + "risk_score": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "risk_level": { + "type": "string", + "enum": [ + "clear", + "watch", + "elevated", + "critical" + ] + }, + "risk_confidence": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "action": { + "type": "string", + "enum": [ + "allow", + "monitor", + "review", + "block" + ] + }, + "current_score": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "current_tier": { + "type": "string", + "enum": [ + "Unverified", + "Emerging", + "Established", + "Trusted", + "Elite" + ] + }, + "score_confidence": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "score_recommendation": { + "type": "string", + "enum": [ + "proceed", + "proceed_with_caution", + "insufficient_history", + "high_risk", + "flagged_for_review", + "rpc_unavailable" + ] + }, + "score_model_version": { + "type": "string" + }, + "last_scored_at": { + "type": "string", + "format": "date-time" + }, + "summary": { + "type": "object", + "properties": { + "report_count": { + "type": "integer" + }, + "unique_reporters": { + "type": "integer" + }, + "total_penalty_applied": { + "type": "integer" + }, + "open_disputes": { + "type": "integer" + }, + "resolved_disputes": { + "type": "integer" + }, + "sybil_flagged": { + "type": "boolean" + }, + "sybil_indicators": { + "type": "array", + "items": { + "type": "string" + } + }, + "gaming_indicators": { + "type": "array", + "items": { + "type": "string" + } + }, + "rating_count": { + "type": "integer" + }, + "average_rating": { + "type": "number", + "nullable": true + }, + "unique_raters": { + "type": "integer" + }, + "intent_count": { + "type": "integer" + }, + "conversions": { + "type": "integer" + }, + "conversion_rate": { + "type": "number" + }, + "active_creator_stakes": { + "type": "integer" + }, + "active_staked_amount": { + "type": "number" + }, + "active_score_boost": { + "type": "integer" + }, + "slashed_creator_stakes": { + "type": "integer" + }, + "slashed_staked_amount": { + "type": "number" + }, + "reason_breakdown": { + "type": "array", + "items": { + "type": "object", + "properties": { + "reason": { + "type": "string" + }, + "count": { + "type": "integer" + } + } + } + } + } + }, + "factors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RiskFactor" + } + }, + "matched_patterns": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RiskPatternMatch" + } + } + } + }, + "ClusterMember": { + "type": "object", + "properties": { + "wallet": { + "type": "string" + }, + "cluster_name": { + "type": "string" + }, + "confidence": { + "type": "number" + }, + "assigned_at": { + "type": "string", + "format": "date-time" + }, + "current_score": { + "type": "integer", + "nullable": true + }, + "current_tier": { + "type": "string", + "nullable": true + } + } + }, + "ClusterLinkedWallet": { + "type": "object", + "properties": { + "rank": { + "type": "integer" + }, + "wallet": { + "type": "string" + }, + "total_tx_count": { + "type": "integer" + }, + "total_volume": { + "type": "number" + }, + "last_interaction": { + "type": "string", + "format": "date-time" + }, + "relationship_strength": { + "type": "string", + "enum": [ + "primary", + "secondary" + ] + } + } + }, + "ClusterResponse": { + "type": "object", + "properties": { + "wallet": { + "type": "string" + }, + "cluster_id": { + "type": "string" + }, + "cluster_name": { + "type": "string", + "enum": [ + "organic_network", + "repeat_counterparty_network", + "broker_hub", + "sybil_ring", + "fraud_hotspot", + "isolated_newcomer" + ] + }, + "confidence": { + "type": "number" + }, + "assigned_at": { + "type": "string", + "format": "date-time" + }, + "source": { + "type": "string", + "enum": [ + "inferred" + ] + }, + "member_count": { + "type": "integer" + }, + "current_score": { + "type": "integer" + }, + "current_tier": { + "type": "string" + }, + "risk_level": { + "type": "string", + "enum": [ + "clear", + "watch", + "elevated", + "critical" + ] + }, + "summary": { + "type": "object", + "properties": { + "counterparty_count": { + "type": "integer" + }, + "total_tx_count": { + "type": "integer" + }, + "total_volume": { + "type": "number" + }, + "report_count": { + "type": "integer" + }, + "unique_reporters": { + "type": "integer" + }, + "total_penalty_applied": { + "type": "integer" + }, + "sybil_flagged": { + "type": "boolean" + } + } + }, + "evidence": { + "type": "array", + "items": { + "type": "string" + } + }, + "members": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ClusterMember" + } + }, + "linked_wallets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ClusterLinkedWallet" + } + } + } + }, + "Dimension": { + "type": "object", + "properties": { + "score": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "data": { + "type": "object" + } + } + }, + "RegisterBody": { + "type": "object", + "required": [ + "wallet" + ], + "properties": { + "wallet": { + "type": "string", + "description": "Wallet address to register" + }, + "name": { + "type": "string", + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 500 + }, + "github_url": { + "type": "string", + "format": "uri", + "maxLength": 200 + }, + "website_url": { + "type": "string", + "format": "uri", + "maxLength": 200 + } + } + }, + "RegisterResponse": { + "type": "object", + "properties": { + "wallet": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "registered", + "updated" + ] + }, + "registeredAt": { + "type": "string", + "format": "date-time" + }, + "name": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "github_url": { + "type": "string", + "nullable": true + }, + "website_url": { + "type": "string", + "nullable": true + }, + "github_verified": { + "type": "boolean" + }, + "github_stars": { + "type": "integer", + "nullable": true + }, + "github_pushed_at": { + "type": "string", + "nullable": true + } + } + }, + "LeaderboardResponse": { + "type": "object", + "properties": { + "leaderboard": { + "type": "array", + "items": { + "type": "object", + "properties": { + "rank": { + "type": "integer" + }, + "wallet": { + "type": "string" + }, + "score": { + "type": "integer" + }, + "tier": { + "type": "string" + }, + "daysAlive": { + "type": "integer" + }, + "isRegistered": { + "type": "boolean" + }, + "githubVerified": { + "type": "boolean" + } + } + } + }, + "totalAgentsScored": { + "type": "integer" + }, + "totalAgentsRegistered": { + "type": "integer" + }, + "lastUpdated": { + "type": "string", + "format": "date-time" + } + } + }, + "ReportResponse": { + "type": "object", + "properties": { + "reportId": { + "type": "string", + "format": "uuid" + }, + "status": { + "type": "string", + "enum": [ + "accepted" + ] + }, + "targetCurrentScore": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "penaltyApplied": { + "type": "integer" + } + } + }, + "RatingRequest": { + "type": "object", + "required": [ + "rated_wallet", + "tx_hash", + "rating" + ], + "properties": { + "rated_wallet": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{40}$", + "description": "Wallet being rated" + }, + "tx_hash": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{64}$", + "description": "Indexed transaction hash between payer and rated wallet with at least $0.10 USDC of settlement" + }, + "rating": { + "type": "integer", + "minimum": 1, + "maximum": 5, + "description": "Star rating from 1 to 5" + }, + "comment": { + "type": "string", + "maxLength": 500, + "description": "Optional comment about the transaction" + } + } + }, + "RatingResponse": { + "type": "object", + "properties": { + "ratingId": { + "type": "string", + "format": "uuid" + }, + "status": { + "type": "string", + "enum": [ + "accepted" + ] + }, + "ratedWallet": { + "type": "string" + }, + "txHash": { + "type": "string" + }, + "rating": { + "type": "integer", + "minimum": 1, + "maximum": 5 + }, + "averageRating": { + "type": "number" + }, + "ratingCount": { + "type": "integer" + } + } + }, + "StakeRequest": { + "type": "object", + "required": [ + "agent_wallet", + "stake_tx_hash", + "fee_tx_hash" + ], + "properties": { + "agent_wallet": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{40}$", + "description": "Agent wallet receiving the creator stake" + }, + "stake_tx_hash": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{64}$", + "description": "Indexed USDC transfer hash for the creator -> agent stake transfer" + }, + "fee_tx_hash": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{64}$", + "description": "Indexed USDC transfer hash for the creator -> PAY_TO 1% DJD fee transfer" + } + } + }, + "StakeResponse": { + "type": "object", + "properties": { + "stakeId": { + "type": "string", + "format": "uuid" + }, + "status": { + "type": "string", + "enum": [ + "active", + "slashed" + ] + }, + "creatorWallet": { + "type": "string" + }, + "agentWallet": { + "type": "string" + }, + "stakeTxHash": { + "type": "string" + }, + "feeTxHash": { + "type": "string" + }, + "stakeAmount": { + "type": "number" + }, + "feeAmount": { + "type": "number" + }, + "scoreBoost": { + "type": "integer" + }, + "activeStakeCount": { + "type": "integer" + }, + "activeStakedAmount": { + "type": "number" + }, + "activeScoreBoost": { + "type": "integer" + } + } + }, + "BlacklistResponse": { + "type": "object", + "properties": { + "wallet": { + "type": "string" + }, + "reported": { + "type": "boolean", + "description": "True if the wallet has active fraud reports" + }, + "reportCount": { + "type": "integer" + }, + "mostRecentDate": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "reasons": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Unique report reason categories" + }, + "disputeStatus": { + "type": "string", + "enum": [ + "none", + "open", + "resolved" + ], + "description": "Aggregate dispute state for reports filed against this wallet" + } + } + }, + "DataDecayResponse": { + "type": "object", + "properties": { + "wallet": { + "type": "string" + }, + "current_score": { + "type": "integer", + "nullable": true + }, + "current_tier": { + "type": "string", + "nullable": true + }, + "decay": { + "type": "array", + "items": { + "type": "object", + "properties": { + "score": { + "type": "integer" + }, + "recorded_at": { + "type": "string", + "format": "date-time" + } + } + } + }, + "count": { + "type": "integer" + }, + "returned": { + "type": "integer" + }, + "period": { + "type": "object", + "properties": { + "from": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "to": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + }, + "trend": { + "type": "object", + "nullable": true, + "properties": { + "direction": { + "type": "string", + "enum": [ + "improving", + "declining", + "stable" + ] + }, + "change_pct": { + "type": "number" + }, + "avg_score": { + "type": "number" + }, + "min_score": { + "type": "integer" + }, + "max_score": { + "type": "integer" + } + } + }, + "trajectory": { + "type": "object", + "properties": { + "velocity": { + "type": "number", + "nullable": true + }, + "momentum": { + "type": "number", + "nullable": true + }, + "direction": { + "type": "string", + "enum": [ + "improving", + "declining", + "stable", + "volatile", + "new" + ] + }, + "volatility": { + "type": "number" + }, + "modifier": { + "type": "integer" + }, + "dataPoints": { + "type": "integer" + }, + "spanDays": { + "type": "number" + } + } + } + } + }, + "DataGraphCounterparty": { + "type": "object", + "properties": { + "rank": { + "type": "integer" + }, + "wallet": { + "type": "string" + }, + "tx_count_outbound": { + "type": "integer" + }, + "tx_count_inbound": { + "type": "integer" + }, + "total_tx_count": { + "type": "integer" + }, + "volume_outbound": { + "type": "number" + }, + "volume_inbound": { + "type": "number" + }, + "total_volume": { + "type": "number" + }, + "first_interaction": { + "type": "string", + "format": "date-time" + }, + "last_interaction": { + "type": "string", + "format": "date-time" + } + } + }, + "DataGraphResponse": { + "type": "object", + "properties": { + "wallet": { + "type": "string" + }, + "current_score": { + "type": "integer", + "nullable": true + }, + "current_tier": { + "type": "string", + "nullable": true + }, + "sybil_flagged": { + "type": "boolean" + }, + "counterparties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DataGraphCounterparty" + } + }, + "count": { + "type": "integer" + }, + "returned": { + "type": "integer" + }, + "summary": { + "type": "object", + "properties": { + "counterparty_count": { + "type": "integer" + }, + "outbound_tx_count": { + "type": "integer" + }, + "inbound_tx_count": { + "type": "integer" + }, + "total_tx_count": { + "type": "integer" + }, + "volume_outbound": { + "type": "number" + }, + "volume_inbound": { + "type": "number" + }, + "total_volume": { + "type": "number" + }, + "first_interaction": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "last_interaction": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + } + } + }, + "DataRatingsBreakdown": { + "type": "object", + "properties": { + "rating": { + "type": "integer", + "minimum": 1, + "maximum": 5 + }, + "count": { + "type": "integer" + } + } + }, + "DataRatingsItem": { + "type": "object", + "properties": { + "rating_id": { + "type": "string", + "format": "uuid" + }, + "rater_wallet": { + "type": "string" + }, + "tx_hash": { + "type": "string" + }, + "rating": { + "type": "integer", + "minimum": 1, + "maximum": 5 + }, + "comment": { + "type": "string", + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time" + } + } + }, + "DataRatingsResponse": { + "type": "object", + "properties": { + "wallet": { + "type": "string" + }, + "current_score": { + "type": "integer", + "nullable": true + }, + "current_tier": { + "type": "string", + "nullable": true + }, + "average_rating": { + "type": "number" + }, + "rating_count": { + "type": "integer" + }, + "unique_raters": { + "type": "integer" + }, + "most_recent_rating_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "breakdown": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DataRatingsBreakdown" + } + }, + "ratings": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DataRatingsItem" + } + }, + "count": { + "type": "integer" + }, + "returned": { + "type": "integer" + } + } + }, + "DataIntentTierBreakdown": { + "type": "object", + "properties": { + "tier_requested": { + "type": "string" + }, + "count": { + "type": "integer" + }, + "conversions": { + "type": "integer" + }, + "conversion_rate": { + "type": "number" + } + } + }, + "DataIntentItem": { + "type": "object", + "properties": { + "counterparty_wallet": { + "type": "string" + }, + "query_timestamp": { + "type": "string", + "format": "date-time" + }, + "followed_by_tx": { + "type": "boolean" + }, + "tx_hash": { + "type": "string", + "nullable": true + }, + "tx_timestamp": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "time_to_tx_ms": { + "type": "integer", + "nullable": true + }, + "endpoint": { + "type": "string", + "nullable": true + }, + "tier_requested": { + "type": "string", + "nullable": true + }, + "price_paid": { + "type": "number", + "nullable": true + } + } + }, + "DataIntentResponse": { + "type": "object", + "properties": { + "wallet": { + "type": "string" + }, + "current_score": { + "type": "integer", + "nullable": true + }, + "current_tier": { + "type": "string", + "nullable": true + }, + "summary": { + "type": "object", + "properties": { + "intent_count": { + "type": "integer" + }, + "conversions": { + "type": "integer" + }, + "conversion_rate": { + "type": "number" + }, + "avg_time_to_tx_ms": { + "type": "integer", + "nullable": true + }, + "avg_time_to_tx_hours": { + "type": "number", + "nullable": true + }, + "most_recent_query_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "most_recent_conversion_at": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + }, + "by_tier": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DataIntentTierBreakdown" + } + }, + "intents": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DataIntentItem" + } + }, + "count": { + "type": "integer" + }, + "returned": { + "type": "integer" + } + } + }, + "EconomySurvivalResponse": { + "type": "object", + "properties": { + "as_of": { + "type": "string", + "format": "date-time" + }, + "summary": { + "type": "object", + "properties": { + "total_wallets": { + "type": "integer" + }, + "active_7d": { + "type": "integer" + }, + "active_30d": { + "type": "integer" + }, + "dormant_30d": { + "type": "integer" + }, + "avg_days_since_last_seen": { + "type": "number", + "nullable": true + } + } + }, + "cohorts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "horizon_days": { + "type": "integer" + }, + "eligible_wallets": { + "type": "integer" + }, + "surviving_wallets": { + "type": "integer" + }, + "survival_rate": { + "type": "number" + } + } + } + }, + "by_tier": { + "type": "array", + "items": { + "type": "object", + "properties": { + "tier": { + "type": "string" + }, + "wallet_count": { + "type": "integer" + }, + "active_30d": { + "type": "integer" + }, + "survival_rate_30d": { + "type": "number" + } + } + } + }, + "at_risk_wallets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "wallet": { + "type": "string" + }, + "current_score": { + "type": "integer", + "nullable": true + }, + "current_tier": { + "type": "string", + "nullable": true + }, + "first_seen": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "last_seen": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "days_since_last_seen": { + "type": "number", + "nullable": true + }, + "score_change_30d": { + "type": "number", + "nullable": true + }, + "risk_bucket": { + "type": "string", + "enum": [ + "declining", + "dormant" + ] + } + } + } + }, + "returned": { + "type": "integer" + } + } + }, + "EconomySummaryResponse": { + "type": "object", + "properties": { + "period": { + "type": "string", + "enum": [ + "daily", + "weekly", + "monthly" + ] + }, + "limit": { + "type": "integer" + }, + "count": { + "type": "integer" + }, + "metrics": { + "type": "array", + "items": { + "type": "object", + "properties": { + "period_start": { + "type": "string", + "format": "date-time" + }, + "period_end": { + "type": "string", + "format": "date-time" + }, + "period_type": { + "type": "string" + }, + "total_wallets": { + "type": "integer" + }, + "new_wallets": { + "type": "integer" + }, + "dead_wallets": { + "type": "integer" + }, + "active_wallets": { + "type": "integer" + }, + "total_tx_count": { + "type": "integer" + }, + "total_volume": { + "type": "number" + }, + "avg_tx_size": { + "type": "number" + }, + "median_score": { + "type": "number" + }, + "avg_score": { + "type": "number" + }, + "elite_count": { + "type": "integer" + }, + "trusted_count": { + "type": "integer" + }, + "established_count": { + "type": "integer" + }, + "emerging_count": { + "type": "integer" + }, + "unverified_count": { + "type": "integer" + }, + "total_fraud_reports": { + "type": "integer" + }, + "total_queries": { + "type": "integer" + } + } + } + } + } + }, + "EconomyVolumeResponse": { + "type": "object", + "properties": { + "period": { + "type": "string", + "enum": [ + "daily", + "weekly", + "monthly" + ] + }, + "limit": { + "type": "integer" + }, + "count": { + "type": "integer" + }, + "series": { + "type": "array", + "items": { + "type": "object", + "properties": { + "period_start": { + "type": "string", + "format": "date-time" + }, + "period_end": { + "type": "string", + "format": "date-time" + }, + "total_tx_count": { + "type": "integer" + }, + "total_volume": { + "type": "number" + }, + "avg_tx_size": { + "type": "number" + }, + "active_wallets": { + "type": "integer" + }, + "new_wallets": { + "type": "integer" + } + } + } + } + } + }, + "FraudDisputeRequest": { + "type": "object", + "required": [ + "report_id", + "reason", + "details" + ], + "properties": { + "report_id": { + "type": "string", + "description": "Fraud report ID from the reports or feed endpoints" + }, + "reason": { + "type": "string", + "enum": [ + "fulfilled_service", + "mistaken_identity", + "resolved_offchain", + "inaccurate_report", + "other" + ] + }, + "details": { + "type": "string", + "description": "Supporting explanation or evidence for the dispute" + } + } + }, + "FraudDisputeResponse": { + "type": "object", + "properties": { + "disputeId": { + "type": "string", + "format": "uuid" + }, + "status": { + "type": "string", + "enum": [ + "open" + ] + }, + "reportId": { + "type": "string" + }, + "targetWallet": { + "type": "string" + } + } + }, + "ApiKeyCreatedResponse": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "Auto-incremented key ID" + }, + "key": { + "type": "string", + "description": "The raw API key (djd_live_...) — returned ONLY on creation" + }, + "key_prefix": { + "type": "string", + "description": "First 16 characters of the key for identification" + }, + "wallet": { + "type": "string" + }, + "name": { + "type": "string", + "nullable": true + }, + "tier": { + "type": "string" + }, + "monthly_limit": { + "type": "integer" + }, + "usage_reset_at": { + "type": "string", + "format": "date-time" + }, + "message": { + "type": "string" + } + } + }, + "ApiKeyListItem": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "key_prefix": { + "type": "string" + }, + "wallet": { + "type": "string" + }, + "name": { + "type": "string", + "nullable": true + }, + "tier": { + "type": "string" + }, + "monthly_limit": { + "type": "integer" + }, + "monthly_used": { + "type": "integer" + }, + "usage_reset_at": { + "type": "string", + "format": "date-time" + }, + "is_active": { + "type": "integer", + "enum": [ + 0, + 1 + ] + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "last_used_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "revoked_at": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + }, + "WebhookCreatedResponse": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "wallet": { + "type": "string" + }, + "url": { + "type": "string" + }, + "secret": { + "type": "string", + "description": "HMAC secret for verifying webhook signatures — returned ONLY on creation" + }, + "events": { + "type": "array", + "items": { + "type": "string" + } + }, + "threshold_score": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "nullable": true + }, + "forensics_filter": { + "$ref": "#/components/schemas/WebhookForensicsFilter" + }, + "presets_applied": { + "type": "array", + "items": { + "type": "string" + } + }, + "tier": { + "type": "string" + }, + "message": { + "type": "string" + } + } + }, + "PublicWebhookCreatedResponse": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "url": { + "type": "string" + }, + "secret": { + "type": "string", + "description": "HMAC secret for verifying webhook signatures — returned ONLY on creation" + }, + "events": { + "type": "array", + "items": { + "type": "string" + } + }, + "threshold_score": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "nullable": true + }, + "forensics_filter": { + "$ref": "#/components/schemas/WebhookForensicsFilter" + }, + "presets_applied": { + "type": "array", + "items": { + "type": "string" + } + }, + "message": { + "type": "string" + } + } + }, + "WebhookListItem": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "wallet": { + "type": "string" + }, + "url": { + "type": "string" + }, + "events": { + "type": "array", + "items": { + "type": "string" + } + }, + "tier": { + "type": "string" + }, + "threshold_score": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "nullable": true + }, + "forensics_filter": { + "$ref": "#/components/schemas/WebhookForensicsFilter" + }, + "is_active": { + "type": "integer", + "enum": [ + 0, + 1 + ] + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "failure_count": { + "type": "integer" + }, + "last_delivery_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "disabled_at": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + }, + "PublicWebhookListItem": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "url": { + "type": "string" + }, + "events": { + "type": "array", + "items": { + "type": "string" + } + }, + "tier": { + "type": "string" + }, + "threshold_score": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "nullable": true + }, + "forensics_filter": { + "$ref": "#/components/schemas/WebhookForensicsFilter" + }, + "is_active": { + "type": "integer", + "enum": [ + 0, + 1 + ] + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "failure_count": { + "type": "integer" + }, + "last_delivery_at": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + }, + "WebhookDetailResponse": { + "allOf": [ + { + "$ref": "#/components/schemas/WebhookCreatedResponse" + }, + { + "type": "object", + "properties": { + "is_active": { + "type": "integer", + "enum": [ + 0, + 1 + ] + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "failure_count": { + "type": "integer" + }, + "last_delivery_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "disabled_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "recent_deliveries": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "event_type": { + "type": "string" + }, + "status_code": { + "type": "integer", + "nullable": true + }, + "attempt": { + "type": "integer" + }, + "delivered_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time" + } + } + } + } + } + } + ] + }, + "WebhookPreset": { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "score_monitoring", + "forensics_monitoring", + "forensics_disputes", + "forensics_watchlist", + "anomaly_monitoring" + ] + }, + "description": { + "type": "string" + }, + "events": { + "type": "array", + "items": { + "type": "string" + } + }, + "threshold_score_default": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "nullable": true + } + } + }, + "WebhookForensicsFilter": { + "type": "object", + "nullable": true, + "properties": { + "minimum_risk_level": { + "type": "string", + "enum": [ + "clear", + "watch", + "elevated", + "critical" + ], + "description": "Only emit Forensics events whose resulting risk level meets or exceeds this threshold." + }, + "reasons": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "failed_delivery", + "payment_fraud", + "impersonation", + "malicious_behavior", + "other" + ] + }, + "description": "Only emit Forensics events tied to these report reasons." + } + } + }, + "MonitoringPreset": { + "type": "object", + "properties": { + "policy_type": { + "type": "string", + "enum": [ + "score_monitoring", + "forensics_monitoring", + "forensics_disputes", + "forensics_watchlist", + "anomaly_monitoring" + ] + }, + "description": { + "type": "string" + }, + "events": { + "type": "array", + "items": { + "type": "string" + } + }, + "threshold_score_default": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "nullable": true + }, + "supports_threshold_score": { + "type": "boolean" + }, + "supports_forensics_filter": { + "type": "boolean" + } + } + }, + "MonitoringSubscription": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "target_wallet": { + "type": "string" + }, + "policy_type": { + "type": "string", + "enum": [ + "score_monitoring", + "forensics_monitoring", + "forensics_disputes", + "forensics_watchlist", + "anomaly_monitoring" + ] + }, + "url": { + "type": "string" + }, + "events": { + "type": "array", + "items": { + "type": "string" + } + }, + "threshold_score": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "nullable": true + }, + "forensics_filter": { + "$ref": "#/components/schemas/WebhookForensicsFilter" + }, + "is_active": { + "type": "boolean" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "disabled_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "failure_count": { + "type": "integer" + }, + "last_delivery_at": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + }, + "MonitoringSubscriptionCreatedResponse": { + "allOf": [ + { + "$ref": "#/components/schemas/MonitoringSubscription" + }, + { + "type": "object", + "properties": { + "secret": { + "type": "string", + "description": "HMAC secret for verifying webhook signatures — returned ONLY on creation" + }, + "presets_applied": { + "type": "array", + "items": { + "type": "string" + } + }, + "message": { + "type": "string" + } + } + } + ] + }, + "ScoreHistoryResponse": { + "type": "object", + "properties": { + "wallet": { + "type": "string" + }, + "history": { + "type": "array", + "items": { + "type": "object", + "properties": { + "score": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "confidence": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "model_version": { + "type": "string" + }, + "calculated_at": { + "type": "string", + "format": "date-time" + } + } + } + }, + "count": { + "type": "integer", + "description": "Total records matching the query" + }, + "returned": { + "type": "integer", + "description": "Number of records in this response" + }, + "period": { + "type": "object", + "properties": { + "from": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "to": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + }, + "trend": { + "type": "object", + "nullable": true, + "description": "Trend analysis (present when 2+ data points exist)", + "properties": { + "direction": { + "type": "string", + "enum": [ + "improving", + "declining", + "stable" + ] + }, + "change_pct": { + "type": "number", + "description": "Percentage change from earliest to latest score" + }, + "avg_score": { + "type": "number" + }, + "min_score": { + "type": "integer" + }, + "max_score": { + "type": "integer" + } + } + } + } + }, + "ForensicsOverviewResponse": { + "type": "object", + "properties": { + "wallet": { + "type": "string" + }, + "risk_level": { + "type": "string", + "enum": [ + "clear", + "watch", + "elevated", + "critical" + ] + }, + "current_score": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "nullable": true + }, + "current_tier": { + "type": "string", + "nullable": true + }, + "report_count": { + "type": "integer" + }, + "total_penalty_applied": { + "type": "integer" + }, + "unique_reporters": { + "type": "integer" + }, + "most_recent_report_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "dispute_status": { + "type": "string", + "enum": [ + "none", + "open", + "resolved" + ] + }, + "open_disputes": { + "type": "integer" + }, + "resolved_disputes": { + "type": "integer" + }, + "score_history_entries": { + "type": "integer" + }, + "reasons": { + "type": "array", + "items": { + "type": "object", + "properties": { + "reason": { + "type": "string" + }, + "count": { + "type": "integer" + } + } + } + }, + "recent_reports": { + "type": "array", + "items": { + "type": "object", + "properties": { + "report_id": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "penalty_applied": { + "type": "integer" + } + } + } + } + } + }, + "ForensicsTimelineResponse": { + "type": "object", + "properties": { + "wallet": { + "type": "string" + }, + "risk_level": { + "type": "string", + "enum": [ + "clear", + "watch", + "elevated", + "critical" + ] + }, + "events": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "score_snapshot" + ] + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "score": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "confidence": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "model_version": { + "type": "string" + } + } + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "fraud_report" + ] + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "report_id": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "penalty_applied": { + "type": "integer" + } + } + } + ] + } + }, + "count": { + "type": "integer", + "description": "Total combined events matching the query" + }, + "returned": { + "type": "integer", + "description": "Number of events in this response" + }, + "breakdown": { + "type": "object", + "properties": { + "score_snapshots": { + "type": "integer" + }, + "fraud_reports": { + "type": "integer" + } + } + }, + "report_summary": { + "type": "object", + "properties": { + "report_count": { + "type": "integer" + }, + "total_penalty_applied": { + "type": "integer" + } + } + }, + "period": { + "type": "object", + "properties": { + "from": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "to": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + }, + "trend": { + "type": "object", + "nullable": true, + "properties": { + "direction": { + "type": "string", + "enum": [ + "improving", + "declining", + "stable" + ] + }, + "change_pct": { + "type": "number" + }, + "avg_score": { + "type": "number" + }, + "min_score": { + "type": "integer" + }, + "max_score": { + "type": "integer" + } + } + }, + "trajectory": { + "type": "object", + "properties": { + "velocity": { + "type": "number", + "nullable": true + }, + "momentum": { + "type": "number", + "nullable": true + }, + "direction": { + "type": "string", + "enum": [ + "improving", + "declining", + "stable", + "volatile", + "new" + ] + }, + "volatility": { + "type": "number" + }, + "modifier": { + "type": "integer" + }, + "dataPoints": { + "type": "integer" + }, + "spanDays": { + "type": "number" + } + } + } + } + }, + "ForensicsReportsResponse": { + "type": "object", + "properties": { + "wallet": { + "type": "string" + }, + "risk_level": { + "type": "string", + "enum": [ + "clear", + "watch", + "elevated", + "critical" + ] + }, + "reports": { + "type": "array", + "items": { + "type": "object", + "properties": { + "report_id": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "details": { + "type": "string" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "penalty_applied": { + "type": "integer" + } + } + } + }, + "count": { + "type": "integer", + "description": "Total reports matching the query" + }, + "returned": { + "type": "integer", + "description": "Number of reports in this response" + }, + "unique_reporters": { + "type": "integer" + }, + "total_penalty_applied": { + "type": "integer" + }, + "period": { + "type": "object", + "properties": { + "from": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "to": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + } + } + }, + "ForensicsWatchlistResponse": { + "type": "object", + "properties": { + "wallets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "rank": { + "type": "integer" + }, + "wallet": { + "type": "string" + }, + "risk_level": { + "type": "string", + "enum": [ + "clear", + "watch", + "elevated", + "critical" + ] + }, + "current_score": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "nullable": true + }, + "current_tier": { + "type": "string", + "nullable": true + }, + "report_count": { + "type": "integer" + }, + "unique_reporters": { + "type": "integer" + }, + "total_penalty_applied": { + "type": "integer" + }, + "most_recent_report_at": { + "type": "string", + "format": "date-time" + } + } + } + }, + "count": { + "type": "integer", + "description": "Total wallets matching the query" + }, + "returned": { + "type": "integer", + "description": "Number of wallets in this response" + }, + "period": { + "type": "object", + "properties": { + "from": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "to": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + } + } + }, + "ForensicsFeedResponse": { + "type": "object", + "properties": { + "incidents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "report_id": { + "type": "string" + }, + "wallet": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "details": { + "type": "string" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "penalty_applied": { + "type": "integer" + }, + "current_score": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "nullable": true + }, + "current_tier": { + "type": "string", + "nullable": true + }, + "risk_level": { + "type": "string", + "enum": [ + "clear", + "watch", + "elevated", + "critical" + ] + }, + "report_count": { + "type": "integer" + }, + "unique_reporters": { + "type": "integer" + }, + "total_penalty_applied": { + "type": "integer" + } + } + } + }, + "count": { + "type": "integer", + "description": "Total incidents matching the query" + }, + "returned": { + "type": "integer", + "description": "Number of incidents in this response" + }, + "reason_filter": { + "type": "string", + "nullable": true + }, + "period": { + "type": "object", + "properties": { + "from": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "to": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + } + } + }, + "CertificationGrantedResponse": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "wallet": { + "type": "string" + }, + "tier": { + "type": "string" + }, + "score_at_certification": { + "type": "integer" + }, + "granted_at": { + "type": "string", + "format": "date-time" + }, + "expires_at": { + "type": "string", + "format": "date-time" + }, + "is_active": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "links": { + "type": "object", + "properties": { + "certification_badge": { + "type": "string", + "format": "uri" + }, + "score_badge": { + "type": "string", + "format": "uri" + }, + "standards_document": { + "type": "string", + "format": "uri" + }, + "evaluator_preview": { + "type": "string", + "format": "uri" + }, + "agent_profile": { + "type": "string", + "format": "uri" + }, + "certify_readiness": { + "type": "string", + "format": "uri" + } + } + }, + "price_paid_usdc": { + "type": "number" + } + } + }, + "CertificationStatusResponse": { + "type": "object", + "properties": { + "wallet": { + "type": "string" + }, + "tier": { + "type": "string" + }, + "score_at_certification": { + "type": "integer" + }, + "granted_at": { + "type": "string", + "format": "date-time" + }, + "expires_at": { + "type": "string", + "format": "date-time" + }, + "is_valid": { + "type": "boolean" + }, + "links": { + "type": "object", + "properties": { + "certification_badge": { + "type": "string", + "format": "uri" + }, + "score_badge": { + "type": "string", + "format": "uri" + }, + "standards_document": { + "type": "string", + "format": "uri" + }, + "evaluator_preview": { + "type": "string", + "format": "uri" + }, + "agent_profile": { + "type": "string", + "format": "uri" + }, + "certify_readiness": { + "type": "string", + "format": "uri" + } + } + }, + "price_paid_usdc": { + "type": "number" + } + } + }, + "CertificationReadinessResponse": { + "type": "object", + "properties": { + "wallet": { + "type": "string" + }, + "can_apply": { + "type": "boolean" + }, + "status": { + "type": "string", + "enum": [ + "eligible", + "already_certified", + "not_registered", + "score_missing", + "score_expired", + "score_too_low", + "review_pending", + "review_approved", + "review_needs_info", + "review_rejected" + ] + }, + "requirements": { + "type": "object", + "properties": { + "registration": { + "type": "object", + "properties": { + "met": { + "type": "boolean" + } + } + }, + "score": { + "type": "object", + "properties": { + "met": { + "type": "boolean" + }, + "minimum_score": { + "type": "integer" + }, + "current_score": { + "type": "integer", + "nullable": true + }, + "current_tier": { + "type": "string", + "nullable": true + }, + "confidence": { + "type": "number", + "nullable": true + }, + "expires_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "is_fresh": { + "type": "boolean" + } + } + }, + "certification": { + "type": "object", + "properties": { + "active": { + "type": "boolean" + }, + "tier": { + "type": "string", + "nullable": true + }, + "granted_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "expires_at": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + }, + "review": { + "type": "object", + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "type": "string", + "nullable": true, + "enum": [ + "pending", + "approved", + "needs_info", + "rejected" + ] + }, + "requested_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "reviewed_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "reviewed_by": { + "type": "string", + "nullable": true + }, + "review_note": { + "type": "string", + "nullable": true + } + } + } + } + }, + "blockers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } + }, + "next_steps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "label": { + "type": "string" + }, + "href": { + "type": "string", + "format": "uri" + } + } + } + }, + "payment": { + "type": "object", + "properties": { + "protocol": { + "type": "string" + }, + "amount_usdc": { + "type": "number" + }, + "endpoint": { + "type": "string", + "format": "uri" + } + } + }, + "links": { + "type": "object", + "properties": { + "certification_status": { + "type": "string", + "format": "uri" + }, + "certification_badge": { + "type": "string", + "format": "uri" + }, + "score_badge": { + "type": "string", + "format": "uri" + }, + "standards_document": { + "type": "string", + "format": "uri" + }, + "evaluator_preview": { + "type": "string", + "format": "uri" + }, + "agent_profile": { + "type": "string", + "format": "uri" + }, + "certify_readiness": { + "type": "string", + "format": "uri" + }, + "certify_overview": { + "type": "string", + "format": "uri" + }, + "certified_directory": { + "type": "string", + "format": "uri" + }, + "apply_endpoint": { + "type": "string", + "format": "uri" + }, + "review_status": { + "type": "string", + "format": "uri" + } + } + }, + "requested_tier": { + "$ref": "#/components/schemas/CertificationTierSummary" + }, + "eligible_tiers": { + "type": "array", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/CertificationTierSummary" + }, + { + "type": "object", + "properties": { + "eligible": { + "type": "boolean" + }, + "score_gap": { + "type": "integer" + } + } + } + ] + } + } + } + }, + "CertificationReviewRequest": { + "type": "object", + "required": [ + "wallet" + ], + "properties": { + "wallet": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{40}$" + }, + "note": { + "type": "string", + "maxLength": 500 + }, + "tier": { + "type": "string", + "enum": [ + "operational", + "transactional", + "autonomous" + ] + } + } + }, + "CertificationReviewResponse": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "wallet": { + "type": "string" + }, + "requested_by_wallet": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pending", + "approved", + "needs_info", + "rejected" + ] + }, + "requested_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "reviewed_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "reviewed_by": { + "type": "string", + "nullable": true + }, + "requested_score": { + "type": "integer" + }, + "requested_tier": { + "type": "string" + }, + "requested_confidence": { + "type": "number", + "nullable": true + }, + "score_expires_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "request_note": { + "type": "string", + "nullable": true + }, + "review_note": { + "type": "string", + "nullable": true + }, + "current_score": { + "type": "object", + "properties": { + "score": { + "type": "integer", + "nullable": true + }, + "tier": { + "type": "string", + "nullable": true + }, + "confidence": { + "type": "number", + "nullable": true + } + } + }, + "profile": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "github_url": { + "type": "string", + "format": "uri", + "nullable": true + }, + "website_url": { + "type": "string", + "format": "uri", + "nullable": true + }, + "github_verified": { + "type": "boolean" + } + } + }, + "links": { + "type": "object", + "properties": { + "agent_profile": { + "type": "string", + "format": "uri" + }, + "readiness": { + "type": "string", + "format": "uri" + }, + "review_status": { + "type": "string", + "format": "uri" + }, + "apply_endpoint": { + "type": "string", + "format": "uri" + }, + "certified_directory": { + "type": "string", + "format": "uri" + } + } + }, + "message": { + "type": "string" + } + } + }, + "CertificationDirectoryResponse": { + "type": "object", + "properties": { + "as_of": { + "type": "string", + "format": "date-time" + }, + "filters": { + "type": "object", + "properties": { + "limit": { + "type": "integer" + }, + "tier": { + "type": "string", + "nullable": true + }, + "search": { + "type": "string", + "nullable": true + }, + "sort": { + "type": "string", + "enum": [ + "score", + "confidence", + "recent", + "name" + ] + } + } + }, + "total": { + "type": "integer" + }, + "returned": { + "type": "integer" + }, + "certifications": { + "type": "array", + "items": { + "type": "object", + "properties": { + "wallet": { + "type": "string" + }, + "certification": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "tier": { + "type": "string" + }, + "score_at_certification": { + "type": "integer" + }, + "granted_at": { + "type": "string", + "format": "date-time" + }, + "expires_at": { + "type": "string", + "format": "date-time" + } + } + }, + "current_score": { + "type": "object", + "properties": { + "score": { + "type": "integer", + "nullable": true + }, + "tier": { + "type": "string", + "nullable": true + }, + "confidence": { + "type": "number", + "nullable": true + } + } + }, + "profile": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "github_url": { + "type": "string", + "format": "uri", + "nullable": true + }, + "website_url": { + "type": "string", + "format": "uri", + "nullable": true + }, + "github_verified": { + "type": "boolean" + } + } + }, + "links": { + "type": "object", + "properties": { + "certification_badge": { + "type": "string", + "format": "uri" + }, + "score_badge": { + "type": "string", + "format": "uri" + }, + "standards_document": { + "type": "string", + "format": "uri" + }, + "evaluator_preview": { + "type": "string", + "format": "uri" + }, + "agent_profile": { + "type": "string", + "format": "uri" + }, + "certify_readiness": { + "type": "string", + "format": "uri" + } + } + } + } + } + } + } + }, + "CertificationAdminItem": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "wallet": { + "type": "string" + }, + "tier": { + "type": "string" + }, + "score_at_certification": { + "type": "integer" + }, + "granted_at": { + "type": "string", + "format": "date-time" + }, + "expires_at": { + "type": "string", + "format": "date-time" + }, + "is_active": { + "type": "boolean" + }, + "revoked_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "revocation_reason": { + "type": "string", + "nullable": true + } + } + }, + "CertificationRevenueResponse": { + "type": "object", + "properties": { + "total_certifications": { + "type": "integer" + }, + "active_certifications": { + "type": "integer" + }, + "revoked_certifications": { + "type": "integer" + }, + "gross_revenue_usd": { + "type": "number" + }, + "net_revenue_usd": { + "type": "number" + }, + "price_per_cert_usd": { + "type": "number", + "example": 200 + }, + "by_month": { + "type": "array", + "items": { "type": "object", "properties": { - "key": { "type": "string" }, - "label": { "type": "string" }, - "status": { "type": "string", "enum": ["pass", "review", "fail"] }, - "details": { "type": "object", "additionalProperties": true } + "month": { + "type": "string", + "example": "2026-02" + }, + "count": { + "type": "integer" + }, + "revoked_count": { + "type": "integer" + }, + "gross_revenue_usd": { + "type": "number" + }, + "net_revenue_usd": { + "type": "number" + } } } + } + } + }, + "CertificationTierSummary": { + "type": "object", + "properties": { + "key": { + "type": "string", + "enum": [ + "operational", + "transactional", + "autonomous" + ] + }, + "label": { + "type": "string", + "enum": [ + "Operational", + "Transactional", + "Autonomous" + ] + }, + "level": { + "type": "integer", + "enum": [ + 1, + 2, + 3 + ] + }, + "minimum_score": { + "type": "integer" + }, + "price_usdc": { + "type": "number" + }, + "summary": { + "type": "string" + }, + "controls": { + "type": "array", + "items": { + "type": "string" + } }, - "links": { - "type": "object", - "properties": { - "full_score": { "type": "string", "format": "uri" }, - "risk": { "type": "string", "format": "uri" }, - "standards_document": { "type": "string", "format": "uri" }, - "certification_status": { "type": "string", "format": "uri" } + "apply_endpoint": { + "type": "string", + "format": "uri" + } + } + }, + "CertificationTierCatalogResponse": { + "type": "object", + "properties": { + "tiers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CertificationTierSummary" } + }, + "default_tier": { + "type": "string", + "enum": [ + "operational", + "transactional", + "autonomous" + ] } } }, - "FullScoreResponse": { - "allOf": [ - { "$ref": "#/components/schemas/BasicScoreResponse" }, - { + "EvaluatorEvidencePacketResponse": { + "type": "object", + "properties": { + "standard": { + "type": "string", + "enum": [ + "erc-8183-evaluator-evidence-prototype" + ] + }, + "wallet": { + "type": "string" + }, + "packet_id": { + "type": "string" + }, + "packet_hash": { + "type": "string" + }, + "generated_at": { + "type": "string", + "format": "date-time" + }, + "evaluator": { "type": "object", "properties": { - "sybilFlag": { "type": "boolean" }, - "gamingIndicators": { "type": "array", "items": { "type": "string" } }, - "dimensions": { + "decision": { + "type": "string", + "enum": [ + "approve", + "review", + "reject" + ] + }, + "confidence": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "rationale": { + "type": "string" + } + } + }, + "baseline": { + "type": "object", + "properties": { + "profile": { + "type": "string", + "enum": [ + "djd-transactional-settlement-v1" + ] + }, + "settlement_tier": { + "type": "string" + }, + "score_floor": { + "type": "integer" + }, + "certification_floor": { + "type": "string" + }, + "review_triggers": { + "type": "array", + "items": { + "type": "string" + } + }, + "reject_triggers": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "evidence": { + "type": "object", + "properties": { + "score": { + "type": "object", + "properties": { + "current_score": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "current_tier": { + "type": "string" + }, + "score_confidence": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "score_recommendation": { + "type": "string" + }, + "score_model_version": { + "type": "string" + }, + "last_scored_at": { + "type": "string", + "format": "date-time" + } + } + }, + "certification": { "type": "object", "properties": { - "reliability": { "$ref": "#/components/schemas/Dimension" }, - "viability": { "$ref": "#/components/schemas/Dimension" }, - "identity": { "$ref": "#/components/schemas/Dimension" }, - "capability": { "$ref": "#/components/schemas/Dimension" } + "active": { + "type": "boolean" + }, + "tier": { + "type": "string", + "nullable": true + }, + "granted_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "expires_at": { + "type": "string", + "format": "date-time", + "nullable": true + } } }, - "dataAvailability": { + "risk": { "type": "object", "properties": { - "transactionHistory": { "type": "string" }, - "walletAge": { "type": "string" }, - "economicData": { "type": "string" }, - "identityData": { "type": "string" }, - "communityData": { "type": "string" } + "risk_score": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "risk_level": { + "type": "string", + "enum": [ + "clear", + "watch", + "elevated", + "critical" + ] + }, + "action": { + "type": "string", + "enum": [ + "allow", + "monitor", + "review", + "block" + ] + } } }, - "improvementPath": { "type": "array", "items": { "type": "string" } }, - "scoreHistory": { + "market_signals": { + "type": "object", + "properties": { + "rating_count": { + "type": "integer" + }, + "average_rating": { + "type": "number", + "nullable": true + }, + "unique_raters": { + "type": "integer" + }, + "intent_count": { + "type": "integer" + }, + "conversion_rate": { + "type": "number" + }, + "active_creator_stakes": { + "type": "integer" + }, + "active_score_boost": { + "type": "integer" + } + } + }, + "checks": { "type": "array", "items": { "type": "object", "properties": { - "score": { "type": "integer" }, - "calculatedAt": { "type": "string", "format": "date-time" }, - "modelVersion": { "type": "string" } + "key": { + "type": "string" + }, + "label": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pass", + "review", + "fail" + ] + }, + "details": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "forensics": { + "type": "object", + "properties": { + "score_history_entries": { + "type": "integer" + }, + "report_count": { + "type": "integer" + }, + "unique_reporters": { + "type": "integer" + }, + "total_penalty_applied": { + "type": "integer" + }, + "open_disputes": { + "type": "integer" + }, + "resolved_disputes": { + "type": "integer" + }, + "reason_breakdown": { + "type": "array", + "items": { + "type": "object", + "properties": { + "reason": { + "type": "string" + }, + "count": { + "type": "integer" + } + } + } + }, + "recent_reports": { + "type": "array", + "items": { + "type": "object", + "properties": { + "report_id": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "penalty_applied": { + "type": "integer" + } + } + } + } + } + } + } + }, + "artifacts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "label": { + "type": "string" + }, + "category": { + "type": "string", + "enum": [ + "score", + "identity", + "certification", + "forensics", + "market" + ] + }, + "status": { + "type": "string", + "enum": [ + "included", + "recommended", + "missing" + ] + }, + "href": { + "type": "string", + "format": "uri" + }, + "summary": { + "type": "string" + } + } + } + }, + "links": { + "type": "object", + "properties": { + "full_score": { + "type": "string", + "format": "uri" + }, + "risk": { + "type": "string", + "format": "uri" + }, + "standards_document": { + "type": "string", + "format": "uri" + }, + "certification_status": { + "type": "string", + "format": "uri" + }, + "evaluator_preview": { + "type": "string", + "format": "uri" + }, + "forensics_summary": { + "type": "string", + "format": "uri" + }, + "forensics_timeline": { + "type": "string", + "format": "uri" + }, + "forensics_reports": { + "type": "string", + "format": "uri" + }, + "evidence_packet": { + "type": "string", + "format": "uri" + } + } + } + } + }, + "EvaluatorOracleResponse": { + "type": "object", + "properties": { + "standard": { + "type": "string", + "enum": [ + "erc-8183-evaluator-oracle-prototype" + ] + }, + "verdict_id": { + "type": "string" + }, + "wallet": { + "type": "string" + }, + "counterparty_wallet": { + "type": "string", + "nullable": true + }, + "escrow_id": { + "type": "string", + "nullable": true + }, + "decision": { + "type": "string", + "enum": [ + "approve", + "review", + "reject" + ] + }, + "approved": { + "type": "boolean" + }, + "recommendation": { + "type": "string", + "enum": [ + "release", + "manual_review", + "dispute", + "reject" + ] + }, + "confidence": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "agent_score_provider": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "score_model_version": { + "type": "string" + }, + "certification_valid": { + "type": "boolean" + }, + "certification_tier": { + "type": "string", + "nullable": true + }, + "risk_level": { + "type": "string", + "enum": [ + "clear", + "watch", + "elevated", + "critical" + ] + }, + "risk_score": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "sla_metrics": { + "type": "object", + "properties": { + "baseline_profile": { + "type": "string", + "enum": [ + "djd-transactional-settlement-v1" + ] + }, + "settlement_tier": { + "type": "string" + }, + "score_floor": { + "type": "integer" + }, + "score_floor_passed": { + "type": "boolean" + }, + "certification_floor": { + "type": "string" + }, + "certification_floor_passed": { + "type": "boolean" + }, + "risk_guardrail_passed": { + "type": "boolean" + }, + "dispute_guardrail_passed": { + "type": "boolean" + }, + "failed_checks": { + "type": "array", + "items": { + "type": "string" + } + }, + "review_checks": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "forensic_trace_id": { + "type": "string" + }, + "packet_hash": { + "type": "string" + }, + "generated_at": { + "type": "string", + "format": "date-time" + }, + "links": { + "type": "object", + "properties": { + "standards_document": { + "type": "string", + "format": "uri" + }, + "certification_status": { + "type": "string", + "format": "uri" + }, + "forensics_summary": { + "type": "string", + "format": "uri" + }, + "evidence_packet": { + "type": "string", + "format": "uri" + }, + "verdict_record": { + "type": "string", + "format": "uri" + }, + "verdict_history": { + "type": "string", + "format": "uri" + } + } + }, + "attestation": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "signed", + "unsigned" + ] + }, + "scheme": { + "type": "string", + "enum": [ + "eip712" + ] + }, + "source": { + "type": "string", + "enum": [ + "oracle_signer", + "publisher_fallback", + "unconfigured", + "invalid_key" + ] + }, + "signer": { + "type": "string", + "nullable": true + }, + "signature": { + "type": "string", + "nullable": true + }, + "digest": { + "type": "string" + }, + "issued_at": { + "type": "string", + "format": "date-time" + }, + "reason": { + "type": "string", + "nullable": true + }, + "typed_data": { + "type": "object", + "properties": { + "domain": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "chainId": { + "type": "integer" + } + } + }, + "primaryType": { + "type": "string", + "enum": [ + "EvaluatorVerdict" + ] + }, + "types": { + "type": "object", + "properties": { + "EvaluatorVerdict": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string" + } + } + } + } + } + }, + "message": { + "type": "object", + "properties": { + "verdictId": { + "type": "string" + }, + "wallet": { + "type": "string" + }, + "counterpartyWallet": { + "type": "string" + }, + "escrowId": { + "type": "string" + }, + "decision": { + "type": "string" + }, + "recommendation": { + "type": "string" + }, + "approved": { + "type": "boolean" + }, + "confidence": { + "type": "integer" + }, + "agentScoreProvider": { + "type": "integer" + }, + "scoreModelVersion": { + "type": "string" + }, + "certificationValid": { + "type": "boolean" + }, + "certificationTier": { + "type": "string" + }, + "riskLevel": { + "type": "string" + }, + "riskScore": { + "type": "integer" + }, + "forensicTraceId": { + "type": "string" + }, + "packetHash": { + "type": "string" + }, + "generatedAt": { + "type": "string", + "format": "date-time" + } + } } } } } } - ] - }, - "RiskFactor": { - "type": "object", - "properties": { - "key": { "type": "string" }, - "label": { "type": "string" }, - "severity": { "type": "string", "enum": ["low", "medium", "high", "critical"] }, - "contribution": { "type": "integer", "minimum": 0, "maximum": 100 }, - "details": { "type": "object", "additionalProperties": true } - } - }, - "RiskPatternMatch": { - "type": "object", - "properties": { - "pattern_name": { "type": "string" }, - "risk_weight": { "type": "number" }, - "occurrences": { "type": "integer" }, - "first_detected": { "type": "string", "format": "date-time", "nullable": true }, - "last_detected": { "type": "string", "format": "date-time", "nullable": true } } }, - "RiskScoreResponse": { + "EvaluatorVerdictRecordResponse": { "type": "object", "properties": { - "wallet": { "type": "string" }, - "risk_score": { "type": "integer", "minimum": 0, "maximum": 100 }, - "risk_level": { "type": "string", "enum": ["clear", "watch", "elevated", "critical"] }, - "risk_confidence": { "type": "number", "minimum": 0, "maximum": 1 }, - "action": { "type": "string", "enum": ["allow", "monitor", "review", "block"] }, - "current_score": { "type": "integer", "minimum": 0, "maximum": 100 }, - "current_tier": { "type": "string", "enum": ["Unverified", "Emerging", "Established", "Trusted", "Elite"] }, - "score_confidence": { "type": "number", "minimum": 0, "maximum": 1 }, - "score_recommendation": { "type": "string", "enum": ["proceed", "proceed_with_caution", "insufficient_history", "high_risk", "flagged_for_review", "rpc_unavailable"] }, - "score_model_version": { "type": "string" }, - "last_scored_at": { "type": "string", "format": "date-time" }, - "summary": { + "standard": { + "type": "string", + "enum": [ + "erc-8183-evaluator-oracle-prototype" + ] + }, + "verdict_id": { + "type": "string" + }, + "wallet": { + "type": "string" + }, + "counterparty_wallet": { + "type": "string", + "nullable": true + }, + "escrow_id": { + "type": "string", + "nullable": true + }, + "decision": { + "type": "string", + "enum": [ + "approve", + "review", + "reject" + ] + }, + "approved": { + "type": "boolean" + }, + "recommendation": { + "type": "string", + "enum": [ + "release", + "manual_review", + "dispute", + "reject" + ] + }, + "confidence": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "agent_score_provider": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "score_model_version": { + "type": "string" + }, + "certification_valid": { + "type": "boolean" + }, + "certification_tier": { + "type": "string", + "nullable": true + }, + "risk_level": { + "type": "string", + "enum": [ + "clear", + "watch", + "elevated", + "critical" + ] + }, + "risk_score": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "sla_metrics": { "type": "object", "properties": { - "report_count": { "type": "integer" }, - "unique_reporters": { "type": "integer" }, - "total_penalty_applied": { "type": "integer" }, - "open_disputes": { "type": "integer" }, - "resolved_disputes": { "type": "integer" }, - "sybil_flagged": { "type": "boolean" }, - "sybil_indicators": { "type": "array", "items": { "type": "string" } }, - "gaming_indicators": { "type": "array", "items": { "type": "string" } }, - "rating_count": { "type": "integer" }, - "average_rating": { "type": "number", "nullable": true }, - "unique_raters": { "type": "integer" }, - "intent_count": { "type": "integer" }, - "conversions": { "type": "integer" }, - "conversion_rate": { "type": "number" }, - "active_creator_stakes": { "type": "integer" }, - "active_staked_amount": { "type": "number" }, - "active_score_boost": { "type": "integer" }, - "slashed_creator_stakes": { "type": "integer" }, - "slashed_staked_amount": { "type": "number" }, - "reason_breakdown": { + "baseline_profile": { + "type": "string", + "enum": [ + "djd-transactional-settlement-v1" + ] + }, + "settlement_tier": { + "type": "string" + }, + "score_floor": { + "type": "integer" + }, + "score_floor_passed": { + "type": "boolean" + }, + "certification_floor": { + "type": "string" + }, + "certification_floor_passed": { + "type": "boolean" + }, + "risk_guardrail_passed": { + "type": "boolean" + }, + "dispute_guardrail_passed": { + "type": "boolean" + }, + "failed_checks": { "type": "array", "items": { - "type": "object", - "properties": { - "reason": { "type": "string" }, - "count": { "type": "integer" } - } + "type": "string" + } + }, + "review_checks": { + "type": "array", + "items": { + "type": "string" } } } }, - "factors": { - "type": "array", - "items": { "$ref": "#/components/schemas/RiskFactor" } + "forensic_trace_id": { + "type": "string" }, - "matched_patterns": { - "type": "array", - "items": { "$ref": "#/components/schemas/RiskPatternMatch" } + "packet_hash": { + "type": "string" + }, + "generated_at": { + "type": "string", + "format": "date-time" + }, + "links": { + "type": "object", + "properties": { + "standards_document": { + "type": "string", + "format": "uri" + }, + "certification_status": { + "type": "string", + "format": "uri" + }, + "forensics_summary": { + "type": "string", + "format": "uri" + }, + "evidence_packet": { + "type": "string", + "format": "uri" + }, + "verdict_record": { + "type": "string", + "format": "uri" + }, + "verdict_history": { + "type": "string", + "format": "uri" + } + } + }, + "recorded_at": { + "type": "string", + "format": "date-time" + }, + "attestation": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "signed", + "unsigned" + ] + }, + "scheme": { + "type": "string", + "enum": [ + "eip712" + ] + }, + "source": { + "type": "string", + "enum": [ + "oracle_signer", + "publisher_fallback", + "unconfigured", + "invalid_key" + ] + }, + "signer": { + "type": "string", + "nullable": true + }, + "signature": { + "type": "string", + "nullable": true + }, + "digest": { + "type": "string" + }, + "issued_at": { + "type": "string", + "format": "date-time" + }, + "reason": { + "type": "string", + "nullable": true + }, + "typed_data": { + "type": "object", + "properties": { + "domain": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "chainId": { + "type": "integer" + } + } + }, + "primaryType": { + "type": "string", + "enum": [ + "EvaluatorVerdict" + ] + }, + "types": { + "type": "object", + "properties": { + "EvaluatorVerdict": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string" + } + } + } + } + } + }, + "message": { + "type": "object", + "properties": { + "verdictId": { + "type": "string" + }, + "wallet": { + "type": "string" + }, + "counterpartyWallet": { + "type": "string" + }, + "escrowId": { + "type": "string" + }, + "decision": { + "type": "string" + }, + "recommendation": { + "type": "string" + }, + "approved": { + "type": "boolean" + }, + "confidence": { + "type": "integer" + }, + "agentScoreProvider": { + "type": "integer" + }, + "scoreModelVersion": { + "type": "string" + }, + "certificationValid": { + "type": "boolean" + }, + "certificationTier": { + "type": "string" + }, + "riskLevel": { + "type": "string" + }, + "riskScore": { + "type": "integer" + }, + "forensicTraceId": { + "type": "string" + }, + "packetHash": { + "type": "string" + }, + "generatedAt": { + "type": "string", + "format": "date-time" + } + } + } + } + } + } } } }, - "ClusterMember": { - "type": "object", - "properties": { - "wallet": { "type": "string" }, - "cluster_name": { "type": "string" }, - "confidence": { "type": "number" }, - "assigned_at": { "type": "string", "format": "date-time" }, - "current_score": { "type": "integer", "nullable": true }, - "current_tier": { "type": "string", "nullable": true } - } - }, - "ClusterLinkedWallet": { - "type": "object", - "properties": { - "rank": { "type": "integer" }, - "wallet": { "type": "string" }, - "total_tx_count": { "type": "integer" }, - "total_volume": { "type": "number" }, - "last_interaction": { "type": "string", "format": "date-time" }, - "relationship_strength": { "type": "string", "enum": ["primary", "secondary"] } - } - }, - "ClusterResponse": { + "EvaluatorVerdictHistoryResponse": { "type": "object", "properties": { - "wallet": { "type": "string" }, - "cluster_id": { "type": "string" }, - "cluster_name": { + "standard": { "type": "string", - "enum": ["organic_network", "repeat_counterparty_network", "broker_hub", "sybil_ring", "fraud_hotspot", "isolated_newcomer"] + "enum": [ + "djd-evaluator-verdict-history-v1" + ] + }, + "wallet": { + "type": "string" + }, + "total": { + "type": "integer" + }, + "limit": { + "type": "integer" }, - "confidence": { "type": "number" }, - "assigned_at": { "type": "string", "format": "date-time" }, - "source": { "type": "string", "enum": ["inferred"] }, - "member_count": { "type": "integer" }, - "current_score": { "type": "integer" }, - "current_tier": { "type": "string" }, - "risk_level": { "type": "string", "enum": ["clear", "watch", "elevated", "critical"] }, "summary": { "type": "object", "properties": { - "counterparty_count": { "type": "integer" }, - "total_tx_count": { "type": "integer" }, - "total_volume": { "type": "number" }, - "report_count": { "type": "integer" }, - "unique_reporters": { "type": "integer" }, - "total_penalty_applied": { "type": "integer" }, - "sybil_flagged": { "type": "boolean" } + "approvals": { + "type": "integer" + }, + "manual_review": { + "type": "integer" + }, + "disputes": { + "type": "integer" + }, + "rejects": { + "type": "integer" + } } }, - "evidence": { "type": "array", "items": { "type": "string" } }, - "members": { "type": "array", "items": { "$ref": "#/components/schemas/ClusterMember" } }, - "linked_wallets": { "type": "array", "items": { "$ref": "#/components/schemas/ClusterLinkedWallet" } } - } - }, - "Dimension": { - "type": "object", - "properties": { - "score": { "type": "integer", "minimum": 0, "maximum": 100 }, - "data": { "type": "object" } - } - }, - "RegisterBody": { - "type": "object", - "required": ["wallet"], - "properties": { - "wallet": { "type": "string", "description": "Wallet address to register" }, - "name": { "type": "string", "maxLength": 100 }, - "description": { "type": "string", "maxLength": 500 }, - "github_url": { "type": "string", "format": "uri", "maxLength": 200 }, - "website_url": { "type": "string", "format": "uri", "maxLength": 200 } - } - }, - "RegisterResponse": { - "type": "object", - "properties": { - "wallet": { "type": "string" }, - "status": { "type": "string", "enum": ["registered", "updated"] }, - "registeredAt": { "type": "string", "format": "date-time" }, - "name": { "type": "string", "nullable": true }, - "description": { "type": "string", "nullable": true }, - "github_url": { "type": "string", "nullable": true }, - "website_url": { "type": "string", "nullable": true }, - "github_verified": { "type": "boolean" }, - "github_stars": { "type": "integer", "nullable": true }, - "github_pushed_at":{ "type": "string", "nullable": true } - } - }, - "LeaderboardResponse": { - "type": "object", - "properties": { - "leaderboard": { + "items": { "type": "array", "items": { "type": "object", "properties": { - "rank": { "type": "integer" }, - "wallet": { "type": "string" }, - "score": { "type": "integer" }, - "tier": { "type": "string" }, - "daysAlive": { "type": "integer" }, - "isRegistered": { "type": "boolean" }, - "githubVerified":{ "type": "boolean" } + "verdict_id": { + "type": "string" + }, + "recorded_at": { + "type": "string", + "format": "date-time" + }, + "decision": { + "type": "string", + "enum": [ + "approve", + "review", + "reject" + ] + }, + "recommendation": { + "type": "string", + "enum": [ + "release", + "manual_review", + "dispute", + "reject" + ] + }, + "approved": { + "type": "boolean" + }, + "confidence": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "current_score": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "current_tier": { + "type": "string" + }, + "risk_level": { + "type": "string", + "enum": [ + "clear", + "watch", + "elevated", + "critical" + ] + }, + "certification_valid": { + "type": "boolean" + }, + "certification_tier": { + "type": "string", + "nullable": true + }, + "escrow_id": { + "type": "string", + "nullable": true + }, + "counterparty_wallet": { + "type": "string", + "nullable": true + }, + "forensic_trace_id": { + "type": "string" + }, + "packet_hash": { + "type": "string" + }, + "attestation_status": { + "type": "string", + "enum": [ + "signed", + "unsigned" + ] + }, + "attestation_signer": { + "type": "string", + "nullable": true + } } } - }, - "totalAgentsScored": { "type": "integer" }, - "totalAgentsRegistered": { "type": "integer" }, - "lastUpdated": { "type": "string", "format": "date-time" } - } - }, - "ReportResponse": { - "type": "object", - "properties": { - "reportId": { "type": "string", "format": "uuid" }, - "status": { "type": "string", "enum": ["accepted"] }, - "targetCurrentScore":{ "type": "integer", "minimum": 0, "maximum": 100 }, - "penaltyApplied": { "type": "integer" } - } - }, - "RatingRequest": { - "type": "object", - "required": ["rated_wallet", "tx_hash", "rating"], - "properties": { - "rated_wallet": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$", "description": "Wallet being rated" }, - "tx_hash": { "type": "string", "pattern": "^0x[0-9a-fA-F]{64}$", "description": "Indexed transaction hash between payer and rated wallet with at least $0.10 USDC of settlement" }, - "rating": { "type": "integer", "minimum": 1, "maximum": 5, "description": "Star rating from 1 to 5" }, - "comment": { "type": "string", "maxLength": 500, "description": "Optional comment about the transaction" } - } - }, - "RatingResponse": { - "type": "object", - "properties": { - "ratingId": { "type": "string", "format": "uuid" }, - "status": { "type": "string", "enum": ["accepted"] }, - "ratedWallet": { "type": "string" }, - "txHash": { "type": "string" }, - "rating": { "type": "integer", "minimum": 1, "maximum": 5 }, - "averageRating": { "type": "number" }, - "ratingCount": { "type": "integer" } + } } }, - "StakeRequest": { + "EvaluatorCallbackResponse": { "type": "object", - "required": ["agent_wallet", "stake_tx_hash", "fee_tx_hash"], "properties": { - "agent_wallet": { + "standard": { "type": "string", - "pattern": "^0x[0-9a-fA-F]{40}$", - "description": "Agent wallet receiving the creator stake" + "enum": [ + "djd-evaluator-oracle-callback-v1" + ] }, - "stake_tx_hash": { - "type": "string", - "pattern": "^0x[0-9a-fA-F]{64}$", - "description": "Indexed USDC transfer hash for the creator -> agent stake transfer" + "ready": { + "type": "boolean" }, - "fee_tx_hash": { + "reason": { "type": "string", - "pattern": "^0x[0-9a-fA-F]{64}$", - "description": "Indexed USDC transfer hash for the creator -> PAY_TO 1% DJD fee transfer" - } - } - }, - "StakeResponse": { - "type": "object", - "properties": { - "stakeId": { "type": "string", "format": "uuid" }, - "status": { "type": "string", "enum": ["active", "slashed"] }, - "creatorWallet": { "type": "string" }, - "agentWallet": { "type": "string" }, - "stakeTxHash": { "type": "string" }, - "feeTxHash": { "type": "string" }, - "stakeAmount": { "type": "number" }, - "feeAmount": { "type": "number" }, - "scoreBoost": { "type": "integer" }, - "activeStakeCount": { "type": "integer" }, - "activeStakedAmount": { "type": "number" }, - "activeScoreBoost": { "type": "integer" } - } - }, - "BlacklistResponse": { - "type": "object", - "properties": { - "wallet": { "type": "string" }, - "reported": { "type": "boolean", "description": "True if the wallet has active fraud reports" }, - "reportCount": { "type": "integer" }, - "mostRecentDate": { "type": "string", "format": "date-time", "nullable": true }, - "reasons": { "type": "array", "items": { "type": "string" }, "description": "Unique report reason categories" }, - "disputeStatus": { "type": "string", "enum": ["none", "open", "resolved"], "description": "Aggregate dispute state for reports filed against this wallet" } - } - }, - "DataDecayResponse": { - "type": "object", - "properties": { - "wallet": { "type": "string" }, - "current_score": { "type": "integer", "nullable": true }, - "current_tier": { "type": "string", "nullable": true }, - "decay": { - "type": "array", - "items": { - "type": "object", - "properties": { - "score": { "type": "integer" }, - "recorded_at": { "type": "string", "format": "date-time" } + "nullable": true + }, + "verdict_id": { + "type": "string" + }, + "interface": { + "type": "object", + "properties": { + "contract": { + "type": "string", + "enum": [ + "IDJDEvaluatorOracleCallback" + ] + }, + "function": { + "type": "string", + "enum": [ + "receiveVerdict" + ] + }, + "chain_id": { + "type": "integer" + } + } + }, + "verification": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "signed", + "unsigned" + ] + }, + "signer": { + "type": "string", + "nullable": true + }, + "digest": { + "type": "string" + }, + "signature": { + "type": "string", + "nullable": true + }, + "scheme": { + "type": "string", + "enum": [ + "eip712" + ] } } }, - "count": { "type": "integer" }, - "returned": { "type": "integer" }, - "period": { + "verdict": { "type": "object", "properties": { - "from": { "type": "string", "format": "date-time", "nullable": true }, - "to": { "type": "string", "format": "date-time", "nullable": true } + "wallet": { + "type": "string" + }, + "counterparty_wallet": { + "type": "string", + "nullable": true + }, + "escrow_id": { + "type": "string", + "nullable": true + }, + "escrow_id_hash": { + "type": "string" + }, + "decision": { + "type": "string", + "enum": [ + "approve", + "review", + "reject" + ] + }, + "decision_code": { + "type": "integer" + }, + "recommendation": { + "type": "string", + "enum": [ + "release", + "manual_review", + "dispute", + "reject" + ] + }, + "recommendation_code": { + "type": "integer" + }, + "approved": { + "type": "boolean" + }, + "confidence": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "agent_score_provider": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "certification_valid": { + "type": "boolean" + }, + "risk_score": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "packet_hash": { + "type": "string" + } } }, - "trend": { + "callback": { "type": "object", - "nullable": true, "properties": { - "direction": { "type": "string", "enum": ["improving", "declining", "stable"] }, - "change_pct": { "type": "number" }, - "avg_score": { "type": "number" }, - "min_score": { "type": "integer" }, - "max_score": { "type": "integer" } + "selector": { + "type": "string", + "nullable": true + }, + "calldata": { + "type": "string", + "nullable": true + }, + "args": { + "type": "object", + "properties": { + "escrow_id_hash": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "counterparty": { + "type": "string" + }, + "decision_code": { + "type": "integer" + }, + "recommendation_code": { + "type": "integer" + }, + "approved": { + "type": "boolean" + }, + "confidence": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "agent_score_provider": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "certification_valid": { + "type": "boolean" + }, + "risk_score": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "packet_hash": { + "type": "string" + }, + "attestation_digest": { + "type": "string" + }, + "attestation_signature": { + "type": "string", + "nullable": true + } + } + } } }, - "trajectory": { + "transaction": { "type": "object", "properties": { - "velocity": { "type": "number", "nullable": true }, - "momentum": { "type": "number", "nullable": true }, - "direction": { "type": "string", "enum": ["improving", "declining", "stable", "volatile", "new"] }, - "volatility": { "type": "number" }, - "modifier": { "type": "integer" }, - "dataPoints": { "type": "integer" }, - "spanDays": { "type": "number" } + "to": { + "type": "string", + "nullable": true + }, + "data": { + "type": "string", + "nullable": true + }, + "value": { + "type": "string", + "enum": [ + "0" + ] + } } } } }, - "DataGraphCounterparty": { - "type": "object", - "properties": { - "rank": { "type": "integer" }, - "wallet": { "type": "string" }, - "tx_count_outbound": { "type": "integer" }, - "tx_count_inbound": { "type": "integer" }, - "total_tx_count": { "type": "integer" }, - "volume_outbound": { "type": "number" }, - "volume_inbound": { "type": "number" }, - "total_volume": { "type": "number" }, - "first_interaction": { "type": "string", "format": "date-time" }, - "last_interaction": { "type": "string", "format": "date-time" } - } - }, - "DataGraphResponse": { + "EvaluatorVerifierPackageResponse": { "type": "object", "properties": { - "wallet": { "type": "string" }, - "current_score": { "type": "integer", "nullable": true }, - "current_tier": { "type": "string", "nullable": true }, - "sybil_flagged": { "type": "boolean" }, - "counterparties": { - "type": "array", - "items": { "$ref": "#/components/schemas/DataGraphCounterparty" } + "standard": { + "type": "string", + "enum": [ + "djd-evaluator-verifier-package-v1" + ] }, - "count": { "type": "integer" }, - "returned": { "type": "integer" }, - "summary": { + "network": { "type": "object", "properties": { - "counterparty_count": { "type": "integer" }, - "outbound_tx_count": { "type": "integer" }, - "inbound_tx_count": { "type": "integer" }, - "total_tx_count": { "type": "integer" }, - "volume_outbound": { "type": "number" }, - "volume_inbound": { "type": "number" }, - "total_volume": { "type": "number" }, - "first_interaction": { "type": "string", "format": "date-time", "nullable": true }, - "last_interaction": { "type": "string", "format": "date-time", "nullable": true } + "key": { + "type": "string", + "enum": [ + "base", + "base-sepolia" + ] + }, + "chain_id": { + "type": "integer" + }, + "chain_name": { + "type": "string" + }, + "caip2": { + "type": "string" + }, + "environment": { + "type": "string", + "enum": [ + "mainnet", + "testnet" + ] + } } - } - } - }, - "DataRatingsBreakdown": { - "type": "object", - "properties": { - "rating": { "type": "integer", "minimum": 1, "maximum": 5 }, - "count": { "type": "integer" } - } - }, - "DataRatingsItem": { - "type": "object", - "properties": { - "rating_id": { "type": "string", "format": "uuid" }, - "rater_wallet": { "type": "string" }, - "tx_hash": { "type": "string" }, - "rating": { "type": "integer", "minimum": 1, "maximum": 5 }, - "comment": { "type": "string", "nullable": true }, - "created_at": { "type": "string", "format": "date-time" } - } - }, - "DataRatingsResponse": { - "type": "object", - "properties": { - "wallet": { "type": "string" }, - "current_score": { "type": "integer", "nullable": true }, - "current_tier": { "type": "string", "nullable": true }, - "average_rating": { "type": "number" }, - "rating_count": { "type": "integer" }, - "unique_raters": { "type": "integer" }, - "most_recent_rating_at": { "type": "string", "format": "date-time", "nullable": true }, - "breakdown": { - "type": "array", - "items": { "$ref": "#/components/schemas/DataRatingsBreakdown" } - }, - "ratings": { - "type": "array", - "items": { "$ref": "#/components/schemas/DataRatingsItem" } }, - "count": { "type": "integer" }, - "returned": { "type": "integer" } - } - }, - "DataIntentTierBreakdown": { - "type": "object", - "properties": { - "tier_requested": { "type": "string" }, - "count": { "type": "integer" }, - "conversions": { "type": "integer" }, - "conversion_rate": { "type": "number" } - } - }, - "DataIntentItem": { - "type": "object", - "properties": { - "counterparty_wallet": { "type": "string" }, - "query_timestamp": { "type": "string", "format": "date-time" }, - "followed_by_tx": { "type": "boolean" }, - "tx_hash": { "type": "string", "nullable": true }, - "tx_timestamp": { "type": "string", "format": "date-time", "nullable": true }, - "time_to_tx_ms": { "type": "integer", "nullable": true }, - "endpoint": { "type": "string", "nullable": true }, - "tier_requested": { "type": "string", "nullable": true }, - "price_paid": { "type": "number", "nullable": true } - } - }, - "DataIntentResponse": { - "type": "object", - "properties": { - "wallet": { "type": "string" }, - "current_score": { "type": "integer", "nullable": true }, - "current_tier": { "type": "string", "nullable": true }, - "summary": { + "signing": { "type": "object", "properties": { - "intent_count": { "type": "integer" }, - "conversions": { "type": "integer" }, - "conversion_rate": { "type": "number" }, - "avg_time_to_tx_ms": { "type": "integer", "nullable": true }, - "avg_time_to_tx_hours": { "type": "number", "nullable": true }, - "most_recent_query_at": { "type": "string", "format": "date-time", "nullable": true }, - "most_recent_conversion_at": { "type": "string", "format": "date-time", "nullable": true } + "scheme": { + "type": "string", + "enum": [ + "eip712" + ] + }, + "primary_type": { + "type": "string", + "enum": [ + "EvaluatorVerdict" + ] + }, + "domain": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "chainId": { + "type": "integer" + } + } + }, + "types": { + "type": "object", + "additionalProperties": true + }, + "active_signer": { + "type": "object", + "properties": { + "configured": { + "type": "boolean" + }, + "source": { + "type": "string", + "enum": [ + "oracle_signer", + "publisher_fallback", + "unconfigured", + "invalid_key" + ] + }, + "address": { + "type": "string", + "nullable": true + }, + "reason": { + "type": "string", + "nullable": true + } + } + } } }, - "by_tier": { - "type": "array", - "items": { "$ref": "#/components/schemas/DataIntentTierBreakdown" } - }, - "intents": { - "type": "array", - "items": { "$ref": "#/components/schemas/DataIntentItem" } - }, - "count": { "type": "integer" }, - "returned": { "type": "integer" } - } - }, - "EconomySurvivalResponse": { - "type": "object", - "properties": { - "as_of": { "type": "string", "format": "date-time" }, - "summary": { + "contracts": { "type": "object", "properties": { - "total_wallets": { "type": "integer" }, - "active_7d": { "type": "integer" }, - "active_30d": { "type": "integer" }, - "dormant_30d": { "type": "integer" }, - "avg_days_since_last_seen": { "type": "number", "nullable": true } - } - }, - "cohorts": { - "type": "array", - "items": { - "type": "object", - "properties": { - "horizon_days": { "type": "integer" }, - "eligible_wallets": { "type": "integer" }, - "surviving_wallets": { "type": "integer" }, - "survival_rate": { "type": "number" } - } - } - }, - "by_tier": { - "type": "array", - "items": { - "type": "object", - "properties": { - "tier": { "type": "string" }, - "wallet_count": { "type": "integer" }, - "active_30d": { "type": "integer" }, - "survival_rate_30d": { "type": "number" } - } - } - }, - "at_risk_wallets": { - "type": "array", - "items": { - "type": "object", - "properties": { - "wallet": { "type": "string" }, - "current_score": { "type": "integer", "nullable": true }, - "current_tier": { "type": "string", "nullable": true }, - "first_seen": { "type": "string", "format": "date-time", "nullable": true }, - "last_seen": { "type": "string", "format": "date-time", "nullable": true }, - "days_since_last_seen": { "type": "number", "nullable": true }, - "score_change_30d": { "type": "number", "nullable": true }, - "risk_bucket": { "type": "string", "enum": ["declining", "dormant"] } + "callback_interface": { + "type": "object", + "properties": { + "contract": { + "type": "string", + "enum": [ + "IDJDEvaluatorOracleCallback" + ] + }, + "function": { + "type": "string", + "enum": [ + "receiveVerdict" + ] + }, + "signature": { + "type": "string" + }, + "selector": { + "type": "string" + }, + "abi": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "verifier": { + "type": "object", + "properties": { + "contract": { + "type": "string", + "enum": [ + "DJDEvaluatorVerdictVerifier" + ] + }, + "constructor": { + "type": "object", + "properties": { + "initial_signer": { + "type": "string", + "nullable": true + } + } + }, + "methods": { + "type": "object", + "properties": { + "hash_verdict": { + "type": "object", + "properties": { + "signature": { + "type": "string" + }, + "selector": { + "type": "string" + } + } + }, + "verify_verdict": { + "type": "object", + "properties": { + "signature": { + "type": "string" + }, + "selector": { + "type": "string" + } + } + }, + "verify_digest": { + "type": "object", + "properties": { + "signature": { + "type": "string" + }, + "selector": { + "type": "string" + } + } + }, + "set_oracle_signer": { + "type": "object", + "properties": { + "signature": { + "type": "string" + }, + "selector": { + "type": "string" + } + } + }, + "transfer_ownership": { + "type": "object", + "properties": { + "signature": { + "type": "string" + }, + "selector": { + "type": "string" + } + } + } + } + }, + "abi": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "sources": { + "type": "array", + "items": { + "type": "object", + "properties": { + "contract": { + "type": "string" + }, + "path": { + "type": "string" + }, + "license": { + "type": "string" + }, + "sha256": { + "type": "string" + }, + "source": { + "type": "string" + } + } + } + }, + "settlement_example": { + "type": "object", + "properties": { + "contract": { + "type": "string", + "enum": [ + "DJDEvaluatorEscrowSettlementExample" + ] + }, + "function": { + "type": "string", + "enum": [ + "settleWithDJDVerdict" + ] + }, + "signature": { + "type": "string" + }, + "selector": { + "type": "string" + }, + "constructor": { + "type": "object", + "properties": { + "verifier": { + "type": "string", + "nullable": true + }, + "provider": { + "type": "string", + "enum": [ + "wallet_from_verdict" + ] + }, + "counterparty": { + "type": "string", + "enum": [ + "counterparty_wallet_from_verdict_or_zero" + ] + }, + "escrow_id_hash": { + "type": "string", + "enum": [ + "keccak256(escrow_id)" + ] + } + } + }, + "abi": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + } } } }, - "returned": { "type": "integer" } - } - }, - "EconomySummaryResponse": { - "type": "object", - "properties": { - "period": { "type": "string", "enum": ["daily", "weekly", "monthly"] }, - "limit": { "type": "integer" }, - "count": { "type": "integer" }, - "metrics": { - "type": "array", - "items": { - "type": "object", - "properties": { - "period_start": { "type": "string", "format": "date-time" }, - "period_end": { "type": "string", "format": "date-time" }, - "period_type": { "type": "string" }, - "total_wallets": { "type": "integer" }, - "new_wallets": { "type": "integer" }, - "dead_wallets": { "type": "integer" }, - "active_wallets": { "type": "integer" }, - "total_tx_count": { "type": "integer" }, - "total_volume": { "type": "number" }, - "avg_tx_size": { "type": "number" }, - "median_score": { "type": "number" }, - "avg_score": { "type": "number" }, - "elite_count": { "type": "integer" }, - "trusted_count": { "type": "integer" }, - "established_count": { "type": "integer" }, - "emerging_count": { "type": "integer" }, - "unverified_count": { "type": "integer" }, - "total_fraud_reports": { "type": "integer" }, - "total_queries": { "type": "integer" } - } - } - } - } - }, - "EconomyVolumeResponse": { - "type": "object", - "properties": { - "period": { "type": "string", "enum": ["daily", "weekly", "monthly"] }, - "limit": { "type": "integer" }, - "count": { "type": "integer" }, - "series": { + "endpoints": { + "type": "object", + "properties": { + "oracle_verdict": { + "type": "string" + }, + "verdict_record": { + "type": "string" + }, + "callback_calldata": { + "type": "string" + }, + "docs": { + "type": "string" + }, + "verifier_proof": { + "type": "string" + }, + "escrow_settlement": { + "type": "string" + }, + "deploy_plan": { + "type": "string" + }, + "artifact_package": { + "type": "string" + }, + "deployment_registry": { + "type": "string" + } + } + }, + "notes": { "type": "array", "items": { - "type": "object", - "properties": { - "period_start": { "type": "string", "format": "date-time" }, - "period_end": { "type": "string", "format": "date-time" }, - "total_tx_count": { "type": "integer" }, - "total_volume": { "type": "number" }, - "avg_tx_size": { "type": "number" }, - "active_wallets": { "type": "integer" }, - "new_wallets": { "type": "integer" } - } + "type": "string" } } } }, - "FraudDisputeRequest": { + "EvaluatorVerifierProofResponse": { "type": "object", - "required": ["report_id", "reason", "details"], "properties": { - "report_id": { "type": "string", "description": "Fraud report ID from the reports or feed endpoints" }, + "standard": { + "type": "string", + "enum": [ + "djd-evaluator-verifier-proof-v1" + ] + }, + "ready": { + "type": "boolean" + }, "reason": { "type": "string", - "enum": ["fulfilled_service", "mistaken_identity", "resolved_offchain", "inaccurate_report", "other"] + "nullable": true }, - "details": { "type": "string", "description": "Supporting explanation or evidence for the dispute" } - } - }, - "FraudDisputeResponse": { - "type": "object", - "properties": { - "disputeId": { "type": "string", "format": "uuid" }, - "status": { "type": "string", "enum": ["open"] }, - "reportId": { "type": "string" }, - "targetWallet": { "type": "string" } - } - }, - - "ApiKeyCreatedResponse": { - "type": "object", - "properties": { - "id": { "type": "integer", "description": "Auto-incremented key ID" }, - "key": { "type": "string", "description": "The raw API key (djd_live_...) — returned ONLY on creation" }, - "key_prefix": { "type": "string", "description": "First 16 characters of the key for identification" }, - "wallet": { "type": "string" }, - "name": { "type": "string", "nullable": true }, - "tier": { "type": "string" }, - "monthly_limit": { "type": "integer" }, - "usage_reset_at": { "type": "string", "format": "date-time" }, - "message": { "type": "string" } - } - }, - "ApiKeyListItem": { - "type": "object", - "properties": { - "id": { "type": "integer" }, - "key_prefix": { "type": "string" }, - "wallet": { "type": "string" }, - "name": { "type": "string", "nullable": true }, - "tier": { "type": "string" }, - "monthly_limit": { "type": "integer" }, - "monthly_used": { "type": "integer" }, - "usage_reset_at": { "type": "string", "format": "date-time" }, - "is_active": { "type": "integer", "enum": [0, 1] }, - "created_at": { "type": "string", "format": "date-time" }, - "last_used_at": { "type": "string", "format": "date-time", "nullable": true }, - "revoked_at": { "type": "string", "format": "date-time", "nullable": true } - } - }, - - "WebhookCreatedResponse": { - "type": "object", - "properties": { - "id": { "type": "integer" }, - "wallet": { "type": "string" }, - "url": { "type": "string" }, - "secret": { "type": "string", "description": "HMAC secret for verifying webhook signatures — returned ONLY on creation" }, - "events": { "type": "array", "items": { "type": "string" } }, - "threshold_score": { "type": "integer", "minimum": 0, "maximum": 100, "nullable": true }, - "forensics_filter": { "$ref": "#/components/schemas/WebhookForensicsFilter" }, - "presets_applied": { "type": "array", "items": { "type": "string" } }, - "tier": { "type": "string" }, - "message": { "type": "string" } - } - }, - "PublicWebhookCreatedResponse": { - "type": "object", - "properties": { - "id": { "type": "integer" }, - "url": { "type": "string" }, - "secret": { "type": "string", "description": "HMAC secret for verifying webhook signatures — returned ONLY on creation" }, - "events": { "type": "array", "items": { "type": "string" } }, - "threshold_score": { "type": "integer", "minimum": 0, "maximum": 100, "nullable": true }, - "forensics_filter": { "$ref": "#/components/schemas/WebhookForensicsFilter" }, - "presets_applied": { "type": "array", "items": { "type": "string" } }, - "message": { "type": "string" } - } - }, - "WebhookListItem": { - "type": "object", - "properties": { - "id": { "type": "integer" }, - "wallet": { "type": "string" }, - "url": { "type": "string" }, - "events": { "type": "array", "items": { "type": "string" } }, - "tier": { "type": "string" }, - "threshold_score": { "type": "integer", "minimum": 0, "maximum": 100, "nullable": true }, - "forensics_filter": { "$ref": "#/components/schemas/WebhookForensicsFilter" }, - "is_active": { "type": "integer", "enum": [0, 1] }, - "created_at": { "type": "string", "format": "date-time" }, - "failure_count": { "type": "integer" }, - "last_delivery_at": { "type": "string", "format": "date-time", "nullable": true }, - "disabled_at": { "type": "string", "format": "date-time", "nullable": true } - } - }, - "PublicWebhookListItem": { - "type": "object", - "properties": { - "id": { "type": "integer" }, - "url": { "type": "string" }, - "events": { "type": "array", "items": { "type": "string" } }, - "tier": { "type": "string" }, - "threshold_score": { "type": "integer", "minimum": 0, "maximum": 100, "nullable": true }, - "forensics_filter": { "$ref": "#/components/schemas/WebhookForensicsFilter" }, - "is_active": { "type": "integer", "enum": [0, 1] }, - "created_at": { "type": "string", "format": "date-time" }, - "failure_count": { "type": "integer" }, - "last_delivery_at": { "type": "string", "format": "date-time", "nullable": true } - } - }, - "WebhookDetailResponse": { - "allOf": [ - { "$ref": "#/components/schemas/WebhookCreatedResponse" }, - { + "verdict_id": { + "type": "string" + }, + "verifier": { "type": "object", "properties": { - "is_active": { "type": "integer", "enum": [0, 1] }, - "created_at": { "type": "string", "format": "date-time" }, - "failure_count": { "type": "integer" }, - "last_delivery_at": { "type": "string", "format": "date-time", "nullable": true }, - "disabled_at": { "type": "string", "format": "date-time", "nullable": true }, - "recent_deliveries": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { "type": "integer" }, - "event_type": { "type": "string" }, - "status_code": { "type": "integer", "nullable": true }, - "attempt": { "type": "integer" }, - "delivered_at": { "type": "string", "format": "date-time", "nullable": true }, - "created_at": { "type": "string", "format": "date-time" } + "contract": { + "type": "string", + "enum": [ + "DJDEvaluatorVerdictVerifier" + ] + }, + "function": { + "type": "string", + "enum": [ + "verifyVerdict" + ] + }, + "selector": { + "type": "string" + }, + "chain_id": { + "type": "integer" + } + } + }, + "attestation": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "signed", + "unsigned" + ] + }, + "signer": { + "type": "string", + "nullable": true + }, + "digest": { + "type": "string" + }, + "signature": { + "type": "string", + "nullable": true + }, + "scheme": { + "type": "string", + "enum": [ + "eip712" + ] + } + } + }, + "verdict": { + "type": "object", + "additionalProperties": true + }, + "call": { + "type": "object", + "properties": { + "selector": { + "type": "string", + "nullable": true + }, + "calldata": { + "type": "string", + "nullable": true + }, + "args": { + "type": "object", + "properties": { + "verdict": { + "type": "object", + "additionalProperties": true + }, + "signature": { + "type": "string", + "nullable": true } } } } - } - ] - }, - "WebhookPreset": { - "type": "object", - "properties": { - "name": { "type": "string", "enum": ["score_monitoring", "forensics_monitoring", "forensics_disputes", "forensics_watchlist", "anomaly_monitoring"] }, - "description": { "type": "string" }, - "events": { "type": "array", "items": { "type": "string" } }, - "threshold_score_default": { "type": "integer", "minimum": 0, "maximum": 100, "nullable": true } - } - }, - "WebhookForensicsFilter": { - "type": "object", - "nullable": true, - "properties": { - "minimum_risk_level": { - "type": "string", - "enum": ["clear", "watch", "elevated", "critical"], - "description": "Only emit Forensics events whose resulting risk level meets or exceeds this threshold." }, - "reasons": { - "type": "array", - "items": { - "type": "string", - "enum": ["failed_delivery", "payment_fraud", "impersonation", "malicious_behavior", "other"] - }, - "description": "Only emit Forensics events tied to these report reasons." + "transaction": { + "type": "object", + "properties": { + "to": { + "type": "string", + "nullable": true + }, + "data": { + "type": "string", + "nullable": true + }, + "value": { + "type": "string", + "enum": [ + "0" + ] + } + } + }, + "links": { + "type": "object", + "properties": { + "verdict_record": { + "type": "string" + }, + "verifier_package": { + "type": "string" + }, + "deployment_registry": { + "type": "string" + } + } + }, + "resolution": { + "type": "object", + "properties": { + "source": { + "type": "string", + "enum": [ + "explicit", + "published_registry", + "unresolved" + ] + }, + "contract_address": { + "type": "string", + "nullable": true + }, + "registry_updated_at": { + "type": "string", + "nullable": true + }, + "published_deployment": { + "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "published_at": { + "type": "string", + "nullable": true + }, + "network": { + "type": "object", + "properties": { + "key": { + "type": "string", + "nullable": true + }, + "chain_id": { + "type": "integer", + "nullable": true + }, + "chain_name": { + "type": "string", + "nullable": true + }, + "caip2": { + "type": "string", + "nullable": true + }, + "environment": { + "type": "string", + "nullable": true + } + } + }, + "verdict_id": { + "type": "string", + "nullable": true + }, + "deployer": { + "type": "string", + "nullable": true + }, + "contracts": { + "type": "object", + "properties": { + "verifier": { + "type": "object", + "properties": { + "contract": { + "type": "string", + "nullable": true + }, + "address": { + "type": "string", + "nullable": true + }, + "tx_hash": { + "type": "string", + "nullable": true + }, + "action": { + "type": "string", + "nullable": true + } + } + }, + "escrow": { + "type": "object", + "properties": { + "contract": { + "type": "string", + "nullable": true + }, + "address": { + "type": "string", + "nullable": true + }, + "tx_hash": { + "type": "string", + "nullable": true + }, + "action": { + "type": "string", + "nullable": true + } + } + } + } + }, + "verification": { + "type": "object", + "properties": { + "oracle_signer": { + "type": "string", + "nullable": true + }, + "escrow_verifier": { + "type": "string", + "nullable": true + }, + "escrow_provider": { + "type": "string", + "nullable": true + }, + "escrow_counterparty": { + "type": "string", + "nullable": true + }, + "escrow_id_hash": { + "type": "string", + "nullable": true + } + } + }, + "inputs": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "nullable": true + }, + "counterparty": { + "type": "string", + "nullable": true + }, + "escrow_id": { + "type": "string", + "nullable": true + } + } + }, + "checks": { + "type": "object", + "properties": { + "preflight": { + "type": "boolean", + "nullable": true + }, + "verified": { + "type": "boolean", + "nullable": true + }, + "smoked": { + "type": "boolean", + "nullable": true + }, + "health": { + "type": "boolean", + "nullable": true + }, + "staged": { + "type": "boolean", + "nullable": true + } + } + }, + "explorer": { + "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "verifier_address": { + "type": "string", + "nullable": true + }, + "verifier_transaction": { + "type": "string", + "nullable": true + }, + "escrow_address": { + "type": "string", + "nullable": true + }, + "escrow_transaction": { + "type": "string", + "nullable": true + } + } + } + ] + }, + "links": { + "type": "object", + "properties": { + "verifier_package": { + "type": "string" + }, + "deployment_registry": { + "type": "string" + }, + "verifier_proof": { + "type": "string", + "nullable": true + }, + "escrow_settlement": { + "type": "string", + "nullable": true + }, + "deploy_bundle": { + "type": "string", + "nullable": true + } + } + } + } + } + ] + } + } } } }, - "MonitoringPreset": { + "EvaluatorEscrowSettlementResponse": { "type": "object", "properties": { - "policy_type": { + "standard": { "type": "string", - "enum": ["score_monitoring", "forensics_monitoring", "forensics_disputes", "forensics_watchlist", "anomaly_monitoring"] + "enum": [ + "djd-evaluator-escrow-settlement-v1" + ] }, - "description": { "type": "string" }, - "events": { "type": "array", "items": { "type": "string" } }, - "threshold_score_default": { "type": "integer", "minimum": 0, "maximum": 100, "nullable": true }, - "supports_threshold_score": { "type": "boolean" }, - "supports_forensics_filter": { "type": "boolean" } - } - }, - "MonitoringSubscription": { - "type": "object", - "properties": { - "id": { "type": "string", "format": "uuid" }, - "target_wallet": { "type": "string" }, - "policy_type": { + "ready": { + "type": "boolean" + }, + "reason": { "type": "string", - "enum": ["score_monitoring", "forensics_monitoring", "forensics_disputes", "forensics_watchlist", "anomaly_monitoring"] + "nullable": true }, - "url": { "type": "string" }, - "events": { "type": "array", "items": { "type": "string" } }, - "threshold_score": { "type": "integer", "minimum": 0, "maximum": 100, "nullable": true }, - "forensics_filter": { "$ref": "#/components/schemas/WebhookForensicsFilter" }, - "is_active": { "type": "boolean" }, - "created_at": { "type": "string", "format": "date-time" }, - "disabled_at": { "type": "string", "format": "date-time", "nullable": true }, - "failure_count": { "type": "integer" }, - "last_delivery_at": { "type": "string", "format": "date-time", "nullable": true } - } - }, - "MonitoringSubscriptionCreatedResponse": { - "allOf": [ - { "$ref": "#/components/schemas/MonitoringSubscription" }, - { + "verdict_id": { + "type": "string" + }, + "escrow": { "type": "object", "properties": { - "secret": { + "contract": { "type": "string", - "description": "HMAC secret for verifying webhook signatures — returned ONLY on creation" + "enum": [ + "DJDEvaluatorEscrowSettlementExample" + ] + }, + "function": { + "type": "string", + "enum": [ + "settleWithDJDVerdict" + ] }, - "presets_applied": { "type": "array", "items": { "type": "string" } }, - "message": { "type": "string" } + "selector": { + "type": "string" + }, + "chain_id": { + "type": "integer" + } } - } - ] - }, - - "ScoreHistoryResponse": { - "type": "object", - "properties": { - "wallet": { "type": "string" }, - "history": { - "type": "array", - "items": { - "type": "object", - "properties": { - "score": { "type": "integer", "minimum": 0, "maximum": 100 }, - "confidence": { "type": "number", "minimum": 0, "maximum": 1 }, - "model_version": { "type": "string" }, - "calculated_at": { "type": "string", "format": "date-time" } + }, + "verifier": { + "type": "object", + "properties": { + "contract": { + "type": "string", + "enum": [ + "DJDEvaluatorVerdictVerifier" + ] + }, + "function": { + "type": "string", + "enum": [ + "verifyVerdict" + ] } } }, - "count": { "type": "integer", "description": "Total records matching the query" }, - "returned": { "type": "integer", "description": "Number of records in this response" }, - "period": { + "attestation": { "type": "object", "properties": { - "from": { "type": "string", "format": "date-time", "nullable": true }, - "to": { "type": "string", "format": "date-time", "nullable": true } + "status": { + "type": "string", + "enum": [ + "signed", + "unsigned" + ] + }, + "signer": { + "type": "string", + "nullable": true + }, + "digest": { + "type": "string" + }, + "signature": { + "type": "string", + "nullable": true + }, + "scheme": { + "type": "string", + "enum": [ + "eip712" + ] + } } }, - "trend": { + "settlement": { "type": "object", - "nullable": true, - "description": "Trend analysis (present when 2+ data points exist)", "properties": { - "direction": { "type": "string", "enum": ["improving", "declining", "stable"] }, - "change_pct": { "type": "number", "description": "Percentage change from earliest to latest score" }, - "avg_score": { "type": "number" }, - "min_score": { "type": "integer" }, - "max_score": { "type": "integer" } + "recommendation": { + "type": "string", + "enum": [ + "release", + "manual_review", + "dispute", + "reject" + ] + }, + "approved": { + "type": "boolean" + }, + "outcome": { + "type": "string", + "enum": [ + "release", + "manual_review", + "dispute", + "reject" + ] + }, + "release_authorized": { + "type": "boolean" + } } - } - } - }, - - "ForensicsOverviewResponse": { - "type": "object", - "properties": { - "wallet": { "type": "string" }, - "risk_level": { "type": "string", "enum": ["clear", "watch", "elevated", "critical"] }, - "current_score": { "type": "integer", "minimum": 0, "maximum": 100, "nullable": true }, - "current_tier": { "type": "string", "nullable": true }, - "report_count": { "type": "integer" }, - "total_penalty_applied": { "type": "integer" }, - "unique_reporters": { "type": "integer" }, - "most_recent_report_at": { "type": "string", "format": "date-time", "nullable": true }, - "dispute_status": { "type": "string", "enum": ["none", "open", "resolved"] }, - "open_disputes": { "type": "integer" }, - "resolved_disputes": { "type": "integer" }, - "score_history_entries": { "type": "integer" }, - "reasons": { - "type": "array", - "items": { - "type": "object", - "properties": { - "reason": { "type": "string" }, - "count": { "type": "integer" } + }, + "verdict": { + "type": "object", + "additionalProperties": true + }, + "call": { + "type": "object", + "properties": { + "selector": { + "type": "string", + "nullable": true + }, + "calldata": { + "type": "string", + "nullable": true + }, + "args": { + "type": "object", + "properties": { + "verdict": { + "type": "object", + "additionalProperties": true + }, + "signature": { + "type": "string", + "nullable": true + } + } + } + } + }, + "transaction": { + "type": "object", + "properties": { + "to": { + "type": "string", + "nullable": true + }, + "data": { + "type": "string", + "nullable": true + }, + "value": { + "type": "string", + "enum": [ + "0" + ] } } }, - "recent_reports": { - "type": "array", - "items": { - "type": "object", - "properties": { - "report_id": { "type": "string" }, - "reason": { "type": "string" }, - "created_at": { "type": "string", "format": "date-time" }, - "penalty_applied": { "type": "integer" } + "links": { + "type": "object", + "properties": { + "verdict_record": { + "type": "string" + }, + "verifier_package": { + "type": "string" + }, + "verifier_proof": { + "type": "string" + }, + "deployment_registry": { + "type": "string" + } + } + }, + "resolution": { + "type": "object", + "properties": { + "source": { + "type": "string", + "enum": [ + "explicit", + "published_registry", + "unresolved" + ] + }, + "contract_address": { + "type": "string", + "nullable": true + }, + "registry_updated_at": { + "type": "string", + "nullable": true + }, + "published_deployment": { + "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "published_at": { + "type": "string", + "nullable": true + }, + "network": { + "type": "object", + "properties": { + "key": { + "type": "string", + "nullable": true + }, + "chain_id": { + "type": "integer", + "nullable": true + }, + "chain_name": { + "type": "string", + "nullable": true + }, + "caip2": { + "type": "string", + "nullable": true + }, + "environment": { + "type": "string", + "nullable": true + } + } + }, + "verdict_id": { + "type": "string", + "nullable": true + }, + "deployer": { + "type": "string", + "nullable": true + }, + "contracts": { + "type": "object", + "properties": { + "verifier": { + "type": "object", + "properties": { + "contract": { + "type": "string", + "nullable": true + }, + "address": { + "type": "string", + "nullable": true + }, + "tx_hash": { + "type": "string", + "nullable": true + }, + "action": { + "type": "string", + "nullable": true + } + } + }, + "escrow": { + "type": "object", + "properties": { + "contract": { + "type": "string", + "nullable": true + }, + "address": { + "type": "string", + "nullable": true + }, + "tx_hash": { + "type": "string", + "nullable": true + }, + "action": { + "type": "string", + "nullable": true + } + } + } + } + }, + "verification": { + "type": "object", + "properties": { + "oracle_signer": { + "type": "string", + "nullable": true + }, + "escrow_verifier": { + "type": "string", + "nullable": true + }, + "escrow_provider": { + "type": "string", + "nullable": true + }, + "escrow_counterparty": { + "type": "string", + "nullable": true + }, + "escrow_id_hash": { + "type": "string", + "nullable": true + } + } + }, + "inputs": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "nullable": true + }, + "counterparty": { + "type": "string", + "nullable": true + }, + "escrow_id": { + "type": "string", + "nullable": true + } + } + }, + "checks": { + "type": "object", + "properties": { + "preflight": { + "type": "boolean", + "nullable": true + }, + "verified": { + "type": "boolean", + "nullable": true + }, + "smoked": { + "type": "boolean", + "nullable": true + }, + "health": { + "type": "boolean", + "nullable": true + }, + "staged": { + "type": "boolean", + "nullable": true + } + } + }, + "explorer": { + "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "verifier_address": { + "type": "string", + "nullable": true + }, + "verifier_transaction": { + "type": "string", + "nullable": true + }, + "escrow_address": { + "type": "string", + "nullable": true + }, + "escrow_transaction": { + "type": "string", + "nullable": true + } + } + } + ] + }, + "links": { + "type": "object", + "properties": { + "verifier_package": { + "type": "string" + }, + "deployment_registry": { + "type": "string" + }, + "verifier_proof": { + "type": "string", + "nullable": true + }, + "escrow_settlement": { + "type": "string", + "nullable": true + }, + "deploy_bundle": { + "type": "string", + "nullable": true + } + } + } + } + } + ] } } } } }, - - "ForensicsTimelineResponse": { + "EvaluatorDeploymentPlanResponse": { "type": "object", "properties": { - "wallet": { "type": "string" }, - "risk_level": { "type": "string", "enum": ["clear", "watch", "elevated", "critical"] }, - "events": { - "type": "array", - "items": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { "type": "string", "enum": ["score_snapshot"] }, - "timestamp": { "type": "string", "format": "date-time" }, - "score": { "type": "integer", "minimum": 0, "maximum": 100 }, - "confidence": { "type": "number", "minimum": 0, "maximum": 1 }, - "model_version": { "type": "string" } - } - }, - { - "type": "object", - "properties": { - "type": { "type": "string", "enum": ["fraud_report"] }, - "timestamp": { "type": "string", "format": "date-time" }, - "report_id": { "type": "string" }, - "reason": { "type": "string" }, - "penalty_applied": { "type": "integer" } - } - } - ] - } + "standard": { + "type": "string", + "enum": [ + "djd-evaluator-deploy-plan-v1" + ] }, - "count": { "type": "integer", "description": "Total combined events matching the query" }, - "returned": { "type": "integer", "description": "Number of events in this response" }, - "breakdown": { + "network": { "type": "object", "properties": { - "score_snapshots": { "type": "integer" }, - "fraud_reports": { "type": "integer" } + "key": { + "type": "string", + "enum": [ + "base", + "base-sepolia" + ] + }, + "chain_id": { + "type": "integer" + }, + "chain_name": { + "type": "string" + }, + "caip2": { + "type": "string" + }, + "environment": { + "type": "string", + "enum": [ + "mainnet", + "testnet" + ] + } } }, - "report_summary": { + "verdict_id": { + "type": "string" + }, + "verifier": { "type": "object", "properties": { - "report_count": { "type": "integer" }, - "total_penalty_applied": { "type": "integer" } + "contract": { + "type": "string", + "enum": [ + "DJDEvaluatorVerdictVerifier" + ] + }, + "constructor": { + "type": "object", + "properties": { + "initial_signer": { + "type": "string", + "nullable": true + } + } + }, + "deployment_ready": { + "type": "boolean" + }, + "reason": { + "type": "string", + "nullable": true + } } }, - "period": { + "escrow": { "type": "object", "properties": { - "from": { "type": "string", "format": "date-time", "nullable": true }, - "to": { "type": "string", "format": "date-time", "nullable": true } + "contract": { + "type": "string", + "enum": [ + "DJDEvaluatorEscrowSettlementExample" + ] + }, + "constructor": { + "type": "object", + "properties": { + "verifier": { + "type": "string", + "nullable": true + }, + "provider": { + "type": "string" + }, + "counterparty": { + "type": "string" + }, + "escrow_id_hash": { + "type": "string" + }, + "escrow_id": { + "type": "string", + "nullable": true + }, + "verifier_source": { + "type": "string", + "enum": [ + "explicit", + "published_registry", + "manual_required" + ] + } + } + }, + "deployment_ready": { + "type": "boolean" + }, + "reason": { + "type": "string", + "nullable": true + } } }, - "trend": { + "links": { "type": "object", - "nullable": true, "properties": { - "direction": { "type": "string", "enum": ["improving", "declining", "stable"] }, - "change_pct": { "type": "number" }, - "avg_score": { "type": "number" }, - "min_score": { "type": "integer" }, - "max_score": { "type": "integer" } + "verifier_package": { + "type": "string" + }, + "verifier_proof": { + "type": "string" + }, + "escrow_settlement": { + "type": "string" + }, + "deployment_registry": { + "type": "string" + } } }, - "trajectory": { - "type": "object", - "properties": { - "velocity": { "type": "number", "nullable": true }, - "momentum": { "type": "number", "nullable": true }, - "direction": { "type": "string", "enum": ["improving", "declining", "stable", "volatile", "new"] }, - "volatility": { "type": "number" }, - "modifier": { "type": "integer" }, - "dataPoints": { "type": "integer" }, - "spanDays": { "type": "number" } + "notes": { + "type": "array", + "items": { + "type": "string" } } } }, - - "ForensicsReportsResponse": { + "EvaluatorArtifactPackageResponse": { "type": "object", "properties": { - "wallet": { "type": "string" }, - "risk_level": { "type": "string", "enum": ["clear", "watch", "elevated", "critical"] }, - "reports": { - "type": "array", - "items": { - "type": "object", - "properties": { - "report_id": { "type": "string" }, - "reason": { "type": "string" }, - "details": { "type": "string" }, - "created_at": { "type": "string", "format": "date-time" }, - "penalty_applied": { "type": "integer" } + "standard": { + "type": "string", + "enum": [ + "djd-evaluator-artifact-package-v1" + ] + }, + "available": { + "type": "boolean" + }, + "compiler": { + "anyOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "solc" + ] + }, + "version": { + "type": "string" + }, + "via_ir": { + "type": "boolean" + } + } + }, + { + "type": "null" } - } + ] }, - "count": { "type": "integer", "description": "Total reports matching the query" }, - "returned": { "type": "integer", "description": "Number of reports in this response" }, - "unique_reporters": { "type": "integer" }, - "total_penalty_applied": { "type": "integer" }, - "period": { + "summary": { "type": "object", "properties": { - "from": { "type": "string", "format": "date-time", "nullable": true }, - "to": { "type": "string", "format": "date-time", "nullable": true } + "total": { + "type": "integer" + }, + "deployable": { + "type": "integer" + }, + "interfaces": { + "type": "integer" + } } - } - } - }, - - "ForensicsWatchlistResponse": { - "type": "object", - "properties": { - "wallets": { + }, + "contracts": { "type": "array", "items": { "type": "object", "properties": { - "rank": { "type": "integer" }, - "wallet": { "type": "string" }, - "risk_level": { "type": "string", "enum": ["clear", "watch", "elevated", "critical"] }, - "current_score": { "type": "integer", "minimum": 0, "maximum": 100, "nullable": true }, - "current_tier": { "type": "string", "nullable": true }, - "report_count": { "type": "integer" }, - "unique_reporters": { "type": "integer" }, - "total_penalty_applied": { "type": "integer" }, - "most_recent_report_at": { "type": "string", "format": "date-time" } + "contract": { + "type": "string" + }, + "artifact_kind": { + "type": "string", + "enum": [ + "contract", + "interface" + ] + }, + "deployable": { + "type": "boolean" + }, + "artifact_path": { + "type": "string" + }, + "source_path": { + "type": "string" + }, + "source_sha256": { + "type": "string" + }, + "bytecode_sha256": { + "type": "string" + }, + "deployed_bytecode_sha256": { + "type": "string" + }, + "constructor": { + "anyOf": [ + { + "type": "object", + "properties": { + "inputs": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + } + }, + { + "type": "null" + } + ] + }, + "abi": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + }, + "bytecode": { + "type": "string" + }, + "deployed_bytecode": { + "type": "string" + }, + "method_identifiers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } } } }, - "count": { "type": "integer", "description": "Total wallets matching the query" }, - "returned": { "type": "integer", "description": "Number of wallets in this response" }, - "period": { + "links": { "type": "object", "properties": { - "from": { "type": "string", "format": "date-time", "nullable": true }, - "to": { "type": "string", "format": "date-time", "nullable": true } + "verifier_package": { + "type": "string" + }, + "deploy_plan": { + "type": "string" + }, + "docs": { + "type": "string" + } } - } - } - }, - - "ForensicsFeedResponse": { - "type": "object", - "properties": { - "incidents": { + }, + "notes": { "type": "array", "items": { - "type": "object", - "properties": { - "report_id": { "type": "string" }, - "wallet": { "type": "string" }, - "reason": { "type": "string" }, - "details": { "type": "string" }, - "created_at": { "type": "string", "format": "date-time" }, - "penalty_applied": { "type": "integer" }, - "current_score": { "type": "integer", "minimum": 0, "maximum": 100, "nullable": true }, - "current_tier": { "type": "string", "nullable": true }, - "risk_level": { "type": "string", "enum": ["clear", "watch", "elevated", "critical"] }, - "report_count": { "type": "integer" }, - "unique_reporters": { "type": "integer" }, - "total_penalty_applied": { "type": "integer" } - } - } - }, - "count": { "type": "integer", "description": "Total incidents matching the query" }, - "returned": { "type": "integer", "description": "Number of incidents in this response" }, - "reason_filter": { "type": "string", "nullable": true }, - "period": { - "type": "object", - "properties": { - "from": { "type": "string", "format": "date-time", "nullable": true }, - "to": { "type": "string", "format": "date-time", "nullable": true } + "type": "string" } } } }, - - "CertificationGrantedResponse": { + "EvaluatorDeploymentBundleResponse": { "type": "object", "properties": { - "id": { "type": "integer" }, - "wallet": { "type": "string" }, - "tier": { "type": "string" }, - "score_at_certification": { "type": "integer" }, - "granted_at": { "type": "string", "format": "date-time" }, - "expires_at": { "type": "string", "format": "date-time" }, - "is_active": { "type": "boolean" }, - "message": { "type": "string" }, - "links": { + "standard": { + "type": "string", + "enum": [ + "djd-evaluator-deploy-bundle-v1" + ] + }, + "network": { "type": "object", "properties": { - "certification_badge": { "type": "string", "format": "uri" }, - "score_badge": { "type": "string", "format": "uri" }, - "standards_document": { "type": "string", "format": "uri" }, - "evaluator_preview": { "type": "string", "format": "uri" }, - "agent_profile": { "type": "string", "format": "uri" }, - "certify_readiness": { "type": "string", "format": "uri" } + "key": { + "type": "string", + "enum": [ + "base", + "base-sepolia" + ] + }, + "chain_id": { + "type": "integer" + }, + "chain_name": { + "type": "string" + }, + "caip2": { + "type": "string" + }, + "environment": { + "type": "string", + "enum": [ + "mainnet", + "testnet" + ] + } } - } - } - }, - "CertificationStatusResponse": { - "type": "object", - "properties": { - "wallet": { "type": "string" }, - "tier": { "type": "string" }, - "score_at_certification": { "type": "integer" }, - "granted_at": { "type": "string", "format": "date-time" }, - "expires_at": { "type": "string", "format": "date-time" }, - "is_valid": { "type": "boolean" }, - "links": { + }, + "verdict_id": { + "type": "string" + }, + "artifacts": { "type": "object", "properties": { - "certification_badge": { "type": "string", "format": "uri" }, - "score_badge": { "type": "string", "format": "uri" }, - "standards_document": { "type": "string", "format": "uri" }, - "evaluator_preview": { "type": "string", "format": "uri" }, - "agent_profile": { "type": "string", "format": "uri" }, - "certify_readiness": { "type": "string", "format": "uri" } + "available": { + "type": "boolean" + }, + "compiler": { + "anyOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "solc" + ] + }, + "version": { + "type": "string" + }, + "via_ir": { + "type": "boolean" + } + } + }, + { + "type": "null" + } + ] + }, + "verifier": { + "anyOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "escrow": { + "anyOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "null" + } + ] + } } - } - } - }, - "CertificationReadinessResponse": { - "type": "object", - "properties": { - "wallet": { "type": "string" }, - "can_apply": { "type": "boolean" }, - "status": { - "type": "string", - "enum": [ - "eligible", - "already_certified", - "not_registered", - "score_missing", - "score_expired", - "score_too_low", - "review_pending", - "review_approved", - "review_needs_info", - "review_rejected" - ] }, - "requirements": { + "deployment": { "type": "object", "properties": { - "registration": { - "type": "object", - "properties": { - "met": { "type": "boolean" } - } - }, - "score": { - "type": "object", - "properties": { - "met": { "type": "boolean" }, - "minimum_score": { "type": "integer" }, - "current_score": { "type": "integer", "nullable": true }, - "current_tier": { "type": "string", "nullable": true }, - "confidence": { "type": "number", "nullable": true }, - "expires_at": { "type": "string", "format": "date-time", "nullable": true }, - "is_fresh": { "type": "boolean" } + "order": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "verifier", + "escrow" + ] } }, - "certification": { + "verifier": { "type": "object", "properties": { - "active": { "type": "boolean" }, - "tier": { "type": "string", "nullable": true }, - "granted_at": { "type": "string", "format": "date-time", "nullable": true }, - "expires_at": { "type": "string", "format": "date-time", "nullable": true } + "action": { + "type": "string", + "enum": [ + "deploy", + "use_existing" + ] + }, + "contract": { + "type": "string", + "enum": [ + "DJDEvaluatorVerdictVerifier" + ] + }, + "current_address": { + "type": "string", + "nullable": true + }, + "constructor": { + "type": "object", + "properties": { + "initial_signer": { + "type": "string", + "nullable": true + } + } + }, + "deployment_ready": { + "type": "boolean" + }, + "reason": { + "type": "string", + "nullable": true + } } }, - "review": { + "escrow": { "type": "object", "properties": { - "exists": { "type": "boolean" }, - "status": { + "action": { "type": "string", - "nullable": true, - "enum": ["pending", "approved", "needs_info", "rejected"] + "enum": [ + "deploy" + ] + }, + "contract": { + "type": "string", + "enum": [ + "DJDEvaluatorEscrowSettlementExample" + ] }, - "requested_at": { "type": "string", "format": "date-time", "nullable": true }, - "reviewed_at": { "type": "string", "format": "date-time", "nullable": true }, - "reviewed_by": { "type": "string", "nullable": true }, - "review_note": { "type": "string", "nullable": true } + "constructor": { + "type": "object", + "properties": { + "verifier": { + "type": "string", + "nullable": true + }, + "verifier_source": { + "type": "string", + "enum": [ + "existing", + "deployment_output" + ] + }, + "provider": { + "type": "string" + }, + "counterparty": { + "type": "string" + }, + "escrow_id_hash": { + "type": "string" + }, + "escrow_id": { + "type": "string", + "nullable": true + } + } + }, + "deployment_ready": { + "type": "boolean" + }, + "reason": { + "type": "string", + "nullable": true + } } } } }, - "blockers": { + "links": { + "type": "object", + "properties": { + "verifier_package": { + "type": "string" + }, + "verifier_proof": { + "type": "string" + }, + "escrow_settlement": { + "type": "string" + }, + "artifact_package": { + "type": "string" + }, + "bundle": { + "type": "string" + } + } + }, + "notes": { "type": "array", "items": { - "type": "object", - "properties": { - "code": { "type": "string" }, - "message": { "type": "string" } + "type": "string" + } + } + } + }, + "EvaluatorNetworkCatalogResponse": { + "type": "object", + "properties": { + "standard": { + "type": "string", + "enum": [ + "djd-evaluator-network-catalog-v1" + ] + }, + "default_network": { + "type": "object", + "properties": { + "key": { + "type": "string", + "enum": [ + "base", + "base-sepolia" + ] + }, + "chain_id": { + "type": "integer" + }, + "chain_name": { + "type": "string" + }, + "caip2": { + "type": "string" + }, + "environment": { + "type": "string", + "enum": [ + "mainnet", + "testnet" + ] } } }, - "next_steps": { + "supported_networks": { "type": "array", "items": { "type": "object", "properties": { - "code": { "type": "string" }, - "label": { "type": "string" }, - "href": { "type": "string", "format": "uri" } + "key": { + "type": "string", + "enum": [ + "base", + "base-sepolia" + ] + }, + "chain_id": { + "type": "integer" + }, + "chain_name": { + "type": "string" + }, + "caip2": { + "type": "string" + }, + "environment": { + "type": "string", + "enum": [ + "mainnet", + "testnet" + ] + }, + "explorer": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "base_url": { + "type": "string" + } + } + }, + "deployment": { + "type": "object", + "properties": { + "rpc_env_var": { + "type": "string" + }, + "bundle_param": { + "type": "string" + }, + "verifier_package": { + "type": "string" + }, + "deployment_registry": { + "type": "string" + } + } + } } } }, - "payment": { + "signing": { "type": "object", "properties": { - "protocol": { "type": "string" }, - "amount_usdc": { "type": "number" }, - "endpoint": { "type": "string", "format": "uri" } + "active_signer": { + "type": "object", + "properties": { + "configured": { + "type": "boolean" + }, + "source": { + "type": "string", + "enum": [ + "oracle_signer", + "publisher_fallback", + "unconfigured", + "invalid_key" + ] + }, + "address": { + "type": "string", + "nullable": true + }, + "reason": { + "type": "string", + "nullable": true + } + } + } } }, - "links": { + "artifacts": { "type": "object", "properties": { - "certification_status": { "type": "string", "format": "uri" }, - "certification_badge": { "type": "string", "format": "uri" }, - "score_badge": { "type": "string", "format": "uri" }, - "standards_document": { "type": "string", "format": "uri" }, - "evaluator_preview": { "type": "string", "format": "uri" }, - "agent_profile": { "type": "string", "format": "uri" }, - "certify_readiness": { "type": "string", "format": "uri" }, - "certify_overview": { "type": "string", "format": "uri" }, - "certified_directory": { "type": "string", "format": "uri" }, - "apply_endpoint": { "type": "string", "format": "uri" }, - "review_status": { "type": "string", "format": "uri" } + "available": { + "type": "boolean" + } } } } }, - "CertificationReviewRequest": { - "type": "object", - "required": ["wallet"], - "properties": { - "wallet": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$" }, - "note": { "type": "string", "maxLength": 500 } - } - }, - "CertificationReviewResponse": { + "EvaluatorDeploymentRegistryResponse": { "type": "object", "properties": { - "id": { "type": "integer" }, - "wallet": { "type": "string" }, - "requested_by_wallet": { "type": "string" }, - "status": { + "standard": { "type": "string", - "enum": ["pending", "approved", "needs_info", "rejected"] - }, - "requested_at": { "type": "string", "format": "date-time" }, - "updated_at": { "type": "string", "format": "date-time" }, - "reviewed_at": { "type": "string", "format": "date-time", "nullable": true }, - "reviewed_by": { "type": "string", "nullable": true }, - "requested_score": { "type": "integer" }, - "requested_tier": { "type": "string" }, - "requested_confidence": { "type": "number", "nullable": true }, - "score_expires_at": { "type": "string", "format": "date-time", "nullable": true }, - "request_note": { "type": "string", "nullable": true }, - "review_note": { "type": "string", "nullable": true }, - "current_score": { - "type": "object", - "properties": { - "score": { "type": "integer", "nullable": true }, - "tier": { "type": "string", "nullable": true }, - "confidence": { "type": "number", "nullable": true } - } - }, - "profile": { - "type": "object", - "properties": { - "name": { "type": "string", "nullable": true }, - "description": { "type": "string", "nullable": true }, - "github_url": { "type": "string", "format": "uri", "nullable": true }, - "website_url": { "type": "string", "format": "uri", "nullable": true }, - "github_verified": { "type": "boolean" } - } + "enum": [ + "djd-evaluator-deployments-v1" + ] }, - "links": { + "registry": { "type": "object", "properties": { - "agent_profile": { "type": "string", "format": "uri" }, - "readiness": { "type": "string", "format": "uri" }, - "review_status": { "type": "string", "format": "uri" }, - "apply_endpoint": { "type": "string", "format": "uri" }, - "certified_directory": { "type": "string", "format": "uri" } + "available": { + "type": "boolean" + }, + "updated_at": { + "type": "string", + "nullable": true + }, + "deployment_count": { + "type": "integer" + }, + "error": { + "type": "string", + "nullable": true + } } }, - "message": { "type": "string" } - } - }, - "CertificationDirectoryResponse": { - "type": "object", - "properties": { - "as_of": { "type": "string", "format": "date-time" }, - "filters": { + "filter": { "type": "object", "properties": { - "limit": { "type": "integer" }, - "tier": { "type": "string", "nullable": true }, - "search": { "type": "string", "nullable": true }, - "sort": { + "network": { "type": "string", - "enum": ["score", "confidence", "recent", "name"] + "nullable": true, + "enum": [ + "base", + "base-sepolia", + null + ] } } }, - "total": { "type": "integer" }, - "returned": { "type": "integer" }, - "certifications": { + "networks": { "type": "array", "items": { "type": "object", "properties": { - "wallet": { "type": "string" }, - "certification": { - "type": "object", - "properties": { - "id": { "type": "integer" }, - "tier": { "type": "string" }, - "score_at_certification": { "type": "integer" }, - "granted_at": { "type": "string", "format": "date-time" }, - "expires_at": { "type": "string", "format": "date-time" } - } + "key": { + "type": "string", + "enum": [ + "base", + "base-sepolia" + ] }, - "current_score": { - "type": "object", - "properties": { - "score": { "type": "integer", "nullable": true }, - "tier": { "type": "string", "nullable": true }, - "confidence": { "type": "number", "nullable": true } - } + "chain_id": { + "type": "integer" }, - "profile": { - "type": "object", - "properties": { - "name": { "type": "string", "nullable": true }, - "description": { "type": "string", "nullable": true }, - "github_url": { "type": "string", "format": "uri", "nullable": true }, - "website_url": { "type": "string", "format": "uri", "nullable": true }, - "github_verified": { "type": "boolean" } - } + "chain_name": { + "type": "string" }, - "links": { + "caip2": { + "type": "string" + }, + "environment": { + "type": "string", + "enum": [ + "mainnet", + "testnet" + ] + }, + "explorer": { "type": "object", "properties": { - "certification_badge": { "type": "string", "format": "uri" }, - "score_badge": { "type": "string", "format": "uri" }, - "standards_document": { "type": "string", "format": "uri" }, - "evaluator_preview": { "type": "string", "format": "uri" }, - "agent_profile": { "type": "string", "format": "uri" }, - "certify_readiness": { "type": "string", "format": "uri" } + "name": { + "type": "string" + }, + "base_url": { + "type": "string" + } } + }, + "rpc_env_var": { + "type": "string" + }, + "deployed": { + "type": "boolean" + }, + "deployment": { + "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "published_at": { + "type": "string", + "nullable": true + }, + "verdict_id": { + "type": "string", + "nullable": true + }, + "deployer": { + "type": "string", + "nullable": true + }, + "contracts": { + "type": "object", + "properties": { + "verifier": { + "type": "object", + "properties": { + "contract": { + "type": "string", + "nullable": true + }, + "address": { + "type": "string", + "nullable": true + }, + "tx_hash": { + "type": "string", + "nullable": true + }, + "action": { + "type": "string", + "nullable": true + } + } + }, + "escrow": { + "type": "object", + "properties": { + "contract": { + "type": "string", + "nullable": true + }, + "address": { + "type": "string", + "nullable": true + }, + "tx_hash": { + "type": "string", + "nullable": true + }, + "action": { + "type": "string", + "nullable": true + } + } + } + } + }, + "verification": { + "type": "object", + "properties": { + "oracle_signer": { + "type": "string", + "nullable": true + }, + "escrow_verifier": { + "type": "string", + "nullable": true + }, + "escrow_provider": { + "type": "string", + "nullable": true + }, + "escrow_counterparty": { + "type": "string", + "nullable": true + }, + "escrow_id_hash": { + "type": "string", + "nullable": true + } + } + }, + "inputs": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "nullable": true + }, + "counterparty": { + "type": "string", + "nullable": true + }, + "escrow_id": { + "type": "string", + "nullable": true + } + } + }, + "checks": { + "type": "object", + "properties": { + "preflight": { + "type": "boolean", + "nullable": true + }, + "verified": { + "type": "boolean", + "nullable": true + }, + "smoked": { + "type": "boolean", + "nullable": true + }, + "health": { + "type": "boolean", + "nullable": true + }, + "staged": { + "type": "boolean", + "nullable": true + } + } + }, + "explorer": { + "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "verifier_address": { + "type": "string", + "nullable": true + }, + "verifier_transaction": { + "type": "string", + "nullable": true + }, + "escrow_address": { + "type": "string", + "nullable": true + }, + "escrow_transaction": { + "type": "string", + "nullable": true + } + } + } + ] + }, + "links": { + "type": "object", + "properties": { + "verifier_package": { + "type": "string" + }, + "deployment_registry": { + "type": "string" + }, + "verifier_proof": { + "type": "string", + "nullable": true + }, + "escrow_settlement": { + "type": "string", + "nullable": true + }, + "deploy_bundle": { + "type": "string", + "nullable": true + } + } + } + } + } + ] } } } } } }, - "CertificationAdminItem": { - "type": "object", - "properties": { - "id": { "type": "integer" }, - "wallet": { "type": "string" }, - "tier": { "type": "string" }, - "score_at_certification": { "type": "integer" }, - "granted_at": { "type": "string", "format": "date-time" }, - "expires_at": { "type": "string", "format": "date-time" }, - "is_active": { "type": "boolean" }, - "revoked_at": { "type": "string", "format": "date-time", "nullable": true }, - "revocation_reason": { "type": "string", "nullable": true } - } - }, - "CertificationRevenueResponse": { + "EvaluatorDeploymentPromotionResponse": { "type": "object", "properties": { - "total_certifications": { "type": "integer" }, - "active_certifications": { "type": "integer" }, - "revoked_certifications": { "type": "integer" }, - "gross_revenue_usd": { "type": "number" }, - "net_revenue_usd": { "type": "number" }, - "price_per_cert_usd": { "type": "integer", "example": 99 }, - "by_month": { - "type": "array", - "items": { - "type": "object", - "properties": { - "month": { "type": "string", "example": "2026-02" }, - "count": { "type": "integer" }, - "revoked_count": { "type": "integer" }, - "gross_revenue_usd":{ "type": "number" }, - "net_revenue_usd": { "type": "number" } + "standard": { + "type": "string", + "enum": [ + "djd-evaluator-promotion-bundle-v1" + ] + }, + "ready": { + "type": "boolean" + }, + "reason": { + "type": "string", + "nullable": true, + "enum": [ + "deployment_not_published", + null + ] + }, + "source": { + "type": "string", + "enum": [ + "published_registry" + ] + }, + "registry": { + "type": "object", + "properties": { + "available": { + "type": "boolean" + }, + "updated_at": { + "type": "string", + "nullable": true + }, + "error": { + "type": "string", + "nullable": true + } + } + }, + "network": { + "type": "object", + "properties": { + "key": { + "type": "string", + "enum": [ + "base", + "base-sepolia" + ] + }, + "chain_id": { + "type": "integer" + }, + "chain_name": { + "type": "string" + }, + "caip2": { + "type": "string" + }, + "environment": { + "type": "string", + "enum": [ + "mainnet", + "testnet" + ] + }, + "rpc_env_var": { + "type": "string" + }, + "explorer": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "base_url": { + "type": "string" + } + } } } + }, + "deployment": { + "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "published_at": { + "type": "string", + "nullable": true + }, + "verdict_id": { + "type": "string", + "nullable": true + }, + "deployer": { + "type": "string", + "nullable": true + }, + "contracts": { + "type": "object", + "properties": { + "verifier": { + "type": "object", + "properties": { + "contract": { + "type": "string", + "nullable": true + }, + "address": { + "type": "string", + "nullable": true + }, + "tx_hash": { + "type": "string", + "nullable": true + }, + "action": { + "type": "string", + "nullable": true + } + } + }, + "escrow": { + "type": "object", + "properties": { + "contract": { + "type": "string", + "nullable": true + }, + "address": { + "type": "string", + "nullable": true + }, + "tx_hash": { + "type": "string", + "nullable": true + }, + "action": { + "type": "string", + "nullable": true + } + } + } + } + }, + "verification": { + "type": "object", + "properties": { + "oracle_signer": { + "type": "string", + "nullable": true + }, + "escrow_verifier": { + "type": "string", + "nullable": true + }, + "escrow_provider": { + "type": "string", + "nullable": true + }, + "escrow_counterparty": { + "type": "string", + "nullable": true + }, + "escrow_id_hash": { + "type": "string", + "nullable": true + } + } + }, + "inputs": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "nullable": true + }, + "counterparty": { + "type": "string", + "nullable": true + }, + "escrow_id": { + "type": "string", + "nullable": true + } + } + }, + "checks": { + "type": "object", + "properties": { + "preflight": { + "type": "boolean", + "nullable": true + }, + "verified": { + "type": "boolean", + "nullable": true + }, + "smoked": { + "type": "boolean", + "nullable": true + }, + "health": { + "type": "boolean", + "nullable": true + }, + "staged": { + "type": "boolean", + "nullable": true + } + } + }, + "explorer": { + "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "verifier_address": { + "type": "string", + "nullable": true + }, + "verifier_transaction": { + "type": "string", + "nullable": true + }, + "escrow_address": { + "type": "string", + "nullable": true + }, + "escrow_transaction": { + "type": "string", + "nullable": true + } + } + } + ] + }, + "links": { + "type": "object", + "properties": { + "verifier_package": { + "type": "string" + }, + "deployment_registry": { + "type": "string" + }, + "verifier_proof": { + "type": "string", + "nullable": true + }, + "escrow_settlement": { + "type": "string", + "nullable": true + }, + "deploy_bundle": { + "type": "string", + "nullable": true + } + } + } + } + } + ] + }, + "outputs": { + "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "variables": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "generic": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "network_scoped": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "dotenv": { + "type": "string" + }, + "shell": { + "type": "string" + }, + "github_output": { + "type": "string" + } + } + } + ] } } } @@ -3883,27 +12889,70 @@ "responses": { "BadRequest": { "description": "Invalid request parameters", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } }, "PaymentRequired": { "description": "x402 payment required. The response includes a WWW-Authenticate header with payment details. Use an x402-compatible client to pay and retry.", "headers": { - "WWW-Authenticate": { "schema": { "type": "string" } } + "WWW-Authenticate": { + "schema": { + "type": "string" + } + } } }, "RateLimited": { "description": "Rate limit exceeded (120 requests/hour per payer wallet on paid endpoints)", "headers": { - "RateLimit-Limit": { "schema": { "type": "integer" }, "description": "Maximum requests per window" }, - "RateLimit-Remaining": { "schema": { "type": "integer" }, "description": "Requests remaining in current window" }, - "RateLimit-Reset": { "schema": { "type": "string", "format": "date-time" }, "description": "When the current window resets" }, - "Retry-After": { "schema": { "type": "integer" }, "description": "Seconds until rate limit resets" } + "RateLimit-Limit": { + "schema": { + "type": "integer" + }, + "description": "Maximum requests per window" + }, + "RateLimit-Remaining": { + "schema": { + "type": "integer" + }, + "description": "Requests remaining in current window" + }, + "RateLimit-Reset": { + "schema": { + "type": "string", + "format": "date-time" + }, + "description": "When the current window resets" + }, + "Retry-After": { + "schema": { + "type": "integer" + }, + "description": "Seconds until rate limit resets" + } }, - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } }, "Unauthorized": { "description": "Missing or invalid authentication credentials", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } }, "FreeTierExhausted": { "description": "Free tier daily quota exhausted (10 requests/day). Upgrade to paid x402 for unlimited access.", @@ -3912,21 +12961,43 @@ "schema": { "type": "object", "properties": { - "error": { "type": "string" }, - "message": { "type": "string" }, + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, "upgrade": { "type": "object", "properties": { - "docs": { "type": "string" }, - "pricing": { "type": "object" }, - "protocol": { "type": "string" }, - "network": { "type": "string" } + "docs": { + "type": "string" + }, + "pricing": { + "type": "object" + }, + "protocol": { + "type": "string" + }, + "network": { + "type": "string" + } } } } } } } + }, + "NotFound": { + "description": "Resource not found", + "content": { + "application/json": { + "schema": { + "": "#/components/schemas/Error" + } + } + } } }, "securitySchemes": { diff --git a/package-lock.json b/package-lock.json index 02dd784..0655106 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,9 @@ "@types/better-sqlite3": "^7.6.11", "@types/node": "^22.9.0", "@types/uuid": "^10.0.0", + "ganache": "^7.9.2", "pino-pretty": "^13.1.3", + "solc": "^0.8.34", "tsx": "^4.19.2", "typescript": "^5.7.2", "vitest": "^4.0.18" @@ -2055,6 +2057,23 @@ "dev": true, "license": "MIT" }, + "node_modules/command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/dateformat": { "version": "4.6.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", @@ -2233,205 +2252,4137 @@ "ws": "8.17.1" }, "engines": { - "node": ">=14.0.0" + "node": ">=14.0.0" + } + }, + "node_modules/ethers/node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", + "license": "MIT", + "peer": true + }, + "node_modules/ethers/node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethers/node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethers/node_modules/@types/node": { + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/ethers/node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD", + "peer": true + }, + "node_modules/ethers/node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT", + "peer": true + }, + "node_modules/ethers/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-copy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-4.0.2.tgz", + "integrity": "sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/filelist": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.5.tgz", + "integrity": "sha512-ct/ckWBV/9Dg3MlvCXsLcSUyoWwv9mCKqlhLNB2DAuXR/NZolSXlQqP5dyy6guWlPXBhodZyZ5lGPQcbQDxrEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^10.2.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/ganache": { + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/ganache/-/ganache-7.9.2.tgz", + "integrity": "sha512-7gsVVDpO9AhrFyDMWWl7SpMsPpqGcnAzjxz3k32LheIPNd64p2XsY9GYRdhWmKuryb60W1iaWPZWDkFKlbRWHA==", + "bundleDependencies": [ + "@trufflesuite/bigint-buffer", + "keccak", + "leveldown", + "secp256k1" + ], + "dev": true, + "hasShrinkwrap": true, + "license": "MIT", + "dependencies": { + "@trufflesuite/bigint-buffer": "1.1.10", + "@trufflesuite/uws-js-unofficial": "20.30.0-unofficial.0", + "@types/bn.js": "^5.1.0", + "@types/lru-cache": "5.1.1", + "@types/seedrandom": "3.0.1", + "abstract-level": "1.0.3", + "abstract-leveldown": "7.2.0", + "async-eventemitter": "0.2.4", + "emittery": "0.10.0", + "keccak": "3.0.2", + "leveldown": "6.1.0", + "secp256k1": "4.0.3" + }, + "bin": { + "ganache": "dist/node/cli.js", + "ganache-cli": "dist/node/cli.js" + }, + "optionalDependencies": { + "bufferutil": "4.0.5", + "utf-8-validate": "5.0.7" + } + }, + "node_modules/ganache/node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "extraneous": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/ganache/node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "extraneous": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/ganache/node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "extraneous": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ganache/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "extraneous": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/ganache/node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "extraneous": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/ganache/node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "extraneous": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/ganache/node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "extraneous": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/ganache/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "extraneous": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/ganache/node_modules/@microsoft/api-extractor": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.20.1.tgz", + "integrity": "sha512-T7cqcK+JpvHGOj7cD2ZCCWS7Xgru1uOqZwrV/FSUdyKVs5fopZcbBSuetwD/akst3O7Ypryg3UOLP54S/vnVmA==", + "extraneous": true, + "dependencies": { + "@microsoft/api-extractor-model": "7.16.0", + "@microsoft/tsdoc": "0.13.2", + "@microsoft/tsdoc-config": "~0.15.2", + "@rushstack/node-core-library": "3.45.1", + "@rushstack/rig-package": "0.3.8", + "@rushstack/ts-command-line": "4.10.7", + "colors": "~1.2.1", + "lodash": "~4.17.15", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "source-map": "~0.6.1", + "typescript": "~4.5.2" + }, + "bin": { + "api-extractor": "bin/api-extractor" + } + }, + "node_modules/ganache/node_modules/@microsoft/api-extractor-model": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.16.0.tgz", + "integrity": "sha512-0FOrbNIny8mzBrzQnSIkEjAXk0JMSnPmWYxt3ZDTPVg9S8xIPzB6lfgTg9+Mimu0RKCpGKBpd+v2WcR5vGzyUQ==", + "extraneous": true, + "dependencies": { + "@microsoft/tsdoc": "0.13.2", + "@microsoft/tsdoc-config": "~0.15.2", + "@rushstack/node-core-library": "3.45.1" + } + }, + "node_modules/ganache/node_modules/@microsoft/api-extractor/node_modules/typescript": { + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", + "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", + "extraneous": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/ganache/node_modules/@microsoft/tsdoc": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.13.2.tgz", + "integrity": "sha512-WrHvO8PDL8wd8T2+zBGKrMwVL5IyzR3ryWUsl0PXgEV0QHup4mTLi0QcATefGI6Gx9Anu7vthPyyyLpY0EpiQg==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@microsoft/tsdoc-config": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.15.2.tgz", + "integrity": "sha512-mK19b2wJHSdNf8znXSMYVShAHktVr/ib0Ck2FA3lsVBSEhSI/TfXT7DJQkAYgcztTuwazGcg58ZjYdk0hTCVrA==", + "extraneous": true, + "dependencies": { + "@microsoft/tsdoc": "0.13.2", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" + } + }, + "node_modules/ganache/node_modules/@microsoft/tsdoc-config/node_modules/resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "extraneous": true, + "dependencies": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + }, + "node_modules/ganache/node_modules/@rushstack/node-core-library": { + "version": "3.45.1", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.45.1.tgz", + "integrity": "sha512-BwdssTNe007DNjDBxJgInHg8ePytIPyT0La7ZZSQZF9+rSkT42AygXPGvbGsyFfEntjr4X37zZSJI7yGzL16cQ==", + "extraneous": true, + "dependencies": { + "@types/node": "12.20.24", + "colors": "~1.2.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "timsort": "~0.3.0", + "z-schema": "~5.0.2" + } + }, + "node_modules/ganache/node_modules/@rushstack/node-core-library/node_modules/@types/node": { + "version": "12.20.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.24.tgz", + "integrity": "sha512-yxDeaQIAJlMav7fH5AQqPH1u8YIuhYJXYBzxaQ4PifsU0GDO38MSdmEDeRlIxrKbC6NbEaaEHDanWb+y30U8SQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@rushstack/rig-package": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.3.8.tgz", + "integrity": "sha512-MDWg1xovea99PWloSiYMjFcCLsrdjFtYt6aOyHNs5ojn5mxrzR6U9F83hvbQjTWnKPMvZtr0vcek+4n+OQOp3Q==", + "extraneous": true, + "dependencies": { + "resolve": "~1.17.0", + "strip-json-comments": "~3.1.1" + } + }, + "node_modules/ganache/node_modules/@rushstack/ts-command-line": { + "version": "4.10.7", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.10.7.tgz", + "integrity": "sha512-CjS+DfNXUSO5Ab2wD1GBGtUTnB02OglRWGqfaTcac9Jn45V5MeUOsq/wA8wEeS5Y/3TZ2P1k+IWdVDiuOFP9Og==", + "extraneous": true, + "dependencies": { + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "colors": "~1.2.1", + "string-argv": "~0.3.1" + } + }, + "node_modules/ganache/node_modules/@trufflesuite/bigint-buffer": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@trufflesuite/bigint-buffer/-/bigint-buffer-1.1.10.tgz", + "integrity": "sha512-pYIQC5EcMmID74t26GCC67946mgTJFiLXOT/BYozgrd4UEY2JHEGLhWi9cMiQCt5BSqFEvKkCHNnoj82SRjiEw==", + "dev": true, + "hasInstallScript": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "node-gyp-build": "4.4.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/ganache/node_modules/@trufflesuite/bigint-buffer/node_modules/node-gyp-build": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz", + "integrity": "sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/ganache/node_modules/@trufflesuite/uws-js-unofficial": { + "version": "20.30.0-unofficial.0", + "resolved": "https://registry.npmjs.org/@trufflesuite/uws-js-unofficial/-/uws-js-unofficial-20.30.0-unofficial.0.tgz", + "integrity": "sha512-r5X0aOQcuT6pLwTRLD+mPnAM/nlKtvIK4Z+My++A8tTOR0qTjNRx8UB8jzRj3D+p9PMAp5LnpCUUGmz7/TppwA==", + "dev": true, + "dependencies": { + "ws": "8.13.0" + }, + "optionalDependencies": { + "bufferutil": "4.0.7", + "utf-8-validate": "6.0.3" + } + }, + "node_modules/ganache/node_modules/@trufflesuite/uws-js-unofficial/node_modules/bufferutil": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz", + "integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/ganache/node_modules/@trufflesuite/uws-js-unofficial/node_modules/utf-8-validate": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.3.tgz", + "integrity": "sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/ganache/node_modules/@trufflesuite/uws-js-unofficial/node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/ganache/node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@types/abstract-leveldown": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/abstract-leveldown/-/abstract-leveldown-7.2.0.tgz", + "integrity": "sha512-q5veSX6zjUy/DlDhR4Y4cU0k2Ar+DT2LUraP00T19WLmTO6Se1djepCCaqU6nQrwcJ5Hyo/CWqxTzrrFg8eqbQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@types/argparse": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@types/bn.js": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", + "integrity": "sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/ganache/node_modules/@types/eslint": { + "version": "8.4.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", + "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==", + "extraneous": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/ganache/node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "extraneous": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/ganache/node_modules/@types/estree": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@types/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==", + "dev": true + }, + "node_modules/ganache/node_modules/@types/mocha": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz", + "integrity": "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@types/node": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.0.tgz", + "integrity": "sha512-eMhwJXc931Ihh4tkU+Y7GiLzT/y/DBNpNtr4yU9O2w3SYBsr9NaOPhQlLKRmoWtI54uNwuo0IOUFQjVOTZYRvw==", + "dev": true + }, + "node_modules/ganache/node_modules/@types/seedrandom": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-3.0.1.tgz", + "integrity": "sha512-giB9gzDeiCeloIXDgzFBCgjj1k4WxcDrZtGl6h1IqmUPlxF+Nx8Ve+96QCyDZ/HseB/uvDsKbpib9hU5cU53pw==", + "dev": true + }, + "node_modules/ganache/node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "extraneous": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "node_modules/ganache/node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "extraneous": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/ganache/node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "extraneous": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/ganache/node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "extraneous": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/ganache/node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "extraneous": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/ganache/node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "extraneous": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/ganache/node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "extraneous": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/ganache/node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "extraneous": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/ganache/node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "extraneous": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/ganache/node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "extraneous": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/ganache/node_modules/@webpack-cli/configtest": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@webpack-cli/info": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "extraneous": true, + "dependencies": { + "envinfo": "^7.7.3" + } + }, + "node_modules/ganache/node_modules/@webpack-cli/serve": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/abstract-level": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.3.tgz", + "integrity": "sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA==", + "dev": true, + "dependencies": { + "buffer": "^6.0.3", + "catering": "^2.1.0", + "is-buffer": "^2.0.5", + "level-supports": "^4.0.0", + "level-transcoder": "^1.0.1", + "module-error": "^1.0.1", + "queue-microtask": "^1.2.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/ganache/node_modules/abstract-level/node_modules/level-supports": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-4.0.1.tgz", + "integrity": "sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/ganache/node_modules/abstract-leveldown": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-7.2.0.tgz", + "integrity": "sha512-DnhQwcFEaYsvYDnACLZhMmCWd3rkOeEvglpa4q5i/5Jlm3UIsWaxVzuXvDLFCSCWRO3yy2/+V/G7FusFgejnfQ==", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "buffer": "^6.0.3", + "catering": "^2.0.0", + "is-buffer": "^2.0.5", + "level-concat-iterator": "^3.0.0", + "level-supports": "^2.0.1", + "queue-microtask": "^1.2.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "extraneous": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ganache/node_modules/acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "extraneous": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ganache/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "extraneous": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "node_modules/ganache/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "extraneous": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "extraneous": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "extraneous": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ganache/node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "extraneous": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/ganache/node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "extraneous": true, + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ganache/node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/assert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", + "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "extraneous": true, + "dependencies": { + "es6-object-assign": "^1.1.0", + "is-nan": "^1.2.1", + "object-is": "^1.0.1", + "util": "^0.12.0" + } + }, + "node_modules/ganache/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/ganache/node_modules/async-eventemitter": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/async-eventemitter/-/async-eventemitter-0.2.4.tgz", + "integrity": "sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw==", + "dev": true, + "dependencies": { + "async": "^2.4.0" + } + }, + "node_modules/ganache/node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "extraneous": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/ganache/node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "extraneous": true, + "engines": { + "node": "*" + } + }, + "node_modules/ganache/node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "extraneous": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/ganache/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "extraneous": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/ganache/node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "extraneous": true, + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ganache/node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "extraneous": true, + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/ganache/node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "extraneous": true, + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/ganache/node_modules/browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "extraneous": true, + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/ganache/node_modules/browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "extraneous": true, + "dependencies": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "node_modules/ganache/node_modules/browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "extraneous": true, + "dependencies": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/ganache/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/ganache/node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/bufferutil": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.5.tgz", + "integrity": "sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/ganache/node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "extraneous": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "node_modules/ganache/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "extraneous": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/caniuse-lite": { + "version": "1.0.30001435", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001435.tgz", + "integrity": "sha512-kdCkUTjR+v4YAJelyiDTqiu82BDr4W4CP5sgTA0ZBmqn30XfS2ZghPLMowik9TPhS+psWJiUNxsqLyurDbmutA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/catering": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz", + "integrity": "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "extraneous": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "extraneous": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "extraneous": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/ganache/node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "extraneous": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ganache/node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "extraneous": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ganache/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "extraneous": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/ganache/node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "extraneous": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "extraneous": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ganache/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/colors": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz", + "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==", + "extraneous": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/ganache/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "extraneous": true + }, + "node_modules/ganache/node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "extraneous": true, + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/ganache/node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "extraneous": true, + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/ganache/node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "extraneous": true, + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/ganache/node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "extraneous": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/ganache/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "extraneous": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ganache/node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "extraneous": true, + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ganache/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "extraneous": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ganache/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "extraneous": true + }, + "node_modules/ganache/node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "extraneous": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "extraneous": true, + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "extraneous": true, + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/ganache/node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "extraneous": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/ganache/node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "extraneous": true, + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/ganache/node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/electron-to-chromium": { + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/ganache/node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/ganache/node_modules/emittery": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.0.tgz", + "integrity": "sha512-AGvFfs+d0JKCJQ4o01ASQLGPmSCxgfU9RFXvzPvZdjKK8oscynksuJhWrSTSw7j7Ep/sZct5b5ZhYCi8S/t0HQ==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/ganache/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "extraneous": true + }, + "node_modules/ganache/node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "extraneous": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ganache/node_modules/enhanced-resolve": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", + "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", + "extraneous": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ganache/node_modules/envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "extraneous": true, + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ganache/node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "extraneous": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "extraneous": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "extraneous": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ganache/node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "extraneous": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/ganache/node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "extraneous": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/ganache/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "extraneous": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/ganache/node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "extraneous": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/ganache/node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "extraneous": true, + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/ganache/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "extraneous": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "extraneous": true + }, + "node_modules/ganache/node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "extraneous": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/ganache/node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "extraneous": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "extraneous": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "extraneous": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/ganache/node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "extraneous": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/ganache/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "extraneous": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/ganache/node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "extraneous": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/ganache/node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "extraneous": true + }, + "node_modules/ganache/node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "extraneous": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/ganache/node_modules/get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "extraneous": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "node_modules/ganache/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "extraneous": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "extraneous": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ganache/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "extraneous": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ganache/node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "extraneous": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + } + }, + "node_modules/ganache/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "extraneous": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/ganache/node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "extraneous": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/ganache/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "extraneous": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + } + }, + "node_modules/ganache/node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "extraneous": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "extraneous": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "extraneous": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ganache/node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/ganache/node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "extraneous": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/ganache/node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/ganache/node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", + "extraneous": true + }, + "node_modules/ganache/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "extraneous": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ganache/node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/ganache/node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "extraneous": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "extraneous": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/ganache/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/ganache/node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "extraneous": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/ganache/node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "extraneous": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "extraneous": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ganache/node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "extraneous": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "extraneous": true, + "dependencies": { + "has": "^1.0.3" + } + }, + "node_modules/ganache/node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "extraneous": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ganache/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "extraneous": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "extraneous": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ganache/node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "extraneous": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "extraneous": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/ganache/node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "extraneous": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ganache/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "extraneous": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "extraneous": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "extraneous": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ganache/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "extraneous": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/ganache/node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "extraneous": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/ganache/node_modules/js-yaml/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "extraneous": true + }, + "node_modules/ganache/node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "extraneous": true + }, + "node_modules/ganache/node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "extraneous": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "extraneous": true, + "dependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/ganache/node_modules/keccak": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz", + "integrity": "sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==", + "dev": true, + "hasInstallScript": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ganache/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "extraneous": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ganache/node_modules/level-concat-iterator": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-3.1.0.tgz", + "integrity": "sha512-BWRCMHBxbIqPxJ8vHOvKUsaO0v1sLYZtjN3K2iZJsRBYtp+ONsY6Jfi6hy9K3+zolgQRryhIn2NRZjZnWJ9NmQ==", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "catering": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/level-js": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/level-js/-/level-js-6.1.0.tgz", + "integrity": "sha512-i7mPtkZm68aewfv0FnIUWvFUFfoyzIvVKnUmuQGrelEkP72vSPTaA1SGneWWoCV5KZJG4wlzbJLp1WxVNGuc6A==", + "extraneous": true, + "dependencies": { + "abstract-leveldown": "^7.2.0", + "buffer": "^6.0.3", + "inherits": "^2.0.3", + "ltgt": "^2.1.2", + "run-parallel-limit": "^1.1.0" + } + }, + "node_modules/ganache/node_modules/level-supports": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-2.1.0.tgz", + "integrity": "sha512-E486g1NCjW5cF78KGPrMDRBYzPuueMZ6VBXHT6gC7A8UYWGiM14fGgp+s/L1oFfDWSPV/+SFkYCmZ0SiESkRKA==", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/level-transcoder": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/level-transcoder/-/level-transcoder-1.0.1.tgz", + "integrity": "sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==", + "dev": true, + "dependencies": { + "buffer": "^6.0.3", + "module-error": "^1.0.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/ganache/node_modules/leveldown": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/leveldown/-/leveldown-6.1.0.tgz", + "integrity": "sha512-8C7oJDT44JXxh04aSSsfcMI8YiaGRhOFI9/pMEL7nWJLVsWajDPTRxsSHTM2WcTVY5nXM+SuRHzPPi0GbnDX+w==", + "dev": true, + "hasInstallScript": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "abstract-leveldown": "^7.2.0", + "napi-macros": "~2.0.0", + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/ganache/node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "extraneous": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/ganache/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "extraneous": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/ganache/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "extraneous": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/ganache/node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "extraneous": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "extraneous": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/ltgt": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", + "integrity": "sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/mcl-wasm": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mcl-wasm/-/mcl-wasm-0.9.0.tgz", + "integrity": "sha512-rvU7L/68ZrDk4dpPZteiEqvK9nB/1XbbHmuLK6qIvc4xuuJb/iv1p5X3KEyq6AYatLnc+zbdSlLXTlKgTnCRZQ==", + "extraneous": true, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/ganache/node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "extraneous": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/ganache/node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "extraneous": true + }, + "node_modules/ganache/node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "extraneous": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/ganache/node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "extraneous": true, + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/ganache/node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "extraneous": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ganache/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "extraneous": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ganache/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "extraneous": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/ganache/node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/ganache/node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "extraneous": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ganache/node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "extraneous": true + }, + "node_modules/ganache/node_modules/mocha": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", + "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", + "extraneous": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.2", + "debug": "4.3.2", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.7", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.25", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.1.5", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/ganache/node_modules/module-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/module-error/-/module-error-1.0.2.tgz", + "integrity": "sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/nanoid": { + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "extraneous": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/ganache/node_modules/napi-macros": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz", + "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/ganache/node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/ganache/node_modules/node-gyp-build": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", + "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/ganache/node_modules/node-loader": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/node-loader/-/node-loader-1.0.2.tgz", + "integrity": "sha512-myxAxpyMR7knjA4Uzwf3gjxaMtxSWj2vpm9o6AYWWxQ1S3XMBNeG2vzYcp/5eW03cBGfgSxyP+wntP8qhBJNhQ==", + "extraneous": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/ganache/node_modules/node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "extraneous": true + }, + "node_modules/ganache/node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "extraneous": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ganache/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "extraneous": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "extraneous": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "extraneous": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "extraneous": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/ganache/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "extraneous": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", + "extraneous": true + }, + "node_modules/ganache/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "extraneous": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "extraneous": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "extraneous": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "extraneous": true, + "dependencies": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/ganache/node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "extraneous": true + }, + "node_modules/ganache/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "extraneous": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ganache/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "extraneous": true, + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/ganache/node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "extraneous": true, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/ganache/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "extraneous": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "extraneous": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "extraneous": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "extraneous": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "extraneous": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "extraneous": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/ganache/node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "extraneous": true, + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/ganache/node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "extraneous": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/ganache/node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "extraneous": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/ganache/node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "extraneous": true, + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/ganache/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ganache/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "extraneous": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/ganache/node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "extraneous": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/ganache/node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "extraneous": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ganache/node_modules/resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "extraneous": true, + "dependencies": { + "path-parse": "^1.0.6" + } + }, + "node_modules/ganache/node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "extraneous": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "extraneous": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/ganache/node_modules/run-parallel-limit": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz", + "integrity": "sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==", + "extraneous": true, + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/ganache/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/ganache/node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "extraneous": true + }, + "node_modules/ganache/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "extraneous": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/ganache/node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", + "dev": true, + "hasInstallScript": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "elliptic": "^6.5.4", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ganache/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "extraneous": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "extraneous": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/ganache/node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "extraneous": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/ganache/node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "extraneous": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "extraneous": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/shebang-loader": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/shebang-loader/-/shebang-loader-0.0.1.tgz", + "integrity": "sha512-nQvhUHvKyzGK5aqPxHfHB5nlAN2EZ2U61S2G0YrxAuCRU5iGhFcxxRiaAdb18UoRS1zVMhRz4gdQ1xFEg3AOyA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "extraneous": true, + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ganache/node_modules/shx": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.3.tgz", + "integrity": "sha512-nZJ3HFWVoTSyyB+evEKjJ1STiixGztlqwKLTUNV5KqMWtGey9fTd4KU1gdZ1X9BV6215pswQ/Jew9NsuS/fNDA==", + "extraneous": true, + "dependencies": { + "minimist": "^1.2.3", + "shelljs": "^0.8.4" + }, + "bin": { + "shx": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "extraneous": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ganache/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "extraneous": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/ganache/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "extraneous": true + }, + "node_modules/ganache/node_modules/stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "extraneous": true, + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, + "node_modules/ganache/node_modules/stream-http": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", + "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", + "extraneous": true, + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" + } + }, + "node_modules/ganache/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/ganache/node_modules/string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "extraneous": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/ganache/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "extraneous": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "extraneous": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "extraneous": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "extraneous": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "extraneous": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/terser": { + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz", + "integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==", + "extraneous": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/terser-webpack-plugin": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.5.tgz", + "integrity": "sha512-3luOVHku5l0QBeYS8r4CdHYWEGMmIj3H1U64jgkdZzECcSOJAyJ9TjuqcQZvw1Y+4AOBN9SeYJPJmFn2cM4/2g==", + "extraneous": true, + "dependencies": { + "jest-worker": "^27.0.6", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1", + "terser": "^5.7.2" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/ganache/node_modules/timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==", + "extraneous": true + }, + "node_modules/ganache/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "extraneous": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ganache/node_modules/ts-loader": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.3.1.tgz", + "integrity": "sha512-OkyShkcZTsTwyS3Kt7a4rsT/t2qvEVQuKCTg4LJmpj9fhFR7ukGdZwV6Qq3tRUkqcXtfGpPR7+hFKHCG/0d3Lw==", + "extraneous": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/ganache/node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "extraneous": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + } + }, + "node_modules/ganache/node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "extraneous": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/ganache/node_modules/typescript": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "extraneous": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ganache/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "extraneous": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/ganache/node_modules/update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "extraneous": true, + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + } + }, + "node_modules/ganache/node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "extraneous": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/ganache/node_modules/utf-8-validate": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.7.tgz", + "integrity": "sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/ganache/node_modules/util": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", + "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "extraneous": true, + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/ganache/node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/ganache/node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "extraneous": true + }, + "node_modules/ganache/node_modules/validator": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "extraneous": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/ganache/node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "extraneous": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ganache/node_modules/webpack": { + "version": "5.65.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.65.0.tgz", + "integrity": "sha512-Q5or2o6EKs7+oKmJo7LaqZaMOlDWQse9Tm5l1WAfU/ujLGN5Pb0SqGeVkN/4bpPmEqEP5RnVhiqsOtWtUVwGRw==", + "extraneous": true, + "dependencies": { + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.50", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.8.3", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.4", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.3.1", + "webpack-sources": "^3.2.2" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ganache/node_modules/webpack-cli": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.1.tgz", + "integrity": "sha512-JYRFVuyFpzDxMDB+v/nanUdQYcZtqFPGzmlW4s+UkPMFhSpfRNmf1z4AwYcHJVdvEFAM7FFCQdNTpsBYhDLusQ==", + "extraneous": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.1.0", + "@webpack-cli/info": "^1.4.0", + "@webpack-cli/serve": "^1.6.0", + "colorette": "^2.0.14", + "commander": "^7.0.0", + "execa": "^5.0.0", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ganache/node_modules/webpack-cli/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "extraneous": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/ganache/node_modules/webpack-cli/node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "extraneous": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/ganache/node_modules/webpack-cli/node_modules/rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "extraneous": true, + "dependencies": { + "resolve": "^1.9.0" + }, + "engines": { + "node": ">= 0.10" } }, - "node_modules/ethers/node_modules/@adraffy/ens-normalize": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", - "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", - "license": "MIT", - "peer": true - }, - "node_modules/ethers/node_modules/@noble/curves": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", - "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", - "license": "MIT", - "peer": true, + "node_modules/ganache/node_modules/webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "extraneous": true, "dependencies": { - "@noble/hashes": "1.3.2" + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" }, - "funding": { - "url": "https://paulmillr.com/funding/" + "engines": { + "node": ">=10.0.0" } }, - "node_modules/ethers/node_modules/@noble/hashes": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", - "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", - "license": "MIT", - "peer": true, + "node_modules/ganache/node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "extraneous": true, "engines": { - "node": ">= 16" + "node": ">=10.13.0" + } + }, + "node_modules/ganache/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "extraneous": true, + "dependencies": { + "isexe": "^2.0.0" }, - "funding": { - "url": "https://paulmillr.com/funding/" + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/ethers/node_modules/@types/node": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", - "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", - "license": "MIT", - "peer": true, + "node_modules/ganache/node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "extraneous": true, "dependencies": { - "undici-types": "~6.19.2" + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/ethers/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", - "license": "0BSD", - "peer": true + "node_modules/ganache/node_modules/wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "extraneous": true }, - "node_modules/ethers/node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "license": "MIT", - "peer": true + "node_modules/ganache/node_modules/workerpool": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", + "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "extraneous": true }, - "node_modules/ethers/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" + "node_modules/ganache/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "extraneous": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "engines": { + "node": ">=10" } }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" + "node_modules/ganache/node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "extraneous": true }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "license": "(MIT OR WTFPL)", + "node_modules/ganache/node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "extraneous": true, "engines": { - "node": ">=6" + "node": ">=0.4" } }, - "node_modules/expect-type": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", - "dev": true, - "license": "Apache-2.0", + "node_modules/ganache/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "extraneous": true, "engines": { - "node": ">=12.0.0" + "node": ">=10" } }, - "node_modules/fast-copy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-4.0.2.tgz", - "integrity": "sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" + "node_modules/ganache/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "extraneous": true + }, + "node_modules/ganache/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "extraneous": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "engines": { + "node": ">=10" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "license": "MIT" + "node_modules/ganache/node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "extraneous": true, + "engines": { + "node": ">=10" + } }, - "node_modules/filelist": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.5.tgz", - "integrity": "sha512-ct/ckWBV/9Dg3MlvCXsLcSUyoWwv9mCKqlhLNB2DAuXR/NZolSXlQqP5dyy6guWlPXBhodZyZ5lGPQcbQDxrEQ==", - "dev": true, - "license": "Apache-2.0", + "node_modules/ganache/node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "extraneous": true, "dependencies": { - "minimatch": "^10.2.1" + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" }, "engines": { - "node": "20 || >=22" + "node": ">=10" } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "license": "MIT" + "node_modules/ganache/node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "extraneous": true, + "engines": { + "node": ">=6" + } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "node_modules/ganache/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "extraneous": true, "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/z-schema": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.4.tgz", + "integrity": "sha512-gm/lx3hDzJNcLwseIeQVm1UcwhWIKpSB4NqH89pTBtFns4k/HDHudsICtvG05Bvw/Mv3jMyk700y5dadueLHdA==", + "extraneous": true, + "dependencies": { + "commander": "^2.20.3", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" } }, "node_modules/get-caller-file": { @@ -2617,6 +6568,13 @@ "node": ">=10" } }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", + "dev": true, + "license": "MIT" + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -2633,6 +6591,15 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/mimic-response": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", @@ -2765,6 +6732,16 @@ "wrappy": "1" } }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ox": { "version": "0.12.4", "resolved": "https://registry.npmjs.org/ox/-/ox-0.12.4.tgz", @@ -3284,6 +7261,38 @@ "ethers": "^5.6.8 || ^6.0.8" } }, + "node_modules/solc": { + "version": "0.8.34", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.34.tgz", + "integrity": "sha512-qf8HajA1sHhXRV0hMSDXLjVbc4v3Q+SQbL9zok+1WmgVj7Z4oMjMHxaysCzfGtFVqjZdfDDJWyZI+tcx5bO7Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "command-exists": "^1.2.8", + "commander": "^8.1.0", + "follow-redirects": "^1.12.1", + "js-sha3": "0.8.0", + "memorystream": "^0.3.1", + "semver": "^5.5.0", + "tmp": "0.0.33" + }, + "bin": { + "solcjs": "solc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/solc/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/sonic-boom": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz", @@ -3473,6 +7482,19 @@ "node": ">=14.0.0" } }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", diff --git a/package.json b/package.json index 60e1d5d..6c30732 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,19 @@ "start": "node dist/index.js", "start:api": "node dist/api.js", "start:worker": "node dist/worker.js", - "smoke:deploy": "node scripts/post-deploy-smoke.mjs", - "audit:promotion": "node scripts/promotion-audit.mjs", + "smoke:deploy": "node --env-file-if-exists=.env scripts/post-deploy-smoke.mjs", + "audit:promotion": "node --env-file-if-exists=.env scripts/promotion-audit.mjs", + "contracts:compile": "node --env-file-if-exists=.env scripts/compile-contracts.mjs", + "contracts:check": "node --env-file-if-exists=.env scripts/compile-contracts.mjs --check", + "contracts:bootstrap-env": "node --env-file-if-exists=.env scripts/bootstrap-evaluator-env.mjs", + "contracts:preflight": "node --env-file-if-exists=.env scripts/preflight-evaluator-stack.mjs", + "contracts:deploy": "node --env-file-if-exists=.env scripts/deploy-evaluator-stack.mjs", + "contracts:publish": "node --env-file-if-exists=.env scripts/publish-evaluator-stack.mjs", + "contracts:promote": "node --env-file-if-exists=.env scripts/promote-evaluator-stack.mjs", + "contracts:verify": "node --env-file-if-exists=.env scripts/verify-evaluator-stack.mjs", + "contracts:smoke": "node --env-file-if-exists=.env scripts/smoke-evaluator-stack.mjs", + "contracts:stage": "node --env-file-if-exists=.env scripts/stage-evaluator-stack.mjs", + "contracts:test:e2e": "vitest run tests/contracts-e2e.test.ts", "render:fly-config": "node scripts/render-fly-config.mjs", "typecheck": "tsc --noEmit", "test": "vitest run", @@ -42,7 +53,9 @@ "@types/better-sqlite3": "^7.6.11", "@types/node": "^22.9.0", "@types/uuid": "^10.0.0", + "ganache": "^7.9.2", "pino-pretty": "^13.1.3", + "solc": "^0.8.34", "tsx": "^4.19.2", "typescript": "^5.7.2", "vitest": "^4.0.18" diff --git a/scripts/bootstrap-evaluator-env.mjs b/scripts/bootstrap-evaluator-env.mjs new file mode 100644 index 0000000..958f184 --- /dev/null +++ b/scripts/bootstrap-evaluator-env.mjs @@ -0,0 +1,73 @@ +import { writeFileSync } from 'node:fs' +import { pathToFileURL } from 'node:url' +import { runEvaluatorStackPreflight } from './preflight-evaluator-stack.mjs' + +function normalizeFormat(value) { + const normalized = String(value ?? 'dotenv') + .trim() + .toLowerCase() + + if (['dotenv', 'shell', 'json'].includes(normalized)) { + return normalized + } + + return 'dotenv' +} + +function writeTextFile(outputPath, contents) { + if (typeof outputPath !== 'string' || outputPath.trim().length === 0) { + return null + } + + writeFileSync(outputPath, contents) + return outputPath +} + +export async function bootstrapEvaluatorStackEnv(options = {}) { + const preflight = await runEvaluatorStackPreflight(options) + const selectedFormat = normalizeFormat(options.format ?? process.env.DJD_ENV_BOOTSTRAP_FORMAT) + const json = JSON.stringify(preflight.guidance.recommended_env, null, 2) + '\n' + const renderedByFormat = { + dotenv: preflight.guidance.dotenv, + shell: preflight.guidance.shell, + json, + } + const rendered = renderedByFormat[selectedFormat] + + const result = { + standard: 'djd-evaluator-env-bootstrap-v1', + ok: true, + ready: preflight.ok, + network: preflight.network, + preflight: { + ok: preflight.ok, + missing: preflight.guidance.missing, + }, + outputs: { + dotenv: preflight.guidance.dotenv, + shell: preflight.guidance.shell, + json, + selected_format: selectedFormat, + selected_contents: rendered, + }, + file: null, + } + + result.file = writeTextFile( + options.outputPath ?? process.env.DJD_ENV_BOOTSTRAP_OUTPUT_PATH, + rendered, + ) + + return result +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + bootstrapEvaluatorStackEnv() + .then((result) => { + console.log(JSON.stringify(result, null, 2)) + }) + .catch((error) => { + console.error(`[contracts:bootstrap-env] FAILED: ${error instanceof Error ? error.message : String(error)}`) + process.exit(1) + }) +} diff --git a/scripts/compile-contracts.mjs b/scripts/compile-contracts.mjs new file mode 100644 index 0000000..642f7cd --- /dev/null +++ b/scripts/compile-contracts.mjs @@ -0,0 +1,185 @@ +import { createHash } from 'node:crypto' +import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import { fileURLToPath } from 'node:url' +import solc from 'solc' + +const __dirname = dirname(fileURLToPath(import.meta.url)) +const ROOT = join(__dirname, '..') +const CONTRACTS_DIR = join(ROOT, 'contracts') +const ARTIFACTS_DIR = join(ROOT, 'artifacts', 'contracts') +const CHECK_ONLY = process.argv.includes('--check') + +const CONTRACT_FILES = [ + 'IDJDEvaluatorOracleCallback.sol', + 'DJDEvaluatorVerdictVerifier.sol', + 'DJDEvaluatorEscrowSettlementExample.sol', +] + +function sha256(value) { + return createHash('sha256').update(value).digest('hex') +} + +function normalizeHex(value) { + return value && value.length > 0 ? `0x${value}` : '0x' +} + +function loadSources() { + return Object.fromEntries( + CONTRACT_FILES.map((file) => [ + file, + { + content: readFileSync(join(CONTRACTS_DIR, file), 'utf8'), + }, + ]), + ) +} + +function buildCompilerInput(sources) { + return { + language: 'Solidity', + sources, + settings: { + viaIR: true, + optimizer: { + enabled: true, + runs: 200, + }, + outputSelection: { + '*': { + '*': [ + 'abi', + 'evm.bytecode.object', + 'evm.deployedBytecode.object', + 'evm.methodIdentifiers', + 'metadata', + ], + }, + }, + }, + } +} + +function compileContracts() { + const sources = loadSources() + const input = buildCompilerInput(sources) + const output = JSON.parse(solc.compile(JSON.stringify(input))) + + const errors = (output.errors ?? []).filter((entry) => entry.severity === 'error') + if (errors.length > 0) { + throw new Error(errors.map((entry) => entry.formattedMessage).join('\n\n')) + } + + const artifacts = [] + for (const file of CONTRACT_FILES) { + const compiledContracts = output.contracts?.[file] ?? {} + for (const [contractName, artifact] of Object.entries(compiledContracts)) { + const source = sources[file].content + const metadata = JSON.parse(artifact.metadata) + const constructorAbi = (artifact.abi ?? []).find((entry) => entry.type === 'constructor') ?? null + + artifacts.push({ + contract: contractName, + artifact_kind: normalizeHex(artifact.evm?.bytecode?.object ?? '') === '0x' ? 'interface' : 'contract', + source_path: `contracts/${file}`, + source_sha256: sha256(source), + compiler: { + name: 'solc', + version: solc.version(), + optimizer: { + enabled: true, + runs: 200, + }, + via_ir: true, + }, + abi: artifact.abi ?? [], + bytecode: normalizeHex(artifact.evm?.bytecode?.object ?? ''), + deployed_bytecode: normalizeHex(artifact.evm?.deployedBytecode?.object ?? ''), + method_identifiers: artifact.evm?.methodIdentifiers ?? {}, + constructor: constructorAbi + ? { + inputs: constructorAbi.inputs ?? [], + } + : null, + metadata, + }) + } + } + + artifacts.sort((left, right) => left.contract.localeCompare(right.contract)) + + return { + standard: 'djd-solidity-artifacts-v1', + compiler: { + name: 'solc', + version: solc.version(), + via_ir: true, + }, + contracts: artifacts.map((artifact) => ({ + contract: artifact.contract, + artifact_kind: artifact.artifact_kind, + artifact_path: `artifacts/contracts/${artifact.contract}.json`, + source_path: artifact.source_path, + source_sha256: artifact.source_sha256, + bytecode_sha256: sha256(artifact.bytecode), + deployed_bytecode_sha256: sha256(artifact.deployed_bytecode), + })), + artifacts, + } +} + +function writeIfChanged(filePath, nextContents) { + let currentContents = null + try { + currentContents = readFileSync(filePath, 'utf8') + } catch {} + + if (currentContents === nextContents) { + return false + } + + if (CHECK_ONLY) { + throw new Error(`Artifact out of date: ${filePath}`) + } + + writeFileSync(filePath, nextContents) + return true +} + +function main() { + const compiled = compileContracts() + const manifest = { + standard: compiled.standard, + compiler: compiled.compiler, + contracts: compiled.contracts, + } + + if (!CHECK_ONLY) { + rmSync(ARTIFACTS_DIR, { recursive: true, force: true }) + mkdirSync(ARTIFACTS_DIR, { recursive: true }) + } + + let wroteAny = false + for (const artifact of compiled.artifacts) { + const artifactPath = join(ARTIFACTS_DIR, `${artifact.contract}.json`) + const contents = JSON.stringify(artifact, null, 2) + '\n' + if (writeIfChanged(artifactPath, contents)) { + wroteAny = true + } + } + + const manifestPath = join(ARTIFACTS_DIR, 'manifest.json') + if (writeIfChanged(manifestPath, JSON.stringify(manifest, null, 2) + '\n')) { + wroteAny = true + } + + if (CHECK_ONLY) { + console.log(`[contracts] OK ${compiled.artifacts.length} artifacts are up to date`) + } else { + console.log( + `[contracts] ${wroteAny ? 'Wrote' : 'Verified'} ${compiled.artifacts.length} artifacts with ${compiled.compiler.version}`, + ) + } +} + +main() diff --git a/scripts/deploy-evaluator-stack.mjs b/scripts/deploy-evaluator-stack.mjs new file mode 100644 index 0000000..6bd3103 --- /dev/null +++ b/scripts/deploy-evaluator-stack.mjs @@ -0,0 +1,325 @@ +import { readFileSync, writeFileSync } from 'node:fs' +import { pathToFileURL } from 'node:url' +import { createPublicClient, createWalletClient, http, isAddress } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { buildRuntimeChain, resolveRpcUrl } from './evaluator-stack-runtime.mjs' + +function requirePrivateKey(value, fieldName) { + if (!/^0x[a-fA-F0-9]{64}$/.test(value ?? '')) { + throw new Error(`Missing or invalid ${fieldName}`) + } + return value.toLowerCase() +} + +function requireArtifact(entry, contractName) { + if (!entry) { + throw new Error(`Missing artifact entry for ${contractName}`) + } + if (!entry.deployable) { + throw new Error(`Artifact ${contractName} is not deployable`) + } + if (!Array.isArray(entry.abi)) { + throw new Error(`Artifact ${contractName} is missing ABI`) + } + if (typeof entry.bytecode !== 'string' || !entry.bytecode.startsWith('0x')) { + throw new Error(`Artifact ${contractName} is missing bytecode`) + } + return entry +} + +export function validateDeploymentBundle(bundle) { + if (!bundle || bundle.standard !== 'djd-evaluator-deploy-bundle-v1') { + throw new Error('Invalid deployment bundle payload') + } + if (!bundle.artifacts?.available) { + throw new Error('Deployment bundle does not include compiled artifacts') + } + if (!bundle.network?.chain_id || !bundle.network?.chain_name) { + throw new Error('Deployment bundle is missing network metadata') + } + return bundle +} + +function resolveExplorerBaseUrl(chainId) { + if (chainId === 8453) return 'https://basescan.org' + if (chainId === 84532) return 'https://sepolia.basescan.org' + return null +} + +function buildExplorerLinks(chainId, deployment) { + const baseUrl = resolveExplorerBaseUrl(chainId) + if (!baseUrl) { + return null + } + + return { + verifier_address: `${baseUrl}/address/${deployment.contracts.verifier.address}`, + verifier_transaction: deployment.contracts.verifier.tx_hash + ? `${baseUrl}/tx/${deployment.contracts.verifier.tx_hash}` + : null, + escrow_address: `${baseUrl}/address/${deployment.contracts.escrow.address}`, + escrow_transaction: deployment.contracts.escrow.tx_hash + ? `${baseUrl}/tx/${deployment.contracts.escrow.tx_hash}` + : null, + } +} + +export function buildDeploymentBundleUrl(options = {}) { + const apiBaseUrl = options.apiBaseUrl ?? process.env.DJD_API_BASE_URL + const verdictId = options.verdictId ?? process.env.DJD_VERDICT_ID + const network = options.network ?? process.env.DJD_NETWORK + const verifierContract = options.verifierContract ?? process.env.DJD_VERIFIER_CONTRACT + + if (typeof apiBaseUrl !== 'string' || apiBaseUrl.length === 0) { + throw new Error('Missing DJD_API_BASE_URL') + } + if (typeof verdictId !== 'string' || verdictId.length === 0) { + throw new Error('Missing DJD_VERDICT_ID') + } + + const url = new URL('/v1/score/evaluator/deploy/bundle', apiBaseUrl) + url.searchParams.set('id', verdictId) + if (typeof network === 'string' && network.length > 0) { + url.searchParams.set('network', network) + } + if (typeof verifierContract === 'string' && verifierContract.length > 0) { + url.searchParams.set('verifier_contract', verifierContract) + } + return url.toString() +} + +function maybeBuildDeploymentBundleUrl(options = {}) { + const apiBaseUrl = options.apiBaseUrl ?? process.env.DJD_API_BASE_URL + const verdictId = options.verdictId ?? process.env.DJD_VERDICT_ID + + if (typeof apiBaseUrl !== 'string' || apiBaseUrl.length === 0) { + return null + } + if (typeof verdictId !== 'string' || verdictId.length === 0) { + return null + } + + return buildDeploymentBundleUrl(options) +} + +export async function fetchDeploymentBundle(bundleUrl, requestInit = {}) { + const response = await fetch(bundleUrl, requestInit) + if (!response.ok) { + throw new Error(`Bundle request failed: ${response.status} ${response.statusText}`) + } + return validateDeploymentBundle(await response.json()) +} + +function readJsonFile(filePath, label) { + if (typeof filePath !== 'string' || filePath.trim().length === 0) { + throw new Error(`Missing ${label}`) + } + return JSON.parse(readFileSync(filePath, 'utf8')) +} + +export async function resolveDeploymentBundle(options = {}) { + if (options.bundle) { + return validateDeploymentBundle(options.bundle) + } + + const bundlePath = options.bundlePath ?? process.env.DJD_DEPLOY_BUNDLE_PATH + if (typeof bundlePath === 'string' && bundlePath.trim().length > 0) { + return validateDeploymentBundle(readJsonFile(bundlePath, 'DJD_DEPLOY_BUNDLE_PATH')) + } + + const bundleUrl = options.bundleUrl ?? process.env.DJD_DEPLOY_BUNDLE_URL + const derivedBundleUrl = !bundleUrl ? maybeBuildDeploymentBundleUrl(options) : null + const requestHeaders = options.requestHeaders ?? {} + + if (!bundleUrl && !derivedBundleUrl) { + throw new Error('Provide bundle, DJD_DEPLOY_BUNDLE_PATH, DJD_DEPLOY_BUNDLE_URL, or DJD_API_BASE_URL + DJD_VERDICT_ID') + } + + return await fetchDeploymentBundle(bundleUrl ?? derivedBundleUrl, { headers: requestHeaders }) +} + +export function writeDeploymentResult(outputPath, result) { + if (typeof outputPath !== 'string' || outputPath.trim().length === 0) { + return + } + + writeFileSync(outputPath, JSON.stringify(result, null, 2) + '\n') +} + +export async function deployEvaluatorStackFromBundle(options) { + const bundle = validateDeploymentBundle(options.bundle) + const rpcResolution = resolveRpcUrl({ + rpcUrl: options.rpcUrl, + network: bundle.network, + }) + const deployerPrivateKey = requirePrivateKey(options.deployerPrivateKey, 'deployerPrivateKey') + + if (!rpcResolution.ok) { + throw new Error(`Missing rpcUrl. Set ${rpcResolution.expected_envs.join(' or ')}`) + } + const rpcUrl = rpcResolution.rpcUrl + + const deployerAccount = privateKeyToAccount(deployerPrivateKey) + const chain = buildRuntimeChain(bundle.network, rpcUrl) + const publicClient = createPublicClient({ + chain, + transport: http(rpcUrl), + }) + const walletClient = createWalletClient({ + account: deployerAccount, + chain, + transport: http(rpcUrl), + }) + + const verifierArtifact = requireArtifact(bundle.artifacts.verifier, 'DJDEvaluatorVerdictVerifier') + const escrowArtifact = requireArtifact(bundle.artifacts.escrow, 'DJDEvaluatorEscrowSettlementExample') + + let verifierAddress = bundle.deployment.verifier.current_address + let verifierTxHash = null + + if (bundle.deployment.verifier.action === 'deploy') { + const initialSigner = bundle.deployment.verifier.constructor?.initial_signer + if (!isAddress(initialSigner ?? '')) { + throw new Error('Deployment bundle is missing a valid verifier initial_signer') + } + + verifierTxHash = await walletClient.deployContract({ + abi: verifierArtifact.abi, + bytecode: verifierArtifact.bytecode, + args: [initialSigner], + account: deployerAccount, + chain, + }) + const verifierReceipt = await publicClient.waitForTransactionReceipt({ hash: verifierTxHash }) + verifierAddress = verifierReceipt.contractAddress + } + + if (!isAddress(verifierAddress ?? '')) { + throw new Error('Deployment bundle did not resolve a usable verifier address') + } + + const escrowConstructor = bundle.deployment.escrow.constructor + if (!isAddress(escrowConstructor.provider ?? '')) { + throw new Error('Deployment bundle escrow constructor is missing a valid provider address') + } + if (!isAddress(escrowConstructor.counterparty ?? '')) { + throw new Error('Deployment bundle escrow constructor is missing a valid counterparty address') + } + if (typeof escrowConstructor.escrow_id_hash !== 'string' || !/^0x[a-fA-F0-9]{64}$/.test(escrowConstructor.escrow_id_hash)) { + throw new Error('Deployment bundle escrow constructor is missing a valid escrow_id_hash') + } + if (escrowConstructor.escrow_id !== null && typeof escrowConstructor.escrow_id !== 'string') { + throw new Error('Deployment bundle escrow constructor is missing a valid escrow_id') + } + + const escrowTxHash = await walletClient.deployContract({ + abi: escrowArtifact.abi, + bytecode: escrowArtifact.bytecode, + args: [ + verifierAddress, + escrowConstructor.provider, + escrowConstructor.counterparty, + escrowConstructor.escrow_id_hash, + ], + account: deployerAccount, + chain, + }) + const escrowReceipt = await publicClient.waitForTransactionReceipt({ hash: escrowTxHash }) + const escrowAddress = escrowReceipt.contractAddress + if (!isAddress(escrowAddress ?? '')) { + throw new Error('Escrow deployment did not return a usable address') + } + + const [oracleSigner, escrowVerifier, escrowProvider, escrowCounterparty, escrowIdHash] = await Promise.all([ + publicClient.readContract({ + address: verifierAddress, + abi: verifierArtifact.abi, + functionName: 'oracleSigner', + }), + publicClient.readContract({ + address: escrowAddress, + abi: escrowArtifact.abi, + functionName: 'verifier', + }), + publicClient.readContract({ + address: escrowAddress, + abi: escrowArtifact.abi, + functionName: 'provider', + }), + publicClient.readContract({ + address: escrowAddress, + abi: escrowArtifact.abi, + functionName: 'counterparty', + }), + publicClient.readContract({ + address: escrowAddress, + abi: escrowArtifact.abi, + functionName: 'escrowIdHash', + }), + ]) + + const result = { + standard: 'djd-evaluator-deploy-result-v1', + network: bundle.network, + verdict_id: bundle.verdict_id, + deployer: deployerAccount.address, + contracts: { + verifier: { + contract: bundle.deployment.verifier.contract, + address: verifierAddress, + tx_hash: verifierTxHash, + action: bundle.deployment.verifier.action, + }, + escrow: { + contract: bundle.deployment.escrow.contract, + address: escrowAddress, + tx_hash: escrowTxHash, + action: bundle.deployment.escrow.action, + }, + }, + verification: { + oracle_signer: oracleSigner, + escrow_verifier: escrowVerifier, + escrow_provider: escrowProvider, + escrow_counterparty: escrowCounterparty, + escrow_id_hash: escrowIdHash, + }, + inputs: { + network_key: bundle.network.key ?? null, + provider: escrowConstructor.provider, + counterparty: escrowConstructor.counterparty, + escrow_id: escrowConstructor.escrow_id ?? null, + }, + links: bundle.links, + } + + return { + ...result, + explorer: buildExplorerLinks(bundle.network.chain_id, result), + } +} + +export async function runEvaluatorStackDeployment(options = {}) { + const rpcUrl = options.rpcUrl + const deployerPrivateKey = options.deployerPrivateKey ?? process.env.DJD_DEPLOYER_PRIVATE_KEY + const outputPath = options.outputPath ?? process.env.DJD_DEPLOY_RESULT_PATH + const resolvedBundle = await resolveDeploymentBundle(options) + const result = await deployEvaluatorStackFromBundle({ + bundle: resolvedBundle, + rpcUrl, + deployerPrivateKey, + }) + writeDeploymentResult(outputPath, result) + return result +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + runEvaluatorStackDeployment() + .then((result) => { + console.log(JSON.stringify(result, null, 2)) + }) + .catch((error) => { + console.error(`[deploy] FAILED: ${error instanceof Error ? error.message : String(error)}`) + process.exit(1) + }) +} diff --git a/scripts/evaluator-stack-deployment-source.mjs b/scripts/evaluator-stack-deployment-source.mjs new file mode 100644 index 0000000..3752c1a --- /dev/null +++ b/scripts/evaluator-stack-deployment-source.mjs @@ -0,0 +1,405 @@ +import { existsSync, readFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import { fileURLToPath } from 'node:url' +import { isAddress } from 'viem' +import { getDefaultRuntimeNetwork, resolveRuntimeNetwork } from './evaluator-stack-runtime.mjs' + +const __dirname = dirname(fileURLToPath(import.meta.url)) +const DEFAULT_REGISTRY_PATH = join(__dirname, '..', 'data', 'evaluator-deployments.json') +const PROMOTION_BUNDLE_STANDARD = 'djd-evaluator-promotion-bundle-v1' + +function readJsonFile(filePath, label) { + if (typeof filePath !== 'string' || filePath.trim().length === 0) { + throw new Error(`Missing ${label}`) + } + return JSON.parse(readFileSync(filePath, 'utf8')) +} + +function validateDeploymentResultPayload(result) { + if (!result || result.standard !== 'djd-evaluator-deploy-result-v1') { + throw new Error('Invalid deployment result payload') + } + if (!result.network?.chain_id || !result.network?.chain_name) { + throw new Error('Deployment result is missing network metadata') + } + if (!isAddress(result.contracts?.verifier?.address ?? '')) { + throw new Error('Deployment result is missing verifier address') + } + if (!isAddress(result.contracts?.escrow?.address ?? '')) { + throw new Error('Deployment result is missing escrow address') + } + return result +} + +function maybeBuildDeploymentRegistryUrl(options = {}) { + const apiBaseUrl = options.apiBaseUrl ?? process.env.DJD_API_BASE_URL + if (typeof apiBaseUrl !== 'string' || apiBaseUrl.trim().length === 0) { + return null + } + + const network = resolveRuntimeNetwork(options.network ?? process.env.DJD_NETWORK) ?? getDefaultRuntimeNetwork() + const url = new URL('/v1/score/evaluator/deployments', apiBaseUrl) + url.searchParams.set('network', network.key) + return url.toString() +} + +function maybeBuildPromotionBundleUrl(options = {}) { + const configuredUrl = options.promotionUrl ?? process.env.DJD_PROMOTION_URL + if (typeof configuredUrl === 'string' && configuredUrl.trim().length > 0) { + return configuredUrl.trim() + } + + const apiBaseUrl = options.apiBaseUrl ?? process.env.DJD_API_BASE_URL + if (typeof apiBaseUrl !== 'string' || apiBaseUrl.trim().length === 0) { + return null + } + + const network = resolveRuntimeNetwork(options.network ?? process.env.DJD_NETWORK) ?? getDefaultRuntimeNetwork() + const url = new URL('/v1/score/evaluator/promotion', apiBaseUrl) + url.searchParams.set('network', network.key) + return url.toString() +} + +function normalizeRuntimeNetwork(options = {}) { + return resolveRuntimeNetwork(options.network ?? process.env.DJD_NETWORK) ?? getDefaultRuntimeNetwork() +} + +function findRawRegistryEntry(rawRegistry, runtimeNetwork) { + if (!rawRegistry || rawRegistry.standard !== 'djd-evaluator-deployment-registry-v1') { + throw new Error('Invalid deployment registry payload') + } + + const direct = rawRegistry.deployments?.[runtimeNetwork.key] + if (direct) { + return direct + } + + return ( + Object.values(rawRegistry.deployments ?? {}).find((entry) => { + if (entry?.network?.key === runtimeNetwork.key) { + return true + } + if (entry?.network?.chain_id === runtimeNetwork.chain_id) { + return true + } + return false + }) ?? null + ) +} + +function findApiRegistryDeployment(registryView, runtimeNetwork) { + if (!registryView || registryView.standard !== 'djd-evaluator-deployments-v1' || !Array.isArray(registryView.networks)) { + throw new Error('Invalid deployment registry view payload') + } + + const matchingNetwork = registryView.networks.find((entry) => entry?.key === runtimeNetwork.key) + return matchingNetwork?.deployment ?? null +} + +function normalizeDeploymentResultFromRegistryEntry(entry, runtimeNetwork, source) { + if (!isAddress(entry?.contracts?.verifier?.address ?? '')) { + throw new Error('Deployment registry entry is missing verifier address') + } + if (!isAddress(entry?.contracts?.escrow?.address ?? '')) { + throw new Error('Deployment registry entry is missing escrow address') + } + if (!isAddress(entry?.verification?.oracle_signer ?? '')) { + throw new Error('Deployment registry entry is missing oracle signer metadata') + } + + return validateDeploymentResultPayload({ + standard: 'djd-evaluator-deploy-result-v1', + network: { + key: entry?.network?.key ?? runtimeNetwork.key, + chain_id: entry?.network?.chain_id ?? runtimeNetwork.chain_id, + chain_name: entry?.network?.chain_name ?? runtimeNetwork.chain_name, + caip2: entry?.network?.caip2 ?? `eip155:${entry?.network?.chain_id ?? runtimeNetwork.chain_id}`, + environment: entry?.network?.environment ?? runtimeNetwork.environment, + }, + verdict_id: entry?.verdict_id ?? null, + deployer: entry?.deployer ?? null, + contracts: { + verifier: { + contract: entry?.contracts?.verifier?.contract ?? null, + address: entry?.contracts?.verifier?.address ?? null, + tx_hash: entry?.contracts?.verifier?.tx_hash ?? null, + action: entry?.contracts?.verifier?.action ?? null, + }, + escrow: { + contract: entry?.contracts?.escrow?.contract ?? null, + address: entry?.contracts?.escrow?.address ?? null, + tx_hash: entry?.contracts?.escrow?.tx_hash ?? null, + action: entry?.contracts?.escrow?.action ?? null, + }, + }, + verification: { + oracle_signer: entry?.verification?.oracle_signer ?? null, + escrow_verifier: entry?.verification?.escrow_verifier ?? null, + escrow_provider: entry?.verification?.escrow_provider ?? null, + escrow_counterparty: entry?.verification?.escrow_counterparty ?? null, + escrow_id_hash: entry?.verification?.escrow_id_hash ?? null, + }, + inputs: { + network_key: entry?.inputs?.network_key ?? runtimeNetwork.key, + provider: entry?.inputs?.provider ?? null, + counterparty: entry?.inputs?.counterparty ?? null, + escrow_id: entry?.inputs?.escrow_id ?? null, + }, + explorer: entry?.explorer ?? null, + links: { + verifier_package: entry?.links?.verifier_package ?? null, + verifier_proof: entry?.links?.verifier_proof ?? null, + escrow_settlement: entry?.links?.escrow_settlement ?? null, + artifact_package: entry?.links?.artifact_package ?? null, + bundle: entry?.links?.bundle ?? entry?.links?.deploy_bundle ?? null, + deployment_registry: source.kind === 'api_registry' ? source.location : null, + }, + }) +} + +function validatePromotionBundlePayload(bundle, runtimeNetwork) { + if (!bundle || bundle.standard !== PROMOTION_BUNDLE_STANDARD) { + throw new Error('Invalid promotion bundle payload') + } + if (bundle.ready !== true || !bundle.deployment || !bundle.network) { + return null + } + + const bundleNetworkKey = bundle.network?.key ?? null + const bundleChainId = bundle.network?.chain_id ?? null + if (bundleNetworkKey && bundleNetworkKey !== runtimeNetwork.key && bundleChainId !== runtimeNetwork.chain_id) { + throw new Error( + `Promotion bundle network mismatch: expected ${runtimeNetwork.key}, got ${bundleNetworkKey}`, + ) + } + + return bundle +} + +function normalizeDeploymentResultFromPromotionBundle(bundle, runtimeNetwork, source) { + const deployment = bundle.deployment + const artifactPackage = + bundle.outputs?.variables?.DJD_ARTIFACT_PACKAGE_URL ?? + bundle.outputs?.generic?.DJD_ARTIFACT_PACKAGE_URL ?? + null + + return validateDeploymentResultPayload({ + standard: 'djd-evaluator-deploy-result-v1', + network: { + key: bundle.network?.key ?? deployment?.network?.key ?? runtimeNetwork.key, + chain_id: bundle.network?.chain_id ?? deployment?.network?.chain_id ?? runtimeNetwork.chain_id, + chain_name: bundle.network?.chain_name ?? deployment?.network?.chain_name ?? runtimeNetwork.chain_name, + caip2: + bundle.network?.caip2 ?? + deployment?.network?.caip2 ?? + `eip155:${bundle.network?.chain_id ?? deployment?.network?.chain_id ?? runtimeNetwork.chain_id}`, + environment: + bundle.network?.environment ?? deployment?.network?.environment ?? runtimeNetwork.environment, + }, + verdict_id: deployment?.verdict_id ?? null, + deployer: deployment?.deployer ?? null, + contracts: { + verifier: { + contract: deployment?.contracts?.verifier?.contract ?? null, + address: deployment?.contracts?.verifier?.address ?? null, + tx_hash: deployment?.contracts?.verifier?.tx_hash ?? null, + action: deployment?.contracts?.verifier?.action ?? null, + }, + escrow: { + contract: deployment?.contracts?.escrow?.contract ?? null, + address: deployment?.contracts?.escrow?.address ?? null, + tx_hash: deployment?.contracts?.escrow?.tx_hash ?? null, + action: deployment?.contracts?.escrow?.action ?? null, + }, + }, + verification: { + oracle_signer: deployment?.verification?.oracle_signer ?? null, + escrow_verifier: deployment?.verification?.escrow_verifier ?? deployment?.contracts?.verifier?.address ?? null, + escrow_provider: deployment?.verification?.escrow_provider ?? null, + escrow_counterparty: deployment?.verification?.escrow_counterparty ?? null, + escrow_id_hash: deployment?.verification?.escrow_id_hash ?? null, + }, + inputs: { + network_key: bundle.network?.key ?? runtimeNetwork.key, + provider: deployment?.inputs?.provider ?? null, + counterparty: deployment?.inputs?.counterparty ?? null, + escrow_id: deployment?.inputs?.escrow_id ?? null, + }, + explorer: deployment?.explorer ?? null, + links: { + verifier_package: deployment?.links?.verifier_package ?? null, + verifier_proof: deployment?.links?.verifier_proof ?? null, + escrow_settlement: deployment?.links?.escrow_settlement ?? null, + artifact_package: artifactPackage, + bundle: deployment?.links?.deploy_bundle ?? null, + deployment_registry: deployment?.links?.deployment_registry ?? source.location ?? null, + }, + }) +} + +async function resolveDeploymentResultFromRegistryPath(registryPath, runtimeNetwork) { + if (!existsSync(registryPath)) { + return null + } + + const rawRegistry = readJsonFile(registryPath, 'DJD_EVALUATOR_DEPLOYMENTS_PATH') + const entry = findRawRegistryEntry(rawRegistry, runtimeNetwork) + if (!entry) { + return null + } + + return { + deploymentResult: normalizeDeploymentResultFromRegistryEntry(entry, runtimeNetwork, { + kind: 'registry_file', + location: registryPath, + }), + source: { + kind: 'registry_file', + location: registryPath, + network: runtimeNetwork.key, + }, + } +} + +async function fetchJson(url, requestInit, label) { + const response = await fetch(url, requestInit) + if (!response.ok) { + throw new Error(`${label} request failed: ${response.status} ${response.statusText}`) + } + return await response.json() +} + +async function resolveDeploymentResultFromRegistryUrl(registryUrl, runtimeNetwork, options = {}) { + const registryView = await fetchJson( + registryUrl, + { headers: options.requestHeaders ?? {} }, + 'Deployment registry', + ) + const deployment = findApiRegistryDeployment(registryView, runtimeNetwork) + if (!deployment) { + return null + } + + return { + deploymentResult: normalizeDeploymentResultFromRegistryEntry(deployment, runtimeNetwork, { + kind: 'api_registry', + location: registryUrl, + }), + source: { + kind: 'api_registry', + location: registryUrl, + network: runtimeNetwork.key, + }, + } +} + +async function resolveDeploymentResultFromPromotionBundle(bundle, runtimeNetwork, source) { + const validatedBundle = validatePromotionBundlePayload(bundle, runtimeNetwork) + if (!validatedBundle) { + return null + } + + return { + deploymentResult: normalizeDeploymentResultFromPromotionBundle(validatedBundle, runtimeNetwork, source), + source, + } +} + +async function resolveDeploymentResultFromPromotionUrl(promotionUrl, runtimeNetwork, options = {}) { + const bundle = await fetchJson( + promotionUrl, + { headers: options.requestHeaders ?? {} }, + 'Promotion bundle', + ) + + return await resolveDeploymentResultFromPromotionBundle(bundle, runtimeNetwork, { + kind: 'api_promotion_bundle', + location: promotionUrl, + network: runtimeNetwork.key, + }) +} + +export async function resolveEvaluatorStackDeploymentResult(options = {}) { + if (options.deploymentResult) { + return { + deploymentResult: validateDeploymentResultPayload(options.deploymentResult), + source: { + kind: 'deployment_result', + location: 'inline', + network: options.deploymentResult?.network?.key ?? null, + }, + } + } + + const deploymentResultPath = options.deploymentResultPath ?? process.env.DJD_DEPLOY_RESULT_PATH + if (typeof deploymentResultPath === 'string' && deploymentResultPath.trim().length > 0) { + return { + deploymentResult: validateDeploymentResultPayload( + readJsonFile(deploymentResultPath, 'DJD_DEPLOY_RESULT_PATH'), + ), + source: { + kind: 'deployment_result_file', + location: deploymentResultPath, + network: null, + }, + } + } + + const runtimeNetwork = normalizeRuntimeNetwork(options) + + if (options.promotionBundle) { + const resolved = await resolveDeploymentResultFromPromotionBundle(options.promotionBundle, runtimeNetwork, { + kind: 'inline_promotion_bundle', + location: 'inline', + network: runtimeNetwork.key, + }) + if (resolved) { + return resolved + } + } + + const configuredRegistryPath = options.registryPath ?? process.env.DJD_EVALUATOR_DEPLOYMENTS_PATH + const pathsToTry = [] + if (typeof configuredRegistryPath === 'string' && configuredRegistryPath.trim().length > 0) { + pathsToTry.push(configuredRegistryPath.trim()) + } + if (!pathsToTry.includes(DEFAULT_REGISTRY_PATH)) { + pathsToTry.push(DEFAULT_REGISTRY_PATH) + } + + for (const registryPath of pathsToTry) { + const resolved = await resolveDeploymentResultFromRegistryPath(registryPath, runtimeNetwork) + if (resolved) { + return resolved + } + } + + const promotionUrl = maybeBuildPromotionBundleUrl(options) + if (typeof promotionUrl === 'string' && promotionUrl.trim().length > 0) { + try { + const resolved = await resolveDeploymentResultFromPromotionUrl(promotionUrl.trim(), runtimeNetwork, options) + if (resolved) { + return resolved + } + } catch { + // Fall through to the older deployment registry resolution path. + } + } + + const registryUrl = + options.deploymentsUrl ?? + process.env.DJD_DEPLOYMENTS_URL ?? + maybeBuildDeploymentRegistryUrl(options) + if (typeof registryUrl === 'string' && registryUrl.trim().length > 0) { + const resolved = await resolveDeploymentResultFromRegistryUrl(registryUrl.trim(), runtimeNetwork, options) + if (resolved) { + return resolved + } + } + + throw new Error( + `Unable to resolve deployment result for network=${runtimeNetwork.key}. Provide DJD_DEPLOY_RESULT_PATH or publish a deployment registry entry.`, + ) +} + +export { validateDeploymentResultPayload } diff --git a/scripts/evaluator-stack-runtime.mjs b/scripts/evaluator-stack-runtime.mjs new file mode 100644 index 0000000..fbdca61 --- /dev/null +++ b/scripts/evaluator-stack-runtime.mjs @@ -0,0 +1,195 @@ +import { base, baseSepolia } from 'viem/chains' + +const NETWORKS = { + base: { + key: 'base', + chain_id: base.id, + chain_name: base.name, + environment: 'mainnet', + rpc_env_var: 'DJD_BASE_RPC_URL', + chain: base, + }, + 'base-sepolia': { + key: 'base-sepolia', + chain_id: baseSepolia.id, + chain_name: baseSepolia.name, + environment: 'testnet', + rpc_env_var: 'DJD_BASE_SEPOLIA_RPC_URL', + chain: baseSepolia, + }, +} + +const NETWORK_ALIASES = { + base: 'base', + 'base-mainnet': 'base', + '8453': 'base', + 'eip155:8453': 'base', + 'base-sepolia': 'base-sepolia', + base_sepolia: 'base-sepolia', + '84532': 'base-sepolia', + 'eip155:84532': 'base-sepolia', +} + +export function getDefaultRuntimeNetwork() { + return NETWORKS.base +} + +export function listRuntimeNetworks() { + return Object.values(NETWORKS) +} + +export function resolveRuntimeNetwork(rawNetwork) { + if (rawNetwork === undefined || rawNetwork === null || String(rawNetwork).trim() === '') { + return getDefaultRuntimeNetwork() + } + + const normalized = String(rawNetwork).trim().toLowerCase() + const key = NETWORK_ALIASES[normalized] + return key ? NETWORKS[key] : null +} + +export function resolveRuntimeNetworkFromMetadata(metadata) { + if (metadata?.key && resolveRuntimeNetwork(metadata.key)) { + return resolveRuntimeNetwork(metadata.key) + } + + if (metadata?.chain_id === NETWORKS['base-sepolia'].chain_id) { + return NETWORKS['base-sepolia'] + } + + if (metadata?.chain_id === NETWORKS.base.chain_id) { + return NETWORKS.base + } + + return getDefaultRuntimeNetwork() +} + +export function buildRuntimeChain(metadata, rpcUrl) { + const network = resolveRuntimeNetworkFromMetadata(metadata) + return { + ...network.chain, + id: metadata?.chain_id ?? network.chain_id, + name: metadata?.chain_name ?? network.chain_name, + rpcUrls: { + default: { http: [rpcUrl] }, + public: { http: [rpcUrl] }, + }, + } +} + +export function resolveRpcUrl(options = {}) { + const runtimeNetwork = + typeof options.network === 'string' + ? resolveRuntimeNetwork(options.network) + : options.network + ? resolveRuntimeNetworkFromMetadata(options.network) + : resolveRuntimeNetwork(options.networkKey ?? process.env.DJD_NETWORK) + const network = runtimeNetwork ?? getDefaultRuntimeNetwork() + const explicitRpcUrl = options.rpcUrl ?? process.env.DJD_RPC_URL + if (typeof explicitRpcUrl === 'string' && explicitRpcUrl.trim().length > 0) { + return { + ok: true, + rpcUrl: explicitRpcUrl.trim(), + source: options.rpcUrl ? 'rpcUrl' : 'DJD_RPC_URL', + network, + expected_envs: ['DJD_RPC_URL'], + } + } + + const envVar = network.rpc_env_var + const networkRpcUrl = process.env[envVar] + if (typeof networkRpcUrl === 'string' && networkRpcUrl.trim().length > 0) { + return { + ok: true, + rpcUrl: networkRpcUrl.trim(), + source: envVar, + network, + expected_envs: ['DJD_RPC_URL', envVar], + } + } + + return { + ok: false, + rpcUrl: null, + source: null, + network, + expected_envs: ['DJD_RPC_URL', envVar], + } +} + +export function detectBundleSource(options = {}) { + if (options.bundle) { + return { + ready: true, + source: 'bundle', + details: null, + } + } + + const bundlePath = options.bundlePath ?? process.env.DJD_DEPLOY_BUNDLE_PATH + if (typeof bundlePath === 'string' && bundlePath.trim().length > 0) { + return { + ready: true, + source: 'DJD_DEPLOY_BUNDLE_PATH', + details: bundlePath.trim(), + } + } + + const bundleUrl = options.bundleUrl ?? process.env.DJD_DEPLOY_BUNDLE_URL + if (typeof bundleUrl === 'string' && bundleUrl.trim().length > 0) { + return { + ready: true, + source: 'DJD_DEPLOY_BUNDLE_URL', + details: bundleUrl.trim(), + } + } + + const apiBaseUrl = options.apiBaseUrl ?? process.env.DJD_API_BASE_URL + const verdictId = options.verdictId ?? process.env.DJD_VERDICT_ID + if ( + typeof apiBaseUrl === 'string' && + apiBaseUrl.trim().length > 0 && + typeof verdictId === 'string' && + verdictId.trim().length > 0 + ) { + return { + ready: true, + source: 'DJD_API_BASE_URL + DJD_VERDICT_ID', + details: { + api_base_url: apiBaseUrl.trim(), + verdict_id: verdictId.trim(), + }, + } + } + + return { + ready: false, + source: null, + details: null, + } +} + +export function isValidPrivateKey(value) { + return /^0x[a-fA-F0-9]{64}$/.test(value ?? '') +} + +export function detectDeployerKeySource(options = {}) { + if (isValidPrivateKey(options.deployerPrivateKey)) { + return { + ready: true, + source: 'deployerPrivateKey', + } + } + + if (isValidPrivateKey(process.env.DJD_DEPLOYER_PRIVATE_KEY)) { + return { + ready: true, + source: 'DJD_DEPLOYER_PRIVATE_KEY', + } + } + + return { + ready: false, + source: null, + } +} diff --git a/scripts/post-deploy-smoke.mjs b/scripts/post-deploy-smoke.mjs index 4e9c056..4c3f4ec 100644 --- a/scripts/post-deploy-smoke.mjs +++ b/scripts/post-deploy-smoke.mjs @@ -32,6 +32,21 @@ export function validatePublicHealthPayload(payload, expectedReleaseSha = null) throw new Error('Public /health payload did not include numeric uptime') } + const detailedOnlyFields = [ + 'modelVersion', + 'experimentalStatus', + 'warnings', + 'runtime', + 'integrations', + 'database', + 'indexer', + 'jobs', + ] + const leakedField = detailedOnlyFields.find((field) => Object.prototype.hasOwnProperty.call(payload, field)) + if (leakedField) { + throw new Error(`Public /health payload leaked detailed field "${leakedField}"`) + } + const normalizedExpectedReleaseSha = normalizeReleaseSha(expectedReleaseSha) if (!normalizedExpectedReleaseSha) { return @@ -82,6 +97,51 @@ export function validateDetailedHealthPayload(payload, expectedRuntimeMode) { } } +export function validatePublicMetricsLockdown(status) { + if (status !== 401 && status !== 403) { + throw new Error(`Public /metrics returned HTTP ${status}; expected 401 or 403`) + } +} + +export function validateRobotsTxt(body, publicBaseUrl) { + if (typeof body !== 'string' || body.length === 0) { + throw new Error('robots.txt did not return a text body') + } + if (!body.includes(`Sitemap: ${publicBaseUrl}/sitemap.xml`)) { + throw new Error('robots.txt did not point at /sitemap.xml') + } +} + +export function validateSitemapXml(body, publicBaseUrl) { + if (typeof body !== 'string' || body.length === 0) { + throw new Error('sitemap.xml did not return an XML body') + } + if (!body.includes('${publicBaseUrl}/`)) { + throw new Error('sitemap.xml did not include the homepage URL') + } +} + +export function validateAgentMetadata(payload, publicBaseUrl) { + if (!payload || typeof payload !== 'object') { + throw new Error('agent.json did not return an object payload') + } + if (payload.url !== publicBaseUrl) { + throw new Error(`agent.json url mismatch: expected ${publicBaseUrl}, got ${payload.url}`) + } + if (payload.docs !== `${publicBaseUrl}/docs`) { + throw new Error('agent.json docs link mismatch') + } + if (payload.openapi !== `${publicBaseUrl}/openapi.json`) { + throw new Error('agent.json openapi link mismatch') + } + if (payload.payment?.discovery !== `${publicBaseUrl}/.well-known/x402`) { + throw new Error('agent.json payment discovery link mismatch') + } +} + function formatHealthWarnings(payload) { if (!Array.isArray(payload?.warnings) || payload.warnings.length === 0) { return null @@ -102,6 +162,7 @@ function formatHealthWarnings(payload) { export async function runPostDeploySmokeCheck(options = {}) { const healthUrl = options.healthUrl ?? process.env.DJD_HEALTHCHECK_URL ?? 'https://djdagentscore.dev/health' + const publicBaseUrl = new URL('.', healthUrl).toString().replace(/\/$/, '') const adminKey = options.adminKey ?? process.env.DJD_ADMIN_KEY ?? '' const expectedRuntimeMode = normalizeRuntimeMode( options.expectedRuntimeMode ?? process.env.DJD_EXPECT_RUNTIME_MODE ?? 'combined', @@ -132,6 +193,27 @@ export async function runPostDeploySmokeCheck(options = {}) { const publicPayload = await publicResponse.json() validatePublicHealthPayload(publicPayload, expectedReleaseSha) + const metricsResponse = await fetch(`${publicBaseUrl}/metrics`) + validatePublicMetricsLockdown(metricsResponse.status) + + const robotsResponse = await fetch(`${publicBaseUrl}/robots.txt`) + if (!robotsResponse.ok) { + throw new Error(`robots.txt returned HTTP ${robotsResponse.status}`) + } + validateRobotsTxt(await robotsResponse.text(), publicBaseUrl) + + const sitemapResponse = await fetch(`${publicBaseUrl}/sitemap.xml`) + if (!sitemapResponse.ok) { + throw new Error(`sitemap.xml returned HTTP ${sitemapResponse.status}`) + } + validateSitemapXml(await sitemapResponse.text(), publicBaseUrl) + + const agentResponse = await fetch(`${publicBaseUrl}/.well-known/agent.json`) + if (!agentResponse.ok) { + throw new Error(`agent.json returned HTTP ${agentResponse.status}`) + } + validateAgentMetadata(await agentResponse.json(), publicBaseUrl) + if (adminKey) { const detailedResponse = await fetch(healthUrl, { headers: { 'x-admin-key': adminKey }, diff --git a/scripts/preflight-evaluator-stack.mjs b/scripts/preflight-evaluator-stack.mjs new file mode 100644 index 0000000..33b3ca6 --- /dev/null +++ b/scripts/preflight-evaluator-stack.mjs @@ -0,0 +1,327 @@ +import { pathToFileURL } from 'node:url' +import { createPublicClient, http } from 'viem' +import { resolveDeploymentBundle } from './deploy-evaluator-stack.mjs' +import { + buildRuntimeChain, + detectBundleSource, + detectDeployerKeySource, + getDefaultRuntimeNetwork, + listRuntimeNetworks, + resolveRpcUrl, + resolveRuntimeNetwork, + resolveRuntimeNetworkFromMetadata, +} from './evaluator-stack-runtime.mjs' + +function normalizeBoolean(value, fallback = false) { + if (typeof value === 'boolean') { + return value + } + + if (typeof value !== 'string') { + return fallback + } + + const normalized = value.trim().toLowerCase() + if (['1', 'true', 'yes', 'on'].includes(normalized)) return true + if (['0', 'false', 'no', 'off'].includes(normalized)) return false + return fallback +} + +function normalizeRuntimeMode(value) { + const mode = String(value ?? 'combined') + .trim() + .toLowerCase() + if (['combined', 'api', 'worker'].includes(mode)) { + return mode + } + return 'combined' +} + +function normalizeReleaseSha(value) { + if (typeof value !== 'string') { + return null + } + + const normalized = value.trim().toLowerCase() + return normalized.length > 0 ? normalized : null +} + +function serializeError(error) { + if (error instanceof Error) { + return { + message: error.message, + name: error.name, + } + } + + return { + message: String(error), + name: 'Error', + } +} + +function summarizeBundleGuidance() { + return 'Provide bundle, DJD_DEPLOY_BUNDLE_PATH, DJD_DEPLOY_BUNDLE_URL, or DJD_API_BASE_URL + DJD_VERDICT_ID' +} + +function sanitizeUrl(value) { + if (typeof value !== 'string' || value.length === 0) { + return null + } + + try { + const url = new URL(value) + if (url.username) url.username = '***' + if (url.password) url.password = '***' + if (url.search) url.search = '?...' + return url.toString() + } catch { + return value + } +} + +function escapeDotenvValue(value) { + const normalized = String(value ?? '') + if (/^[A-Za-z0-9_./:-]*$/.test(normalized)) { + return normalized + } + return `"${normalized.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n')}"` +} + +function escapeShellValue(value) { + return `'${String(value ?? '').replace(/'/g, `'\"'\"'`)}'` +} + +function formatKeyValueLines(variables, formatter) { + return Object.entries(variables) + .map(([key, value]) => formatter(key, value)) + .join('\n') +} + +function buildRecommendedEnv(network) { + return { + DJD_NETWORK: network.key, + [network.rpc_env_var]: '', + DJD_DEPLOYER_PRIVATE_KEY: '', + DJD_API_BASE_URL: '', + DJD_VERDICT_ID: '', + } +} + +function buildPreflightGuidance(report, network) { + const recommendedEnv = buildRecommendedEnv(network) + const missing = { + bundle: report.bundle.ready + ? null + : { + recommended_envs: ['DJD_API_BASE_URL', 'DJD_VERDICT_ID'], + accepted_sources: [ + 'DJD_API_BASE_URL + DJD_VERDICT_ID', + 'DJD_DEPLOY_BUNDLE_URL', + 'DJD_DEPLOY_BUNDLE_PATH', + ], + }, + rpc: + report.rpc.ready && report.rpc.reachable && report.rpc.chain_id_matches_expected === true + ? null + : { + expected_envs: report.rpc.expected_envs, + }, + deployer: report.deployer.ready + ? null + : { + expected_envs: ['DJD_DEPLOYER_PRIVATE_KEY'], + }, + } + + return { + ready: report.ok, + missing, + recommended_env: recommendedEnv, + dotenv: `${formatKeyValueLines(recommendedEnv, (key, value) => `${key}=${escapeDotenvValue(value)}`)}\n`, + shell: `${formatKeyValueLines(recommendedEnv, (key, value) => `export ${key}=${escapeShellValue(value)}`)}\n`, + notes: [ + 'Fill the recommended env values in .env or your shell before running contracts:preflight.', + 'Bundle can come from DJD_API_BASE_URL + DJD_VERDICT_ID, DJD_DEPLOY_BUNDLE_URL, or DJD_DEPLOY_BUNDLE_PATH.', + ], + } +} + +function buildNetworkSummary(metadata, runtimeNetwork) { + return { + key: metadata?.key ?? runtimeNetwork.key, + chain_id: metadata?.chain_id ?? runtimeNetwork.chain_id, + chain_name: metadata?.chain_name ?? runtimeNetwork.chain_name, + environment: metadata?.environment ?? runtimeNetwork.environment, + rpc_env_var: runtimeNetwork.rpc_env_var, + } +} + +async function resolveBundleForPreflight(options = {}) { + const source = detectBundleSource(options) + + if (!source.ready) { + return { + ready: false, + validated: false, + source: null, + details: null, + bundle: null, + error: { + message: summarizeBundleGuidance(), + name: 'BundleSourceError', + }, + } + } + + try { + const bundle = await resolveDeploymentBundle(options) + return { + ready: true, + validated: true, + source: source.source, + details: source.details, + bundle, + error: null, + } + } catch (error) { + return { + ready: false, + validated: false, + source: source.source, + details: source.details, + bundle: null, + error: serializeError(error), + } + } +} + +export async function runEvaluatorStackPreflight(options = {}) { + const runHealth = normalizeBoolean( + options.runHealth ?? process.env.DJD_STAGE_RUN_HEALTH, + Boolean(options.health ?? options.healthUrl ?? process.env.DJD_HEALTHCHECK_URL), + ) + const bundleResolution = await resolveBundleForPreflight(options) + const bundleNetworkMetadata = bundleResolution.bundle?.network ?? null + const runtimeNetwork = + (bundleNetworkMetadata + ? resolveRuntimeNetworkFromMetadata(bundleNetworkMetadata) + : resolveRuntimeNetwork(options.network ?? process.env.DJD_NETWORK)) ?? getDefaultRuntimeNetwork() + const network = buildNetworkSummary(bundleNetworkMetadata, runtimeNetwork) + const rpcResolution = resolveRpcUrl({ + rpcUrl: options.rpcUrl, + network: bundleNetworkMetadata ?? runtimeNetwork, + networkKey: options.network ?? process.env.DJD_NETWORK, + }) + const deployer = detectDeployerKeySource(options) + const healthOptions = + typeof options.health === 'object' && options.health !== null && !Array.isArray(options.health) ? options.health : {} + + const report = { + standard: 'djd-evaluator-preflight-v1', + ok: false, + network, + available_networks: listRuntimeNetworks().map((entry) => ({ + key: entry.key, + chain_id: entry.chain_id, + chain_name: entry.chain_name, + environment: entry.environment, + rpc_env_var: entry.rpc_env_var, + })), + bundle: { + ready: bundleResolution.ready, + validated: bundleResolution.validated, + source: bundleResolution.source, + details: bundleResolution.details, + verdict_id: bundleResolution.bundle?.verdict_id ?? null, + network: bundleResolution.bundle?.network ?? null, + artifact_package_available: bundleResolution.bundle?.artifacts?.available ?? false, + error: bundleResolution.error, + }, + rpc: { + ready: rpcResolution.ok, + source: rpcResolution.source, + expected_envs: rpcResolution.expected_envs, + display_url: rpcResolution.ok ? sanitizeUrl(rpcResolution.rpcUrl) : null, + reachable: false, + chain_id: null, + chain_id_matches_expected: null, + error: null, + }, + deployer: { + ready: deployer.ready, + source: deployer.source, + }, + health: { + run: runHealth, + ready: true, + health_url: + healthOptions.healthUrl ?? + options.healthUrl ?? + process.env.DJD_HEALTHCHECK_URL ?? + 'https://djdagentscore.dev/health', + admin_key_present: typeof (healthOptions.adminKey ?? process.env.DJD_ADMIN_KEY) === 'string' && + String(healthOptions.adminKey ?? process.env.DJD_ADMIN_KEY).trim().length > 0, + expected_runtime_mode: normalizeRuntimeMode( + healthOptions.expectedRuntimeMode ?? options.expectedRuntimeMode ?? process.env.DJD_EXPECT_RUNTIME_MODE, + ), + expected_release_sha: normalizeReleaseSha( + healthOptions.expectedReleaseSha ?? options.expectedReleaseSha ?? process.env.DJD_EXPECT_RELEASE_SHA, + ), + }, + guidance: null, + } + + if (!rpcResolution.ok) { + report.rpc.error = { + message: `Missing rpcUrl. Set ${rpcResolution.expected_envs.join(' or ')}`, + name: 'RpcConfigError', + } + } else { + try { + const publicClient = createPublicClient({ + chain: buildRuntimeChain(network, rpcResolution.rpcUrl), + transport: http(rpcResolution.rpcUrl), + }) + const actualChainId = await publicClient.getChainId() + report.rpc.reachable = true + report.rpc.chain_id = actualChainId + report.rpc.chain_id_matches_expected = actualChainId === network.chain_id + + if (report.rpc.chain_id_matches_expected !== true) { + report.rpc.error = { + message: `RPC chain mismatch: expected ${network.chain_id}, got ${actualChainId}`, + name: 'RpcChainMismatchError', + } + } + } catch (error) { + report.rpc.error = serializeError(error) + } + } + + report.ok = Boolean( + report.bundle.ready && + report.bundle.validated && + report.rpc.ready && + report.rpc.reachable && + report.rpc.chain_id_matches_expected === true && + report.deployer.ready, + ) + report.guidance = buildPreflightGuidance(report, network) + + return report +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + runEvaluatorStackPreflight() + .then((report) => { + console.log(JSON.stringify(report, null, 2)) + if (!report.ok) { + process.exit(1) + } + }) + .catch((error) => { + console.error(`[contracts:preflight] FAILED: ${error instanceof Error ? error.message : String(error)}`) + process.exit(1) + }) +} diff --git a/scripts/promote-evaluator-stack.mjs b/scripts/promote-evaluator-stack.mjs new file mode 100644 index 0000000..f17e34c --- /dev/null +++ b/scripts/promote-evaluator-stack.mjs @@ -0,0 +1,412 @@ +import { appendFileSync, writeFileSync } from 'node:fs' +import { pathToFileURL } from 'node:url' +import { resolveEvaluatorStackDeploymentResult } from './evaluator-stack-deployment-source.mjs' + +const PROMOTION_STANDARD = 'djd-evaluator-promotion-env-v1' +const PROMOTION_BUNDLE_STANDARD = 'djd-evaluator-promotion-bundle-v1' +const GITHUB_OUTPUT_ENV = 'GITHUB_OUTPUT' + +function writeJsonFile(outputPath, payload) { + if (typeof outputPath !== 'string' || outputPath.trim().length === 0) { + return null + } + + writeFileSync(outputPath, JSON.stringify(payload, null, 2) + '\n') + return outputPath +} + +function writeTextFile(outputPath, contents) { + if (typeof outputPath !== 'string' || outputPath.trim().length === 0) { + return null + } + + writeFileSync(outputPath, contents) + return outputPath +} + +function appendTextFile(outputPath, contents) { + if (typeof outputPath !== 'string' || outputPath.trim().length === 0) { + return null + } + + appendFileSync(outputPath, contents) + return outputPath +} + +function sanitizeNetworkEnvSegment(value) { + return String(value ?? '') + .trim() + .replace(/[^a-zA-Z0-9]+/g, '_') + .replace(/^_+|_+$/g, '') + .toUpperCase() +} + +function inferApiBaseUrlFromLinks(links = {}) { + const candidates = [ + links.verifier_package, + links.artifact_package, + links.verifier_proof, + links.escrow_settlement, + links.bundle, + links.deployment_registry, + links.promotion_bundle, + ].filter((value) => typeof value === 'string' && value.trim().length > 0) + + const suffixes = [ + '/v1/score/evaluator/verifier', + '/v1/score/evaluator/artifacts', + '/v1/score/evaluator/proof', + '/v1/score/evaluator/escrow', + '/v1/score/evaluator/deploy/bundle', + '/v1/score/evaluator/deployments', + '/v1/score/evaluator/promotion', + ] + + for (const candidate of candidates) { + for (const suffix of suffixes) { + const index = candidate.indexOf(suffix) + if (index > 0) { + return candidate.slice(0, index) + } + } + } + + return null +} + +function buildRouteUrl(apiBaseUrl, pathname, params = {}) { + if (typeof apiBaseUrl !== 'string' || apiBaseUrl.trim().length === 0) { + return null + } + + const url = new URL(pathname, apiBaseUrl) + for (const [key, value] of Object.entries(params)) { + if (value === undefined || value === null || value === '') { + continue + } + url.searchParams.set(key, String(value)) + } + return url.toString() +} + +async function fetchJson(url, requestInit, label) { + const response = await fetch(url, requestInit) + if (!response.ok) { + throw new Error(`${label} request failed: ${response.status} ${response.statusText}`) + } + return await response.json() +} + +function ensureTrailingNewline(value) { + const normalized = String(value ?? '') + return normalized.endsWith('\n') ? normalized : `${normalized}\n` +} + +function normalizeRenderedOutputs(outputs = {}) { + return { + ...outputs, + dotenv: ensureTrailingNewline(outputs.dotenv ?? ''), + shell: ensureTrailingNewline(outputs.shell ?? ''), + github_output: ensureTrailingNewline(outputs.github_output ?? ''), + } +} + +function validatePromotionBundlePayload(payload, options = {}) { + if (!payload || payload.standard !== PROMOTION_BUNDLE_STANDARD) { + throw new Error('Invalid promotion bundle payload') + } + + if (!payload.ready || !payload.outputs || !payload.deployment || !payload.network) { + throw new Error(`Promotion bundle is not ready${payload?.reason ? `: ${payload.reason}` : ''}`) + } + + const expectedNetwork = options.network ?? process.env.DJD_NETWORK ?? null + if ( + typeof expectedNetwork === 'string' && + expectedNetwork.trim().length > 0 && + payload.network?.key && + payload.network.key !== expectedNetwork.trim() + ) { + throw new Error( + `Promotion bundle network mismatch: expected=${expectedNetwork.trim()} actual=${payload.network.key}`, + ) + } + + return payload +} + +function maybeBuildPromotionBundleUrl(options = {}) { + const configuredUrl = options.promotionUrl ?? process.env.DJD_PROMOTION_URL + if (typeof configuredUrl === 'string' && configuredUrl.trim().length > 0) { + return configuredUrl.trim() + } + + const apiBaseUrl = options.apiBaseUrl ?? process.env.DJD_API_BASE_URL + return buildRouteUrl(apiBaseUrl, '/v1/score/evaluator/promotion', { + network: options.network ?? process.env.DJD_NETWORK, + }) +} + +async function resolvePromotionBundle(options = {}) { + if (options.promotionBundle) { + return { + bundle: validatePromotionBundlePayload(options.promotionBundle, options), + source: { + kind: 'inline_promotion_bundle', + location: 'inline', + network: options.promotionBundle?.network?.key ?? null, + }, + } + } + + const promotionUrl = maybeBuildPromotionBundleUrl(options) + if (typeof promotionUrl !== 'string' || promotionUrl.length === 0) { + return null + } + + try { + const bundle = validatePromotionBundlePayload( + await fetchJson(promotionUrl, { headers: options.requestHeaders ?? {} }, 'Promotion bundle'), + options, + ) + + return { + bundle, + source: { + kind: 'api_promotion_bundle', + location: promotionUrl, + network: bundle.network?.key ?? options.network ?? null, + }, + } + } catch { + return null + } +} + +function normalizePromotionLinks(deploymentResult, deploymentSource, options = {}) { + const networkKey = deploymentResult.network?.key ?? null + const verdictId = deploymentResult.verdict_id ?? null + const apiBaseUrl = + options.apiBaseUrl ?? process.env.DJD_API_BASE_URL ?? inferApiBaseUrlFromLinks(deploymentResult.links) + + return { + api_base_url: typeof apiBaseUrl === 'string' && apiBaseUrl.trim().length > 0 ? apiBaseUrl.trim() : null, + verifier_package: + deploymentResult.links?.verifier_package ?? + buildRouteUrl(apiBaseUrl, '/v1/score/evaluator/verifier', { + network: networkKey, + }), + artifact_package: + deploymentResult.links?.artifact_package ?? + buildRouteUrl(apiBaseUrl, '/v1/score/evaluator/artifacts'), + verifier_proof: + deploymentResult.links?.verifier_proof ?? + buildRouteUrl(apiBaseUrl, '/v1/score/evaluator/proof', { + id: verdictId, + network: networkKey, + }), + escrow_settlement: + deploymentResult.links?.escrow_settlement ?? + buildRouteUrl(apiBaseUrl, '/v1/score/evaluator/escrow', { + id: verdictId, + network: networkKey, + }), + deploy_bundle: + deploymentResult.links?.bundle ?? + buildRouteUrl(apiBaseUrl, '/v1/score/evaluator/deploy/bundle', { + id: verdictId, + network: networkKey, + }), + deployment_registry: + deploymentResult.links?.deployment_registry ?? + (deploymentSource.kind === 'api_registry' ? deploymentSource.location : null) ?? + buildRouteUrl(apiBaseUrl, '/v1/score/evaluator/deployments', { + network: networkKey, + }), + } +} + +function setVariable(target, key, value) { + if (value === undefined || value === null || value === '') { + return + } + target[key] = String(value) +} + +function buildGenericVariables(deploymentResult, deploymentSource, promotionLinks) { + const variables = {} + setVariable(variables, 'DJD_NETWORK', deploymentResult.network?.key) + setVariable(variables, 'DJD_CHAIN_ID', deploymentResult.network?.chain_id) + setVariable(variables, 'DJD_CHAIN_NAME', deploymentResult.network?.chain_name) + setVariable(variables, 'DJD_CAIP2', deploymentResult.network?.caip2) + setVariable(variables, 'DJD_ENVIRONMENT', deploymentResult.network?.environment) + setVariable(variables, 'DJD_VERDICT_ID', deploymentResult.verdict_id) + setVariable(variables, 'DJD_DEPLOYMENT_SOURCE', deploymentSource.kind) + setVariable(variables, 'DJD_DEPLOYMENT_SOURCE_LOCATION', deploymentSource.location) + setVariable(variables, 'DJD_DEPLOYER_ADDRESS', deploymentResult.deployer) + setVariable(variables, 'DJD_VERIFIER_CONTRACT', deploymentResult.contracts?.verifier?.address) + setVariable(variables, 'DJD_ESCROW_CONTRACT', deploymentResult.contracts?.escrow?.address) + setVariable(variables, 'DJD_ORACLE_SIGNER', deploymentResult.verification?.oracle_signer) + setVariable( + variables, + 'DJD_ESCROW_PROVIDER', + deploymentResult.verification?.escrow_provider ?? deploymentResult.inputs?.provider, + ) + setVariable( + variables, + 'DJD_ESCROW_COUNTERPARTY', + deploymentResult.verification?.escrow_counterparty ?? deploymentResult.inputs?.counterparty, + ) + setVariable(variables, 'DJD_ESCROW_ID', deploymentResult.inputs?.escrow_id) + setVariable(variables, 'DJD_ESCROW_ID_HASH', deploymentResult.verification?.escrow_id_hash) + setVariable(variables, 'DJD_API_BASE_URL', promotionLinks.api_base_url) + setVariable(variables, 'DJD_VERIFIER_PACKAGE_URL', promotionLinks.verifier_package) + setVariable(variables, 'DJD_ARTIFACT_PACKAGE_URL', promotionLinks.artifact_package) + setVariable(variables, 'DJD_VERIFIER_PROOF_URL', promotionLinks.verifier_proof) + setVariable(variables, 'DJD_ESCROW_SETTLEMENT_URL', promotionLinks.escrow_settlement) + setVariable(variables, 'DJD_DEPLOY_BUNDLE_URL', promotionLinks.deploy_bundle) + setVariable(variables, 'DJD_DEPLOYMENTS_URL', promotionLinks.deployment_registry) + setVariable(variables, 'DJD_VERIFIER_EXPLORER_URL', deploymentResult.explorer?.verifier_address) + setVariable(variables, 'DJD_VERIFIER_TX_URL', deploymentResult.explorer?.verifier_transaction) + setVariable(variables, 'DJD_ESCROW_EXPLORER_URL', deploymentResult.explorer?.escrow_address) + setVariable(variables, 'DJD_ESCROW_TX_URL', deploymentResult.explorer?.escrow_transaction) + return variables +} + +function buildNetworkScopedVariables(deploymentResult, promotionLinks) { + const networkSegment = sanitizeNetworkEnvSegment(deploymentResult.network?.key ?? deploymentResult.network?.chain_id) + const variables = {} + if (!networkSegment) { + return variables + } + + setVariable(variables, `DJD_${networkSegment}_NETWORK`, deploymentResult.network?.key) + setVariable(variables, `DJD_${networkSegment}_CHAIN_ID`, deploymentResult.network?.chain_id) + setVariable(variables, `DJD_${networkSegment}_VERDICT_ID`, deploymentResult.verdict_id) + setVariable(variables, `DJD_${networkSegment}_VERIFIER_CONTRACT`, deploymentResult.contracts?.verifier?.address) + setVariable(variables, `DJD_${networkSegment}_ESCROW_CONTRACT`, deploymentResult.contracts?.escrow?.address) + setVariable(variables, `DJD_${networkSegment}_ORACLE_SIGNER`, deploymentResult.verification?.oracle_signer) + setVariable(variables, `DJD_${networkSegment}_VERIFIER_PACKAGE_URL`, promotionLinks.verifier_package) + setVariable(variables, `DJD_${networkSegment}_ARTIFACT_PACKAGE_URL`, promotionLinks.artifact_package) + setVariable(variables, `DJD_${networkSegment}_VERIFIER_PROOF_URL`, promotionLinks.verifier_proof) + setVariable(variables, `DJD_${networkSegment}_ESCROW_SETTLEMENT_URL`, promotionLinks.escrow_settlement) + setVariable(variables, `DJD_${networkSegment}_DEPLOY_BUNDLE_URL`, promotionLinks.deploy_bundle) + setVariable(variables, `DJD_${networkSegment}_DEPLOYMENTS_URL`, promotionLinks.deployment_registry) + return variables +} + +function escapeDotenvValue(value) { + const normalized = String(value) + if (/^[A-Za-z0-9_./:-]+$/.test(normalized)) { + return normalized + } + return `"${normalized.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n')}"` +} + +function escapeShellValue(value) { + return `'${String(value).replace(/'/g, `'\"'\"'`)}'` +} + +function formatKeyValueLines(variables, formatter) { + return Object.entries(variables) + .map(([key, value]) => formatter(key, value)) + .join('\n') +} + +export async function promoteEvaluatorStackDeployment(options = {}) { + const promotionBundle = await resolvePromotionBundle(options) + const result = promotionBundle + ? { + standard: PROMOTION_STANDARD, + ok: true, + network: promotionBundle.bundle.network, + deployment: { + verdict_id: promotionBundle.bundle.deployment?.verdict_id ?? null, + source: promotionBundle.source, + deployer: promotionBundle.bundle.deployment?.deployer ?? null, + contracts: promotionBundle.bundle.deployment?.contracts ?? null, + verification: promotionBundle.bundle.deployment?.verification ?? null, + inputs: promotionBundle.bundle.deployment?.inputs ?? null, + }, + outputs: normalizeRenderedOutputs(promotionBundle.bundle.outputs), + files: { + json: null, + dotenv: null, + shell: null, + github_output: null, + }, + } + : await (async () => { + const { deploymentResult, source: deploymentSource } = await resolveEvaluatorStackDeploymentResult(options) + const promotionLinks = normalizePromotionLinks(deploymentResult, deploymentSource, options) + const genericVariables = buildGenericVariables(deploymentResult, deploymentSource, promotionLinks) + const networkScopedVariables = buildNetworkScopedVariables(deploymentResult, promotionLinks) + const allVariables = { + ...genericVariables, + ...networkScopedVariables, + } + + const dotenv = `${formatKeyValueLines(allVariables, (key, value) => `${key}=${escapeDotenvValue(value)}`)}\n` + const shell = `${formatKeyValueLines(allVariables, (key, value) => `export ${key}=${escapeShellValue(value)}`)}\n` + const githubOutput = `${formatKeyValueLines(allVariables, (key, value) => `${key}=${value}`)}\n` + + return { + standard: PROMOTION_STANDARD, + ok: true, + network: deploymentResult.network, + deployment: { + verdict_id: deploymentResult.verdict_id ?? null, + source: deploymentSource, + deployer: deploymentResult.deployer ?? null, + contracts: deploymentResult.contracts, + verification: deploymentResult.verification, + inputs: deploymentResult.inputs ?? null, + }, + outputs: { + variables: allVariables, + generic: genericVariables, + network_scoped: networkScopedVariables, + dotenv, + shell, + github_output: githubOutput, + }, + files: { + json: null, + dotenv: null, + shell: null, + github_output: null, + }, + } + })() + + result.files.json = writeJsonFile(options.outputPath ?? process.env.DJD_PROMOTION_OUTPUT_PATH, result) + result.files.dotenv = writeTextFile( + options.dotenvPath ?? process.env.DJD_PROMOTION_DOTENV_PATH, + result.outputs.dotenv, + ) + result.files.shell = writeTextFile(options.shellPath ?? process.env.DJD_PROMOTION_SHELL_PATH, result.outputs.shell) + result.files.github_output = appendTextFile( + options.githubOutputPath ?? process.env.DJD_PROMOTION_GITHUB_OUTPUT_PATH ?? process.env[GITHUB_OUTPUT_ENV], + result.outputs.github_output, + ) + + if (result.files.json) { + writeJsonFile(result.files.json, result) + } + + return result +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + promoteEvaluatorStackDeployment() + .then((result) => { + console.log(JSON.stringify(result, null, 2)) + if (!result.ok) { + process.exit(1) + } + }) + .catch((error) => { + console.error(`[contracts:promote] FAILED: ${error instanceof Error ? error.message : String(error)}`) + process.exit(1) + }) +} diff --git a/scripts/publish-evaluator-stack.mjs b/scripts/publish-evaluator-stack.mjs new file mode 100644 index 0000000..13a99bc --- /dev/null +++ b/scripts/publish-evaluator-stack.mjs @@ -0,0 +1,190 @@ +import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import { fileURLToPath, pathToFileURL } from 'node:url' + +const __dirname = dirname(fileURLToPath(import.meta.url)) +const DEFAULT_REGISTRY_PATH = join(__dirname, '..', 'data', 'evaluator-deployments.json') + +function readJsonFile(filePath, label) { + if (typeof filePath !== 'string' || filePath.trim().length === 0) { + throw new Error(`Missing ${label}`) + } + + return JSON.parse(readFileSync(filePath, 'utf8')) +} + +function isAddress(value) { + return /^0x[a-fA-F0-9]{40}$/.test(value ?? '') +} + +function normalizeBoolean(value, fallback = false) { + if (typeof value === 'boolean') { + return value + } + + if (typeof value !== 'string') { + return fallback + } + + const normalized = value.trim().toLowerCase() + if (['1', 'true', 'yes', 'on'].includes(normalized)) return true + if (['0', 'false', 'no', 'off'].includes(normalized)) return false + return fallback +} + +function validateDeploymentResult(result) { + if (!result || result.standard !== 'djd-evaluator-deploy-result-v1') { + throw new Error('Invalid deployment result payload') + } + if (!result.network?.chain_id || !result.network?.chain_name) { + throw new Error('Deployment result is missing network metadata') + } + if (!isAddress(result.contracts?.verifier?.address ?? '')) { + throw new Error('Deployment result is missing verifier address') + } + if (!isAddress(result.contracts?.escrow?.address ?? '')) { + throw new Error('Deployment result is missing escrow address') + } + if (!isAddress(result.verification?.oracle_signer ?? '')) { + throw new Error('Deployment result is missing oracle signer verification metadata') + } + return result +} + +function validateStageReport(report) { + if (!report || report.standard !== 'djd-evaluator-stage-report-v1') { + throw new Error('Invalid stage report payload') + } + return report +} + +function getRegistryPath(options = {}) { + const configuredPath = options.registryPath ?? process.env.DJD_EVALUATOR_DEPLOYMENTS_PATH + return typeof configuredPath === 'string' && configuredPath.trim().length > 0 + ? configuredPath.trim() + : DEFAULT_REGISTRY_PATH +} + +function loadRegistry(path) { + if (!existsSync(path)) { + return { + standard: 'djd-evaluator-deployment-registry-v1', + updated_at: null, + deployments: {}, + } + } + + const parsed = JSON.parse(readFileSync(path, 'utf8')) + if (parsed.standard !== 'djd-evaluator-deployment-registry-v1' || typeof parsed.deployments !== 'object') { + throw new Error('Invalid deployment registry payload') + } + return parsed +} + +function deriveChecks(options = {}) { + if (options.checks && typeof options.checks === 'object') { + return { + preflight: options.checks.preflight ?? null, + verified: options.checks.verified ?? null, + smoked: options.checks.smoked ?? null, + health: options.checks.health ?? null, + staged: options.checks.staged ?? null, + } + } + + const stageReport = options.stageReport + if (!stageReport) { + return { + preflight: null, + verified: null, + smoked: null, + health: null, + staged: null, + } + } + + return { + preflight: stageReport.steps?.preflight?.ok ?? null, + verified: stageReport.steps?.verify?.ok ?? null, + smoked: stageReport.steps?.smoke?.ok ?? null, + health: stageReport.steps?.health?.skipped ? null : stageReport.steps?.health?.ok ?? null, + staged: stageReport.ok ?? null, + } +} + +function buildRegistryEntry(deploymentResult, checks, publishedAt) { + return { + published_at: publishedAt, + network: deploymentResult.network, + verdict_id: deploymentResult.verdict_id ?? null, + deployer: deploymentResult.deployer ?? null, + contracts: deploymentResult.contracts, + verification: deploymentResult.verification, + inputs: deploymentResult.inputs, + explorer: deploymentResult.explorer ?? null, + links: deploymentResult.links ?? {}, + checks, + } +} + +export async function publishEvaluatorStackDeployment(options = {}) { + const deploymentResult = validateDeploymentResult( + options.deploymentResult ?? + readJsonFile(options.deploymentResultPath ?? process.env.DJD_DEPLOY_RESULT_PATH, 'DJD_DEPLOY_RESULT_PATH'), + ) + const stageReport = + options.stageReport ?? + (options.stageReportPath ?? process.env.DJD_STAGE_REPORT_PATH + ? validateStageReport(readJsonFile(options.stageReportPath ?? process.env.DJD_STAGE_REPORT_PATH, 'DJD_STAGE_REPORT_PATH')) + : null) + const allowPartial = normalizeBoolean(options.allowPartial ?? process.env.DJD_PUBLISH_ALLOW_PARTIAL, false) + const checks = deriveChecks({ + checks: options.checks, + stageReport, + }) + + if (!allowPartial && (checks.preflight !== true || checks.verified !== true || checks.smoked !== true)) { + throw new Error('Publishing requires successful preflight, verify, and smoke checks or DJD_PUBLISH_ALLOW_PARTIAL=true') + } + + const registryPath = getRegistryPath(options) + const registry = loadRegistry(registryPath) + const networkKey = deploymentResult.network?.key ?? String(deploymentResult.network?.chain_id ?? 'unknown') + const publishedAt = options.publishedAt ?? new Date().toISOString() + const replacing = Object.prototype.hasOwnProperty.call(registry.deployments, networkKey) + + registry.deployments[networkKey] = buildRegistryEntry(deploymentResult, checks, publishedAt) + registry.updated_at = publishedAt + + mkdirSync(dirname(registryPath), { recursive: true }) + writeFileSync(registryPath, JSON.stringify(registry, null, 2) + '\n') + + return { + standard: 'djd-evaluator-deployment-publish-v1', + ok: true, + registry_path: registryPath, + published_at: publishedAt, + network: deploymentResult.network, + replacing, + checks, + deployment: registry.deployments[networkKey], + registry_summary: { + updated_at: registry.updated_at, + deployment_count: Object.keys(registry.deployments).length, + }, + } +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + publishEvaluatorStackDeployment() + .then((result) => { + console.log(JSON.stringify(result, null, 2)) + if (!result.ok) { + process.exit(1) + } + }) + .catch((error) => { + console.error(`[contracts:publish] FAILED: ${error instanceof Error ? error.message : String(error)}`) + process.exit(1) + }) +} diff --git a/scripts/smoke-evaluator-stack.mjs b/scripts/smoke-evaluator-stack.mjs new file mode 100644 index 0000000..7f389f9 --- /dev/null +++ b/scripts/smoke-evaluator-stack.mjs @@ -0,0 +1,305 @@ +import { readFileSync } from 'node:fs' +import { pathToFileURL } from 'node:url' +import { createPublicClient, http } from 'viem' +import { buildDeploymentBundleUrl, fetchDeploymentBundle } from './deploy-evaluator-stack.mjs' +import { + resolveEvaluatorStackDeploymentResult, + validateDeploymentResultPayload, +} from './evaluator-stack-deployment-source.mjs' +import { buildRuntimeChain, resolveRpcUrl, resolveRuntimeNetworkFromMetadata } from './evaluator-stack-runtime.mjs' +import { verifyEvaluatorStackDeployment } from './verify-evaluator-stack.mjs' + +const OUTCOME_CODES = { + release: 1, + manual_review: 2, + dispute: 3, + reject: 4, +} + +function readJsonFile(filePath, label) { + if (typeof filePath !== 'string' || filePath.trim().length === 0) { + throw new Error(`Missing ${label}`) + } + return JSON.parse(readFileSync(filePath, 'utf8')) +} + +function maybeBuildDeploymentBundleUrl(options = {}) { + const apiBaseUrl = options.apiBaseUrl ?? process.env.DJD_API_BASE_URL + const verdictId = options.verdictId ?? process.env.DJD_VERDICT_ID + + if (typeof apiBaseUrl !== 'string' || apiBaseUrl.length === 0) { + return null + } + if (typeof verdictId !== 'string' || verdictId.length === 0) { + return null + } + + return buildDeploymentBundleUrl(options) +} + +async function fetchJson(url, requestInit, label) { + const response = await fetch(url, requestInit) + if (!response.ok) { + throw new Error(`${label} request failed: ${response.status} ${response.statusText}`) + } + return await response.json() +} + +function buildRouteUrl(baseUrl, params = {}) { + const url = new URL(baseUrl) + for (const [key, value] of Object.entries(params)) { + if (value === undefined || value === null || value === '') { + continue + } + url.searchParams.set(key, String(value)) + } + return url.toString() +} + +function validateBundle(bundle) { + if (!bundle || bundle.standard !== 'djd-evaluator-deploy-bundle-v1') { + throw new Error('Invalid deployment bundle payload') + } + if (!Array.isArray(bundle.artifacts?.verifier?.abi) || !Array.isArray(bundle.artifacts?.escrow?.abi)) { + throw new Error('Deployment bundle must include verifier and escrow ABI artifacts') + } + return bundle +} + +function validateProofPayload(payload, expectedChainId, expectedVerifierAddress) { + if (!payload || payload.standard !== 'djd-evaluator-verifier-proof-v1') { + throw new Error('Invalid evaluator proof payload') + } + if (payload.ready !== true) { + throw new Error(`Evaluator proof is not ready: ${payload.reason ?? 'unknown'}`) + } + if (payload.verifier?.chain_id !== expectedChainId) { + throw new Error(`Evaluator proof chain mismatch: expected ${expectedChainId}, got ${payload.verifier?.chain_id}`) + } + if (payload.attestation?.status !== 'signed' || typeof payload.attestation?.signature !== 'string') { + throw new Error('Evaluator proof is missing a signed attestation') + } + if (payload.transaction?.to?.toLowerCase() !== expectedVerifierAddress.toLowerCase()) { + throw new Error('Evaluator proof transaction target does not match deployed verifier') + } + if (payload.transaction?.data !== payload.call?.calldata) { + throw new Error('Evaluator proof transaction data does not match call calldata') + } + return payload +} + +function validateEscrowPayload(payload, expectedChainId, expectedEscrowAddress) { + if (!payload || payload.standard !== 'djd-evaluator-escrow-settlement-v1') { + throw new Error('Invalid evaluator escrow payload') + } + if (payload.ready !== true) { + throw new Error(`Evaluator escrow payload is not ready: ${payload.reason ?? 'unknown'}`) + } + if (payload.escrow?.chain_id !== expectedChainId) { + throw new Error(`Evaluator escrow chain mismatch: expected ${expectedChainId}, got ${payload.escrow?.chain_id}`) + } + if (payload.transaction?.to?.toLowerCase() !== expectedEscrowAddress.toLowerCase()) { + throw new Error('Evaluator escrow transaction target does not match deployed escrow contract') + } + if (payload.transaction?.data !== payload.call?.calldata) { + throw new Error('Evaluator escrow transaction data does not match call calldata') + } + return payload +} + +async function resolveDeploymentBundle(options, deploymentResult) { + const explicitBundle = options.bundle + if (explicitBundle) { + return validateBundle(explicitBundle) + } + + const explicitBundlePath = options.bundlePath ?? process.env.DJD_DEPLOY_BUNDLE_PATH + if (explicitBundlePath) { + return validateBundle(readJsonFile(explicitBundlePath, 'DJD_DEPLOY_BUNDLE_PATH')) + } + + const explicitBundleUrl = options.bundleUrl ?? process.env.DJD_DEPLOY_BUNDLE_URL + const linkedBundleUrl = deploymentResult?.links?.bundle ?? null + const derivedBundleUrl = + !explicitBundleUrl && !linkedBundleUrl + ? maybeBuildDeploymentBundleUrl({ + ...options, + network: options.network ?? deploymentResult?.network?.key ?? null, + }) + : null + const bundleUrl = explicitBundleUrl ?? linkedBundleUrl ?? derivedBundleUrl + + if (!bundleUrl) { + throw new Error('Provide bundle, bundlePath, bundleUrl, or DJD_API_BASE_URL + DJD_VERDICT_ID') + } + + return validateBundle(await fetchDeploymentBundle(bundleUrl, { headers: options.requestHeaders ?? {} })) +} + +export async function runEvaluatorStackSmoke(options = {}) { + const { deploymentResult, source: deploymentSource } = await resolveEvaluatorStackDeploymentResult(options) + const validatedDeploymentResult = validateDeploymentResultPayload(deploymentResult) + const rpcResolution = resolveRpcUrl({ + rpcUrl: options.rpcUrl, + network: validatedDeploymentResult.network, + }) + if (!rpcResolution.ok) { + throw new Error(`Missing rpcUrl. Set ${rpcResolution.expected_envs.join(' or ')}`) + } + const rpcUrl = rpcResolution.rpcUrl + const bundle = await resolveDeploymentBundle(options, validatedDeploymentResult) + const requestHeaders = options.requestHeaders ?? {} + const verification = await verifyEvaluatorStackDeployment({ + ...options, + rpcUrl, + deploymentResult: validatedDeploymentResult, + bundle, + }) + + if (!verification.ok) { + throw new Error(`Deployment verification failed: ${verification.failed_checks.join(', ')}`) + } + + const networkKey = resolveRuntimeNetworkFromMetadata(validatedDeploymentResult.network).key + const verdictId = options.verdictId ?? process.env.DJD_VERDICT_ID ?? validatedDeploymentResult.verdict_id + if (typeof verdictId !== 'string' || verdictId.length === 0) { + throw new Error('Missing evaluator verdict id for smoke check') + } + + const proofBaseUrl = + options.proofUrl ?? + validatedDeploymentResult.links?.verifier_proof ?? + (options.apiBaseUrl ?? process.env.DJD_API_BASE_URL + ? `${options.apiBaseUrl ?? process.env.DJD_API_BASE_URL}/v1/score/evaluator/proof` + : null) + const escrowBaseUrl = + options.escrowUrl ?? + validatedDeploymentResult.links?.escrow_settlement ?? + (options.apiBaseUrl ?? process.env.DJD_API_BASE_URL + ? `${options.apiBaseUrl ?? process.env.DJD_API_BASE_URL}/v1/score/evaluator/escrow` + : null) + + if (!proofBaseUrl || !escrowBaseUrl) { + throw new Error('Missing proof or escrow smoke endpoint URL') + } + + const useRegistryAddressResolution = + !options.proofUrl && + !options.escrowUrl && + (deploymentSource.kind === 'registry_file' || deploymentSource.kind === 'api_registry') + + const proofUrl = buildRouteUrl(proofBaseUrl, { + id: verdictId, + network: networkKey, + target_contract: useRegistryAddressResolution ? null : validatedDeploymentResult.contracts.verifier.address, + }) + const escrowUrl = buildRouteUrl(escrowBaseUrl, { + id: verdictId, + network: networkKey, + escrow_contract: useRegistryAddressResolution ? null : validatedDeploymentResult.contracts.escrow.address, + }) + + const [proofPayload, escrowPayload] = await Promise.all([ + fetchJson(proofUrl, { headers: requestHeaders }, 'Evaluator proof'), + fetchJson(escrowUrl, { headers: requestHeaders }, 'Evaluator escrow'), + ]) + + const validatedProof = validateProofPayload( + proofPayload, + validatedDeploymentResult.network.chain_id, + validatedDeploymentResult.contracts.verifier.address, + ) + const validatedEscrow = validateEscrowPayload( + escrowPayload, + validatedDeploymentResult.network.chain_id, + validatedDeploymentResult.contracts.escrow.address, + ) + + const publicClient = createPublicClient({ + chain: buildRuntimeChain(validatedDeploymentResult.network, rpcUrl), + transport: http(rpcUrl), + }) + const verifierAbi = bundle.artifacts.verifier.abi + const escrowAbi = bundle.artifacts.escrow.abi + const signature = validatedProof.attestation.signature + + const [onchainDigest, verifierAccepted] = await Promise.all([ + publicClient.readContract({ + address: validatedDeploymentResult.contracts.verifier.address, + abi: verifierAbi, + functionName: 'hashVerdict', + args: [validatedProof.verdict], + }), + publicClient.readContract({ + address: validatedDeploymentResult.contracts.verifier.address, + abi: verifierAbi, + functionName: 'verifyVerdict', + args: [validatedProof.verdict, signature], + }), + ]) + + if (verifierAccepted !== true) { + throw new Error('Onchain verifier rejected the evaluator proof attestation') + } + + const simulation = await publicClient.simulateContract({ + address: validatedDeploymentResult.contracts.escrow.address, + abi: escrowAbi, + functionName: 'settleWithDJDVerdict', + args: [validatedEscrow.verdict, validatedEscrow.attestation.signature], + account: validatedEscrow.verdict.wallet, + }) + + const expectedOutcomeCode = OUTCOME_CODES[validatedEscrow.settlement.outcome] + const simulatedOutcomeCode = Number(simulation.result) + if (simulatedOutcomeCode !== expectedOutcomeCode) { + throw new Error( + `Escrow simulation outcome mismatch: expected ${expectedOutcomeCode}, got ${simulatedOutcomeCode}`, + ) + } + + return { + standard: 'djd-evaluator-live-smoke-v1', + ok: true, + verdict_id: verdictId, + network: validatedDeploymentResult.network, + deployment: { + source: deploymentSource, + used_registry_address_resolution: useRegistryAddressResolution, + }, + deployment_verification: verification, + api: { + proof_url: proofUrl, + escrow_url: escrowUrl, + proof_ready: validatedProof.ready, + escrow_ready: validatedEscrow.ready, + }, + verifier: { + address: validatedDeploymentResult.contracts.verifier.address, + digest: onchainDigest, + digest_matches_attestation: onchainDigest === validatedProof.attestation.digest, + accepted: verifierAccepted, + }, + escrow: { + address: validatedDeploymentResult.contracts.escrow.address, + expected_outcome: validatedEscrow.settlement.outcome, + expected_outcome_code: expectedOutcomeCode, + simulated_outcome_code: simulatedOutcomeCode, + simulation_ok: simulatedOutcomeCode === expectedOutcomeCode, + }, + } +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + runEvaluatorStackSmoke() + .then((result) => { + console.log(JSON.stringify(result, null, 2)) + if (!result.ok) { + process.exit(1) + } + }) + .catch((error) => { + console.error(`[contracts:smoke] FAILED: ${error instanceof Error ? error.message : String(error)}`) + process.exit(1) + }) +} diff --git a/scripts/stage-evaluator-stack.mjs b/scripts/stage-evaluator-stack.mjs new file mode 100644 index 0000000..a9f4080 --- /dev/null +++ b/scripts/stage-evaluator-stack.mjs @@ -0,0 +1,353 @@ +import { writeFileSync } from 'node:fs' +import { pathToFileURL } from 'node:url' +import { runEvaluatorStackDeployment } from './deploy-evaluator-stack.mjs' +import { runPostDeploySmokeCheck } from './post-deploy-smoke.mjs' +import { runEvaluatorStackPreflight } from './preflight-evaluator-stack.mjs' +import { promoteEvaluatorStackDeployment } from './promote-evaluator-stack.mjs' +import { publishEvaluatorStackDeployment } from './publish-evaluator-stack.mjs' +import { runEvaluatorStackSmoke } from './smoke-evaluator-stack.mjs' +import { verifyEvaluatorStackDeployment } from './verify-evaluator-stack.mjs' + +function writeStageReport(outputPath, report) { + if (typeof outputPath !== 'string' || outputPath.trim().length === 0) { + return + } + + writeFileSync(outputPath, JSON.stringify(report, null, 2) + '\n') +} + +function normalizeBoolean(value, fallback = false) { + if (typeof value === 'boolean') { + return value + } + + if (typeof value !== 'string') { + return fallback + } + + const normalized = value.trim().toLowerCase() + if (['1', 'true', 'yes', 'on'].includes(normalized)) return true + if (['0', 'false', 'no', 'off'].includes(normalized)) return false + return fallback +} + +function serializeError(error) { + if (error instanceof Error) { + return { + message: error.message, + name: error.name, + } + } + + return { + message: String(error), + name: 'Error', + } +} + +function summarizePreflightFailure(result) { + const failures = [] + if (!result?.bundle?.ready) failures.push('bundle') + if (!result?.rpc?.ready || !result?.rpc?.reachable || result?.rpc?.chain_id_matches_expected !== true) { + failures.push('rpc') + } + if (!result?.deployer?.ready) failures.push('deployer') + return failures.join(', ') || 'unknown' +} + +function resolvePromotionPaths(options = {}) { + return { + outputPath: + options.promotionOutputPath ?? + process.env.DJD_STAGE_PROMOTION_OUTPUT_PATH ?? + process.env.DJD_PROMOTION_OUTPUT_PATH ?? + null, + dotenvPath: + options.promotionDotenvPath ?? + process.env.DJD_STAGE_PROMOTION_DOTENV_PATH ?? + process.env.DJD_PROMOTION_DOTENV_PATH ?? + null, + shellPath: + options.promotionShellPath ?? + process.env.DJD_STAGE_PROMOTION_SHELL_PATH ?? + process.env.DJD_PROMOTION_SHELL_PATH ?? + null, + githubOutputPath: + options.promotionGithubOutputPath ?? + process.env.DJD_STAGE_PROMOTION_GITHUB_OUTPUT_PATH ?? + process.env.DJD_PROMOTION_GITHUB_OUTPUT_PATH ?? + process.env.GITHUB_OUTPUT ?? + null, + } +} + +function hasPromotionSink(paths) { + return Object.values(paths).some((value) => typeof value === 'string' && value.trim().length > 0) +} + +export async function runEvaluatorStackStage(options = {}) { + const startedAt = new Date().toISOString() + const outputPath = options.outputPath ?? process.env.DJD_STAGE_REPORT_PATH + const runHealth = normalizeBoolean( + options.runHealth ?? process.env.DJD_STAGE_RUN_HEALTH, + Boolean(options.health ?? process.env.DJD_HEALTHCHECK_URL), + ) + const runPublish = normalizeBoolean(options.publishRegistry ?? process.env.DJD_STAGE_PUBLISH_REGISTRY, false) + const promotionPaths = resolvePromotionPaths(options) + const runPromote = normalizeBoolean( + options.promote ?? process.env.DJD_STAGE_PROMOTE, + runPublish || hasPromotionSink(promotionPaths), + ) + + const report = { + standard: 'djd-evaluator-stage-report-v1', + started_at: startedAt, + finished_at: null, + ok: false, + steps: { + preflight: { + ok: false, + skipped: false, + result: null, + error: null, + }, + deploy: { + ok: false, + skipped: false, + result: null, + error: null, + }, + verify: { + ok: false, + skipped: false, + result: null, + error: null, + }, + smoke: { + ok: false, + skipped: false, + result: null, + error: null, + }, + health: { + ok: false, + skipped: !runHealth, + result: null, + error: null, + }, + publish: { + ok: false, + skipped: !runPublish, + result: null, + error: null, + }, + promote: { + ok: false, + skipped: !runPromote, + result: null, + error: null, + }, + }, + } + + const finalize = () => { + report.finished_at = new Date().toISOString() + report.ok = + report.steps.preflight.ok && + report.steps.deploy.ok && + report.steps.verify.ok && + report.steps.smoke.ok && + (report.steps.health.skipped || report.steps.health.ok) && + (report.steps.publish.skipped || report.steps.publish.ok) && + (report.steps.promote.skipped || report.steps.promote.ok) + writeStageReport(outputPath, report) + return report + } + + try { + const preflightResult = await runEvaluatorStackPreflight(options) + report.steps.preflight.ok = preflightResult.ok + report.steps.preflight.result = preflightResult + if (!preflightResult.ok) { + report.steps.preflight.error = { + message: `Preflight failed: ${summarizePreflightFailure(preflightResult)}`, + name: 'PreflightError', + } + report.steps.deploy.skipped = true + report.steps.verify.skipped = true + report.steps.smoke.skipped = true + report.steps.health.skipped = true + report.steps.publish.skipped = true + report.steps.promote.skipped = true + return finalize() + } + } catch (error) { + report.steps.preflight.error = serializeError(error) + report.steps.deploy.skipped = true + report.steps.verify.skipped = true + report.steps.smoke.skipped = true + report.steps.health.skipped = true + report.steps.publish.skipped = true + report.steps.promote.skipped = true + return finalize() + } + + let deploymentResult + try { + deploymentResult = await runEvaluatorStackDeployment(options) + report.steps.deploy.ok = true + report.steps.deploy.result = deploymentResult + } catch (error) { + report.steps.deploy.error = serializeError(error) + report.steps.verify.skipped = true + report.steps.smoke.skipped = true + report.steps.health.skipped = true + report.steps.publish.skipped = true + report.steps.promote.skipped = true + return finalize() + } + + let verifyResult + try { + verifyResult = await verifyEvaluatorStackDeployment({ + ...options, + deploymentResult, + }) + report.steps.verify.ok = verifyResult.ok + report.steps.verify.result = verifyResult + if (!verifyResult.ok) { + report.steps.verify.error = { + message: `Verification failed: ${verifyResult.failed_checks.join(', ')}`, + name: 'VerificationError', + } + report.steps.smoke.skipped = true + report.steps.health.skipped = true + report.steps.publish.skipped = true + report.steps.promote.skipped = true + return finalize() + } + } catch (error) { + report.steps.verify.error = serializeError(error) + report.steps.smoke.skipped = true + report.steps.health.skipped = true + report.steps.publish.skipped = true + report.steps.promote.skipped = true + return finalize() + } + + try { + const smokeResult = await runEvaluatorStackSmoke({ + ...options, + deploymentResult, + }) + report.steps.smoke.ok = smokeResult.ok + report.steps.smoke.result = smokeResult + if (!smokeResult.ok) { + report.steps.smoke.error = { + message: 'Evaluator smoke step reported ok=false', + name: 'SmokeError', + } + report.steps.health.skipped = true + report.steps.publish.skipped = true + report.steps.promote.skipped = true + return finalize() + } + } catch (error) { + report.steps.smoke.error = serializeError(error) + report.steps.health.skipped = true + report.steps.publish.skipped = true + report.steps.promote.skipped = true + return finalize() + } + + if (!runHealth) { + if (!runPublish && !runPromote) { + return finalize() + } + } else { + try { + await runPostDeploySmokeCheck(options.health ?? options) + report.steps.health.ok = true + report.steps.health.result = { + checked: true, + } + } catch (error) { + report.steps.health.error = serializeError(error) + report.steps.publish.skipped = true + report.steps.promote.skipped = true + return finalize() + } + } + + if (runPublish) { + try { + const publishResult = await publishEvaluatorStackDeployment({ + ...options, + deploymentResult, + checks: { + preflight: report.steps.preflight.ok, + verified: report.steps.verify.ok, + smoked: report.steps.smoke.ok, + health: runHealth ? report.steps.health.ok : null, + staged: true, + }, + }) + report.steps.publish.ok = publishResult.ok + report.steps.publish.result = publishResult + } catch (error) { + report.steps.publish.error = serializeError(error) + report.steps.promote.skipped = true + return finalize() + } + } + + if (!runPromote) { + return finalize() + } + + try { + const promoteResult = await promoteEvaluatorStackDeployment( + report.steps.publish.ok + ? { + network: deploymentResult.network?.key ?? options.network ?? null, + deploymentResult: null, + deploymentResultPath: '', + registryPath: options.registryPath ?? process.env.DJD_EVALUATOR_DEPLOYMENTS_PATH, + apiBaseUrl: options.apiBaseUrl, + outputPath: promotionPaths.outputPath, + dotenvPath: promotionPaths.dotenvPath, + shellPath: promotionPaths.shellPath, + githubOutputPath: promotionPaths.githubOutputPath, + } + : { + deploymentResult, + apiBaseUrl: options.apiBaseUrl, + outputPath: promotionPaths.outputPath, + dotenvPath: promotionPaths.dotenvPath, + shellPath: promotionPaths.shellPath, + githubOutputPath: promotionPaths.githubOutputPath, + }, + ) + report.steps.promote.ok = promoteResult.ok + report.steps.promote.result = promoteResult + } catch (error) { + report.steps.promote.error = serializeError(error) + return finalize() + } + + return finalize() +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + runEvaluatorStackStage() + .then((report) => { + console.log(JSON.stringify(report, null, 2)) + if (!report.ok) { + process.exit(1) + } + }) + .catch((error) => { + console.error(`[contracts:stage] FAILED: ${error instanceof Error ? error.message : String(error)}`) + process.exit(1) + }) +} diff --git a/scripts/verify-evaluator-stack.mjs b/scripts/verify-evaluator-stack.mjs new file mode 100644 index 0000000..bf0e08c --- /dev/null +++ b/scripts/verify-evaluator-stack.mjs @@ -0,0 +1,189 @@ +import { readFileSync } from 'node:fs' +import { pathToFileURL } from 'node:url' +import { createPublicClient, http } from 'viem' +import { buildDeploymentBundleUrl, fetchDeploymentBundle } from './deploy-evaluator-stack.mjs' +import { + resolveEvaluatorStackDeploymentResult, + validateDeploymentResultPayload, +} from './evaluator-stack-deployment-source.mjs' +import { buildRuntimeChain, resolveRpcUrl } from './evaluator-stack-runtime.mjs' + +function maybeBuildDeploymentBundleUrl(options = {}) { + const apiBaseUrl = options.apiBaseUrl ?? process.env.DJD_API_BASE_URL + const verdictId = options.verdictId ?? process.env.DJD_VERDICT_ID + + if (typeof apiBaseUrl !== 'string' || apiBaseUrl.length === 0) { + return null + } + if (typeof verdictId !== 'string' || verdictId.length === 0) { + return null + } + + return buildDeploymentBundleUrl(options) +} + +function readJsonFile(filePath, label) { + if (typeof filePath !== 'string' || filePath.trim().length === 0) { + throw new Error(`Missing ${label}`) + } + return JSON.parse(readFileSync(filePath, 'utf8')) +} + +function validateBundle(bundle) { + if (!bundle || bundle.standard !== 'djd-evaluator-deploy-bundle-v1') { + throw new Error('Invalid deployment bundle payload') + } + return bundle +} + +export async function verifyEvaluatorStackDeployment(options = {}) { + const { deploymentResult, source: deploymentSource } = await resolveEvaluatorStackDeploymentResult(options) + const result = validateDeploymentResultPayload(deploymentResult) + const rpcResolution = resolveRpcUrl({ + rpcUrl: options.rpcUrl, + network: result.network, + }) + const explicitBundlePath = options.bundlePath ?? process.env.DJD_DEPLOY_BUNDLE_PATH + const explicitBundleUrl = options.bundleUrl ?? process.env.DJD_DEPLOY_BUNDLE_URL + const linkedBundleUrl = result?.links?.bundle ?? null + const derivedBundleUrl = + !options.bundle && !explicitBundlePath && !explicitBundleUrl && !linkedBundleUrl + ? maybeBuildDeploymentBundleUrl({ + ...options, + network: options.network ?? result.network?.key ?? null, + }) + : null + const bundle = + options.bundle ?? + (explicitBundlePath + ? readJsonFile(explicitBundlePath, 'DJD_DEPLOY_BUNDLE_PATH') + : explicitBundleUrl || linkedBundleUrl || derivedBundleUrl + ? await fetchDeploymentBundle(explicitBundleUrl ?? linkedBundleUrl ?? derivedBundleUrl) + : null) + + if (!rpcResolution.ok) { + throw new Error(`Missing rpcUrl. Set ${rpcResolution.expected_envs.join(' or ')}`) + } + + const validatedBundle = bundle ? validateBundle(bundle) : null + const chain = buildRuntimeChain(result.network, rpcResolution.rpcUrl) + const publicClient = createPublicClient({ + chain, + transport: http(rpcResolution.rpcUrl), + }) + + const verifierAddress = result.contracts.verifier.address + const escrowAddress = result.contracts.escrow.address + + const verifierAbi = validatedBundle?.artifacts?.verifier?.abi + const escrowAbi = validatedBundle?.artifacts?.escrow?.abi + if (!Array.isArray(verifierAbi) || !Array.isArray(escrowAbi)) { + throw new Error('Verification requires verifier and escrow ABI artifacts') + } + + const [oracleSigner, escrowVerifier, escrowProvider, escrowCounterparty, escrowIdHash, settled, releaseAuthorized] = + await Promise.all([ + publicClient.readContract({ + address: verifierAddress, + abi: verifierAbi, + functionName: 'oracleSigner', + }), + publicClient.readContract({ + address: escrowAddress, + abi: escrowAbi, + functionName: 'verifier', + }), + publicClient.readContract({ + address: escrowAddress, + abi: escrowAbi, + functionName: 'provider', + }), + publicClient.readContract({ + address: escrowAddress, + abi: escrowAbi, + functionName: 'counterparty', + }), + publicClient.readContract({ + address: escrowAddress, + abi: escrowAbi, + functionName: 'escrowIdHash', + }), + publicClient.readContract({ + address: escrowAddress, + abi: escrowAbi, + functionName: 'settled', + }), + publicClient.readContract({ + address: escrowAddress, + abi: escrowAbi, + functionName: 'releaseAuthorized', + }), + ]) + + const checks = { + oracle_signer_matches: oracleSigner === result.verification.oracle_signer, + escrow_verifier_matches: escrowVerifier === result.verification.escrow_verifier, + escrow_provider_matches: escrowProvider === result.verification.escrow_provider, + escrow_counterparty_matches: escrowCounterparty === result.verification.escrow_counterparty, + escrow_id_hash_matches: escrowIdHash === result.verification.escrow_id_hash, + } + + const bundleChecks = validatedBundle + ? { + verifier_constructor_matches: + validatedBundle.deployment.verifier.action === 'use_existing' + ? verifierAddress === validatedBundle.deployment.verifier.current_address + : oracleSigner === validatedBundle.deployment.verifier.constructor.initial_signer, + escrow_constructor_matches: + escrowProvider === validatedBundle.deployment.escrow.constructor.provider && + escrowCounterparty === validatedBundle.deployment.escrow.constructor.counterparty && + escrowIdHash === validatedBundle.deployment.escrow.constructor.escrow_id_hash, + } + : null + + const failedChecks = Object.entries({ + ...checks, + ...(bundleChecks ?? {}), + }) + .filter(([, passed]) => !passed) + .map(([name]) => name) + + return { + standard: 'djd-evaluator-deploy-verification-v1', + ok: failedChecks.length === 0, + verdict_id: result.verdict_id, + network: result.network, + contracts: result.contracts, + onchain: { + oracle_signer: oracleSigner, + escrow_verifier: escrowVerifier, + escrow_provider: escrowProvider, + escrow_counterparty: escrowCounterparty, + escrow_id_hash: escrowIdHash, + settled, + release_authorized: releaseAuthorized, + }, + checks, + bundle_checks: bundleChecks, + failed_checks: failedChecks, + deployment: deploymentSource, + links: { + bundle: explicitBundleUrl ?? linkedBundleUrl ?? derivedBundleUrl ?? null, + deployment_registry: result.links?.deployment_registry ?? null, + }, + } +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + verifyEvaluatorStackDeployment() + .then((result) => { + console.log(JSON.stringify(result, null, 2)) + if (!result.ok) { + process.exit(1) + } + }) + .catch((error) => { + console.error(`[verify] FAILED: ${error instanceof Error ? error.message : String(error)}`) + process.exit(1) + }) +} diff --git a/src/app.ts b/src/app.ts index 2101027..7046948 100644 --- a/src/app.ts +++ b/src/app.ts @@ -204,7 +204,147 @@ const x402Middleware = paymentMiddlewareFromConfig( decision: 'review', confidence: 0.78, risk: { risk_level: 'watch', action: 'monitor' }, - certification: { active: true, tier: 'Trusted' }, + certification: { active: true, tier: 'Transactional' }, + }, + }, + }), + }, + }, + '/v1/score/evaluator/evidence': { + accepts: [payment(ENDPOINT_PRICING['/v1/score/evaluator/evidence'])], + description: + 'ERC-8183 evaluator evidence packet for a wallet, packaging the settlement verdict with certification baseline, forensics summary, and traceable artifacts', + extensions: { + ...declareDiscoveryExtension({ + input: { wallet: '0x1234567890abcdef1234567890abcdef12345678' }, + inputSchema: { + properties: { + wallet: { type: 'string', description: 'Wallet address to generate an evaluator evidence packet for' }, + }, + required: ['wallet'], + }, + output: { + example: { + wallet: '0x1234...', + standard: 'erc-8183-evaluator-evidence-prototype', + packet_id: 'evidence_7f3a4d1c93a2bc10', + evaluator: { decision: 'review', confidence: 0.78 }, + baseline: { profile: 'djd-transactional-settlement-v1', settlement_tier: 'Transactional' }, + }, + }, + }), + }, + }, + '/v1/score/evaluator/oracle': { + accepts: [payment(ENDPOINT_PRICING['/v1/score/evaluator/oracle'])], + description: + 'ERC-8183 oracle-ready evaluator verdict for a wallet, persisting a compact settlement decision with traceable forensic metadata and a signed EIP-712 attestation when a signer is configured', + extensions: { + ...declareDiscoveryExtension({ + input: { + wallet: '0x1234567890abcdef1234567890abcdef12345678', + counterparty_wallet: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd', + escrow_id: 'escrow-123', + }, + inputSchema: { + properties: { + wallet: { type: 'string', description: 'Provider wallet address to evaluate for settlement' }, + counterparty_wallet: { type: 'string', description: 'Optional client or counterparty wallet' }, + escrow_id: { type: 'string', description: 'Optional escrow or settlement reference id' }, + }, + required: ['wallet'], + }, + output: { + example: { + standard: 'erc-8183-evaluator-oracle-prototype', + verdict_id: 'verdict_123e4567-e89b-42d3-a456-426614174000', + recommendation: 'manual_review', + approved: false, + confidence: 78, + forensic_trace_id: 'trace_7f3a4d1c93a2bc10', + }, + }, + }), + }, + }, + '/v1/score/evaluator/verdict': { + accepts: [payment(ENDPOINT_PRICING['/v1/score/evaluator/verdict'])], + description: 'Fetch a previously issued evaluator oracle verdict by id, including its attestation envelope', + extensions: { + ...declareDiscoveryExtension({ + input: { id: 'verdict_123e4567-e89b-42d3-a456-426614174000' }, + inputSchema: { + properties: { + id: { type: 'string', description: 'Stored evaluator verdict id returned by the oracle endpoint' }, + }, + required: ['id'], + }, + output: { + example: { + verdict_id: 'verdict_123e4567-e89b-42d3-a456-426614174000', + recommendation: 'release', + recorded_at: '2026-03-12T02:00:00.000Z', + }, + }, + }), + }, + }, + '/v1/score/evaluator/verdicts': { + accepts: [payment(ENDPOINT_PRICING['/v1/score/evaluator/verdicts'])], + description: 'List recent persisted evaluator verdicts for a wallet', + extensions: { + ...declareDiscoveryExtension({ + input: { wallet: '0x1234567890abcdef1234567890abcdef12345678', limit: 10 }, + inputSchema: { + properties: { + wallet: { type: 'string', description: 'Wallet address to inspect for verdict history' }, + limit: { type: 'integer', description: 'Max verdicts to return (default 10, max 50)' }, + }, + required: ['wallet'], + }, + output: { + example: { + standard: 'djd-evaluator-verdict-history-v1', + wallet: '0x1234...', + total: 2, + summary: { approvals: 1, manual_review: 1, disputes: 0, rejects: 0 }, + }, + }, + }), + }, + }, + '/v1/score/evaluator/callback': { + accepts: [payment(ENDPOINT_PRICING['/v1/score/evaluator/callback'])], + description: + 'Contract-ready callback payload for a stored evaluator verdict, returning ABI-backed calldata when the verdict attestation is signed', + extensions: { + ...declareDiscoveryExtension({ + input: { + id: 'verdict_123e4567-e89b-42d3-a456-426614174000', + target_contract: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd', + }, + inputSchema: { + properties: { + id: { type: 'string', description: 'Stored evaluator verdict id to convert into callback calldata' }, + target_contract: { + type: 'string', + description: 'Optional target contract address to place into the returned transaction envelope', + }, + }, + required: ['id'], + }, + output: { + example: { + standard: 'djd-evaluator-oracle-callback-v1', + ready: true, + callback: { + selector: '0x12345678', + calldata: '0x12345678deadbeef', + }, + transaction: { + to: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd', + value: '0', + }, }, }, }), @@ -781,19 +921,20 @@ const x402Middleware = paymentMiddlewareFromConfig( }, '/v1/certification/apply': { accepts: [payment(ENDPOINT_PRICING['/v1/certification/apply'])], - description: 'Apply for annual DJD Certified Agent badge — verified on-chain reputation', + description: 'Legacy transactional certification apply path for the payer wallet', extensions: { ...declareDiscoveryExtension({ - bodyType: 'json', - input: { wallet: '0x1234567890abcdef1234567890abcdef12345678' }, + input: { payer_wallet: '0x1234567890abcdef1234567890abcdef12345678' }, inputSchema: { - properties: { wallet: { type: 'string', description: 'Wallet to certify' } }, - required: ['wallet'], + properties: { + payer_wallet: { type: 'string', description: 'Wallet inferred from x402 payment proof or payer header' }, + }, + required: ['payer_wallet'], }, output: { example: { certificationId: 'cert_abc123', - tier: 'gold', + tier: 'Transactional', expiresAt: '2027-02-25', badgeUrl: '/v1/badge/0x1234...', }, @@ -801,6 +942,18 @@ const x402Middleware = paymentMiddlewareFromConfig( }), }, }, + '/v1/certification/apply/operational': { + accepts: [payment(ENDPOINT_PRICING['/v1/certification/apply/operational'])], + description: 'Tier 1 / Operational certification for the payer wallet', + }, + '/v1/certification/apply/transactional': { + accepts: [payment(ENDPOINT_PRICING['/v1/certification/apply/transactional'])], + description: 'Tier 2 / Transactional certification for the payer wallet', + }, + '/v1/certification/apply/autonomous': { + accepts: [payment(ENDPOINT_PRICING['/v1/certification/apply/autonomous'])], + description: 'Tier 3 / Autonomous certification for the payer wallet', + }, }, facilitatorClient, [{ network: NETWORK, server: new ExactEvmScheme() }], @@ -809,6 +962,11 @@ const x402Middleware = paymentMiddlewareFromConfig( const PAID_ROUTES = new Set([ '/v1/score/full', '/v1/score/evaluator', + '/v1/score/evaluator/evidence', + '/v1/score/evaluator/oracle', + '/v1/score/evaluator/verdict', + '/v1/score/evaluator/verdicts', + '/v1/score/evaluator/callback', '/v1/score/risk', '/v1/cluster', '/v1/score/refresh', @@ -831,6 +989,9 @@ const PAID_ROUTES = new Set([ '/v1/forensics/reports', '/v1/forensics/timeline', '/v1/certification/apply', + '/v1/certification/apply/operational', + '/v1/certification/apply/transactional', + '/v1/certification/apply/autonomous', ]) app.use(async (c, next) => { diff --git a/src/blockchain.ts b/src/blockchain.ts index 95f9651..9558592 100644 --- a/src/blockchain.ts +++ b/src/blockchain.ts @@ -52,8 +52,17 @@ export function getPublicClient(): Client { export const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' as const -// ERC-8004 Identity Registry — placeholder until deployed -const ERC8004_REGISTRY = '0x0000000000000000000000000000000000000000' as const +const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' as const + +function resolveConfiguredAddress(rawAddress: string | undefined): `0x${string}` { + if (/^0x[a-fA-F0-9]{40}$/.test(rawAddress ?? '')) { + return rawAddress!.toLowerCase() as `0x${string}` + } + return ZERO_ADDRESS +} + +// ERC-8004 Identity Registry — configurable for Base deployments as the spec matures. +export const ERC8004_IDENTITY_REGISTRY = resolveConfiguredAddress(process.env.ERC8004_IDENTITY_REGISTRY) // ERC-8004 Reputation Registry — live on Base mainnet export const ERC8004_REPUTATION_REGISTRY = '0x8004BAa17C55a88189AE136b182e5fdA19dE9b63' as const @@ -76,6 +85,23 @@ const BLOCKS_PER_DAY = 43_200n const LOG_CHUNK_SIZE = 10_000n const LOG_PARALLEL_BATCH = 5 +export function getERC8004RegistryStatus(): { + identity_registry: string | null + identity_registry_configured: boolean + reputation_registry: string + network: 'base' + chain_id: number +} { + const identityConfigured = ERC8004_IDENTITY_REGISTRY !== ZERO_ADDRESS + return { + identity_registry: identityConfigured ? ERC8004_IDENTITY_REGISTRY : null, + identity_registry_configured: identityConfigured, + reputation_registry: ERC8004_REPUTATION_REGISTRY, + network: 'base', + chain_id: base.id, + } +} + // ---------- Helpers ---------- function clamp(val: T, min: T, max: T): T { @@ -225,12 +251,12 @@ export async function estimateWalletAgeDays( * Returns false if the registry contract is not deployed or the call fails. */ export async function checkERC8004Registration(wallet: `0x${string}`): Promise { - if (ERC8004_REGISTRY === '0x0000000000000000000000000000000000000000') { + if (ERC8004_IDENTITY_REGISTRY === ZERO_ADDRESS) { return false } try { const result = await getPublicClient().readContract({ - address: ERC8004_REGISTRY, + address: ERC8004_IDENTITY_REGISTRY, abi: ERC8004_ABI, functionName: 'isRegistered', args: [wallet], diff --git a/src/config/constants.ts b/src/config/constants.ts index d865b3c..15a66c1 100644 --- a/src/config/constants.ts +++ b/src/config/constants.ts @@ -14,6 +14,11 @@ import { buildPublicUrl } from './public.js' export const ENDPOINT_PRICING: Record = { '/v1/score/full': 0.1, '/v1/score/evaluator': 0.35, + '/v1/score/evaluator/evidence': 0.45, + '/v1/score/evaluator/oracle': 0.6, + '/v1/score/evaluator/verdict': 0.12, + '/v1/score/evaluator/verdicts': 0.3, + '/v1/score/evaluator/callback': 0.2, '/v1/score/risk': 0.5, '/v1/score/refresh': 0.25, '/v1/report': 0.02, @@ -35,7 +40,10 @@ export const ENDPOINT_PRICING: Record = { '/v1/forensics/reports': 0.15, '/v1/forensics/watchlist': 0.25, '/v1/forensics/timeline': 0.2, - '/v1/certification/apply': 99.0, + '/v1/certification/apply': 200.0, + '/v1/certification/apply/operational': 50.0, + '/v1/certification/apply/transactional': 200.0, + '/v1/certification/apply/autonomous': 500.0, } // ── Tier Configuration ────────────────────────────────────────────────────── @@ -243,8 +251,6 @@ export const REPUTATION_PUBLISHER_CONFIG = { INTER_TX_DELAY_MS: 3_000, /** Min ETH balance required to publish (0.001 ETH) */ MIN_ETH_BALANCE: 1_000_000_000_000_000n, - /** Full score endpoint URL embedded in on-chain record */ - SCORE_ENDPOINT: buildPublicUrl('/v1/score/full'), } as const // ── Anomaly Detector ──────────────────────────────────────────────────── diff --git a/src/config/modelVersion.ts b/src/config/modelVersion.ts new file mode 100644 index 0000000..ca579ac --- /dev/null +++ b/src/config/modelVersion.ts @@ -0,0 +1 @@ +export const MODEL_VERSION = '2.5.0' diff --git a/src/contracts/djdEvaluatorEscrowSettlementExample.ts b/src/contracts/djdEvaluatorEscrowSettlementExample.ts new file mode 100644 index 0000000..3bfaa10 --- /dev/null +++ b/src/contracts/djdEvaluatorEscrowSettlementExample.ts @@ -0,0 +1,35 @@ +import { encodeFunctionData, parseAbi, toFunctionSelector } from 'viem' +import { EVALUATOR_VERDICT_TUPLE_SIGNATURE, type EvaluatorVerdictVerifierInput } from './djdEvaluatorVerdictVerifier.js' + +export const DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_CONTRACT = 'DJDEvaluatorEscrowSettlementExample' +export const DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_FUNCTION = 'settleWithDJDVerdict' +export const DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_SIGNATURE = `settleWithDJDVerdict(${EVALUATOR_VERDICT_TUPLE_SIGNATURE},bytes)` +export const DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_SELECTOR = toFunctionSelector( + DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_SIGNATURE, +) + +export const DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_ABI = parseAbi([ + 'constructor(address verifier,address provider,address counterparty,bytes32 escrowIdHash)', + 'function verifier() view returns (address)', + 'function provider() view returns (address)', + 'function counterparty() view returns (address)', + 'function escrowIdHash() view returns (bytes32)', + 'function settled() view returns (bool)', + 'function outcome() view returns (uint8)', + 'function lastVerdictDigest() view returns (bytes32)', + 'function lastPacketHash() view returns (bytes32)', + 'function releaseAuthorized() view returns (bool)', + `function settleWithDJDVerdict(${EVALUATOR_VERDICT_TUPLE_SIGNATURE} verdict,bytes signature) returns (uint8)`, + 'event VerdictSettled(bytes32 indexed verdictDigest,bytes32 indexed packetHash,uint8 outcome,bool approved,address provider,address counterparty)', +]) + +export function encodeEvaluatorEscrowSettlement(params: { + verdict: EvaluatorVerdictVerifierInput + signature: `0x${string}` +}): `0x${string}` { + return encodeFunctionData({ + abi: DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_ABI, + functionName: DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_FUNCTION, + args: [params.verdict, params.signature], + }) +} diff --git a/src/contracts/djdEvaluatorOracleCallback.ts b/src/contracts/djdEvaluatorOracleCallback.ts new file mode 100644 index 0000000..417115b --- /dev/null +++ b/src/contracts/djdEvaluatorOracleCallback.ts @@ -0,0 +1,67 @@ +import { encodeFunctionData, keccak256, parseAbi, stringToHex, toFunctionSelector } from 'viem' + +export const DJD_EVALUATOR_ORACLE_CALLBACK_ABI = parseAbi([ + 'function receiveVerdict(bytes32 escrowIdHash,address provider,address counterparty,uint8 decisionCode,uint8 recommendationCode,bool approved,uint16 confidence,uint16 agentScoreProvider,bool certificationValid,uint16 riskScore,bytes32 packetHash,bytes32 attestationDigest,bytes attestationSignature)', +]) + +export const DJD_EVALUATOR_ORACLE_CALLBACK_FUNCTION = 'receiveVerdict' +export const DJD_EVALUATOR_ORACLE_CALLBACK_INTERFACE = 'IDJDEvaluatorOracleCallback' +export const DJD_EVALUATOR_ORACLE_CALLBACK_SIGNATURE = + 'receiveVerdict(bytes32,address,address,uint8,uint8,bool,uint16,uint16,bool,uint16,bytes32,bytes32,bytes)' +export const DJD_EVALUATOR_ORACLE_CALLBACK_SELECTOR = toFunctionSelector(DJD_EVALUATOR_ORACLE_CALLBACK_SIGNATURE) +export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' as const +export const ZERO_BYTES32 = `0x${'0'.repeat(64)}` as const + +export const DJD_DECISION_CODES = { + approve: 0, + review: 1, + reject: 2, +} as const + +export const DJD_RECOMMENDATION_CODES = { + release: 0, + manual_review: 1, + dispute: 2, + reject: 3, +} as const + +export function buildEscrowIdHash(escrowId: string | null): `0x${string}` { + if (!escrowId) return ZERO_BYTES32 + return keccak256(stringToHex(escrowId)) +} + +export function encodeEvaluatorOracleCallback(params: { + provider: `0x${string}` + counterparty: `0x${string}` | null + decisionCode: number + recommendationCode: number + approved: boolean + confidence: number + agentScoreProvider: number + certificationValid: boolean + riskScore: number + packetHash: `0x${string}` + attestationDigest: `0x${string}` + attestationSignature: `0x${string}` + escrowId: string | null +}): `0x${string}` { + return encodeFunctionData({ + abi: DJD_EVALUATOR_ORACLE_CALLBACK_ABI, + functionName: DJD_EVALUATOR_ORACLE_CALLBACK_FUNCTION, + args: [ + buildEscrowIdHash(params.escrowId), + params.provider, + params.counterparty ?? ZERO_ADDRESS, + params.decisionCode, + params.recommendationCode, + params.approved, + params.confidence, + params.agentScoreProvider, + params.certificationValid, + params.riskScore, + params.packetHash, + params.attestationDigest, + params.attestationSignature, + ], + }) +} diff --git a/src/contracts/djdEvaluatorVerdictVerifier.ts b/src/contracts/djdEvaluatorVerdictVerifier.ts new file mode 100644 index 0000000..f278032 --- /dev/null +++ b/src/contracts/djdEvaluatorVerdictVerifier.ts @@ -0,0 +1,68 @@ +import { encodeFunctionData, parseAbi, toFunctionSelector } from 'viem' + +export const DJD_EVALUATOR_VERDICT_PRIMARY_TYPE = 'EvaluatorVerdict' +export const DJD_EVALUATOR_VERDICT_VERIFIER_CONTRACT = 'DJDEvaluatorVerdictVerifier' +export const DJD_EVALUATOR_VERDICT_VERIFIER_FUNCTION = 'verifyVerdict' +export const DJD_EVALUATOR_VERDICT_VERIFIER_ABI = parseAbi([ + 'constructor(address initialSigner)', + 'function owner() view returns (address)', + 'function oracleSigner() view returns (address)', + 'function domainSeparator() view returns (bytes32)', + 'function setOracleSigner(address newSigner)', + 'function transferOwnership(address newOwner)', + 'function hashVerdict((string verdictId,address wallet,address counterpartyWallet,string escrowId,string decision,string recommendation,bool approved,uint16 confidence,uint16 agentScoreProvider,string scoreModelVersion,bool certificationValid,string certificationTier,string riskLevel,uint16 riskScore,string forensicTraceId,bytes32 packetHash,string generatedAt) verdict) view returns (bytes32)', + 'function verifyVerdict((string verdictId,address wallet,address counterpartyWallet,string escrowId,string decision,string recommendation,bool approved,uint16 confidence,uint16 agentScoreProvider,string scoreModelVersion,bool certificationValid,string certificationTier,string riskLevel,uint16 riskScore,string forensicTraceId,bytes32 packetHash,string generatedAt) verdict,bytes signature) view returns (bool)', + 'function verifyDigest(bytes32 digest,bytes signature) view returns (bool)', + 'event OracleSignerUpdated(address indexed previousSigner,address indexed newSigner)', + 'event OwnershipTransferred(address indexed previousOwner,address indexed newOwner)', +]) + +export const EVALUATOR_VERDICT_TUPLE_SIGNATURE = + '(string verdictId,address wallet,address counterpartyWallet,string escrowId,string decision,string recommendation,bool approved,uint16 confidence,uint16 agentScoreProvider,string scoreModelVersion,bool certificationValid,string certificationTier,string riskLevel,uint16 riskScore,string forensicTraceId,bytes32 packetHash,string generatedAt)' + +export const DJD_EVALUATOR_VERDICT_VERIFIER_SIGNATURES = { + hashVerdict: `hashVerdict(${EVALUATOR_VERDICT_TUPLE_SIGNATURE})`, + verifyVerdict: `verifyVerdict(${EVALUATOR_VERDICT_TUPLE_SIGNATURE},bytes)`, + verifyDigest: 'verifyDigest(bytes32,bytes)', + setOracleSigner: 'setOracleSigner(address)', + transferOwnership: 'transferOwnership(address)', +} as const + +export const DJD_EVALUATOR_VERDICT_VERIFIER_SELECTORS = { + hashVerdict: toFunctionSelector(DJD_EVALUATOR_VERDICT_VERIFIER_SIGNATURES.hashVerdict), + verifyVerdict: toFunctionSelector(DJD_EVALUATOR_VERDICT_VERIFIER_SIGNATURES.verifyVerdict), + verifyDigest: toFunctionSelector(DJD_EVALUATOR_VERDICT_VERIFIER_SIGNATURES.verifyDigest), + setOracleSigner: toFunctionSelector(DJD_EVALUATOR_VERDICT_VERIFIER_SIGNATURES.setOracleSigner), + transferOwnership: toFunctionSelector(DJD_EVALUATOR_VERDICT_VERIFIER_SIGNATURES.transferOwnership), +} as const + +export interface EvaluatorVerdictVerifierInput { + verdictId: string + wallet: `0x${string}` + counterpartyWallet: `0x${string}` + escrowId: string + decision: string + recommendation: string + approved: boolean + confidence: number + agentScoreProvider: number + scoreModelVersion: string + certificationValid: boolean + certificationTier: string + riskLevel: string + riskScore: number + forensicTraceId: string + packetHash: `0x${string}` + generatedAt: string +} + +export function encodeEvaluatorVerdictVerification(params: { + verdict: EvaluatorVerdictVerifierInput + signature: `0x${string}` +}): `0x${string}` { + return encodeFunctionData({ + abi: DJD_EVALUATOR_VERDICT_VERIFIER_ABI, + functionName: DJD_EVALUATOR_VERDICT_VERIFIER_FUNCTION, + args: [params.verdict, params.signature], + }) +} diff --git a/src/db/analyticsQueries.ts b/src/db/analyticsQueries.ts index e0981a3..bbac1c4 100644 --- a/src/db/analyticsQueries.ts +++ b/src/db/analyticsQueries.ts @@ -437,22 +437,26 @@ export interface ReputationPublication { wallet: string composite_score: number model_version: string + endpoint: string | null + feedback_hash: string | null tx_hash: string | null published_at: string } const stmtGetPublication = db.prepare<[string], ReputationPublication>(` - SELECT wallet, composite_score, model_version, tx_hash, published_at + SELECT wallet, composite_score, model_version, endpoint, feedback_hash, tx_hash, published_at FROM reputation_publications WHERE wallet = ? `) const stmtUpsertPublication = db.prepare(` - INSERT INTO reputation_publications (wallet, composite_score, model_version, tx_hash, published_at) - VALUES (@wallet, @composite_score, @model_version, @tx_hash, @published_at) + INSERT INTO reputation_publications (wallet, composite_score, model_version, endpoint, feedback_hash, tx_hash, published_at) + VALUES (@wallet, @composite_score, @model_version, @endpoint, @feedback_hash, @tx_hash, @published_at) ON CONFLICT(wallet) DO UPDATE SET composite_score = excluded.composite_score, model_version = excluded.model_version, + endpoint = excluded.endpoint, + feedback_hash = excluded.feedback_hash, tx_hash = excluded.tx_hash, published_at = excluded.published_at `) @@ -461,12 +465,16 @@ export function upsertPublication(pub: { wallet: string composite_score: number model_version: string + endpoint: string + feedback_hash: string tx_hash: string | null }): void { stmtUpsertPublication.run({ wallet: pub.wallet, composite_score: pub.composite_score, model_version: pub.model_version, + endpoint: pub.endpoint, + feedback_hash: pub.feedback_hash, tx_hash: pub.tx_hash, published_at: new Date().toISOString(), }) diff --git a/src/db/certificationQueries.ts b/src/db/certificationQueries.ts index 74dd064..4d4f9f7 100644 --- a/src/db/certificationQueries.ts +++ b/src/db/certificationQueries.ts @@ -7,8 +7,8 @@ const stmtGetActiveCertification = db.prepare<[string], CertificationRow>(` `) const stmtInsertCertification = db.prepare(` - INSERT INTO certifications (wallet, tier, score_at_certification, expires_at) - VALUES (?, ?, ?, datetime('now', '+1 year')) + INSERT INTO certifications (wallet, tier, score_at_certification, price_paid_usdc, expires_at) + VALUES (?, ?, ?, ?, datetime('now', '+1 year')) `) const stmtGetCertificationById = db.prepare<[number], CertificationRow>(` @@ -25,6 +25,7 @@ const stmtListActiveCertificationDirectory = db.prepare<[string | null, string | c.wallet, c.tier, c.score_at_certification, + c.price_paid_usdc, c.granted_at, c.expires_at, c.is_active, @@ -162,8 +163,8 @@ const stmtCertificationRevenueByMonth = db.prepare<[], CertificationRevenueByMon strftime('%Y-%m', granted_at) as month, COUNT(*) as count, SUM(CASE WHEN revoked_at IS NOT NULL THEN 1 ELSE 0 END) as revoked_count, - SUM(99) as gross_revenue_usd, - SUM(CASE WHEN revoked_at IS NULL THEN 99 ELSE 0 END) as net_revenue_usd + SUM(price_paid_usdc) as gross_revenue_usd, + SUM(CASE WHEN revoked_at IS NULL THEN price_paid_usdc ELSE 0 END) as net_revenue_usd FROM certifications GROUP BY strftime('%Y-%m', granted_at) ORDER BY month DESC @@ -174,6 +175,7 @@ export interface CertificationRow { wallet: string tier: string score_at_certification: number + price_paid_usdc: number granted_at: string expires_at: string is_active: number @@ -240,8 +242,13 @@ export function getActiveCertification(wallet: string): CertificationRow | undef return stmtGetActiveCertification.get(wallet) } -export function insertCertification(wallet: string, tier: string, scoreAtCertification: number): CertificationRow { - const result = stmtInsertCertification.run(wallet, tier, scoreAtCertification) +export function insertCertification( + wallet: string, + tier: string, + scoreAtCertification: number, + pricePaidUsd = 99, +): CertificationRow { + const result = stmtInsertCertification.run(wallet, tier, scoreAtCertification, pricePaidUsd) return stmtGetCertificationById.get(Number(result.lastInsertRowid))! } @@ -262,14 +269,16 @@ export function getCertificationRevenueSummary(): CertificationRevenueSummary { const active = stmtCountActiveCertifications.get()!.count const revoked = stmtCountRevokedCertifications.get()!.count const byMonth = stmtCertificationRevenueByMonth.all() + const grossRevenue = byMonth.reduce((sum, row) => sum + row.gross_revenue_usd, 0) + const netRevenue = byMonth.reduce((sum, row) => sum + row.net_revenue_usd, 0) return { total_certifications: total, active_certifications: active, revoked_certifications: revoked, - gross_revenue_usd: total * 99, - net_revenue_usd: (total - revoked) * 99, - price_per_cert_usd: 99, + gross_revenue_usd: grossRevenue, + net_revenue_usd: netRevenue, + price_per_cert_usd: total > 0 ? Math.round((grossRevenue / total) * 100) / 100 : 0, by_month: byMonth, } } diff --git a/src/db/evaluatorQueries.ts b/src/db/evaluatorQueries.ts new file mode 100644 index 0000000..972ed3f --- /dev/null +++ b/src/db/evaluatorQueries.ts @@ -0,0 +1,126 @@ +import { db } from './connection.js' + +export interface EvaluatorVerdictRow { + id: string + wallet: string + counterparty_wallet: string | null + escrow_id: string | null + baseline_profile: string + certification_floor: string + current_score: number + current_tier: string + score_confidence: number + risk_score: number + risk_level: string + certification_active: number + certification_tier: string | null + decision: string + recommendation: string + approved: number + confidence: number + packet_hash: string + forensic_trace_id: string + attestation_scheme: string + attestation_status: string + attestation_digest: string + attestation_signature: string | null + attestation_signer: string | null + attestation_reason: string | null + attested_at: string | null + payload_json: string + created_at: string +} + +const stmtInsertEvaluatorVerdict = db.prepare(` + INSERT INTO evaluator_verdicts ( + id, + wallet, + counterparty_wallet, + escrow_id, + baseline_profile, + certification_floor, + current_score, + current_tier, + score_confidence, + risk_score, + risk_level, + certification_active, + certification_tier, + decision, + recommendation, + approved, + confidence, + packet_hash, + forensic_trace_id, + attestation_scheme, + attestation_status, + attestation_digest, + attestation_signature, + attestation_signer, + attestation_reason, + attested_at, + payload_json, + created_at + ) VALUES ( + @id, + @wallet, + @counterparty_wallet, + @escrow_id, + @baseline_profile, + @certification_floor, + @current_score, + @current_tier, + @score_confidence, + @risk_score, + @risk_level, + @certification_active, + @certification_tier, + @decision, + @recommendation, + @approved, + @confidence, + @packet_hash, + @forensic_trace_id, + @attestation_scheme, + @attestation_status, + @attestation_digest, + @attestation_signature, + @attestation_signer, + @attestation_reason, + @attested_at, + @payload_json, + @created_at + ) +`) + +const stmtGetEvaluatorVerdict = db.prepare<[string], EvaluatorVerdictRow>(` + SELECT * + FROM evaluator_verdicts + WHERE id = ? + LIMIT 1 +`) + +const stmtListEvaluatorVerdictsByWallet = db.prepare<[string, number], EvaluatorVerdictRow>(` + SELECT * + FROM evaluator_verdicts + WHERE wallet = ? + ORDER BY created_at DESC, id DESC + LIMIT ? +`) + +export function insertEvaluatorVerdict( + verdict: Omit & { created_at?: string }, +): void { + stmtInsertEvaluatorVerdict.run({ + ...verdict, + created_at: verdict.created_at ?? new Date().toISOString(), + }) +} + +export function getEvaluatorVerdict(id: string): EvaluatorVerdictRow | undefined { + return stmtGetEvaluatorVerdict.get(id) +} + +export function listEvaluatorVerdictsByWallet(wallet: string, limit: number): EvaluatorVerdictRow[] { + return stmtListEvaluatorVerdictsByWallet.all(wallet, limit) +} diff --git a/src/db/queries.ts b/src/db/queries.ts index fe3a9f5..41f78d8 100644 --- a/src/db/queries.ts +++ b/src/db/queries.ts @@ -10,6 +10,7 @@ export * from './certificationQueries.js' export * from './clusterQueries.js' export * from './dataQueries.js' export * from './directoryQueries.js' +export * from './evaluatorQueries.js' export * from './evidenceQueries.js' export * from './forensicsQueries.js' export * from './growthQueries.js' diff --git a/src/db/schema.ts b/src/db/schema.ts index 14ba350..82faf42 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -613,6 +613,7 @@ db.exec(` wallet TEXT NOT NULL, tier TEXT NOT NULL, score_at_certification INTEGER NOT NULL, + price_paid_usdc REAL NOT NULL DEFAULT 200, granted_at TEXT NOT NULL DEFAULT (datetime('now')), expires_at TEXT NOT NULL, is_active INTEGER NOT NULL DEFAULT 1, @@ -626,6 +627,7 @@ addColumnIfMissing('certifications', 'is_active', 'INTEGER NOT NULL DEFAULT 1') addColumnIfMissing('certifications', 'tx_hash', 'TEXT') addColumnIfMissing('certifications', 'revoked_at', 'TEXT') addColumnIfMissing('certifications', 'revocation_reason', 'TEXT') +addColumnIfMissing('certifications', 'price_paid_usdc', 'REAL NOT NULL DEFAULT 99') db.exec(` CREATE INDEX IF NOT EXISTS idx_certs_wallet ON certifications(wallet); @@ -654,7 +656,7 @@ db.exec(` `) addColumnIfMissing('certification_review_requests', 'requested_by_wallet', 'TEXT NOT NULL DEFAULT ""') -addColumnIfMissing('certification_review_requests', 'requested_tier', 'TEXT NOT NULL DEFAULT "Trusted"') +addColumnIfMissing('certification_review_requests', 'requested_tier', 'TEXT NOT NULL DEFAULT "Transactional"') addColumnIfMissing('certification_review_requests', 'requested_score', 'INTEGER NOT NULL DEFAULT 0') addColumnIfMissing('certification_review_requests', 'requested_confidence', 'REAL') addColumnIfMissing('certification_review_requests', 'score_expires_at', 'TEXT') @@ -677,10 +679,60 @@ db.exec(` wallet TEXT PRIMARY KEY, composite_score INTEGER NOT NULL, model_version TEXT NOT NULL, + endpoint TEXT, + feedback_hash TEXT, tx_hash TEXT, published_at TEXT NOT NULL DEFAULT (datetime('now')) ); `) +addColumnIfMissing('reputation_publications', 'endpoint', 'TEXT') +addColumnIfMissing('reputation_publications', 'feedback_hash', 'TEXT') + +// ── Evaluator Verdict Records ─────────────────────────────────────────────── +db.exec(` + CREATE TABLE IF NOT EXISTS evaluator_verdicts ( + id TEXT PRIMARY KEY, + wallet TEXT NOT NULL, + counterparty_wallet TEXT, + escrow_id TEXT, + baseline_profile TEXT NOT NULL, + certification_floor TEXT NOT NULL, + current_score INTEGER NOT NULL, + current_tier TEXT NOT NULL, + score_confidence REAL NOT NULL, + risk_score INTEGER NOT NULL, + risk_level TEXT NOT NULL, + certification_active INTEGER NOT NULL DEFAULT 0, + certification_tier TEXT, + decision TEXT NOT NULL, + recommendation TEXT NOT NULL, + approved INTEGER NOT NULL DEFAULT 0, + confidence REAL NOT NULL, + packet_hash TEXT NOT NULL, + forensic_trace_id TEXT NOT NULL, + attestation_scheme TEXT NOT NULL DEFAULT 'eip712', + attestation_status TEXT NOT NULL DEFAULT 'unsigned', + attestation_digest TEXT NOT NULL DEFAULT '', + attestation_signature TEXT, + attestation_signer TEXT, + attestation_reason TEXT, + attested_at TEXT, + payload_json TEXT NOT NULL, + created_at TEXT NOT NULL DEFAULT (datetime('now')) + ); +`) +addColumnIfMissing('evaluator_verdicts', 'attestation_scheme', 'TEXT NOT NULL DEFAULT "eip712"') +addColumnIfMissing('evaluator_verdicts', 'attestation_status', 'TEXT NOT NULL DEFAULT "unsigned"') +addColumnIfMissing('evaluator_verdicts', 'attestation_digest', 'TEXT NOT NULL DEFAULT ""') +addColumnIfMissing('evaluator_verdicts', 'attestation_signature', 'TEXT') +addColumnIfMissing('evaluator_verdicts', 'attestation_signer', 'TEXT') +addColumnIfMissing('evaluator_verdicts', 'attestation_reason', 'TEXT') +addColumnIfMissing('evaluator_verdicts', 'attested_at', 'TEXT') +db.exec(` + CREATE INDEX IF NOT EXISTS idx_evaluator_verdicts_wallet ON evaluator_verdicts(wallet, created_at DESC); + CREATE INDEX IF NOT EXISTS idx_evaluator_verdicts_escrow ON evaluator_verdicts(escrow_id, created_at DESC); + CREATE INDEX IF NOT EXISTS idx_evaluator_verdicts_decision ON evaluator_verdicts(decision, created_at DESC); +`) // ── Stripe Billing ────────────────────────────────────────────────────────── db.exec(` diff --git a/src/errors.ts b/src/errors.ts index 3b8b446..a058fb8 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -97,6 +97,9 @@ export const ErrorCodes = { HISTORY_NOT_FOUND: 'history_not_found', FORENSICS_NOT_FOUND: 'forensics_not_found', INVALID_DATE_RANGE: 'invalid_date_range', + INVALID_NETWORK: 'invalid_network', + INVALID_EVALUATOR_VERDICT_ID: 'invalid_evaluator_verdict_id', + EVALUATOR_VERDICT_NOT_FOUND: 'evaluator_verdict_not_found', // Analytics INVALID_PERIOD: 'invalid_period', diff --git a/src/jobs/outcomeMatcher.ts b/src/jobs/outcomeMatcher.ts index 9eef54d..2654aa8 100644 --- a/src/jobs/outcomeMatcher.ts +++ b/src/jobs/outcomeMatcher.ts @@ -6,8 +6,8 @@ * for model validation. */ import type { Database as DatabaseType } from 'better-sqlite3' +import { MODEL_VERSION } from '../config/modelVersion.js' import { log } from '../logger.js' -import { MODEL_VERSION } from '../scoring/responseBuilders.js' import { jobStats } from './jobStats.js' interface UnmatchedLookup { diff --git a/src/jobs/reputationPublisher.ts b/src/jobs/reputationPublisher.ts index bf8bfe0..e7f2131 100644 --- a/src/jobs/reputationPublisher.ts +++ b/src/jobs/reputationPublisher.ts @@ -20,6 +20,7 @@ import { privateKeyToAccount } from 'viem/accounts' import { base } from 'viem/chains' import { ERC8004_REPUTATION_REGISTRY, getPublicClient } from '../blockchain.js' import { REPUTATION_PUBLISHER_CONFIG } from '../config/constants.js' +import { buildPublicUrl } from '../config/public.js' import { getScoresNeedingPublication, upsertPublication } from '../db.js' import { log } from '../logger.js' import { withRetry } from '../utils/retry.js' @@ -28,7 +29,7 @@ import { withRetry } from '../utils/retry.js' const TAG = 'erc8004-publisher' -const { MIN_CONFIDENCE, SCORE_DELTA, BATCH_LIMIT, TX_TIMEOUT_MS, INTER_TX_DELAY_MS, MIN_ETH_BALANCE, SCORE_ENDPOINT } = +const { MIN_CONFIDENCE, SCORE_DELTA, BATCH_LIMIT, TX_TIMEOUT_MS, INTER_TX_DELAY_MS, MIN_ETH_BALANCE } = REPUTATION_PUBLISHER_CONFIG const GIVE_FEEDBACK_ABI = parseAbi([ @@ -119,6 +120,7 @@ export async function runReputationPublisher(): Promise { const modelVersion = score.model_version ?? 'v1' // Build the off-chain payload that feedbackHash commits to + const endpoint = buildPublicUrl(`/v1/score/erc8004?wallet=${score.wallet}`) const offChainPayload = { wallet: score.wallet, composite_score: score.composite_score, @@ -126,6 +128,7 @@ export async function runReputationPublisher(): Promise { confidence: score.confidence, tier: score.tier, calculated_at: score.calculated_at, + endpoint, } const feedbackHash = buildFeedbackHash(offChainPayload) @@ -144,7 +147,7 @@ export async function runReputationPublisher(): Promise { 0, // valueDecimals — score is an integer 0-100 'djd-composite', modelVersion, - SCORE_ENDPOINT, + endpoint, '', // feedbackURI — all data available at the endpoint feedbackHash, ], @@ -165,6 +168,8 @@ export async function runReputationPublisher(): Promise { wallet: score.wallet, composite_score: score.composite_score, model_version: modelVersion, + endpoint, + feedback_hash: feedbackHash, tx_hash: txHash, }) published++ diff --git a/src/middleware/queryLogger.ts b/src/middleware/queryLogger.ts index 241e7b6..2c02772 100644 --- a/src/middleware/queryLogger.ts +++ b/src/middleware/queryLogger.ts @@ -17,6 +17,15 @@ const FREE_ENDPOINTS = new Set([ '/v1/analytics/event', '/v1/leaderboard', '/v1/score/basic', + '/v1/score/evaluator/verifier', + '/v1/score/evaluator/networks', + '/v1/score/evaluator/deployments', + '/v1/score/evaluator/promotion', + '/v1/score/evaluator/artifacts', + '/v1/score/evaluator/proof', + '/v1/score/evaluator/escrow', + '/v1/score/evaluator/deploy', + '/v1/score/evaluator/deploy/bundle', '/v1/badge', '/v1/agent/register', '/v1/data/economy', @@ -49,6 +58,7 @@ const ONCHAIN_FEE_ENDPOINTS = new Set([ function tierFromEndpoint(endpoint: string): string { if (endpoint.includes('/score/basic')) return 'basic' if (endpoint.includes('/score/full')) return 'full' + if (endpoint.includes('/score/evaluator')) return 'evaluator' if (endpoint.includes('/score/risk')) return 'risk' if (endpoint.includes('/score/refresh')) return 'refresh' if (endpoint.includes('/score/batch')) return 'batch' diff --git a/src/middleware/responseHeaders.ts b/src/middleware/responseHeaders.ts index 0d12c7d..c114f29 100644 --- a/src/middleware/responseHeaders.ts +++ b/src/middleware/responseHeaders.ts @@ -4,9 +4,9 @@ */ import type { MiddlewareHandler } from 'hono' +import { MODEL_VERSION } from '../config/modelVersion.js' import { getPublicOrigin } from '../config/public.js' import { getReleaseMetadata, getRuntimeMode } from '../config/runtimeMetadata.js' -import { MODEL_VERSION } from '../scoring/responseBuilders.js' export { MODEL_VERSION } const PUBLIC_SITE_ORIGIN = getPublicOrigin() diff --git a/src/routes/blog.ts b/src/routes/blog.ts index c9583d6..19df76a 100644 --- a/src/routes/blog.ts +++ b/src/routes/blog.ts @@ -13,6 +13,7 @@ const blogHead = (title: string, description: string, slug = '') => description, path: `/blog${slug ? `/${slug}` : ''}`, ogType: slug ? 'article' : 'website', + canonicalUrl: buildPublicUrl(`/blog${slug ? `/${slug}` : ''}`), extraHead: ``, })} .blog-shell{max-width:1180px;margin:0 auto;padding:0 28px 0} @@ -36,10 +37,10 @@ const blogHead = (title: string, description: string, slug = '') => .post-card{grid-template-columns:1fr;padding:24px} }` -const blogNav = renderPublicNav('blog', '/pricing', 'Get Started') +const blogNav = renderPublicNav('blog', '/#lookup', 'Screen wallets') const blogFooter = renderPublicFooter({ - copy: 'Research, release notes, and field reports from DJD Agent Score as we build trust infrastructure for apps, marketplaces, and agent networks.', + copy: 'Research, release notes, and field reports from DJD Agent Score as we build wallet screening for payouts, paid routes, and agent networks.', }) // ─── Shared article CSS (used by all blog posts) ─── @@ -87,7 +88,7 @@ const blogPostHead = (title: string, description: string, slug: string, extraCss const listingHtml = `${blogHead( 'Blog', - 'Insights on wallet scoring, sybil detection, standards, and trust infrastructure for the autonomous agent economy.', + 'Insights on wallet screening, sybil detection, standards, and payout-safe agent workflows.', )} .blog-hero{max-width:1080px;margin:0 auto;padding:120px 32px 60px;text-align:center} .blog-hero .chip{display:inline-flex;align-items:center;gap:8px;font-family:'JetBrains Mono',monospace;font-size:11px;font-weight:500;color:var(--accent);background:var(--accent-dim);border:1px solid var(--border-hi);padding:6px 16px;border-radius:100px;letter-spacing:.5px;text-transform:uppercase;margin-bottom:24px} @@ -114,8 +115,8 @@ ${blogNav}
Insights
-

Building trust infrastructure
in the open

-

Research, analysis, and updates from the DJD Agent Score team on wallet scoring, sybil detection, standards, and the agent economy.

+

Wallet screening research
and release notes

+

Research, analysis, and updates from the DJD Agent Score team on screening wallets before payouts, paid routes, and autonomous settlement.

@@ -794,7 +795,7 @@ const rssXml = ` DJD Agent Score Blog ${BLOG_URL} - Insights on wallet scoring, sybil detection, standards, and trust infrastructure for the autonomous agent economy. + Insights on wallet screening, sybil detection, standards, and payout-safe agent workflows. en-us ${blogPosts[0].date} diff --git a/src/routes/certification.ts b/src/routes/certification.ts index 82ad7f1..bf17d90 100644 --- a/src/routes/certification.ts +++ b/src/routes/certification.ts @@ -2,6 +2,7 @@ * Certified Agent Badge — Phase B monetization * * Public (free): + * GET /tiers — inspect the certification tier catalog * GET /readiness — check whether a wallet can apply for certification * GET /review — inspect the latest certification review request for a wallet * POST /review — submit a certification review request @@ -9,15 +10,18 @@ * GET /:wallet — check certification status * GET /badge/:wallet — SVG badge (green if certified, gray if not) * - * Paid ($99 USDC via x402): - * POST /apply — apply for certification + * Paid (tier-priced x402): + * POST /apply — legacy transactional apply path + * POST /apply/operational — Tier 1 / Operational + * POST /apply/transactional — Tier 2 / Transactional + * POST /apply/autonomous — Tier 3 / Autonomous * * Admin (X-ADMIN-KEY): * GET /admin/all — list all certifications * POST /admin/:id/revoke — revoke a certification * GET /admin/revenue — revenue summary */ -import { Hono } from 'hono' +import { Hono, type Context } from 'hono' import type { AppEnv } from '../types/hono-env.js' import { errorResponse } from '../errors.js' import { adminAuth } from '../middleware/adminAuth.js' @@ -26,6 +30,7 @@ import { getCertificationRevenue, getCertificationBadgeView, getCertificationDirectoryView, + getCertificationTierCatalogView, issueCertificationFromReviewRequest, getCertificationReadinessView, getCertificationReviewStatusView, @@ -44,8 +49,17 @@ const certification = new Hono() // ── Public: Certified directory ───────────────────────────────────────────── +certification.get('/tiers', (c) => { + const outcome = getCertificationTierCatalogView() + if (!outcome.ok) { + return c.json(errorResponse(outcome.code, outcome.message, outcome.details), outcome.status) + } + + return c.json(outcome.data) +}) + certification.get('/readiness', (c) => { - const outcome = getCertificationReadinessView(c.req.query('wallet')) + const outcome = getCertificationReadinessView(c.req.query('wallet'), c.req.query('tier')) if (!outcome.ok) { return c.json(errorResponse(outcome.code, outcome.message, outcome.details), outcome.status) } @@ -77,10 +91,11 @@ certification.get('/review', (c) => { }) certification.post('/review', async (c) => { - const body = await c.req.json<{ wallet?: string; note?: string }>().catch(() => null) + const body = await c.req.json<{ wallet?: string; note?: string; tier?: string }>().catch(() => null) const outcome = submitCertificationReviewRequest({ wallet: body?.wallet, note: body?.note, + tier: body?.tier, }) if (!outcome.ok) { return c.json(errorResponse(outcome.code, outcome.message, outcome.details), outcome.status) @@ -89,17 +104,6 @@ certification.post('/review', async (c) => { return c.json(outcome.data, outcome.status ?? 201) }) -// ── Public: Check certification status ────────────────────────────────────── - -certification.get('/:wallet', (c) => { - const outcome = getCertificationStatusView(c.req.param('wallet')) - if (!outcome.ok) { - return c.json(errorResponse(outcome.code, outcome.message, outcome.details), outcome.status) - } - - return c.json(outcome.data) -}) - // ── Public: SVG badge ─────────────────────────────────────────────────────── certification.get('/badge/:wallet', (c) => { @@ -114,29 +118,70 @@ certification.get('/badge/:wallet', (c) => { return c.body(outcome.data.svg) }) -// ── Paid: Apply for certification ($99 USDC) ─────────────────────────────── -// Certification requires x402 payment of $99 USDC. API key auth alone is NOT +// ── Paid: Apply for certification ─────────────────────────────────────────── +// Certification requires tier-specific x402 payment. API key auth alone is NOT // sufficient — the key bypasses per-request x402 fees but certification is a // one-time purchase that must be paid via x402. -certification.post('/apply', (c) => { +function ensureCertificationPayment(c: Context) { const paymentHeader = c.req.header('X-PAYMENT') ?? c.req.header('x-payment') if (c.get('apiKeyId') && !paymentHeader) { return c.json( errorResponse( 'payment_required', - 'Certification requires $99 USDC payment via x402. API key authentication alone is not sufficient for this endpoint.', + 'Certification requires tier-priced x402 payment. API key authentication alone is not sufficient for this endpoint.', ), 402, ) } - const outcome = applyForCertificationByPayer(getPayerWallet(c)) + return null +} + +function handleCertificationApply(c: Context, tier?: string) { + const paymentError = ensureCertificationPayment(c) + if (paymentError) { + return paymentError + } + + const outcome = applyForCertificationByPayer(getPayerWallet(c), tier) if (!outcome.ok) { return c.json(errorResponse(outcome.code, outcome.message, outcome.details), outcome.status) } return c.json(outcome.data, outcome.status ?? 201) +} + +certification.post('/apply', (c) => { + return handleCertificationApply(c) +}) + +certification.post('/apply/operational', (c) => { + return handleCertificationApply(c, 'operational') +}) + +certification.post('/apply/transactional', (c) => { + return handleCertificationApply(c, 'transactional') +}) + +certification.post('/apply/autonomous', (c) => { + return handleCertificationApply(c, 'autonomous') +}) + +// Keep a flexible path for internal callers and future route expansion. +certification.post('/apply/:tier', (c) => { + return handleCertificationApply(c, c.req.param('tier')) +}) + +// ── Public: Check certification status ────────────────────────────────────── + +certification.get('/:wallet', (c) => { + const outcome = getCertificationStatusView(c.req.param('wallet')) + if (!outcome.ok) { + return c.json(errorResponse(outcome.code, outcome.message, outcome.details), outcome.status) + } + + return c.json(outcome.data) }) // ── Admin: List all certifications ────────────────────────────────────────── diff --git a/src/routes/legal.ts b/src/routes/legal.ts index a2df6ab..eff0448 100644 --- a/src/routes/legal.ts +++ b/src/routes/legal.ts @@ -10,6 +10,28 @@ const indexHtmlTemplate = readFileSync(join(__dirname, '../../index.html'), 'utf const legal = new Hono() +const publicSitemapPaths = [ + '/', + '/leaderboard', + '/terms', + '/privacy', + '/certify', + '/directory', + '/docs', + '/pricing', + '/blog', + '/methodology', +] as const + +function escapeXml(value: string): string { + return value + .replaceAll('&', '&') + .replaceAll('<', '<') + .replaceAll('>', '>') + .replaceAll('"', '"') + .replaceAll("'", ''') +} + function renderLandingPageHtml(): string { return indexHtmlTemplate .replaceAll('__DJD_PUBLIC_BASE_URL__', buildPublicUrl()) @@ -47,7 +69,22 @@ legal.get('/robots.txt', (c) => { 'Disallow: /portal\n' + 'Disallow: /reviewer\n' + '\n' + - `Sitemap: ${buildPublicUrl('/openapi.json')}\n`, + `Sitemap: ${buildPublicUrl('/sitemap.xml')}\n`, + ) +}) + +// ── sitemap.xml ───────────────────────────────────────────────────── +legal.get('/sitemap.xml', (c) => { + c.header('Content-Type', 'application/xml; charset=UTF-8') + c.header('Cache-Control', 'public, max-age=86400') + + const urls = publicSitemapPaths.map((path) => ` ${escapeXml(buildPublicUrl(path))}`).join('\n') + + return c.body( + `\n` + + `\n` + + `${urls}\n` + + `\n`, ) }) diff --git a/src/routes/score.ts b/src/routes/score.ts index e210603..4b51d0a 100644 --- a/src/routes/score.ts +++ b/src/routes/score.ts @@ -1,6 +1,27 @@ import { Hono, type Context } from 'hono' import { errorResponse, ErrorCodes } from '../errors.js' -import { getEvaluatorPreview } from '../services/evaluatorService.js' +import { getEvaluatorArtifactPackageView } from '../services/contractArtifactService.js' +import { + getEvaluatorDeploymentPromotionBundleView, + getEvaluatorDeploymentRegistryView, +} from '../services/evaluatorDeploymentRegistryService.js' +import { + getEvaluatorNetworkCatalogView, + getEvaluatorDeploymentBundleView, + getEvaluatorDeploymentPlanView, + getEvaluatorEscrowSettlementView, + getEvaluatorVerifierPackageView, + getEvaluatorVerifierProofView, +} from '../services/evaluatorContractService.js' +import { resolveEvaluatorNetwork } from '../services/evaluatorNetworkService.js' +import { + getEvaluatorContractCallbackView, + getEvaluatorEvidencePacket, + getEvaluatorOracleVerdict, + getEvaluatorPreview, + getEvaluatorVerdictRecord, + listEvaluatorVerdictHistory, +} from '../services/evaluatorService.js' import { getRiskScore } from '../services/riskService.js' import { getBasicScore, @@ -34,6 +55,160 @@ score.get('/erc8004', async (c) => { return c.json(outcome.data) }) +// GET /v1/score/evaluator/verifier +score.get('/evaluator/verifier', (c) => { + const network = resolveEvaluatorNetwork(c.req.query('network')) + if (!network) { + return c.json(errorResponse(ErrorCodes.INVALID_NETWORK, 'Invalid or unsupported network'), 400) + } + + return c.json(getEvaluatorVerifierPackageView(network)) +}) + +// GET /v1/score/evaluator/networks +score.get('/evaluator/networks', (c) => c.json(getEvaluatorNetworkCatalogView())) + +// GET /v1/score/evaluator/deployments +score.get('/evaluator/deployments', (c) => { + const outcome = getEvaluatorDeploymentRegistryView(c.req.query('network')) + if (!outcome.ok) { + return c.json(errorResponse(outcome.code, outcome.message, outcome.details), outcome.status) + } + + return c.json(outcome.data) +}) + +// GET /v1/score/evaluator/promotion +score.get('/evaluator/promotion', (c) => { + const outcome = getEvaluatorDeploymentPromotionBundleView(c.req.query('network')) + if (!outcome.ok) { + return c.json(errorResponse(outcome.code, outcome.message, outcome.details), outcome.status) + } + + return c.json(outcome.data) +}) + +// GET /v1/score/evaluator/artifacts +score.get('/evaluator/artifacts', (c) => c.json(getEvaluatorArtifactPackageView())) + +// GET /v1/score/evaluator/proof?id=verdict_...&target_contract=0x... +score.get('/evaluator/proof', (c) => { + const outcome = getEvaluatorVerifierProofView({ + rawVerdictId: c.req.query('id'), + rawTargetContract: c.req.query('target_contract'), + rawNetwork: c.req.query('network'), + }) + if (!outcome.ok) { + return c.json(errorResponse(outcome.code, outcome.message, outcome.details), outcome.status) + } + + return c.json(outcome.data) +}) + +// GET /v1/score/evaluator/escrow?id=verdict_...&escrow_contract=0x... +score.get('/evaluator/escrow', (c) => { + const outcome = getEvaluatorEscrowSettlementView({ + rawVerdictId: c.req.query('id'), + rawEscrowContract: c.req.query('escrow_contract'), + rawNetwork: c.req.query('network'), + }) + if (!outcome.ok) { + return c.json(errorResponse(outcome.code, outcome.message, outcome.details), outcome.status) + } + + return c.json(outcome.data) +}) + +// GET /v1/score/evaluator/deploy?id=verdict_...&verifier_contract=0x... +score.get('/evaluator/deploy', (c) => { + const outcome = getEvaluatorDeploymentPlanView({ + rawVerdictId: c.req.query('id'), + rawVerifierContract: c.req.query('verifier_contract'), + rawNetwork: c.req.query('network'), + }) + if (!outcome.ok) { + return c.json(errorResponse(outcome.code, outcome.message, outcome.details), outcome.status) + } + + return c.json(outcome.data) +}) + +// GET /v1/score/evaluator/deploy/bundle?id=verdict_...&verifier_contract=0x... +score.get('/evaluator/deploy/bundle', (c) => { + const outcome = getEvaluatorDeploymentBundleView({ + rawVerdictId: c.req.query('id'), + rawVerifierContract: c.req.query('verifier_contract'), + rawNetwork: c.req.query('network'), + }) + if (!outcome.ok) { + return c.json(errorResponse(outcome.code, outcome.message, outcome.details), outcome.status) + } + + return c.json(outcome.data) +}) + +// GET /v1/score/evaluator/evidence?wallet=0x... +score.get('/evaluator/evidence', async (c) => { + const outcome = await getEvaluatorEvidencePacket(c.req.query('wallet')) + if (!outcome.ok) { + return c.json(errorResponse(outcome.code, outcome.message, outcome.details), outcome.status) + } + + return c.json(outcome.data) +}) + +// GET /v1/score/evaluator/oracle?wallet=0x...&counterparty_wallet=0x...&escrow_id=abc +score.get('/evaluator/oracle', async (c) => { + const outcome = await getEvaluatorOracleVerdict({ + rawWallet: c.req.query('wallet'), + rawCounterpartyWallet: c.req.query('counterparty_wallet'), + rawEscrowId: c.req.query('escrow_id'), + rawNetwork: c.req.query('network'), + }) + if (!outcome.ok) { + return c.json(errorResponse(outcome.code, outcome.message, outcome.details), outcome.status) + } + + return c.json(outcome.data) +}) + +// GET /v1/score/evaluator/verdict?id=verdict_... +score.get('/evaluator/verdict', (c) => { + const outcome = getEvaluatorVerdictRecord(c.req.query('id')) + if (!outcome.ok) { + return c.json(errorResponse(outcome.code, outcome.message, outcome.details), outcome.status) + } + + return c.json(outcome.data) +}) + +// GET /v1/score/evaluator/callback?id=verdict_...&target_contract=0x... +score.get('/evaluator/callback', (c) => { + const outcome = getEvaluatorContractCallbackView({ + rawVerdictId: c.req.query('id'), + rawTargetContract: c.req.query('target_contract'), + rawNetwork: c.req.query('network'), + }) + if (!outcome.ok) { + return c.json(errorResponse(outcome.code, outcome.message, outcome.details), outcome.status) + } + + return c.json(outcome.data) +}) + +// GET /v1/score/evaluator/verdicts?wallet=0x...&limit=20 +score.get('/evaluator/verdicts', (c) => { + const outcome = listEvaluatorVerdictHistory({ + rawWallet: c.req.query('wallet'), + rawLimit: c.req.query('limit'), + }) + if (!outcome.ok) { + return c.json(errorResponse(outcome.code, outcome.message, outcome.details), outcome.status) + } + + return c.json(outcome.data) +}) + // GET /v1/score/full?wallet=0x... score.get('/full', async (c) => { const outcome = await getFullScore(c.req.query('wallet')) diff --git a/src/scoring b/src/scoring index 30ae701..c51249d 160000 --- a/src/scoring +++ b/src/scoring @@ -1 +1 @@ -Subproject commit 30ae70101c54c65ee2f3e5fafcf6521ea123af20 +Subproject commit c51249da89e59ab04af795cc847eacf02437ebb5 diff --git a/src/services/adminService.ts b/src/services/adminService.ts index 88d1251..8e7d67e 100644 --- a/src/services/adminService.ts +++ b/src/services/adminService.ts @@ -1,3 +1,4 @@ +import { MODEL_VERSION } from '../config/modelVersion.js' import { adjustScoreByStakeBoost, countFraudDisputes, @@ -16,7 +17,6 @@ import { import { ErrorCodes } from '../errors.js' import { queueWebhookEvent } from '../jobs/webhookDelivery.js' import { generateCalibrationReport } from '../scoring/calibrationReport.js' -import { MODEL_VERSION } from '../scoring/responseBuilders.js' import type { DisputeResolution, FraudDisputeResolutionBody } from '../types.js' import { normalizeWallet } from '../utils/walletUtils.js' import { getAdminGrowthFunnelView } from './growthService.js' @@ -37,6 +37,7 @@ const RESETTABLE_TABLES = [ 'subscriptions', 'monitoring_subscriptions', 'certifications', + 'evaluator_verdicts', 'reputation_publications', 'webhook_deliveries', 'webhooks', diff --git a/src/services/certificationService.ts b/src/services/certificationService.ts index 9bf132b..aa444d6 100644 --- a/src/services/certificationService.ts +++ b/src/services/certificationService.ts @@ -24,6 +24,15 @@ import { } from '../db.js' import { makeBadge } from '../utils/badgeGenerator.js' import { normalizeWallet } from '../utils/walletUtils.js' +import { + type CertificationTierDefinition, + type CertificationTierKey, + getCertificationApplyPath, + getCertificationTier, + getCertificationTierByStoredValue, + getDefaultCertificationTier, + listCertificationTiers, +} from './certificationTiers.js' export interface CertificationApplyError { ok: false @@ -33,6 +42,7 @@ export interface CertificationApplyError { | 'cert_requirements_not_met' | 'cert_score_too_low' | 'cert_not_registered' + | 'cert_invalid_tier' | 'cert_already_active' | 'cert_not_found' | 'cert_review_not_found' @@ -50,10 +60,22 @@ interface CertificationSuccess { export type CertificationResult = CertificationApplyError | CertificationSuccess +export interface CertificationTierView { + key: CertificationTierKey + label: CertificationTierDefinition['label'] + level: CertificationTierDefinition['level'] + minimum_score: number + price_usdc: number + summary: string + controls: string[] + apply_endpoint: string +} + export interface CertificationStatusView { wallet: string tier: string score_at_certification: number + price_paid_usdc: number granted_at: string expires_at: string is_valid: true @@ -127,6 +149,13 @@ export interface CertificationDirectoryView { export interface CertificationReadinessView { wallet: string can_apply: boolean + requested_tier: CertificationTierView + eligible_tiers: Array< + CertificationTierView & { + eligible: boolean + score_gap: number + } + > status: | 'eligible' | 'already_certified' @@ -247,53 +276,100 @@ export interface CertificationIssuedFromReviewView { message: string } -const applyForCertificationTxn = db.transaction((wallet: string): CertificationApplyResult => { - const scoreRow = getScore(wallet) - if (!scoreRow || scoreRow.expires_at <= new Date().toISOString()) { - return { - ok: false, - code: 'cert_requirements_not_met', - message: 'Score has expired — request a fresh score first', - status: 400, - } - } - - if (scoreRow.composite_score < 75) { - return { - ok: false, - code: 'cert_score_too_low', - message: 'Composite score must be >= 75 for certification', - status: 400, - details: { current_score: scoreRow.composite_score }, - } +function buildCertificationTierView(tier: CertificationTierDefinition): CertificationTierView { + return { + key: tier.key, + label: tier.label, + level: tier.level, + minimum_score: tier.minimumScore, + price_usdc: tier.priceUsd, + summary: tier.summary, + controls: [...tier.controls], + apply_endpoint: buildPublicUrl(getCertificationApplyPath(tier.key)), } +} - const registration = getRegistration(wallet) - if (!registration) { +function resolveCertificationTier( + rawTier: string | null | undefined, +): CertificationResult { + if (rawTier == null || rawTier.trim().length === 0) { return { - ok: false, - code: 'cert_not_registered', - message: 'Agent must be registered before applying for certification', - status: 400, + ok: true, + data: getDefaultCertificationTier(), } } - const existingCert = getActiveCertification(wallet) - if (existingCert) { + const tier = getCertificationTier(rawTier) + if (tier) { return { - ok: false, - code: 'cert_already_active', - message: 'Wallet already has an active certification', - status: 409, + ok: true, + data: tier, } } return { - ok: true, - status: 201, - data: buildCertificationApplyView(insertCertification(wallet, scoreRow.tier, scoreRow.composite_score)), + ok: false, + code: 'cert_invalid_tier', + message: 'Certification tier must be operational, transactional, or autonomous', + status: 400, } -}) +} + +const applyForCertificationTxn = db.transaction( + (wallet: string, tier: CertificationTierDefinition): CertificationApplyResult => { + const scoreRow = getScore(wallet) + if (!scoreRow || scoreRow.expires_at <= new Date().toISOString()) { + return { + ok: false, + code: 'cert_requirements_not_met', + message: 'Score has expired — request a fresh score first', + status: 400, + } + } + + if (scoreRow.composite_score < tier.minimumScore) { + return { + ok: false, + code: 'cert_score_too_low', + message: `${tier.label} certification requires a score of at least ${tier.minimumScore}`, + status: 400, + details: { + current_score: scoreRow.composite_score, + requested_tier: tier.label, + minimum_score: tier.minimumScore, + }, + } + } + + const registration = getRegistration(wallet) + if (!registration) { + return { + ok: false, + code: 'cert_not_registered', + message: 'Agent must be registered before applying for certification', + status: 400, + } + } + + const existingCert = getActiveCertification(wallet) + if (existingCert) { + return { + ok: false, + code: 'cert_already_active', + message: 'Wallet already has an active certification', + status: 409, + } + } + + return { + ok: true, + status: 201, + data: buildCertificationApplyView( + insertCertification(wallet, tier.label, scoreRow.composite_score, tier.priceUsd), + ), + } + }, +) export const CERTIFICATION_DIRECTORY_SORTS = ['score', 'confidence', 'recent', 'name'] as const export type CertificationDirectorySort = (typeof CERTIFICATION_DIRECTORY_SORTS)[number] @@ -347,23 +423,27 @@ function buildCertificationLinks(wallet: string): CertificationStatusView['links } } -function buildCertificationReadinessLinks(wallet: string): CertificationReadinessView['links'] { +function buildCertificationReadinessLinks( + wallet: string, + tier: CertificationTierDefinition, +): CertificationReadinessView['links'] { return { ...buildCertificationLinks(wallet), certification_status: buildPublicUrl(`/v1/certification/${wallet}`), certify_overview: buildPublicUrl(`/certify?wallet=${wallet}`), certified_directory: buildPublicUrl('/directory'), - apply_endpoint: buildPublicUrl('/v1/certification/apply'), - review_status: buildPublicUrl(`/v1/certification/review?wallet=${wallet}`), + apply_endpoint: buildPublicUrl(getCertificationApplyPath(tier.key)), + review_status: buildPublicUrl(`/v1/certification/review?wallet=${wallet}&tier=${tier.key}`), } } -function buildCertificationReviewLinks(wallet: string): CertificationReviewLinks { +function buildCertificationReviewLinks(wallet: string, requestedTier?: string | null): CertificationReviewLinks { + const tier = getCertificationTierByStoredValue(requestedTier) ?? getDefaultCertificationTier() return { agent_profile: buildPublicUrl(`/agent/${wallet}`), - readiness: buildPublicUrl(`/v1/certification/readiness?wallet=${wallet}`), - review_status: buildPublicUrl(`/v1/certification/review?wallet=${wallet}`), - apply_endpoint: buildPublicUrl('/v1/certification/apply'), + readiness: buildPublicUrl(`/v1/certification/readiness?wallet=${wallet}&tier=${tier.key}`), + review_status: buildPublicUrl(`/v1/certification/review?wallet=${wallet}&tier=${tier.key}`), + apply_endpoint: buildPublicUrl(getCertificationApplyPath(tier.key)), certified_directory: buildPublicUrl('/directory'), } } @@ -373,6 +453,7 @@ function buildCertificationStatusView(cert: CertificationRow): CertificationStat wallet: cert.wallet, tier: cert.tier, score_at_certification: cert.score_at_certification, + price_paid_usdc: cert.price_paid_usdc, granted_at: cert.granted_at, expires_at: cert.expires_at, is_valid: true, @@ -470,7 +551,7 @@ function buildCertificationReviewView(row: CertificationReviewRequestRow): Certi website_url: row.website_url, github_verified: row.github_verified === 1, }, - links: buildCertificationReviewLinks(row.wallet), + links: buildCertificationReviewLinks(row.wallet, row.requested_tier), message: buildCertificationReviewMessage(row.status as CertificationReviewStatus), } } @@ -542,20 +623,37 @@ export function getCertificationStatus(wallet: string): CertificationRow | null export function getCertificationReadinessView( rawWallet: string | null | undefined, + rawTier?: string | null | undefined, ): CertificationResult { const wallet = normalizeWallet(rawWallet) if (!wallet) { return invalidWalletError('Valid Ethereum wallet address required') } + const resolvedTier = resolveCertificationTier(rawTier) + if (!resolvedTier.ok) { + return resolvedTier + } + + const requestedTier = resolvedTier.data + const registration = getRegistration(wallet) const score = getScore(wallet) const certification = getActiveCertification(wallet) const review = getLatestCertificationReviewRequest(wallet) const nowIso = new Date().toISOString() - const links = buildCertificationReadinessLinks(wallet) + const links = buildCertificationReadinessLinks(wallet, requestedTier) const scoreIsFresh = !!score && score.expires_at > nowIso - const scoreMeetsThreshold = !!score && score.composite_score >= 75 + const scoreMeetsThreshold = !!score && score.composite_score >= requestedTier.minimumScore + const requestedTierView = buildCertificationTierView(requestedTier) + const eligibleTiers = listCertificationTiers().map((tier) => { + const eligible = !!score && scoreIsFresh && score.composite_score >= tier.minimumScore + return { + ...buildCertificationTierView(tier), + eligible, + score_gap: score ? Math.max(0, tier.minimumScore - score.composite_score) : tier.minimumScore, + } + }) let status: CertificationReadinessView['status'] = 'eligible' let canApply = false @@ -610,7 +708,7 @@ export function getCertificationReadinessView( status = 'score_too_low' blockers.push({ code: 'cert_score_too_low', - message: `Certification requires a score of at least 75. This wallet is currently ${score.composite_score}.`, + message: `${requestedTier.label} certification requires a score of at least ${requestedTier.minimumScore}. This wallet is currently ${score.composite_score}.`, }) nextSteps = [ { code: 'review_profile', label: 'Review agent profile', href: links.agent_profile }, @@ -674,6 +772,8 @@ export function getCertificationReadinessView( data: { wallet, can_apply: canApply, + requested_tier: requestedTierView, + eligible_tiers: eligibleTiers, status, requirements: { registration: { @@ -681,7 +781,7 @@ export function getCertificationReadinessView( }, score: { met: !!score && scoreIsFresh && scoreMeetsThreshold, - minimum_score: 75, + minimum_score: requestedTier.minimumScore, current_score: score?.composite_score ?? null, current_tier: score?.tier ?? null, confidence: score?.confidence ?? null, @@ -707,7 +807,7 @@ export function getCertificationReadinessView( next_steps: nextSteps, payment: { protocol: 'x402', - amount_usdc: 99, + amount_usdc: requestedTier.priceUsd, endpoint: links.apply_endpoint, }, links, @@ -742,12 +842,20 @@ export function getCertificationReviewStatusView( export function submitCertificationReviewRequest(params: { wallet: string | null | undefined note?: string | null | undefined + tier?: string | null | undefined }): CertificationResult { const wallet = normalizeWallet(params.wallet) if (!wallet) { return invalidWalletError('Valid Ethereum wallet address required') } + const resolvedTier = resolveCertificationTier(params.tier) + if (!resolvedTier.ok) { + return resolvedTier + } + + const requestedTier = resolvedTier.data + const latestReview = getLatestCertificationReviewRequest(wallet) if (latestReview?.status === 'approved') { return { @@ -782,13 +890,17 @@ export function submitCertificationReviewRequest(params: { } } - if (score.composite_score < 75) { + if (score.composite_score < requestedTier.minimumScore) { return { ok: false, code: 'cert_score_too_low', - message: 'Composite score must be >= 75 before a review request can be submitted', + message: `${requestedTier.label} certification requires a score of at least ${requestedTier.minimumScore} before a review request can be submitted`, status: 400, - details: { current_score: score.composite_score }, + details: { + current_score: score.composite_score, + requested_tier: requestedTier.label, + minimum_score: requestedTier.minimumScore, + }, } } @@ -813,7 +925,7 @@ export function submitCertificationReviewRequest(params: { const reviewRequest = insertCertificationReviewRequest( wallet, wallet, - score.tier, + requestedTier.label, score.composite_score, score.confidence ?? null, score.expires_at ?? null, @@ -940,7 +1052,7 @@ export function issueCertificationFromReviewRequest(params: { } } - const issuance = applyForCertification(review.wallet) + const issuance = applyForCertification(review.wallet, review.requested_tier) if (!issuance.ok) { return issuance } @@ -1038,9 +1150,27 @@ export function getCertificationBadgeView(rawWallet: string | null | undefined): } } -export function applyForCertification(wallet: string): CertificationApplyResult { +export function getCertificationTierCatalogView(): CertificationResult<{ + tiers: CertificationTierView[] + default_tier: CertificationTierKey +}> { + return { + ok: true, + data: { + tiers: listCertificationTiers().map(buildCertificationTierView), + default_tier: getDefaultCertificationTier().key, + }, + } +} + +export function applyForCertification(wallet: string, rawTier?: string | null | undefined): CertificationApplyResult { + const resolvedTier = resolveCertificationTier(rawTier) + if (!resolvedTier.ok) { + return resolvedTier + } + try { - return applyForCertificationTxn(wallet) + return applyForCertificationTxn(wallet, resolvedTier.data) } catch (err) { if (err instanceof Error && err.message.includes('UNIQUE constraint failed')) { return { @@ -1054,13 +1184,16 @@ export function applyForCertification(wallet: string): CertificationApplyResult } } -export function applyForCertificationByPayer(rawWallet: string | null | undefined): CertificationApplyResult { +export function applyForCertificationByPayer( + rawWallet: string | null | undefined, + rawTier?: string | null | undefined, +): CertificationApplyResult { const wallet = normalizeWallet(rawWallet) if (!wallet) { return invalidWalletError('Valid Ethereum wallet address required') } - return applyForCertification(wallet) + return applyForCertification(wallet, rawTier) } export function listCertificationRecords(): CertificationAdminRecordView[] { diff --git a/src/services/certificationTiers.ts b/src/services/certificationTiers.ts new file mode 100644 index 0000000..e5546f0 --- /dev/null +++ b/src/services/certificationTiers.ts @@ -0,0 +1,101 @@ +export const CERTIFICATION_TIER_KEYS = ['operational', 'transactional', 'autonomous'] as const + +export type CertificationTierKey = (typeof CERTIFICATION_TIER_KEYS)[number] + +export interface CertificationTierDefinition { + key: CertificationTierKey + label: 'Operational' | 'Transactional' | 'Autonomous' + level: 1 | 2 | 3 + minimumScore: number + priceUsd: number + summary: string + controls: string[] +} + +const CERTIFICATION_TIERS: readonly CertificationTierDefinition[] = [ + { + key: 'operational', + label: 'Operational', + level: 1, + minimumScore: 60, + priceUsd: 50, + summary: 'Reliable execution for bounded tasks with a basic audit trail.', + controls: ['Basic audit trail', 'Defined scope of work', 'Score >= 60'], + }, + { + key: 'transactional', + label: 'Transactional', + level: 2, + minimumScore: 75, + priceUsd: 200, + summary: 'Financially sensitive work with monitoring, spend controls, and fuller logs.', + controls: ['Transaction logging', 'Spend guardrails', 'Enhanced monitoring', 'Score >= 75'], + }, + { + key: 'autonomous', + label: 'Autonomous', + level: 3, + minimumScore: 90, + priceUsd: 500, + summary: 'Consequential autonomy with strong evidence, escalation, and forensic expectations.', + controls: ['Forensic trail', 'Escalation readiness', 'Autonomy review', 'Score >= 90'], + }, +] as const + +const DEFAULT_CERTIFICATION_TIER_KEY: CertificationTierKey = 'transactional' + +const CERTIFICATION_TIER_ALIAS_MAP = new Map() + +for (const tier of CERTIFICATION_TIERS) { + const aliases = [ + tier.key, + tier.label, + tier.label.toLowerCase(), + `tier-${tier.level}`, + `tier${tier.level}`, + String(tier.level), + ] + + for (const alias of aliases) { + CERTIFICATION_TIER_ALIAS_MAP.set(alias.trim().toLowerCase(), tier) + } +} + +// Backward-compatible aliases for earlier single-tier certification records. +CERTIFICATION_TIER_ALIAS_MAP.set('trusted', CERTIFICATION_TIER_ALIAS_MAP.get('transactional')!) +CERTIFICATION_TIER_ALIAS_MAP.set('elite', CERTIFICATION_TIER_ALIAS_MAP.get('autonomous')!) +CERTIFICATION_TIER_ALIAS_MAP.set('established', CERTIFICATION_TIER_ALIAS_MAP.get('operational')!) + +export function listCertificationTiers(): CertificationTierDefinition[] { + return [...CERTIFICATION_TIERS] +} + +export function getDefaultCertificationTier(): CertificationTierDefinition { + return CERTIFICATION_TIER_ALIAS_MAP.get(DEFAULT_CERTIFICATION_TIER_KEY)! +} + +export function getCertificationTier(rawTier: string | null | undefined): CertificationTierDefinition | null { + const normalized = rawTier?.trim().toLowerCase() + if (!normalized) return null + return CERTIFICATION_TIER_ALIAS_MAP.get(normalized) ?? null +} + +export function getCertificationTierByStoredValue( + rawTier: string | null | undefined, +): CertificationTierDefinition | null { + return getCertificationTier(rawTier) +} + +export function getHighestEligibleCertificationTier(score: number): CertificationTierDefinition | null { + for (const tier of [...CERTIFICATION_TIERS].sort((left, right) => right.minimumScore - left.minimumScore)) { + if (score >= tier.minimumScore) { + return tier + } + } + + return null +} + +export function getCertificationApplyPath(tierKey: CertificationTierKey): string { + return `/v1/certification/apply/${tierKey}` +} diff --git a/src/services/contractArtifactService.ts b/src/services/contractArtifactService.ts new file mode 100644 index 0000000..3b49421 --- /dev/null +++ b/src/services/contractArtifactService.ts @@ -0,0 +1,178 @@ +import { readFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import { fileURLToPath } from 'node:url' +import { buildPublicUrl } from '../config/public.js' + +const __dirname = dirname(fileURLToPath(import.meta.url)) +const ARTIFACTS_DIR = join(__dirname, '..', '..', 'artifacts', 'contracts') +const ARTIFACT_MANIFEST_PATH = join(ARTIFACTS_DIR, 'manifest.json') + +export interface ContractArtifactManifestEntry { + contract: string + artifact_kind: 'contract' | 'interface' + artifact_path: string + source_path: string + source_sha256: string + bytecode_sha256: string + deployed_bytecode_sha256: string +} + +export interface ContractArtifactManifest { + standard: 'djd-solidity-artifacts-v1' + compiler: { + name: 'solc' + version: string + via_ir: boolean + } + contracts: ContractArtifactManifestEntry[] +} + +export interface CompiledContractArtifact { + contract: string + artifact_kind: 'contract' | 'interface' + source_path: string + source_sha256: string + compiler: { + name: 'solc' + version: string + optimizer: { + enabled: boolean + runs: number + } + via_ir: boolean + } + abi: unknown[] + bytecode: string + deployed_bytecode: string + method_identifiers: Record + constructor: { + inputs: unknown[] + } | null + metadata: Record +} + +export interface EvaluatorArtifactPackageView { + standard: 'djd-evaluator-artifact-package-v1' + available: boolean + compiler: ContractArtifactManifest['compiler'] | null + summary: { + total: number + deployable: number + interfaces: number + } + contracts: Array<{ + contract: string + artifact_kind: 'contract' | 'interface' + deployable: boolean + artifact_path: string + source_path: string + source_sha256: string + bytecode_sha256: string + deployed_bytecode_sha256: string + constructor: CompiledContractArtifact['constructor'] + abi: unknown[] + bytecode: string + deployed_bytecode: string + method_identifiers: Record + }> + links: { + verifier_package: string + deploy_plan: string + docs: string + } + notes: string[] +} + +export function loadArtifactManifest(): ContractArtifactManifest | null { + try { + return JSON.parse(readFileSync(ARTIFACT_MANIFEST_PATH, 'utf8')) as ContractArtifactManifest + } catch { + return null + } +} + +export function loadArtifact(contract: string): CompiledContractArtifact | null { + try { + return JSON.parse(readFileSync(join(ARTIFACTS_DIR, `${contract}.json`), 'utf8')) as CompiledContractArtifact + } catch { + return null + } +} + +export function getEvaluatorArtifactPackageView(): EvaluatorArtifactPackageView { + const manifest = loadArtifactManifest() + if (!manifest) { + return { + standard: 'djd-evaluator-artifact-package-v1', + available: false, + compiler: null, + summary: { + total: 0, + deployable: 0, + interfaces: 0, + }, + contracts: [], + links: { + verifier_package: buildPublicUrl('/v1/score/evaluator/verifier'), + deploy_plan: buildPublicUrl('/v1/score/evaluator/deploy?id=verdict_...'), + docs: buildPublicUrl('/docs'), + }, + notes: [ + 'Compiled Solidity artifacts are not available in this runtime yet.', + 'Run npm run contracts:compile before depending on the artifact package endpoint.', + ], + } + } + + const contracts = manifest.contracts + .map((entry) => { + const artifact = loadArtifact(entry.contract) + if (!artifact) { + return null + } + + return { + contract: entry.contract, + artifact_kind: entry.artifact_kind, + deployable: entry.artifact_kind === 'contract', + artifact_path: entry.artifact_path, + source_path: entry.source_path, + source_sha256: entry.source_sha256, + bytecode_sha256: entry.bytecode_sha256, + deployed_bytecode_sha256: entry.deployed_bytecode_sha256, + constructor: artifact.constructor, + abi: artifact.abi, + bytecode: artifact.bytecode, + deployed_bytecode: artifact.deployed_bytecode, + method_identifiers: artifact.method_identifiers, + } + }) + .filter((entry): entry is NonNullable => entry !== null) + + return { + standard: 'djd-evaluator-artifact-package-v1', + available: true, + compiler: manifest.compiler, + summary: { + total: contracts.length, + deployable: contracts.filter((entry) => entry.deployable).length, + interfaces: contracts.filter((entry) => !entry.deployable).length, + }, + contracts, + links: { + verifier_package: buildPublicUrl('/v1/score/evaluator/verifier'), + deploy_plan: buildPublicUrl('/v1/score/evaluator/deploy?id=verdict_...'), + docs: buildPublicUrl('/docs'), + }, + notes: [ + 'Artifacts are compiled with solc optimizer runs=200 and viaIR enabled so the verifier contract compiles cleanly.', + 'Use deployable contract entries for constructor bytecode and interface entries for ABI-only integrations.', + ], + } +} + +export function getEvaluatorArtifactContractEntry( + contract: string, +): EvaluatorArtifactPackageView['contracts'][number] | null { + return getEvaluatorArtifactPackageView().contracts.find((entry) => entry.contract === contract) ?? null +} diff --git a/src/services/discoveryService.ts b/src/services/discoveryService.ts index 6b710e6..e8f877a 100644 --- a/src/services/discoveryService.ts +++ b/src/services/discoveryService.ts @@ -317,7 +317,7 @@ ${renderPublicNav('docs', '/pricing', 'View Pricing')} ${renderPublicFooter({ - copy: 'DJD documentation covers the current trust infrastructure product: score, Certify, evaluator, monitoring, and directory surfaces for apps and agent networks on Base.', + copy: 'DJD documentation covers the current wallet screening product: score, Certify, evaluator, monitoring, and directory surfaces for apps and agent networks on Base.', })}` } @@ -336,7 +336,7 @@ export function getX402DiscoveryView(requestUrl: string, forwardedProto?: string service: { name: SERVICE_TITLE, description: - 'Trust infrastructure for apps and agents on Base. ' + + 'Wallet screening and trust signals for apps and agents on Base. ' + 'Score wallets, publish public trust surfaces, and gate payouts or x402 routes before money moves.', version: SERVICE_VERSION, docs: `${baseUrl}/docs`, @@ -349,7 +349,7 @@ export function getX402DiscoveryView(requestUrl: string, forwardedProto?: string price: 0, description: 'Free basic score (0–100) with tier and recommendation. 10/day per IP.', input: { query: { wallet: { type: 'string', required: true, description: 'Ethereum wallet address' } } }, - output: { example: { wallet: '0x…', score: 78, tier: 'Established', recommendation: 'transact' } }, + output: { example: { wallet: '0x…', score: 78, tier: 'Trusted', recommendation: 'proceed' } }, }, { path: '/v1/score/erc8004', @@ -363,8 +363,8 @@ export function getX402DiscoveryView(requestUrl: string, forwardedProto?: string wallet: '0x…', agent_id: '123456789', standard: 'erc-8004-compatible', - reputation: { composite_score: 78, tier: 'Established', confidence: 0.82 }, - certification: { active: true, tier: 'Trusted' }, + reputation: { composite_score: 78, tier: 'Trusted', confidence: 0.85 }, + certification: { active: true, tier: 'Transactional' }, }, }, }, @@ -373,17 +373,29 @@ export function getX402DiscoveryView(requestUrl: string, forwardedProto?: string method: 'GET', price: 0, description: - 'Free certification readiness check that tells a wallet whether it can apply, what is blocking it, and what to do next.', - input: { query: { wallet: { type: 'string', required: true, description: 'Ethereum wallet address' } } }, + 'Free certification readiness check that tells a wallet whether it can apply for a requested certification tier, what is blocking it, and what to do next.', + input: { + query: { + wallet: { type: 'string', required: true, description: 'Ethereum wallet address' }, + tier: { type: 'string', required: false, description: 'operational, transactional, or autonomous' }, + }, + }, output: { example: { wallet: '0x…', can_apply: true, status: 'eligible', - payment: { protocol: 'x402', amount_usdc: 99 }, + requested_tier: { key: 'transactional', label: 'Transactional', minimum_score: 75, price_usdc: 200 }, + payment: { protocol: 'x402', amount_usdc: 200 }, }, }, }, + { + path: '/v1/certification/tiers', + method: 'GET', + price: 0, + description: 'Free certification tier catalog with score thresholds, x402 prices, and tier metadata.', + }, { path: '/v1/certification/review', method: 'GET/POST', @@ -392,13 +404,17 @@ export function getX402DiscoveryView(requestUrl: string, forwardedProto?: string 'Free certification review queue surface for submitting a review request or reading the latest reviewer status for a wallet.', input: { query: { wallet: { type: 'string', required: true, description: 'Ethereum wallet address' } }, - body: { wallet: { type: 'string', required: true }, note: { type: 'string', required: false } }, + body: { + wallet: { type: 'string', required: true }, + note: { type: 'string', required: false }, + tier: { type: 'string', required: false, description: 'operational, transactional, or autonomous' }, + }, }, output: { example: { wallet: '0x…', status: 'pending', - requested_tier: 'Trusted', + requested_tier: 'Transactional', requested_score: 82, }, }, @@ -418,6 +434,24 @@ export function getX402DiscoveryView(requestUrl: string, forwardedProto?: string }, }, }, + { + path: '/v1/certification/apply/operational', + method: 'POST', + price: ENDPOINT_PRICING['/v1/certification/apply/operational'], + description: 'Paid Tier 1 / Operational certification issuance for the payer wallet.', + }, + { + path: '/v1/certification/apply/transactional', + method: 'POST', + price: ENDPOINT_PRICING['/v1/certification/apply/transactional'], + description: 'Paid Tier 2 / Transactional certification issuance for the payer wallet.', + }, + { + path: '/v1/certification/apply/autonomous', + method: 'POST', + price: ENDPOINT_PRICING['/v1/certification/apply/autonomous'], + description: 'Paid Tier 3 / Autonomous certification issuance for the payer wallet.', + }, { path: '/v1/score/full', method: 'GET', @@ -430,9 +464,94 @@ export function getX402DiscoveryView(requestUrl: string, forwardedProto?: string method: 'GET', price: ENDPOINT_PRICING['/v1/score/evaluator'], description: - 'ERC-8183 evaluator prototype that returns an approve, review, or reject recommendation for settlement readiness.', + 'ERC-8183 evaluator prototype that returns an approve, review, or reject recommendation for settlement readiness, plus links into standards and forensics surfaces.', input: { query: { wallet: { type: 'string', required: true } } }, }, + { + path: '/v1/score/evaluator/evidence', + method: 'GET', + price: ENDPOINT_PRICING['/v1/score/evaluator/evidence'], + description: + 'ERC-8183 evaluator evidence packet that packages the settlement verdict with certification baseline, forensics summary, and traceable evidence artifacts.', + input: { query: { wallet: { type: 'string', required: true } } }, + }, + { + path: '/v1/score/evaluator/oracle', + method: 'GET', + price: ENDPOINT_PRICING['/v1/score/evaluator/oracle'], + description: + 'ERC-8183 oracle-style verdict endpoint that persists a compact settlement decision with recommendation, forensic trace id, and a signed EIP-712 attestation when a signer is configured.', + input: { + query: { + wallet: { type: 'string', required: true }, + counterparty_wallet: { type: 'string' }, + escrow_id: { type: 'string' }, + }, + }, + }, + { + path: '/v1/score/evaluator/verdict', + method: 'GET', + price: ENDPOINT_PRICING['/v1/score/evaluator/verdict'], + description: + 'Fetch a previously issued evaluator oracle verdict record by id, including its attestation metadata.', + input: { query: { id: { type: 'string', required: true } } }, + }, + { + path: '/v1/score/evaluator/verdicts', + method: 'GET', + price: ENDPOINT_PRICING['/v1/score/evaluator/verdicts'], + description: 'List recent persisted evaluator verdicts for a wallet, including approval and dispute mix.', + input: { query: { wallet: { type: 'string', required: true }, limit: { type: 'integer' } } }, + }, + { + path: '/v1/score/evaluator/callback', + method: 'GET', + price: ENDPOINT_PRICING['/v1/score/evaluator/callback'], + description: + 'Convert a stored evaluator verdict into ABI-backed callback calldata for a relayer or escrow contract. Returns a ready=false envelope when the verdict attestation is unsigned.', + input: { query: { id: { type: 'string', required: true }, target_contract: { type: 'string' } } }, + }, + { + path: '/v1/score/evaluator/proof', + method: 'GET', + price: 0, + description: + 'Free verifier calldata package for a stored verdict. If target_contract is omitted and a published verifier deployment exists for the requested network, the transaction envelope targets that live verifier automatically.', + input: { query: { id: { type: 'string', required: true }, target_contract: { type: 'string' } } }, + }, + { + path: '/v1/score/evaluator/escrow', + method: 'GET', + price: 0, + description: + 'Free escrow-settlement calldata package for a stored verdict. If escrow_contract is omitted and a published escrow deployment exists for the requested network, the transaction envelope targets that live escrow automatically.', + input: { query: { id: { type: 'string', required: true }, escrow_contract: { type: 'string' } } }, + }, + { + path: '/v1/score/evaluator/deploy', + method: 'GET', + price: 0, + description: + 'Free verifier and escrow deployment plan. If verifier_contract is omitted and a published verifier deployment exists for the requested network, the plan reuses that live verifier automatically.', + input: { query: { id: { type: 'string', required: true }, verifier_contract: { type: 'string' } } }, + }, + { + path: '/v1/score/evaluator/deployments', + method: 'GET', + price: 0, + description: + 'Live deployment registry for DJD evaluator verifier and escrow contracts, keyed by network for consumers that need the currently published onchain addresses.', + input: { query: { network: { type: 'string', enum: ['base', 'base-sepolia'] } } }, + }, + { + path: '/v1/score/evaluator/promotion', + method: 'GET', + price: 0, + description: + 'Ready-to-consume env bundle for the currently published verifier and escrow deployment on a network, including dotenv, shell export, and GitHub output formats.', + input: { query: { network: { type: 'string', enum: ['base', 'base-sepolia'] } } }, + }, { path: '/v1/score/risk', method: 'GET', diff --git a/src/services/evaluatorAttestationService.ts b/src/services/evaluatorAttestationService.ts new file mode 100644 index 0000000..ee51ba3 --- /dev/null +++ b/src/services/evaluatorAttestationService.ts @@ -0,0 +1,236 @@ +import { hashTypedData } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { getDefaultEvaluatorNetwork } from './evaluatorNetworkService.js' + +const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' as const + +export const EVALUATOR_VERDICT_PRIMARY_TYPE = 'EvaluatorVerdict' as const + +export const EVALUATOR_VERDICT_TYPES = { + EvaluatorVerdict: [ + { name: 'verdictId', type: 'string' }, + { name: 'wallet', type: 'address' }, + { name: 'counterpartyWallet', type: 'address' }, + { name: 'escrowId', type: 'string' }, + { name: 'decision', type: 'string' }, + { name: 'recommendation', type: 'string' }, + { name: 'approved', type: 'bool' }, + { name: 'confidence', type: 'uint16' }, + { name: 'agentScoreProvider', type: 'uint16' }, + { name: 'scoreModelVersion', type: 'string' }, + { name: 'certificationValid', type: 'bool' }, + { name: 'certificationTier', type: 'string' }, + { name: 'riskLevel', type: 'string' }, + { name: 'riskScore', type: 'uint16' }, + { name: 'forensicTraceId', type: 'string' }, + { name: 'packetHash', type: 'bytes32' }, + { name: 'generatedAt', type: 'string' }, + ], +} as const + +export function buildEvaluatorVerdictDomain(chainId = getDefaultEvaluatorNetwork().chainId) { + return { + name: 'DJD Evaluator Verdict', + version: '1', + chainId, + } as const +} + +export const EVALUATOR_VERDICT_DOMAIN = buildEvaluatorVerdictDomain() + +type AttestationSource = 'oracle_signer' | 'publisher_fallback' | 'unconfigured' | 'invalid_key' +type AttestationStatus = 'signed' | 'unsigned' + +export interface EvaluatorVerdictAttestationView { + status: AttestationStatus + scheme: 'eip712' + source: AttestationSource + signer: string | null + signature: string | null + digest: string + issued_at: string + reason: string | null + typed_data: { + domain: ReturnType + primaryType: typeof EVALUATOR_VERDICT_PRIMARY_TYPE + types: typeof EVALUATOR_VERDICT_TYPES + message: { + verdictId: string + wallet: `0x${string}` + counterpartyWallet: `0x${string}` + escrowId: string + decision: string + recommendation: string + approved: boolean + confidence: number + agentScoreProvider: number + scoreModelVersion: string + certificationValid: boolean + certificationTier: string + riskLevel: string + riskScore: number + forensicTraceId: string + packetHash: `0x${string}` + generatedAt: string + } + } +} + +export interface EvaluatorVerdictAttestationInput { + verdict_id: string + wallet: `0x${string}` + counterparty_wallet: `0x${string}` | null + escrow_id: string | null + decision: string + recommendation: string + approved: boolean + confidence: number + agent_score_provider: number + score_model_version: string + certification_valid: boolean + certification_tier: string | null + risk_level: string + risk_score: number + forensic_trace_id: string + packet_hash: `0x${string}` + generated_at: string +} + +export function buildEvaluatorVerdictTypedData( + input: EvaluatorVerdictAttestationInput, + options?: { + chainId?: number + }, +): { + digest: string + typed_data: EvaluatorVerdictAttestationView['typed_data'] +} { + const typed_data = { + domain: buildEvaluatorVerdictDomain(options?.chainId), + types: EVALUATOR_VERDICT_TYPES, + primaryType: EVALUATOR_VERDICT_PRIMARY_TYPE, + message: { + verdictId: input.verdict_id, + wallet: input.wallet, + counterpartyWallet: input.counterparty_wallet ?? ZERO_ADDRESS, + escrowId: input.escrow_id ?? '', + decision: input.decision, + recommendation: input.recommendation, + approved: input.approved, + confidence: input.confidence, + agentScoreProvider: input.agent_score_provider, + scoreModelVersion: input.score_model_version, + certificationValid: input.certification_valid, + certificationTier: input.certification_tier ?? '', + riskLevel: input.risk_level, + riskScore: input.risk_score, + forensicTraceId: input.forensic_trace_id, + packetHash: input.packet_hash, + generatedAt: input.generated_at, + }, + } + + return { + digest: hashTypedData(typed_data), + typed_data, + } +} + +function resolveSignerConfig(): { + source: Exclude + key: `0x${string}` +} | null { + const oracleKey = process.env.ORACLE_SIGNER_PRIVATE_KEY + if (/^0x[a-fA-F0-9]{64}$/.test(oracleKey ?? '')) { + return { source: 'oracle_signer', key: oracleKey!.toLowerCase() as `0x${string}` } + } + + const publisherKey = process.env.PUBLISHER_PRIVATE_KEY + if (/^0x[a-fA-F0-9]{64}$/.test(publisherKey ?? '')) { + return { source: 'publisher_fallback', key: publisherKey!.toLowerCase() as `0x${string}` } + } + + return null +} + +function getInvalidKeyReason(): string | null { + const oracleKey = process.env.ORACLE_SIGNER_PRIVATE_KEY + if (oracleKey && !/^0x[a-fA-F0-9]{64}$/.test(oracleKey)) { + return 'ORACLE_SIGNER_PRIVATE_KEY is invalid' + } + + const publisherKey = process.env.PUBLISHER_PRIVATE_KEY + if (publisherKey && !/^0x[a-fA-F0-9]{64}$/.test(publisherKey)) { + return 'PUBLISHER_PRIVATE_KEY is invalid' + } + + return null +} + +export function getEvaluatorAttestationSignerStatus(): { + configured: boolean + source: AttestationSource + address: string | null + reason: string | null +} { + const signerConfig = resolveSignerConfig() + const invalidKeyReason = getInvalidKeyReason() + + if (!signerConfig) { + return { + configured: false, + source: invalidKeyReason ? 'invalid_key' : 'unconfigured', + address: null, + reason: invalidKeyReason ?? 'No oracle signing key configured', + } + } + + const account = privateKeyToAccount(signerConfig.key) + return { + configured: true, + source: signerConfig.source, + address: account.address, + reason: null, + } +} + +export async function buildEvaluatorVerdictAttestation( + input: EvaluatorVerdictAttestationInput, + options?: { + chainId?: number + }, +): Promise { + const { digest, typed_data } = buildEvaluatorVerdictTypedData(input, options) + const issuedAt = new Date().toISOString() + const signerConfig = resolveSignerConfig() + const invalidKeyReason = getInvalidKeyReason() + + if (!signerConfig) { + return { + status: 'unsigned', + scheme: 'eip712', + source: invalidKeyReason ? 'invalid_key' : 'unconfigured', + signer: null, + signature: null, + digest, + issued_at: issuedAt, + reason: invalidKeyReason ?? 'No oracle signing key configured', + typed_data, + } + } + + const account = privateKeyToAccount(signerConfig.key) + const signature = await account.signTypedData(typed_data) + + return { + status: 'signed', + scheme: 'eip712', + source: signerConfig.source, + signer: account.address, + signature, + digest, + issued_at: issuedAt, + reason: null, + typed_data, + } +} diff --git a/src/services/evaluatorContractService.ts b/src/services/evaluatorContractService.ts new file mode 100644 index 0000000..0a5ad58 --- /dev/null +++ b/src/services/evaluatorContractService.ts @@ -0,0 +1,1037 @@ +import { createHash } from 'node:crypto' +import { readFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import { fileURLToPath } from 'node:url' +import { buildPublicUrl } from '../config/public.js' +import { + DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_ABI, + DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_CONTRACT, + DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_FUNCTION, + DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_SELECTOR, + DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_SIGNATURE, + encodeEvaluatorEscrowSettlement, +} from '../contracts/djdEvaluatorEscrowSettlementExample.js' +import { + buildEscrowIdHash, + DJD_EVALUATOR_ORACLE_CALLBACK_ABI, + DJD_EVALUATOR_ORACLE_CALLBACK_FUNCTION, + DJD_EVALUATOR_ORACLE_CALLBACK_INTERFACE, + DJD_EVALUATOR_ORACLE_CALLBACK_SELECTOR, + DJD_EVALUATOR_ORACLE_CALLBACK_SIGNATURE, + ZERO_ADDRESS, +} from '../contracts/djdEvaluatorOracleCallback.js' +import { + DJD_EVALUATOR_VERDICT_VERIFIER_ABI, + DJD_EVALUATOR_VERDICT_VERIFIER_CONTRACT, + DJD_EVALUATOR_VERDICT_VERIFIER_FUNCTION, + DJD_EVALUATOR_VERDICT_VERIFIER_SELECTORS, + DJD_EVALUATOR_VERDICT_VERIFIER_SIGNATURES, + encodeEvaluatorVerdictVerification, +} from '../contracts/djdEvaluatorVerdictVerifier.js' +import { normalizeWallet } from '../utils/walletUtils.js' +import { + type EvaluatorArtifactPackageView, + getEvaluatorArtifactContractEntry, + getEvaluatorArtifactPackageView, +} from './contractArtifactService.js' +import { + buildEvaluatorVerdictDomain, + EVALUATOR_VERDICT_PRIMARY_TYPE, + EVALUATOR_VERDICT_TYPES, + getEvaluatorAttestationSignerStatus, +} from './evaluatorAttestationService.js' +import { + getPublishedEvaluatorDeployment, + type PublishedEvaluatorDeployment, +} from './evaluatorDeploymentRegistryService.js' +import { + type EvaluatorNetworkConfig, + findEvaluatorNetworkByChainId, + getDefaultEvaluatorNetwork, + getEvaluatorVerdictChainId, + listEvaluatorNetworks, + resolveEvaluatorNetwork, +} from './evaluatorNetworkService.js' +import { + type EvaluatorServiceResult, + type EvaluatorStoredVerdictView, + getEvaluatorVerdictRecord, +} from './evaluatorService.js' + +const __dirname = dirname(fileURLToPath(import.meta.url)) +const CONTRACTS_DIR = join(__dirname, '..', '..', 'contracts') + +function readContractSource(fileName: string, contract: string) { + const source = readFileSync(join(CONTRACTS_DIR, fileName), 'utf8') + return { + contract, + path: `contracts/${fileName}`, + license: 'MIT' as const, + sha256: createHash('sha256').update(source).digest('hex'), + source, + } +} + +const CONTRACT_SOURCES = [ + readContractSource('IDJDEvaluatorOracleCallback.sol', DJD_EVALUATOR_ORACLE_CALLBACK_INTERFACE), + readContractSource('DJDEvaluatorVerdictVerifier.sol', DJD_EVALUATOR_VERDICT_VERIFIER_CONTRACT), + readContractSource('DJDEvaluatorEscrowSettlementExample.sol', DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_CONTRACT), +] + +function toJsonAbi(abi: unknown): unknown[] { + return JSON.parse(JSON.stringify(abi)) as unknown[] +} + +function toNetworkView(network: EvaluatorNetworkConfig) { + return { + key: network.key, + chain_id: network.chainId, + chain_name: network.chainName, + caip2: network.caip2, + environment: network.environment, + } +} + +function invalidNetworkResult(rawNetwork: string | undefined): EvaluatorServiceResult { + return { + ok: false, + code: 'invalid_network', + message: 'Invalid or unsupported network', + status: 400, + details: { + network: rawNetwork ?? null, + supported_networks: listEvaluatorNetworks().map((network) => network.key), + }, + } +} + +function resolveVerifierPackageNetwork(rawNetwork: string | undefined): EvaluatorServiceResult { + const network = resolveEvaluatorNetwork(rawNetwork) + if (!network) { + return invalidNetworkResult(rawNetwork) + } + + return { + ok: true, + data: network, + } +} + +function resolveStoredVerdictNetwork( + verdict: Pick, + rawNetwork: string | undefined, +): EvaluatorServiceResult { + const verdictChainId = getEvaluatorVerdictChainId(verdict.attestation) + const verdictNetwork = findEvaluatorNetworkByChainId(verdictChainId) ?? getDefaultEvaluatorNetwork() + + if (rawNetwork === undefined || rawNetwork.trim() === '') { + return { + ok: true, + data: verdictNetwork, + } + } + + const requestedNetwork = resolveEvaluatorNetwork(rawNetwork) + if (!requestedNetwork) { + return invalidNetworkResult(rawNetwork) + } + + if (requestedNetwork.chainId !== verdictChainId) { + return { + ok: false, + code: 'invalid_network', + message: 'Stored evaluator verdict was issued for a different network', + status: 400, + details: { + verdict_id: verdict.verdict_id, + verdict_network: verdictNetwork.key, + verdict_chain_id: verdictChainId, + requested_network: requestedNetwork.key, + requested_chain_id: requestedNetwork.chainId, + suggestion: `Request a fresh evaluator oracle verdict with network=${requestedNetwork.key} before generating onchain calldata.`, + }, + } + } + + return { + ok: true, + data: requestedNetwork, + } +} + +function resolvePublishedDeployment(network: EvaluatorNetworkConfig): { + registry_updated_at: string | null + deployment: PublishedEvaluatorDeployment | null +} { + const published = getPublishedEvaluatorDeployment(network) + return { + registry_updated_at: published.registry.updated_at, + deployment: published.deployment, + } +} + +function resolveProofTargetContract( + network: EvaluatorNetworkConfig, + explicitTargetContract: string | null, +): { + source: 'explicit' | 'published_registry' | 'unresolved' + contract_address: string | null + registry_updated_at: string | null + published_deployment: PublishedEvaluatorDeployment | null +} { + const published = resolvePublishedDeployment(network) + if (explicitTargetContract) { + return { + source: 'explicit', + contract_address: explicitTargetContract, + registry_updated_at: published.registry_updated_at, + published_deployment: published.deployment, + } + } + + const publishedVerifier = published.deployment?.contracts.verifier.address ?? null + if (publishedVerifier) { + return { + source: 'published_registry', + contract_address: publishedVerifier, + registry_updated_at: published.registry_updated_at, + published_deployment: published.deployment, + } + } + + return { + source: 'unresolved', + contract_address: null, + registry_updated_at: published.registry_updated_at, + published_deployment: published.deployment, + } +} + +function resolveEscrowTargetContract( + network: EvaluatorNetworkConfig, + explicitEscrowContract: string | null, +): { + source: 'explicit' | 'published_registry' | 'unresolved' + contract_address: string | null + registry_updated_at: string | null + published_deployment: PublishedEvaluatorDeployment | null +} { + const published = resolvePublishedDeployment(network) + if (explicitEscrowContract) { + return { + source: 'explicit', + contract_address: explicitEscrowContract, + registry_updated_at: published.registry_updated_at, + published_deployment: published.deployment, + } + } + + const publishedEscrow = published.deployment?.contracts.escrow.address ?? null + if (publishedEscrow) { + return { + source: 'published_registry', + contract_address: publishedEscrow, + registry_updated_at: published.registry_updated_at, + published_deployment: published.deployment, + } + } + + return { + source: 'unresolved', + contract_address: null, + registry_updated_at: published.registry_updated_at, + published_deployment: published.deployment, + } +} + +export interface EvaluatorVerifierPackageView { + standard: 'djd-evaluator-verifier-package-v1' + network: ReturnType + signing: { + scheme: 'eip712' + primary_type: typeof EVALUATOR_VERDICT_PRIMARY_TYPE + domain: ReturnType + types: typeof EVALUATOR_VERDICT_TYPES + active_signer: ReturnType + } + contracts: { + callback_interface: { + contract: typeof DJD_EVALUATOR_ORACLE_CALLBACK_INTERFACE + function: typeof DJD_EVALUATOR_ORACLE_CALLBACK_FUNCTION + signature: typeof DJD_EVALUATOR_ORACLE_CALLBACK_SIGNATURE + selector: typeof DJD_EVALUATOR_ORACLE_CALLBACK_SELECTOR + abi: unknown[] + } + verifier: { + contract: typeof DJD_EVALUATOR_VERDICT_VERIFIER_CONTRACT + constructor: { + initial_signer: string | null + } + methods: { + hash_verdict: { + signature: string + selector: string + } + verify_verdict: { + signature: string + selector: string + } + verify_digest: { + signature: string + selector: string + } + set_oracle_signer: { + signature: string + selector: string + } + transfer_ownership: { + signature: string + selector: string + } + } + abi: unknown[] + } + settlement_example: { + contract: typeof DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_CONTRACT + function: typeof DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_FUNCTION + signature: typeof DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_SIGNATURE + selector: typeof DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_SELECTOR + constructor: { + verifier: string | null + provider: 'wallet_from_verdict' + counterparty: 'counterparty_wallet_from_verdict_or_zero' + escrow_id: 'escrow_id_from_verdict' + escrow_id_hash: 'keccak256(escrow_id)' + } + abi: unknown[] + } + sources: typeof CONTRACT_SOURCES + } + endpoints: { + oracle_verdict: string + verdict_record: string + callback_calldata: string + deployment_registry: string + artifact_package: string + verifier_proof: string + escrow_settlement: string + deploy_plan: string + docs: string + } + notes: string[] +} + +export interface EvaluatorVerifierProofView { + standard: 'djd-evaluator-verifier-proof-v1' + ready: boolean + reason: string | null + verdict_id: string + verifier: { + contract: typeof DJD_EVALUATOR_VERDICT_VERIFIER_CONTRACT + function: typeof DJD_EVALUATOR_VERDICT_VERIFIER_FUNCTION + selector: string + chain_id: number + } + attestation: { + status: 'signed' | 'unsigned' + signer: string | null + digest: string + signature: string | null + scheme: 'eip712' + } + verdict: EvaluatorStoredVerdictView['attestation']['typed_data']['message'] + call: { + selector: string | null + calldata: string | null + args: { + verdict: EvaluatorStoredVerdictView['attestation']['typed_data']['message'] + signature: string | null + } + } + transaction: { + to: string | null + data: string | null + value: '0' + } + resolution: { + source: 'explicit' | 'published_registry' | 'unresolved' + contract_address: string | null + registry_updated_at: string | null + published_deployment: PublishedEvaluatorDeployment | null + } + links: { + verdict_record: string + verifier_package: string + deployment_registry: string + } +} + +export interface EvaluatorEscrowSettlementView { + standard: 'djd-evaluator-escrow-settlement-v1' + ready: boolean + reason: string | null + verdict_id: string + escrow: { + contract: typeof DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_CONTRACT + function: typeof DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_FUNCTION + selector: string + chain_id: number + } + verifier: { + contract: typeof DJD_EVALUATOR_VERDICT_VERIFIER_CONTRACT + function: typeof DJD_EVALUATOR_VERDICT_VERIFIER_FUNCTION + } + attestation: { + status: 'signed' | 'unsigned' + signer: string | null + digest: string + signature: string | null + scheme: 'eip712' + } + settlement: { + recommendation: string + approved: boolean + outcome: 'release' | 'manual_review' | 'dispute' | 'reject' + release_authorized: boolean + } + verdict: EvaluatorStoredVerdictView['attestation']['typed_data']['message'] + call: { + selector: string | null + calldata: string | null + args: { + verdict: EvaluatorStoredVerdictView['attestation']['typed_data']['message'] + signature: string | null + } + } + transaction: { + to: string | null + data: string | null + value: '0' + } + resolution: { + source: 'explicit' | 'published_registry' | 'unresolved' + contract_address: string | null + registry_updated_at: string | null + published_deployment: PublishedEvaluatorDeployment | null + } + links: { + verdict_record: string + verifier_package: string + verifier_proof: string + deployment_registry: string + } +} + +export interface EvaluatorDeploymentPlanView { + standard: 'djd-evaluator-deploy-plan-v1' + network: ReturnType + verdict_id: string + verifier: { + contract: typeof DJD_EVALUATOR_VERDICT_VERIFIER_CONTRACT + constructor: { + initial_signer: string | null + } + deployment_ready: boolean + reason: string | null + } + escrow: { + contract: typeof DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_CONTRACT + constructor: { + verifier: string | null + verifier_source: 'explicit' | 'published_registry' | 'manual_required' + provider: string + counterparty: string + escrow_id: string | null + escrow_id_hash: string + } + deployment_ready: boolean + reason: string | null + } + links: { + verifier_package: string + verifier_proof: string + escrow_settlement: string + deployment_registry: string + } + notes: string[] +} + +export interface EvaluatorDeploymentBundleView { + standard: 'djd-evaluator-deploy-bundle-v1' + network: EvaluatorDeploymentPlanView['network'] + verdict_id: string + artifacts: { + available: boolean + compiler: EvaluatorArtifactPackageView['compiler'] + verifier: EvaluatorArtifactPackageView['contracts'][number] | null + escrow: EvaluatorArtifactPackageView['contracts'][number] | null + } + deployment: { + order: Array<'verifier' | 'escrow'> + verifier: { + action: 'deploy' | 'use_existing' + contract: typeof DJD_EVALUATOR_VERDICT_VERIFIER_CONTRACT + current_address: string | null + constructor: EvaluatorDeploymentPlanView['verifier']['constructor'] + deployment_ready: boolean + reason: string | null + } + escrow: { + action: 'deploy' + contract: typeof DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_CONTRACT + constructor: { + verifier: string | null + verifier_source: 'existing' | 'deployment_output' + provider: string + counterparty: string + escrow_id: string | null + escrow_id_hash: string + } + deployment_ready: boolean + reason: string | null + } + } + links: EvaluatorDeploymentPlanView['links'] & { + artifact_package: string + bundle: string + } + notes: string[] +} + +export interface EvaluatorNetworkCatalogView { + standard: 'djd-evaluator-network-catalog-v1' + default_network: EvaluatorVerifierPackageView['network'] + supported_networks: Array< + EvaluatorVerifierPackageView['network'] & { + explorer: { + name: string + base_url: string + } + deployment: { + rpc_env_var: string + bundle_param: string + verifier_package: string + deployment_registry: string + } + } + > + signing: { + active_signer: ReturnType + } + artifacts: { + available: boolean + } +} + +export function getEvaluatorVerifierPackageView( + network: EvaluatorNetworkConfig = getDefaultEvaluatorNetwork(), +): EvaluatorVerifierPackageView { + const signerStatus = getEvaluatorAttestationSignerStatus() + + return { + standard: 'djd-evaluator-verifier-package-v1', + network: toNetworkView(network), + signing: { + scheme: 'eip712', + primary_type: EVALUATOR_VERDICT_PRIMARY_TYPE, + domain: buildEvaluatorVerdictDomain(network.chainId), + types: EVALUATOR_VERDICT_TYPES, + active_signer: signerStatus, + }, + contracts: { + callback_interface: { + contract: DJD_EVALUATOR_ORACLE_CALLBACK_INTERFACE, + function: DJD_EVALUATOR_ORACLE_CALLBACK_FUNCTION, + signature: DJD_EVALUATOR_ORACLE_CALLBACK_SIGNATURE, + selector: DJD_EVALUATOR_ORACLE_CALLBACK_SELECTOR, + abi: toJsonAbi(DJD_EVALUATOR_ORACLE_CALLBACK_ABI), + }, + verifier: { + contract: DJD_EVALUATOR_VERDICT_VERIFIER_CONTRACT, + constructor: { + initial_signer: signerStatus.address, + }, + methods: { + hash_verdict: { + signature: DJD_EVALUATOR_VERDICT_VERIFIER_SIGNATURES.hashVerdict, + selector: DJD_EVALUATOR_VERDICT_VERIFIER_SELECTORS.hashVerdict, + }, + verify_verdict: { + signature: DJD_EVALUATOR_VERDICT_VERIFIER_SIGNATURES.verifyVerdict, + selector: DJD_EVALUATOR_VERDICT_VERIFIER_SELECTORS.verifyVerdict, + }, + verify_digest: { + signature: DJD_EVALUATOR_VERDICT_VERIFIER_SIGNATURES.verifyDigest, + selector: DJD_EVALUATOR_VERDICT_VERIFIER_SELECTORS.verifyDigest, + }, + set_oracle_signer: { + signature: DJD_EVALUATOR_VERDICT_VERIFIER_SIGNATURES.setOracleSigner, + selector: DJD_EVALUATOR_VERDICT_VERIFIER_SELECTORS.setOracleSigner, + }, + transfer_ownership: { + signature: DJD_EVALUATOR_VERDICT_VERIFIER_SIGNATURES.transferOwnership, + selector: DJD_EVALUATOR_VERDICT_VERIFIER_SELECTORS.transferOwnership, + }, + }, + abi: toJsonAbi(DJD_EVALUATOR_VERDICT_VERIFIER_ABI), + }, + settlement_example: { + contract: DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_CONTRACT, + function: DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_FUNCTION, + signature: DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_SIGNATURE, + selector: DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_SELECTOR, + constructor: { + verifier: signerStatus.address, + provider: 'wallet_from_verdict', + counterparty: 'counterparty_wallet_from_verdict_or_zero', + escrow_id: 'escrow_id_from_verdict', + escrow_id_hash: 'keccak256(escrow_id)', + }, + abi: toJsonAbi(DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_ABI), + }, + sources: CONTRACT_SOURCES, + }, + endpoints: { + oracle_verdict: buildPublicUrl(`/v1/score/evaluator/oracle?wallet=0x...&network=${network.key}`), + verdict_record: buildPublicUrl('/v1/score/evaluator/verdict?id=verdict_...'), + callback_calldata: buildPublicUrl(`/v1/score/evaluator/callback?id=verdict_...&network=${network.key}`), + deployment_registry: buildPublicUrl(`/v1/score/evaluator/deployments?network=${network.key}`), + artifact_package: buildPublicUrl('/v1/score/evaluator/artifacts'), + verifier_proof: buildPublicUrl(`/v1/score/evaluator/proof?id=verdict_...&network=${network.key}`), + escrow_settlement: buildPublicUrl(`/v1/score/evaluator/escrow?id=verdict_...&network=${network.key}`), + deploy_plan: buildPublicUrl(`/v1/score/evaluator/deploy?id=verdict_...&network=${network.key}`), + docs: buildPublicUrl('/docs'), + }, + notes: [ + `Deploy DJDEvaluatorVerdictVerifier on ${network.chainName} with the active DJD oracle signer address as the constructor argument.`, + 'Use /v1/score/evaluator/oracle or /v1/score/evaluator/verdict to fetch the full typed verdict before calling verifyVerdict onchain.', + 'Use DJDEvaluatorEscrowSettlementExample as a reference consumer that verifies the signed verdict before recording release, review, dispute, or reject state.', + 'Use /v1/score/evaluator/callback as a relay helper only; it carries compact settlement fields and the attestation digest, not the full EIP-712 preimage.', + 'Issue the oracle verdict for the same target network that will verify it onchain. Base and Base Sepolia use different EIP-712 chain ids.', + signerStatus.configured + ? 'Signed evaluator verdicts are currently available when the oracle signer remains configured.' + : 'Evaluator verdicts remain unsigned until ORACLE_SIGNER_PRIVATE_KEY or PUBLISHER_PRIVATE_KEY is configured.', + ], + } +} + +export function getEvaluatorNetworkCatalogView(): EvaluatorNetworkCatalogView { + const signerStatus = getEvaluatorAttestationSignerStatus() + const artifacts = getEvaluatorArtifactPackageView() + const defaultNetwork = getDefaultEvaluatorNetwork() + + return { + standard: 'djd-evaluator-network-catalog-v1', + default_network: toNetworkView(defaultNetwork), + supported_networks: listEvaluatorNetworks().map((network) => ({ + ...toNetworkView(network), + explorer: { + name: network.explorer.name, + base_url: network.explorer.baseUrl, + }, + deployment: { + rpc_env_var: network.rpcEnvVar, + bundle_param: network.key, + verifier_package: buildPublicUrl(`/v1/score/evaluator/verifier?network=${network.key}`), + deployment_registry: buildPublicUrl(`/v1/score/evaluator/deployments?network=${network.key}`), + }, + })), + signing: { + active_signer: signerStatus, + }, + artifacts: { + available: artifacts.available, + }, + } +} + +export function getEvaluatorVerifierProofView(params: { + rawVerdictId: string | undefined + rawTargetContract?: string | undefined + rawNetwork?: string | undefined +}): EvaluatorServiceResult { + const storedVerdict = getEvaluatorVerdictRecord(params.rawVerdictId) + if (!storedVerdict.ok) { + return storedVerdict + } + + const networkOutcome = resolveStoredVerdictNetwork(storedVerdict.data, params.rawNetwork) + if (!networkOutcome.ok) { + return networkOutcome + } + + const targetContract = + params.rawTargetContract === undefined || params.rawTargetContract === '' + ? null + : normalizeWallet(params.rawTargetContract) + if (params.rawTargetContract && !targetContract) { + return { + ok: false, + code: 'invalid_wallet', + message: 'Invalid or missing target_contract address', + status: 400, + } + } + + const verdict = storedVerdict.data + const targetResolution = resolveProofTargetContract(networkOutcome.data, targetContract) + const verdictMessage = verdict.attestation.typed_data.message + const isSigned = verdict.attestation.status === 'signed' && verdict.attestation.signature !== null + const calldata = isSigned + ? encodeEvaluatorVerdictVerification({ + verdict: { + verdictId: verdictMessage.verdictId, + wallet: verdictMessage.wallet, + counterpartyWallet: verdictMessage.counterpartyWallet, + escrowId: verdictMessage.escrowId, + decision: verdictMessage.decision, + recommendation: verdictMessage.recommendation, + approved: verdictMessage.approved, + confidence: verdictMessage.confidence, + agentScoreProvider: verdictMessage.agentScoreProvider, + scoreModelVersion: verdictMessage.scoreModelVersion, + certificationValid: verdictMessage.certificationValid, + certificationTier: verdictMessage.certificationTier, + riskLevel: verdictMessage.riskLevel, + riskScore: verdictMessage.riskScore, + forensicTraceId: verdictMessage.forensicTraceId, + packetHash: verdictMessage.packetHash, + generatedAt: verdictMessage.generatedAt, + }, + signature: verdict.attestation.signature as `0x${string}`, + }) + : null + + return { + ok: true, + data: { + standard: 'djd-evaluator-verifier-proof-v1', + ready: isSigned, + reason: isSigned ? null : 'verdict_attestation_unsigned', + verdict_id: verdict.verdict_id, + verifier: { + contract: DJD_EVALUATOR_VERDICT_VERIFIER_CONTRACT, + function: DJD_EVALUATOR_VERDICT_VERIFIER_FUNCTION, + selector: DJD_EVALUATOR_VERDICT_VERIFIER_SELECTORS.verifyVerdict, + chain_id: networkOutcome.data.chainId, + }, + attestation: { + status: verdict.attestation.status, + signer: verdict.attestation.signer, + digest: verdict.attestation.digest, + signature: verdict.attestation.signature, + scheme: verdict.attestation.scheme, + }, + verdict: verdictMessage, + call: { + selector: isSigned ? DJD_EVALUATOR_VERDICT_VERIFIER_SELECTORS.verifyVerdict : null, + calldata, + args: { + verdict: verdictMessage, + signature: verdict.attestation.signature, + }, + }, + transaction: { + to: targetResolution.contract_address, + data: calldata, + value: '0', + }, + resolution: targetResolution, + links: { + verdict_record: buildPublicUrl(`/v1/score/evaluator/verdict?id=${encodeURIComponent(verdict.verdict_id)}`), + verifier_package: buildPublicUrl(`/v1/score/evaluator/verifier?network=${networkOutcome.data.key}`), + deployment_registry: buildPublicUrl(`/v1/score/evaluator/deployments?network=${networkOutcome.data.key}`), + }, + }, + } +} + +export function getEvaluatorEscrowSettlementView(params: { + rawVerdictId: string | undefined + rawEscrowContract?: string | undefined + rawNetwork?: string | undefined +}): EvaluatorServiceResult { + const storedVerdict = getEvaluatorVerdictRecord(params.rawVerdictId) + if (!storedVerdict.ok) { + return storedVerdict + } + + const networkOutcome = resolveStoredVerdictNetwork(storedVerdict.data, params.rawNetwork) + if (!networkOutcome.ok) { + return networkOutcome + } + + const escrowContract = + params.rawEscrowContract === undefined || params.rawEscrowContract === '' + ? null + : normalizeWallet(params.rawEscrowContract) + if (params.rawEscrowContract && !escrowContract) { + return { + ok: false, + code: 'invalid_wallet', + message: 'Invalid or missing escrow_contract address', + status: 400, + } + } + + const verdict = storedVerdict.data + const escrowResolution = resolveEscrowTargetContract(networkOutcome.data, escrowContract) + const verdictMessage = verdict.attestation.typed_data.message + const isSigned = verdict.attestation.status === 'signed' && verdict.attestation.signature !== null + const calldata = isSigned + ? encodeEvaluatorEscrowSettlement({ + verdict: { + verdictId: verdictMessage.verdictId, + wallet: verdictMessage.wallet, + counterpartyWallet: verdictMessage.counterpartyWallet, + escrowId: verdictMessage.escrowId, + decision: verdictMessage.decision, + recommendation: verdictMessage.recommendation, + approved: verdictMessage.approved, + confidence: verdictMessage.confidence, + agentScoreProvider: verdictMessage.agentScoreProvider, + scoreModelVersion: verdictMessage.scoreModelVersion, + certificationValid: verdictMessage.certificationValid, + certificationTier: verdictMessage.certificationTier, + riskLevel: verdictMessage.riskLevel, + riskScore: verdictMessage.riskScore, + forensicTraceId: verdictMessage.forensicTraceId, + packetHash: verdictMessage.packetHash, + generatedAt: verdictMessage.generatedAt, + }, + signature: verdict.attestation.signature as `0x${string}`, + }) + : null + + const outcome = + verdict.recommendation === 'release' && verdict.approved + ? 'release' + : verdict.recommendation === 'manual_review' + ? 'manual_review' + : verdict.recommendation === 'dispute' + ? 'dispute' + : 'reject' + + return { + ok: true, + data: { + standard: 'djd-evaluator-escrow-settlement-v1', + ready: isSigned, + reason: isSigned ? null : 'verdict_attestation_unsigned', + verdict_id: verdict.verdict_id, + escrow: { + contract: DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_CONTRACT, + function: DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_FUNCTION, + selector: DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_SELECTOR, + chain_id: networkOutcome.data.chainId, + }, + verifier: { + contract: DJD_EVALUATOR_VERDICT_VERIFIER_CONTRACT, + function: DJD_EVALUATOR_VERDICT_VERIFIER_FUNCTION, + }, + attestation: { + status: verdict.attestation.status, + signer: verdict.attestation.signer, + digest: verdict.attestation.digest, + signature: verdict.attestation.signature, + scheme: verdict.attestation.scheme, + }, + settlement: { + recommendation: verdict.recommendation, + approved: verdict.approved, + outcome, + release_authorized: outcome === 'release', + }, + verdict: verdictMessage, + call: { + selector: isSigned ? DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_SELECTOR : null, + calldata, + args: { + verdict: verdictMessage, + signature: verdict.attestation.signature, + }, + }, + transaction: { + to: escrowResolution.contract_address, + data: calldata, + value: '0', + }, + resolution: escrowResolution, + links: { + verdict_record: buildPublicUrl(`/v1/score/evaluator/verdict?id=${encodeURIComponent(verdict.verdict_id)}`), + verifier_package: buildPublicUrl(`/v1/score/evaluator/verifier?network=${networkOutcome.data.key}`), + verifier_proof: buildPublicUrl( + `/v1/score/evaluator/proof?id=${encodeURIComponent(verdict.verdict_id)}&network=${networkOutcome.data.key}`, + ), + deployment_registry: buildPublicUrl(`/v1/score/evaluator/deployments?network=${networkOutcome.data.key}`), + }, + }, + } +} + +export function getEvaluatorDeploymentPlanView(params: { + rawVerdictId: string | undefined + rawVerifierContract?: string | undefined + rawNetwork?: string | undefined +}): EvaluatorServiceResult { + const storedVerdict = getEvaluatorVerdictRecord(params.rawVerdictId) + if (!storedVerdict.ok) { + return storedVerdict + } + + const networkOutcome = resolveStoredVerdictNetwork(storedVerdict.data, params.rawNetwork) + if (!networkOutcome.ok) { + return networkOutcome + } + + const explicitVerifierContract = + params.rawVerifierContract === undefined || params.rawVerifierContract === '' + ? null + : normalizeWallet(params.rawVerifierContract) + if (params.rawVerifierContract && !explicitVerifierContract) { + return { + ok: false, + code: 'invalid_wallet', + message: 'Invalid or missing verifier_contract address', + status: 400, + } + } + + const signerStatus = getEvaluatorAttestationSignerStatus() + const verdict = storedVerdict.data + const publishedDeployment = resolvePublishedDeployment(networkOutcome.data) + const publishedVerifierContract = publishedDeployment.deployment?.contracts.verifier.address ?? null + const verifierContract = explicitVerifierContract ?? publishedVerifierContract + const verifierSource = explicitVerifierContract + ? 'explicit' + : publishedVerifierContract + ? 'published_registry' + : 'manual_required' + + return { + ok: true, + data: { + standard: 'djd-evaluator-deploy-plan-v1', + network: toNetworkView(networkOutcome.data), + verdict_id: verdict.verdict_id, + verifier: { + contract: DJD_EVALUATOR_VERDICT_VERIFIER_CONTRACT, + constructor: { + initial_signer: signerStatus.address, + }, + deployment_ready: signerStatus.configured, + reason: signerStatus.reason, + }, + escrow: { + contract: DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_CONTRACT, + constructor: { + verifier: verifierContract, + verifier_source: verifierSource, + provider: verdict.wallet, + counterparty: verdict.counterparty_wallet ?? ZERO_ADDRESS, + escrow_id: verdict.escrow_id, + escrow_id_hash: buildEscrowIdHash(verdict.escrow_id), + }, + deployment_ready: verifierContract !== null, + reason: verifierContract ? null : 'verifier_contract_required', + }, + links: { + verifier_package: buildPublicUrl(`/v1/score/evaluator/verifier?network=${networkOutcome.data.key}`), + verifier_proof: buildPublicUrl( + `/v1/score/evaluator/proof?id=${encodeURIComponent(verdict.verdict_id)}&network=${networkOutcome.data.key}`, + ), + escrow_settlement: buildPublicUrl( + `/v1/score/evaluator/escrow?id=${encodeURIComponent(verdict.verdict_id)}&network=${networkOutcome.data.key}`, + ), + deployment_registry: buildPublicUrl(`/v1/score/evaluator/deployments?network=${networkOutcome.data.key}`), + }, + notes: [ + 'Deploy DJDEvaluatorVerdictVerifier first so the escrow consumer can reference a concrete verifier address.', + 'Use the provider, counterparty, and escrow id hash from this plan when deploying DJDEvaluatorEscrowSettlementExample.', + `This deployment plan is locked to ${networkOutcome.data.chainName}. Use a verdict issued for the same network when verifying onchain.`, + verifierSource === 'published_registry' + ? 'A published verifier deployment was found for this network, so the escrow consumer can reuse it without a manual verifier_contract parameter.' + : verifierSource === 'explicit' + ? 'The escrow consumer is configured to use the explicit verifier_contract override supplied in this request.' + : 'No published verifier deployment was found for this network, so verifier_contract is still required for escrow deployment planning.', + signerStatus.configured + ? 'The verifier constructor can use the currently active DJD oracle signer address.' + : 'Set ORACLE_SIGNER_PRIVATE_KEY or PUBLISHER_PRIVATE_KEY before relying on signed verdict verification in production.', + ], + }, + } +} + +export function getEvaluatorDeploymentBundleView(params: { + rawVerdictId: string | undefined + rawVerifierContract?: string | undefined + rawNetwork?: string | undefined +}): EvaluatorServiceResult { + const plan = getEvaluatorDeploymentPlanView(params) + if (!plan.ok) { + return plan + } + + const artifacts = getEvaluatorArtifactPackageView() + const verifierArtifact = getEvaluatorArtifactContractEntry(DJD_EVALUATOR_VERDICT_VERIFIER_CONTRACT) + const escrowArtifact = getEvaluatorArtifactContractEntry(DJD_EVALUATOR_ESCROW_SETTLEMENT_EXAMPLE_CONTRACT) + const usingExistingVerifier = plan.data.escrow.constructor.verifier !== null + + return { + ok: true, + data: { + standard: 'djd-evaluator-deploy-bundle-v1', + network: plan.data.network, + verdict_id: plan.data.verdict_id, + artifacts: { + available: artifacts.available, + compiler: artifacts.compiler, + verifier: verifierArtifact, + escrow: escrowArtifact, + }, + deployment: { + order: ['verifier', 'escrow'], + verifier: { + action: usingExistingVerifier ? 'use_existing' : 'deploy', + contract: plan.data.verifier.contract, + current_address: plan.data.escrow.constructor.verifier, + constructor: plan.data.verifier.constructor, + deployment_ready: usingExistingVerifier ? true : plan.data.verifier.deployment_ready, + reason: usingExistingVerifier ? null : plan.data.verifier.reason, + }, + escrow: { + action: 'deploy', + contract: plan.data.escrow.contract, + constructor: { + ...plan.data.escrow.constructor, + verifier_source: usingExistingVerifier ? 'existing' : 'deployment_output', + }, + deployment_ready: artifacts.available && escrowArtifact !== null, + reason: !artifacts.available + ? 'artifact_package_unavailable' + : escrowArtifact === null + ? 'escrow_artifact_missing' + : null, + }, + }, + links: { + ...plan.data.links, + artifact_package: buildPublicUrl('/v1/score/evaluator/artifacts'), + bundle: buildPublicUrl( + `/v1/score/evaluator/deploy/bundle?id=${encodeURIComponent(plan.data.verdict_id)}&network=${encodeURIComponent(plan.data.network.key)}${ + plan.data.escrow.constructor.verifier + ? `&verifier_contract=${encodeURIComponent(plan.data.escrow.constructor.verifier)}` + : '' + }`, + ), + }, + notes: [ + 'Use this bundle when a deploy tool needs both the constructor plan and the compiled artifact payloads in one response.', + usingExistingVerifier + ? 'The bundle is configured to reuse the provided verifier contract address and deploy only the escrow consumer.' + : 'The bundle is configured to deploy a new verifier first and then feed that address into the escrow consumer deployment.', + ...plan.data.notes, + ], + }, + } +} diff --git a/src/services/evaluatorDeploymentRegistryService.ts b/src/services/evaluatorDeploymentRegistryService.ts new file mode 100644 index 0000000..d5a05df --- /dev/null +++ b/src/services/evaluatorDeploymentRegistryService.ts @@ -0,0 +1,548 @@ +import { existsSync, readFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import { fileURLToPath } from 'node:url' +import { buildPublicUrl } from '../config/public.js' +import { + type EvaluatorNetworkConfig, + findEvaluatorNetworkByChainId, + getDefaultEvaluatorNetwork, + listEvaluatorNetworks, + resolveEvaluatorNetwork, +} from './evaluatorNetworkService.js' +import type { EvaluatorServiceResult } from './evaluatorService.js' + +const __dirname = dirname(fileURLToPath(import.meta.url)) +const DEFAULT_REGISTRY_PATH = join(__dirname, '..', '..', 'data', 'evaluator-deployments.json') + +interface EvaluatorDeploymentRegistryEntryDocument { + published_at?: string | null + network?: { + key?: string + chain_id?: number + chain_name?: string + caip2?: string + environment?: string + } + verdict_id?: string | null + deployer?: string | null + contracts?: { + verifier?: { + contract?: string | null + address?: string | null + tx_hash?: string | null + action?: string | null + } + escrow?: { + contract?: string | null + address?: string | null + tx_hash?: string | null + action?: string | null + } + } + verification?: { + oracle_signer?: string | null + escrow_verifier?: string | null + escrow_provider?: string | null + escrow_counterparty?: string | null + escrow_id_hash?: string | null + } + inputs?: { + network_key?: string | null + provider?: string | null + counterparty?: string | null + escrow_id?: string | null + } + explorer?: { + verifier_address?: string | null + verifier_transaction?: string | null + escrow_address?: string | null + escrow_transaction?: string | null + } | null + links?: Record + checks?: { + preflight?: boolean | null + verified?: boolean | null + smoked?: boolean | null + health?: boolean | null + staged?: boolean | null + } +} + +interface EvaluatorDeploymentRegistryDocument { + standard?: string + updated_at?: string | null + deployments?: Record +} + +export interface PublishedEvaluatorDeployment { + published_at: string | null + network: { + key: string | null + chain_id: number | null + chain_name: string | null + caip2: string | null + environment: string | null + } + verdict_id: string | null + deployer: string | null + contracts: { + verifier: { + contract: string | null + address: string | null + tx_hash: string | null + action: string | null + } + escrow: { + contract: string | null + address: string | null + tx_hash: string | null + action: string | null + } + } + verification: { + oracle_signer: string | null + escrow_verifier: string | null + escrow_provider: string | null + escrow_counterparty: string | null + escrow_id_hash: string | null + } + inputs: { + provider: string | null + counterparty: string | null + escrow_id: string | null + } + checks: { + preflight: boolean | null + verified: boolean | null + smoked: boolean | null + health: boolean | null + staged: boolean | null + } + explorer: EvaluatorDeploymentRegistryEntryDocument['explorer'] + links: { + verifier_package: string + deployment_registry: string + verifier_proof: string | null + escrow_settlement: string | null + deploy_bundle: string | null + } +} + +export interface EvaluatorDeploymentRegistryView { + standard: 'djd-evaluator-deployments-v1' + registry: { + available: boolean + updated_at: string | null + deployment_count: number + error: string | null + } + filter: { + network: string | null + } + networks: Array<{ + key: string + chain_id: number + chain_name: string + caip2: string + environment: string + explorer: { + name: string + base_url: string + } + rpc_env_var: string + deployed: boolean + deployment: null | PublishedEvaluatorDeployment + }> +} + +export interface EvaluatorDeploymentPromotionBundleView { + standard: 'djd-evaluator-promotion-bundle-v1' + ready: boolean + reason: 'deployment_not_published' | null + source: 'published_registry' + registry: { + available: boolean + updated_at: string | null + error: string | null + } + network: { + key: string + chain_id: number + chain_name: string + caip2: string + environment: string + rpc_env_var: string + explorer: { + name: string + base_url: string + } + } + deployment: PublishedEvaluatorDeployment | null + outputs: null | { + variables: Record + generic: Record + network_scoped: Record + dotenv: string + shell: string + github_output: string + } +} + +function invalidNetworkResult(rawNetwork: string | undefined): EvaluatorServiceResult { + return { + ok: false, + code: 'invalid_network', + message: 'Invalid or unsupported network', + status: 400, + details: { + network: rawNetwork ?? null, + supported_networks: listEvaluatorNetworks().map((network) => network.key), + }, + } +} + +function getRegistryPath(): string { + const configuredPath = process.env.DJD_EVALUATOR_DEPLOYMENTS_PATH + return typeof configuredPath === 'string' && configuredPath.trim().length > 0 + ? configuredPath.trim() + : DEFAULT_REGISTRY_PATH +} + +function sanitizeNetworkEnvSegment(value: string): string { + return value + .trim() + .replace(/[^a-zA-Z0-9]+/g, '_') + .replace(/^_+|_+$/g, '') + .toUpperCase() +} + +function setVariable(target: Record, key: string, value: string | number | null | undefined): void { + if (value === null || value === undefined || value === '') { + return + } + + target[key] = String(value) +} + +function escapeDotenvValue(value: string): string { + if (/^[A-Za-z0-9_./:-]+$/.test(value)) { + return value + } + + return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n')}"` +} + +function escapeShellValue(value: string): string { + return `'${value.replace(/'/g, `'"'"'`)}'` +} + +function formatKeyValueLines( + variables: Record, + formatter: (key: string, value: string) => string, +): string { + return `${Object.entries(variables) + .map(([key, value]) => formatter(key, value)) + .join('\n')}\n` +} + +function buildPromotionOutputs( + network: EvaluatorNetworkConfig, + deployment: PublishedEvaluatorDeployment, +): EvaluatorDeploymentPromotionBundleView['outputs'] { + const generic: Record = {} + const networkScoped: Record = {} + const networkSegment = sanitizeNetworkEnvSegment(network.key) + const apiBaseUrl = buildPublicUrl() + + setVariable(generic, 'DJD_NETWORK', network.key) + setVariable(generic, 'DJD_CHAIN_ID', network.chainId) + setVariable(generic, 'DJD_CHAIN_NAME', network.chainName) + setVariable(generic, 'DJD_CAIP2', network.caip2) + setVariable(generic, 'DJD_ENVIRONMENT', network.environment) + setVariable(generic, 'DJD_VERDICT_ID', deployment.verdict_id) + setVariable(generic, 'DJD_DEPLOYMENT_SOURCE', 'published_registry') + setVariable(generic, 'DJD_DEPLOYMENT_SOURCE_LOCATION', deployment.links.deployment_registry) + setVariable(generic, 'DJD_DEPLOYER_ADDRESS', deployment.deployer) + setVariable(generic, 'DJD_VERIFIER_CONTRACT', deployment.contracts.verifier.address) + setVariable(generic, 'DJD_ESCROW_CONTRACT', deployment.contracts.escrow.address) + setVariable(generic, 'DJD_ORACLE_SIGNER', deployment.verification.oracle_signer) + setVariable(generic, 'DJD_ESCROW_PROVIDER', deployment.inputs.provider ?? deployment.verification.escrow_provider) + setVariable( + generic, + 'DJD_ESCROW_COUNTERPARTY', + deployment.inputs.counterparty ?? deployment.verification.escrow_counterparty, + ) + setVariable(generic, 'DJD_ESCROW_ID', deployment.inputs.escrow_id) + setVariable(generic, 'DJD_ESCROW_ID_HASH', deployment.verification.escrow_id_hash) + setVariable(generic, 'DJD_API_BASE_URL', apiBaseUrl) + setVariable(generic, 'DJD_VERIFIER_PACKAGE_URL', deployment.links.verifier_package) + setVariable(generic, 'DJD_VERIFIER_PROOF_URL', deployment.links.verifier_proof) + setVariable(generic, 'DJD_ESCROW_SETTLEMENT_URL', deployment.links.escrow_settlement) + setVariable(generic, 'DJD_DEPLOY_BUNDLE_URL', deployment.links.deploy_bundle) + setVariable(generic, 'DJD_DEPLOYMENTS_URL', deployment.links.deployment_registry) + setVariable(generic, 'DJD_ARTIFACT_PACKAGE_URL', buildPublicUrl('/v1/score/evaluator/artifacts')) + setVariable(generic, 'DJD_VERIFIER_EXPLORER_URL', deployment.explorer?.verifier_address ?? null) + setVariable(generic, 'DJD_VERIFIER_TX_URL', deployment.explorer?.verifier_transaction ?? null) + setVariable(generic, 'DJD_ESCROW_EXPLORER_URL', deployment.explorer?.escrow_address ?? null) + setVariable(generic, 'DJD_ESCROW_TX_URL', deployment.explorer?.escrow_transaction ?? null) + + setVariable(networkScoped, `DJD_${networkSegment}_NETWORK`, network.key) + setVariable(networkScoped, `DJD_${networkSegment}_CHAIN_ID`, network.chainId) + setVariable(networkScoped, `DJD_${networkSegment}_VERDICT_ID`, deployment.verdict_id) + setVariable(networkScoped, `DJD_${networkSegment}_VERIFIER_CONTRACT`, deployment.contracts.verifier.address) + setVariable(networkScoped, `DJD_${networkSegment}_ESCROW_CONTRACT`, deployment.contracts.escrow.address) + setVariable(networkScoped, `DJD_${networkSegment}_ORACLE_SIGNER`, deployment.verification.oracle_signer) + setVariable(networkScoped, `DJD_${networkSegment}_VERIFIER_PACKAGE_URL`, deployment.links.verifier_package) + setVariable(networkScoped, `DJD_${networkSegment}_VERIFIER_PROOF_URL`, deployment.links.verifier_proof) + setVariable(networkScoped, `DJD_${networkSegment}_ESCROW_SETTLEMENT_URL`, deployment.links.escrow_settlement) + setVariable(networkScoped, `DJD_${networkSegment}_DEPLOY_BUNDLE_URL`, deployment.links.deploy_bundle) + setVariable(networkScoped, `DJD_${networkSegment}_DEPLOYMENTS_URL`, deployment.links.deployment_registry) + setVariable( + networkScoped, + `DJD_${networkSegment}_ARTIFACT_PACKAGE_URL`, + buildPublicUrl('/v1/score/evaluator/artifacts'), + ) + + const variables = { + ...generic, + ...networkScoped, + } + + return { + variables, + generic, + network_scoped: networkScoped, + dotenv: formatKeyValueLines(variables, (key, value) => `${key}=${escapeDotenvValue(value)}`), + shell: formatKeyValueLines(variables, (key, value) => `export ${key}=${escapeShellValue(value)}`), + github_output: formatKeyValueLines(variables, (key, value) => `${key}=${value}`), + } +} + +function loadRegistryDocument(): { + available: boolean + updated_at: string | null + deployments: Record + error: string | null +} { + const filePath = getRegistryPath() + if (!existsSync(filePath)) { + return { + available: false, + updated_at: null, + deployments: {}, + error: null, + } + } + + try { + const parsed = JSON.parse(readFileSync(filePath, 'utf8')) as EvaluatorDeploymentRegistryDocument + if (parsed.standard !== 'djd-evaluator-deployment-registry-v1') { + return { + available: false, + updated_at: null, + deployments: {}, + error: 'registry_invalid_standard', + } + } + + return { + available: true, + updated_at: parsed.updated_at ?? null, + deployments: parsed.deployments ?? {}, + error: null, + } + } catch { + return { + available: false, + updated_at: null, + deployments: {}, + error: 'registry_unreadable', + } + } +} + +function resolveDeploymentEntryForNetwork( + deployments: Record, + network: EvaluatorNetworkConfig, +): EvaluatorDeploymentRegistryEntryDocument | null { + const direct = deployments[network.key] + if (direct) { + return direct + } + + return ( + Object.values(deployments).find((entry) => { + if (entry.network?.key === network.key) { + return true + } + + const entryNetwork = findEvaluatorNetworkByChainId(entry.network?.chain_id ?? null) + return entryNetwork?.key === network.key + }) ?? null + ) +} + +function toDeploymentView( + entry: EvaluatorDeploymentRegistryEntryDocument, + network: EvaluatorNetworkConfig, +): PublishedEvaluatorDeployment { + return { + published_at: entry.published_at ?? null, + network: { + key: entry.network?.key ?? network.key, + chain_id: entry.network?.chain_id ?? network.chainId, + chain_name: entry.network?.chain_name ?? network.chainName, + caip2: entry.network?.caip2 ?? network.caip2, + environment: entry.network?.environment ?? network.environment, + }, + verdict_id: entry.verdict_id ?? null, + deployer: entry.deployer ?? null, + contracts: { + verifier: { + contract: entry.contracts?.verifier?.contract ?? null, + address: entry.contracts?.verifier?.address ?? null, + tx_hash: entry.contracts?.verifier?.tx_hash ?? null, + action: entry.contracts?.verifier?.action ?? null, + }, + escrow: { + contract: entry.contracts?.escrow?.contract ?? null, + address: entry.contracts?.escrow?.address ?? null, + tx_hash: entry.contracts?.escrow?.tx_hash ?? null, + action: entry.contracts?.escrow?.action ?? null, + }, + }, + verification: { + oracle_signer: entry.verification?.oracle_signer ?? null, + escrow_verifier: entry.verification?.escrow_verifier ?? null, + escrow_provider: entry.verification?.escrow_provider ?? null, + escrow_counterparty: entry.verification?.escrow_counterparty ?? null, + escrow_id_hash: entry.verification?.escrow_id_hash ?? null, + }, + inputs: { + provider: entry.inputs?.provider ?? null, + counterparty: entry.inputs?.counterparty ?? null, + escrow_id: entry.inputs?.escrow_id ?? null, + }, + checks: { + preflight: entry.checks?.preflight ?? null, + verified: entry.checks?.verified ?? null, + smoked: entry.checks?.smoked ?? null, + health: entry.checks?.health ?? null, + staged: entry.checks?.staged ?? null, + }, + explorer: entry.explorer ?? null, + links: { + verifier_package: buildPublicUrl(`/v1/score/evaluator/verifier?network=${network.key}`), + deployment_registry: buildPublicUrl(`/v1/score/evaluator/deployments?network=${network.key}`), + verifier_proof: entry.links?.verifier_proof ?? null, + escrow_settlement: entry.links?.escrow_settlement ?? null, + deploy_bundle: entry.links?.bundle ?? null, + }, + } +} + +export function getPublishedEvaluatorDeployment(network: EvaluatorNetworkConfig): { + registry: { + available: boolean + updated_at: string | null + error: string | null + } + deployment: PublishedEvaluatorDeployment | null +} { + const registry = loadRegistryDocument() + const entry = resolveDeploymentEntryForNetwork(registry.deployments, network) + + return { + registry: { + available: registry.available, + updated_at: registry.updated_at, + error: registry.error, + }, + deployment: entry ? toDeploymentView(entry, network) : null, + } +} + +export function getEvaluatorDeploymentRegistryView( + rawNetwork?: string | undefined, +): EvaluatorServiceResult { + const selectedNetwork = + rawNetwork === undefined || rawNetwork.trim() === '' ? null : resolveEvaluatorNetwork(rawNetwork) + if (rawNetwork !== undefined && rawNetwork.trim() !== '' && !selectedNetwork) { + return invalidNetworkResult(rawNetwork) + } + + const networks = selectedNetwork ? [selectedNetwork] : listEvaluatorNetworks() + const registry = loadRegistryDocument() + + return { + ok: true, + data: { + standard: 'djd-evaluator-deployments-v1', + registry: { + available: registry.available, + updated_at: registry.updated_at, + deployment_count: Object.keys(registry.deployments).length, + error: registry.error, + }, + filter: { + network: selectedNetwork?.key ?? null, + }, + networks: networks.map((network) => { + const entry = resolveDeploymentEntryForNetwork(registry.deployments, network) + return { + key: network.key, + chain_id: network.chainId, + chain_name: network.chainName, + caip2: network.caip2, + environment: network.environment, + explorer: { + name: network.explorer.name, + base_url: network.explorer.baseUrl, + }, + rpc_env_var: network.rpcEnvVar, + deployed: entry !== null, + deployment: entry ? toDeploymentView(entry, network) : null, + } + }), + }, + } +} + +export function getEvaluatorDeploymentPromotionBundleView( + rawNetwork?: string | undefined, +): EvaluatorServiceResult { + const network = + rawNetwork === undefined || rawNetwork.trim() === '' + ? getDefaultEvaluatorNetwork() + : resolveEvaluatorNetwork(rawNetwork) + if (!network) { + return invalidNetworkResult(rawNetwork) + } + + const published = getPublishedEvaluatorDeployment(network) + + return { + ok: true, + data: { + standard: 'djd-evaluator-promotion-bundle-v1', + ready: published.deployment !== null, + reason: published.deployment ? null : 'deployment_not_published', + source: 'published_registry', + registry: published.registry, + network: { + key: network.key, + chain_id: network.chainId, + chain_name: network.chainName, + caip2: network.caip2, + environment: network.environment, + rpc_env_var: network.rpcEnvVar, + explorer: { + name: network.explorer.name, + base_url: network.explorer.baseUrl, + }, + }, + deployment: published.deployment, + outputs: published.deployment ? buildPromotionOutputs(network, published.deployment) : null, + }, + } +} diff --git a/src/services/evaluatorNetworkService.ts b/src/services/evaluatorNetworkService.ts new file mode 100644 index 0000000..14dc6ff --- /dev/null +++ b/src/services/evaluatorNetworkService.ts @@ -0,0 +1,98 @@ +import type { Chain } from 'viem' +import { base, baseSepolia } from 'viem/chains' + +export type EvaluatorNetworkKey = 'base' | 'base-sepolia' + +export interface EvaluatorNetworkConfig { + key: EvaluatorNetworkKey + chainId: number + chainName: string + caip2: string + environment: 'mainnet' | 'testnet' + viemChain: Chain + explorer: { + name: 'BaseScan' + baseUrl: string + } + rpcEnvVar: string +} + +const EVALUATOR_NETWORKS: Record = { + base: { + key: 'base', + chainId: base.id, + chainName: base.name, + caip2: `eip155:${base.id}`, + environment: 'mainnet', + viemChain: base, + explorer: { + name: 'BaseScan', + baseUrl: 'https://basescan.org', + }, + rpcEnvVar: 'DJD_BASE_RPC_URL', + }, + 'base-sepolia': { + key: 'base-sepolia', + chainId: baseSepolia.id, + chainName: baseSepolia.name, + caip2: `eip155:${baseSepolia.id}`, + environment: 'testnet', + viemChain: baseSepolia, + explorer: { + name: 'BaseScan', + baseUrl: 'https://sepolia.basescan.org', + }, + rpcEnvVar: 'DJD_BASE_SEPOLIA_RPC_URL', + }, +} + +const NETWORK_ALIASES: Record = { + base: 'base', + 'base-mainnet': 'base', + 'eip155:8453': 'base', + '8453': 'base', + 'base-sepolia': 'base-sepolia', + base_sepolia: 'base-sepolia', + 'eip155:84532': 'base-sepolia', + '84532': 'base-sepolia', +} + +export function getDefaultEvaluatorNetwork(): EvaluatorNetworkConfig { + return EVALUATOR_NETWORKS.base +} + +export function listEvaluatorNetworks(): EvaluatorNetworkConfig[] { + return Object.values(EVALUATOR_NETWORKS) +} + +export function getEvaluatorNetworkByKey(key: EvaluatorNetworkKey): EvaluatorNetworkConfig { + return EVALUATOR_NETWORKS[key] +} + +export function findEvaluatorNetworkByChainId(chainId: number | undefined | null): EvaluatorNetworkConfig | null { + if (typeof chainId !== 'number' || !Number.isFinite(chainId)) { + return null + } + + return listEvaluatorNetworks().find((network) => network.chainId === chainId) ?? null +} + +export function resolveEvaluatorNetwork(rawNetwork: string | undefined): EvaluatorNetworkConfig | null { + if (rawNetwork === undefined || rawNetwork.trim() === '') { + return getDefaultEvaluatorNetwork() + } + + return NETWORK_ALIASES[rawNetwork.trim().toLowerCase()] + ? EVALUATOR_NETWORKS[NETWORK_ALIASES[rawNetwork.trim().toLowerCase()]] + : null +} + +export function getEvaluatorVerdictChainId(attestation: { + typed_data?: { + domain?: { + chainId?: number + } + } +}): number { + return attestation.typed_data?.domain?.chainId ?? getDefaultEvaluatorNetwork().chainId +} diff --git a/src/services/evaluatorService.ts b/src/services/evaluatorService.ts index 51426af..70549ba 100644 --- a/src/services/evaluatorService.ts +++ b/src/services/evaluatorService.ts @@ -1,15 +1,52 @@ +import { createHash, randomUUID } from 'node:crypto' import { buildPublicUrl } from '../config/public.js' -import { getActiveCertification, getRegistration } from '../db.js' -import { getRiskScore } from './riskService.js' +import { + buildEscrowIdHash, + DJD_DECISION_CODES, + DJD_EVALUATOR_ORACLE_CALLBACK_FUNCTION, + DJD_EVALUATOR_ORACLE_CALLBACK_INTERFACE, + DJD_RECOMMENDATION_CODES, + encodeEvaluatorOracleCallback, + ZERO_ADDRESS, +} from '../contracts/djdEvaluatorOracleCallback.js' +import { + countScoreHistory, + getActiveCertification, + getRegistration, + getEvaluatorVerdict as getStoredEvaluatorVerdict, + insertEvaluatorVerdict, + listEvaluatorVerdictsByWallet, + listFraudReportsByTarget, +} from '../db.js' +import { ErrorCodes } from '../errors.js' +import { normalizeWallet } from '../utils/walletUtils.js' +import { getCertificationTierByStoredValue, getDefaultCertificationTier } from './certificationTiers.js' +import { + buildEvaluatorVerdictAttestation, + buildEvaluatorVerdictTypedData, + type EvaluatorVerdictAttestationInput, + type EvaluatorVerdictAttestationView, +} from './evaluatorAttestationService.js' +import { + findEvaluatorNetworkByChainId, + getDefaultEvaluatorNetwork, + getEvaluatorVerdictChainId, + listEvaluatorNetworks, + resolveEvaluatorNetwork, +} from './evaluatorNetworkService.js' +import { getRiskScore, type RiskScoreView } from './riskService.js' type EvaluatorDecision = 'approve' | 'review' | 'reject' type EvaluatorCheckStatus = 'pass' | 'review' | 'fail' +type EvaluatorArtifactStatus = 'included' | 'recommended' | 'missing' +type EvaluatorArtifactCategory = 'score' | 'identity' | 'certification' | 'forensics' | 'market' +type EvaluatorRecommendation = 'release' | 'manual_review' | 'dispute' | 'reject' interface EvaluatorServiceError { ok: false code: string message: string - status: 400 + status: 400 | 404 details?: Record } @@ -20,6 +57,34 @@ interface EvaluatorServiceSuccess { export type EvaluatorServiceResult = EvaluatorServiceError | EvaluatorServiceSuccess +interface EvaluatorLinks { + evaluator_preview: string + full_score: string + risk: string + standards_document: string + certification_status: string + forensics_summary: string + forensics_timeline: string + forensics_reports: string + evidence_packet: string +} + +interface EvaluatorOracleLinks { + standards_document: string + certification_status: string + forensics_summary: string + evidence_packet: string + verdict_record: string + verdict_history: string +} + +interface EvaluatorCheckView { + key: string + label: string + status: EvaluatorCheckStatus + details: Record +} + export interface EvaluatorPreviewView { standard: 'erc-8183-evaluator-prototype' wallet: string @@ -54,17 +119,189 @@ export interface EvaluatorPreviewView { active_creator_stakes: number active_score_boost: number } - checks: Array<{ + checks: EvaluatorCheckView[] + links: EvaluatorLinks +} + +export interface EvaluatorEvidencePacketView { + standard: 'erc-8183-evaluator-evidence-prototype' + wallet: string + packet_id: string + packet_hash: string + generated_at: string + evaluator: { + decision: EvaluatorDecision + confidence: number + rationale: string + } + baseline: { + profile: 'djd-transactional-settlement-v1' + settlement_tier: string + score_floor: number + certification_floor: string + review_triggers: string[] + reject_triggers: string[] + } + evidence: { + score: EvaluatorPreviewView['score'] + certification: EvaluatorPreviewView['certification'] + risk: EvaluatorPreviewView['risk'] + market_signals: EvaluatorPreviewView['market_signals'] + checks: EvaluatorPreviewView['checks'] + forensics: { + score_history_entries: number + report_count: number + unique_reporters: number + total_penalty_applied: number + open_disputes: number + resolved_disputes: number + reason_breakdown: RiskScoreView['summary']['reason_breakdown'] + recent_reports: Array<{ + report_id: string + reason: string + created_at: string + penalty_applied: number + }> + } + } + artifacts: Array<{ key: string label: string - status: EvaluatorCheckStatus - details: Record + category: EvaluatorArtifactCategory + status: EvaluatorArtifactStatus + href: string + summary: string }> - links: { - full_score: string - risk: string - standards_document: string - certification_status: string + links: EvaluatorLinks +} + +export interface EvaluatorOracleView { + standard: 'erc-8183-evaluator-oracle-prototype' + verdict_id: string + wallet: string + counterparty_wallet: string | null + escrow_id: string | null + decision: EvaluatorDecision + approved: boolean + recommendation: EvaluatorRecommendation + confidence: number + agent_score_provider: number + score_model_version: string + certification_valid: boolean + certification_tier: string | null + risk_level: string + risk_score: number + sla_metrics: { + baseline_profile: 'djd-transactional-settlement-v1' + settlement_tier: string + score_floor: number + score_floor_passed: boolean + certification_floor: string + certification_floor_passed: boolean + risk_guardrail_passed: boolean + dispute_guardrail_passed: boolean + failed_checks: string[] + review_checks: string[] + } + forensic_trace_id: string + packet_hash: string + generated_at: string + attestation: EvaluatorVerdictAttestationView + links: EvaluatorOracleLinks +} + +export interface EvaluatorStoredVerdictView extends EvaluatorOracleView { + recorded_at: string +} + +export interface EvaluatorVerdictHistoryView { + standard: 'djd-evaluator-verdict-history-v1' + wallet: string + total: number + limit: number + summary: { + approvals: number + manual_review: number + disputes: number + rejects: number + } + items: Array<{ + verdict_id: string + recorded_at: string + decision: EvaluatorDecision + recommendation: EvaluatorRecommendation + approved: boolean + confidence: number + current_score: number + current_tier: string + risk_level: string + certification_valid: boolean + certification_tier: string | null + escrow_id: string | null + counterparty_wallet: string | null + forensic_trace_id: string + packet_hash: string + attestation_status: 'signed' | 'unsigned' + attestation_signer: string | null + }> +} + +export interface EvaluatorContractCallbackView { + standard: 'djd-evaluator-oracle-callback-v1' + ready: boolean + reason: string | null + verdict_id: string + interface: { + contract: 'IDJDEvaluatorOracleCallback' + function: 'receiveVerdict' + chain_id: number + } + verification: { + status: 'signed' | 'unsigned' + signer: string | null + digest: string + signature: string | null + scheme: 'eip712' + } + verdict: { + wallet: string + counterparty_wallet: string | null + escrow_id: string | null + escrow_id_hash: string + decision: EvaluatorDecision + decision_code: number + recommendation: EvaluatorRecommendation + recommendation_code: number + approved: boolean + confidence: number + agent_score_provider: number + certification_valid: boolean + risk_score: number + packet_hash: string + } + callback: { + selector: string | null + calldata: string | null + args: { + escrow_id_hash: string + provider: string + counterparty: string + decision_code: number + recommendation_code: number + approved: boolean + confidence: number + agent_score_provider: number + certification_valid: boolean + risk_score: number + packet_hash: string + attestation_digest: string + attestation_signature: string | null + } + } + transaction: { + to: string | null + data: string | null + value: '0' } } @@ -73,6 +310,48 @@ function round(value: number, digits = 2): number { return Math.round(value * factor) / factor } +function invalidWalletError(field = 'wallet'): EvaluatorServiceError { + return { + ok: false, + code: ErrorCodes.INVALID_WALLET, + message: `Invalid or missing ${field} address`, + status: 400, + } +} + +function invalidVerdictIdError(): EvaluatorServiceError { + return { + ok: false, + code: ErrorCodes.INVALID_EVALUATOR_VERDICT_ID, + message: 'Invalid or missing evaluator verdict id', + status: 400, + } +} + +function invalidNetworkError(rawNetwork: string | undefined, details?: Record): EvaluatorServiceError { + return { + ok: false, + code: ErrorCodes.INVALID_NETWORK, + message: 'Invalid or unsupported network', + status: 400, + details: { + network: rawNetwork ?? null, + supported_networks: listEvaluatorNetworks().map((network) => network.key), + ...(details ?? {}), + }, + } +} + +function verdictNotFoundError(verdictId: string): EvaluatorServiceError { + return { + ok: false, + code: ErrorCodes.EVALUATOR_VERDICT_NOT_FOUND, + message: 'Evaluator verdict not found', + status: 404, + details: { verdict_id: verdictId }, + } +} + function summarizeDecision(decision: EvaluatorDecision): string { switch (decision) { case 'approve': @@ -84,18 +363,207 @@ function summarizeDecision(decision: EvaluatorDecision): string { } } -export async function getEvaluatorPreview( - rawWallet: string | undefined, -): Promise> { - const risk = await getRiskScore(rawWallet) - if (!risk.ok) { - return risk +function buildEvaluatorLinks(wallet: string): EvaluatorLinks { + return { + evaluator_preview: buildPublicUrl(`/v1/score/evaluator?wallet=${wallet}`), + full_score: buildPublicUrl(`/v1/score/full?wallet=${wallet}`), + risk: buildPublicUrl(`/v1/score/risk?wallet=${wallet}`), + standards_document: buildPublicUrl(`/v1/score/erc8004?wallet=${wallet}`), + certification_status: buildPublicUrl(`/v1/certification/${wallet}`), + forensics_summary: buildPublicUrl(`/v1/forensics/summary?wallet=${wallet}`), + forensics_timeline: buildPublicUrl(`/v1/forensics/timeline?wallet=${wallet}`), + forensics_reports: buildPublicUrl(`/v1/forensics/reports?wallet=${wallet}`), + evidence_packet: buildPublicUrl(`/v1/score/evaluator/evidence?wallet=${wallet}`), } +} - const wallet = risk.data.wallet - const registration = getRegistration(wallet) - const certification = getActiveCertification(wallet) - const checks: EvaluatorPreviewView['checks'] = [] +function buildPacketHash(payload: Record): string { + return `0x${createHash('sha256').update(JSON.stringify(payload)).digest('hex')}` +} + +function buildVerdictLinks(wallet: string, verdictId: string): EvaluatorOracleLinks { + return { + standards_document: buildPublicUrl(`/v1/score/erc8004?wallet=${wallet}`), + certification_status: buildPublicUrl(`/v1/certification/${wallet}`), + forensics_summary: buildPublicUrl(`/v1/forensics/summary?wallet=${wallet}`), + evidence_packet: buildPublicUrl(`/v1/score/evaluator/evidence?wallet=${wallet}`), + verdict_record: buildPublicUrl(`/v1/score/evaluator/verdict?id=${encodeURIComponent(verdictId)}`), + verdict_history: buildPublicUrl(`/v1/score/evaluator/verdicts?wallet=${wallet}`), + } +} + +function buildArtifact(params: { + key: string + label: string + category: EvaluatorArtifactCategory + status: EvaluatorArtifactStatus + href: string + summary: string +}): EvaluatorEvidencePacketView['artifacts'][number] { + return params +} + +function getCheckStatus(checks: EvaluatorCheckView[], key: string): EvaluatorCheckStatus | null { + return checks.find((check) => check.key === key)?.status ?? null +} + +function buildRecommendation(checks: EvaluatorCheckView[], decision: EvaluatorDecision): EvaluatorRecommendation { + if (decision === 'approve') return 'release' + + const failedChecks = new Set(checks.filter((check) => check.status === 'fail').map((check) => check.key)) + const reviewChecks = new Set(checks.filter((check) => check.status === 'review').map((check) => check.key)) + + if (failedChecks.has('dispute_pressure')) return 'dispute' + if (failedChecks.has('risk_guardrail') || failedChecks.has('score_strength')) return 'reject' + if (reviewChecks.has('dispute_pressure')) return 'dispute' + + return 'manual_review' +} + +function parseStoredVerdictPayload(payloadJson: string): EvaluatorOracleView | null { + try { + return JSON.parse(payloadJson) as EvaluatorOracleView + } catch { + return null + } +} + +function buildVerdictAttestationInput(params: { + verdict_id: string + wallet: string + counterparty_wallet: string | null + escrow_id: string | null + decision: EvaluatorDecision + recommendation: EvaluatorRecommendation + approved: boolean + confidence: number + agent_score_provider: number + score_model_version: string + certification_valid: boolean + certification_tier: string | null + risk_level: string + risk_score: number + forensic_trace_id: string + packet_hash: string + generated_at: string +}): EvaluatorVerdictAttestationInput { + return { + verdict_id: params.verdict_id, + wallet: params.wallet as `0x${string}`, + counterparty_wallet: (params.counterparty_wallet as `0x${string}` | null) ?? null, + escrow_id: params.escrow_id, + decision: params.decision, + recommendation: params.recommendation, + approved: params.approved, + confidence: params.confidence, + agent_score_provider: params.agent_score_provider, + score_model_version: params.score_model_version, + certification_valid: params.certification_valid, + certification_tier: params.certification_tier, + risk_level: params.risk_level, + risk_score: params.risk_score, + forensic_trace_id: params.forensic_trace_id, + packet_hash: params.packet_hash as `0x${string}`, + generated_at: params.generated_at, + } +} + +function resolveRequestedEvaluatorNetwork( + rawNetwork: string | undefined, +): EvaluatorServiceResult> { + const network = resolveEvaluatorNetwork(rawNetwork) + if (!network) { + return invalidNetworkError(rawNetwork) + } + + return { ok: true, data: network } +} + +function resolveStoredVerdictNetwork( + verdict: Pick, + rawNetwork: string | undefined, +): EvaluatorServiceResult> { + const verdictChainId = getEvaluatorVerdictChainId(verdict.attestation) + const verdictNetwork = findEvaluatorNetworkByChainId(verdictChainId) ?? getDefaultEvaluatorNetwork() + + if (rawNetwork === undefined || rawNetwork.trim() === '') { + return { ok: true, data: verdictNetwork } + } + + const requestedNetwork = resolveEvaluatorNetwork(rawNetwork) + if (!requestedNetwork) { + return invalidNetworkError(rawNetwork) + } + + if (requestedNetwork.chainId !== verdictChainId) { + return invalidNetworkError(rawNetwork, { + verdict_id: verdict.verdict_id, + verdict_chain_id: verdictChainId, + verdict_network: verdictNetwork.key, + requested_chain_id: requestedNetwork.chainId, + requested_network: requestedNetwork.key, + suggestion: `Request a fresh evaluator oracle verdict with network=${requestedNetwork.key} before generating contract calldata.`, + }) + } + + return { ok: true, data: requestedNetwork } +} + +function hydrateStoredAttestation( + stored: ReturnType, + payload: Partial, +): EvaluatorVerdictAttestationView { + const input = buildVerdictAttestationInput({ + verdict_id: payload.verdict_id ?? stored?.id ?? '', + wallet: payload.wallet ?? stored?.wallet ?? '0x0000000000000000000000000000000000000000', + counterparty_wallet: payload.counterparty_wallet ?? stored?.counterparty_wallet ?? null, + escrow_id: payload.escrow_id ?? stored?.escrow_id ?? null, + decision: (payload.decision ?? stored?.decision ?? 'review') as EvaluatorDecision, + recommendation: (payload.recommendation ?? stored?.recommendation ?? 'manual_review') as EvaluatorRecommendation, + approved: payload.approved ?? stored?.approved === 1, + confidence: payload.confidence ?? stored?.confidence ?? 0, + agent_score_provider: payload.agent_score_provider ?? stored?.current_score ?? 0, + score_model_version: payload.score_model_version ?? 'unknown', + certification_valid: payload.certification_valid ?? stored?.certification_active === 1, + certification_tier: payload.certification_tier ?? stored?.certification_tier ?? null, + risk_level: payload.risk_level ?? stored?.risk_level ?? 'watch', + risk_score: payload.risk_score ?? stored?.risk_score ?? 0, + forensic_trace_id: payload.forensic_trace_id ?? stored?.forensic_trace_id ?? '', + packet_hash: payload.packet_hash ?? stored?.packet_hash ?? `0x${'0'.repeat(64)}`, + generated_at: payload.generated_at ?? stored?.created_at ?? new Date(0).toISOString(), + }) + const { digest, typed_data } = buildEvaluatorVerdictTypedData(input) + + const legacyAttestation = payload.attestation + if (legacyAttestation) { + return { + ...legacyAttestation, + typed_data: legacyAttestation.typed_data ?? typed_data, + digest: legacyAttestation.digest ?? digest, + } + } + + return { + status: stored?.attestation_status === 'signed' ? 'signed' : 'unsigned', + scheme: 'eip712', + source: stored?.attestation_signer ? 'publisher_fallback' : 'unconfigured', + signer: stored?.attestation_signer ?? null, + signature: stored?.attestation_signature ?? null, + digest: stored?.attestation_digest || digest, + issued_at: stored?.attested_at ?? stored?.created_at ?? new Date(0).toISOString(), + reason: stored?.attestation_reason ?? (stored?.attestation_signer ? null : 'No oracle signing key configured'), + typed_data, + } +} + +function buildAssessment( + risk: RiskScoreView, + registration: ReturnType, + certification: ReturnType, +): Omit { + const wallet = risk.wallet + const links = buildEvaluatorLinks(wallet) + const checks: EvaluatorCheckView[] = [] const pushCheck = ( key: string, @@ -106,55 +574,80 @@ export async function getEvaluatorPreview( checks.push({ key, label, status, details }) } - if (risk.data.current_score >= 75 && risk.data.score_confidence >= 0.6) { + if (risk.current_score >= 75 && risk.score_confidence >= 0.6) { pushCheck('score_strength', 'Score strength', 'pass', { - current_score: risk.data.current_score, - current_tier: risk.data.current_tier, - score_confidence: risk.data.score_confidence, + current_score: risk.current_score, + current_tier: risk.current_tier, + score_confidence: risk.score_confidence, + settlement_floor: 75, }) - } else if (risk.data.current_score >= 60 && risk.data.score_confidence >= 0.4) { + } else if (risk.current_score >= 60 && risk.score_confidence >= 0.4) { pushCheck('score_strength', 'Score strength', 'review', { - current_score: risk.data.current_score, - current_tier: risk.data.current_tier, - score_confidence: risk.data.score_confidence, + current_score: risk.current_score, + current_tier: risk.current_tier, + score_confidence: risk.score_confidence, + settlement_floor: 75, }) } else { pushCheck('score_strength', 'Score strength', 'fail', { - current_score: risk.data.current_score, - current_tier: risk.data.current_tier, - score_confidence: risk.data.score_confidence, + current_score: risk.current_score, + current_tier: risk.current_tier, + score_confidence: risk.score_confidence, + settlement_floor: 75, }) } - if (risk.data.risk_level === 'critical') { + if (risk.risk_level === 'critical') { pushCheck('risk_guardrail', 'Risk guardrail', 'fail', { - risk_score: risk.data.risk_score, - risk_level: risk.data.risk_level, - action: risk.data.action, + risk_score: risk.risk_score, + risk_level: risk.risk_level, + action: risk.action, }) - } else if (risk.data.risk_level === 'elevated') { + } else if (risk.risk_level === 'elevated') { pushCheck('risk_guardrail', 'Risk guardrail', 'review', { - risk_score: risk.data.risk_score, - risk_level: risk.data.risk_level, - action: risk.data.action, + risk_score: risk.risk_score, + risk_level: risk.risk_level, + action: risk.action, }) } else { pushCheck('risk_guardrail', 'Risk guardrail', 'pass', { - risk_score: risk.data.risk_score, - risk_level: risk.data.risk_level, - action: risk.data.action, + risk_score: risk.risk_score, + risk_level: risk.risk_level, + action: risk.action, }) } - if (certification) { - pushCheck('certification', 'Certification status', 'pass', { + const settlementTier = getDefaultCertificationTier() + const certificationTier = getCertificationTierByStoredValue(certification?.tier ?? null) + if (certification && certificationTier && certificationTier.level >= settlementTier.level) { + pushCheck('certification', 'Certification baseline', 'pass', { active: true, tier: certification.tier, + normalized_tier: certificationTier.label, + settlement_baseline: settlementTier.label, + expires_at: certification.expires_at, + }) + } else if (certification && certificationTier) { + pushCheck('certification', 'Certification baseline', 'review', { + active: true, + tier: certification.tier, + normalized_tier: certificationTier.label, + settlement_baseline: settlementTier.label, + note: `${certificationTier.label} certification is below the default ${settlementTier.label} settlement baseline`, + expires_at: certification.expires_at, + }) + } else if (certification) { + pushCheck('certification', 'Certification baseline', 'review', { + active: true, + tier: certification.tier, + settlement_baseline: settlementTier.label, + note: 'Certification exists, but the tier could not be normalized to the current baseline ladder', expires_at: certification.expires_at, }) } else { - pushCheck('certification', 'Certification status', 'review', { + pushCheck('certification', 'Certification baseline', 'review', { active: false, + settlement_baseline: settlementTier.label, note: 'No active DJD certification on file', }) } @@ -171,42 +664,42 @@ export async function getEvaluatorPreview( }) } - const averageRating = risk.data.summary.average_rating - if (risk.data.summary.rating_count < 2 || averageRating === null || averageRating >= 4) { + const averageRating = risk.summary.average_rating + if (risk.summary.rating_count < 2 || averageRating === null || averageRating >= 4) { pushCheck('counterparty_sentiment', 'Counterparty sentiment', 'pass', { - rating_count: risk.data.summary.rating_count, + rating_count: risk.summary.rating_count, average_rating: averageRating, - unique_raters: risk.data.summary.unique_raters, + unique_raters: risk.summary.unique_raters, }) } else if (averageRating >= 3) { pushCheck('counterparty_sentiment', 'Counterparty sentiment', 'review', { - rating_count: risk.data.summary.rating_count, + rating_count: risk.summary.rating_count, average_rating: averageRating, - unique_raters: risk.data.summary.unique_raters, + unique_raters: risk.summary.unique_raters, }) } else { pushCheck('counterparty_sentiment', 'Counterparty sentiment', 'fail', { - rating_count: risk.data.summary.rating_count, + rating_count: risk.summary.rating_count, average_rating: averageRating, - unique_raters: risk.data.summary.unique_raters, + unique_raters: risk.summary.unique_raters, }) } - if (risk.data.summary.report_count === 0 && risk.data.summary.open_disputes === 0) { + if (risk.summary.report_count === 0 && risk.summary.open_disputes === 0) { pushCheck('dispute_pressure', 'Dispute pressure', 'pass', { - report_count: risk.data.summary.report_count, - open_disputes: risk.data.summary.open_disputes, + report_count: risk.summary.report_count, + open_disputes: risk.summary.open_disputes, }) - } else if (risk.data.summary.report_count <= 1 && risk.data.summary.open_disputes === 0) { + } else if (risk.summary.report_count <= 1 && risk.summary.open_disputes === 0) { pushCheck('dispute_pressure', 'Dispute pressure', 'review', { - report_count: risk.data.summary.report_count, - open_disputes: risk.data.summary.open_disputes, + report_count: risk.summary.report_count, + open_disputes: risk.summary.open_disputes, }) } else { pushCheck('dispute_pressure', 'Dispute pressure', 'fail', { - report_count: risk.data.summary.report_count, - open_disputes: risk.data.summary.open_disputes, - total_penalty_applied: risk.data.summary.total_penalty_applied, + report_count: risk.summary.report_count, + open_disputes: risk.summary.open_disputes, + total_penalty_applied: risk.summary.total_penalty_applied, }) } @@ -219,8 +712,8 @@ export async function getEvaluatorPreview( 0.2, Math.min( 0.98, - risk.data.score_confidence * 0.4 + - risk.data.risk_confidence * 0.4 + + risk.score_confidence * 0.4 + + risk.risk_confidence * 0.4 + (checks.length - reviewCount - failCount) * 0.06 - reviewCount * 0.05 - failCount * 0.12, @@ -228,48 +721,528 @@ export async function getEvaluatorPreview( ), ) + return { + wallet, + decision, + confidence, + rationale: summarizeDecision(decision), + score: { + current_score: risk.current_score, + current_tier: risk.current_tier, + score_confidence: risk.score_confidence, + score_recommendation: risk.score_recommendation, + score_model_version: risk.score_model_version, + last_scored_at: risk.last_scored_at, + }, + certification: { + active: certification !== undefined, + tier: certification?.tier ?? null, + granted_at: certification?.granted_at ?? null, + expires_at: certification?.expires_at ?? null, + }, + risk: { + risk_score: risk.risk_score, + risk_level: risk.risk_level, + action: risk.action, + }, + market_signals: { + rating_count: risk.summary.rating_count, + average_rating: risk.summary.average_rating, + unique_raters: risk.summary.unique_raters, + intent_count: risk.summary.intent_count, + conversion_rate: round(risk.summary.conversion_rate), + active_creator_stakes: risk.summary.active_creator_stakes, + active_score_boost: risk.summary.active_score_boost, + }, + checks, + links, + } +} + +export async function getEvaluatorPreview( + rawWallet: string | undefined, +): Promise> { + const risk = await getRiskScore(rawWallet) + if (!risk.ok) { + return risk + } + + const wallet = risk.data.wallet + const registration = getRegistration(wallet) + const certification = getActiveCertification(wallet) + const assessment = buildAssessment(risk.data, registration, certification) + return { ok: true, data: { standard: 'erc-8183-evaluator-prototype', + ...assessment, + }, + } +} + +export async function getEvaluatorEvidencePacket( + rawWallet: string | undefined, +): Promise> { + const risk = await getRiskScore(rawWallet) + if (!risk.ok) { + return risk + } + + const wallet = risk.data.wallet + const registration = getRegistration(wallet) + const certification = getActiveCertification(wallet) + const assessment = buildAssessment(risk.data, registration, certification) + const recentReports = listFraudReportsByTarget(wallet, { limit: 3 }).map((report) => ({ + report_id: report.id, + reason: report.reason, + created_at: report.created_at, + penalty_applied: report.penalty_applied, + })) + const scoreHistoryEntries = countScoreHistory(wallet) + const settlementTier = getDefaultCertificationTier() + const generatedAt = new Date().toISOString() + const packetPayload = { + wallet, + generated_at: generatedAt, + decision: assessment.decision, + confidence: assessment.confidence, + baseline_profile: 'djd-transactional-settlement-v1', + checks: assessment.checks, + report_count: risk.data.summary.report_count, + open_disputes: risk.data.summary.open_disputes, + recent_reports: recentReports, + } + const packetHash = buildPacketHash(packetPayload) + const artifacts: EvaluatorEvidencePacketView['artifacts'] = [ + buildArtifact({ + key: 'standards_document', + label: 'ERC-8004 reputation document', + category: 'identity', + status: 'included', + href: assessment.links.standards_document, + summary: 'Current ERC-8004-compatible reputation document for the evaluated wallet.', + }), + buildArtifact({ + key: 'full_score', + label: 'Full score breakdown', + category: 'score', + status: 'included', + href: assessment.links.full_score, + summary: `Current score ${assessment.score.current_score}/100 with ${Math.round(assessment.score.score_confidence * 100)}% confidence.`, + }), + buildArtifact({ + key: 'certification_status', + label: 'Certification status', + category: 'certification', + status: assessment.certification.active ? 'included' : 'recommended', + href: assessment.links.certification_status, + summary: assessment.certification.active + ? `${assessment.certification.tier} certification is on file for settlement review.` + : `No active certification is on file; ${settlementTier.label} is the preferred settlement baseline.`, + }), + buildArtifact({ + key: 'forensics_summary', + label: 'Forensics summary', + category: 'forensics', + status: 'included', + href: assessment.links.forensics_summary, + summary: `${risk.data.summary.report_count} reports, ${risk.data.summary.open_disputes} open disputes, risk level ${assessment.risk.risk_level}.`, + }), + buildArtifact({ + key: 'forensics_timeline', + label: 'Forensics timeline', + category: 'forensics', + status: scoreHistoryEntries > 0 ? 'included' : 'recommended', + href: assessment.links.forensics_timeline, + summary: + scoreHistoryEntries > 0 + ? `${scoreHistoryEntries} score history snapshots are available for forensic review.` + : 'No score history snapshots are currently available in the forensic timeline.', + }), + buildArtifact({ + key: 'forensics_reports', + label: 'Forensics reports', + category: 'forensics', + status: recentReports.length > 0 ? 'included' : 'included', + href: assessment.links.forensics_reports, + summary: + recentReports.length > 0 + ? `${recentReports.length} recent incident reports are attached to the evidence packet.` + : 'No fraud reports are currently attached to this wallet.', + }), + ] + + return { + ok: true, + data: { + standard: 'erc-8183-evaluator-evidence-prototype', wallet, - decision, - confidence, - rationale: summarizeDecision(decision), - score: { - current_score: risk.data.current_score, - current_tier: risk.data.current_tier, - score_confidence: risk.data.score_confidence, - score_recommendation: risk.data.score_recommendation, - score_model_version: risk.data.score_model_version, - last_scored_at: risk.data.last_scored_at, - }, - certification: { - active: certification !== undefined, - tier: certification?.tier ?? null, - granted_at: certification?.granted_at ?? null, - expires_at: certification?.expires_at ?? null, + packet_id: `evidence_${packetHash.slice(2, 18)}`, + packet_hash: packetHash, + generated_at: generatedAt, + evaluator: { + decision: assessment.decision, + confidence: assessment.confidence, + rationale: assessment.rationale, }, - risk: { - risk_score: risk.data.risk_score, - risk_level: risk.data.risk_level, - action: risk.data.action, + baseline: { + profile: 'djd-transactional-settlement-v1', + settlement_tier: settlementTier.label, + score_floor: settlementTier.minimumScore, + certification_floor: settlementTier.label, + review_triggers: [ + 'Operational-only certification or no active certification', + 'Elevated risk level', + 'A recent report or low-confidence score band', + ], + reject_triggers: ['Critical risk level', 'Hard fail on dispute pressure or counterparty sentiment'], }, - market_signals: { - rating_count: risk.data.summary.rating_count, - average_rating: risk.data.summary.average_rating, - unique_raters: risk.data.summary.unique_raters, - intent_count: risk.data.summary.intent_count, - conversion_rate: round(risk.data.summary.conversion_rate), - active_creator_stakes: risk.data.summary.active_creator_stakes, - active_score_boost: risk.data.summary.active_score_boost, + evidence: { + score: assessment.score, + certification: assessment.certification, + risk: assessment.risk, + market_signals: assessment.market_signals, + checks: assessment.checks, + forensics: { + score_history_entries: scoreHistoryEntries, + report_count: risk.data.summary.report_count, + unique_reporters: risk.data.summary.unique_reporters, + total_penalty_applied: risk.data.summary.total_penalty_applied, + open_disputes: risk.data.summary.open_disputes, + resolved_disputes: risk.data.summary.resolved_disputes, + reason_breakdown: risk.data.summary.reason_breakdown, + recent_reports: recentReports, + }, }, - checks, + artifacts, links: { - full_score: buildPublicUrl(`/v1/score/full?wallet=${wallet}`), - risk: buildPublicUrl(`/v1/score/risk?wallet=${wallet}`), - standards_document: buildPublicUrl(`/v1/score/erc8004?wallet=${wallet}`), - certification_status: buildPublicUrl(`/v1/certification/${wallet}`), + ...assessment.links, + }, + }, + } +} + +export async function getEvaluatorOracleVerdict(params: { + rawWallet: string | undefined + rawCounterpartyWallet?: string | undefined + rawEscrowId?: string | undefined + rawNetwork?: string | undefined +}): Promise> { + const networkOutcome = resolveRequestedEvaluatorNetwork(params.rawNetwork) + if (!networkOutcome.ok) { + return networkOutcome + } + + const evidenceOutcome = await getEvaluatorEvidencePacket(params.rawWallet) + if (!evidenceOutcome.ok) { + return evidenceOutcome + } + + const counterpartyWallet = + params.rawCounterpartyWallet === undefined || params.rawCounterpartyWallet === '' + ? null + : normalizeWallet(params.rawCounterpartyWallet) + if (params.rawCounterpartyWallet && !counterpartyWallet) { + return invalidWalletError('counterparty_wallet') + } + + const evidence = evidenceOutcome.data + const verdictId = `verdict_${randomUUID()}` + const settlementTier = getDefaultCertificationTier() + const scoreFloorPassed = getCheckStatus(evidence.evidence.checks, 'score_strength') === 'pass' + const certificationFloorPassed = getCheckStatus(evidence.evidence.checks, 'certification') === 'pass' + const riskGuardrailPassed = getCheckStatus(evidence.evidence.checks, 'risk_guardrail') === 'pass' + const disputeGuardrailPassed = getCheckStatus(evidence.evidence.checks, 'dispute_pressure') === 'pass' + const recommendation = buildRecommendation(evidence.evidence.checks, evidence.evaluator.decision) + const forensicTraceId = `trace_${evidence.packet_hash.slice(2, 18)}` + const links = buildVerdictLinks(evidence.wallet, verdictId) + const corePayload: Omit = { + standard: 'erc-8183-evaluator-oracle-prototype', + verdict_id: verdictId, + wallet: evidence.wallet, + counterparty_wallet: counterpartyWallet, + escrow_id: params.rawEscrowId?.trim() ? params.rawEscrowId.trim() : null, + decision: evidence.evaluator.decision, + approved: evidence.evaluator.decision === 'approve', + recommendation, + confidence: Math.round(evidence.evaluator.confidence * 100), + agent_score_provider: evidence.evidence.score.current_score, + score_model_version: evidence.evidence.score.score_model_version, + certification_valid: certificationFloorPassed, + certification_tier: evidence.evidence.certification.tier, + risk_level: evidence.evidence.risk.risk_level, + risk_score: evidence.evidence.risk.risk_score, + sla_metrics: { + baseline_profile: evidence.baseline.profile, + settlement_tier: settlementTier.label, + score_floor: evidence.baseline.score_floor, + score_floor_passed: scoreFloorPassed, + certification_floor: evidence.baseline.certification_floor, + certification_floor_passed: certificationFloorPassed, + risk_guardrail_passed: riskGuardrailPassed, + dispute_guardrail_passed: disputeGuardrailPassed, + failed_checks: evidence.evidence.checks.filter((check) => check.status === 'fail').map((check) => check.key), + review_checks: evidence.evidence.checks.filter((check) => check.status === 'review').map((check) => check.key), + }, + forensic_trace_id: forensicTraceId, + packet_hash: evidence.packet_hash, + generated_at: evidence.generated_at, + links, + } + const attestation = await buildEvaluatorVerdictAttestation( + buildVerdictAttestationInput({ + verdict_id: corePayload.verdict_id, + wallet: corePayload.wallet, + counterparty_wallet: corePayload.counterparty_wallet, + escrow_id: corePayload.escrow_id, + decision: corePayload.decision, + recommendation: corePayload.recommendation, + approved: corePayload.approved, + confidence: corePayload.confidence, + agent_score_provider: corePayload.agent_score_provider, + score_model_version: corePayload.score_model_version, + certification_valid: corePayload.certification_valid, + certification_tier: corePayload.certification_tier, + risk_level: corePayload.risk_level, + risk_score: corePayload.risk_score, + forensic_trace_id: corePayload.forensic_trace_id, + packet_hash: corePayload.packet_hash, + generated_at: corePayload.generated_at, + }), + { + chainId: networkOutcome.data.chainId, + }, + ) + const payload: EvaluatorOracleView = { + ...corePayload, + attestation, + } + + insertEvaluatorVerdict({ + id: verdictId, + wallet: payload.wallet, + counterparty_wallet: payload.counterparty_wallet, + escrow_id: payload.escrow_id, + baseline_profile: payload.sla_metrics.baseline_profile, + certification_floor: payload.sla_metrics.certification_floor, + current_score: payload.agent_score_provider, + current_tier: evidence.evidence.score.current_tier, + score_confidence: evidence.evidence.score.score_confidence, + risk_score: payload.risk_score, + risk_level: payload.risk_level, + certification_active: payload.certification_valid ? 1 : 0, + certification_tier: payload.certification_tier, + decision: payload.decision, + recommendation: payload.recommendation, + approved: payload.approved ? 1 : 0, + confidence: payload.confidence, + packet_hash: payload.packet_hash, + forensic_trace_id: payload.forensic_trace_id, + attestation_scheme: payload.attestation.scheme, + attestation_status: payload.attestation.status, + attestation_digest: payload.attestation.digest, + attestation_signature: payload.attestation.signature, + attestation_signer: payload.attestation.signer, + attestation_reason: payload.attestation.reason, + attested_at: payload.attestation.issued_at, + payload_json: JSON.stringify(payload), + created_at: payload.generated_at, + }) + + return { + ok: true, + data: payload, + } +} + +export function getEvaluatorVerdictRecord( + rawVerdictId: string | undefined, +): EvaluatorServiceResult { + const verdictId = rawVerdictId?.trim() + if (!verdictId) { + return invalidVerdictIdError() + } + + const stored = getStoredEvaluatorVerdict(verdictId) + if (!stored) { + return verdictNotFoundError(verdictId) + } + + const payload = parseStoredVerdictPayload(stored.payload_json) + if (!payload) { + return verdictNotFoundError(verdictId) + } + const attestation = hydrateStoredAttestation(stored, payload) + + return { + ok: true, + data: { + ...payload, + attestation, + recorded_at: stored.created_at, + }, + } +} + +export function listEvaluatorVerdictHistory(params: { + rawWallet: string | undefined + rawLimit?: string | undefined +}): EvaluatorServiceResult { + const wallet = normalizeWallet(params.rawWallet) + if (!wallet) { + return invalidWalletError() + } + + const parsedLimit = Number.parseInt(params.rawLimit ?? '10', 10) + const limit = Number.isNaN(parsedLimit) ? 10 : Math.min(Math.max(parsedLimit, 1), 50) + const rows = listEvaluatorVerdictsByWallet(wallet, limit) + + const items: EvaluatorVerdictHistoryView['items'] = rows.map((row) => { + const payload = parseStoredVerdictPayload(row.payload_json) + return { + verdict_id: row.id, + recorded_at: row.created_at, + decision: row.decision as EvaluatorDecision, + recommendation: row.recommendation as EvaluatorRecommendation, + approved: row.approved === 1, + confidence: row.confidence, + current_score: row.current_score, + current_tier: row.current_tier, + risk_level: row.risk_level, + certification_valid: row.certification_active === 1, + certification_tier: row.certification_tier, + escrow_id: row.escrow_id, + counterparty_wallet: row.counterparty_wallet, + forensic_trace_id: row.forensic_trace_id, + packet_hash: payload?.packet_hash ?? row.packet_hash, + attestation_status: row.attestation_status === 'signed' ? 'signed' : 'unsigned', + attestation_signer: row.attestation_signer, + } + }) + + return { + ok: true, + data: { + standard: 'djd-evaluator-verdict-history-v1', + wallet, + total: items.length, + limit, + summary: { + approvals: items.filter((item) => item.recommendation === 'release').length, + manual_review: items.filter((item) => item.recommendation === 'manual_review').length, + disputes: items.filter((item) => item.recommendation === 'dispute').length, + rejects: items.filter((item) => item.recommendation === 'reject').length, + }, + items, + }, + } +} + +export function getEvaluatorContractCallbackView(params: { + rawVerdictId: string | undefined + rawTargetContract?: string | undefined + rawNetwork?: string | undefined +}): EvaluatorServiceResult { + const storedVerdict = getEvaluatorVerdictRecord(params.rawVerdictId) + if (!storedVerdict.ok) { + return storedVerdict + } + + const networkOutcome = resolveStoredVerdictNetwork(storedVerdict.data, params.rawNetwork) + if (!networkOutcome.ok) { + return networkOutcome + } + + const targetContract = + params.rawTargetContract === undefined || params.rawTargetContract === '' + ? null + : normalizeWallet(params.rawTargetContract) + if (params.rawTargetContract && !targetContract) { + return invalidWalletError('target_contract') + } + + const verdict = storedVerdict.data + const decisionCode = DJD_DECISION_CODES[verdict.decision] + const recommendationCode = DJD_RECOMMENDATION_CODES[verdict.recommendation] + const escrowIdHash = buildEscrowIdHash(verdict.escrow_id) + const isSigned = verdict.attestation.status === 'signed' && verdict.attestation.signature !== null + const calldata = isSigned + ? encodeEvaluatorOracleCallback({ + provider: verdict.wallet as `0x${string}`, + counterparty: (verdict.counterparty_wallet as `0x${string}` | null) ?? null, + decisionCode, + recommendationCode, + approved: verdict.approved, + confidence: verdict.confidence, + agentScoreProvider: verdict.agent_score_provider, + certificationValid: verdict.certification_valid, + riskScore: verdict.risk_score, + packetHash: verdict.packet_hash as `0x${string}`, + attestationDigest: verdict.attestation.digest as `0x${string}`, + attestationSignature: verdict.attestation.signature as `0x${string}`, + escrowId: verdict.escrow_id, + }) + : null + + return { + ok: true, + data: { + standard: 'djd-evaluator-oracle-callback-v1', + ready: isSigned, + reason: isSigned ? null : 'verdict_attestation_unsigned', + verdict_id: verdict.verdict_id, + interface: { + contract: DJD_EVALUATOR_ORACLE_CALLBACK_INTERFACE, + function: DJD_EVALUATOR_ORACLE_CALLBACK_FUNCTION, + chain_id: networkOutcome.data.chainId, + }, + verification: { + status: verdict.attestation.status, + signer: verdict.attestation.signer, + digest: verdict.attestation.digest, + signature: verdict.attestation.signature, + scheme: verdict.attestation.scheme, + }, + verdict: { + wallet: verdict.wallet, + counterparty_wallet: verdict.counterparty_wallet, + escrow_id: verdict.escrow_id, + escrow_id_hash: escrowIdHash, + decision: verdict.decision, + decision_code: decisionCode, + recommendation: verdict.recommendation, + recommendation_code: recommendationCode, + approved: verdict.approved, + confidence: verdict.confidence, + agent_score_provider: verdict.agent_score_provider, + certification_valid: verdict.certification_valid, + risk_score: verdict.risk_score, + packet_hash: verdict.packet_hash, + }, + callback: { + selector: calldata ? calldata.slice(0, 10) : null, + calldata, + args: { + escrow_id_hash: escrowIdHash, + provider: verdict.wallet, + counterparty: verdict.counterparty_wallet ?? ZERO_ADDRESS, + decision_code: decisionCode, + recommendation_code: recommendationCode, + approved: verdict.approved, + confidence: verdict.confidence, + agent_score_provider: verdict.agent_score_provider, + certification_valid: verdict.certification_valid, + risk_score: verdict.risk_score, + packet_hash: verdict.packet_hash, + attestation_digest: verdict.attestation.digest, + attestation_signature: verdict.attestation.signature, + }, + }, + transaction: { + to: targetContract, + data: calldata, + value: '0', }, }, } diff --git a/src/services/opsService.ts b/src/services/opsService.ts index ccb7fbf..2b9052e 100644 --- a/src/services/opsService.ts +++ b/src/services/opsService.ts @@ -1,4 +1,5 @@ import { envEnabled } from '../config/env.js' +import { MODEL_VERSION } from '../config/modelVersion.js' import type { ReleaseMetadata } from '../config/runtimeMetadata.js' import { getReleaseMetadata, getRuntimeMode } from '../config/runtimeMetadata.js' import { @@ -13,7 +14,7 @@ import { import { getIndexerStatus } from '../jobs/blockchainIndexer.js' import { jobStats } from '../jobs/jobStats.js' import { getHttpCounters, uptimeSeconds } from '../metrics.js' -import { MODEL_VERSION } from '../scoring/responseBuilders.js' +import { getEvaluatorAttestationSignerStatus } from './evaluatorAttestationService.js' interface PublicHealthPayload { status: 'ok' @@ -58,6 +59,12 @@ interface DetailedHealthPayload extends PublicHealthPayload { configured: boolean active: boolean } + evaluatorOracleSigner: { + configured: boolean + active: boolean + source: 'oracle_signer' | 'publisher_fallback' | 'unconfigured' | 'invalid_key' + address: string | null + } } database: { cachedScores: number @@ -121,6 +128,7 @@ function buildDetailedHealthPayload(): DetailedHealthPayload { const hourlyRefreshEnabled = envEnabled('ENABLE_HOURLY_REFRESH') const githubTokenConfigured = Boolean(process.env.GITHUB_TOKEN) const publisherConfigured = Boolean(process.env.PUBLISHER_PRIVATE_KEY) + const oracleSigner = getEvaluatorAttestationSignerStatus() const warnings: DetailedHealthPayload['warnings'] = [] if (!githubTokenConfigured) { @@ -137,6 +145,15 @@ function buildDetailedHealthPayload(): DetailedHealthPayload { }) } + if (!oracleSigner.configured) { + warnings.push({ + code: oracleSigner.source === 'invalid_key' ? 'oracle_signer_invalid' : 'oracle_signer_missing', + message: + oracleSigner.reason ?? + 'Neither ORACLE_SIGNER_PRIVATE_KEY nor a valid PUBLISHER_PRIVATE_KEY is set — signed evaluator verdicts are disabled.', + }) + } + return { ...buildPublicHealthPayload(), modelVersion: MODEL_VERSION, @@ -171,6 +188,12 @@ function buildDetailedHealthPayload(): DetailedHealthPayload { configured: publisherConfigured, active: workerEnabled && publisherConfigured, }, + evaluatorOracleSigner: { + configured: oracleSigner.configured, + active: workerEnabled && oracleSigner.configured, + source: oracleSigner.source, + address: oracleSigner.address, + }, }, database: { cachedScores: countCachedScores(), diff --git a/src/services/standardsService.ts b/src/services/standardsService.ts index a9ca82f..1756f5d 100644 --- a/src/services/standardsService.ts +++ b/src/services/standardsService.ts @@ -1,3 +1,5 @@ +import { getERC8004RegistryStatus } from '../blockchain.js' +import { REPUTATION_PUBLISHER_CONFIG } from '../config/constants.js' import { buildPublicUrl } from '../config/public.js' import { getActiveCertification, getRegistration, getReputationPublication } from '../db.js' import { ErrorCodes } from '../errors.js' @@ -42,6 +44,9 @@ export interface Erc8004CompatibleScoreView { } identity: { registered: boolean + erc8004_registered: boolean + erc8004_registry_configured: boolean + erc8004_registry_contract: string | null name: string | null description: string | null github_url: string | null @@ -61,11 +66,17 @@ export interface Erc8004CompatibleScoreView { publication: { published: boolean registry: 'erc-8004' + network: 'base' + chain_id: number + registry_contract: string endpoint: string tx_hash: string | null + feedback_hash: string | null published_at: string | null score_at_publication: number | null model_version: string | null + eligible_now: boolean + eligibility_reasons: string[] } links: { basic_score: string @@ -102,6 +113,26 @@ export async function getErc8004CompatibleScoreView( const registration = getRegistration(wallet) const certification = getActiveCertification(wallet) const publication = getReputationPublication(wallet) + const registryStatus = getERC8004RegistryStatus() + const currentDocumentUrl = buildPublicUrl(`/v1/score/erc8004?wallet=${wallet}`) + const erc8004Registered = score.dimensions.identity.data?.erc8004Registered === true + const publicationDelta = publication ? Math.abs(score.score - publication.composite_score) : null + const eligibilityReasons: string[] = [] + let eligibleNow = true + + if (score.confidence < REPUTATION_PUBLISHER_CONFIG.MIN_CONFIDENCE) { + eligibleNow = false + eligibilityReasons.push('confidence_below_threshold') + } + + if (publication && publicationDelta !== null && publicationDelta < REPUTATION_PUBLISHER_CONFIG.SCORE_DELTA) { + eligibleNow = false + eligibilityReasons.push('score_change_below_threshold') + } + + if (eligibleNow) { + eligibilityReasons.push(publication ? 'republish_ready' : 'first_publication_ready') + } return { ok: true, @@ -127,6 +158,9 @@ export async function getErc8004CompatibleScoreView( }, identity: { registered: registration !== undefined, + erc8004_registered: erc8004Registered, + erc8004_registry_configured: registryStatus.identity_registry_configured, + erc8004_registry_contract: registryStatus.identity_registry, name: registration?.name ?? null, description: registration?.description ?? null, github_url: registration?.github_url ?? null, @@ -146,11 +180,17 @@ export async function getErc8004CompatibleScoreView( publication: { published: publication !== undefined, registry: 'erc-8004', - endpoint: buildPublicUrl(`/v1/score/full?wallet=${wallet}`), + network: registryStatus.network, + chain_id: registryStatus.chain_id, + registry_contract: registryStatus.reputation_registry, + endpoint: publication?.endpoint ?? currentDocumentUrl, tx_hash: publication?.tx_hash ?? null, + feedback_hash: publication?.feedback_hash ?? null, published_at: publication?.published_at ?? null, score_at_publication: publication?.composite_score ?? null, model_version: publication?.model_version ?? null, + eligible_now: eligibleNow, + eligibility_reasons: eligibilityReasons, }, links: { basic_score: buildPublicUrl(`/v1/score/basic?wallet=${wallet}`), diff --git a/src/templates/agentProfile.ts b/src/templates/agentProfile.ts index a8e8fb2..84e0ae5 100644 --- a/src/templates/agentProfile.ts +++ b/src/templates/agentProfile.ts @@ -422,12 +422,12 @@ ${
${ certifyReadiness === 'eligible' - ? 'This wallet meets the visible prerequisites for DJD certification: registration plus a fresh score of 75 or higher. The next step is the one-time x402 certification purchase.' + ? 'This wallet meets the visible prerequisites for DJD certification. The default transactional lane starts at 75, with operational at 60 and autonomous at 90.' : certifyReadiness === 'register' ? 'Certification starts with identity context. Register the agent first so counterparties can inspect project metadata instead of a bare wallet address.' : certifyReadiness === 'refresh' ? 'Certification requires a fresh score snapshot. Re-score this wallet before applying so the certification decision is based on current data.' - : 'DJD certification currently requires a composite score of 75 or higher. Improve the trust profile, then apply once the wallet is in the Trusted band.' + : 'DJD certification is tiered: Operational starts at 60, Transactional at 75, and Autonomous at 90. Improve the trust profile, then apply for the highest eligible lane.' }
@@ -401,10 +401,18 @@ export function certifyPageHtml(): string {
Price
Method
+
+
+
GET /v1/certification/tiers
+
Free catalog of the Operational, Transactional, and Autonomous certification tiers with score thresholds and x402 pricing.
+
+
Free
+
GET
+
GET /v1/certification/readiness
-
Checks whether a wallet is ready for certification and returns blockers, review state, and next-step links.
+
Checks whether a wallet is ready for a requested certification tier and returns blockers, review state, tier pricing, and next-step links.
Free
GET
@@ -425,12 +433,36 @@ export function certifyPageHtml(): string {
Free
POST
+
+
+
POST /v1/certification/apply/operational
+
Tier 1 issuance path for bounded, operational agents.
+
+
$50
+
POST
+
+
+
+
POST /v1/certification/apply/transactional
+
Tier 2 issuance path for agents handling financially sensitive work.
+
+
$200
+
POST
+
+
+
+
POST /v1/certification/apply/autonomous
+
Tier 3 issuance path for high-consequence autonomous agents.
+
+
$500
+
POST
+
POST /v1/certification/apply
-
Final issuance endpoint for certification. Paid x402 route used when a wallet is actually ready to certify.
+
Legacy transactional apply path kept for compatibility. Prefer the tier-specific routes above.
-
x402
+
$200
POST
@@ -449,6 +481,86 @@ export function certifyPageHtml(): string {
$x402
GET
+
+
+
GET /v1/score/evaluator/evidence?wallet=0x...
+
Paid evaluator evidence packet that bundles the verdict with certification baseline, forensics summary, and traceable evidence links.
+
+
$0.45
+
GET
+
+
+
+
GET /v1/score/evaluator/oracle?wallet=0x...
+
Compact oracle-style verdict endpoint that persists a settlement recommendation, forensic trace id, verdict record links, and a signed attestation when the DJD oracle signer is configured.
+
+
$0.60
+
GET
+
+
+
+
GET /v1/score/evaluator/callback?id=verdict_...
+
Contract-ready callback envelope that turns a stored signed verdict into ABI-backed calldata for a relayer or escrow contract.
+
+
$0.20
+
GET
+
+
+
+
GET /v1/score/evaluator/verifier
+
Free Solidity verifier package with ABI, selectors, typed-data schema, and source for Base or Base Sepolia integrations.
+
+
Free
+
GET
+
+
+
+
GET /v1/score/evaluator/networks
+
Free supported-network catalog for the DJD oracle stack, including Base mainnet, Base Sepolia, explorer links, and deploy bundle params.
+
+
Free
+
GET
+
+
+
+
GET /v1/score/evaluator/artifacts
+
Free compiled Solidity artifact package with ABI, bytecode, deployed bytecode, and constructor metadata for DJD onchain integrations.
+
+
Free
+
GET
+
+
+
+
GET /v1/score/evaluator/proof?id=verdict_...
+
Free verifier-proof envelope that turns a stored signed verdict into verifyVerdict calldata for the DJD Solidity verifier contract.
+
+
Free
+
GET
+
+
+
+
GET /v1/score/evaluator/escrow?id=verdict_...
+
Free escrow-settlement envelope that turns a stored signed verdict into settleWithDJDVerdict calldata for the reference DJD escrow consumer contract.
+
+
Free
+
GET
+
+
+
+
GET /v1/score/evaluator/deploy?id=verdict_...
+
Free deployment plan that maps a stored verdict into constructor args for the DJD verifier and reference escrow consumer contracts.
+
+
Free
+
GET
+
+
+
+
GET /v1/score/evaluator/deploy/bundle?id=verdict_...
+
Free deployment bundle that combines the verdict-driven constructor plan with the compiled verifier and escrow artifacts in one payload, locked to the verdict’s target network.
+
+
Free
+
GET
+
@@ -467,8 +579,6 @@ export function certifyPageHtml(): string { ${renderPublicFooter({ - copy: 'Explorer is the live dashboard view into DJD trust infrastructure on Base. It keeps the same brand system as the pricing and certification surfaces while preserving its operational layout.', + copy: 'Explorer is the live dashboard view into DJD wallet screening on Base. It keeps the same brand system as the pricing and certification surfaces while preserving its operational layout.', })}` } diff --git a/src/templates/legal.ts b/src/templates/legal.ts index cad2b34..b3607e7 100644 --- a/src/templates/legal.ts +++ b/src/templates/legal.ts @@ -9,7 +9,7 @@ const SUPPORT_EMAIL = getSupportEmail() export const wrapHtml = (title: string, content: string) => `${renderPublicPage({ title: `${title} - DJD Agent Score`, - description: `${title} for DJD Agent Score, the trust infrastructure platform for the agent economy.`, + description: `${title} for DJD Agent Score, the wallet screening platform for payouts, paid routes, and agent workflows.`, path: title === 'Terms of Service' ? '/terms' : title === 'Privacy Policy' ? '/privacy' : '/', ctaHref: '/pricing', ctaLabel: 'View Pricing', @@ -85,7 +85,7 @@ export const wrapHtml = (title: string, content: string) => .legal-prose tr:nth-child(even) td{background:rgba(7,17,31,0.38)} `, footerCopy: - 'DJD Agent Score provides trust infrastructure for the agent economy. Legal pages use the same public brand system as the rest of the product.', + 'DJD Agent Score provides wallet screening and trust signals for payouts, paid routes, and agent workflows. Legal pages use the same public brand system as the rest of the product.', content: `
${content}
`, })}` @@ -350,7 +350,7 @@ export const leaderboardHtml = `

`, footerCopy: - 'DJD Agent Score LLC provides trust infrastructure for agent marketplaces, payouts, and settlement flows on Base.', + 'DJD Agent Score LLC provides wallet screening and trust signals for payouts, paid routes, and settlement flows on Base.', }) } diff --git a/src/templates/publicPage.ts b/src/templates/publicPage.ts index 3c7025c..724e144 100644 --- a/src/templates/publicPage.ts +++ b/src/templates/publicPage.ts @@ -16,6 +16,9 @@ interface PublicHeadOptions { description: string path: string ogType?: 'website' | 'article' + canonicalUrl?: string + imageUrl?: string + imageAlt?: string extraHead?: string } @@ -530,8 +533,49 @@ function navLink(href: string, label: string, key: PublicNavKey, active?: Public return `${label}` } +function escapeXml(value: string): string { + return value + .replaceAll('&', '&') + .replaceAll('<', '<') + .replaceAll('>', '>') + .replaceAll('"', '"') + .replaceAll("'", ''') +} + +function buildDefaultSocialImage(title: string, description: string): string { + const svg = ` + + + + + + + + + + + + DJD Agent Score + ${escapeXml(title)} + ${escapeXml(description)} + Screen wallets before payout + Trust signals for payouts, paid routes, and agent workflows +` + + return `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(svg)}` +} + export function renderPublicHeadStart(options: PublicHeadOptions): string { - const { title, description, path, ogType = 'website', extraHead = '' } = options + const { + title, + description, + path, + ogType = 'website', + canonicalUrl = buildPublicUrl(path), + imageUrl = buildDefaultSocialImage(title, description), + imageAlt = `${title} on DJD Agent Score`, + extraHead = '', + } = options return ` @@ -540,14 +584,19 @@ export function renderPublicHeadStart(options: PublicHeadOptions): string { ${title} + - + + + + + @@ -590,7 +639,7 @@ export function renderPublicNav( export function renderPublicFooter(options: PublicFooterOptions = {}): string { const copy = options.copy ?? - `DJD Agent Score is trust infrastructure for apps, marketplaces, and agent networks on Base. Questions? Contact ${getSupportEmail()}.` + `DJD Agent Score helps apps and agent operators screen wallets before payouts, paid routes, and settlement on Base. Questions? Contact ${getSupportEmail()}.` return `