Skip to content

Repository files navigation

TEE Options Pricer — Flare Compute Extension

A TEE-powered options market maker running as a Flare Compute Extension (FCE) on Coston2. The enclave receives RFQ parameters on-chain, prices European call/put options using a GBM Monte Carlo simulation seeded from Flare's on-chain SecureRandom, and returns an EIP-712 signed Quote attested by the TEE's hardware key.

Pricing pipeline (per RFQ):

  1. Fetch XRP/USD spot from FTSO (getFeedByIdInWei via ContractRegistry)
  2. Compute 30-day realized vol from 31 FTSO archive-block snapshots
  3. Derive per-RFQ MC seed: sha256(Relay.getRandomNumber() || rfq_id) % 2³²
  4. Run 10k-path vectorised GBM Monte Carlo + Black-Scholes convergence check
  5. Sign EIP-712 Quote struct via TEE /sign endpoint — hardware-attested key
  6. Publish attestation token + pricing inputs on-chain via Relay contract

Cloned from the fce-sign starter template by Flare Foundation and extended for our EthGlobal Cannes project. Python implementation only (python/app/).

Note: .env and config/proxy/extension_proxy.toml are gitignored — never committed.

Repository Layout

python/
  app/             ← our code: config, abi, handlers, eip712_signer, get_ftso_spot, get_realized_vol, mc_pricer, bs_pricer
  base/            ← framework infrastructure (DO NOT MODIFY)
  integration/     ← end-to-end test harness (see Step 7)
    e2e_rfq_pipeline.py   ← 11-step full pipeline runner
    steps/                ← individual step scripts for isolated testing
  tests/           ← unit tests (16 passing, 2 xfail)
  main.py
contract/
  InstructionSender.sol   ← modified: PRICING/QUOTE OPType, sendInstruction(), getExtensionId()
go/tools/          ← deployment and registration CLI tools (Go — not our code)

Deployed State (Coston2, chain ID 114)

Variable Value
INSTRUCTION_SENDER 0xe578e001cb877849672f24A065060702F3eb1DB5
EXTENSION_ID 0x00...0ea (234 decimal)
TUNNEL_URL https://noisy-noncuratively-dalene.ngrok-free.dev (fixed domain)
TEE_ID (PRODUCTION) 0xbc7c58e4D6EA000603273f7F8f3687B03414430d
code hash 0x194844cf417dde867073e5ab7199fa4d21fd82b5dbe2bdea8b3d7fc18d10fdc2

Steps 0–6 below are already completed for this deployment. Only re-run them if starting fresh from a clean environment.

Shared contract

contract/InstructionSender.sol is modified from the original template:

  • sendInstruction(bytes payload) replaces updateKey / sign — sends PRICING/QUOTE to the TEE
  • getExtensionId() replaces the auto-getter _extensionId() (private state variable)
  • onlyOwner on sendInstruction — owner is the deployer EOA

Setup (from scratch)

All deployment and registration tools are in go/tools/. They interact with smart contracts and the TEE proxy. Go >= 1.23 required.

Prerequisites

  • Docker
  • ngrok with a fixed domain (or cloudflared, no account required)
  • Foundry (forge, cast)
  • A funded Coston2 wallet (C2FLR for gas + TEE registration fees)
  • Go >= 1.23

Step 0: Configure environment

cp .env.example .env
# Fill in: PRIVATE_KEY, INITIAL_OWNER, LANGUAGE=python

cp config/proxy/extension_proxy.toml.example config/proxy/extension_proxy.toml
# Fill in the DB credentials for the Coston2 C-chain indexer ([db] section).
# Obtain credentials from the Flare team at the venue.

Step 1: Deploy InstructionSender

Already done — deployed at 0xe578e001cb877849672f24A065060702F3eb1DB5. Skip unless starting fresh.

Our InstructionSender.sol differs from the template: sendInstruction(bytes) replaces updateKey/sign, and getExtensionId() replaces the auto-getter. The deploy-contract tool handles compilation via Foundry.

cd go/tools
go run ./cmd/deploy-contract
# Save printed address to .env as INSTRUCTION_SENDER

Step 2: Register the extension

Already done — EXTENSION_ID = 234 (0x00...0ea). Skip unless starting fresh.

cd go/tools
go run ./cmd/register-extension
# Save printed ID to .env as EXTENSION_ID

Step 3: Start the extension stack

⚠️ Critical: docker compose build generates a new teeID which invalidates PRODUCTION registration (circular dependency — see Troubleshooting). Only rebuild when you genuinely need to deploy new container code. Use docker compose restart for all other restarts.

First start (or after a code change that requires a rebuild):

docker compose build
docker compose up -d

All other restarts (safe — preserves teeID):

docker compose restart

Wait for the proxy to become healthy:

until curl -sf http://localhost:6676/info >/dev/null 2>&1; do sleep 2; done
echo "Extension proxy is ready"

Step 4: Start tunnel

Already running — fixed ngrok domain: https://noisy-noncuratively-dalene.ngrok-free.dev

In a separate terminal:

ngrok http --domain=noisy-noncuratively-dalene.ngrok-free.dev 6676

Or with cloudflared (generates a fresh URL each time — must update .env):

