Skip to content

Commit ca5a18b

Browse files
author
Neo
committed
feat: full Web3 capability surface + modular communications
Expands the platform from 30 blockchain services to 221 discrete Web3 capabilities, data-driven through a single catalog file. Adds 9 pluggable notification channels with a modular, re-runnable setup wizard. The paymaster pattern (platform pays gas) is preserved bit-for-bit. CAPABILITY FRAMEWORK runtime/capabilities/ __init__.py — exposes CapabilityRegistry + catalog catalog.py — single source of truth: 221 capability entries across 21 categories (contracts, DeFi, DeFi Advanced, NFT, NFT Finance, Identity, Governance, Social, Creator, Payments, Bridging, Staking, Privacy, Oracles, Storage, Compute, Real-world, Markets, Security, Gaming, Infrastructure). Every entry carries service/method/action mapping, params schema, paymaster flag, min_tier, protocol. Exposes install_action_map() which merges catalog entries into the existing dispatcher dicts without overwriting anything (refuses on conflict). registry.py — CapabilityRegistry facade: list_capabilities, list_by_category, describe(id), async invoke(id). runtime/blockchain/services/service_dispatcher.py Install hook merges catalog into ACTION_MAP, _STATE_MODIFYING_ACTIONS, ACTION_TO_FEED_EVENT at module import. Trinity's platform_action tool now exposes all 221 capabilities automatically. NEW SERVICES (14 stubs covering missing Web3 surface) restaking/ EigenLayer, Symbiotic, Karak, Lido, Rocket Pool mpc/ Threshold sigs, social recovery, session keys tba/ ERC-6551 token-bound accounts auctions/ Dutch/English/sealed-bid + orderbook DEX nft_lending/ BendDAO, NFTfi, Arcade, breeding compute/ Akash, Gensyn, Render, DePIN storage/ Filecoin, Ceramic, OrbitDB ccip/ CCIP, Hyperlane, Wormhole, Axelar, Stargate creator_platforms/ Sound.xyz, Mirror, Paragraph social_protocols/ Lens, Farcaster, Push Protocol, social/creator tokens advanced_governance/ veToken, quadratic, RetroPGF, bribes oracles_plus/ Pyth, RedStone, API3, Chainlink Keepers kyc/ Sumsub, Persona, self-sovereign KYC credentials payment_channels/ Raiden-style state channels Each service follows the duck-typed pattern, returns not_deployed_response() until real integrations land, and lazy-creates a GasSponsor instance so transactions route through the existing paymaster. GATEWAY CAPABILITY ENDPOINTS GET /api/v1/capabilities list + optional filters GET /api/v1/capabilities/categories counts per category GET /api/v1/capabilities/{id} descriptor POST /api/v1/capabilities/{id}/invoke generic invoker NOTIFICATIONS (9 pluggable channels) runtime/notifications/ base.py Channel ABC (send, available, enabled) dispatcher.py NotificationDispatcher: fan-out, legacy config migration, test_channel() helper telegram.py Bot messaging with Markdown discord.py Webhook with color-coded embeds slack.py Incoming webhook email_smtp.py SMTP with STARTTLS/SSL sms_twilio.py Twilio REST whatsapp_twilio.py Twilio WhatsApp web_chat.py Live SSE feed into built-in web UI ios_push.py APNs for MTRX iOS webhook.py Generic JSON POST MODULAR SETUP WIZARD setup/ Per-channel re-runnable configurators _shared.py Shared helpers (ask, save, update_env, test) telegram.py Auto-discovers chat_id by waiting for first msg discord.py, slack.py, email.py, sms.py, whatsapp.py, web_chat.py, ios_push.py, webhook.py setup_communications.py Interactive menu + CLI: `python setup_communications.py` `python setup_communications.py telegram` `python setup_communications.py --list` setup_telegram.py Backwards-compat shim setup.py configure_communications() now delegates to setup/*.py per channel, supports all 9 CONFIG + VALIDATION openmatrix.config.json.example Full notifications block with 9 channels + 14 new service stubs. Removed legacy referrals/ metered_api blocks (moved to MTRX iOS). runtime/config/validation.py Added secret fields for all notification channels. MTRX EXTENSIONS REGISTRY extensions/registry.json Regenerated from catalog (v2.0.0). 20 components, 221 capabilities exposed to the iOS app. NET STATS Capabilities: 30 services → 221 capabilities Categories: 1 flat list → 21 categories Services: 30 → 44 (14 new stubs) ACTION_MAP: 150 → 280 entries (0 conflicts) Channels: 4 → 9 (+ SMS, WhatsApp, Slack, Web chat, iOS push) Setup wizard: monolithic → modular + re-runnable Files compile: all 40 modified/new files pass py_compile Paymaster: unchanged — zero edits to gas_sponsor.py
1 parent 1634860 commit ca5a18b

63 files changed

Lines changed: 3910 additions & 1045 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

extensions/registry.json

Lines changed: 582 additions & 733 deletions
Large diffs are not rendered by default.

gateway/service_routes.py

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,12 +368,18 @@ def register_routes(self, app: web.Application) -> None:
368368
app.router.add_post("/api/v1/privacy/transfer", self._handle_private_transfer)
369369
app.router.add_post("/api/v1/privacy/stealth-address", self._handle_stealth_address)
370370

371+
# ── Capability Registry (data-driven Web3 capability surface) ──
372+
app.router.add_get("/api/v1/capabilities", self._handle_capabilities_list)
373+
app.router.add_get("/api/v1/capabilities/categories", self._handle_capabilities_categories)
374+
app.router.add_get("/api/v1/capabilities/{capability_id}", self._handle_capability_detail)
375+
app.router.add_post("/api/v1/capabilities/{capability_id}/invoke", self._handle_capability_invoke)
376+
371377
# Batch dispatch and live event stream (used by MTRXPackager)
372378
app.router.add_post("/api/v1/batch", self._handle_batch)
373379
app.router.add_get("/api/v1/events/stream", self._handle_event_stream)
374380

375381
self._build_batch_route_map()
376-
logger.info("ServiceRoutes: registered %d endpoints", 118)
382+
logger.info("ServiceRoutes: registered %d endpoints", 122)
377383

378384
# ------------------------------------------------------------------
379385
# Batch route map — mirrors every non-batch route above so we can
@@ -1870,6 +1876,62 @@ async def _handle_stealth_address(self, request: web.Request) -> web.Response:
18701876
)
18711877
return self._ok(result)
18721878

