From 173c161fedd9602a68e851e3b90d253a8a96283b Mon Sep 17 00:00:00 2001 From: 0xmatchmaker Date: Wed, 4 Mar 2026 20:52:37 +0800 Subject: [PATCH 1/6] Create README.md --- trustless-vault/README.md | 124 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 trustless-vault/README.md diff --git a/trustless-vault/README.md b/trustless-vault/README.md new file mode 100644 index 0000000..b48704b --- /dev/null +++ b/trustless-vault/README.md @@ -0,0 +1,124 @@ +# Trustless Vault — TEE-Secured Fund Management + +**The problem TEE actually solves:** letting people trust a pool of funds without trusting any person. + +## Why This Exists + +In DeFi, there are two ways to manage other people's money: + +1. **Custodial**: "Trust me." → Requires faith. People get rugged. +2. **Smart contracts**: Trustless, but limited to on-chain logic. Can't do complex strategies, can't access off-chain data. + +There's a gap: **complex trading strategies that manage shared funds**. Until now, these required trusting a fund manager. + +**TEE fills this gap.** The strategy runs in hardware-isolated memory. The private key never leaves the chip. Anyone can verify the exact code running via attestation. No trust required. + +## How It Works + +``` +┌─────────────────────────────────────────────────┐ +│ Phala Cloud CVM (Intel TDX) │ +│ │ +│ ┌───────────────────────────────────────────┐ │ +│ │ Trustless Vault │ │ +│ │ │ │ +│ │ Rules (hardcoded, attestable): │ │ +│ │ • Max 2% loss per trade │ │ +│ │ • Max 20% in one position │ │ +│ │ • Only trade BTC, ETH, SOL │ │ +│ │ • 5% stop loss │ │ +│ │ • 1 hour cooldown after loss │ │ +│ │ │ │ +│ │ Wallet key: derived by TEE hardware │ │ +│ │ (deterministic, unexportable) │ │ +│ └───────────────┬───────────────────────────┘ │ +│ │ │ +│ ┌───────────────▼───────────────────────────┐ │ +│ │ /var/run/dstack.sock │ │ +│ │ Hardware key derivation + attestation │ │ +│ └───────────────────────────────────────────┘ │ +└─────────────────────┬───────────────────────────┘ + │ HTTPS + ┌───────────┴───────────┐ + │ Hyperliquid DEX │ + │ (funding rates, │ + │ prices, trades) │ + └───────────────────────┘ +``` + +## The Trust Model + +| Question | Without TEE | With TEE | +|----------|-------------|----------| +| Who controls the private key? | Fund manager | Hardware (nobody) | +| Can the rules be changed secretly? | Yes | No — changes the attestation | +| Can someone steal the funds? | Yes, if they have the key | No — key is in hardware | +| How do I verify the strategy? | Read the whitepaper and hope | Read the code, check attestation | +| What if the server is compromised? | Funds at risk | Key still safe in TEE | + +## Verification + +Anyone can verify this vault: + +```bash +# 1. Get the attestation +phala cvms attestation + +# 2. Find the compose_hash in the event log +# Example: compose_hash = 06e91a8969f7... + +# 3. Hash the docker-compose.yaml yourself +# The hash must match + +# 4. Read vault.py — the rules are right there: +# MAX_LOSS_PER_TRADE = 0.02 +# MAX_POSITION_RATIO = 0.20 +# ALLOWED_ASSETS = ["BTC", "ETH", "SOL"] +# These CANNOT be changed without changing the hash. + +# 5. Check the vault state +curl https://-8080.dstack-.phala.network/ +curl https://-8080.dstack-.phala.network/rules +curl https://-8080.dstack-.phala.network/verify +``` + +## Deploy + +```bash +phala deploy --name my-vault --compose docker-compose.yaml +``` + +## API + +| Endpoint | Description | +|----------|-------------| +| `GET /` | Full vault state: wallet, signals, prices, positions, P&L | +| `GET /rules` | Trading rules (hardcoded, covered by attestation) | +| `GET /verify` | Step-by-step guide to verify this vault | + +## Why Not Just Use a Smart Contract? + +Smart contracts are great for simple rules (swap, lend, stake). But they can't: + +- Call off-chain APIs (funding rates from Hyperliquid) +- Run complex strategies (momentum detection, multi-factor signals) +- React in real-time to market conditions (latency matters) + +TEE gives you the **trustlessness of smart contracts** with the **flexibility of off-chain code**. + +## Cost + +- Instance: `tdx.small` (1 vCPU, 2GB RAM) +- Rate: ~$0.058/hour ≈ $42/month +- Phala Cloud free tier: $20 credit on signup + +## Security Considerations + +- The vault wallet key is derived deterministically from the app's identity. Redeploying with the same compose file on the same KMS produces the same key. +- Changing ANY code changes the compose hash, which changes the attestation. Depositors should monitor for attestation changes. +- The vault currently trusts Hyperliquid's API responses. A production vault should verify data from multiple sources. +- This is an example. Do not deposit significant funds without thorough auditing. + +## License + +Apache-2.0 From 11aa12f75a8a53949c7229d52cb3fb3967e02fdd Mon Sep 17 00:00:00 2001 From: 0xmatchmaker Date: Wed, 4 Mar 2026 20:53:48 +0800 Subject: [PATCH 2/6] Create docker-compose.yaml --- trustless-vault/docker-compose.yaml | 169 ++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 trustless-vault/docker-compose.yaml diff --git a/trustless-vault/docker-compose.yaml b/trustless-vault/docker-compose.yaml new file mode 100644 index 0000000..9bdbba9 --- /dev/null +++ b/trustless-vault/docker-compose.yaml @@ -0,0 +1,169 @@ +services: + vault: + build: + context: . + dockerfile_inline: | + FROM python:3.12-slim@sha256:d51616d5860ba60aa1786987d93b6aaebc05dd70f59f4cc36b008e9768cb88f1 + WORKDIR /app + RUN pip install --no-cache-dir requests>=2.31.0 eth-account>=0.10.0 + RUN cat > vault.py <<'PYEOF' + import os, time, json, logging, threading, hashlib + from pathlib import Path + from decimal import Decimal + from http.server import HTTPServer, BaseHTTPRequestHandler + from urllib.parse import urlparse + try: + import requests + except ImportError: + import subprocess, sys + subprocess.check_call([sys.executable, "-m", "pip", "install", "requests"]) + import requests + + logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S") + logger = logging.getLogger(__name__) + + # === RULES: hardcoded, covered by attestation, immutable === + MAX_LOSS_PER_TRADE = Decimal("0.02") + MAX_POSITION_RATIO = Decimal("0.20") + STOP_LOSS = Decimal("0.05") + FUNDING_THRESHOLD = 50.0 + ALLOWED_ASSETS = ["BTC", "ETH", "SOL"] + COOLDOWN_AFTER_LOSS = 3600 + + STATE = { + "vault_address": None, + "total_deposited": "0", + "depositors": {}, + "positions": [], + "signals": [], + "prices": {}, + "pnl": "0", + "last_scan": None, + "scans": 0, + "status": "initializing", + "rules": { + "max_loss_per_trade": str(MAX_LOSS_PER_TRADE), + "max_position_ratio": str(MAX_POSITION_RATIO), + "stop_loss": str(STOP_LOSS), + "allowed_assets": ALLOWED_ASSETS, + "funding_threshold": FUNDING_THRESHOLD, + "cooldown_after_loss_seconds": COOLDOWN_AFTER_LOSS, + }, + } + + def init_vault_wallet(): + from eth_account import Account + key_path = Path("/app/data/.vault_key") + key_path.parent.mkdir(parents=True, exist_ok=True) + if key_path.exists(): + wallet = Account.from_key(key_path.read_text().strip()) + logger.info("Loaded vault wallet: %s", wallet.address) + else: + wallet = Account.create() + key_path.write_text(wallet.key.hex()) + logger.info("Created vault wallet in TEE: %s", wallet.address) + return wallet + + HL_API = "https://api.hyperliquid.xyz/info" + + def scan_funding(): + try: + resp = requests.post(HL_API, json={"type": "metaAndAssetCtxs"}, headers={"Content-Type": "application/json"}, timeout=10) + data = resp.json() + meta, ctxs = data[0]["universe"], data[1] + signals = [] + for i, asset in enumerate(meta): + sym = asset["name"] + if sym not in ALLOWED_ASSETS: + continue + funding = float(ctxs[i].get("funding", 0)) + annual = funding * 3 * 365 * 100 + if abs(annual) > FUNDING_THRESHOLD: + signals.append({"symbol": sym, "funding_annual_pct": round(annual, 1), "direction": "SHORT" if annual > 0 else "LONG", "timestamp": time.strftime("%Y-%m-%d %H:%M:%S")}) + return sorted(signals, key=lambda s: abs(s["funding_annual_pct"]), reverse=True) + except Exception as e: + logger.error("Funding scan failed: %s", e) + return [] + + def get_prices(): + try: + resp = requests.post(HL_API, json={"type": "allMids"}, headers={"Content-Type": "application/json"}, timeout=10) + mids = resp.json() + return {k: round(float(v), 2) for k, v in mids.items() if k in ALLOWED_ASSETS} + except Exception as e: + logger.error("Price fetch failed: %s", e) + return {} + + def check_trade_allowed(signal, balance): + if balance <= 0: + return False, "no funds" + pos_size = float(balance) * float(MAX_POSITION_RATIO) + max_loss = pos_size * float(STOP_LOSS) + if max_loss > float(balance) * float(MAX_LOSS_PER_TRADE): + return False, "position too large for risk limits" + return True, "ok" + + class VaultHandler(BaseHTTPRequestHandler): + def do_GET(self): + path = urlparse(self.path).path + if path == "/rules": + data = STATE["rules"] + elif path == "/verify": + data = {"message": "Verify this vault by checking the attestation", "steps": ["1. Run: phala cvms attestation ", "2. Note the compose_hash", "3. Compare with docker-compose.yaml in this repo", "4. If they match, the vault runs exactly this code", "5. The rules are hardcoded and immutable"], "vault_address": STATE["vault_address"], "rules": STATE["rules"]} + else: + data = STATE + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.send_header("Access-Control-Allow-Origin", "*") + self.end_headers() + self.wfile.write(json.dumps(data, indent=2, default=str).encode()) + def log_message(self, *a): + pass + + def main(): + logger.info("=" * 60) + logger.info(" TRUSTLESS VAULT") + logger.info(" Secured by TEE | Verified by Attestation") + logger.info("=" * 60) + logger.info("Rules: max_loss=%s, max_pos=%s, stop=%s, assets=%s", MAX_LOSS_PER_TRADE, MAX_POSITION_RATIO, STOP_LOSS, ALLOWED_ASSETS) + wallet = init_vault_wallet() + STATE["vault_address"] = wallet.address + STATE["status"] = "running" + logger.info("Vault address: %s", wallet.address) + threading.Thread(target=lambda: HTTPServer(("0.0.0.0", 8080), VaultHandler).serve_forever(), daemon=True).start() + logger.info("API on :8080 — GET / | /rules | /verify") + interval = int(os.environ.get("SCAN_INTERVAL", "60")) + while True: + STATE["signals"] = scan_funding() + STATE["prices"] = get_prices() + STATE["last_scan"] = time.strftime("%Y-%m-%d %H:%M:%S") + STATE["scans"] += 1 + for s in STATE["signals"]: + ok, reason = check_trade_allowed(s, 0) + logger.info("Signal: %s %s (%s%%) — %s", s["direction"], s["symbol"], s["funding_annual_pct"], "WOULD TRADE" if ok else "BLOCKED: " + reason) + if STATE["prices"]: + logger.info("Prices: %s", " | ".join("%s=$%s" % (k, "{:,.0f}".format(v)) for k, v in STATE["prices"].items())) + logger.info("Scan #%d done. Next in %ds.", STATE["scans"], interval) + time.sleep(interval) + + if __name__ == "__main__": + main() + PYEOF + CMD ["python3", "-u", "vault.py"] + environment: + - PYTHONUNBUFFERED=1 + - SCAN_INTERVAL=60 + ports: + - "8080:8080" + volumes: + - vault-data:/app/data + - /var/run/dstack.sock:/var/run/dstack.sock + restart: unless-stopped + healthcheck: + test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8080')"] + interval: 30s + timeout: 10s + retries: 3 + +volumes: + vault-data: From 815c2fb56d735e626561f45609bbc6e4aea85195 Mon Sep 17 00:00:00 2001 From: 0xmatchmaker Date: Wed, 4 Mar 2026 20:54:51 +0800 Subject: [PATCH 3/6] Add example environment variable for scan interval --- trustless-vault/.env.example | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 trustless-vault/.env.example diff --git a/trustless-vault/.env.example b/trustless-vault/.env.example new file mode 100644 index 0000000..ca8d441 --- /dev/null +++ b/trustless-vault/.env.example @@ -0,0 +1,2 @@ +# Scan interval in seconds (default: 60) +SCAN_INTERVAL=60 From 3e86fda4d82f977363919cc0e32342d792ed7bce Mon Sep 17 00:00:00 2001 From: 0xmatchmaker Date: Thu, 5 Mar 2026 08:32:05 +0800 Subject: [PATCH 4/6] remove old docker-compose --- trustless-vault/docker-compose.yaml | 169 ---------------------------- 1 file changed, 169 deletions(-) delete mode 100644 trustless-vault/docker-compose.yaml diff --git a/trustless-vault/docker-compose.yaml b/trustless-vault/docker-compose.yaml deleted file mode 100644 index 9bdbba9..0000000 --- a/trustless-vault/docker-compose.yaml +++ /dev/null @@ -1,169 +0,0 @@ -services: - vault: - build: - context: . - dockerfile_inline: | - FROM python:3.12-slim@sha256:d51616d5860ba60aa1786987d93b6aaebc05dd70f59f4cc36b008e9768cb88f1 - WORKDIR /app - RUN pip install --no-cache-dir requests>=2.31.0 eth-account>=0.10.0 - RUN cat > vault.py <<'PYEOF' - import os, time, json, logging, threading, hashlib - from pathlib import Path - from decimal import Decimal - from http.server import HTTPServer, BaseHTTPRequestHandler - from urllib.parse import urlparse - try: - import requests - except ImportError: - import subprocess, sys - subprocess.check_call([sys.executable, "-m", "pip", "install", "requests"]) - import requests - - logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S") - logger = logging.getLogger(__name__) - - # === RULES: hardcoded, covered by attestation, immutable === - MAX_LOSS_PER_TRADE = Decimal("0.02") - MAX_POSITION_RATIO = Decimal("0.20") - STOP_LOSS = Decimal("0.05") - FUNDING_THRESHOLD = 50.0 - ALLOWED_ASSETS = ["BTC", "ETH", "SOL"] - COOLDOWN_AFTER_LOSS = 3600 - - STATE = { - "vault_address": None, - "total_deposited": "0", - "depositors": {}, - "positions": [], - "signals": [], - "prices": {}, - "pnl": "0", - "last_scan": None, - "scans": 0, - "status": "initializing", - "rules": { - "max_loss_per_trade": str(MAX_LOSS_PER_TRADE), - "max_position_ratio": str(MAX_POSITION_RATIO), - "stop_loss": str(STOP_LOSS), - "allowed_assets": ALLOWED_ASSETS, - "funding_threshold": FUNDING_THRESHOLD, - "cooldown_after_loss_seconds": COOLDOWN_AFTER_LOSS, - }, - } - - def init_vault_wallet(): - from eth_account import Account - key_path = Path("/app/data/.vault_key") - key_path.parent.mkdir(parents=True, exist_ok=True) - if key_path.exists(): - wallet = Account.from_key(key_path.read_text().strip()) - logger.info("Loaded vault wallet: %s", wallet.address) - else: - wallet = Account.create() - key_path.write_text(wallet.key.hex()) - logger.info("Created vault wallet in TEE: %s", wallet.address) - return wallet - - HL_API = "https://api.hyperliquid.xyz/info" - - def scan_funding(): - try: - resp = requests.post(HL_API, json={"type": "metaAndAssetCtxs"}, headers={"Content-Type": "application/json"}, timeout=10) - data = resp.json() - meta, ctxs = data[0]["universe"], data[1] - signals = [] - for i, asset in enumerate(meta): - sym = asset["name"] - if sym not in ALLOWED_ASSETS: - continue - funding = float(ctxs[i].get("funding", 0)) - annual = funding * 3 * 365 * 100 - if abs(annual) > FUNDING_THRESHOLD: - signals.append({"symbol": sym, "funding_annual_pct": round(annual, 1), "direction": "SHORT" if annual > 0 else "LONG", "timestamp": time.strftime("%Y-%m-%d %H:%M:%S")}) - return sorted(signals, key=lambda s: abs(s["funding_annual_pct"]), reverse=True) - except Exception as e: - logger.error("Funding scan failed: %s", e) - return [] - - def get_prices(): - try: - resp = requests.post(HL_API, json={"type": "allMids"}, headers={"Content-Type": "application/json"}, timeout=10) - mids = resp.json() - return {k: round(float(v), 2) for k, v in mids.items() if k in ALLOWED_ASSETS} - except Exception as e: - logger.error("Price fetch failed: %s", e) - return {} - - def check_trade_allowed(signal, balance): - if balance <= 0: - return False, "no funds" - pos_size = float(balance) * float(MAX_POSITION_RATIO) - max_loss = pos_size * float(STOP_LOSS) - if max_loss > float(balance) * float(MAX_LOSS_PER_TRADE): - return False, "position too large for risk limits" - return True, "ok" - - class VaultHandler(BaseHTTPRequestHandler): - def do_GET(self): - path = urlparse(self.path).path - if path == "/rules": - data = STATE["rules"] - elif path == "/verify": - data = {"message": "Verify this vault by checking the attestation", "steps": ["1. Run: phala cvms attestation ", "2. Note the compose_hash", "3. Compare with docker-compose.yaml in this repo", "4. If they match, the vault runs exactly this code", "5. The rules are hardcoded and immutable"], "vault_address": STATE["vault_address"], "rules": STATE["rules"]} - else: - data = STATE - self.send_response(200) - self.send_header("Content-Type", "application/json") - self.send_header("Access-Control-Allow-Origin", "*") - self.end_headers() - self.wfile.write(json.dumps(data, indent=2, default=str).encode()) - def log_message(self, *a): - pass - - def main(): - logger.info("=" * 60) - logger.info(" TRUSTLESS VAULT") - logger.info(" Secured by TEE | Verified by Attestation") - logger.info("=" * 60) - logger.info("Rules: max_loss=%s, max_pos=%s, stop=%s, assets=%s", MAX_LOSS_PER_TRADE, MAX_POSITION_RATIO, STOP_LOSS, ALLOWED_ASSETS) - wallet = init_vault_wallet() - STATE["vault_address"] = wallet.address - STATE["status"] = "running" - logger.info("Vault address: %s", wallet.address) - threading.Thread(target=lambda: HTTPServer(("0.0.0.0", 8080), VaultHandler).serve_forever(), daemon=True).start() - logger.info("API on :8080 — GET / | /rules | /verify") - interval = int(os.environ.get("SCAN_INTERVAL", "60")) - while True: - STATE["signals"] = scan_funding() - STATE["prices"] = get_prices() - STATE["last_scan"] = time.strftime("%Y-%m-%d %H:%M:%S") - STATE["scans"] += 1 - for s in STATE["signals"]: - ok, reason = check_trade_allowed(s, 0) - logger.info("Signal: %s %s (%s%%) — %s", s["direction"], s["symbol"], s["funding_annual_pct"], "WOULD TRADE" if ok else "BLOCKED: " + reason) - if STATE["prices"]: - logger.info("Prices: %s", " | ".join("%s=$%s" % (k, "{:,.0f}".format(v)) for k, v in STATE["prices"].items())) - logger.info("Scan #%d done. Next in %ds.", STATE["scans"], interval) - time.sleep(interval) - - if __name__ == "__main__": - main() - PYEOF - CMD ["python3", "-u", "vault.py"] - environment: - - PYTHONUNBUFFERED=1 - - SCAN_INTERVAL=60 - ports: - - "8080:8080" - volumes: - - vault-data:/app/data - - /var/run/dstack.sock:/var/run/dstack.sock - restart: unless-stopped - healthcheck: - test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8080')"] - interval: 30s - timeout: 10s - retries: 3 - -volumes: - vault-data: From 9e0086eecd9afc8b7c1f573c5130176bc90e24aa Mon Sep 17 00:00:00 2001 From: 0xmatchmaker Date: Thu, 5 Mar 2026 08:36:37 +0800 Subject: [PATCH 5/6] address review: dstack SDK, separate files, attestation quote --- trustless-vault/Dockerfile | 10 + trustless-vault/docker-compose.yaml | 16 ++ trustless-vault/requirements.txt | 3 + trustless-vault/vault.py | 279 ++++++++++++++++++++++++++++ 4 files changed, 308 insertions(+) create mode 100644 trustless-vault/Dockerfile create mode 100644 trustless-vault/docker-compose.yaml create mode 100644 trustless-vault/requirements.txt create mode 100644 trustless-vault/vault.py diff --git a/trustless-vault/Dockerfile b/trustless-vault/Dockerfile new file mode 100644 index 0000000..b201157 --- /dev/null +++ b/trustless-vault/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.12-slim + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY vault.py . + +CMD ["python3", "-u", "vault.py"] diff --git a/trustless-vault/docker-compose.yaml b/trustless-vault/docker-compose.yaml new file mode 100644 index 0000000..1808aa0 --- /dev/null +++ b/trustless-vault/docker-compose.yaml @@ -0,0 +1,16 @@ +services: + vault: + build: . + environment: + - PYTHONUNBUFFERED=1 + - SCAN_INTERVAL=${SCAN_INTERVAL:-60} + ports: + - "8080:8080" + volumes: + - /var/run/dstack.sock:/var/run/dstack.sock + restart: unless-stopped + healthcheck: + test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8080')"] + interval: 30s + timeout: 10s + retries: 3 diff --git a/trustless-vault/requirements.txt b/trustless-vault/requirements.txt new file mode 100644 index 0000000..be8fb97 --- /dev/null +++ b/trustless-vault/requirements.txt @@ -0,0 +1,3 @@ +requests>=2.31.0 +eth-account>=0.10.0 +dstack-sdk>=0.5.0 diff --git a/trustless-vault/vault.py b/trustless-vault/vault.py new file mode 100644 index 0000000..90de187 --- /dev/null +++ b/trustless-vault/vault.py @@ -0,0 +1,279 @@ +""" +Trustless Vault — TEE-Secured Fund Management + +The one thing only TEE can do: let people trust a shared pool of funds +without trusting any person. + +Rules are hardcoded. Key is derived from hardware. Attestation proves everything. +Change one character → hash changes → depositors know immediately. + +NOTE: This vault trusts Hyperliquid's API responses as-is. +A production vault should cross-validate with multiple data sources. +""" + +import os +import time +import json +import hashlib +import logging +import threading +from decimal import Decimal +from http.server import HTTPServer, BaseHTTPRequestHandler +from urllib.parse import urlparse + +import requests +from dstack_sdk import DstackClient +from dstack_sdk.ethereum import to_account + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s [%(levelname)s] %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", +) +logger = logging.getLogger(__name__) + + +# ============================================================================= +# RULES — hardcoded, covered by attestation, immutable +# Changing any value changes the compose hash. Depositors can verify at any time. +# ============================================================================= + +MAX_LOSS_PER_TRADE = Decimal("0.02") # 2% max loss per trade +MAX_POSITION_RATIO = Decimal("0.20") # 20% max single position +STOP_LOSS = Decimal("0.05") # 5% stop loss +FUNDING_THRESHOLD = 50.0 # Annual rate % to trigger signal +ALLOWED_ASSETS = ["BTC", "ETH", "SOL"] # Only trade these +COOLDOWN_AFTER_LOSS = 3600 # 1 hour cooldown after loss + + +# ============================================================================= +# Vault state +# ============================================================================= + +STATE = { + "vault_address": None, + "total_deposited": "0", + "depositors": {}, + "positions": [], + "signals": [], + "prices": {}, + "pnl": "0", + "last_scan": None, + "scans": 0, + "status": "initializing", + "rules": { + "max_loss_per_trade": str(MAX_LOSS_PER_TRADE), + "max_position_ratio": str(MAX_POSITION_RATIO), + "stop_loss": str(STOP_LOSS), + "allowed_assets": ALLOWED_ASSETS, + "funding_threshold": FUNDING_THRESHOLD, + "cooldown_after_loss_seconds": COOLDOWN_AFTER_LOSS, + }, +} + + +# ============================================================================= +# TEE wallet — derived from hardware, deterministic, unexportable +# ============================================================================= + +def init_vault_wallet(): + """ + Derive the vault wallet from TEE hardware identity via dstack SDK. + + The key is deterministic: same app_id + same path = same key, always. + No disk storage needed. Even if the VM is destroyed and recreated, + the same compose file will produce the same wallet. + + The key NEVER leaves the TEE hardware. + """ + client = DstackClient() + key_response = client.get_key(path="/vault/main", purpose="trustless-vault-wallet") + wallet = to_account(key_response) + logger.info("Vault wallet derived from TEE hardware: %s", wallet.address) + return wallet, client + + +# ============================================================================= +# Market data +# ============================================================================= + +HL_API = "https://api.hyperliquid.xyz/info" + + +def scan_funding(): + """Find extreme funding rate opportunities on allowed assets.""" + try: + resp = requests.post( + HL_API, + json={"type": "metaAndAssetCtxs"}, + headers={"Content-Type": "application/json"}, + timeout=10, + ) + data = resp.json() + meta, ctxs = data[0]["universe"], data[1] + signals = [] + for i, asset in enumerate(meta): + sym = asset["name"] + if sym not in ALLOWED_ASSETS: + continue + funding = float(ctxs[i].get("funding", 0)) + annual = funding * 3 * 365 * 100 + if abs(annual) > FUNDING_THRESHOLD: + signals.append({ + "symbol": sym, + "funding_annual_pct": round(annual, 1), + "direction": "SHORT" if annual > 0 else "LONG", + "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"), + }) + return sorted(signals, key=lambda s: abs(s["funding_annual_pct"]), reverse=True) + except Exception as e: + logger.error("Funding scan failed: %s", e) + return [] + + +def get_prices(): + """Get current prices for allowed assets.""" + try: + resp = requests.post( + HL_API, + json={"type": "allMids"}, + headers={"Content-Type": "application/json"}, + timeout=10, + ) + mids = resp.json() + return {k: round(float(v), 2) for k, v in mids.items() if k in ALLOWED_ASSETS} + except Exception as e: + logger.error("Price fetch failed: %s", e) + return {} + + +# ============================================================================= +# Risk engine — these checks CANNOT be bypassed +# ============================================================================= + +def check_trade_allowed(signal, balance): + """Risk checks before any trade. Hardcoded, verified by attestation.""" + if balance <= 0: + return False, "no funds" + pos_size = float(balance) * float(MAX_POSITION_RATIO) + max_loss = pos_size * float(STOP_LOSS) + if max_loss > float(balance) * float(MAX_LOSS_PER_TRADE): + return False, "position too large for risk limits" + return True, "ok" + + +# ============================================================================= +# HTTP API — transparent state for depositors +# ============================================================================= + +# Global reference set by main() +_dstack_client = None + +class VaultHandler(BaseHTTPRequestHandler): + """ + Public API. Anyone can inspect the full state. + + GET / → Full vault state + GET /rules → Trading rules (hardcoded, attestable) + GET /verify → Attestation quote + verification guide + """ + + def do_GET(self): + path = urlparse(self.path).path + + if path == "/rules": + data = STATE["rules"] + elif path == "/verify": + # Return actual attestation quote for cryptographic verification + quote_data = None + try: + if _dstack_client and STATE["vault_address"]: + report = hashlib.sha256( + STATE["vault_address"].encode() + ).digest()[:64] + quote_response = _dstack_client.get_quote(report) + quote_data = { + "quote": quote_response.quote, + "event_log": quote_response.event_log, + } + except Exception as e: + quote_data = {"error": str(e)} + + data = { + "vault_address": STATE["vault_address"], + "rules": STATE["rules"], + "attestation": quote_data, + "verify_steps": [ + "1. The 'attestation.quote' is a TDX quote from TEE hardware", + "2. Decode it to extract RTMR values and compose_hash", + "3. Compare compose_hash with the docker-compose.yaml in this repo", + "4. If they match, this vault runs exactly the published code", + "5. The rules above are hardcoded — any change breaks the hash", + ], + } + else: + data = STATE + + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.send_header("Access-Control-Allow-Origin", "*") + self.end_headers() + self.wfile.write(json.dumps(data, indent=2, default=str).encode()) + + def log_message(self, *_args): + pass + + +# ============================================================================= +# Main loop +# ============================================================================= + +def main(): + global _dstack_client + + logger.info("=" * 60) + logger.info(" TRUSTLESS VAULT") + logger.info(" Secured by TEE | Verified by Attestation") + logger.info("=" * 60) + logger.info("Rules: max_loss=%s, max_pos=%s, stop=%s, assets=%s", + MAX_LOSS_PER_TRADE, MAX_POSITION_RATIO, STOP_LOSS, ALLOWED_ASSETS) + + wallet, _dstack_client = init_vault_wallet() + STATE["vault_address"] = wallet.address + STATE["status"] = "running" + + logger.info("Vault address: %s", wallet.address) + + # Start HTTP server + server = HTTPServer(("0.0.0.0", 8080), VaultHandler) + threading.Thread(target=server.serve_forever, daemon=True).start() + logger.info("API on :8080 — GET / | /rules | /verify") + + interval = int(os.environ.get("SCAN_INTERVAL", "60")) + + while True: + STATE["signals"] = scan_funding() + STATE["prices"] = get_prices() + STATE["last_scan"] = time.strftime("%Y-%m-%d %H:%M:%S") + STATE["scans"] += 1 + + for s in STATE["signals"]: + ok, reason = check_trade_allowed(s, 0) + logger.info( + "Signal: %s %s (%s%%) — %s", + s["direction"], s["symbol"], s["funding_annual_pct"], + "WOULD TRADE" if ok else "BLOCKED: " + reason, + ) + + if STATE["prices"]: + logger.info( + "Prices: %s", + " | ".join(f"{k}=${v:,.0f}" for k, v in STATE["prices"].items()), + ) + + logger.info("Scan #%d done. Next in %ds.", STATE["scans"], interval) + time.sleep(interval) + + +if __name__ == "__main__": + main() From bccc3d20e74bf9f6fdb3b17bece29a012cc8895d Mon Sep 17 00:00:00 2001 From: 0xmatchmaker Date: Thu, 5 Mar 2026 08:38:26 +0800 Subject: [PATCH 6/6] update README with attestation verification --- trustless-vault/README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/trustless-vault/README.md b/trustless-vault/README.md index b48704b..5eef404 100644 --- a/trustless-vault/README.md +++ b/trustless-vault/README.md @@ -61,13 +61,15 @@ There's a gap: **complex trading strategies that manage shared funds**. Until no Anyone can verify this vault: ```bash -# 1. Get the attestation -phala cvms attestation +# 1. Get the attestation quote directly from the vault +curl https://-8080.dstack-.phala.network/verify +# Returns actual TDX quote with RTMR values -# 2. Find the compose_hash in the event log -# Example: compose_hash = 06e91a8969f7... +# 2. Or use the CLI +phala cvms attestation +# Find the compose_hash in the event log -# 3. Hash the docker-compose.yaml yourself +# 3. Hash the docker-compose.yaml + vault.py yourself # The hash must match # 4. Read vault.py — the rules are right there: @@ -79,7 +81,6 @@ phala cvms attestation # 5. Check the vault state curl https://-8080.dstack-.phala.network/ curl https://-8080.dstack-.phala.network/rules -curl https://-8080.dstack-.phala.network/verify ``` ## Deploy @@ -114,9 +115,9 @@ TEE gives you the **trustlessness of smart contracts** with the **flexibility of ## Security Considerations -- The vault wallet key is derived deterministically from the app's identity. Redeploying with the same compose file on the same KMS produces the same key. +- The vault wallet key is derived deterministically from the app's identity via `dstack_sdk.get_key()`. Same app_id = same key. No disk storage needed. - Changing ANY code changes the compose hash, which changes the attestation. Depositors should monitor for attestation changes. -- The vault currently trusts Hyperliquid's API responses. A production vault should verify data from multiple sources. +- **This vault trusts Hyperliquid's API responses as-is.** A production vault should cross-validate with multiple data sources to mitigate API manipulation risks. - This is an example. Do not deposit significant funds without thorough auditing. ## License