cloudflared tunnel --url http://localhost:6676
# Update TUNNEL_URL in .env with the new URL

Note: The tunnel must stay running for the entire session. The ngrok fixed domain survives restarts without needing to update .env.

Step 5: Add TEE version

cd go/tools
& "C:\Program Files\Go\bin\go.exe" run ./cmd/allow-tee-version -p http://localhost:6676

Step 6: Register the TEE machine

Make sure TUNNEL_URL is set correctly in .env.

cd go/tools
& "C:\Program Files\Go\bin\go.exe" run ./cmd/register-tee -p http://localhost:6676 -l

The -l flag enables local/test mode (required on Coston2 — TEE returns a test attestation token, not a real GCP JWT).

Wait for the TEE to reach PRODUCTION status before running the tests. You can monitor with:

curl http://localhost:6676/info
# "lastSigningPolicyId" must be non-zero for the TEE to process instructions

Step 7: Run integration tests

Prerequisites: Steps 0–6 complete, TEE status = PRODUCTION, .env has INSTRUCTION_SENDER, TUNNEL_URL, PRIVATE_KEY.

Full pipeline runner (11 steps)

cd python
python integration/e2e_rfq_pipeline.py

Steps and what they confirm:

Step What Expected
1 RPC connect (Coston2, chain ID 114) eth_chainId = 114
2 Wallet balance check balance > 0 C2FLR
3 GET /info on ext-proxy teeId, codeHash, signingPolicyId non-zero
4 getExtensionId() on-chain matches EXTENSION_ID in .env
5 ABI-encode RFQ payload no revert
6 eth_call simulate sendInstruction no revert
7 sendInstruction real tx → parse TeeInstructionsSent log status=1, instructionId emitted
8 Poll GET /action/result/{id} until status=1 TEE processed pricing + signed quote
9 Inspect raw TEE result JSON result fields present
10 ABI-decode result: price_mc, price_bs, delta, vega, seed valid 18-decimal values
11 Assertions: prices positive, MC/BS gap < 15%, delta ∈ (0,1) all pass

Steps 1–7 confirmed passing on Coston2 (real tx, gas=172665, status=1, block 29000826). Steps 8–11 require TEE status = PRODUCTION.

Individual step scripts (isolated debugging)

cd python
python integration/steps/step_01_connect_rpc.py
python integration/steps/step_07_submit_tx.py
# etc.

Each step script is self-contained and imports only what it needs.

Unit tests (pricing logic only, no network)

cd python
python -m unittest discover -s tests -p 'test_*.py'
# 16 passing, 2 xfail

Port reference

Service Container port Host port
ext-proxy internal 6663 6675
ext-proxy external 6664 6676
redis 6379 6383

The tunnel exposes host port 6676 (ext-proxy external) to the internet.

Troubleshooting

teeID invalidated after container rebuild (circular dependency)

docker compose build generates a new container image → new signing key → new teeID. The ext-proxy only forwards results from PRODUCTION teeIDs. Completing registration requires the ext-proxy to forward the attestation result — so the new teeID cannot self-register through the proxy it hasn't been admitted into yet.

Symptom: step 8 poll returns HTTP 404; docker compose logs ext-proxy shows invalid teeID; curl http://localhost:6676/info shows lastSigningPolicyId: 0.

Avoid it: use docker compose restart for all restarts. Only run docker compose build when you genuinely need to deploy new container code.

Recovery (after an unavoidable rebuild):

cd go/tools
& "C:\Program Files\Go\bin\go.exe" run ./cmd/allow-tee-version -p http://localhost:6676
& "C:\Program Files\Go\bin\go.exe" run ./cmd/register-tee -p http://localhost:6676 -l -command Rap

-command Rap skips pre-registration and sends a fresh attestation + availability check. Wait for the TEE to reach PRODUCTION before retrying the integration tests.

Proxy won't start / DB sync error

The proxy needs a synced C-chain indexer DB. Check the proxy logs and verify the DB credentials in config/proxy/extension_proxy.toml:

docker compose logs ext-proxy

Transaction reverts

Ensure your wallet has enough C2FLR for gas + fees.

to-production times out

Try restarting the proxy — it may have missed a signing policy round:

docker compose restart

If that doesn't help, the FDC attestation flow requires active relay providers on Coston2. If no relay infrastructure is running, the availability check won't complete.

Tunnel URL changed

If using cloudflared (ephemeral URL), update TUNNEL_URL in .env and restart the Docker stack, then re-run steps 5–6. With ngrok fixed domain this doesn't apply.

Cleanup

To shut down all local services and prepare for a fresh start:

Stop the Docker stack

docker compose down

This stops and removes all containers (redis, ext-proxy, extension-tee).

Full reset (start from scratch)

If you want to completely reset and follow the README from the beginning:

# Remove built images (forces rebuild)
docker compose down --rmi local

# Clear environment state
rm -f .env config/proxy/extension_proxy.toml

After a full reset, start again from Step 0.

Note: On-chain state (deployed contracts, registered extensions, registered TEEs) cannot be reset. Each fresh start will deploy a new InstructionSender contract and register a new extension. This is fine for testing — Coston2 is a testnet.

About

TEE-powered options market maker running as a Flare Compute Extension on Coston2

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages