Operator RFQ Pricing Server
gRPC server that generates EIP-712 signed price quotes for Tangle services and jobs. Operators run this alongside their blueprint node. Service consumers request quotes via gRPC, then submit them on-chain.
Two RFQ Modes
Service Creation RFQ (GetPrice)
Used with createServiceFromQuotes() on the Tangle contract. The operator quotes a totalCost for running an entire service instance for a given TTL.
Consumer → GetPrice(blueprint_id, ttl_blocks) → Operator
Operator → signs QuoteDetails{totalCost, blueprintId, ttlBlocks, securityCommitments, resourceCommitments}
Consumer → createServiceFromQuotes(blueprintId, [signedQuotes], config, callers, ttl)
Price is computed from the operator's resource pricing config (default_pricing.toml) and node benchmarks. The engine automatically benchmarks CPU/memory/storage/GPU and multiplies by configured rates.
Per-Job RFQ (GetJobPrice)
Used with submitJobFromQuote() on the Tangle contract. The operator quotes a specific price for a single job execution.
Consumer → GetJobPrice(service_id, job_index) → Operator
Operator → signs JobQuoteDetails{serviceId, jobIndex, price, timestamp, expiry}
Consumer → submitJobFromQuote(serviceId, jobIndex, inputs, [signedQuotes])
Price is looked up from the operator's per-job pricing config: a (service_id, job_index) → price_in_wei map.
Pricing Configuration
Resource pricing (default_pricing.toml)
Controls service creation quotes. Operators set per-resource rates in USD:
# Default rates for all blueprints
[]
= [
{ = "CPU", = 1, = 0.001 },
{ = "MemoryMB", = 1024, = 0.00005 },
{ = "StorageMB", = 1024, = 0.00002 },
{ = "GPU", = 1, = 0.005 },
]
# Override for a specific blueprint ID
[]
= [
{ = "CPU", = 1, = 0.0015 },
{ = "GPU", = 2, = 0.007 },
]
Supported resource kinds: CPU, MemoryMB, StorageMB, NetworkEgressMB, NetworkIngressMB, GPU, Request, Invocation, ExecutionTimeMS.
Per-job pricing (config/job_pricing.toml)
Controls job RFQ quotes. Each section is a service ID, keys are job indices, values are prices in wei (strings for large numbers):
# Service 1
[]
= "1000000000000000" # Job 0: 0.001 ETH
= "5000000000000000" # Job 1: 0.005 ETH
= "20000000000000000" # Job 6: 0.02 ETH (e.g. LLM prompt)
= "250000000000000000" # Job 7: 0.25 ETH (e.g. agent task)
Pass via CLI: --job-pricing-config config/job_pricing.toml or env JOB_PRICING_CONFIG_PATH. If not provided, GetJobPrice returns NOT_FOUND for all jobs.
For programmatic use (e.g. dynamic pricing in a custom operator binary), use PricingEngineService::with_job_pricing():
let job_config = load_job_pricing_from_toml?;
let service = with_job_pricing;
To enable subscription/event-driven GetPrice requests, attach a subscription config:
let service = service.with_subscription_pricing;
Operator config (operator.toml)
# RocksDB path for benchmark cache
= "data/pricing-engine"
# Benchmark settings
= 60 # seconds per benchmark run
= 5 # seconds between samples
# Operator keystore (k256 keypair for EIP-712 signing)
= "data/keystore"
# gRPC server
= "0.0.0.0"
= 50051
= 30 # seconds
= 256
# How long signed quotes remain valid
= 300
Running
OPERATOR_HTTP_RPC=https://rpc.tangle.tools \
OPERATOR_WS_RPC=wss://rpc.tangle.tools \
OPERATOR_TANGLE_CONTRACT=0x... \
OPERATOR_STAKING_CONTRACT=0x... \
OPERATOR_STATUS_REGISTRY_CONTRACT=0x... \
All CLI flags:
| Flag | Env | Description |
|---|---|---|
--config |
OPERATOR_CONFIG_PATH |
Path to operator.toml |
--pricing-config |
PRICING_CONFIG_PATH |
Resource pricing table (TOML) |
--job-pricing-config |
JOB_PRICING_CONFIG_PATH |
Per-job pricing table (TOML) |
--http-rpc-endpoint |
OPERATOR_HTTP_RPC |
Tangle EVM HTTP RPC endpoint |
--ws-rpc-endpoint |
OPERATOR_WS_RPC |
Tangle EVM WebSocket endpoint |
--blueprint-id |
OPERATOR_BLUEPRINT_ID |
Blueprint ID to watch for activations |
--service-id |
OPERATOR_SERVICE_ID |
Optional: fixed service ID to benchmark |
--tangle-contract |
OPERATOR_TANGLE_CONTRACT |
ITangle proxy contract address |
--staking-contract |
OPERATOR_STAKING_CONTRACT |
MultiAssetDelegation contract |
--status-registry-contract |
OPERATOR_STATUS_REGISTRY_CONTRACT |
OperatorStatusRegistry contract |
How It Works Internally
- Bootstrap — reads
operator.toml, loads pricing table, initializes RocksDB benchmark cache, derives operator Ethereum address from k256 keystore - Event ingestion — polls
ITangle::ServiceActivated/ServiceTerminatedlogs, enqueues benchmarks for each new activation - Benchmark — runs CPU/memory/storage/GPU benchmarks, caches profiles per blueprint
- RPC handling — verifies proof-of-work, computes price (service quotes use benchmarks + resource rates; job quotes use the
JobPricingConfigmap) - Signing — hashes ABI-encoded structs with EIP-712 (
TangleQuotedomain, version1), signs with k256 ECDSA
EIP-712 Signing Details
Both quote types use the same EIP-712 domain:
name: "TangleQuote"
version: "1"
chainId: <chain_id>
verifyingContract: <tangle_proxy_address>
Service quotes use QUOTE_TYPEHASH:
QuoteDetails(uint64 blueprintId,uint64 ttlBlocks,uint256 totalCost,uint64 timestamp,uint64 expiry,AssetSecurityCommitment[] securityCommitments,ResourceCommitment[] resourceCommitments)
Job quotes use JOB_QUOTE_TYPEHASH:
JobQuoteDetails(uint64 serviceId,uint8 jobIndex,uint256 price,uint64 timestamp,uint64 expiry)
For standalone signing without the full pricing engine, use JobQuoteSigner from blueprint-tangle-extra:
use ;
let signer = new;
let signed = signer.sign;
For Blueprint Developers
If your blueprint uses RFQ pricing (instead of or alongside fixed setJobEventRates):
- Embed or run the pricing engine — either integrate
PricingEngineServiceinto your operator binary or run it as a sidecar - Define job prices — populate
JobPricingConfigwith(service_id, job_index) → priceentries based on your blueprint's job types and cost structure - Expose operator config — let operators tune prices via your blueprint's config file (model costs, resource multipliers, margins)
- Fixed rates vs RFQ — you can use both: set
setJobEventRates()on-chain for standard pricing, and support RFQ for jobs where operators want custom pricing (premium models, large batches, etc.)
The on-chain protocol handles verification, replay protection, payment collection, and distribution. See tnt-core/docs/PRICING.md for the full protocol-level pricing reference.
Testing
# Signer roundtrip + EIP-712 compatibility
# RPC server unit tests (18 tests covering success, errors, signature verification)
# All tests
Security
- Proof-of-work — SHA-256 challenge prevents gRPC abuse
- EIP-712 signatures — quotes are signed with the operator's k256 key and verified on-chain by the Tangle contract
- Replay protection — each quote digest is marked as used on-chain after submission
- Expiry — quotes have explicit
expirytimestamps; themaxQuoteAgeprotocol parameter (default 1 hour) rejects stale quotes - Keystore keys never leave disk. Keep keystore path and contract addresses private.