chore: pnpm lockfile, info-defi-oracle-138 app, token-lists, OMNL discovery output
- Refresh pnpm-lock.yaml / workspace after prior merge - Add Chain 138 info hub SPA (info-defi-oracle-138) - Token list and validation script tweaks; path_b report; Hyperledger proxmox install notes - HYBX implementation roadmap and routing graph data model Note: transaction-composer is a nested git repo — convert to submodule before tracking. Made-with: Cursor
This commit is contained in:
@@ -34,6 +34,10 @@ CLOUDFLARE_TUNNEL_ID_MIFOS_R630_02=
|
||||
CLOUDFLARE_TUNNEL_TOKEN_MIFOS_R630_02=
|
||||
CLOUDFLARE_ORIGIN_CA_KEY=
|
||||
CLOUDFLARE_ACCOUNT_ID=
|
||||
# Turnstile (Captcha) for IRU marketplace inquiry — Dashboard → Turnstile; NOT the DNS API key
|
||||
CLOUDFLARE_TURNSTILE_SECRET_KEY=
|
||||
# dbis_core Vite marketplace: VITE_CLOUDFLARE_TURNSTILE_SITE_KEY=
|
||||
# Sankofa portal Next.js (sibling repo): NEXT_PUBLIC_CLOUDFLARE_TURNSTILE_SITE_KEY=
|
||||
|
||||
# --- ClouDNS ---
|
||||
CLOUDNS_AUTH_ID=
|
||||
@@ -100,10 +104,18 @@ AWS_S3_BUCKET=
|
||||
AZURE_STORAGE_CONNECTION_STRING=
|
||||
AZURE_STORAGE_CONTAINER=
|
||||
|
||||
# --- Pinata (IPFS pinning; token logos) ---
|
||||
# Dashboard: https://app.pinata.cloud — API Keys → JWT or key/secret.
|
||||
# scripts/upload-token-logos-to-ipfs.sh uses PINATA_JWT only (Bearer for pinFileToIPFS).
|
||||
PINATA_JWT=
|
||||
PINATA_API_KEY=
|
||||
PINATA_API_SECRET=
|
||||
|
||||
# --- Blockchain / SMOM-DBIS-138 (use smom-dbis-138/.env for PRIVATE_KEY) ---
|
||||
PRIVATE_KEY=
|
||||
RPC_URL_138=
|
||||
RPC_URL_138_PUBLIC=
|
||||
# Ethereum L1 — used for dual-anchor attestation with scripts/omnl/omnl-chain138-attestation-tx.sh (consumes ETH gas). Alias: RPC_URL_MAINNET.
|
||||
ETHEREUM_MAINNET_RPC=
|
||||
CHAIN_651940_RPC_URL=
|
||||
ETHERLINK_RPC_URL=
|
||||
@@ -122,6 +134,51 @@ MOONPAY_SECRET_KEY=
|
||||
RAMP_NETWORK_API_KEY=
|
||||
ONRAMPER_API_KEY=
|
||||
|
||||
# --- GRU Transport / cW hard-peg bridge controls (Chain 138 -> public chains) ---
|
||||
# Canonical L1 bridge env used by the GRU transport overlay and token-aggregation.
|
||||
CHAIN138_L1_BRIDGE=
|
||||
# Legacy alias still used by some deployment helpers.
|
||||
CW_L1_BRIDGE_CHAIN138=
|
||||
CW_BRIDGE_MAINNET=
|
||||
CW_BRIDGE_CRONOS=
|
||||
CW_BRIDGE_BSC=
|
||||
CW_BRIDGE_POLYGON=
|
||||
CW_BRIDGE_GNOSIS=
|
||||
CW_BRIDGE_AVALANCHE=
|
||||
CW_BRIDGE_BASE=
|
||||
CW_BRIDGE_ARBITRUM=
|
||||
CW_BRIDGE_OPTIMISM=
|
||||
CW_RESERVE_VERIFIER_CHAIN138=
|
||||
CW_STABLECOIN_RESERVE_VAULT=
|
||||
CW_RESERVE_SYSTEM=
|
||||
CW_ATTACH_VERIFIER_TO_L1=1
|
||||
CW_REQUIRE_VAULT_BACKING=
|
||||
CW_REQUIRE_RESERVE_SYSTEM_BALANCE=
|
||||
CW_REQUIRE_TOKEN_OWNER_MATCH_VAULT=
|
||||
CW_CANONICAL_USDT=
|
||||
CW_CANONICAL_USDC=
|
||||
CW_USDT_RESERVE_ASSET=
|
||||
CW_USDC_RESERVE_ASSET=
|
||||
CW_MAX_OUTSTANDING_USDT_MAINNET=
|
||||
CW_MAX_OUTSTANDING_USDC_MAINNET=
|
||||
CW_MAX_OUTSTANDING_USDT_CRONOS=
|
||||
CW_MAX_OUTSTANDING_USDC_CRONOS=
|
||||
CW_MAX_OUTSTANDING_USDT_BSC=
|
||||
CW_MAX_OUTSTANDING_USDC_BSC=
|
||||
CW_MAX_OUTSTANDING_USDT_POLYGON=
|
||||
CW_MAX_OUTSTANDING_USDC_POLYGON=
|
||||
CW_MAX_OUTSTANDING_USDT_GNOSIS=
|
||||
CW_MAX_OUTSTANDING_USDC_GNOSIS=
|
||||
CW_MAX_OUTSTANDING_USDT_AVALANCHE=
|
||||
CW_MAX_OUTSTANDING_USDC_AVALANCHE=
|
||||
CW_MAX_OUTSTANDING_USDT_BASE=
|
||||
CW_MAX_OUTSTANDING_USDC_BASE=
|
||||
CW_MAX_OUTSTANDING_USDT_ARBITRUM=
|
||||
CW_MAX_OUTSTANDING_USDC_ARBITRUM=
|
||||
CW_MAX_OUTSTANDING_USDT_OPTIMISM=
|
||||
CW_MAX_OUTSTANDING_USDC_OPTIMISM=
|
||||
CW_FREEZE_AVAX_L2_CONFIG=
|
||||
|
||||
# --- Alerts & monitoring ---
|
||||
SLACK_WEBHOOK_URL=
|
||||
PAGERDUTY_INTEGRATION_KEY=
|
||||
@@ -129,6 +186,17 @@ EMAIL_ALERT_API_URL=
|
||||
EMAIL_ALERT_RECIPIENTS=
|
||||
SENTRY_DSN=
|
||||
|
||||
# --- dbis_core IRU / marketplace outbound mail (optional; Proxmox Mail Proxy VMID 100 = 192.168.11.32) ---
|
||||
# EMAIL_PROVIDER=smtp
|
||||
# SMTP_HOST=192.168.11.32
|
||||
# SMTP_PORT=587
|
||||
# SMTP_SECURE=false
|
||||
# SMTP_USER=
|
||||
# SMTP_PASSWORD=
|
||||
# EMAIL_FROM=
|
||||
# EMAIL_FROM_NAME=SolaceNet
|
||||
# DBIS_SALES_EMAIL=
|
||||
|
||||
# --- Legal / e-signature ---
|
||||
E_SIGNATURE_BASE_URL=
|
||||
|
||||
|
||||
223
hybx_jurisdictional_cheat_sheets_implementation_roadmap.md
Normal file
223
hybx_jurisdictional_cheat_sheets_implementation_roadmap.md
Normal file
@@ -0,0 +1,223 @@
|
||||
# HYBX Jurisdictional Cheat Sheets — Implementation Roadmap
|
||||
|
||||
This document **operationalizes** the blueprint in [hybx_jurisdictional_cheat_sheets_technical_plan.md](hybx_jurisdictional_cheat_sheets_technical_plan.md). It defines build order, a **canonical schema v1**, API/service boundaries, integration contracts with the Compliance & Routing Sidecar and the routing graph model, and explicit non-goals. It does not replace the technical plan.
|
||||
|
||||
---
|
||||
|
||||
## 1. Scope statement
|
||||
|
||||
- **Inherits** the JIS vision: deterministic jurisdiction knowledge for banking, payments, liquidity, settlement, and regulatory execution (see technical plan Purpose and Core Objective).
|
||||
- **This roadmap** only specifies: what to implement first, schema norms, MVP geography, read APIs, and how consumers (especially the sidecar) call JIS.
|
||||
- **Out of roadmap detail**: full legal research, production data feeds, and UI design specs (covered at a high level only).
|
||||
|
||||
---
|
||||
|
||||
## 2. Architecture placement
|
||||
|
||||
JIS is a **read-mostly fact service**. It is **not** the Policy DSL: policies interpret facts; JIS supplies structured **facts** (permissions, rails, risk tiers, citations).
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Composer[Transaction_Composer]
|
||||
Orch[Orchestrator]
|
||||
Sidecar[Compliance_Routing_Sidecar]
|
||||
JIS[JIS_Service]
|
||||
Policy[Policy_DSL_future]
|
||||
Composer --> Orch
|
||||
Orch --> Sidecar
|
||||
Sidecar --> JIS
|
||||
Sidecar --> Policy
|
||||
JIS -.->|facts_only| Sidecar
|
||||
```
|
||||
|
||||
**Flow (conceptual):** Transaction graph / compiled transaction enters the sidecar; sidecar performs jurisdiction lookup against JIS (by ids, currencies, corridor pairs); sidecar combines JIS outputs with internal rules and optional future Policy DSL to produce PASS/WARN/FAIL, routing constraints, and explainable decisions.
|
||||
|
||||
---
|
||||
|
||||
## 3. Canonical schema v1 (normative for code)
|
||||
|
||||
Aligns with the technical plan “Data Model Design / Core Structure” example. All profiles are JSON-serializable documents stored as versioned rows.
|
||||
|
||||
### 3.1 Root envelope (every stored profile)
|
||||
|
||||
| Field | MVP required | Full profile | Description |
|
||||
|--------|--------------|--------------|-------------|
|
||||
| `schemaVersion` | yes | yes | e.g. `"1.0.0"` for this spec. |
|
||||
| `jurisdictionId` | yes | yes | Stable id (see section 4). |
|
||||
| `profileVersion` | yes | yes | Semver or monotonic string for this jurisdiction’s content. |
|
||||
| `effectiveFrom` | yes | yes | ISO-8601 date/time. |
|
||||
| `effectiveTo` | no | optional | Null if current. |
|
||||
| `dataQuality` | yes | yes | `complete` \| `partial` \| `stub` — MVP seeds often `partial` or `stub`. |
|
||||
| `sourceRefs` | recommended | yes | Array of `{ "title", "url"?, "publisher"?, "retrievedAt"? }` per subsection or aggregated. |
|
||||
| `core` | yes | yes | Registry fields (see 3.2). |
|
||||
| `currencyRules` | yes | yes | Array or map by currency code; see 3.3. |
|
||||
| `fxRules` | yes | yes | FX convertibility, controls, limits (may be stub). |
|
||||
| `settlementSystems` | yes | yes | List of settlement rail summaries. |
|
||||
| `licensingRules` | partial | yes | License types applicable in jurisdiction; MVP may be empty array with `dataQuality`. |
|
||||
| `crossBorderRules` | partial | yes | Inbound/outbound, restricted jurisdictions. |
|
||||
| `riskProfile` | yes | yes | Political/financial/sanctions tiers as scalars or enums. |
|
||||
| `sanctions` | no | optional | Module from technical plan § Sanctions Intelligence. |
|
||||
| `documentation` | no | optional | Required doc types for payments. |
|
||||
| `feeGovernance` | no | optional | Max fees, disclosures (technical plan § Fee Governance). |
|
||||
|
||||
### 3.2 `core` (jurisdiction registry)
|
||||
|
||||
Minimum MVP fields:
|
||||
|
||||
- `countryName`, `isoCountryCode` (ISO 3166-1 alpha-2)
|
||||
- `primaryFinancialRegulator` (string)
|
||||
- `legalSystemType` (enum or string)
|
||||
- `centralBankName` (string)
|
||||
- `primaryCurrency` (ISO 4217)
|
||||
- `timeZones` (string[])
|
||||
- `politicalRiskTier`, `financialRiskTier` (string or number; align with technical plan Risk Intelligence)
|
||||
|
||||
Full profile adds: region, capital, secondary regulators, languages, extended sanctions status fields as in technical plan.
|
||||
|
||||
### 3.3 `currencyRules` (per currency or default)
|
||||
|
||||
Each entry should support at least:
|
||||
|
||||
- `currency` (ISO 4217)
|
||||
- `convertibilityLevel` (enum: e.g. `fully_convertible`, `managed`, `restricted`)
|
||||
- `settlementType` (string or enum)
|
||||
- `liquidityAvailability` (enum or coarse string)
|
||||
- `centralBankRestrictions` (string, optional)
|
||||
|
||||
### 3.4 Versioning rules
|
||||
|
||||
- **schemaVersion**: bump **minor** for backward-compatible new optional fields; **major** for breaking renames.
|
||||
- **profileVersion**: independent per `jurisdictionId`; any material regulatory update increments profile version and sets `effectiveFrom`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Jurisdiction ID scheme
|
||||
|
||||
- **Default:** ISO 3166-1 alpha-2 uppercase (`ID`, `US`, `SG`, `GB`).
|
||||
- **Sub-national or zones:** use hyphenated suffixes, e.g. `US-NY` only if the product truly needs sub-national rules; otherwise keep national id and encode specificity in `core` or overlays.
|
||||
- **EU:** prefer national ids (`DE`, `FR`) for MVP; introduce `EU` or `EU-DE` only when cross-EU harmonized profiles are maintained as first-class rows.
|
||||
- **Special financial centers:** e.g. `HK`, `MO` as ISO codes; avoid custom ids unless they are registered in an internal enum table.
|
||||
|
||||
Document every non-ISO id in a single **registry table** in the implementation repo.
|
||||
|
||||
---
|
||||
|
||||
## 5. Phased delivery
|
||||
|
||||
| Phase | Goal | Outcome |
|
||||
|-------|------|--------|
|
||||
| **0** | Schema + validation | JSON Schema (or TypeScript types) + validator; one sample fixture (e.g. `ID`) that validates. |
|
||||
| **1** | JIS MVP service | `GET /jurisdiction/{id}` returns active profile by id; `POST /jurisdiction/query` with `{ "ids": string[] }` returns map id → profile; store in **Postgres** (prod) or **SQLite** (local dev); **no Elasticsearch**. |
|
||||
| **2** | Pilot dataset | Seed **5–10** jurisdictions; **minimum** `ID`, `US`, `SG`, `GB`. Use `dataQuality: partial` or `stub` and `sourceRefs` where legal detail is incomplete. |
|
||||
| **3** | Sidecar integration contract | Written contract (this section expanded in runbooks): request/response fields, caching, error handling; optional `constraintHints` for routing graph (see section 6). |
|
||||
| **4** | Search (optional) | Add Elasticsearch or Postgres FTS **only** when query patterns exceed id/batch lookup. |
|
||||
| **5** | Dashboard / visualization | Defer until API and seeds are stable (technical plan Visualization). |
|
||||
|
||||
---
|
||||
|
||||
## 6. Integration with existing HYBX artifacts
|
||||
|
||||
### 6.1 Compliance & Routing Sidecar
|
||||
|
||||
- Reference: [hybx_compliance_routing_sidecar_technical_plan.md](hybx_compliance_routing_sidecar_technical_plan.md).
|
||||
- Sidecar `POST /evaluate-transaction` (or equivalent) should resolve **all jurisdiction ids** present on the transaction graph, participant registry, or routing plan, then call JIS (batch) before or during compliance/routing engines.
|
||||
- JIS returns **facts only**; the sidecar applies rules (AML/KYC/sanctions workflows may still use other services; JIS supplies jurisdiction-grounded permissions and tiers).
|
||||
|
||||
### 6.2 Routing graph data model
|
||||
|
||||
- Reference: [hybx_routing_graph_data_model.md](hybx_routing_graph_data_model.md) (jurisdiction overlays, `regulatoryPenalty`, transaction-level filters).
|
||||
- **Mapping:** JIS `riskProfile`, `fxRules`, `crossBorderRules`, and `currencyRules` inform:
|
||||
- vertex `jurisdiction` and `capabilities` validation;
|
||||
- edge `jurisdictions[]` tags and suggested `costVector.regulatoryPenalty` (sidecar computes penalty from JIS facts + policy weights).
|
||||
- JIS does **not** store the routing graph; it **constrains** how the graph may be traversed.
|
||||
|
||||
### 6.3 Transaction Composer
|
||||
|
||||
- Optional future: jurisdiction pickers on nodes, auto-tagging `jurisdictionId` on `TransactionNodeData`. **Out of scope** for phases 0–1 unless product prioritizes it.
|
||||
- Composer continues to emit design-time graphs; orchestrator/sidecar enrich with registry ids.
|
||||
|
||||
---
|
||||
|
||||
## 7. Sidecar integration contract (phase 3 deliverable detail)
|
||||
|
||||
**Batch request (example):**
|
||||
|
||||
```json
|
||||
{
|
||||
"jurisdictionIds": ["ID", "US"],
|
||||
"context": {
|
||||
"currencies": ["USD", "IDR"],
|
||||
"corridor": { "fromJurisdiction": "US", "toJurisdiction": "ID" },
|
||||
"effectiveAt": "2026-03-29T12:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Batch response (example shape):**
|
||||
|
||||
```json
|
||||
{
|
||||
"profiles": {
|
||||
"ID": { "schemaVersion": "1.0.0", "jurisdictionId": "ID", "profileVersion": "0.1.0", "dataQuality": "partial", "core": {}, "currencyRules": [], "fxRules": {}, "settlementSystems": [], "crossBorderRules": {}, "riskProfile": {} }
|
||||
},
|
||||
"notFound": []
|
||||
}
|
||||
```
|
||||
|
||||
**Errors:** HTTP 404 for unknown id on single GET; batch omits missing ids in `profiles` and lists them in `notFound` (or returns explicit errors—choose one behavior and document in OpenAPI).
|
||||
|
||||
**Caching:** Sidecar or API gateway may cache by `(jurisdictionId, profileVersion)` with TTL aligned to technical plan (configurable; target lookup < 100 ms p95 at steady state with cache).
|
||||
|
||||
---
|
||||
|
||||
## 8. Data acquisition and legal posture
|
||||
|
||||
- **MVP:** Manually curated YAML/JSON seeds in-repo or loaded from migration; every non-trivial field should have `sourceRefs` where possible.
|
||||
- **Production expansion:** Licensed legal/regulatory data providers and official publications (technical plan Data Acquisition Model). **No** commitment to automated scraping in MVP code paths.
|
||||
- **Disclaimer:** Profiles are engineering aids; final legal interpretation remains with compliance officers and counsel.
|
||||
|
||||
---
|
||||
|
||||
## 9. Non-goals (explicit)
|
||||
|
||||
- Full **global coverage** on day one (technical plan Phase 1/2 expansion applies).
|
||||
- **Real-time** regulatory crawling as a blocking dependency for MVP.
|
||||
- **Multilingual** content in MVP (technical plan Localization deferred).
|
||||
- **Graph database** for JIS core (optional in technical plan; not required for MVP).
|
||||
- Replacing **sanctions screening vendors** or **KYC** systems—JIS complements them with jurisdiction facts.
|
||||
|
||||
---
|
||||
|
||||
## 10. Acceptance criteria (“MVP ready”)
|
||||
|
||||
1. At least **four** jurisdiction profiles (`ID`, `US`, `SG`, `GB`) validate against **schema v1**.
|
||||
2. `GET /jurisdiction/{id}` and `POST /jurisdiction/query` implemented and documented (OpenAPI or equivalent).
|
||||
3. With a **warm cache**, p95 read latency meets **< 100 ms** internal target under nominal load (align with technical plan Performance Targets).
|
||||
4. **One worked example** documented end-to-end: transaction touching **USD→IDR** with participants in **US** and **ID**, showing which JIS fields the sidecar would read for FX and cross-border checks, and how results feed [hybx_routing_graph_data_model.md](hybx_routing_graph_data_model.md) overlays.
|
||||
|
||||
### Worked example (narrative)
|
||||
|
||||
- Transaction: remitter in **US**, beneficiary bank in **ID**, USD notionally converted to IDR.
|
||||
- Sidecar resolves `US` and `ID` profiles; checks `fxRules` and `crossBorderRules` for both; reads `currencyRules` for USD/IDR; compares against transaction amounts and rails in the routing plan.
|
||||
- If a rule indicates heightened friction, sidecar may set routing graph `regulatoryPenalty` on affected edges or return WARN with explanation references to `sourceRefs`.
|
||||
|
||||
---
|
||||
|
||||
## 11. References
|
||||
|
||||
| Document | Role |
|
||||
|----------|------|
|
||||
| [hybx_jurisdictional_cheat_sheets_technical_plan.md](hybx_jurisdictional_cheat_sheets_technical_plan.md) | Full JIS blueprint (subsystems, storage, visualization, global coverage). |
|
||||
| [hybx_compliance_routing_sidecar_technical_plan.md](hybx_compliance_routing_sidecar_technical_plan.md) | Sidecar engines, API sketch, evaluation flow. |
|
||||
| [hybx_routing_graph_data_model.md](hybx_routing_graph_data_model.md) | Routing vertices/edges, `costVector`, jurisdiction overlays. |
|
||||
| [transaction-composer/](transaction-composer/) | Design-time transaction graph and compiler (optional future jurisdiction fields). |
|
||||
|
||||
---
|
||||
|
||||
## Document control
|
||||
|
||||
| Item | Value |
|
||||
|------|--------|
|
||||
| Roadmap version | 1.0 |
|
||||
| Aligns with technical plan | As of repo snapshot; amend when blueprint changes |
|
||||
| Next reviews | After phase 1 API freeze; after first pilot dataset sign-off |
|
||||
435
hybx_routing_graph_data_model.md
Normal file
435
hybx_routing_graph_data_model.md
Normal file
@@ -0,0 +1,435 @@
|
||||
# HYBX Routing Graph Data Model
|
||||
|
||||
**Purpose:** Define the canonical **routing graph** used by the Compliance & Routing Sidecar (see [hybx_compliance_routing_sidecar_technical_plan.md](hybx_compliance_routing_sidecar_technical_plan.md)) for graph-based pathfinding, liquidity resolution, fee path construction, and jurisdiction-aware routing. This is the mathematical backbone for nostro pathfinding, multi-bank routing, liquidity selection, and failover as **alternate paths** on the same graph.
|
||||
|
||||
**Audience:** Engineers implementing the Routing Engine, orchestration adapters, and registry services.
|
||||
|
||||
**Non-goals:** Policy DSL grammar, liquidity registry service APIs, explanation-engine templates, and failover algorithms are specified elsewhere.
|
||||
|
||||
---
|
||||
|
||||
## 1. Purpose and scope
|
||||
|
||||
The routing graph models the **operational payment network** relevant to a transaction: institutions, account corridors, liquidity pools, FX venues, settlement rails, fee agents, and beneficiary anchors, connected by **directed, weighted** relationships.
|
||||
|
||||
- **In scope:** Vertex types, edge types, cost vectors, liquidity and fee metadata, jurisdiction overlays, serialization, versioning, and mapping from the Transaction Composer compile output.
|
||||
- **Out of scope:** Specific shortest-path or MOCO (multi-objective) solver implementations; this document defines **inputs** those solvers consume.
|
||||
|
||||
The Transaction Composer UI produces a **design-time pipeline graph** (user intent). The sidecar may **project** that into one or more **routing subgraphs** and enrich them with edges and weights that are not drawn in the UI (e.g. alternate correspondents, backup rails).
|
||||
|
||||
---
|
||||
|
||||
## 2. Conceptual model
|
||||
|
||||
Let **G = (V, E)** be a **directed graph** with:
|
||||
|
||||
- **V**: typed vertices (routing nodes).
|
||||
- **E**: typed directed edges with metadata and costs.
|
||||
|
||||
### 2.1 Two layers
|
||||
|
||||
| Layer | Meaning | Typical source of truth |
|
||||
|--------|---------|-------------------------|
|
||||
| **Topology** | Durable relationships: who may route to whom, which rails exist, static eligibility. | Reference data, contracts, registry |
|
||||
| **Snapshot** | Time-bound overlays: available liquidity, current fee quotes, latency estimates, risk adjustments for **this evaluation**. | Pool registry, market data, sidecar snapshot builder |
|
||||
|
||||
At evaluation time, the engine consumes a **materialized view**: topology ∪ snapshot overlays. The JSON envelope below supports both.
|
||||
|
||||
### 2.2 Relationship to sidecar API
|
||||
|
||||
The sidecar plan describes `POST /evaluate-transaction` with `transactionGraph` and responses including `routingPlan`. This document defines the **shape of the network graph** (and fragments thereof) that backs **routingPlan** generation—not the full HTTP schema.
|
||||
|
||||
---
|
||||
|
||||
## 3. Vertex (node) types
|
||||
|
||||
Vertices are discriminated by `vertexKind`. All vertices share a common base; extensions are kind-specific.
|
||||
|
||||
### 3.1 Base fields (all kinds)
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|--------|------|----------|-------------|
|
||||
| `id` | string | yes | Stable unique id in this graph (UUID or registry key). |
|
||||
| `vertexKind` | enum | yes | One of the kinds below. |
|
||||
| `displayName` | string | no | Human label. |
|
||||
| `jurisdiction` | string | yes | ISO-3166 alpha-2, or internal jurisdiction code (e.g. `ID-JK`). |
|
||||
| `identifiers` | object | no | `bic`, `lei`, `internalOrgId`, etc. |
|
||||
| `capabilities` | object | no | See below. |
|
||||
| `riskTier` | string \| number | no | Opaque tier for risk engine (e.g. `T1`–`T3`). |
|
||||
| `metadata` | object | no | Opaque extensibility. |
|
||||
|
||||
**Capabilities object (optional, common pattern):**
|
||||
|
||||
```json
|
||||
{
|
||||
"currenciesAllowed": ["USD", "IDR"],
|
||||
"fxPermitted": true,
|
||||
"settlementTypes": ["RTGS", "ACH"],
|
||||
"maxSingleTransfer": { "amount": "1000000000", "currency": "USD" }
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 Vertex kind: `Institution`
|
||||
|
||||
Legal or operational entity (bank, CB, PSP). Maps from composer participants and some operational nodes.
|
||||
|
||||
### 3.3 Vertex kind: `AccountCorridor`
|
||||
|
||||
Abstract **nostro/vostro or nostro-like** relationship endpoint—not necessarily one ledger row. Used for pathfinding between institutions.
|
||||
|
||||
- Typical use: attach liquidity and correspondent edges to corridors.
|
||||
|
||||
### 3.4 Vertex kind: `LiquidityPool`
|
||||
|
||||
A **source of fungible liquidity** in a currency (or synthetic pool id).
|
||||
|
||||
- Should align with sidecar liquidity sketch: currency, amount, provider, expiry (carried in snapshot overlay on the pool or on incident edges).
|
||||
|
||||
### 3.5 Vertex kind: `FxVenue`
|
||||
|
||||
FX conversion capability (desk, LP, internal book).
|
||||
|
||||
### 3.6 Vertex kind: `SettlementRail`
|
||||
|
||||
Logical settlement channel (RTGS system, chain leg, internal settlement batch).
|
||||
|
||||
### 3.7 Vertex kind: `FeeAgent`
|
||||
|
||||
Optional dedicated vertex when fees are not folded into `Institution` (e.g. third-party fee collector).
|
||||
|
||||
### 3.8 Vertex kind: `BeneficiaryAnchor`
|
||||
|
||||
Terminal or near-terminal node representing beneficiary credit location (could be institution + product, or a logical “credit to PT Parak at Bank Kanaya”).
|
||||
|
||||
---
|
||||
|
||||
## 4. Edge types
|
||||
|
||||
Edges are **directed**: `(sourceId, targetId)`. Each edge has:
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|--------|------|----------|-------------|
|
||||
| `id` | string | yes | Unique edge id. |
|
||||
| `sourceId` | string | yes | Vertex `id`. |
|
||||
| `targetId` | string | yes | Vertex `id`. |
|
||||
| `edgeKind` | enum | yes | See below. |
|
||||
| `costVector` | object | yes | Normalized costs (section 5). |
|
||||
| `jurisdictions` | string[] | no | Tags for crossing rules; default may inherit from endpoints. |
|
||||
| `validFrom` / `validTo` | string (ISO-8601) | no | Validity window for this edge. |
|
||||
| `policyRefs` | string[] | no | Opaque ids for future Policy DSL bindings. |
|
||||
| `liquidityRef` | string | no | Pool or line id when edge represents funding. |
|
||||
| `feeRef` | string | no | Link to fee schedule id. |
|
||||
| `metadata` | object | no | Extensibility. |
|
||||
|
||||
### 4.1 Edge kind: `correspondent`
|
||||
|
||||
Bank-to-bank (or institution-to-institution) routing leg; may attach to `AccountCorridor` vertices or directly to `Institution` depending on model granularity.
|
||||
|
||||
### 4.2 Edge kind: `liquidity_link`
|
||||
|
||||
Connects a pool to a corridor, venue, or institution **consumption** point.
|
||||
|
||||
### 4.3 Edge kind: `fx_quote`
|
||||
|
||||
Connects currency/state A to B through an `FxVenue` (often modeled as two edges via the venue, or one bundled edge with pair metadata in `metadata`).
|
||||
|
||||
### 4.4 Edge kind: `fee_hop`
|
||||
|
||||
Explicit fee accrual or pass-through segment (supports building a **fee propagation tree** as a subgraph).
|
||||
|
||||
### 4.5 Edge kind: `settlement_path`
|
||||
|
||||
Connects to or from a `SettlementRail` or `BeneficiaryAnchor`.
|
||||
|
||||
---
|
||||
|
||||
## 5. Weights and multi-criteria cost
|
||||
|
||||
The sidecar Routing Engine (see technical plan) uses a **weighted directed graph** with dimensions including **fee cost**, **latency**, and **liquidity availability**.
|
||||
|
||||
### 5.1 `costVector` (required on every edge)
|
||||
|
||||
Recommended normalized fields (all optional numbers except at least one should be present for pathfinding):
|
||||
|
||||
| Key | Meaning | Units / notes |
|
||||
|-----|---------|----------------|
|
||||
| `feeCost` | Expected monetary cost of traversing this edge | Normalized to a reference currency in snapshot builder, or raw with `feeCurrency` in `metadata` |
|
||||
| `latencyMs` | Expected processing time | Milliseconds or representative score |
|
||||
| `liquidityAvailability` | How “easy” it is to fund this hop | 0–1 score, or available notional on this hop |
|
||||
| `regulatoryPenalty` | Additive penalty from policy | Non-negative; 0 if none |
|
||||
| `reliability` | Historical success / health | 0–1, can be inverted by solver |
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
"costVector": {
|
||||
"feeCost": 1250.5,
|
||||
"latencyMs": 800,
|
||||
"liquidityAvailability": 0.92,
|
||||
"regulatoryPenalty": 0,
|
||||
"reliability": 0.995
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 Aggregation strategy (normative intent, not algorithm)
|
||||
|
||||
Path cost is a **multi-criteria** problem:
|
||||
|
||||
1. **Hard constraints:** e.g. minimum liquidity availability, blocked jurisdictions, expired `validTo`.
|
||||
2. **Scalarization:** weighted sum `w1*feeCost + w2*latencyMs + w3*(1-liquidityAvailability) + w4*regulatoryPenalty + w5*(1-reliability)` with configurable weights per product lane.
|
||||
3. **Pareto / k-shortest:** optional second phase to present alternates for failover UX.
|
||||
|
||||
Exact solver choice is implementation-defined; this document standardizes **what** is measured on each edge.
|
||||
|
||||
---
|
||||
|
||||
## 6. Liquidity metadata
|
||||
|
||||
Aligned with the sidecar liquidity model (currency, amount, provider, expiry):
|
||||
|
||||
**On `LiquidityPool` vertex or `liquidity_link` edge (snapshot):**
|
||||
|
||||
| Field | Description |
|
||||
|--------|-------------|
|
||||
| `poolId` | Registry identifier |
|
||||
| `currency` | ISO-4217 |
|
||||
| `availableAmount` | Decimal string recommended |
|
||||
| `providerId` | Institution or LP id |
|
||||
| `expiresAt` | ISO-8601 |
|
||||
| `refreshTtlSeconds` | Optional hint for cache |
|
||||
|
||||
---
|
||||
|
||||
## 7. Fee metadata
|
||||
|
||||
Fees may be attached to `fee_hop` edges or embedded in `correspondent` / `settlement_path` via `feeRef`.
|
||||
|
||||
**Suggested fields (snapshot or static):**
|
||||
|
||||
| Field | Description |
|
||||
|--------|-------------|
|
||||
| `feeModel` | `percent`, `flat`, `tiered`, `conditional` |
|
||||
| `bps` | Basis points if percent |
|
||||
| `flatAmount` | If flat |
|
||||
| `currency` | Fee currency |
|
||||
| `conditionRef` | Opaque id for conditional rules (Policy DSL later) |
|
||||
|
||||
**Fee propagation tree:** Derived by taking the subgraph induced by `fee_hop` edges (and optionally fee-bearing segments), orienting edges in flow direction, and interpreting parent/child as **payer → collector → onward**. The routing graph remains the single source of truth; the tree is a **view**, not a second graph.
|
||||
|
||||
---
|
||||
|
||||
## 8. Jurisdiction overlays
|
||||
|
||||
- **Node default:** Each vertex has a primary `jurisdiction`.
|
||||
- **Edge override:** `jurisdictions[]` on an edge marks **crossing** or **rule buckets** (e.g. `US-OFAC`, `EU-PII`).
|
||||
- **Transaction-level filter:** Evaluation request may supply `transactionJurisdictions` or `denyJurisdictionTags`; the pathfinder **prunes** edges violating hard policy (details in Policy DSL doc).
|
||||
|
||||
Compliance outcomes (PASS / WARN / FAIL) are produced by the Compliance Engine; this model only supplies **tags and penalties** (`regulatoryPenalty`) consumed by routing.
|
||||
|
||||
---
|
||||
|
||||
## 9. Mapping from Transaction Composer
|
||||
|
||||
The Transaction Composer ([transaction-composer/](transaction-composer/)) compiles UI graphs into `CompiledTransaction` with buckets: `participants`, `nostroAccounts`, `liquidity`, `fx`, `fees`, `settlement`, plus `topology`.
|
||||
|
||||
**Composer is a pipeline; routing graph is a network.** The table below is a **projection guide**, not a 1:1 id equality (routing vertices may be created per registry lookup).
|
||||
|
||||
| Composer source | Compiler bucket / `NodeKind` | Typical routing vertex kind(s) | Notes |
|
||||
|-----------------|--------------------------------|---------------------------------|--------|
|
||||
| Central / commercial / remittance bank | `participants` (`centralBank`, `commercialBank`, `remittanceInstitution`) | `Institution` | Map `institution` string to registry id; `participantRole` informs source vs beneficiary anchor. |
|
||||
| Nostro | `nostroAccounts` | `AccountCorridor`, `Institution` | Often one corridor per nostro relationship. |
|
||||
| Liquidity | `liquidity` (`liquidityProvider`) | `LiquidityPool`, `Institution`, `liquidity_link` | Pool may be resolved via Liquidity Pool Registry. |
|
||||
| FX | `fx` (`fxConversion`) | `FxVenue`, `fx_quote` edges | May expand to venue + pair legs. |
|
||||
| Fees | `fees` (`feeRouter`) | `FeeAgent` or `Institution`, `fee_hop` | Multiple fee nodes become multiple hops or a small fee subgraph. |
|
||||
| Settlement | `settlement` | `SettlementRail`, `BeneficiaryAnchor` | Beneficiary text may map to anchor + rail. |
|
||||
| Topology edges | `topology.edges` | Mixed `edgeKind` | Composer edges imply **ordering**; routing edges add **weights** and **alternates**. |
|
||||
|
||||
**Important:** The composer `topology.orderedNodeIds` defines **design order**. The routing engine may introduce **parallel paths** (e.g. two correspondent edges) not present in the UI graph.
|
||||
|
||||
---
|
||||
|
||||
## 10. Serialization and versioning
|
||||
|
||||
### 10.1 JSON envelope
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "1.0.0",
|
||||
"graphId": "rg-2026-03-29-indonesia-demo",
|
||||
"effectiveAt": "2026-03-29T12:00:00Z",
|
||||
"vertices": [],
|
||||
"edges": [],
|
||||
"overlays": {
|
||||
"liquiditySnapshotId": "optional-registry-pointer",
|
||||
"feeScheduleSnapshotId": "optional",
|
||||
"notes": "optional"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 10.2 Stability rules
|
||||
|
||||
- **Patch** (1.0.x): Add optional fields only; do not remove or rename required fields.
|
||||
- **Minor** (1.x.0): Add new `vertexKind` / `edgeKind` values; old consumers ignore unknown kinds if possible.
|
||||
- **Major** (x.0.0): Breaking renames or semantic changes.
|
||||
|
||||
---
|
||||
|
||||
## 11. Worked example (minimal)
|
||||
|
||||
Scenario aligned with composer demo intent: **OMNL → BNI nostro/liquidity → USD/IDR FX → fees → settlement at Bank Kanaya; beneficiary PT Parak International.** Includes one **alternate** `correspondent` edge as a failover hint (not expanded into full failover logic).
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "1.0.0",
|
||||
"graphId": "example-omnl-bni-kanaya",
|
||||
"effectiveAt": "2026-03-29T12:00:00Z",
|
||||
"vertices": [
|
||||
{
|
||||
"id": "v-omnl",
|
||||
"vertexKind": "Institution",
|
||||
"displayName": "OMNL",
|
||||
"jurisdiction": "ID",
|
||||
"identifiers": { "internalOrgId": "OMNL" },
|
||||
"capabilities": { "currenciesAllowed": ["USD"], "fxPermitted": false }
|
||||
},
|
||||
{
|
||||
"id": "v-bni",
|
||||
"vertexKind": "Institution",
|
||||
"displayName": "BNI",
|
||||
"jurisdiction": "ID",
|
||||
"identifiers": { "bic": "BNINIDJA" }
|
||||
},
|
||||
{
|
||||
"id": "v-nostro-bni-usd",
|
||||
"vertexKind": "AccountCorridor",
|
||||
"displayName": "BNI USD Nostro corridor",
|
||||
"jurisdiction": "ID",
|
||||
"identifiers": { "internalOrgId": "BNI", "currency": "USD" }
|
||||
},
|
||||
{
|
||||
"id": "v-pool-bni-usd",
|
||||
"vertexKind": "LiquidityPool",
|
||||
"displayName": "BNI USD pool",
|
||||
"jurisdiction": "ID",
|
||||
"metadata": {
|
||||
"currency": "USD",
|
||||
"availableAmount": "70000000000",
|
||||
"providerId": "v-bni",
|
||||
"expiresAt": "2026-03-29T18:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "v-fx-bni",
|
||||
"vertexKind": "FxVenue",
|
||||
"displayName": "BNI FX",
|
||||
"jurisdiction": "ID",
|
||||
"capabilities": { "currenciesAllowed": ["USD", "IDR"], "fxPermitted": true }
|
||||
},
|
||||
{
|
||||
"id": "v-rail-id",
|
||||
"vertexKind": "SettlementRail",
|
||||
"displayName": "ID domestic settlement",
|
||||
"jurisdiction": "ID",
|
||||
"capabilities": { "settlementTypes": ["RTGS"] }
|
||||
},
|
||||
{
|
||||
"id": "v-kanaya",
|
||||
"vertexKind": "Institution",
|
||||
"displayName": "Bank Kanaya",
|
||||
"jurisdiction": "ID"
|
||||
},
|
||||
{
|
||||
"id": "v-parak",
|
||||
"vertexKind": "BeneficiaryAnchor",
|
||||
"displayName": "PT Parak International",
|
||||
"jurisdiction": "ID",
|
||||
"metadata": { "creditInstitutionId": "v-kanaya" }
|
||||
},
|
||||
{
|
||||
"id": "v-alt-correspondent",
|
||||
"vertexKind": "Institution",
|
||||
"displayName": "Alternate correspondent (failover candidate)",
|
||||
"jurisdiction": "ID"
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"id": "e-omnl-nostro",
|
||||
"sourceId": "v-omnl",
|
||||
"targetId": "v-nostro-bni-usd",
|
||||
"edgeKind": "correspondent",
|
||||
"costVector": { "feeCost": 0, "latencyMs": 200, "liquidityAvailability": 1, "regulatoryPenalty": 0, "reliability": 0.999 }
|
||||
},
|
||||
{
|
||||
"id": "e-nostro-pool",
|
||||
"sourceId": "v-nostro-bni-usd",
|
||||
"targetId": "v-pool-bni-usd",
|
||||
"edgeKind": "liquidity_link",
|
||||
"liquidityRef": "pool-bni-usd-001",
|
||||
"costVector": { "feeCost": 0, "latencyMs": 50, "liquidityAvailability": 0.95, "regulatoryPenalty": 0, "reliability": 0.998 }
|
||||
},
|
||||
{
|
||||
"id": "e-pool-fx",
|
||||
"sourceId": "v-pool-bni-usd",
|
||||
"targetId": "v-fx-bni",
|
||||
"edgeKind": "liquidity_link",
|
||||
"costVector": { "feeCost": 0, "latencyMs": 100, "liquidityAvailability": 0.93, "regulatoryPenalty": 0, "reliability": 0.997 }
|
||||
},
|
||||
{
|
||||
"id": "e-fx-rail",
|
||||
"sourceId": "v-fx-bni",
|
||||
"targetId": "v-rail-id",
|
||||
"edgeKind": "fx_quote",
|
||||
"metadata": { "pair": "USD/IDR", "rateRef": "DEMO-15000" },
|
||||
"costVector": { "feeCost": 500, "latencyMs": 400, "liquidityAvailability": 0.9, "regulatoryPenalty": 0, "reliability": 0.996 }
|
||||
},
|
||||
{
|
||||
"id": "e-fee-compliance",
|
||||
"sourceId": "v-rail-id",
|
||||
"targetId": "v-kanaya",
|
||||
"edgeKind": "fee_hop",
|
||||
"feeRef": "fee-reg-omnl-bni",
|
||||
"costVector": { "feeCost": 200, "latencyMs": 50, "liquidityAvailability": 1, "regulatoryPenalty": 0, "reliability": 0.999 }
|
||||
},
|
||||
{
|
||||
"id": "e-settle-parak",
|
||||
"sourceId": "v-kanaya",
|
||||
"targetId": "v-parak",
|
||||
"edgeKind": "settlement_path",
|
||||
"costVector": { "feeCost": 0, "latencyMs": 300, "liquidityAvailability": 1, "regulatoryPenalty": 0, "reliability": 0.998 }
|
||||
},
|
||||
{
|
||||
"id": "e-omnl-alt-nostro",
|
||||
"sourceId": "v-omnl",
|
||||
"targetId": "v-alt-correspondent",
|
||||
"edgeKind": "correspondent",
|
||||
"costVector": { "feeCost": 800, "latencyMs": 350, "liquidityAvailability": 0.7, "regulatoryPenalty": 0, "reliability": 0.99 },
|
||||
"metadata": { "role": "failover_candidate" }
|
||||
}
|
||||
],
|
||||
"overlays": {
|
||||
"liquiditySnapshotId": "demo-snapshot-001",
|
||||
"notes": "Demonstration only; ids and costs are illustrative."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Related work (next documents)
|
||||
|
||||
| Next artifact | What this routing graph doc provides |
|
||||
|---------------|--------------------------------------|
|
||||
| **Policy DSL specification** | `policyRefs` on edges, `regulatoryPenalty`, jurisdiction tags, and constraint hooks for hard rejects. |
|
||||
| **Liquidity pool registry model** | Canonical `poolId`, refresh TTL, and binding to `LiquidityPool` vertices / `liquidity_link` edges. |
|
||||
| **Decision explanation engine** | Path trace over `vertices`/`edges` with human-readable labels and cost breakdowns from `costVector`. |
|
||||
| **Failover routing strategy model** | k-shortest paths, edge_disjoint alternates, and scoring on top of this graph without redefining V/E types. |
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [hybx_compliance_routing_sidecar_technical_plan.md](hybx_compliance_routing_sidecar_technical_plan.md) — sidecar architecture, Routing Engine, API sketch.
|
||||
- [transaction-composer/src/types/nodeTypes.ts](transaction-composer/src/types/nodeTypes.ts) — `NodeKind`, `TransactionNodeData`.
|
||||
- [transaction-composer/src/orchestration/transactionCompiler.ts](transaction-composer/src/orchestration/transactionCompiler.ts) — `CompiledTransaction` buckets and topology.
|
||||
53
info-defi-oracle-138/README.md
Normal file
53
info-defi-oracle-138/README.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# info.defi-oracle.io — Chain 138 public hub
|
||||
|
||||
Static SPA (Vite + React) for **https://info.defi-oracle.io**: compliant **c\*** tokens, **cW\*** registry (from `cross-chain-pmm-lps/config/deployment-status.json`), live **pools** and **quotes** from the token-aggregation API, **CCIP routing** tables, and wallet **swapExactIn** on `DODOPMMIntegration`.
|
||||
|
||||
## Develop
|
||||
|
||||
```bash
|
||||
cd /path/to/proxmox
|
||||
pnpm install
|
||||
pnpm --filter info-defi-oracle-138 dev
|
||||
```
|
||||
|
||||
Open http://localhost:5180
|
||||
|
||||
## Environment (optional)
|
||||
|
||||
| Variable | Purpose |
|
||||
|----------|---------|
|
||||
| `VITE_TOKEN_AGGREGATION_API_BASE` | Token-aggregation origin (default `https://dbis-api.d-bis.org`). The client also tries the same host with `/token-aggregation` prefix and `https://explorer.d-bis.org` variants until one returns HTTP 200. |
|
||||
| `VITE_RPC_URL_138` | Chain 138 RPC for wallet / reads (default `https://rpc.defi-oracle.io`) |
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
export VITE_TOKEN_AGGREGATION_API_BASE=https://dbis-api.d-bis.org
|
||||
export VITE_RPC_URL_138=https://rpc.defi-oracle.io
|
||||
pnpm --filter info-defi-oracle-138 build
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
pnpm --filter info-defi-oracle-138 build
|
||||
```
|
||||
|
||||
Output: `info-defi-oracle-138/dist/` — deploy as static files (any CDN, Nginx, object storage, or LXC).
|
||||
|
||||
## Deploy `info.defi-oracle.io`
|
||||
|
||||
1. **DNS:** Point `info.defi-oracle.io` (A/AAAA or CNAME) to your edge (Cloudflare, NPMplus host, etc.).
|
||||
2. **TLS:** Issue certificate for `info.defi-oracle.io` (same pattern as `rpc.defi-oracle.io`).
|
||||
3. **NPMplus (or Nginx):** Proxy host → upstream serving `dist/` with `try_files $uri $uri/ /index.html` for SPA routes.
|
||||
4. **API:** Ensure `VITE_TOKEN_AGGREGATION_API_BASE` at build time points to a **public** token-aggregation URL that allows browser CORS (service already uses `cors()`).
|
||||
|
||||
See also: `docs/04-configuration/E2E_ENDPOINTS_LIST.md` (dbis-api hosts), `docs/04-configuration/ALL_VMIDS_ENDPOINTS.md` (VMID targets).
|
||||
|
||||
## Refreshing cW\* addresses
|
||||
|
||||
cW\* tables are **bundled at build** from `cross-chain-pmm-lps/config/deployment-status.json`. Rebuild and redeploy after registry changes.
|
||||
|
||||
## Canonical on-chain addresses
|
||||
|
||||
Built-in constants follow `docs/11-references/CONTRACT_ADDRESSES_REFERENCE.md` (e.g. `DODOPMMIntegration`, cUSDT/cUSDC on 138).
|
||||
23
info-defi-oracle-138/index.html
Normal file
23
info-defi-oracle-138/index.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Chain 138 (DeFi Oracle Meta Mainnet): compliant c* tokens, bridged cW* registry, live pools, routing, and atomic swaps."
|
||||
/>
|
||||
<title>info.defi-oracle.io — Chain 138</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,600;0,9..40,700;1,9..40,400&family=Instrument+Serif:ital@0;1&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
27
info-defi-oracle-138/package.json
Normal file
27
info-defi-oracle-138/package.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "info-defi-oracle-138",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -p tsconfig.json && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "^5.90.21",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.30.3",
|
||||
"viem": "^2.46.1",
|
||||
"wagmi": "^2.19.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.10.2",
|
||||
"@types/react": "^18.3.28",
|
||||
"@types/react-dom": "^18.3.7",
|
||||
"@vitejs/plugin-react": "^4.7.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^5.4.21"
|
||||
}
|
||||
}
|
||||
5
info-defi-oracle-138/public/favicon.svg
Normal file
5
info-defi-oracle-138/public/favicon.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none">
|
||||
<rect width="32" height="32" rx="6" fill="#0f172a"/>
|
||||
<path d="M8 22V10l4 6 4-6v12" stroke="#38bdf8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="22" cy="16" r="4" stroke="#a78bfa" stroke-width="2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 318 B |
25
info-defi-oracle-138/src/App.tsx
Normal file
25
info-defi-oracle-138/src/App.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
|
||||
import { Layout } from '@/components/Layout';
|
||||
import { HomePage } from '@/pages/HomePage';
|
||||
import { TokensPage } from '@/pages/TokensPage';
|
||||
import { PoolsPage } from '@/pages/PoolsPage';
|
||||
import { SwapPage } from '@/pages/SwapPage';
|
||||
import { RoutingPage } from '@/pages/RoutingPage';
|
||||
|
||||
export function App() {
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route element={<Layout />}>
|
||||
<Route path="/" element={<HomePage />} />
|
||||
<Route path="/chain-138" element={<Navigate to="/" replace />} />
|
||||
<Route path="/tokens" element={<TokensPage />} />
|
||||
<Route path="/pools" element={<PoolsPage />} />
|
||||
<Route path="/swap" element={<SwapPage />} />
|
||||
<Route path="/routing" element={<RoutingPage />} />
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
);
|
||||
}
|
||||
44
info-defi-oracle-138/src/abi/integration.ts
Normal file
44
info-defi-oracle-138/src/abi/integration.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
export const dodopmmIntegrationAbi = [
|
||||
{
|
||||
name: 'swapExactIn',
|
||||
type: 'function',
|
||||
stateMutability: 'nonpayable',
|
||||
inputs: [
|
||||
{ name: 'pool', type: 'address' },
|
||||
{ name: 'tokenIn', type: 'address' },
|
||||
{ name: 'amountIn', type: 'uint256' },
|
||||
{ name: 'minAmountOut', type: 'uint256' },
|
||||
],
|
||||
outputs: [{ name: 'amountOut', type: 'uint256' }],
|
||||
},
|
||||
] as const;
|
||||
|
||||
export const erc20Abi = [
|
||||
{
|
||||
name: 'approve',
|
||||
type: 'function',
|
||||
stateMutability: 'nonpayable',
|
||||
inputs: [
|
||||
{ name: 'spender', type: 'address' },
|
||||
{ name: 'amount', type: 'uint256' },
|
||||
],
|
||||
outputs: [{ type: 'bool' }],
|
||||
},
|
||||
{
|
||||
name: 'allowance',
|
||||
type: 'function',
|
||||
stateMutability: 'view',
|
||||
inputs: [
|
||||
{ name: 'owner', type: 'address' },
|
||||
{ name: 'spender', type: 'address' },
|
||||
],
|
||||
outputs: [{ type: 'uint256' }],
|
||||
},
|
||||
{
|
||||
name: 'decimals',
|
||||
type: 'function',
|
||||
stateMutability: 'view',
|
||||
inputs: [],
|
||||
outputs: [{ type: 'uint8' }],
|
||||
},
|
||||
] as const;
|
||||
49
info-defi-oracle-138/src/api/client.ts
Normal file
49
info-defi-oracle-138/src/api/client.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { TOKEN_AGGREGATION_BASE } from '@/config';
|
||||
|
||||
export class ApiError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public status: number,
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'ApiError';
|
||||
}
|
||||
}
|
||||
|
||||
/** Try primary URL, then /token-aggregation prefix on same origin, then public explorer hosts. */
|
||||
function aggregationBases(): string[] {
|
||||
const primary = TOKEN_AGGREGATION_BASE.replace(/\/$/, '');
|
||||
let origin = '';
|
||||
try {
|
||||
origin = new URL(primary).origin;
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
const list = [
|
||||
primary,
|
||||
...(origin ? [`${origin}/token-aggregation`] : []),
|
||||
'https://explorer.d-bis.org',
|
||||
'https://explorer.d-bis.org/token-aggregation',
|
||||
].filter(Boolean);
|
||||
return [...new Set(list)];
|
||||
}
|
||||
|
||||
export async function fetchApi<T>(path: string): Promise<T> {
|
||||
const p = path.startsWith('/') ? path : `/${path}`;
|
||||
let last: Error | null = null;
|
||||
for (const base of aggregationBases()) {
|
||||
const url = `${base}${p}`;
|
||||
try {
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) {
|
||||
const text = await res.text().catch(() => '');
|
||||
last = new ApiError(text || res.statusText, res.status);
|
||||
continue;
|
||||
}
|
||||
return res.json() as Promise<T>;
|
||||
} catch (e) {
|
||||
last = e instanceof Error ? e : new Error(String(e));
|
||||
}
|
||||
}
|
||||
throw last || new Error('All token-aggregation API bases failed');
|
||||
}
|
||||
61
info-defi-oracle-138/src/api/types.ts
Normal file
61
info-defi-oracle-138/src/api/types.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
export type TokenRow = {
|
||||
address: string;
|
||||
symbol?: string;
|
||||
name?: string;
|
||||
decimals?: number;
|
||||
hasDodoPool?: boolean;
|
||||
pmmPool?: string | null;
|
||||
market?: { priceUsd?: string; volume24h?: string; tvl?: string };
|
||||
};
|
||||
|
||||
export type TokensResponse = {
|
||||
tokens: TokenRow[];
|
||||
pagination: { limit: number; offset: number; count: number };
|
||||
};
|
||||
|
||||
export type PoolRow = {
|
||||
address: string;
|
||||
dex?: string;
|
||||
token0?: { address: string; symbol?: string };
|
||||
token1?: { address: string; symbol?: string };
|
||||
reserves?: { reserve0?: string; reserve1?: string };
|
||||
tvl?: string;
|
||||
volume24h?: string;
|
||||
feeTier?: string;
|
||||
};
|
||||
|
||||
export type PoolsResponse = { pools: PoolRow[] };
|
||||
|
||||
export type QuoteResponse = {
|
||||
amountOut: string | null;
|
||||
poolAddress?: string | null;
|
||||
dexType?: string | null;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
export type BridgeRoutesResponse = {
|
||||
routes: {
|
||||
weth9: Record<string, string>;
|
||||
weth10: Record<string, string>;
|
||||
trustless?: Record<string, string>;
|
||||
};
|
||||
chain138Bridges: { weth9: string; weth10: string; trustless?: string };
|
||||
tokenMappingApi?: unknown;
|
||||
};
|
||||
|
||||
export type TokenMappingResponse = {
|
||||
tokens?: string[];
|
||||
addressMapFromTo?: Record<string, string>;
|
||||
addressMapToFrom?: Record<string, string>;
|
||||
};
|
||||
|
||||
export type NetworksResponse = {
|
||||
version: string;
|
||||
networks: Array<{
|
||||
chainId: string;
|
||||
chainIdDecimal: number;
|
||||
chainName: string;
|
||||
rpcUrls: string[];
|
||||
blockExplorerUrls?: string[];
|
||||
}>;
|
||||
};
|
||||
17
info-defi-oracle-138/src/chains.ts
Normal file
17
info-defi-oracle-138/src/chains.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { defineChain } from 'viem';
|
||||
|
||||
export const chain138 = defineChain({
|
||||
id: 138,
|
||||
name: 'DeFi Oracle Meta Mainnet',
|
||||
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
||||
rpcUrls: {
|
||||
default: {
|
||||
http: [
|
||||
(import.meta.env.VITE_RPC_URL_138 as string | undefined) || 'https://rpc.defi-oracle.io',
|
||||
],
|
||||
},
|
||||
},
|
||||
blockExplorers: {
|
||||
default: { name: 'Blockscout', url: 'https://explorer.d-bis.org' },
|
||||
},
|
||||
});
|
||||
50
info-defi-oracle-138/src/components/Layout.tsx
Normal file
50
info-defi-oracle-138/src/components/Layout.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { NavLink, Outlet } from 'react-router-dom';
|
||||
import { CHAIN_ID, EXPLORER_BASE, TOKEN_AGGREGATION_BASE } from '@/config';
|
||||
|
||||
const linkClass = ({ isActive }: { isActive: boolean }) => (isActive ? 'active' : undefined);
|
||||
|
||||
export function Layout() {
|
||||
return (
|
||||
<div className="layout">
|
||||
<header className="site">
|
||||
<div className="brand">
|
||||
info.defi-oracle.io
|
||||
<span> — Chain {CHAIN_ID}</span>
|
||||
</div>
|
||||
<nav className="site">
|
||||
<NavLink to="/" end className={linkClass}>
|
||||
Overview
|
||||
</NavLink>
|
||||
<NavLink to="/tokens" className={linkClass}>
|
||||
c* & cW*
|
||||
</NavLink>
|
||||
<NavLink to="/pools" className={linkClass}>
|
||||
Pools
|
||||
</NavLink>
|
||||
<NavLink to="/swap" className={linkClass}>
|
||||
Swap
|
||||
</NavLink>
|
||||
<NavLink to="/routing" className={linkClass}>
|
||||
Routing
|
||||
</NavLink>
|
||||
</nav>
|
||||
</header>
|
||||
<Outlet />
|
||||
<footer className="site">
|
||||
<p>
|
||||
Live data from token-aggregation API:{' '}
|
||||
<span className="mono">{TOKEN_AGGREGATION_BASE}</span>
|
||||
<br />
|
||||
Explorer:{' '}
|
||||
<a href={EXPLORER_BASE} target="_blank" rel="noreferrer">
|
||||
{EXPLORER_BASE}
|
||||
</a>
|
||||
{' · '}
|
||||
Repo docs:{' '}
|
||||
<span className="mono">docs/04-configuration/DEX_AND_AGGREGATORS_CHAIN138_EXPLAINER.md</span>,{' '}
|
||||
<span className="mono">docs/11-references/CW_STAR_CMC_COINGECKO_LISTING_STATUS.md</span>
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
18
info-defi-oracle-138/src/config.ts
Normal file
18
info-defi-oracle-138/src/config.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
/** Chain 138 hub — align with docs/11-references/CONTRACT_ADDRESSES_REFERENCE.md */
|
||||
export const CHAIN_ID = 138;
|
||||
|
||||
export const EXPLORER_BASE = 'https://explorer.d-bis.org';
|
||||
|
||||
export const DODOPMM_INTEGRATION =
|
||||
'0x5BDc62f1ae7D630c37A8B363a1d49845356Ee72d' as const;
|
||||
|
||||
export const TOKEN_AGGREGATION_BASE =
|
||||
(import.meta.env.VITE_TOKEN_AGGREGATION_API_BASE as string | undefined)?.replace(/\/$/, '') ||
|
||||
'https://dbis-api.d-bis.org';
|
||||
|
||||
export const RPC_URL_138 =
|
||||
(import.meta.env.VITE_RPC_URL_138 as string | undefined) || 'https://rpc.defi-oracle.io';
|
||||
|
||||
/** Canonical cUSDT / cUSDC on Chain 138 (see EXPLORER_TOKEN_LIST_CROSSCHECK) */
|
||||
export const CANONICAL_CUSDT = '0x93E66202A11B1772E55407B32B44e5Cd8eda7f22';
|
||||
export const CANONICAL_CUSDC = '0xf22258f57794CC8E06237084b353Ab30fFfa640b';
|
||||
34
info-defi-oracle-138/src/data/deployment-status.ts
Normal file
34
info-defi-oracle-138/src/data/deployment-status.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Bridged cW* registry (public EVM chains). Source: cross-chain-pmm-lps/config/deployment-status.json
|
||||
* Re-import or sync when addresses change.
|
||||
*/
|
||||
import raw from '../../../cross-chain-pmm-lps/config/deployment-status.json';
|
||||
|
||||
export type DeploymentStatus = typeof raw;
|
||||
|
||||
export const deploymentStatus: DeploymentStatus = raw;
|
||||
|
||||
export function listCwChains(): Array<{
|
||||
chainId: string;
|
||||
name: string;
|
||||
tokens: Array<{ symbol: string; address: string }>;
|
||||
}> {
|
||||
const out: Array<{
|
||||
chainId: string;
|
||||
name: string;
|
||||
tokens: Array<{ symbol: string; address: string }>;
|
||||
}> = [];
|
||||
const chains = deploymentStatus.chains as Record<
|
||||
string,
|
||||
{ name: string; cwTokens: Record<string, string> }
|
||||
>;
|
||||
for (const [chainId, row] of Object.entries(chains)) {
|
||||
const tokens = Object.entries(row.cwTokens || {}).map(([symbol, address]) => ({
|
||||
symbol,
|
||||
address,
|
||||
}));
|
||||
if (tokens.length === 0) continue;
|
||||
out.push({ chainId, name: row.name, tokens });
|
||||
}
|
||||
return out.sort((a, b) => Number(a.chainId) - Number(b.chainId));
|
||||
}
|
||||
33
info-defi-oracle-138/src/main.tsx
Normal file
33
info-defi-oracle-138/src/main.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { WagmiProvider, createConfig, http } from 'wagmi';
|
||||
import { injected } from 'wagmi/connectors';
|
||||
import { chain138 } from '@/chains';
|
||||
import { RPC_URL_138 } from '@/config';
|
||||
import { App } from '@/App';
|
||||
import '@/styles/global.css';
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: { retry: 1, refetchOnWindowFocus: false },
|
||||
},
|
||||
});
|
||||
|
||||
const wagmiConfig = createConfig({
|
||||
chains: [chain138],
|
||||
connectors: [injected()],
|
||||
transports: {
|
||||
[chain138.id]: http(RPC_URL_138),
|
||||
},
|
||||
});
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<WagmiProvider config={wagmiConfig}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<App />
|
||||
</QueryClientProvider>
|
||||
</WagmiProvider>
|
||||
</StrictMode>,
|
||||
);
|
||||
85
info-defi-oracle-138/src/pages/HomePage.tsx
Normal file
85
info-defi-oracle-138/src/pages/HomePage.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { CHAIN_ID, EXPLORER_BASE, RPC_URL_138 } from '@/config';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { fetchApi } from '@/api/client';
|
||||
import type { NetworksResponse } from '@/api/types';
|
||||
|
||||
export function HomePage() {
|
||||
const { data, isError } = useQuery({
|
||||
queryKey: ['networks'],
|
||||
queryFn: () => fetchApi<NetworksResponse>('/api/v1/networks'),
|
||||
staleTime: 60_000,
|
||||
});
|
||||
|
||||
const n138 = data?.networks?.find((x) => x.chainIdDecimal === CHAIN_ID);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>DeFi Oracle Meta Mainnet</h1>
|
||||
<p className="lead">
|
||||
Public hub for compliant <strong>c*</strong> tokens on Chain {CHAIN_ID}, the{' '}
|
||||
<strong>cW*</strong> bridged registry on partner networks, live PMM pools, bridge routing, and
|
||||
single-hop atomic swaps via DODOPMMIntegration.
|
||||
</p>
|
||||
|
||||
<div className="card-grid">
|
||||
<Link to="/tokens" className="card" style={{ textDecoration: 'none', color: 'inherit' }}>
|
||||
<h3>c* & cW*</h3>
|
||||
<p>Canonical Chain {CHAIN_ID} tokens from the API plus bridged cW* addresses per chain.</p>
|
||||
</Link>
|
||||
<Link to="/pools" className="card" style={{ textDecoration: 'none', color: 'inherit' }}>
|
||||
<h3>Pools</h3>
|
||||
<p>Indexed liquidity from token-aggregation (refreshes with indexer).</p>
|
||||
</Link>
|
||||
<Link to="/swap" className="card" style={{ textDecoration: 'none', color: 'inherit' }}>
|
||||
<h3>Atomic swap</h3>
|
||||
<p>Quote + wallet: approve and call swapExactIn on the integration contract.</p>
|
||||
</Link>
|
||||
<Link to="/routing" className="card" style={{ textDecoration: 'none', color: 'inherit' }}>
|
||||
<h3>Routing</h3>
|
||||
<p>CCIP WETH bridge table and cross-chain token mapping for 138.</p>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<h2>Network</h2>
|
||||
{isError && (
|
||||
<p className="muted">
|
||||
Could not load <span className="mono">/api/v1/networks</span> — check API base URL and CORS.
|
||||
Default RPC below still works for wallets.
|
||||
</p>
|
||||
)}
|
||||
<div className="table-wrap">
|
||||
<table className="data">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Chain ID</th>
|
||||
<td className="mono">{CHAIN_ID}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>RPC (fallback)</th>
|
||||
<td className="mono">{RPC_URL_138}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Explorer</th>
|
||||
<td>
|
||||
<a href={EXPLORER_BASE}>{EXPLORER_BASE}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{n138 && (
|
||||
<>
|
||||
<tr>
|
||||
<th>Name (API)</th>
|
||||
<td>{n138.chainName}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>RPC (API)</th>
|
||||
<td className="mono">{n138.rpcUrls?.[0] ?? '—'}</td>
|
||||
</tr>
|
||||
</>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
116
info-defi-oracle-138/src/pages/PoolsPage.tsx
Normal file
116
info-defi-oracle-138/src/pages/PoolsPage.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { fetchApi } from '@/api/client';
|
||||
import type { PoolsResponse, TokensResponse } from '@/api/types';
|
||||
import { CHAIN_ID, EXPLORER_BASE } from '@/config';
|
||||
|
||||
function explorerAddress(addr: string) {
|
||||
return `${EXPLORER_BASE}/address/${addr}`;
|
||||
}
|
||||
|
||||
async function fetchPoolsForToken(address: string): Promise<PoolsResponse['pools']> {
|
||||
try {
|
||||
const res = await fetchApi<PoolsResponse>(
|
||||
`/api/v1/tokens/${address}/pools?chainId=${CHAIN_ID}`,
|
||||
);
|
||||
return res.pools || [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export function PoolsPage() {
|
||||
const tokensQuery = useQuery({
|
||||
queryKey: ['tokens-pools', CHAIN_ID],
|
||||
queryFn: () =>
|
||||
fetchApi<TokensResponse>(`/api/v1/tokens?chainId=${CHAIN_ID}&limit=150&includeDodoPool=true`),
|
||||
staleTime: 30_000,
|
||||
});
|
||||
|
||||
const poolAggQuery = useQuery({
|
||||
queryKey: ['aggregate-pools', tokensQuery.data?.tokens?.map((t) => t.address).join(',')],
|
||||
enabled: Boolean(tokensQuery.data?.tokens?.length),
|
||||
queryFn: async () => {
|
||||
const tokens = tokensQuery.data!.tokens.filter((t) =>
|
||||
(t.symbol || '').toLowerCase().startsWith('c'),
|
||||
);
|
||||
const slice = tokens.slice(0, 48);
|
||||
const rows: Array<{
|
||||
token: string;
|
||||
symbol?: string;
|
||||
pool: PoolsResponse['pools'][0];
|
||||
}> = [];
|
||||
const batchSize = 8;
|
||||
for (let i = 0; i < slice.length; i += batchSize) {
|
||||
const batch = slice.slice(i, i + batchSize);
|
||||
const results = await Promise.all(
|
||||
batch.map(async (t) => {
|
||||
const pools = await fetchPoolsForToken(t.address);
|
||||
return pools.map((pool) => ({ token: t.address, symbol: t.symbol, pool }));
|
||||
}),
|
||||
);
|
||||
for (const r of results) rows.push(...r);
|
||||
}
|
||||
return rows;
|
||||
},
|
||||
staleTime: 45_000,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Liquidity pools</h1>
|
||||
<p className="lead">
|
||||
Pools are loaded from the token-aggregation indexer for Chain {CHAIN_ID} compliant (c*)
|
||||
tokens (sampled up to 48 symbols). TVL and volume update with the service refresh cycle.
|
||||
</p>
|
||||
|
||||
{tokensQuery.isError && (
|
||||
<div className="err-box">{(tokensQuery.error as Error).message}</div>
|
||||
)}
|
||||
{poolAggQuery.isFetching && <p className="muted">Loading pool details…</p>}
|
||||
{poolAggQuery.isError && (
|
||||
<div className="err-box">{(poolAggQuery.error as Error).message}</div>
|
||||
)}
|
||||
|
||||
{poolAggQuery.data && (
|
||||
<div className="table-wrap">
|
||||
<table className="data">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Pool</th>
|
||||
<th>DEX</th>
|
||||
<th>Pair</th>
|
||||
<th>TVL</th>
|
||||
<th>Vol 24h</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{poolAggQuery.data.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={5} className="muted">
|
||||
No pools returned for sampled tokens (API unreachable or indexer empty).
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{poolAggQuery.data.map(({ pool, token, symbol }) => (
|
||||
<tr key={`${pool.address}-${token}`}>
|
||||
<td className="mono">
|
||||
<a href={explorerAddress(pool.address)} target="_blank" rel="noreferrer">
|
||||
{pool.address.slice(0, 8)}…{pool.address.slice(-6)}
|
||||
</a>
|
||||
</td>
|
||||
<td>{pool.dex || '—'}</td>
|
||||
<td>
|
||||
<span className="badge badge-c">{symbol || '?'}</span>{' '}
|
||||
{pool.token0?.symbol || '?'} / {pool.token1?.symbol || '?'}
|
||||
</td>
|
||||
<td>{pool.tvl ?? '—'}</td>
|
||||
<td>{pool.volume24h ?? '—'}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
148
info-defi-oracle-138/src/pages/RoutingPage.tsx
Normal file
148
info-defi-oracle-138/src/pages/RoutingPage.tsx
Normal file
@@ -0,0 +1,148 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { fetchApi } from '@/api/client';
|
||||
import type { BridgeRoutesResponse, TokenMappingResponse } from '@/api/types';
|
||||
import { CHAIN_ID, EXPLORER_BASE } from '@/config';
|
||||
import bridgeRoutesFallback from '@repo-config/bridge-routes-chain138-default.json';
|
||||
|
||||
const PAIRS: Array<{ from: number; to: number; label: string }> = [
|
||||
{ from: CHAIN_ID, to: 1, label: `${CHAIN_ID} → Ethereum` },
|
||||
{ from: 1, to: CHAIN_ID, label: `Ethereum → ${CHAIN_ID}` },
|
||||
{ from: CHAIN_ID, to: 651940, label: `${CHAIN_ID} → ALL (651940)` },
|
||||
];
|
||||
|
||||
export function RoutingPage() {
|
||||
const routesQ = useQuery({
|
||||
queryKey: ['bridge-routes'],
|
||||
queryFn: async (): Promise<BridgeRoutesResponse> => {
|
||||
try {
|
||||
return await fetchApi<BridgeRoutesResponse>('/api/v1/bridge/routes');
|
||||
} catch {
|
||||
return bridgeRoutesFallback as BridgeRoutesResponse;
|
||||
}
|
||||
},
|
||||
staleTime: 120_000,
|
||||
});
|
||||
|
||||
const mappingQs = useQuery({
|
||||
queryKey: ['token-mappings', PAIRS],
|
||||
queryFn: async () => {
|
||||
const out: Array<{ label: string; data: TokenMappingResponse | null; error?: string }> = [];
|
||||
for (const p of PAIRS) {
|
||||
try {
|
||||
const data = await fetchApi<TokenMappingResponse>(
|
||||
`/api/v1/token-mapping?fromChain=${p.from}&toChain=${p.to}`,
|
||||
);
|
||||
out.push({ label: p.label, data });
|
||||
} catch (e) {
|
||||
out.push({
|
||||
label: p.label,
|
||||
data: null,
|
||||
error: (e as Error).message,
|
||||
});
|
||||
}
|
||||
}
|
||||
return out;
|
||||
},
|
||||
staleTime: 120_000,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Routing & bridges</h1>
|
||||
<p className="lead">
|
||||
CCIP WETH9/WETH10 bridge addresses and cross-chain token mapping for building swap → bridge →
|
||||
swap flows. Single-hop quotes remain on the <a href="/swap">Swap</a> tab.
|
||||
</p>
|
||||
|
||||
<h2>Chain {CHAIN_ID} bridge contracts</h2>
|
||||
{routesQ.isError && <div className="err-box">{(routesQ.error as Error).message}</div>}
|
||||
<p className="muted" style={{ marginBottom: '0.75rem' }}>
|
||||
Bridge table uses the live API when any configured host returns 200; if all bases fail, the UI
|
||||
falls back to <span className="mono">config/bridge-routes-chain138-default.json</span>.
|
||||
</p>
|
||||
{routesQ.data?.chain138Bridges && (
|
||||
<div className="table-wrap">
|
||||
<table className="data">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>WETH9 bridge (138)</th>
|
||||
<td className="mono">
|
||||
<a
|
||||
href={`${EXPLORER_BASE}/address/${routesQ.data.chain138Bridges.weth9}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{routesQ.data.chain138Bridges.weth9}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>WETH10 bridge (138)</th>
|
||||
<td className="mono">
|
||||
<a
|
||||
href={`${EXPLORER_BASE}/address/${routesQ.data.chain138Bridges.weth10}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{routesQ.data.chain138Bridges.weth10}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{routesQ.data.chain138Bridges.trustless && (
|
||||
<tr>
|
||||
<th>Trustless Lockbox (138)</th>
|
||||
<td className="mono">
|
||||
<a
|
||||
href={`${EXPLORER_BASE}/address/${routesQ.data.chain138Bridges.trustless}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{routesQ.data.chain138Bridges.trustless}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<h2>Destination routers (WETH9)</h2>
|
||||
{routesQ.data?.routes?.weth9 && (
|
||||
<div className="table-wrap">
|
||||
<table className="data">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Destination</th>
|
||||
<th>Bridge / router address</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{Object.entries(routesQ.data.routes.weth9).map(([name, addr]) => (
|
||||
<tr key={name}>
|
||||
<td>{name}</td>
|
||||
<td className="mono">{addr}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<h2>Token mapping snapshots</h2>
|
||||
{mappingQs.isFetching && <p className="muted">Loading mappings…</p>}
|
||||
{mappingQs.data?.map((block) => (
|
||||
<div key={block.label} style={{ marginBottom: '1.25rem' }}>
|
||||
<h3 style={{ fontSize: '1rem', marginBottom: '0.35rem' }}>{block.label}</h3>
|
||||
{block.error && <div className="err-box">{block.error}</div>}
|
||||
{block.data?.tokens && (
|
||||
<p className="muted mono" style={{ fontSize: '0.8rem' }}>
|
||||
{block.data.tokens.slice(0, 24).join(', ')}
|
||||
{block.data.tokens.length > 24 ? '…' : ''}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
282
info-defi-oracle-138/src/pages/SwapPage.tsx
Normal file
282
info-defi-oracle-138/src/pages/SwapPage.tsx
Normal file
@@ -0,0 +1,282 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import {
|
||||
useAccount,
|
||||
useConnect,
|
||||
useDisconnect,
|
||||
useReadContract,
|
||||
useWriteContract,
|
||||
useWaitForTransactionReceipt,
|
||||
} from 'wagmi';
|
||||
import { injected } from 'wagmi/connectors';
|
||||
import { parseUnits, formatUnits } from 'viem';
|
||||
import { fetchApi } from '@/api/client';
|
||||
import type { QuoteResponse, TokensResponse } from '@/api/types';
|
||||
import {
|
||||
CHAIN_ID,
|
||||
DODOPMM_INTEGRATION,
|
||||
EXPLORER_BASE,
|
||||
CANONICAL_CUSDT,
|
||||
CANONICAL_CUSDC,
|
||||
} from '@/config';
|
||||
import { dodopmmIntegrationAbi, erc20Abi } from '@/abi/integration';
|
||||
|
||||
export function SwapPage() {
|
||||
const { address, isConnected, chainId } = useAccount();
|
||||
const { connect } = useConnect();
|
||||
const { disconnect } = useDisconnect();
|
||||
|
||||
const tokensQuery = useQuery({
|
||||
queryKey: ['tokens-swap', CHAIN_ID],
|
||||
queryFn: () =>
|
||||
fetchApi<TokensResponse>(`/api/v1/tokens?chainId=${CHAIN_ID}&limit=200&includeDodoPool=true`),
|
||||
staleTime: 60_000,
|
||||
});
|
||||
|
||||
const fallbackTokens = useMemo(
|
||||
() => [
|
||||
{ address: CANONICAL_CUSDT, symbol: 'cUSDT', decimals: 6, name: 'Tether USD (Compliant)' },
|
||||
{ address: CANONICAL_CUSDC, symbol: 'cUSDC', decimals: 6, name: 'USD Coin (Compliant)' },
|
||||
],
|
||||
[],
|
||||
);
|
||||
|
||||
const cTokens = useMemo(() => {
|
||||
const fromApi = (tokensQuery.data?.tokens || []).filter((t) =>
|
||||
(t.symbol || '').toLowerCase().startsWith('c'),
|
||||
);
|
||||
return fromApi.length > 0 ? fromApi : fallbackTokens;
|
||||
}, [tokensQuery.data, fallbackTokens]);
|
||||
|
||||
const [tokenIn, setTokenIn] = useState(CANONICAL_CUSDT);
|
||||
const [tokenOut, setTokenOut] = useState(CANONICAL_CUSDC);
|
||||
const [amountInHuman, setAmountInHuman] = useState('100');
|
||||
|
||||
const tokenInMeta = cTokens.find(
|
||||
(t) => t.address.toLowerCase() === tokenIn.toLowerCase(),
|
||||
);
|
||||
const tokenOutMeta = cTokens.find(
|
||||
(t) => t.address.toLowerCase() === tokenOut.toLowerCase(),
|
||||
);
|
||||
const decimalsIn = tokenInMeta?.decimals ?? 6;
|
||||
const decimalsOut = tokenOutMeta?.decimals ?? 6;
|
||||
|
||||
const amountInWei = useMemo(() => {
|
||||
try {
|
||||
return parseUnits(amountInHuman || '0', decimalsIn);
|
||||
} catch {
|
||||
return 0n;
|
||||
}
|
||||
}, [amountInHuman, decimalsIn]);
|
||||
|
||||
const quoteQuery = useQuery({
|
||||
queryKey: ['quote', tokenIn, tokenOut, amountInHuman, decimalsIn],
|
||||
enabled: amountInWei > 0n && tokenIn !== tokenOut,
|
||||
queryFn: () =>
|
||||
fetchApi<QuoteResponse>(
|
||||
`/api/v1/quote?chainId=${CHAIN_ID}&tokenIn=${tokenIn}&tokenOut=${tokenOut}&amountIn=${amountInWei.toString()}`,
|
||||
),
|
||||
staleTime: 15_000,
|
||||
});
|
||||
|
||||
const amountOutWei = useMemo(() => {
|
||||
const raw = quoteQuery.data?.amountOut;
|
||||
if (!raw) return 0n;
|
||||
try {
|
||||
return BigInt(raw);
|
||||
} catch {
|
||||
return 0n;
|
||||
}
|
||||
}, [quoteQuery.data?.amountOut]);
|
||||
|
||||
const minOut = useMemo(() => (amountOutWei * 99n) / 100n, [amountOutWei]);
|
||||
const poolAddress = quoteQuery.data?.poolAddress as `0x${string}` | undefined;
|
||||
|
||||
const { data: allowance, refetch: refetchAllowance } = useReadContract({
|
||||
address: tokenIn as `0x${string}`,
|
||||
abi: erc20Abi,
|
||||
functionName: 'allowance',
|
||||
args: address ? [address, DODOPMM_INTEGRATION as `0x${string}`] : undefined,
|
||||
query: { enabled: Boolean(address && tokenIn) },
|
||||
});
|
||||
|
||||
const needsApprove =
|
||||
address && allowance !== undefined && amountInWei > 0n && allowance < amountInWei;
|
||||
|
||||
const { writeContract: writeApprove, data: approveHash, isPending: approvePending } =
|
||||
useWriteContract();
|
||||
const approveReceipt = useWaitForTransactionReceipt({ hash: approveHash });
|
||||
|
||||
const { writeContract: writeSwap, data: swapHash, isPending: swapPending } = useWriteContract();
|
||||
const swapReceipt = useWaitForTransactionReceipt({ hash: swapHash });
|
||||
|
||||
useEffect(() => {
|
||||
if (approveReceipt.isSuccess) void refetchAllowance();
|
||||
}, [approveReceipt.isSuccess, refetchAllowance]);
|
||||
|
||||
const wrongChain = isConnected && chainId !== CHAIN_ID;
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Atomic swap (single hop)</h1>
|
||||
<p className="lead">
|
||||
Fetches a quote from the aggregation API, then (on Chain {CHAIN_ID}) approves{' '}
|
||||
<span className="mono">DODOPMMIntegration</span> and calls{' '}
|
||||
<span className="mono">swapExactIn</span> for the returned pool. Multi-hop routing is not
|
||||
available in the API; use sequential swaps if needed.
|
||||
</p>
|
||||
|
||||
<div className="card" style={{ maxWidth: 520 }}>
|
||||
<div className="form-row">
|
||||
{!isConnected ? (
|
||||
<button
|
||||
type="button"
|
||||
className="primary"
|
||||
onClick={() => connect({ connector: injected() })}
|
||||
>
|
||||
Connect wallet
|
||||
</button>
|
||||
) : (
|
||||
<>
|
||||
<span className="mono muted">{address}</span>
|
||||
<button type="button" className="ghost" onClick={() => disconnect()}>
|
||||
Disconnect
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{wrongChain && (
|
||||
<div className="err-box">Switch your wallet to Chain {CHAIN_ID} (DeFi Oracle Meta).</div>
|
||||
)}
|
||||
|
||||
<div className="form-row">
|
||||
<label>
|
||||
Token in
|
||||
<select value={tokenIn} onChange={(e) => setTokenIn(e.target.value)}>
|
||||
{cTokens.map((t) => (
|
||||
<option key={t.address} value={t.address}>
|
||||
{t.symbol} — {t.address.slice(0, 8)}…
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Token out
|
||||
<select value={tokenOut} onChange={(e) => setTokenOut(e.target.value)}>
|
||||
{cTokens.map((t) => (
|
||||
<option key={t.address} value={t.address}>
|
||||
{t.symbol} — {t.address.slice(0, 8)}…
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-row">
|
||||
<label>
|
||||
Amount in ({tokenInMeta?.symbol || 'token'})
|
||||
<input
|
||||
value={amountInHuman}
|
||||
onChange={(e) => setAmountInHuman(e.target.value)}
|
||||
inputMode="decimal"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{quoteQuery.isFetching && <p className="muted">Fetching quote…</p>}
|
||||
{quoteQuery.error && (
|
||||
<div className="err-box">{(quoteQuery.error as Error).message}</div>
|
||||
)}
|
||||
{quoteQuery.data?.error && <div className="err-box">{quoteQuery.data.error}</div>}
|
||||
|
||||
{quoteQuery.data && !quoteQuery.data.error && (
|
||||
<div className="ok-box" style={{ marginBottom: '1rem' }}>
|
||||
<div>
|
||||
Est. out:{' '}
|
||||
<strong>
|
||||
{amountOutWei > 0n
|
||||
? formatUnits(amountOutWei, decimalsOut)
|
||||
: quoteQuery.data.amountOut || '—'}
|
||||
</strong>{' '}
|
||||
(raw minOut with 1% slip: {minOut.toString()})
|
||||
</div>
|
||||
<div className="muted" style={{ marginTop: '0.35rem' }}>
|
||||
Pool:{' '}
|
||||
{poolAddress ? (
|
||||
<a href={`${EXPLORER_BASE}/address/${poolAddress}`} target="_blank" rel="noreferrer">
|
||||
{poolAddress}
|
||||
</a>
|
||||
) : (
|
||||
'—'
|
||||
)}
|
||||
</div>
|
||||
<div className="muted mono" style={{ marginTop: '0.35rem', fontSize: '0.75rem' }}>
|
||||
Integration: {DODOPMM_INTEGRATION}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isConnected &&
|
||||
!wrongChain &&
|
||||
poolAddress &&
|
||||
amountInWei > 0n &&
|
||||
amountOutWei > 0n &&
|
||||
allowance !== undefined && (
|
||||
<div className="form-row">
|
||||
{needsApprove ? (
|
||||
<button
|
||||
type="button"
|
||||
className="primary"
|
||||
disabled={approvePending}
|
||||
onClick={() =>
|
||||
writeApprove({
|
||||
address: tokenIn as `0x${string}`,
|
||||
abi: erc20Abi,
|
||||
functionName: 'approve',
|
||||
args: [DODOPMM_INTEGRATION as `0x${string}`, amountInWei],
|
||||
})
|
||||
}
|
||||
>
|
||||
{approvePending ? 'Approving…' : 'Approve token'}
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className="primary"
|
||||
disabled={swapPending}
|
||||
onClick={() =>
|
||||
writeSwap({
|
||||
address: DODOPMM_INTEGRATION,
|
||||
abi: dodopmmIntegrationAbi,
|
||||
functionName: 'swapExactIn',
|
||||
args: [poolAddress, tokenIn as `0x${string}`, amountInWei, minOut],
|
||||
})
|
||||
}
|
||||
>
|
||||
{swapPending ? 'Swapping…' : 'Swap (swapExactIn)'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{approveHash && (
|
||||
<p className="muted mono" style={{ fontSize: '0.8rem' }}>
|
||||
Approve tx:{' '}
|
||||
<a href={`${EXPLORER_BASE}/tx/${approveHash}`} target="_blank" rel="noreferrer">
|
||||
{approveHash}
|
||||
</a>{' '}
|
||||
{approveReceipt.isSuccess ? '✓' : approveReceipt.isLoading ? '…' : ''}
|
||||
</p>
|
||||
)}
|
||||
{swapHash && (
|
||||
<p className="muted mono" style={{ fontSize: '0.8rem' }}>
|
||||
Swap tx:{' '}
|
||||
<a href={`${EXPLORER_BASE}/tx/${swapHash}`} target="_blank" rel="noreferrer">
|
||||
{swapHash}
|
||||
</a>{' '}
|
||||
{swapReceipt.isSuccess ? '✓' : swapReceipt.isLoading ? '…' : ''}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
106
info-defi-oracle-138/src/pages/TokensPage.tsx
Normal file
106
info-defi-oracle-138/src/pages/TokensPage.tsx
Normal file
@@ -0,0 +1,106 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { fetchApi } from '@/api/client';
|
||||
import type { TokensResponse } from '@/api/types';
|
||||
import { CHAIN_ID, EXPLORER_BASE } from '@/config';
|
||||
import { listCwChains } from '@/data/deployment-status';
|
||||
|
||||
function explorerToken(addr: string) {
|
||||
return `${EXPLORER_BASE}/token/${addr}`;
|
||||
}
|
||||
|
||||
export function TokensPage() {
|
||||
const { data, isLoading, error } = useQuery({
|
||||
queryKey: ['tokens', CHAIN_ID],
|
||||
queryFn: () =>
|
||||
fetchApi<TokensResponse>(`/api/v1/tokens?chainId=${CHAIN_ID}&limit=200&includeDodoPool=true`),
|
||||
staleTime: 30_000,
|
||||
});
|
||||
|
||||
const cwChains = listCwChains();
|
||||
|
||||
const cStar =
|
||||
data?.tokens?.filter((t) => (t.symbol || '').toLowerCase().startsWith('c')) || [];
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>c* & cW* tokens</h1>
|
||||
<p className="lead">
|
||||
<span className="badge badge-c">c*</span> Compliant tokens on Chain {CHAIN_ID} (from indexer).
|
||||
<br />
|
||||
<span className="badge badge-cw">cW*</span> Bridged compliant-wrapped tokens on public EVM chains
|
||||
(from repo <span className="mono">deployment-status.json</span>, rebuild site to refresh).
|
||||
</p>
|
||||
|
||||
<h2>
|
||||
Chain {CHAIN_ID} — c* (live API){' '}
|
||||
{isLoading && <span className="muted">Loading…</span>}
|
||||
</h2>
|
||||
{error && (
|
||||
<div className="err-box">{(error as Error).message}</div>
|
||||
)}
|
||||
{!isLoading && !error && (
|
||||
<div className="table-wrap">
|
||||
<table className="data">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Symbol</th>
|
||||
<th>Name</th>
|
||||
<th>Address</th>
|
||||
<th>PMM</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{cStar.map((t) => (
|
||||
<tr key={t.address}>
|
||||
<td>
|
||||
<span className="badge badge-c">{t.symbol || '?'}</span>
|
||||
</td>
|
||||
<td>{t.name || '—'}</td>
|
||||
<td className="mono">
|
||||
<a href={explorerToken(t.address)} target="_blank" rel="noreferrer">
|
||||
{t.address.slice(0, 10)}…{t.address.slice(-6)}
|
||||
</a>
|
||||
</td>
|
||||
<td>{t.hasDodoPool || t.pmmPool ? 'Yes' : '—'}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<h2>Bridged cW* (registry)</h2>
|
||||
<p className="muted">
|
||||
Addresses are maintained in <span className="mono">cross-chain-pmm-lps/config/deployment-status.json</span>.
|
||||
</p>
|
||||
{cwChains.map(({ chainId, name, tokens }) => (
|
||||
<div key={chainId} style={{ marginBottom: '1.5rem' }}>
|
||||
<h3 style={{ fontSize: '1rem', margin: '0 0 0.5rem' }}>
|
||||
{name}{' '}
|
||||
<span className="muted mono">({chainId})</span>
|
||||
</h3>
|
||||
<div className="table-wrap">
|
||||
<table className="data">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Symbol</th>
|
||||
<th>Address</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{tokens.map((row) => (
|
||||
<tr key={row.symbol}>
|
||||
<td>
|
||||
<span className="badge badge-cw">{row.symbol}</span>
|
||||
</td>
|
||||
<td className="mono">{row.address}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
257
info-defi-oracle-138/src/styles/global.css
Normal file
257
info-defi-oracle-138/src/styles/global.css
Normal file
@@ -0,0 +1,257 @@
|
||||
:root {
|
||||
--bg: #070b14;
|
||||
--surface: #0f1629;
|
||||
--surface2: #151d35;
|
||||
--border: rgba(148, 163, 184, 0.18);
|
||||
--text: #e2e8f0;
|
||||
--muted: #94a3b8;
|
||||
--accent: #38bdf8;
|
||||
--accent2: #a78bfa;
|
||||
--ok: #4ade80;
|
||||
--warn: #fbbf24;
|
||||
--err: #f87171;
|
||||
--radius: 12px;
|
||||
--font-sans: 'DM Sans', system-ui, sans-serif;
|
||||
--font-display: 'Instrument Serif', Georgia, serif;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
#root {
|
||||
margin: 0;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-sans);
|
||||
background: radial-gradient(1200px 600px at 10% -10%, rgba(56, 189, 248, 0.12), transparent),
|
||||
radial-gradient(800px 400px at 90% 0%, rgba(167, 139, 250, 0.1), transparent), var(--bg);
|
||||
color: var(--text);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
code,
|
||||
.mono {
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
.layout {
|
||||
max-width: 1120px;
|
||||
margin: 0 auto;
|
||||
padding: 1.25rem 1.25rem 3rem;
|
||||
}
|
||||
|
||||
header.site {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
padding-bottom: 1.5rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.brand {
|
||||
font-family: var(--font-display);
|
||||
font-size: 1.75rem;
|
||||
font-weight: 400;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
.brand span {
|
||||
color: var(--muted);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
nav.site a {
|
||||
margin-right: 1rem;
|
||||
color: var(--muted);
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
nav.site a:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
nav.site a.active {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: var(--font-display);
|
||||
font-size: 2.25rem;
|
||||
font-weight: 400;
|
||||
margin: 0 0 0.5rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.15rem;
|
||||
font-weight: 700;
|
||||
margin: 2rem 0 0.75rem;
|
||||
color: var(--muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
|
||||
p.lead {
|
||||
color: var(--muted);
|
||||
max-width: 52ch;
|
||||
margin: 0 0 1.5rem;
|
||||
}
|
||||
|
||||
.card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 1.1rem 1.25rem;
|
||||
}
|
||||
.card h3 {
|
||||
margin: 0 0 0.35rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
.card p {
|
||||
margin: 0;
|
||||
font-size: 0.9rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.table-wrap {
|
||||
overflow-x: auto;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
table.data {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
table.data th,
|
||||
table.data td {
|
||||
text-align: left;
|
||||
padding: 0.65rem 0.85rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
table.data th {
|
||||
color: var(--muted);
|
||||
font-weight: 600;
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
table.data tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 0.15rem 0.45rem;
|
||||
border-radius: 6px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
.badge-c {
|
||||
background: rgba(56, 189, 248, 0.15);
|
||||
color: var(--accent);
|
||||
}
|
||||
.badge-cw {
|
||||
background: rgba(167, 139, 250, 0.15);
|
||||
color: var(--accent2);
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
align-items: flex-end;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
font-size: 0.8rem;
|
||||
color: var(--muted);
|
||||
font-weight: 600;
|
||||
}
|
||||
input,
|
||||
select,
|
||||
button {
|
||||
font: inherit;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--surface2);
|
||||
color: var(--text);
|
||||
padding: 0.5rem 0.65rem;
|
||||
}
|
||||
button.primary {
|
||||
background: linear-gradient(135deg, #0ea5e9, #6366f1);
|
||||
border: none;
|
||||
color: white;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
padding: 0.55rem 1.1rem;
|
||||
}
|
||||
button.primary:disabled {
|
||||
opacity: 0.45;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
button.ghost {
|
||||
background: transparent;
|
||||
border-color: var(--border);
|
||||
color: var(--muted);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.err-box {
|
||||
background: rgba(248, 113, 113, 0.1);
|
||||
border: 1px solid rgba(248, 113, 113, 0.35);
|
||||
color: var(--err);
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: var(--radius);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.ok-box {
|
||||
background: rgba(74, 222, 128, 0.08);
|
||||
border: 1px solid rgba(74, 222, 128, 0.25);
|
||||
color: var(--ok);
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: var(--radius);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.muted {
|
||||
color: var(--muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
footer.site {
|
||||
margin-top: 3rem;
|
||||
padding-top: 1.5rem;
|
||||
border-top: 1px solid var(--border);
|
||||
font-size: 0.85rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
10
info-defi-oracle-138/src/vite-env.d.ts
vendored
Normal file
10
info-defi-oracle-138/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_TOKEN_AGGREGATION_API_BASE: string;
|
||||
readonly VITE_RPC_URL_138: string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
23
info-defi-oracle-138/tsconfig.json
Normal file
23
info-defi-oracle-138/tsconfig.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"],
|
||||
"@repo-config/*": ["../config/*"]
|
||||
},
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
10
info-defi-oracle-138/tsconfig.node.json
Normal file
10
info-defi-oracle-138/tsconfig.node.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"strict": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
20
info-defi-oracle-138/vite.config.ts
Normal file
20
info-defi-oracle-138/vite.config.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import path from 'path';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
'@repo-config': path.resolve(__dirname, '../config'),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
port: 5180,
|
||||
},
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
sourcemap: true,
|
||||
},
|
||||
});
|
||||
@@ -1 +1 @@
|
||||
{"totalFilteredRecords":2,"pageItems":[{"id":1,"accountNo":"348952249","externalId":"2db4be95-1eeb-4caa-b6ce-a646205f06fc","status":{"id":300,"code":"clientStatusType.active","value":"Active"},"subStatus":{"active":false,"mandatory":false},"active":true,"activationDate":[2026,2,10],"fullname":"Organisation Mondiale du Numérique L.P.B.C","displayName":"Organisation Mondiale du Numérique L.P.B.C","gender":{"active":false,"mandatory":false},"clientType":{"active":false,"mandatory":false},"clientClassification":{"active":false,"mandatory":false},"isStaff":false,"officeId":2,"officeName":"SHAMRAYAN ENTERPRISES","timeline":{"submittedOnDate":[2026,2,10],"submittedByUsername":"app.omnl","submittedByFirstname":"App","submittedByLastname":"Administrator","activatedOnDate":[2026,2,10],"activatedByUsername":"app.omnl","activatedByFirstname":"App","activatedByLastname":"Administrator"},"legalForm":{"id":2,"code":"legalFormType.entity","value":"Entity"},"clientNonPersonDetails":{"constitution":{"id":16,"name":"Colorado Business Corporation Act","active":false,"mandatory":false},"incorpNumber":"98450070C57395F6B906","mainBusinessLine":{"active":false,"mandatory":false}}},{"id":2,"accountNo":"811204797","externalId":"c9f3b153-a1b7-403e-bd9a-676fe18492f7","status":{"id":300,"code":"clientStatusType.active","value":"Active"},"subStatus":{"active":false,"mandatory":false},"active":true,"activationDate":[2026,2,10],"fullname":"SHAMRAYAN ENTERPRISES","displayName":"SHAMRAYAN ENTERPRISES","gender":{"active":false,"mandatory":false},"clientType":{"active":false,"mandatory":false},"clientClassification":{"active":false,"mandatory":false},"isStaff":false,"officeId":1,"officeName":"Head Office","timeline":{"submittedOnDate":[2026,2,10],"submittedByUsername":"app.omnl","submittedByFirstname":"App","submittedByLastname":"Administrator","activatedOnDate":[2026,2,10],"activatedByUsername":"app.omnl","activatedByFirstname":"App","activatedByLastname":"Administrator"},"legalForm":{"id":2,"code":"legalFormType.entity","value":"Entity"},"clientNonPersonDetails":{"constitution":{"id":21,"name":"Companies Act, 2012","active":false,"mandatory":false},"incorpNumber":"80020002760051","mainBusinessLine":{"active":false,"mandatory":false}}}]}
|
||||
{"totalFilteredRecords":0,"pageItems":[]}
|
||||
|
||||
@@ -1 +1 @@
|
||||
[{"id":1,"name":"Head Office","nameDecorated":"Head Office","externalId":"1","openingDate":[2009,1,1],"hierarchy":"."},{"id":2,"name":"SHAMRAYAN ENTERPRISES","nameDecorated":"....SHAMRAYAN ENTERPRISES","externalId":"80020002760051","openingDate":[2026,2,2],"hierarchy":".2.","parentId":1,"parentName":"Head Office"}]
|
||||
[{"id":1,"name":"OMNL Head Office (DBIS) – Central Bank","nameDecorated":"OMNL Head Office (DBIS) – Central Bank","externalId":"1","openingDate":[2026,1,1],"hierarchy":"."},{"id":10,"name":"Alpha Omega Holdings","nameDecorated":"....Alpha Omega Holdings","externalId":"OMNL-10","openingDate":[2026,1,1],"hierarchy":".10.","parentId":1,"parentName":"OMNL Head Office (DBIS) – Central Bank"},{"id":11,"name":"SGI Capital","nameDecorated":"....SGI Capital","externalId":"OMNL-11","openingDate":[2026,1,1],"hierarchy":".11.","parentId":1,"parentName":"OMNL Head Office (DBIS) – Central Bank"},{"id":12,"name":"Titan Financial","nameDecorated":"....Titan Financial","externalId":"OMNL-12","openingDate":[2026,1,1],"hierarchy":".12.","parentId":1,"parentName":"OMNL Head Office (DBIS) – Central Bank"},{"id":13,"name":"Roy Walker PLLC","nameDecorated":"....Roy Walker PLLC","externalId":"OMNL-13","openingDate":[2026,1,1],"hierarchy":".13.","parentId":1,"parentName":"OMNL Head Office (DBIS) – Central Bank"},{"id":14,"name":"SGI Partners LLC","nameDecorated":"....SGI Partners LLC","externalId":"OMNL-14","openingDate":[2026,1,1],"hierarchy":".14.","parentId":1,"parentName":"OMNL Head Office (DBIS) – Central Bank"},{"id":15,"name":"Tsunami Holdings AG","nameDecorated":"....Tsunami Holdings AG","externalId":"OMNL-15","openingDate":[2026,1,1],"hierarchy":".15.","parentId":1,"parentName":"OMNL Head Office (DBIS) – Central Bank"},{"id":16,"name":"Anakatech LLC","nameDecorated":"....Anakatech LLC","externalId":"OMNL-16","openingDate":[2026,1,1],"hierarchy":".16.","parentId":1,"parentName":"OMNL Head Office (DBIS) – Central Bank"},{"id":17,"name":"Anema Cameron Walker Global","nameDecorated":"....Anema Cameron Walker Global","externalId":"OMNL-17","openingDate":[2026,1,1],"hierarchy":".17.","parentId":1,"parentName":"OMNL Head Office (DBIS) – Central Bank"},{"id":18,"name":"NEPAL RASTRA BANK","nameDecorated":"....NEPAL RASTRA BANK","externalId":"OMNL-18","openingDate":[2026,1,1],"hierarchy":".18.","parentId":1,"parentName":"OMNL Head Office (DBIS) – Central Bank"},{"id":19,"name":"SANIMA BANK LIMITED","nameDecorated":"....SANIMA BANK LIMITED","externalId":"OMNL-19","openingDate":[2026,1,1],"hierarchy":".19.","parentId":1,"parentName":"OMNL Head Office (DBIS) – Central Bank"},{"id":2,"name":"Shamrayan Enterprises","nameDecorated":"....Shamrayan Enterprises","externalId":"OMNL-2","openingDate":[2026,1,1],"hierarchy":".2.","parentId":1,"parentName":"OMNL Head Office (DBIS) – Central Bank"},{"id":20,"name":"Samama Group LLC - Azerbaijan","nameDecorated":"....Samama Group LLC - Azerbaijan","externalId":"SAMAMA-AZ-1703722701","openingDate":[2024,1,10],"hierarchy":".20.","parentId":1,"parentName":"OMNL Head Office (DBIS) – Central Bank"},{"id":21,"name":"Bank Kanaya (Indonesia)","nameDecorated":"....Bank Kanaya (Indonesia)","externalId":"BANK-KANAYA-ID","openingDate":[2026,1,1],"hierarchy":".21.","parentId":1,"parentName":"OMNL Head Office (DBIS) – Central Bank"},{"id":3,"name":"HYBX","nameDecorated":"....HYBX","externalId":"OMNL-3","openingDate":[2026,1,1],"hierarchy":".3.","parentId":1,"parentName":"OMNL Head Office (DBIS) – Central Bank"},{"id":4,"name":"TAJ Private Single Family Office","nameDecorated":"....TAJ Private Single Family Office","externalId":"OMNL-4","openingDate":[2026,1,1],"hierarchy":".4.","parentId":1,"parentName":"OMNL Head Office (DBIS) – Central Bank"},{"id":5,"name":"Aseret Mortgage Bank","nameDecorated":"....Aseret Mortgage Bank","externalId":"OMNL-5","openingDate":[2026,1,1],"hierarchy":".5.","parentId":1,"parentName":"OMNL Head Office (DBIS) – Central Bank"},{"id":6,"name":"Mann Li Family Offices","nameDecorated":"....Mann Li Family Offices","externalId":"OMNL-6","openingDate":[2026,1,1],"hierarchy":".6.","parentId":1,"parentName":"OMNL Head Office (DBIS) – Central Bank"},{"id":7,"name":"Sovereign Order of Malta OSJ","nameDecorated":"....Sovereign Order of Malta OSJ","externalId":"OMNL-7","openingDate":[2026,1,1],"hierarchy":".7.","parentId":1,"parentName":"OMNL Head Office (DBIS) – Central Bank"},{"id":8,"name":"Alltra Mainnet","nameDecorated":"....Alltra Mainnet","externalId":"OMNL-8","openingDate":[2026,1,1],"hierarchy":".8.","parentId":1,"parentName":"OMNL Head Office (DBIS) – Central Bank"},{"id":9,"name":"FIDIS","nameDecorated":"....FIDIS","externalId":"OMNL-9","openingDate":[2026,1,1],"hierarchy":".9.","parentId":1,"parentName":"OMNL Head Office (DBIS) – Central Bank"}]
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"totalFilteredRecords":2,"pageItems":[{"id":1,"accountNo":"166963551","depositType":{"id":100,"code":"depositAccountType.savingsDeposit","value":"Savings"},"externalId":"53c467f6-9023-435a-b6cd-11c875393b61","clientId":1,"clientName":"Organisation Mondiale du Numérique L.P.B.C","savingsProductId":1,"savingsProductName":"USD Wallet","fieldOfficerId":0,"status":{"id":300,"code":"savingsAccountStatusType.active","value":"Active","submittedAndPendingApproval":false,"approved":false,"rejected":false,"withdrawnByApplicant":false,"active":true,"closed":false,"prematureClosed":false,"transferInProgress":false,"transferOnHold":false,"matured":false},"subStatus":{"id":0,"code":"SavingsAccountSubStatusEnum.none","value":"None","none":true,"inactive":false,"dormant":false,"escheat":false,"block":false,"blockCredit":false,"blockDebit":false},"timeline":{"submittedOnDate":[2026,2,10],"submittedByUsername":"app.omnl","submittedByFirstname":"App","submittedByLastname":"Administrator","approvedOnDate":[2026,2,10],"approvedByUsername":"app.omnl","approvedByFirstname":"App","approvedByLastname":"Administrator","activatedOnDate":[2026,2,10],"activatedByUsername":"app.omnl","activatedByFirstname":"App","activatedByLastname":"Administrator"},"currency":{"code":"USD","name":"US Dollar","decimalPlaces":2,"displaySymbol":"$","nameCode":"currency.USD","displayLabel":"US Dollar ($)"},"nominalAnnualInterestRate":0.000000,"interestCompoundingPeriodType":{"id":1,"code":"savings.interest.period.savingsCompoundingInterestPeriodType.daily","value":"Daily"},"interestPostingPeriodType":{"id":4,"code":"savings.interest.posting.period.savingsPostingInterestPeriodType.monthly","value":"Monthly"},"interestCalculationType":{"id":1,"code":"savingsInterestCalculationType.dailybalance","value":"Daily Balance"},"interestCalculationDaysInYearType":{"id":365,"code":"savingsInterestCalculationDaysInYearType.days365","value":"365 Days"},"withdrawalFeeForTransfers":false,"allowOverdraft":false,"enforceMinRequiredBalance":false,"lienAllowed":false,"withHoldTax":false,"lastActiveTransactionDate":[2026,2,10],"isDormancyTrackingActive":false,"summary":{"currency":{"code":"USD","name":"US Dollar","decimalPlaces":2,"displaySymbol":"$","nameCode":"currency.USD","displayLabel":"US Dollar ($)"},"totalInterestPosted":0,"accountBalance":0.000000,"totalOverdraftInterestDerived":0,"interestNotPosted":0,"availableBalance":0.000000}},{"id":2,"accountNo":"277420915","depositType":{"id":100,"code":"depositAccountType.savingsDeposit","value":"Savings"},"externalId":"c4a8ce37-ee04-457e-823a-18ad685e8be0","clientId":1,"clientName":"Organisation Mondiale du Numérique L.P.B.C","savingsProductId":2,"savingsProductName":"EUR Wallet","fieldOfficerId":0,"status":{"id":300,"code":"savingsAccountStatusType.active","value":"Active","submittedAndPendingApproval":false,"approved":false,"rejected":false,"withdrawnByApplicant":false,"active":true,"closed":false,"prematureClosed":false,"transferInProgress":false,"transferOnHold":false,"matured":false},"subStatus":{"id":0,"code":"SavingsAccountSubStatusEnum.none","value":"None","none":true,"inactive":false,"dormant":false,"escheat":false,"block":false,"blockCredit":false,"blockDebit":false},"timeline":{"submittedOnDate":[2026,2,10],"submittedByUsername":"app.omnl","submittedByFirstname":"App","submittedByLastname":"Administrator","approvedOnDate":[2026,2,10],"approvedByUsername":"app.omnl","approvedByFirstname":"App","approvedByLastname":"Administrator","activatedOnDate":[2026,2,10],"activatedByUsername":"app.omnl","activatedByFirstname":"App","activatedByLastname":"Administrator"},"currency":{"code":"EUR","name":"Euro","decimalPlaces":2,"displaySymbol":"€","nameCode":"currency.EUR","displayLabel":"Euro (€)"},"nominalAnnualInterestRate":0.000000,"interestCompoundingPeriodType":{"id":1,"code":"savings.interest.period.savingsCompoundingInterestPeriodType.daily","value":"Daily"},"interestPostingPeriodType":{"id":4,"code":"savings.interest.posting.period.savingsPostingInterestPeriodType.monthly","value":"Monthly"},"interestCalculationType":{"id":1,"code":"savingsInterestCalculationType.dailybalance","value":"Daily Balance"},"interestCalculationDaysInYearType":{"id":365,"code":"savingsInterestCalculationDaysInYearType.days365","value":"365 Days"},"withdrawalFeeForTransfers":false,"allowOverdraft":false,"enforceMinRequiredBalance":false,"lienAllowed":false,"withHoldTax":false,"lastActiveTransactionDate":[2026,2,10],"isDormancyTrackingActive":false,"summary":{"currency":{"code":"EUR","name":"Euro","decimalPlaces":2,"displaySymbol":"€","nameCode":"currency.EUR","displayLabel":"Euro (€)"},"totalInterestPosted":0,"accountBalance":0.000000,"totalOverdraftInterestDerived":0,"interestNotPosted":0,"availableBalance":0.000000}}]}
|
||||
{"totalFilteredRecords":0,"pageItems":[]}
|
||||
|
||||
@@ -1 +1 @@
|
||||
[{"id":1,"name":"USD Wallet","shortName":"USD","currency":{"code":"USD","name":"US Dollar","decimalPlaces":2,"displaySymbol":"$","nameCode":"currency.USD","displayLabel":"US Dollar ($)"},"nominalAnnualInterestRate":0.000000,"interestCompoundingPeriodType":{"id":1,"code":"savings.interest.period.savingsCompoundingInterestPeriodType.daily","value":"Daily"},"interestPostingPeriodType":{"id":4,"code":"savings.interest.posting.period.savingsPostingInterestPeriodType.monthly","value":"Monthly"},"interestCalculationType":{"id":1,"code":"savingsInterestCalculationType.dailybalance","value":"Daily Balance"},"interestCalculationDaysInYearType":{"id":365,"code":"savingsInterestCalculationDaysInYearType.days365","value":"365 Days"},"withdrawalFeeForTransfers":false,"allowOverdraft":false,"overdraftLimit":0.000000,"minRequiredBalance":0.000000,"enforceMinRequiredBalance":false,"maxAllowedLienLimit":0.000000,"lienAllowed":false,"nominalAnnualInterestRateOverdraft":0.000000,"minOverdraftForInterestCalculation":0.000000,"withHoldTax":false,"accountingRule":{"id":1,"code":"accountingRuleType.none","value":"NONE"},"isDormancyTrackingActive":false},{"id":2,"name":"EUR Wallet","shortName":"EUR","currency":{"code":"EUR","name":"Euro","decimalPlaces":2,"displaySymbol":"€","nameCode":"currency.EUR","displayLabel":"Euro (€)"},"nominalAnnualInterestRate":0.000000,"interestCompoundingPeriodType":{"id":1,"code":"savings.interest.period.savingsCompoundingInterestPeriodType.daily","value":"Daily"},"interestPostingPeriodType":{"id":4,"code":"savings.interest.posting.period.savingsPostingInterestPeriodType.monthly","value":"Monthly"},"interestCalculationType":{"id":1,"code":"savingsInterestCalculationType.dailybalance","value":"Daily Balance"},"interestCalculationDaysInYearType":{"id":365,"code":"savingsInterestCalculationDaysInYearType.days365","value":"365 Days"},"withdrawalFeeForTransfers":false,"allowOverdraft":false,"overdraftLimit":0.000000,"minRequiredBalance":0.000000,"enforceMinRequiredBalance":false,"maxAllowedLienLimit":0.000000,"lienAllowed":false,"nominalAnnualInterestRateOverdraft":0.000000,"minOverdraftForInterestCalculation":0.000000,"withHoldTax":false,"accountingRule":{"id":1,"code":"accountingRuleType.none","value":"NONE"},"isDormancyTrackingActive":false}]
|
||||
[]
|
||||
|
||||
319
pnpm-lock.yaml
generated
319
pnpm-lock.yaml
generated
@@ -555,7 +555,7 @@ importers:
|
||||
version: 6.21.0(eslint@8.57.1)(typescript@5.9.3)
|
||||
'@vitejs/plugin-react':
|
||||
specifier: ^4.7.0
|
||||
version: 4.7.0(vite@5.4.21(@types/node@25.2.3))
|
||||
version: 4.7.0(vite@5.4.21(@types/node@20.19.33))
|
||||
'@vitest/ui':
|
||||
specifier: ^1.6.1
|
||||
version: 1.6.1(vitest@1.6.1)
|
||||
@@ -576,19 +576,19 @@ importers:
|
||||
version: 23.2.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)
|
||||
ts-jest:
|
||||
specifier: ^29.4.6
|
||||
version: 29.4.6(@babel/core@7.29.0)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.29.0))(jest-util@30.2.0)(jest@30.2.0(@types/node@25.2.3)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@25.2.3)(typescript@5.9.3)))(typescript@5.9.3)
|
||||
version: 29.4.6(@babel/core@7.29.0)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.29.0))(jest-util@30.2.0)(jest@30.2.0(@types/node@20.19.33)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.33)(typescript@5.9.3)))(typescript@5.9.3)
|
||||
typescript:
|
||||
specifier: ^5.9.3
|
||||
version: 5.9.3
|
||||
vite:
|
||||
specifier: ^5.4.21
|
||||
version: 5.4.21(@types/node@25.2.3)
|
||||
version: 5.4.21(@types/node@20.19.33)
|
||||
vite-plugin-node-polyfills:
|
||||
specifier: ^0.24.0
|
||||
version: 0.24.0(rollup@4.57.1)(vite@5.4.21(@types/node@25.2.3))
|
||||
version: 0.24.0(rollup@4.57.1)(vite@5.4.21(@types/node@20.19.33))
|
||||
vitest:
|
||||
specifier: ^1.6.1
|
||||
version: 1.6.1(@types/node@25.2.3)(@vitest/ui@1.6.1)(jsdom@23.2.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))
|
||||
version: 1.6.1(@types/node@20.19.33)(@vitest/ui@1.6.1)(jsdom@23.2.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))
|
||||
|
||||
smom-dbis-138/services/token-aggregation:
|
||||
dependencies:
|
||||
@@ -681,6 +681,46 @@ importers:
|
||||
specifier: ^5.9.3
|
||||
version: 5.9.3
|
||||
|
||||
smom-dbis-138/test/emoney/api:
|
||||
dependencies:
|
||||
ajv:
|
||||
specifier: ^8.12.0
|
||||
version: 8.18.0
|
||||
ajv-formats:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1(ajv@8.18.0)
|
||||
axios:
|
||||
specifier: ^1.6.2
|
||||
version: 1.13.5
|
||||
graphql:
|
||||
specifier: ^16.8.1
|
||||
version: 16.13.2
|
||||
graphql-request:
|
||||
specifier: ^6.1.0
|
||||
version: 6.1.0(graphql@16.13.2)
|
||||
js-yaml:
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.1
|
||||
devDependencies:
|
||||
'@types/jest':
|
||||
specifier: ^29.5.11
|
||||
version: 29.5.14
|
||||
'@types/js-yaml':
|
||||
specifier: ^4.0.9
|
||||
version: 4.0.9
|
||||
'@types/node':
|
||||
specifier: ^20.10.0
|
||||
version: 20.19.33
|
||||
jest:
|
||||
specifier: ^29.7.0
|
||||
version: 29.7.0(@types/node@20.19.33)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.33)(typescript@5.9.3))
|
||||
ts-jest:
|
||||
specifier: ^29.1.1
|
||||
version: 29.4.6(@babel/core@7.29.0)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.29.0))(jest-util@30.2.0)(jest@29.7.0(@types/node@20.19.33)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.33)(typescript@5.9.3)))(typescript@5.9.3)
|
||||
typescript:
|
||||
specifier: ^5.3.0
|
||||
version: 5.9.3
|
||||
|
||||
transaction-composer:
|
||||
dependencies:
|
||||
'@xstate/react':
|
||||
@@ -2081,6 +2121,11 @@ packages:
|
||||
resolution: {integrity: sha512-5umyLoD5vMxlSVQwtmUXeNCNWs9dzmWykGm1qrHe/pCYrj/1lyJIgJRw+IxoMNodGqtcHEtfDhdNjRDM9yo/TA==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
'@graphql-typed-document-node/core@3.2.0':
|
||||
resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==}
|
||||
peerDependencies:
|
||||
graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
|
||||
|
||||
'@grpc/grpc-js@1.14.3':
|
||||
resolution: {integrity: sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==}
|
||||
engines: {node: '>=12.10.0'}
|
||||
@@ -4750,6 +4795,9 @@ packages:
|
||||
'@types/jest@29.5.14':
|
||||
resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==}
|
||||
|
||||
'@types/js-yaml@4.0.9':
|
||||
resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==}
|
||||
|
||||
'@types/json-schema@7.0.15':
|
||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||
|
||||
@@ -7938,6 +7986,15 @@ packages:
|
||||
graphemer@1.4.0:
|
||||
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
|
||||
|
||||
graphql-request@6.1.0:
|
||||
resolution: {integrity: sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==}
|
||||
peerDependencies:
|
||||
graphql: 14 - 16
|
||||
|
||||
graphql@16.13.2:
|
||||
resolution: {integrity: sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig==}
|
||||
engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
|
||||
|
||||
gtoken@7.1.0:
|
||||
resolution: {integrity: sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@@ -14478,6 +14535,10 @@ snapshots:
|
||||
lit: 2.8.0
|
||||
three: 0.146.0
|
||||
|
||||
'@graphql-typed-document-node/core@3.2.0(graphql@16.13.2)':
|
||||
dependencies:
|
||||
graphql: 16.13.2
|
||||
|
||||
'@grpc/grpc-js@1.14.3':
|
||||
dependencies:
|
||||
'@grpc/proto-loader': 0.8.0
|
||||
@@ -14645,7 +14706,7 @@ snapshots:
|
||||
'@jest/console@29.7.0':
|
||||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
chalk: 4.1.2
|
||||
jest-message-util: 29.7.0
|
||||
jest-util: 29.7.0
|
||||
@@ -14654,7 +14715,7 @@ snapshots:
|
||||
'@jest/console@30.2.0':
|
||||
dependencies:
|
||||
'@jest/types': 30.2.0
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
chalk: 4.1.2
|
||||
jest-message-util: 30.2.0
|
||||
jest-util: 30.2.0
|
||||
@@ -14667,14 +14728,14 @@ snapshots:
|
||||
'@jest/test-result': 29.7.0
|
||||
'@jest/transform': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
ansi-escapes: 4.3.2
|
||||
chalk: 4.1.2
|
||||
ci-info: 3.9.0
|
||||
exit: 0.1.2
|
||||
graceful-fs: 4.2.11
|
||||
jest-changed-files: 29.7.0
|
||||
jest-config: 29.7.0(@types/node@25.2.3)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.33)(typescript@5.9.3))
|
||||
jest-config: 29.7.0(@types/node@20.19.33)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.33)(typescript@5.9.3))
|
||||
jest-haste-map: 29.7.0
|
||||
jest-message-util: 29.7.0
|
||||
jest-regex-util: 29.6.3
|
||||
@@ -14695,7 +14756,7 @@ snapshots:
|
||||
- supports-color
|
||||
- ts-node
|
||||
|
||||
'@jest/core@30.2.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@25.2.3)(typescript@5.9.3))':
|
||||
'@jest/core@30.2.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.33)(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@jest/console': 30.2.0
|
||||
'@jest/pattern': 30.0.1
|
||||
@@ -14703,14 +14764,14 @@ snapshots:
|
||||
'@jest/test-result': 30.2.0
|
||||
'@jest/transform': 30.2.0
|
||||
'@jest/types': 30.2.0
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
ansi-escapes: 4.3.2
|
||||
chalk: 4.1.2
|
||||
ci-info: 4.4.0
|
||||
exit-x: 0.2.2
|
||||
graceful-fs: 4.2.11
|
||||
jest-changed-files: 30.2.0
|
||||
jest-config: 30.2.0(@types/node@25.2.3)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@25.2.3)(typescript@5.9.3))
|
||||
jest-config: 30.2.0(@types/node@20.19.33)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.33)(typescript@5.9.3))
|
||||
jest-haste-map: 30.2.0
|
||||
jest-message-util: 30.2.0
|
||||
jest-regex-util: 30.0.1
|
||||
@@ -14737,14 +14798,14 @@ snapshots:
|
||||
dependencies:
|
||||
'@jest/fake-timers': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
jest-mock: 29.7.0
|
||||
|
||||
'@jest/environment@30.2.0':
|
||||
dependencies:
|
||||
'@jest/fake-timers': 30.2.0
|
||||
'@jest/types': 30.2.0
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
jest-mock: 30.2.0
|
||||
|
||||
'@jest/expect-utils@29.7.0':
|
||||
@@ -14773,7 +14834,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
'@sinonjs/fake-timers': 10.3.0
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
jest-message-util: 29.7.0
|
||||
jest-mock: 29.7.0
|
||||
jest-util: 29.7.0
|
||||
@@ -14782,7 +14843,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@jest/types': 30.2.0
|
||||
'@sinonjs/fake-timers': 13.0.5
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
jest-message-util: 30.2.0
|
||||
jest-mock: 30.2.0
|
||||
jest-util: 30.2.0
|
||||
@@ -14809,7 +14870,7 @@ snapshots:
|
||||
|
||||
'@jest/pattern@30.0.1':
|
||||
dependencies:
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
jest-regex-util: 30.0.1
|
||||
|
||||
'@jest/reporters@29.7.0':
|
||||
@@ -14820,7 +14881,7 @@ snapshots:
|
||||
'@jest/transform': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
chalk: 4.1.2
|
||||
collect-v8-coverage: 1.0.3
|
||||
exit: 0.1.2
|
||||
@@ -14849,7 +14910,7 @@ snapshots:
|
||||
'@jest/transform': 30.2.0
|
||||
'@jest/types': 30.2.0
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
chalk: 4.1.2
|
||||
collect-v8-coverage: 1.0.3
|
||||
exit-x: 0.2.2
|
||||
@@ -14969,7 +15030,7 @@ snapshots:
|
||||
'@jest/schemas': 29.6.3
|
||||
'@types/istanbul-lib-coverage': 2.0.6
|
||||
'@types/istanbul-reports': 3.0.4
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
'@types/yargs': 17.0.35
|
||||
chalk: 4.1.2
|
||||
|
||||
@@ -14979,7 +15040,7 @@ snapshots:
|
||||
'@jest/schemas': 30.0.5
|
||||
'@types/istanbul-lib-coverage': 2.0.6
|
||||
'@types/istanbul-reports': 3.0.4
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
'@types/yargs': 17.0.35
|
||||
chalk: 4.1.2
|
||||
|
||||
@@ -18860,22 +18921,22 @@ snapshots:
|
||||
|
||||
'@types/bn.js@4.11.6':
|
||||
dependencies:
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
|
||||
'@types/bn.js@5.2.0':
|
||||
dependencies:
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
|
||||
'@types/body-parser@1.19.6':
|
||||
dependencies:
|
||||
'@types/connect': 3.4.38
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
|
||||
'@types/cacheable-request@6.0.3':
|
||||
dependencies:
|
||||
'@types/http-cache-semantics': 4.2.0
|
||||
'@types/keyv': 3.1.4
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
'@types/responselike': 1.0.3
|
||||
|
||||
'@types/caseless@0.12.5': {}
|
||||
@@ -18892,7 +18953,7 @@ snapshots:
|
||||
|
||||
'@types/connect@3.4.38':
|
||||
dependencies:
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
|
||||
'@types/cookie-parser@1.4.10(@types/express@4.17.25)':
|
||||
dependencies:
|
||||
@@ -18957,7 +19018,7 @@ snapshots:
|
||||
|
||||
'@types/express-serve-static-core@4.19.8':
|
||||
dependencies:
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
'@types/qs': 6.14.0
|
||||
'@types/range-parser': 1.2.7
|
||||
'@types/send': 1.2.1
|
||||
@@ -18971,7 +19032,7 @@ snapshots:
|
||||
|
||||
'@types/graceful-fs@4.1.9':
|
||||
dependencies:
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
|
||||
'@types/hast@2.3.10':
|
||||
dependencies:
|
||||
@@ -19000,6 +19061,8 @@ snapshots:
|
||||
expect: 29.7.0
|
||||
pretty-format: 29.7.0
|
||||
|
||||
'@types/js-yaml@4.0.9': {}
|
||||
|
||||
'@types/json-schema@7.0.15': {}
|
||||
|
||||
'@types/json5@0.0.29': {}
|
||||
@@ -19011,7 +19074,7 @@ snapshots:
|
||||
|
||||
'@types/keyv@3.1.4':
|
||||
dependencies:
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
|
||||
'@types/lodash@4.17.23': {}
|
||||
|
||||
@@ -19049,7 +19112,7 @@ snapshots:
|
||||
|
||||
'@types/pbkdf2@3.1.2':
|
||||
dependencies:
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
|
||||
'@types/pg@8.16.0':
|
||||
dependencies:
|
||||
@@ -19081,33 +19144,33 @@ snapshots:
|
||||
'@types/request@2.48.13':
|
||||
dependencies:
|
||||
'@types/caseless': 0.12.5
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
'@types/tough-cookie': 4.0.5
|
||||
form-data: 2.5.5
|
||||
|
||||
'@types/responselike@1.0.3':
|
||||
dependencies:
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
|
||||
'@types/secp256k1@4.0.7':
|
||||
dependencies:
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
|
||||
'@types/semver@7.7.1': {}
|
||||
|
||||
'@types/send@0.17.6':
|
||||
dependencies:
|
||||
'@types/mime': 1.3.5
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
|
||||
'@types/send@1.2.1':
|
||||
dependencies:
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
|
||||
'@types/serve-static@1.15.10':
|
||||
dependencies:
|
||||
'@types/http-errors': 2.0.5
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
'@types/send': 0.17.6
|
||||
|
||||
'@types/stack-utils@2.0.3': {}
|
||||
@@ -19136,11 +19199,11 @@ snapshots:
|
||||
|
||||
'@types/ws@7.4.7':
|
||||
dependencies:
|
||||
'@types/node': 22.19.15
|
||||
'@types/node': 20.19.33
|
||||
|
||||
'@types/ws@8.18.1':
|
||||
dependencies:
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
|
||||
'@types/yargs-parser@21.0.3': {}
|
||||
|
||||
@@ -19388,6 +19451,18 @@ snapshots:
|
||||
'@unrs/resolver-binding-win32-x64-msvc@1.11.1':
|
||||
optional: true
|
||||
|
||||
'@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@20.19.33))':
|
||||
dependencies:
|
||||
'@babel/core': 7.29.0
|
||||
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0)
|
||||
'@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0)
|
||||
'@rolldown/pluginutils': 1.0.0-beta.27
|
||||
'@types/babel__core': 7.20.5
|
||||
react-refresh: 0.17.0
|
||||
vite: 5.4.21(@types/node@20.19.33)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@22.19.15))':
|
||||
dependencies:
|
||||
'@babel/core': 7.29.0
|
||||
@@ -19400,18 +19475,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@25.2.3))':
|
||||
dependencies:
|
||||
'@babel/core': 7.29.0
|
||||
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0)
|
||||
'@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0)
|
||||
'@rolldown/pluginutils': 1.0.0-beta.27
|
||||
'@types/babel__core': 7.20.5
|
||||
react-refresh: 0.17.0
|
||||
vite: 5.4.21(@types/node@25.2.3)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@25.2.3)(jiti@1.21.7)(yaml@2.8.2))':
|
||||
dependencies:
|
||||
'@babel/core': 7.29.0
|
||||
@@ -19514,7 +19577,7 @@ snapshots:
|
||||
pathe: 1.1.2
|
||||
picocolors: 1.1.1
|
||||
sirv: 2.0.4
|
||||
vitest: 1.6.1(@types/node@25.2.3)(@vitest/ui@1.6.1)(jsdom@23.2.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))
|
||||
vitest: 1.6.1(@types/node@20.19.33)(@vitest/ui@1.6.1)(jsdom@23.2.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))
|
||||
|
||||
'@vitest/utils@1.6.1':
|
||||
dependencies:
|
||||
@@ -24829,6 +24892,16 @@ snapshots:
|
||||
|
||||
graphemer@1.4.0: {}
|
||||
|
||||
graphql-request@6.1.0(graphql@16.13.2):
|
||||
dependencies:
|
||||
'@graphql-typed-document-node/core': 3.2.0(graphql@16.13.2)
|
||||
cross-fetch: 3.2.0
|
||||
graphql: 16.13.2
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
|
||||
graphql@16.13.2: {}
|
||||
|
||||
gtoken@7.1.0:
|
||||
dependencies:
|
||||
gaxios: 6.7.1
|
||||
@@ -25456,7 +25529,7 @@ snapshots:
|
||||
'@jest/expect': 29.7.0
|
||||
'@jest/test-result': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
chalk: 4.1.2
|
||||
co: 4.6.0
|
||||
dedent: 1.7.1(babel-plugin-macros@3.1.0)
|
||||
@@ -25482,7 +25555,7 @@ snapshots:
|
||||
'@jest/expect': 30.2.0
|
||||
'@jest/test-result': 30.2.0
|
||||
'@jest/types': 30.2.0
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
chalk: 4.1.2
|
||||
co: 4.6.0
|
||||
dedent: 1.7.1(babel-plugin-macros@3.1.0)
|
||||
@@ -25521,15 +25594,15 @@ snapshots:
|
||||
- supports-color
|
||||
- ts-node
|
||||
|
||||
jest-cli@30.2.0(@types/node@25.2.3)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@25.2.3)(typescript@5.9.3)):
|
||||
jest-cli@30.2.0(@types/node@20.19.33)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.33)(typescript@5.9.3)):
|
||||
dependencies:
|
||||
'@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@25.2.3)(typescript@5.9.3))
|
||||
'@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.33)(typescript@5.9.3))
|
||||
'@jest/test-result': 30.2.0
|
||||
'@jest/types': 30.2.0
|
||||
chalk: 4.1.2
|
||||
exit-x: 0.2.2
|
||||
import-local: 3.2.0
|
||||
jest-config: 30.2.0(@types/node@25.2.3)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@25.2.3)(typescript@5.9.3))
|
||||
jest-config: 30.2.0(@types/node@20.19.33)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.33)(typescript@5.9.3))
|
||||
jest-util: 30.2.0
|
||||
jest-validate: 30.2.0
|
||||
yargs: 17.7.2
|
||||
@@ -25571,38 +25644,7 @@ snapshots:
|
||||
- babel-plugin-macros
|
||||
- supports-color
|
||||
|
||||
jest-config@29.7.0(@types/node@25.2.3)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.33)(typescript@5.9.3)):
|
||||
dependencies:
|
||||
'@babel/core': 7.29.0
|
||||
'@jest/test-sequencer': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
babel-jest: 29.7.0(@babel/core@7.29.0)
|
||||
chalk: 4.1.2
|
||||
ci-info: 3.9.0
|
||||
deepmerge: 4.3.1
|
||||
glob: 7.2.3
|
||||
graceful-fs: 4.2.11
|
||||
jest-circus: 29.7.0(babel-plugin-macros@3.1.0)
|
||||
jest-environment-node: 29.7.0
|
||||
jest-get-type: 29.6.3
|
||||
jest-regex-util: 29.6.3
|
||||
jest-resolve: 29.7.0
|
||||
jest-runner: 29.7.0
|
||||
jest-util: 29.7.0
|
||||
jest-validate: 29.7.0
|
||||
micromatch: 4.0.8
|
||||
parse-json: 5.2.0
|
||||
pretty-format: 29.7.0
|
||||
slash: 3.0.0
|
||||
strip-json-comments: 3.1.1
|
||||
optionalDependencies:
|
||||
'@types/node': 25.2.3
|
||||
ts-node: 10.9.2(@types/node@20.19.33)(typescript@5.9.3)
|
||||
transitivePeerDependencies:
|
||||
- babel-plugin-macros
|
||||
- supports-color
|
||||
|
||||
jest-config@30.2.0(@types/node@25.2.3)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@25.2.3)(typescript@5.9.3)):
|
||||
jest-config@30.2.0(@types/node@20.19.33)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.33)(typescript@5.9.3)):
|
||||
dependencies:
|
||||
'@babel/core': 7.29.0
|
||||
'@jest/get-type': 30.1.0
|
||||
@@ -25629,8 +25671,8 @@ snapshots:
|
||||
slash: 3.0.0
|
||||
strip-json-comments: 3.1.1
|
||||
optionalDependencies:
|
||||
'@types/node': 25.2.3
|
||||
ts-node: 10.9.2(@types/node@25.2.3)(typescript@5.9.3)
|
||||
'@types/node': 20.19.33
|
||||
ts-node: 10.9.2(@types/node@20.19.33)(typescript@5.9.3)
|
||||
transitivePeerDependencies:
|
||||
- babel-plugin-macros
|
||||
- supports-color
|
||||
@@ -25678,7 +25720,7 @@ snapshots:
|
||||
'@jest/environment': 29.7.0
|
||||
'@jest/fake-timers': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
jest-mock: 29.7.0
|
||||
jest-util: 29.7.0
|
||||
|
||||
@@ -25687,7 +25729,7 @@ snapshots:
|
||||
'@jest/environment': 30.2.0
|
||||
'@jest/fake-timers': 30.2.0
|
||||
'@jest/types': 30.2.0
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
jest-mock: 30.2.0
|
||||
jest-util: 30.2.0
|
||||
jest-validate: 30.2.0
|
||||
@@ -25698,7 +25740,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
'@types/graceful-fs': 4.1.9
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
anymatch: 3.1.3
|
||||
fb-watchman: 2.0.2
|
||||
graceful-fs: 4.2.11
|
||||
@@ -25713,7 +25755,7 @@ snapshots:
|
||||
jest-haste-map@30.2.0:
|
||||
dependencies:
|
||||
'@jest/types': 30.2.0
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
anymatch: 3.1.3
|
||||
fb-watchman: 2.0.2
|
||||
graceful-fs: 4.2.11
|
||||
@@ -25776,13 +25818,13 @@ snapshots:
|
||||
jest-mock@29.7.0:
|
||||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
jest-util: 29.7.0
|
||||
|
||||
jest-mock@30.2.0:
|
||||
dependencies:
|
||||
'@jest/types': 30.2.0
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
jest-util: 30.2.0
|
||||
|
||||
jest-pnp-resolver@1.2.3(jest-resolve@29.7.0):
|
||||
@@ -25841,7 +25883,7 @@ snapshots:
|
||||
'@jest/test-result': 29.7.0
|
||||
'@jest/transform': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
chalk: 4.1.2
|
||||
emittery: 0.13.1
|
||||
graceful-fs: 4.2.11
|
||||
@@ -25867,7 +25909,7 @@ snapshots:
|
||||
'@jest/test-result': 30.2.0
|
||||
'@jest/transform': 30.2.0
|
||||
'@jest/types': 30.2.0
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
chalk: 4.1.2
|
||||
emittery: 0.13.1
|
||||
exit-x: 0.2.2
|
||||
@@ -25896,7 +25938,7 @@ snapshots:
|
||||
'@jest/test-result': 29.7.0
|
||||
'@jest/transform': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
chalk: 4.1.2
|
||||
cjs-module-lexer: 1.4.3
|
||||
collect-v8-coverage: 1.0.3
|
||||
@@ -25923,7 +25965,7 @@ snapshots:
|
||||
'@jest/test-result': 30.2.0
|
||||
'@jest/transform': 30.2.0
|
||||
'@jest/types': 30.2.0
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
chalk: 4.1.2
|
||||
cjs-module-lexer: 2.2.0
|
||||
collect-v8-coverage: 1.0.3
|
||||
@@ -25995,7 +26037,7 @@ snapshots:
|
||||
jest-util@29.7.0:
|
||||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
chalk: 4.1.2
|
||||
ci-info: 3.9.0
|
||||
graceful-fs: 4.2.11
|
||||
@@ -26004,7 +26046,7 @@ snapshots:
|
||||
jest-util@30.2.0:
|
||||
dependencies:
|
||||
'@jest/types': 30.2.0
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
chalk: 4.1.2
|
||||
ci-info: 4.4.0
|
||||
graceful-fs: 4.2.11
|
||||
@@ -26032,7 +26074,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@jest/test-result': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
ansi-escapes: 4.3.2
|
||||
chalk: 4.1.2
|
||||
emittery: 0.13.1
|
||||
@@ -26043,7 +26085,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@jest/test-result': 30.2.0
|
||||
'@jest/types': 30.2.0
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
ansi-escapes: 4.3.2
|
||||
chalk: 4.1.2
|
||||
emittery: 0.13.1
|
||||
@@ -26052,14 +26094,14 @@ snapshots:
|
||||
|
||||
jest-worker@29.7.0:
|
||||
dependencies:
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
jest-util: 29.7.0
|
||||
merge-stream: 2.0.0
|
||||
supports-color: 8.1.1
|
||||
|
||||
jest-worker@30.2.0:
|
||||
dependencies:
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
'@ungap/structured-clone': 1.3.0
|
||||
jest-util: 30.2.0
|
||||
merge-stream: 2.0.0
|
||||
@@ -26077,12 +26119,12 @@ snapshots:
|
||||
- supports-color
|
||||
- ts-node
|
||||
|
||||
jest@30.2.0(@types/node@25.2.3)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@25.2.3)(typescript@5.9.3)):
|
||||
jest@30.2.0(@types/node@20.19.33)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.33)(typescript@5.9.3)):
|
||||
dependencies:
|
||||
'@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@25.2.3)(typescript@5.9.3))
|
||||
'@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.33)(typescript@5.9.3))
|
||||
'@jest/types': 30.2.0
|
||||
import-local: 3.2.0
|
||||
jest-cli: 30.2.0(@types/node@25.2.3)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@25.2.3)(typescript@5.9.3))
|
||||
jest-cli: 30.2.0(@types/node@20.19.33)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.33)(typescript@5.9.3))
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- babel-plugin-macros
|
||||
@@ -27956,7 +27998,7 @@ snapshots:
|
||||
'@protobufjs/path': 1.1.2
|
||||
'@protobufjs/pool': 1.1.0
|
||||
'@protobufjs/utf8': 1.1.0
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
long: 5.3.2
|
||||
|
||||
proxy-addr@2.0.7:
|
||||
@@ -29429,12 +29471,12 @@ snapshots:
|
||||
babel-jest: 30.2.0(@babel/core@7.29.0)
|
||||
jest-util: 30.2.0
|
||||
|
||||
ts-jest@29.4.6(@babel/core@7.29.0)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.29.0))(jest-util@30.2.0)(jest@30.2.0(@types/node@25.2.3)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@25.2.3)(typescript@5.9.3)))(typescript@5.9.3):
|
||||
ts-jest@29.4.6(@babel/core@7.29.0)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.29.0))(jest-util@30.2.0)(jest@30.2.0(@types/node@20.19.33)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.33)(typescript@5.9.3)))(typescript@5.9.3):
|
||||
dependencies:
|
||||
bs-logger: 0.2.6
|
||||
fast-json-stable-stringify: 2.1.0
|
||||
handlebars: 4.7.8
|
||||
jest: 30.2.0(@types/node@25.2.3)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@25.2.3)(typescript@5.9.3))
|
||||
jest: 30.2.0(@types/node@20.19.33)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.33)(typescript@5.9.3))
|
||||
json5: 2.2.3
|
||||
lodash.memoize: 4.1.2
|
||||
make-error: 1.3.6
|
||||
@@ -29485,25 +29527,6 @@ snapshots:
|
||||
v8-compile-cache-lib: 3.0.1
|
||||
yn: 3.1.1
|
||||
|
||||
ts-node@10.9.2(@types/node@25.2.3)(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@cspotcode/source-map-support': 0.8.1
|
||||
'@tsconfig/node10': 1.0.12
|
||||
'@tsconfig/node12': 1.0.11
|
||||
'@tsconfig/node14': 1.0.3
|
||||
'@tsconfig/node16': 1.0.4
|
||||
'@types/node': 25.2.3
|
||||
acorn: 8.15.0
|
||||
acorn-walk: 8.3.4
|
||||
arg: 4.1.3
|
||||
create-require: 1.1.1
|
||||
diff: 4.0.4
|
||||
make-error: 1.3.6
|
||||
typescript: 5.9.3
|
||||
v8-compile-cache-lib: 3.0.1
|
||||
yn: 3.1.1
|
||||
optional: true
|
||||
|
||||
ts-pattern@5.9.0: {}
|
||||
|
||||
tsconfig-paths@3.15.0:
|
||||
@@ -29980,13 +30003,13 @@ snapshots:
|
||||
- utf-8-validate
|
||||
- zod
|
||||
|
||||
vite-node@1.6.1(@types/node@25.2.3):
|
||||
vite-node@1.6.1(@types/node@20.19.33):
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
debug: 4.4.3
|
||||
pathe: 1.1.2
|
||||
picocolors: 1.1.1
|
||||
vite: 5.4.21(@types/node@25.2.3)
|
||||
vite: 5.4.21(@types/node@20.19.33)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- less
|
||||
@@ -30019,14 +30042,23 @@ snapshots:
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
vite-plugin-node-polyfills@0.24.0(rollup@4.57.1)(vite@5.4.21(@types/node@25.2.3)):
|
||||
vite-plugin-node-polyfills@0.24.0(rollup@4.57.1)(vite@5.4.21(@types/node@20.19.33)):
|
||||
dependencies:
|
||||
'@rollup/plugin-inject': 5.0.5(rollup@4.57.1)
|
||||
node-stdlib-browser: 1.3.1
|
||||
vite: 5.4.21(@types/node@25.2.3)
|
||||
vite: 5.4.21(@types/node@20.19.33)
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
|
||||
vite@5.4.21(@types/node@20.19.33):
|
||||
dependencies:
|
||||
esbuild: 0.21.5
|
||||
postcss: 8.5.6
|
||||
rollup: 4.57.1
|
||||
optionalDependencies:
|
||||
'@types/node': 20.19.33
|
||||
fsevents: 2.3.3
|
||||
|
||||
vite@5.4.21(@types/node@22.19.15):
|
||||
dependencies:
|
||||
esbuild: 0.21.5
|
||||
@@ -30036,15 +30068,6 @@ snapshots:
|
||||
'@types/node': 22.19.15
|
||||
fsevents: 2.3.3
|
||||
|
||||
vite@5.4.21(@types/node@25.2.3):
|
||||
dependencies:
|
||||
esbuild: 0.21.5
|
||||
postcss: 8.5.6
|
||||
rollup: 4.57.1
|
||||
optionalDependencies:
|
||||
'@types/node': 25.2.3
|
||||
fsevents: 2.3.3
|
||||
|
||||
vite@6.4.1(@types/node@25.2.3)(jiti@1.21.7)(yaml@2.8.2):
|
||||
dependencies:
|
||||
esbuild: 0.25.12
|
||||
@@ -30073,7 +30096,7 @@ snapshots:
|
||||
jiti: 1.21.7
|
||||
yaml: 2.8.2
|
||||
|
||||
vitest@1.6.1(@types/node@25.2.3)(@vitest/ui@1.6.1)(jsdom@23.2.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)):
|
||||
vitest@1.6.1(@types/node@20.19.33)(@vitest/ui@1.6.1)(jsdom@23.2.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)):
|
||||
dependencies:
|
||||
'@vitest/expect': 1.6.1
|
||||
'@vitest/runner': 1.6.1
|
||||
@@ -30092,11 +30115,11 @@ snapshots:
|
||||
strip-literal: 2.1.1
|
||||
tinybench: 2.9.0
|
||||
tinypool: 0.8.4
|
||||
vite: 5.4.21(@types/node@25.2.3)
|
||||
vite-node: 1.6.1(@types/node@25.2.3)
|
||||
vite: 5.4.21(@types/node@20.19.33)
|
||||
vite-node: 1.6.1(@types/node@20.19.33)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
'@types/node': 25.2.3
|
||||
'@types/node': 20.19.33
|
||||
'@vitest/ui': 1.6.1(vitest@1.6.1)
|
||||
jsdom: 23.2.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)
|
||||
transitivePeerDependencies:
|
||||
|
||||
@@ -14,6 +14,7 @@ packages:
|
||||
- rpc-translator-138
|
||||
- smom-dbis-138/frontend-dapp
|
||||
- smom-dbis-138/services/token-aggregation
|
||||
- smom-dbis-138/test/emoney/api
|
||||
- info-defi-oracle-138
|
||||
|
||||
ignoredBuiltDependencies:
|
||||
|
||||
@@ -8,7 +8,7 @@ Objective: complete Mainnet Path B by first using already deployed contracts, va
|
||||
- Generated gas report: `reports/deployer_needed_chain_gas_balances_2026-03-04.md` (all required chains `ok`).
|
||||
- Read-only verification status:
|
||||
- `scripts/verify/check-deployer-balance-blockscout-vs-rpc.sh https://rpc-http-pub.d-bis.org https://explorer.d-bis.org/api/v2` -> passed, balances match.
|
||||
- `scripts/verify/check-contracts-on-chain-138.sh http://192.168.11.211:8545` -> passed, 59/59 addresses present (check-contracts-on-chain-138.sh).
|
||||
- `scripts/verify/check-contracts-on-chain-138.sh http://192.168.11.211:8545` -> passed, 59/59 addresses present **at this report date** — **current script list: 64/64** (ISO20022Router added 2026-03-30; see `docs/00-meta/INTEGRATION_GAPS_AND_NEXT_STEPS_2026-03-30.md`).
|
||||
- `smom-dbis-138/scripts/verify-bridge-setup-checklist.sh` -> passed when using `FORCE_RPC_URL=https://rpc-http-pub.d-bis.org`.
|
||||
- `smom-dbis-138/scripts/verify-bridge-prerequisites.sh 0.001` -> passed when using `FORCE_RPC_URL=https://rpc-http-pub.d-bis.org`.
|
||||
- E2E route health:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Hyperledger Services Deployment Guide
|
||||
|
||||
Complete deployment guide for Hyperledger services (Firefly, Cacti, Fabric, Indy) on Proxmox VE.
|
||||
Complete deployment guide for Hyperledger services (Firefly, Cacti, Fabric, Indy, Aries / AnonCreds, Caliper) on Proxmox VE.
|
||||
|
||||
## 📋 Overview
|
||||
|
||||
@@ -9,6 +9,16 @@ This document covers deployment of additional Hyperledger blockchain frameworks
|
||||
- **Cacti**: Blockchain integration platform
|
||||
- **Fabric**: Permissioned blockchain framework
|
||||
- **Indy**: Self-sovereign identity ledger
|
||||
- **Aries / AnonCreds**: ACA-Py based identity-agent runtime
|
||||
- **Caliper**: Benchmark harness and workload workspace
|
||||
|
||||
Current verified primaries in this workspace:
|
||||
- **Cacti** on `5200`
|
||||
- **Fabric** on `6000`
|
||||
- **Firefly** on `6200`
|
||||
- **Indy** on `6400`
|
||||
- **Aries / AnonCreds** on `6500`
|
||||
- **Caliper** on `6600`
|
||||
|
||||
## 🚀 Quick Deployment
|
||||
|
||||
@@ -27,7 +37,15 @@ DEPLOY_FIREFLY=true DEPLOY_CACTI=false DEPLOY_FABRIC=false DEPLOY_INDY=false \
|
||||
./scripts/deployment/deploy-hyperledger-services.sh
|
||||
|
||||
# Deploy only Cacti
|
||||
DEPLOY_FIREFLY=false DEPLOY_CACTI=true DEPLOY_FABRIC=false DEPLOY_INDY=false \
|
||||
DEPLOY_FIREFLY=false DEPLOY_CACTI=true DEPLOY_FABRIC=false DEPLOY_INDY=false DEPLOY_ARIES=false DEPLOY_CALIPER=false \
|
||||
./scripts/deployment/deploy-hyperledger-services.sh
|
||||
|
||||
# Deploy only Aries / AnonCreds
|
||||
DEPLOY_FIREFLY=false DEPLOY_CACTI=false DEPLOY_FABRIC=false DEPLOY_INDY=false DEPLOY_ARIES=true DEPLOY_CALIPER=false \
|
||||
./scripts/deployment/deploy-hyperledger-services.sh
|
||||
|
||||
# Deploy only Caliper
|
||||
DEPLOY_FIREFLY=false DEPLOY_CACTI=false DEPLOY_FABRIC=false DEPLOY_INDY=false DEPLOY_ARIES=false DEPLOY_CALIPER=true \
|
||||
./scripts/deployment/deploy-hyperledger-services.sh
|
||||
```
|
||||
|
||||
@@ -35,103 +53,155 @@ DEPLOY_FIREFLY=false DEPLOY_CACTI=true DEPLOY_FABRIC=false DEPLOY_INDY=false \
|
||||
|
||||
### Hyperledger Firefly
|
||||
|
||||
**VMID Range**: 150-159
|
||||
**Default VMID**: 150
|
||||
**VMID Range**: 6200-6201
|
||||
**Default VMID**: 6200
|
||||
**Container**: `firefly-1`
|
||||
|
||||
**After deployment**:
|
||||
1. Configure Besu connection:
|
||||
```bash
|
||||
pct exec 150 -- bash -c "cd /opt/firefly && \
|
||||
sed -i 's|FF_BLOCKCHAIN_RPC=.*|FF_BLOCKCHAIN_RPC=http://192.168.11.250:8545|' docker-compose.yml && \
|
||||
sed -i 's|FF_BLOCKCHAIN_WS=.*|FF_BLOCKCHAIN_WS=ws://192.168.11.250:8546|' docker-compose.yml"
|
||||
pct exec 6200 -- bash -c "cd /opt/firefly && \
|
||||
sed -i 's|FF_BLOCKCHAIN_RPC=.*|FF_BLOCKCHAIN_RPC=http://192.168.11.211:8545|' docker-compose.yml && \
|
||||
sed -i 's|FF_BLOCKCHAIN_WS=.*|FF_BLOCKCHAIN_WS=ws://192.168.11.211:8546|' docker-compose.yml"
|
||||
```
|
||||
|
||||
2. Start service:
|
||||
```bash
|
||||
pct exec 150 -- systemctl start firefly
|
||||
pct exec 6200 -- systemctl start firefly
|
||||
```
|
||||
|
||||
3. Access API:
|
||||
```bash
|
||||
curl http://10.3.1.60:5000/api/v1/status
|
||||
curl http://192.168.11.35:5000/api/v1/status
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Hyperledger Cacti
|
||||
|
||||
**VMID Range**: 150-159
|
||||
**Default VMID**: 151
|
||||
**VMID Range**: 5200-5202
|
||||
**Default VMID**: 5200
|
||||
**Container**: `cacti-1`
|
||||
|
||||
**After deployment**:
|
||||
1. Configure Besu connection:
|
||||
1. Configure Besu connection if you need a non-default endpoint:
|
||||
```bash
|
||||
pct exec 151 -- bash -c "cd /opt/cacti && \
|
||||
sed -i 's|BESU_RPC_URL=.*|BESU_RPC_URL=http://192.168.11.250:8545|' docker-compose.yml && \
|
||||
sed -i 's|BESU_WS_URL=.*|BESU_WS_URL=ws://192.168.11.250:8546|' docker-compose.yml"
|
||||
pct exec 5200 -- bash -c "cd /opt/cacti && \
|
||||
sed -i 's|http://192.168.11.211:8545|http://192.168.11.211:8545|' docker-compose.yml && \
|
||||
sed -i 's|ws://192.168.11.211:8546|ws://192.168.11.211:8546|' docker-compose.yml"
|
||||
```
|
||||
|
||||
2. Start service:
|
||||
```bash
|
||||
pct exec 151 -- systemctl start cacti
|
||||
pct exec 5200 -- systemctl start cacti
|
||||
```
|
||||
|
||||
3. Access API:
|
||||
3. Wait for the first plugin install, then access API:
|
||||
```bash
|
||||
curl http://10.3.1.61:4000/api/v1/api-server/healthcheck
|
||||
curl http://192.168.11.80:4000/api/v1/api-server/healthcheck
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Hyperledger Fabric
|
||||
|
||||
**VMID Range**: 150-159
|
||||
**Default VMID**: 152
|
||||
**VMID Range**: 6000-6002
|
||||
**Default VMID**: 6000
|
||||
**Container**: `fabric-1`
|
||||
|
||||
**After deployment**:
|
||||
1. Generate crypto materials (customize for your network):
|
||||
1. Start the official sample network:
|
||||
```bash
|
||||
pct exec 152 -- bash
|
||||
cd /opt/fabric
|
||||
# Generate crypto materials
|
||||
cryptogen generate --config=./config/crypto-config.yaml
|
||||
# Generate genesis block
|
||||
configtxgen -profile TestNetwork -channelID test-channel -outputBlock ./network/genesis.block
|
||||
pct exec 6000 -- bash -lc "/usr/local/bin/fabric-up-sample-network.sh"
|
||||
```
|
||||
|
||||
2. Configure network in `docker-compose.yml` (requires manual setup)
|
||||
2. Verify the network:
|
||||
```bash
|
||||
pct exec 6000 -- bash -lc "docker ps --format 'table {{.Names}}\t{{.Status}}'"
|
||||
pct exec 6000 -- bash -lc "cd /opt/fabric/fabric-samples/test-network && \
|
||||
export FABRIC_CFG_PATH=/opt/fabric/fabric-samples/config && \
|
||||
. scripts/envVar.sh && setGlobals 1 && peer channel getinfo -c mychannel && \
|
||||
setGlobals 2 && peer channel getinfo -c mychannel"
|
||||
```
|
||||
|
||||
3. Start Fabric network
|
||||
3. Expected listeners:
|
||||
```bash
|
||||
pct exec 6000 -- ss -ltnp | grep -E ':7050\\b|:7051\\b|:9051\\b|:9443\\b|:9444\\b|:9445\\b'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Hyperledger Indy
|
||||
|
||||
**VMID Range**: 150-159
|
||||
**Default VMID**: 153
|
||||
**VMID Range**: 6400-6402
|
||||
**Default VMID**: 6400
|
||||
**Container**: `indy-1`
|
||||
|
||||
**After deployment**:
|
||||
1. Generate genesis transactions:
|
||||
```bash
|
||||
pct exec 153 -- bash
|
||||
cd /opt/indy
|
||||
# Generate pool transactions (requires pool configuration)
|
||||
# See Indy documentation for pool setup
|
||||
pct exec 6400 -- bash -lc "/usr/local/bin/indy-generate-pool.sh"
|
||||
```
|
||||
|
||||
2. Configure pool name:
|
||||
2. Start the four-node pool:
|
||||
```bash
|
||||
pct exec 153 -- bash -c "cd /opt/indy && \
|
||||
sed -i 's|NETWORK_NAME=.*|NETWORK_NAME=smom-dbis-138|' docker-compose.yml"
|
||||
pct exec 6400 -- systemctl start indy
|
||||
```
|
||||
|
||||
3. Start service:
|
||||
3. Verify the pool:
|
||||
```bash
|
||||
pct exec 153 -- systemctl start indy
|
||||
pct exec 6400 -- bash -lc "docker ps --format 'table {{.Names}}\t{{.Status}}' | grep indy"
|
||||
pct exec 6400 -- ss -ltnp | grep -E ':970[1-8]\\b'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Hyperledger Aries / AnonCreds
|
||||
|
||||
**VMID Range**: 6500
|
||||
**Default VMID**: 6500
|
||||
**Container**: `aries-1`
|
||||
|
||||
**After deployment**:
|
||||
1. Copy the Indy genesis file from the primary Indy CT:
|
||||
```bash
|
||||
pct exec 6400 -- cat /var/lib/indy/sandbox/pool_transactions_genesis > /root/indy-pool_transactions_genesis
|
||||
pct push 6500 /root/indy-pool_transactions_genesis /opt/aries/ledger/pool_transactions_genesis
|
||||
```
|
||||
|
||||
2. Set the real endpoint and start the ACA-Py service:
|
||||
```bash
|
||||
pct exec 6500 -- bash -lc "sed -i 's|^ACAPY_ENDPOINT=.*|ACAPY_ENDPOINT=http://192.168.11.88:8030|' /opt/aries/.env && systemctl start aries"
|
||||
```
|
||||
|
||||
3. Verify the agent:
|
||||
```bash
|
||||
pct exec 6500 -- bash -lc "systemctl is-active aries && curl -fsS http://127.0.0.1:8031/status/live"
|
||||
pct exec 6500 -- bash -lc "docker inspect acapy-agent --format '{{json .Config.Cmd}}'"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Hyperledger Caliper
|
||||
|
||||
**VMID Range**: 6600
|
||||
**Default VMID**: 6600
|
||||
**Container**: `caliper-1`
|
||||
|
||||
**After deployment**:
|
||||
1. Verify the workspace and Besu binding:
|
||||
```bash
|
||||
pct exec 6600 -- bash -lc "/usr/local/bin/caliper-chain138-doctor.sh"
|
||||
```
|
||||
|
||||
2. Confirm the installed packages:
|
||||
```bash
|
||||
pct exec 6600 -- bash -lc "cd /opt/caliper/workspace && npm ls --depth=0 @hyperledger/caliper-cli web3"
|
||||
```
|
||||
|
||||
3. Confirm Chain 138 RPC reachability from the benchmark CT:
|
||||
```bash
|
||||
pct exec 6600 -- bash -lc "curl -fsS http://192.168.11.211:8545 -H 'Content-Type: application/json' --data '{\"jsonrpc\":\"2.0\",\"method\":\"eth_blockNumber\",\"params\":[],\"id\":1}'"
|
||||
```
|
||||
|
||||
## 🔗 Integration
|
||||
@@ -161,46 +231,68 @@ pct exec 153 -- systemctl start indy
|
||||
|
||||
| Service | CPU | RAM | Disk | VMID |
|
||||
|---------|-----|-----|------|------|
|
||||
| Firefly | 2 | 4GB | 50GB | 150 |
|
||||
| Cacti | 2 | 4GB | 50GB | 151 |
|
||||
| Fabric | 4 | 8GB | 100GB | 152 |
|
||||
| Indy | 4 | 8GB | 100GB | 153 |
|
||||
| Firefly | 2 | 4GB | 50GB | 6200 |
|
||||
| Cacti | 2 | 4GB | 50GB | 5200 |
|
||||
| Fabric | 4 | 8GB | 100GB | 6000 |
|
||||
| Indy | 4 | 8GB | 100GB | 6400 |
|
||||
| Aries / AnonCreds | 2 | 4GB | 50GB | 6500 |
|
||||
| Caliper | 2 | 4GB | 50GB | 6600 |
|
||||
|
||||
**Total Additional Resources**: ~12 cores, 24GB RAM, 300GB disk
|
||||
**Total Additional Resources**: ~16 cores, 32GB RAM, 400GB disk
|
||||
|
||||
## 🆘 Troubleshooting
|
||||
|
||||
### Firefly Issues
|
||||
```bash
|
||||
# Check logs
|
||||
pct exec 150 -- journalctl -u firefly -f
|
||||
pct exec 6200 -- journalctl -u firefly -f
|
||||
|
||||
# Check Docker containers
|
||||
pct exec 150 -- docker ps
|
||||
pct exec 150 -- docker-compose -f /opt/firefly/docker-compose.yml logs
|
||||
pct exec 6200 -- docker ps
|
||||
pct exec 6200 -- docker-compose -f /opt/firefly/docker-compose.yml logs
|
||||
```
|
||||
|
||||
### Cacti Issues
|
||||
```bash
|
||||
# Check logs
|
||||
pct exec 151 -- journalctl -u cacti -f
|
||||
pct exec 151 -- docker-compose -f /opt/cacti/docker-compose.yml logs
|
||||
pct exec 5200 -- journalctl -u cacti -f
|
||||
pct exec 5200 -- docker-compose -f /opt/cacti/docker-compose.yml logs
|
||||
```
|
||||
|
||||
### Fabric Issues
|
||||
```bash
|
||||
# Check Docker containers
|
||||
pct exec 152 -- docker ps
|
||||
# Review network logs
|
||||
pct exec 152 -- docker logs <container-name>
|
||||
pct exec 6000 -- docker ps
|
||||
# Re-run the official sample network helper
|
||||
pct exec 6000 -- bash -lc "/usr/local/bin/fabric-up-sample-network.sh"
|
||||
```
|
||||
|
||||
### Indy Issues
|
||||
```bash
|
||||
# Regenerate pool artifacts
|
||||
pct exec 6400 -- bash -lc "/usr/local/bin/indy-generate-pool.sh"
|
||||
# Check all node logs
|
||||
pct exec 153 -- docker-compose -f /opt/indy/docker-compose.yml logs
|
||||
pct exec 6400 -- docker-compose -f /opt/indy/docker-compose.yml logs
|
||||
# Check individual node
|
||||
pct exec 153 -- docker logs indy-node-1
|
||||
pct exec 6400 -- docker logs indy-node-1
|
||||
```
|
||||
|
||||
### Aries / AnonCreds Issues
|
||||
```bash
|
||||
# Check service status
|
||||
pct exec 6500 -- systemctl status aries
|
||||
# Check agent logs
|
||||
pct exec 6500 -- docker logs acapy-agent
|
||||
# Check admin API
|
||||
pct exec 6500 -- curl -fsS http://127.0.0.1:8031/status/live
|
||||
```
|
||||
|
||||
### Caliper Issues
|
||||
```bash
|
||||
# Re-run the workspace doctor
|
||||
pct exec 6600 -- bash -lc "/usr/local/bin/caliper-chain138-doctor.sh"
|
||||
# Inspect workspace dependencies
|
||||
pct exec 6600 -- bash -lc "cd /opt/caliper/workspace && npm ls --depth=0"
|
||||
```
|
||||
|
||||
## 📚 Additional Resources
|
||||
@@ -208,5 +300,6 @@ pct exec 153 -- docker logs indy-node-1
|
||||
- [Firefly Documentation](https://hyperledger.github.io/firefly/)
|
||||
- [Cacti Documentation](https://github.com/hyperledger/cacti)
|
||||
- [Fabric Documentation](https://hyperledger-fabric.readthedocs.io/)
|
||||
- [ACA-Py Documentation](https://aca-py.org/)
|
||||
- [Caliper Documentation](https://hyperledger-caliper.github.io/caliper/)
|
||||
- [Indy Documentation](https://hyperledger-indy.readthedocs.io/)
|
||||
|
||||
|
||||
162
smom-dbis-138-proxmox/install/aries-install.sh
Normal file
162
smom-dbis-138-proxmox/install/aries-install.sh
Normal file
@@ -0,0 +1,162 @@
|
||||
#!/usr/bin/env bash
|
||||
# Installation script for Hyperledger Aries / AnonCreds via ACA-Py in an LXC container
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source /dev/stdin <<<"$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func 2>/dev/null || echo '')"
|
||||
|
||||
ACAPY_IMAGE="${ACAPY_IMAGE:-ghcr.io/openwallet-foundation/acapy-agent:py3.12-1.3-lts}"
|
||||
ARIES_USER="${ARIES_USER:-aries}"
|
||||
ARIES_GROUP="${ARIES_GROUP:-aries}"
|
||||
ARIES_HOME="/opt/aries"
|
||||
ARIES_DATA="/var/lib/aries"
|
||||
ARIES_LEDGER_DIR="$ARIES_HOME/ledger"
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
log_info "Updating system packages..."
|
||||
export LC_ALL=C
|
||||
export LANG=C
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -qq
|
||||
apt-get upgrade -y -qq
|
||||
apt-get install -y -qq \
|
||||
docker.io \
|
||||
docker-compose \
|
||||
curl \
|
||||
wget \
|
||||
jq \
|
||||
ca-certificates \
|
||||
gnupg \
|
||||
lsb-release
|
||||
|
||||
log_success "System packages updated"
|
||||
|
||||
systemctl enable docker
|
||||
systemctl start docker
|
||||
|
||||
log_info "Creating aries user..."
|
||||
if ! id -u "$ARIES_USER" &>/dev/null; then
|
||||
useradd -r -s /bin/bash -d "$ARIES_HOME" -m "$ARIES_USER"
|
||||
usermod -aG docker "$ARIES_USER"
|
||||
log_success "Created aries user"
|
||||
else
|
||||
log_info "Aries user already exists"
|
||||
usermod -aG docker "$ARIES_USER"
|
||||
fi
|
||||
|
||||
log_info "Pulling ACA-Py image..."
|
||||
docker pull "$ACAPY_IMAGE"
|
||||
|
||||
log_info "Creating Aries directories..."
|
||||
mkdir -p "$ARIES_HOME" "$ARIES_DATA" "$ARIES_LEDGER_DIR"
|
||||
chown -R "$ARIES_USER:$ARIES_GROUP" "$ARIES_HOME" "$ARIES_DATA"
|
||||
|
||||
cat > "$ARIES_HOME/.env" <<'EOF'
|
||||
ACAPY_IMAGE=ghcr.io/openwallet-foundation/acapy-agent:py3.12-1.3-lts
|
||||
ACAPY_ADMIN_PORT=8031
|
||||
ACAPY_ENDPOINT_PORT=8030
|
||||
ACAPY_ENDPOINT=http://localhost:8030
|
||||
ACAPY_LABEL=dbis-aries-1
|
||||
ACAPY_WALLET_TYPE=askar-anoncreds
|
||||
ACAPY_WALLET_NAME=dbis-aries-wallet
|
||||
ACAPY_WALLET_KEY=dbis-aries-wallet-key-change-me
|
||||
ACAPY_LOG_LEVEL=info
|
||||
EOF
|
||||
|
||||
cat > "$ARIES_HOME/docker-compose.yml" <<'EOF'
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
acapy:
|
||||
image: ${ACAPY_IMAGE:-ghcr.io/openwallet-foundation/acapy-agent:py3.12-1.3-lts}
|
||||
container_name: acapy-agent
|
||||
command: >
|
||||
start
|
||||
--label ${ACAPY_LABEL:-dbis-aries-1}
|
||||
--inbound-transport http 0.0.0.0 ${ACAPY_ENDPOINT_PORT:-8030}
|
||||
--outbound-transport http
|
||||
--admin 0.0.0.0 ${ACAPY_ADMIN_PORT:-8031}
|
||||
--admin-insecure-mode
|
||||
--endpoint ${ACAPY_ENDPOINT:-http://localhost:8030}
|
||||
--wallet-type ${ACAPY_WALLET_TYPE:-askar-anoncreds}
|
||||
--wallet-name ${ACAPY_WALLET_NAME:-dbis-aries-wallet}
|
||||
--wallet-key ${ACAPY_WALLET_KEY:-dbis-aries-wallet-key-change-me}
|
||||
--auto-provision
|
||||
--genesis-file /opt/aries/ledger/pool_transactions_genesis
|
||||
--auto-accept-invites
|
||||
--auto-accept-requests
|
||||
--auto-ping-connection
|
||||
--public-invites
|
||||
--emit-new-didcomm-prefix
|
||||
--log-level ${ACAPY_LOG_LEVEL:-info}
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- "${ACAPY_ENDPOINT_PORT:-8030}:${ACAPY_ENDPOINT_PORT:-8030}"
|
||||
- "${ACAPY_ADMIN_PORT:-8031}:${ACAPY_ADMIN_PORT:-8031}"
|
||||
volumes:
|
||||
- aries-data:/home/aries/.local/share/aries_cloudagent
|
||||
- ./ledger:/opt/aries/ledger
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
aries-data:
|
||||
EOF
|
||||
|
||||
cat > "$ARIES_LEDGER_DIR/README.txt" <<'EOF'
|
||||
Place the Indy pool genesis file at:
|
||||
/opt/aries/ledger/pool_transactions_genesis
|
||||
|
||||
The live DBIS setup copies this file from the Indy primary:
|
||||
/var/lib/indy/sandbox/pool_transactions_genesis
|
||||
EOF
|
||||
|
||||
cat > /usr/local/bin/aries-healthcheck.sh <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
curl -fsS http://127.0.0.1:8031/status/live | jq .
|
||||
EOF
|
||||
chmod +x /usr/local/bin/aries-healthcheck.sh
|
||||
|
||||
cat > /etc/systemd/system/aries.service <<EOF
|
||||
[Unit]
|
||||
Description=Hyperledger Aries / AnonCreds (ACA-Py)
|
||||
After=docker.service
|
||||
Requires=docker.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
WorkingDirectory=$ARIES_HOME
|
||||
User=$ARIES_USER
|
||||
Group=$ARIES_GROUP
|
||||
ExecStart=/usr/bin/docker-compose -f $ARIES_HOME/docker-compose.yml up -d
|
||||
ExecStop=/usr/bin/docker-compose -f $ARIES_HOME/docker-compose.yml down
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable aries.service
|
||||
|
||||
chown -R "$ARIES_USER:$ARIES_GROUP" "$ARIES_HOME" "$ARIES_DATA"
|
||||
|
||||
log_success "Aries / AnonCreds installation completed!"
|
||||
log_info "Next steps:"
|
||||
log_info "1. Copy the Indy genesis file to $ARIES_LEDGER_DIR/pool_transactions_genesis"
|
||||
log_info "2. Update $ARIES_HOME/.env so ACAPY_ENDPOINT matches the container IP or DNS name"
|
||||
log_info "3. Start service: systemctl start aries"
|
||||
log_info "4. Verify admin API: curl http://localhost:8031/status/live"
|
||||
@@ -6,7 +6,7 @@ set -euo pipefail
|
||||
|
||||
source /dev/stdin <<<"$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func 2>/dev/null || echo '')"
|
||||
|
||||
CACTI_VERSION="${CACTI_VERSION:-latest}"
|
||||
CACTI_IMAGE="${CACTI_IMAGE:-ghcr.io/hyperledger/cactus-connector-besu:2024-07-04-8c030ae}"
|
||||
CACTI_USER="${CACTI_USER:-cacti}"
|
||||
CACTI_GROUP="${CACTI_GROUP:-cacti}"
|
||||
CACTI_HOME="/opt/cacti"
|
||||
@@ -62,8 +62,8 @@ log_info "Creating directories..."
|
||||
mkdir -p "$CACTI_HOME" "$CACTI_DATA"
|
||||
chown -R "$CACTI_USER:$CACTI_GROUP" "$CACTI_HOME" "$CACTI_DATA"
|
||||
|
||||
# Download Cacti docker-compose
|
||||
log_info "Downloading Cacti configuration..."
|
||||
# Create Cacti runtime configuration
|
||||
log_info "Creating Cacti configuration..."
|
||||
cd "$CACTI_HOME"
|
||||
|
||||
# Create docker-compose.yml
|
||||
@@ -72,39 +72,28 @@ version: '3.8'
|
||||
|
||||
services:
|
||||
cactus-api:
|
||||
image: ghcr.io/hyperledger/cactus-cmd-api-server:${CACTI_VERSION:-latest}
|
||||
image: ${CACTI_IMAGE:-ghcr.io/hyperledger/cactus-connector-besu:2024-07-04-8c030ae}
|
||||
container_name: cactus-api
|
||||
security_opt:
|
||||
- apparmor=unconfined
|
||||
ports:
|
||||
- "4000:4000"
|
||||
- "4001:4001"
|
||||
environment:
|
||||
- CACTUS_NODE_ID=cactus-node-1
|
||||
- CACTUS_LOG_LEVEL=info
|
||||
volumes:
|
||||
- cacti-data:/var/lib/cactus
|
||||
- AUTHORIZATION_PROTOCOL=${AUTHORIZATION_PROTOCOL:-NONE}
|
||||
- AUTHORIZATION_CONFIG_JSON=${AUTHORIZATION_CONFIG_JSON:-{}}
|
||||
- GRPC_TLS_ENABLED=${GRPC_TLS_ENABLED:-false}
|
||||
- API_TLS_ENABLED=${API_TLS_ENABLED:-false}
|
||||
- HTTP_PORT=${HTTP_PORT:-4000}
|
||||
- WS_PORT=${WS_PORT:-4001}
|
||||
- API_HOST=${API_HOST:-0.0.0.0}
|
||||
- CACTUS_NODE_ID=${CACTUS_NODE_ID:-cactus-node-1}
|
||||
- CACTUS_LOG_LEVEL=${CACTUS_LOG_LEVEL:-info}
|
||||
- PLUGINS=[{"packageName":"@hyperledger/cactus-plugin-ledger-connector-besu","type":"org.hyperledger.cactus.plugin_import_type.LOCAL","action":"org.hyperledger.cactus.plugin_import_action.INSTALL","options":{"rpcApiHttpHost":"${BESU_RPC_URL:-http://192.168.11.211:8545}","rpcApiWsHost":"${BESU_WS_URL:-ws://192.168.11.211:8546}","instanceId":"${CACTUS_BESU_INSTANCE_ID:-besu-chain-138-connector}"}}]
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- cacti-network
|
||||
|
||||
besu-connector:
|
||||
image: ghcr.io/hyperledger/cactus-plugin-ledger-connector-besu:${CACTI_VERSION:-latest}
|
||||
container_name: cactus-besu-connector
|
||||
depends_on:
|
||||
- cactus-api
|
||||
environment:
|
||||
- BESU_RPC_URL=${BESU_RPC_URL:-http://192.168.11.250:8545}
|
||||
- BESU_WS_URL=${BESU_WS_URL:-ws://192.168.11.250:8546}
|
||||
- CHAIN_ID=${CHAIN_ID:-138}
|
||||
- CACTUS_NODE_ID=cactus-node-1
|
||||
ports:
|
||||
- "4100:4100"
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- cacti-network
|
||||
|
||||
volumes:
|
||||
cacti-data:
|
||||
|
||||
networks:
|
||||
cacti-network:
|
||||
driver: bridge
|
||||
@@ -143,8 +132,7 @@ log_success "Systemd service created"
|
||||
|
||||
log_success "Cacti installation completed!"
|
||||
log_info "Next steps:"
|
||||
log_info "1. Edit $CACTI_HOME/docker-compose.yml to set BESU_RPC_URL and other configs"
|
||||
log_info "2. Set environment variables in /etc/systemd/system/cacti.service or .env file"
|
||||
log_info "3. Start service: systemctl start cacti"
|
||||
log_info "4. Cacti API will be available at: http://localhost:4000"
|
||||
|
||||
log_info "1. Review $CACTI_HOME/docker-compose.yml if you need non-default Besu RPC settings"
|
||||
log_info "2. Start service: systemctl start cacti"
|
||||
log_info "3. Wait for the first plugin install to complete inside the container"
|
||||
log_info "4. Cacti API healthcheck: http://localhost:4000/api/v1/api-server/healthcheck"
|
||||
|
||||
136
smom-dbis-138-proxmox/install/caliper-install.sh
Normal file
136
smom-dbis-138-proxmox/install/caliper-install.sh
Normal file
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env bash
|
||||
# Installation script for Hyperledger Caliper benchmark workspace in an LXC container
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source /dev/stdin <<<"$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func 2>/dev/null || echo '')"
|
||||
|
||||
CALIPER_VERSION="${CALIPER_VERSION:-0.6.0}"
|
||||
CALIPER_NODE_MAJOR="${CALIPER_NODE_MAJOR:-20}"
|
||||
CALIPER_USER="${CALIPER_USER:-caliper}"
|
||||
CALIPER_GROUP="${CALIPER_GROUP:-caliper}"
|
||||
CALIPER_HOME="/opt/caliper"
|
||||
CALIPER_WORKSPACE="$CALIPER_HOME/workspace"
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
log_info "Updating system packages..."
|
||||
export LC_ALL=C
|
||||
export LANG=C
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -qq
|
||||
apt-get upgrade -y -qq
|
||||
apt-get install -y -qq \
|
||||
curl \
|
||||
wget \
|
||||
jq \
|
||||
ca-certificates \
|
||||
gnupg \
|
||||
lsb-release \
|
||||
git \
|
||||
make \
|
||||
g++ \
|
||||
python3
|
||||
|
||||
log_info "Installing Node.js ${CALIPER_NODE_MAJOR}.x..."
|
||||
install -d -m 0755 /etc/apt/keyrings
|
||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \
|
||||
| gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
|
||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_${CALIPER_NODE_MAJOR}.x nodistro main" \
|
||||
> /etc/apt/sources.list.d/nodesource.list
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq nodejs
|
||||
|
||||
log_success "System packages updated"
|
||||
|
||||
log_info "Creating caliper user..."
|
||||
if ! id -u "$CALIPER_USER" &>/dev/null; then
|
||||
useradd -r -s /bin/bash -d "$CALIPER_HOME" -m "$CALIPER_USER"
|
||||
log_success "Created caliper user"
|
||||
else
|
||||
log_info "Caliper user already exists"
|
||||
fi
|
||||
|
||||
log_info "Creating Caliper workspace..."
|
||||
mkdir -p \
|
||||
"$CALIPER_WORKSPACE/networks/besu" \
|
||||
"$CALIPER_WORKSPACE/benchmarks/read-only" \
|
||||
"$CALIPER_WORKSPACE/workloads"
|
||||
|
||||
cat > "$CALIPER_WORKSPACE/package.json" <<EOF
|
||||
{
|
||||
"name": "dbis-chain138-caliper",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"description": "Hyperledger Caliper workspace for DBIS Chain 138 and auxiliary Hyperledger benchmarks",
|
||||
"license": "UNLICENSED"
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > "$CALIPER_WORKSPACE/networks/besu/chain138.example.json" <<'EOF'
|
||||
{
|
||||
"caliper": {
|
||||
"blockchain": "ethereum"
|
||||
},
|
||||
"ethereum": {
|
||||
"url": "ws://192.168.11.211:8546",
|
||||
"transactionConfirmationBlocks": 1,
|
||||
"contracts": {}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > "$CALIPER_WORKSPACE/benchmarks/read-only/config.yaml" <<'EOF'
|
||||
test:
|
||||
name: chain138-read-only-placeholder
|
||||
description: Placeholder benchmark config for the DBIS Chain 138 Caliper workspace.
|
||||
workers:
|
||||
number: 1
|
||||
rounds: []
|
||||
EOF
|
||||
|
||||
cat > "$CALIPER_WORKSPACE/README.md" <<'EOF'
|
||||
# DBIS Caliper workspace
|
||||
|
||||
This workspace is pinned to the official Hyperledger Caliper CLI flow:
|
||||
|
||||
1. `npm install --only=prod @hyperledger/caliper-cli@0.6.0`
|
||||
2. `npx caliper bind --caliper-bind-sut besu:1.4`
|
||||
3. `npx caliper launch manager ...`
|
||||
|
||||
The shipped `chain138.example.json` is a starter network file only. Add funded account material and real benchmark configs before write-heavy runs.
|
||||
EOF
|
||||
|
||||
cat > /usr/local/bin/caliper-chain138-doctor.sh <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd /opt/caliper/workspace
|
||||
npx caliper --version
|
||||
npx caliper bind --caliper-bind-sut besu:1.4 --caliper-bind-cwd /opt/caliper/workspace >/tmp/caliper-bind.log
|
||||
echo "Bound Besu adapter into /opt/caliper/workspace"
|
||||
echo "Network template: /opt/caliper/workspace/networks/besu/chain138.example.json"
|
||||
echo "Bench template: /opt/caliper/workspace/benchmarks/read-only/config.yaml"
|
||||
EOF
|
||||
chmod +x /usr/local/bin/caliper-chain138-doctor.sh
|
||||
|
||||
chown -R "$CALIPER_USER:$CALIPER_GROUP" "$CALIPER_HOME"
|
||||
|
||||
log_info "Installing Hyperledger Caliper CLI ${CALIPER_VERSION}..."
|
||||
su -s /bin/bash - "$CALIPER_USER" -c "cd $CALIPER_WORKSPACE && npm install --only=prod @hyperledger/caliper-cli@${CALIPER_VERSION}"
|
||||
su -s /bin/bash - "$CALIPER_USER" -c "cd $CALIPER_WORKSPACE && npx caliper bind --caliper-bind-sut besu:1.4 --caliper-bind-cwd $CALIPER_WORKSPACE"
|
||||
|
||||
log_success "Caliper installation completed!"
|
||||
log_info "Next steps:"
|
||||
log_info "1. Review $CALIPER_WORKSPACE/networks/besu/chain138.example.json"
|
||||
log_info "2. Add funded account and benchmark artifacts before write tests"
|
||||
log_info "3. Run /usr/local/bin/caliper-chain138-doctor.sh for a quick workspace check"
|
||||
@@ -48,9 +48,6 @@ apt-get install -y -qq \
|
||||
|
||||
log_success "System packages updated"
|
||||
|
||||
# Install Docker Compose v2
|
||||
log_info "Installing Docker Compose..."
|
||||
apt-get install -y -qq docker-compose-plugin
|
||||
systemctl enable docker
|
||||
systemctl start docker
|
||||
|
||||
@@ -64,62 +61,76 @@ else
|
||||
usermod -aG docker "$FABRIC_USER"
|
||||
fi
|
||||
|
||||
# Install Fabric binaries
|
||||
log_info "Installing Hyperledger Fabric binaries..."
|
||||
# Install Docker Compose v2 when available
|
||||
log_info "Installing Docker Compose plugin when available..."
|
||||
apt-get install -y -qq docker-compose-plugin || true
|
||||
|
||||
# Install Fabric binaries, samples, and Docker images
|
||||
log_info "Installing Hyperledger Fabric binaries, samples, and images..."
|
||||
rm -rf /tmp/fabric-samples /tmp/bin /tmp/config
|
||||
cd /tmp
|
||||
curl -sSL https://raw.githubusercontent.com/hyperledger/fabric/main/scripts/install-fabric.sh | bash -s -- binary
|
||||
|
||||
# Move binaries to system location
|
||||
mkdir -p /usr/local/bin/fabric
|
||||
mv fabric-samples/bin/* /usr/local/bin/fabric/
|
||||
chmod +x /usr/local/bin/fabric/*
|
||||
ln -sf /usr/local/bin/fabric/* /usr/local/bin/
|
||||
|
||||
log_success "Fabric binaries installed"
|
||||
curl -sSL https://raw.githubusercontent.com/hyperledger/fabric/main/scripts/install-fabric.sh | bash -s -- binary samples docker
|
||||
|
||||
# Create directories
|
||||
log_info "Creating directories..."
|
||||
mkdir -p "$FABRIC_HOME" "$FABRIC_DATA"
|
||||
chown -R "$FABRIC_USER:$FABRIC_GROUP" "$FABRIC_HOME" "$FABRIC_DATA"
|
||||
mkdir -p "$FABRIC_HOME" "$FABRIC_DATA" "$FABRIC_HOME/config" "$FABRIC_HOME/network" "$FABRIC_HOME/scripts"
|
||||
|
||||
# Download Fabric samples (optional, for reference)
|
||||
log_info "Setting up Fabric environment..."
|
||||
cd "$FABRIC_HOME"
|
||||
# Stage Fabric samples in the canonical location
|
||||
if [[ -d /tmp/fabric-samples ]]; then
|
||||
rm -rf "$FABRIC_HOME/fabric-samples"
|
||||
mv /tmp/fabric-samples "$FABRIC_HOME/fabric-samples"
|
||||
fi
|
||||
|
||||
# Create configuration directory
|
||||
mkdir -p config network scripts
|
||||
# Move binaries to system location
|
||||
mkdir -p /usr/local/bin/fabric
|
||||
if [[ -d "$FABRIC_HOME/fabric-samples/bin" ]]; then
|
||||
cp -a "$FABRIC_HOME/fabric-samples/bin/." /usr/local/bin/fabric/
|
||||
fi
|
||||
chmod +x /usr/local/bin/fabric/* 2>/dev/null || true
|
||||
for fabric_bin in /usr/local/bin/fabric/*; do
|
||||
ln -sf "$fabric_bin" /usr/local/bin/"$(basename "$fabric_bin")"
|
||||
done
|
||||
|
||||
log_info "Creating basic network configuration..."
|
||||
cat > config/configtx.yaml.template <<'EOF'
|
||||
# Configtx template for Fabric network
|
||||
# This is a template - customize for your network
|
||||
Organizations:
|
||||
- &OrdererOrg
|
||||
Name: OrdererOrg
|
||||
ID: OrdererMSP
|
||||
MSPDir: crypto-config/ordererOrganizations/example.com/msp
|
||||
# Copy the official sample configuration so peer/orderer tooling works in-place
|
||||
if [[ -d "$FABRIC_HOME/fabric-samples/config" ]]; then
|
||||
cp -a "$FABRIC_HOME/fabric-samples/config/." "$FABRIC_HOME/config/"
|
||||
elif [[ -d /tmp/config ]]; then
|
||||
cp -a /tmp/config/. "$FABRIC_HOME/config/"
|
||||
fi
|
||||
ln -sfn "$FABRIC_HOME/config" "$FABRIC_HOME/fabric-samples/config"
|
||||
|
||||
- &Org1
|
||||
Name: Org1MSP
|
||||
ID: Org1MSP
|
||||
MSPDir: crypto-config/peerOrganizations/org1.example.com/msp
|
||||
# Nested LXC needs docker run calls to opt out of AppArmor confinement
|
||||
cat > /usr/local/bin/docker <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
Profiles:
|
||||
TestNetwork:
|
||||
Orderer:
|
||||
<<: *OrdererOrg
|
||||
Consortiums:
|
||||
SampleConsortium:
|
||||
Organizations:
|
||||
- <<: *Org1
|
||||
if [[ "${1:-}" == "run" ]]; then
|
||||
exec /usr/bin/docker run --security-opt apparmor=unconfined "${@:2}"
|
||||
fi
|
||||
|
||||
exec /usr/bin/docker "$@"
|
||||
EOF
|
||||
chmod +x /usr/local/bin/docker
|
||||
|
||||
# Provide a reproducible helper for the official sample network
|
||||
cat > /usr/local/bin/fabric-up-sample-network.sh <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
export PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
|
||||
export FABRIC_CFG_PATH=/opt/fabric/fabric-samples/config
|
||||
|
||||
cd /opt/fabric/fabric-samples/test-network
|
||||
./network.sh up createChannel
|
||||
EOF
|
||||
chmod +x /usr/local/bin/fabric-up-sample-network.sh
|
||||
|
||||
log_success "Fabric binaries and sample network assets installed"
|
||||
|
||||
chown -R "$FABRIC_USER:$FABRIC_GROUP" "$FABRIC_HOME"
|
||||
|
||||
log_success "Fabric installation completed!"
|
||||
log_info "Next steps:"
|
||||
log_info "1. Configure Fabric network in $FABRIC_HOME/config/"
|
||||
log_info "2. Generate crypto materials using cryptogen or CA"
|
||||
log_info "3. Create channel configuration"
|
||||
log_info "4. Start Fabric network using docker-compose"
|
||||
|
||||
log_info "1. Start the official sample network with /usr/local/bin/fabric-up-sample-network.sh"
|
||||
log_info "2. Verify peers and orderer with docker ps inside the container"
|
||||
log_info "3. Verify channel health with peer channel getinfo -c mychannel"
|
||||
|
||||
@@ -11,6 +11,9 @@ INDY_USER="${INDY_USER:-indy}"
|
||||
INDY_GROUP="${INDY_GROUP:-indy}"
|
||||
INDY_HOME="/opt/indy"
|
||||
INDY_DATA="/var/lib/indy"
|
||||
INDY_LOG_DIR="/var/log/indy"
|
||||
INDY_IMAGE="${INDY_IMAGE:-hyperledgerlabs/indy-node:latest}"
|
||||
INDY_NETWORK_NAME="${INDY_NETWORK_NAME:-sandbox}"
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
@@ -38,15 +41,7 @@ apt-get install -y -qq \
|
||||
ca-certificates \
|
||||
gnupg \
|
||||
lsb-release \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-dev \
|
||||
libssl-dev \
|
||||
libffi-dev \
|
||||
build-essential \
|
||||
pkg-config \
|
||||
libzmq5 \
|
||||
libzmq3-dev
|
||||
python3
|
||||
|
||||
log_success "System packages updated"
|
||||
|
||||
@@ -64,120 +59,162 @@ else
|
||||
usermod -aG docker "$INDY_USER"
|
||||
fi
|
||||
|
||||
# Install Python dependencies
|
||||
log_info "Installing Python dependencies..."
|
||||
pip3 install --break-system-packages -q \
|
||||
python3-indy \
|
||||
indy-plenum \
|
||||
indy-node \
|
||||
aiohttp
|
||||
# Pull the published Indy node image that carries the working runtime bits
|
||||
log_info "Pulling published Indy node image..."
|
||||
docker pull "$INDY_IMAGE"
|
||||
|
||||
log_success "Python dependencies installed"
|
||||
|
||||
# Create directories
|
||||
# Create directories and config
|
||||
log_info "Creating directories..."
|
||||
mkdir -p "$INDY_HOME" "$INDY_DATA" "$INDY_DATA/data" "$INDY_DATA/logs" "$INDY_DATA/keys"
|
||||
chown -R "$INDY_USER:$INDY_GROUP" "$INDY_HOME" "$INDY_DATA"
|
||||
mkdir -p \
|
||||
"$INDY_HOME" \
|
||||
"$INDY_DATA" \
|
||||
"$INDY_DATA/backup" \
|
||||
"$INDY_DATA/plugins" \
|
||||
"$INDY_LOG_DIR" \
|
||||
/etc/indy
|
||||
chown -R "$INDY_USER:$INDY_GROUP" "$INDY_HOME" "$INDY_DATA" "$INDY_LOG_DIR"
|
||||
|
||||
# Download Indy pool configuration
|
||||
log_info "Setting up Indy configuration..."
|
||||
cd "$INDY_HOME"
|
||||
cat > /etc/indy/indy_config.py <<EOF
|
||||
NETWORK_NAME = '${INDY_NETWORK_NAME}'
|
||||
enableStdOutLogging = False
|
||||
LEDGER_DIR = '/var/lib/indy'
|
||||
LOG_DIR = '/var/log/indy'
|
||||
KEYS_DIR = '/var/lib/indy'
|
||||
GENESIS_DIR = '/var/lib/indy'
|
||||
BACKUP_DIR = '/var/lib/indy/backup'
|
||||
PLUGINS_DIR = '/var/lib/indy/plugins'
|
||||
NODE_INFO_DIR = '/var/lib/indy'
|
||||
baseDir = '/var/lib/indy'
|
||||
logDir = '/var/log/indy'
|
||||
EOF
|
||||
|
||||
# Create docker-compose.yml for Indy nodes
|
||||
cat > docker-compose.yml <<'EOF'
|
||||
cat > "$INDY_HOME/docker-compose.yml" <<'EOF'
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
indy-node-1:
|
||||
image: indy-node:${INDY_VERSION:-1.18.1}
|
||||
image: hyperledgerlabs/indy-node:latest
|
||||
container_name: indy-node-1
|
||||
network_mode: host
|
||||
security_opt:
|
||||
- apparmor=unconfined
|
||||
environment:
|
||||
- NODE_NAME=Node1
|
||||
- NODE_IP=0.0.0.0
|
||||
- NODE_PORT=9701
|
||||
- CLIENT_IP=0.0.0.0
|
||||
- CLIENT_PORT=9702
|
||||
- NETWORK_NAME=${NETWORK_NAME:-sandbox}
|
||||
- INDY_NETWORK_NAME=sandbox
|
||||
- INDY_NODE_NAME=Node1
|
||||
- INDY_NODE_IP=0.0.0.0
|
||||
- INDY_NODE_PORT=9701
|
||||
- INDY_CLIENT_IP=0.0.0.0
|
||||
- INDY_CLIENT_PORT=9702
|
||||
volumes:
|
||||
- indy-data:/var/lib/indy
|
||||
- indy-logs:/var/log/indy
|
||||
ports:
|
||||
- "9701:9701"
|
||||
- "9702:9702"
|
||||
- /var/lib/indy/sandbox:/var/lib/indy/sandbox
|
||||
- /var/lib/indy/keys:/var/lib/indy/keys
|
||||
- /var/lib/indy/backup:/var/lib/indy/backup
|
||||
- /var/lib/indy/plugins:/var/lib/indy/plugins
|
||||
- /var/log/indy:/var/log/indy
|
||||
- /etc/indy/indy_config.py:/etc/indy/indy_config.py
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- indy-network
|
||||
|
||||
indy-node-2:
|
||||
image: indy-node:${INDY_VERSION:-1.18.1}
|
||||
image: hyperledgerlabs/indy-node:latest
|
||||
container_name: indy-node-2
|
||||
network_mode: host
|
||||
security_opt:
|
||||
- apparmor=unconfined
|
||||
environment:
|
||||
- NODE_NAME=Node2
|
||||
- NODE_IP=0.0.0.0
|
||||
- NODE_PORT=9703
|
||||
- CLIENT_IP=0.0.0.0
|
||||
- CLIENT_PORT=9704
|
||||
- NETWORK_NAME=${NETWORK_NAME:-sandbox}
|
||||
- INDY_NETWORK_NAME=sandbox
|
||||
- INDY_NODE_NAME=Node2
|
||||
- INDY_NODE_IP=0.0.0.0
|
||||
- INDY_NODE_PORT=9703
|
||||
- INDY_CLIENT_IP=0.0.0.0
|
||||
- INDY_CLIENT_PORT=9704
|
||||
volumes:
|
||||
- indy-data:/var/lib/indy
|
||||
- indy-logs:/var/log/indy
|
||||
ports:
|
||||
- "9703:9703"
|
||||
- "9704:9704"
|
||||
- /var/lib/indy/sandbox:/var/lib/indy/sandbox
|
||||
- /var/lib/indy/keys:/var/lib/indy/keys
|
||||
- /var/lib/indy/backup:/var/lib/indy/backup
|
||||
- /var/lib/indy/plugins:/var/lib/indy/plugins
|
||||
- /var/log/indy:/var/log/indy
|
||||
- /etc/indy/indy_config.py:/etc/indy/indy_config.py
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- indy-network
|
||||
|
||||
indy-node-3:
|
||||
image: indy-node:${INDY_VERSION:-1.18.1}
|
||||
image: hyperledgerlabs/indy-node:latest
|
||||
container_name: indy-node-3
|
||||
network_mode: host
|
||||
security_opt:
|
||||
- apparmor=unconfined
|
||||
environment:
|
||||
- NODE_NAME=Node3
|
||||
- NODE_IP=0.0.0.0
|
||||
- NODE_PORT=9705
|
||||
- CLIENT_IP=0.0.0.0
|
||||
- CLIENT_PORT=9706
|
||||
- NETWORK_NAME=${NETWORK_NAME:-sandbox}
|
||||
- INDY_NETWORK_NAME=sandbox
|
||||
- INDY_NODE_NAME=Node3
|
||||
- INDY_NODE_IP=0.0.0.0
|
||||
- INDY_NODE_PORT=9705
|
||||
- INDY_CLIENT_IP=0.0.0.0
|
||||
- INDY_CLIENT_PORT=9706
|
||||
volumes:
|
||||
- indy-data:/var/lib/indy
|
||||
- indy-logs:/var/log/indy
|
||||
ports:
|
||||
- "9705:9705"
|
||||
- "9706:9706"
|
||||
- /var/lib/indy/sandbox:/var/lib/indy/sandbox
|
||||
- /var/lib/indy/keys:/var/lib/indy/keys
|
||||
- /var/lib/indy/backup:/var/lib/indy/backup
|
||||
- /var/lib/indy/plugins:/var/lib/indy/plugins
|
||||
- /var/log/indy:/var/log/indy
|
||||
- /etc/indy/indy_config.py:/etc/indy/indy_config.py
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- indy-network
|
||||
|
||||
indy-node-4:
|
||||
image: indy-node:${INDY_VERSION:-1.18.1}
|
||||
image: hyperledgerlabs/indy-node:latest
|
||||
container_name: indy-node-4
|
||||
network_mode: host
|
||||
security_opt:
|
||||
- apparmor=unconfined
|
||||
environment:
|
||||
- NODE_NAME=Node4
|
||||
- NODE_IP=0.0.0.0
|
||||
- NODE_PORT=9707
|
||||
- CLIENT_IP=0.0.0.0
|
||||
- CLIENT_PORT=9708
|
||||
- NETWORK_NAME=${NETWORK_NAME:-sandbox}
|
||||
- INDY_NETWORK_NAME=sandbox
|
||||
- INDY_NODE_NAME=Node4
|
||||
- INDY_NODE_IP=0.0.0.0
|
||||
- INDY_NODE_PORT=9707
|
||||
- INDY_CLIENT_IP=0.0.0.0
|
||||
- INDY_CLIENT_PORT=9708
|
||||
volumes:
|
||||
- indy-data:/var/lib/indy
|
||||
- indy-logs:/var/log/indy
|
||||
ports:
|
||||
- "9707:9707"
|
||||
- "9708:9708"
|
||||
- /var/lib/indy/sandbox:/var/lib/indy/sandbox
|
||||
- /var/lib/indy/keys:/var/lib/indy/keys
|
||||
- /var/lib/indy/backup:/var/lib/indy/backup
|
||||
- /var/lib/indy/plugins:/var/lib/indy/plugins
|
||||
- /var/log/indy:/var/log/indy
|
||||
- /etc/indy/indy_config.py:/etc/indy/indy_config.py
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- indy-network
|
||||
|
||||
volumes:
|
||||
indy-data:
|
||||
indy-logs:
|
||||
|
||||
networks:
|
||||
indy-network:
|
||||
driver: bridge
|
||||
EOF
|
||||
|
||||
chown -R "$INDY_USER:$INDY_GROUP" "$INDY_HOME"
|
||||
cat > /usr/local/bin/indy-generate-pool.sh <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
export PATH=/usr/local/bin:/usr/bin:/bin
|
||||
|
||||
host_ip="${INDY_HOST_IP:-$(hostname -I | cut -d' ' -f1)}"
|
||||
ips="${host_ip},${host_ip},${host_ip},${host_ip}"
|
||||
|
||||
rm -rf /var/lib/indy/sandbox /var/lib/indy/keys
|
||||
mkdir -p /var/lib/indy /var/lib/indy/backup /var/lib/indy/plugins /var/log/indy
|
||||
|
||||
docker pull hyperledgerlabs/indy-node:latest >/dev/null
|
||||
docker run --rm \
|
||||
-v /var/lib/indy:/var/lib/indy \
|
||||
-v /var/log/indy:/var/log/indy \
|
||||
-v /etc/indy:/etc/indy \
|
||||
--entrypoint bash \
|
||||
hyperledgerlabs/indy-node:latest \
|
||||
-lc "generate_indy_pool_transactions --nodes=4 --clients=1 --nodeNum 1 2 3 4 --ips=${ips} --network=sandbox"
|
||||
|
||||
for node in Node1 Node2 Node3 Node4; do
|
||||
mkdir -p "/var/lib/indy/keys/sandbox/keys/${node}/public_keys" "/var/lib/indy/keys/sandbox/keys/${node}/verif_keys"
|
||||
cp -f "/var/lib/indy/sandbox/keys/${node}/public_keys/${node}.key" "/var/lib/indy/keys/sandbox/keys/${node}/public_keys/${node}.key.bootstrap"
|
||||
cp -f "/var/lib/indy/sandbox/keys/${node}/verif_keys/${node}.key" "/var/lib/indy/keys/sandbox/keys/${node}/verif_keys/${node}.key.bootstrap"
|
||||
done
|
||||
|
||||
chown -R indy:indy /var/lib/indy /var/log/indy
|
||||
echo "Generated Indy pool artifacts for ${host_ip}"
|
||||
EOF
|
||||
chmod +x /usr/local/bin/indy-generate-pool.sh
|
||||
|
||||
chown -R "$INDY_USER:$INDY_GROUP" "$INDY_HOME" "$INDY_DATA" "$INDY_LOG_DIR"
|
||||
|
||||
log_success "Indy configuration created"
|
||||
|
||||
@@ -210,9 +247,6 @@ log_success "Systemd service created"
|
||||
|
||||
log_success "Indy installation completed!"
|
||||
log_info "Next steps:"
|
||||
log_info "1. Edit $INDY_HOME/docker-compose.yml to configure network name and nodes"
|
||||
log_info "2. Generate genesis transactions for the pool"
|
||||
log_info "3. Configure node keys and certificates"
|
||||
log_info "4. Start service: systemctl start indy"
|
||||
log_info "5. Indy pool will run 4 nodes for consensus"
|
||||
|
||||
log_info "1. Run /usr/local/bin/indy-generate-pool.sh"
|
||||
log_info "2. Start service: systemctl start indy"
|
||||
log_info "3. Verify listeners: ss -ltnp | grep -E ':970[1-8]\\b'"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Deploy Hyperledger Services (Firefly, Cacti, Fabric, Indy) on Proxmox VE LXC containers
|
||||
# Deploy Hyperledger Services (Firefly, Cacti, Fabric, Indy, Aries, Caliper) on Proxmox VE LXC containers
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -19,12 +19,16 @@ DEPLOY_FIREFLY="${DEPLOY_FIREFLY:-true}"
|
||||
DEPLOY_CACTI="${DEPLOY_CACTI:-true}"
|
||||
DEPLOY_FABRIC="${DEPLOY_FABRIC:-false}"
|
||||
DEPLOY_INDY="${DEPLOY_INDY:-false}"
|
||||
DEPLOY_ARIES="${DEPLOY_ARIES:-false}"
|
||||
DEPLOY_CALIPER="${DEPLOY_CALIPER:-false}"
|
||||
|
||||
# VMID ranges
|
||||
VMID_FIREFLY_START="${VMID_FIREFLY_START:-150}"
|
||||
VMID_CACTI_START="${VMID_CACTI_START:-151}"
|
||||
VMID_FABRIC_START="${VMID_FABRIC_START:-152}"
|
||||
VMID_INDY_START="${VMID_INDY_START:-153}"
|
||||
VMID_FIREFLY_START="${VMID_FIREFLY_START:-6200}"
|
||||
VMID_CACTI_START="${VMID_CACTI_START:-5200}"
|
||||
VMID_FABRIC_START="${VMID_FABRIC_START:-6000}"
|
||||
VMID_INDY_START="${VMID_INDY_START:-6400}"
|
||||
VMID_ARIES_START="${VMID_ARIES_START:-6500}"
|
||||
VMID_CALIPER_START="${VMID_CALIPER_START:-6600}"
|
||||
|
||||
log_info "Starting Hyperledger services deployment..."
|
||||
|
||||
@@ -42,7 +46,7 @@ ensure_os_template "${CONTAINER_OS_TEMPLATE:-local:vztmpl/ubuntu-22.04-standard_
|
||||
|
||||
# Function to create and configure Hyperledger service
|
||||
create_hyperledger_service() {
|
||||
local service_type="$1" # firefly, cacti, fabric, indy
|
||||
local service_type="$1" # firefly, cacti, fabric, indy, aries, caliper
|
||||
local vmid="$2"
|
||||
local hostname="$3"
|
||||
local ip_address="$4"
|
||||
@@ -153,6 +157,46 @@ if [[ "$DEPLOY_FIREFLY" == "true" ]]; then
|
||||
log_success "Deployed Firefly service"
|
||||
fi
|
||||
|
||||
# Deploy Aries / AnonCreds
|
||||
if [[ "$DEPLOY_ARIES" == "true" ]]; then
|
||||
log_info "Deploying Aries / AnonCreds service..."
|
||||
vmid=$VMID_ARIES_START
|
||||
hostname="aries-1"
|
||||
ip_octet=64
|
||||
ip_address="${SERVICES_SUBNET:-10.3.1}.${ip_octet}"
|
||||
|
||||
aries_info=$(create_hyperledger_service \
|
||||
"aries" \
|
||||
"$vmid" \
|
||||
"$hostname" \
|
||||
"$ip_address" \
|
||||
"${ARIES_MEMORY:-4096}" \
|
||||
"${ARIES_CORES:-2}" \
|
||||
"${ARIES_DISK:-50}")
|
||||
|
||||
log_success "Deployed Aries / AnonCreds service"
|
||||
fi
|
||||
|
||||
# Deploy Caliper
|
||||
if [[ "$DEPLOY_CALIPER" == "true" ]]; then
|
||||
log_info "Deploying Caliper service..."
|
||||
vmid=$VMID_CALIPER_START
|
||||
hostname="caliper-1"
|
||||
ip_octet=65
|
||||
ip_address="${SERVICES_SUBNET:-10.3.1}.${ip_octet}"
|
||||
|
||||
caliper_info=$(create_hyperledger_service \
|
||||
"caliper" \
|
||||
"$vmid" \
|
||||
"$hostname" \
|
||||
"$ip_address" \
|
||||
"${CALIPER_MEMORY:-4096}" \
|
||||
"${CALIPER_CORES:-2}" \
|
||||
"${CALIPER_DISK:-50}")
|
||||
|
||||
log_success "Deployed Caliper service"
|
||||
fi
|
||||
|
||||
# Deploy Cacti
|
||||
if [[ "$DEPLOY_CACTI" == "true" ]]; then
|
||||
log_info "Deploying Cacti service..."
|
||||
@@ -245,7 +289,18 @@ EOF
|
||||
echo "INDY_${hostname//-/}_VMID=$vmid" >> "$INVENTORY_FILE"
|
||||
echo "INDY_${hostname//-/}_IP=$ip" >> "$INVENTORY_FILE"
|
||||
fi
|
||||
|
||||
if [[ -n "${aries_info:-}" ]]; then
|
||||
IFS=':' read -r vmid hostname ip <<< "$aries_info"
|
||||
echo "ARIES_${hostname//-/}_VMID=$vmid" >> "$INVENTORY_FILE"
|
||||
echo "ARIES_${hostname//-/}_IP=$ip" >> "$INVENTORY_FILE"
|
||||
fi
|
||||
|
||||
if [[ -n "${caliper_info:-}" ]]; then
|
||||
IFS=':' read -r vmid hostname ip <<< "$caliper_info"
|
||||
echo "CALIPER_${hostname//-/}_VMID=$vmid" >> "$INVENTORY_FILE"
|
||||
echo "CALIPER_${hostname//-/}_IP=$ip" >> "$INVENTORY_FILE"
|
||||
fi
|
||||
fi
|
||||
|
||||
log_success "Hyperledger services deployment completed!"
|
||||
|
||||
|
||||
@@ -38,7 +38,8 @@ node scripts/verify-on-chain.js lists/dbis-138.tokenlist.json
|
||||
token-lists/
|
||||
├── lists/
|
||||
│ └── dbis-138.tokenlist.json # Main token list
|
||||
├── logos/ # Token logos (future)
|
||||
├── logos/
|
||||
│ └── gru/ # Canonical GRU SVG logos for c* assets and Chain 138 list branding
|
||||
├── scripts/
|
||||
│ ├── validate-token-list.js # Schema & validation
|
||||
│ ├── checksum-addresses.js # EIP-55 checksum validation
|
||||
@@ -58,10 +59,19 @@ token-lists/
|
||||
|
||||
## Token List Contents
|
||||
|
||||
Current version: **1.2.0**
|
||||
Current version: **1.7.0**
|
||||
|
||||
Canonical currency and lifecycle state are also tracked in [`../config/gru-iso4217-currency-manifest.json`](../config/gru-iso4217-currency-manifest.json). The token list is the wallet/explorer-facing projection of that broader GRU asset inventory. Canonical GRU SVG logos now live under [`logos/gru/`](logos/gru) and are referenced through raw HTTPS URLs so wallets and explorers can use the same source artwork.
|
||||
|
||||
### Tokens
|
||||
|
||||
Current token count: **18**
|
||||
|
||||
Notable current state:
|
||||
- `cUSDT` and `cUSDC` each have both V1 and staged V2 entries on Chain 138.
|
||||
- Duplicate symbols for V1/V2 are intentional during coexistence and cutover.
|
||||
- Non-USD GRU currencies currently covered in the list are `EUR`, `GBP`, `AUD`, `JPY`, `CHF`, `CAD`, and `XAU`.
|
||||
|
||||
1. **ETH/USD Price Feed** (Oracle)
|
||||
- Address: `0x3304b747E565a97ec8AC220b0B6A1f6ffDB837e6`
|
||||
- Decimals: 8
|
||||
@@ -82,16 +92,30 @@ Current version: **1.2.0**
|
||||
- Decimals: 18
|
||||
- Category: DeFi, Oracle, CCIP
|
||||
|
||||
5. **cUSDT** (Compliant Tether USD)
|
||||
5. **cUSDT** (Compliant Tether USD, V1)
|
||||
- Address: `0x93E66202A11B1772E55407B32B44e5Cd8eda7f22`
|
||||
- Decimals: 6
|
||||
- Category: Stablecoin, DeFi, Compliant
|
||||
|
||||
6. **cUSDC** (Compliant USD Coin)
|
||||
6. **cUSDT** (Compliant Tether USD V2, staged)
|
||||
- Address: `0x8d342d321DdEe97D0c5011DAF8ca0B59DA617D29`
|
||||
- Decimals: 6
|
||||
- Category: Stablecoin, DeFi, Compliant, V2, x402-ready
|
||||
|
||||
7. **cUSDC** (Compliant USD Coin, V1)
|
||||
- Address: `0xf22258f57794CC8E06237084b353Ab30fFfa640b`
|
||||
- Decimals: 6
|
||||
- Category: Stablecoin, DeFi, Compliant
|
||||
|
||||
8. **cUSDC** (Compliant USD Coin V2, staged)
|
||||
- Address: `0x1ac3F4942a71E86A9682D91837E1E71b7BACdF99`
|
||||
- Decimals: 6
|
||||
- Category: Stablecoin, DeFi, Compliant, V2, x402-ready
|
||||
|
||||
9. **Additional GRU currencies**
|
||||
- `cEURC`, `cEURT`, `cGBPC`, `cGBPT`, `cAUDC`, `cJPYC`, `cCHFC`, `cCADC`, `cXAUC`, `cXAUT`
|
||||
- See `lists/dbis-138.tokenlist.json` for the full current set.
|
||||
|
||||
---
|
||||
|
||||
## Validation
|
||||
@@ -104,6 +128,7 @@ All token lists are validated against:
|
||||
- Duplicate detection (addresses and symbols)
|
||||
- Logo URL accessibility
|
||||
- On-chain contract verification
|
||||
- Expected duplicate-symbol warnings during V1/V2 coexistence
|
||||
|
||||
### Running Validations
|
||||
|
||||
@@ -351,4 +376,3 @@ The chain configuration includes:
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-12-22
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -4,16 +4,57 @@
|
||||
* Validates that all logoURI URLs are accessible and return image content
|
||||
*/
|
||||
|
||||
import { readFileSync } from 'fs';
|
||||
import { readFileSync, existsSync, statSync } from 'fs';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname, resolve } from 'path';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const PROJECT_ROOT = resolve(__dirname, '../..');
|
||||
|
||||
const MAX_LOGO_SIZE = 500 * 1024; // 500KB
|
||||
const IMAGE_MIME_TYPES = ['image/png', 'image/jpeg', 'image/jpg', 'image/svg+xml', 'image/webp', 'image/gif'];
|
||||
|
||||
function inferContentTypeFromPath(filePath) {
|
||||
if (filePath.endsWith('.svg')) return 'image/svg+xml';
|
||||
if (filePath.endsWith('.png')) return 'image/png';
|
||||
if (filePath.endsWith('.jpg') || filePath.endsWith('.jpeg')) return 'image/jpeg';
|
||||
if (filePath.endsWith('.webp')) return 'image/webp';
|
||||
if (filePath.endsWith('.gif')) return 'image/gif';
|
||||
return null;
|
||||
}
|
||||
|
||||
function resolveLocalRepoAsset(logoURI) {
|
||||
try {
|
||||
const url = new URL(logoURI);
|
||||
if (url.protocol !== 'https:') return null;
|
||||
|
||||
if (url.hostname === 'raw.githubusercontent.com') {
|
||||
const segments = url.pathname.split('/').filter(Boolean);
|
||||
if (segments.length < 4) return null;
|
||||
const relativePath = segments.slice(3).join('/');
|
||||
const candidate = resolve(PROJECT_ROOT, relativePath);
|
||||
return existsSync(candidate) ? candidate : null;
|
||||
}
|
||||
|
||||
if (url.hostname === 'gitea.d-bis.org') {
|
||||
const marker = '/raw/branch/';
|
||||
const markerIndex = url.pathname.indexOf(marker);
|
||||
if (markerIndex === -1) return null;
|
||||
const afterMarker = url.pathname.slice(markerIndex + marker.length);
|
||||
const pathSegments = afterMarker.split('/').filter(Boolean);
|
||||
if (pathSegments.length < 2) return null;
|
||||
const relativePath = pathSegments.slice(1).join('/');
|
||||
const candidate = resolve(PROJECT_ROOT, relativePath);
|
||||
return existsSync(candidate) ? candidate : null;
|
||||
}
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async function validateLogo(logoURI, tokenInfo) {
|
||||
const issues = [];
|
||||
|
||||
@@ -24,6 +65,24 @@ async function validateLogo(logoURI, tokenInfo) {
|
||||
|
||||
// For HTTPS URLs, validate accessibility
|
||||
if (logoURI.startsWith('https://')) {
|
||||
const localAsset = resolveLocalRepoAsset(logoURI);
|
||||
if (localAsset) {
|
||||
try {
|
||||
const size = statSync(localAsset).size;
|
||||
const contentType = inferContentTypeFromPath(localAsset);
|
||||
if (!contentType || !IMAGE_MIME_TYPES.includes(contentType)) {
|
||||
issues.push(`Local repo asset has unsupported extension: ${localAsset}`);
|
||||
}
|
||||
if (size > MAX_LOGO_SIZE) {
|
||||
issues.push(`Local repo asset too large: ${(size / 1024).toFixed(2)}KB (max ${MAX_LOGO_SIZE / 1024}KB)`);
|
||||
}
|
||||
return issues;
|
||||
} catch (error) {
|
||||
issues.push(`Failed to stat local repo asset: ${error.message}`);
|
||||
return issues;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(logoURI, { method: 'HEAD' });
|
||||
|
||||
@@ -132,4 +191,3 @@ validateLogos(filePath).then(exitCode => {
|
||||
console.error('Unexpected error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ async function getSchema() {
|
||||
return tokenLists.schema;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('⚠️ @uniswap/token-lists package not available, fetching schema from URL...\n');
|
||||
console.log('ℹ️ Using fallback token list schema source\n');
|
||||
}
|
||||
|
||||
// Fallback: fetch schema from Uniswap
|
||||
@@ -69,10 +69,38 @@ function isChecksummed(address) {
|
||||
function enhancedValidation(tokenList) {
|
||||
const errors = [];
|
||||
const warnings = [];
|
||||
const infos = [];
|
||||
const seenAddresses = new Set();
|
||||
const seenSymbols = new Map(); // chainId -> Set of symbols
|
||||
const seenSymbols = new Map(); // chainId -> Map<symbol, token[]>
|
||||
let detectedChainId = null;
|
||||
|
||||
function isAllowedGruVersionDuplicate(existingToken, nextToken) {
|
||||
if (!existingToken?.extensions || !nextToken?.extensions) return false;
|
||||
|
||||
const existingVersion = existingToken.extensions.gruVersion;
|
||||
const nextVersion = nextToken.extensions.gruVersion;
|
||||
const existingCurrencyCode = existingToken.extensions.currencyCode;
|
||||
const nextCurrencyCode = nextToken.extensions.currencyCode;
|
||||
|
||||
if (typeof existingVersion !== 'string' || typeof nextVersion !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (existingVersion === nextVersion) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof existingCurrencyCode !== 'string' || typeof nextCurrencyCode !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (existingCurrencyCode !== nextCurrencyCode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Required fields
|
||||
if (!tokenList.name || typeof tokenList.name !== 'string') {
|
||||
errors.push('Missing or invalid "name" field');
|
||||
@@ -154,13 +182,27 @@ function enhancedValidation(tokenList) {
|
||||
// Symbol uniqueness per chainId
|
||||
const chainId = token.chainId || 0;
|
||||
if (!seenSymbols.has(chainId)) {
|
||||
seenSymbols.set(chainId, new Set());
|
||||
seenSymbols.set(chainId, new Map());
|
||||
}
|
||||
const symbolSet = seenSymbols.get(chainId);
|
||||
if (symbolSet.has(token.symbol.toUpperCase())) {
|
||||
warnings.push(`${prefix}: Duplicate symbol "${token.symbol}" on chainId ${chainId}`);
|
||||
const symbolMap = seenSymbols.get(chainId);
|
||||
const symbolKey = token.symbol.toUpperCase();
|
||||
const existingTokens = symbolMap.get(symbolKey) || [];
|
||||
if (existingTokens.length > 0) {
|
||||
const duplicateAllowed = existingTokens.every(existingToken =>
|
||||
isAllowedGruVersionDuplicate(existingToken, token)
|
||||
);
|
||||
|
||||
if (duplicateAllowed) {
|
||||
infos.push(
|
||||
`${prefix}: Allowed staged GRU duplicate symbol "${token.symbol}" on chainId ${chainId} ` +
|
||||
`(${existingTokens.map(existingToken => existingToken.extensions?.gruVersion).join(', ')} -> ${token.extensions?.gruVersion})`
|
||||
);
|
||||
} else {
|
||||
warnings.push(`${prefix}: Duplicate symbol "${token.symbol}" on chainId ${chainId}`);
|
||||
}
|
||||
}
|
||||
symbolSet.add(token.symbol.toUpperCase());
|
||||
existingTokens.push(token);
|
||||
symbolMap.set(symbolKey, existingTokens);
|
||||
}
|
||||
|
||||
if (typeof token.decimals !== 'number' || token.decimals < 0 || token.decimals > 255) {
|
||||
@@ -181,7 +223,7 @@ function enhancedValidation(tokenList) {
|
||||
}
|
||||
});
|
||||
|
||||
return { errors, warnings, valid: errors.length === 0 };
|
||||
return { errors, warnings, infos, valid: errors.length === 0 };
|
||||
}
|
||||
|
||||
async function validateTokenList(filePath) {
|
||||
@@ -226,6 +268,7 @@ async function validateTokenList(filePath) {
|
||||
validationResult = {
|
||||
errors: [...schemaErrors, ...enhanced.errors],
|
||||
warnings: enhanced.warnings,
|
||||
infos: enhanced.infos,
|
||||
valid: false
|
||||
};
|
||||
}
|
||||
@@ -275,6 +318,14 @@ async function validateTokenList(filePath) {
|
||||
});
|
||||
console.log('');
|
||||
}
|
||||
|
||||
if (validationResult.infos.length > 0) {
|
||||
console.log('ℹ️ Notes:');
|
||||
validationResult.infos.forEach(info => {
|
||||
console.log(` - ${info}`);
|
||||
});
|
||||
console.log('');
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
} else {
|
||||
@@ -295,6 +346,14 @@ async function validateTokenList(filePath) {
|
||||
});
|
||||
console.log('');
|
||||
}
|
||||
|
||||
if (validationResult.infos.length > 0) {
|
||||
console.log('Notes:');
|
||||
validationResult.infos.forEach(info => {
|
||||
console.log(` ℹ️ ${info}`);
|
||||
});
|
||||
console.log('');
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -318,4 +377,3 @@ validateTokenList(filePath).catch(error => {
|
||||
console.error('Unexpected error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
|
||||
1
transaction-composer
Submodule
1
transaction-composer
Submodule
Submodule transaction-composer added at 5b8e3fc5f9
Reference in New Issue
Block a user