1879+
# ------------------------------------------------------------------
1880+
# Capability Registry (data-driven Web3 capability surface)
1881+
# ------------------------------------------------------------------
1882+
1883+
def _capability_registry(self):
1884+
"""Lazy-load the CapabilityRegistry with the gateway's config."""
1885+
reg = getattr(self, "_cap_registry_cache", None)
1886+
if reg is None:
1887+
from runtime.capabilities import CapabilityRegistry
1888+
reg = CapabilityRegistry(self._config)
1889+
self._cap_registry_cache = reg
1890+
return reg
1891+
1892+
async def _handle_capabilities_list(self, request: web.Request) -> web.Response:
1893+
"""``GET /api/v1/capabilities`` — enumerate all capabilities."""
1894+
reg = self._capability_registry()
1895+
category = request.query.get("category")
1896+
min_tier = request.query.get("min_tier")
1897+
available_only = request.query.get("available", "").lower() in {"1", "true", "yes"}
1898+
caps = reg.list_capabilities(
1899+
category=category,
1900+
available_only=available_only,
1901+
min_tier=min_tier,
1902+
)
1903+
return self._ok({"capabilities": caps, "count": len(caps)})
1904+
1905+
async def _handle_capabilities_categories(self, request: web.Request) -> web.Response:
1906+
"""``GET /api/v1/capabilities/categories`` — list categories + counts."""
1907+
reg = self._capability_registry()
1908+
return self._ok({"categories": reg.list_categories()})
1909+
1910+
async def _handle_capability_detail(self, request: web.Request) -> web.Response:
1911+
"""``GET /api/v1/capabilities/{id}`` — descriptor for one capability."""
1912+
capability_id = request.match_info["capability_id"]
1913+
reg = self._capability_registry()
1914+
cap = reg.describe(capability_id)
1915+
if cap is None:
1916+
return web.json_response(
1917+
{"ok": False, "error": "unknown_capability", "capability_id": capability_id},
1918+
status=404,
1919+
)
1920+
return self._ok({"capability": cap})
1921+
1922+
async def _handle_capability_invoke(self, request: web.Request) -> web.Response:
1923+
"""``POST /api/v1/capabilities/{id}/invoke`` — execute a capability."""
1924+
capability_id = request.match_info["capability_id"]
1925+
try:
1926+
body = await request.json()
1927+
except Exception:
1928+
body = {}
1929+
params = body.get("params", {}) if isinstance(body, dict) else {}
1930+
reg = self._capability_registry()
1931+
result = await reg.invoke(capability_id, params)
1932+
status = 200 if result.get("status") == "ok" else 400
1933+
return web.json_response(result, status=status)
1934+
18731935
# ------------------------------------------------------------------
18741936
# Batch dispatch
18751937
# ------------------------------------------------------------------

openmatrix.config.json.example

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,60 @@
9191
},
9292
"notifications": {
9393
"telegram": {
94+
"enabled": false,
9495
"bot_token": "YOUR_TELEGRAM_BOT_TOKEN",
96+
"chat_id": "YOUR_TELEGRAM_CHAT_ID",
9597
"owner_id": "YOUR_TELEGRAM_USER_ID"
98+
},
99+
"discord": {
100+
"enabled": false,
101+
"webhook_url": "YOUR_DISCORD_WEBHOOK_URL",
102+
"username": "0pnMatrx"
103+
},
104+
"slack": {
105+
"enabled": false,
106+
"webhook_url": "YOUR_SLACK_WEBHOOK_URL"
107+
},
108+
"email": {
109+
"enabled": false,
110+
"smtp_host": "smtp.gmail.com",
111+
"smtp_port": 587,
112+
"smtp_user": "YOUR_SMTP_USER",
113+
"smtp_pass": "YOUR_SMTP_APP_PASSWORD",
114+
"from": "YOUR_SMTP_USER",
115+
"to": "YOUR_NOTIFICATION_EMAIL",
116+
"use_ssl": false
117+
},
118+
"sms": {
119+
"enabled": false,
120+
"account_sid": "YOUR_TWILIO_ACCOUNT_SID",
121+
"auth_token": "YOUR_TWILIO_AUTH_TOKEN",
122+
"from_number": "+15551234567",
123+
"to_number": "+15559876543"
124+
},
125+
"whatsapp": {
126+
"enabled": false,
127+
"account_sid": "YOUR_TWILIO_ACCOUNT_SID",
128+
"auth_token": "YOUR_TWILIO_AUTH_TOKEN",
129+
"from_number": "+14155238886",
130+
"to_number": "+15551234567"
131+
},
132+
"web_chat": {
133+
"enabled": true
134+
},
135+
"ios_push": {
136+
"enabled": false,
137+
"auth_key_p8": "",
138+
"key_id": "YOUR_APNS_KEY_ID",
139+
"team_id": "YOUR_APPLE_TEAM_ID",
140+
"bundle_id": "com.opnmatrx.mtrx",
141+
"sandbox": false,
142+
"device_tokens": []
143+
},
144+
"webhook": {
145+
"enabled": false,
146+
"url": "YOUR_WEBHOOK_URL",
147+
"bearer_token": ""
96148
}
97149
},
98150
"services": {
@@ -284,15 +336,21 @@
284336
"enabled": true,
285337
"contract_address": "YOUR_X402_PAYMENTS_CONTRACT_ADDRESS",
286338
"supported_tokens": ["USDC", "ETH"]
287-
}
288-
},
289-
"referrals": {
290-
"enabled": true,
291-
"pro_referral_months": 1,
292-
"enterprise_referral_months": 2
293-
},
294-
"metered_api": {
295-
"enabled": true
339+
},
340+
"restaking": {"enabled": false},
341+
"mpc": {"enabled": false},
342+
"tba": {"enabled": false},
343+
"auctions": {"enabled": false},
344+
"nft_lending": {"enabled": false},
345+
"compute": {"enabled": false},
346+
"storage": {"enabled": false},
347+
"ccip": {"enabled": false},
348+
"creator_platforms": {"enabled": false},
349+
"social_protocols": {"enabled": false},
350+
"advanced_governance": {"enabled": false},
351+
"oracles_plus": {"enabled": false},
352+
"kyc": {"enabled": false},
353+
"payment_channels": {"enabled": false}
296354
},
297355
"protocol_referrals": {
298356
"enabled": true,
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""veToken locks, quadratic voting, RetroPGF, gauge bribes, voting delegation."""
2+
from runtime.blockchain.services.advanced_governance.service import AdvancedGovernanceService
3+
4+
__all__ = ["AdvancedGovernanceService"]
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"""
2+
veToken locks, quadratic voting, RetroPGF, gauge bribes, voting delegation.
3+
4+
This service is a stub until real protocol integrations are wired in.
5+
Every method returns the canonical not_deployed_response until the
6+
backend contracts / adapter SDKs are configured. Once the platform
7+
operator populates the relevant config keys, individual methods can
8+
swap their bodies for the real implementation without changing the
9+
service interface.
10+
11+
All state-modifying methods MUST route on-chain transactions through
12+
``runtime.blockchain.gas_sponsor.GasSponsor`` so the platform pays gas.
13+
"""
14+
15+
from __future__ import annotations
16+
17+
import logging
18+
from typing import Any
19+
20+
from runtime.blockchain.web3_manager import Web3Manager, not_deployed_response
21+
22+
logger = logging.getLogger(__name__)
23+
24+
25+
class AdvancedGovernanceService:
26+
"""veToken locks, quadratic voting, RetroPGF, gauge bribes, voting delegation."""
27+
28+
service_name = "advanced_governance"
29+
30+
def __init__(self, config: dict) -> None:
31+
self._config = config
32+
self._web3 = Web3Manager.get_shared(config)
33+
self._gas_sponsor = None # lazy — only instantiated when needed
34+
35+
def _sponsor(self):
36+
if self._gas_sponsor is None:
37+
from runtime.blockchain.gas_sponsor import GasSponsor
38+
self._gas_sponsor = GasSponsor(self._config)
39+
return self._gas_sponsor
40+
41+
async def vote_escrow(self, **params: Any) -> dict:
42+
"""Stub for vote_escrow. Returns not_deployed until the adapter is wired."""
43+
return not_deployed_response(
44+
self.service_name,
45+
extra={"method": "vote_escrow", "params": params},
46+
)
47+
48+
async def quadratic_vote(self, **params: Any) -> dict:
49+
"""Stub for quadratic_vote. Returns not_deployed until the adapter is wired."""
50+
return not_deployed_response(
51+
self.service_name,
52+
extra={"method": "quadratic_vote", "params": params},
53+
)
54+
55+
async def submit_retropgf(self, **params: Any) -> dict:
56+
"""Stub for submit_retropgf. Returns not_deployed until the adapter is wired."""
57+
return not_deployed_response(
58+
self.service_name,
59+
extra={"method": "submit_retropgf", "params": params},
60+
)
61+
62+
async def place_bribe(self, **params: Any) -> dict:
63+
"""Stub for place_bribe. Returns not_deployed until the adapter is wired."""
64+
return not_deployed_response(
65+
self.service_name,
66+
extra={"method": "place_bribe", "params": params},
67+
)
68+
69+
async def delegate_voting(self, **params: Any) -> dict:
70+
"""Stub for delegate_voting. Returns not_deployed until the adapter is wired."""
71+
return not_deployed_response(
72+
self.service_name,
73+
extra={"method": "delegate_voting", "params": params},
74+
)
75+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""Dutch, English, sealed-bid auctions and orderbook DEX primitives."""
2+
from runtime.blockchain.services.auctions.service import AuctionService
3+
4+
__all__ = ["AuctionService"]
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"""
2+
Dutch, English, sealed-bid auctions and orderbook DEX primitives.
3+
4+
This service is a stub until real protocol integrations are wired in.
5+
Every method returns the canonical not_deployed_response until the
6+
backend contracts / adapter SDKs are configured. Once the platform
7+
operator populates the relevant config keys, individual methods can
8+
swap their bodies for the real implementation without changing the
9+
service interface.
10+
11+
All state-modifying methods MUST route on-chain transactions through
12+
``runtime.blockchain.gas_sponsor.GasSponsor`` so the platform pays gas.
13+
"""
14+
15+
from __future__ import annotations
16+
17+
import logging
18+
from typing import Any
19+
20+
from runtime.blockchain.web3_manager import Web3Manager, not_deployed_response
21+
22+
logger = logging.getLogger(__name__)
23+
24+
25+
class AuctionService:
26+
"""Dutch, English, sealed-bid auctions and orderbook DEX primitives."""
27+
28+
service_name = "auctions"
29+
30+
def __init__(self, config: dict) -> None:
31+
self._config = config
32+
self._web3 = Web3Manager.get_shared(config)
33+
self._gas_sponsor = None # lazy — only instantiated when needed
34+
35+
def _sponsor(self):
36+
if self._gas_sponsor is None:
37+
from runtime.blockchain.gas_sponsor import GasSponsor
38+
self._gas_sponsor = GasSponsor(self._config)
39+
return self._gas_sponsor
40+
41+
async def create_auction(self, **params: Any) -> dict:
42+
"""Stub for create_auction. Returns not_deployed until the adapter is wired."""
43+
return not_deployed_response(
44+
self.service_name,
45+
extra={"method": "create_auction", "params": params},
46+
)
47+
48+
async def place_bid(self, **params: Any) -> dict:
49+
"""Stub for place_bid. Returns not_deployed until the adapter is wired."""
50+
return not_deployed_response(
51+
self.service_name,
52+
extra={"method": "place_bid", "params": params},
53+
)
54+
55+
async def settle_auction(self, **params: Any) -> dict:
56+
"""Stub for settle_auction. Returns not_deployed until the adapter is wired."""
57+
return not_deployed_response(
58+
self.service_name,
59+
extra={"method": "settle_auction", "params": params},
60+
)
61+
62+
async def place_limit_order(self, **params: Any) -> dict:
63+
"""Stub for place_limit_order. Returns not_deployed until the adapter is wired."""
64+
return not_deployed_response(
65+
self.service_name,
66+
extra={"method": "place_limit_order", "params": params},
67+
)
68+
69+
async def cancel_limit_order(self, **params: Any) -> dict:
70+
"""Stub for cancel_limit_order. Returns not_deployed until the adapter is wired."""
71+
return not_deployed_response(
72+
self.service_name,
73+
extra={"method": "cancel_limit_order", "params": params},
74+
)
75+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""Cross-chain messaging and bridging via CCIP, Hyperlane, Wormhole, Axelar, Stargate."""
2+
from runtime.blockchain.services.ccip.service import CrossChainMessagingService
3+
4+
__all__ = ["CrossChainMessagingService"]

0 commit comments

Comments
 (0)