Resolvers (How to become a resolver)
This guide explains how to become a resolver (Market Maker) in the Omniston protocol and integrate with its gRPC API.
Overview
A resolver is a service that provides token exchange rates and executes trades. To become a resolver, you need to:
Register by obtaining a Soul-Bound Token (SBT)
Connect to the gRPC API
Handle quote requests and trades
Registration Process
Step 1: Generate Keys
First, generate one or more Ed25519 key pairs that will be used for authentication:
Step 2: Prepare Metadata
Create a JSON document containing:
Your resolver name
Your public key(s)
Example:
{
"name": "MyResolver",
"publicKeys": [
"b951254b184fae6906a61ab01e37296fbb28961494cacf7befac9f638fcfe40c",
"9397ddd52a0d6033da4a32e654b4afbddcc5d832575e396c0a6f5b213faa199f"
]
}
Step 3: Submit Registration
Your metadata will be stored in an SBT NFT collection
You'll receive your SBT stake address for API authentication
Connecting to the API
Authentication
Create a connection payload:
message ConnectPayload {
uint64 connect_timestamp = 1; // Current timestamp in milliseconds
string stake_address = 2; // Your SBT stake address
}
Sign the payload:
Serialize the
ConnectPayload
to bytesSign using your Ed25519 private key
Base64 encode the signature
Send a
ConnectRequest
:
message ConnectRequest {
bytes payload = 1; // Serialized ConnectPayload
bytes public_key = 2; // Your Ed25519 public key
bytes signature = 3; // Signature of the payload
}
Example connection request:
{
"connect": {
"payload": "CPKnlt2xYxIwRVFDWFNzMnhaMmRoazlUQXh6R3pYcmEyRWJHX1MyU3F5TjhUZmk2Zko4MkVZaVZq",
"public_key": "TAPsnPvWhlOvxEK19rONv4sueMeQzOzlrrIFUOKsi34=",
"signature": "us7nMd9wmUOkuPk0otg6dvUojZwj8VcyXU6HD13BDQhVrzV8sKgyXtKze+9+j9FC1Ghxkx7Jo5FIDeE8ljbADQjyp5bdsWMSMEVRQ1hTczJ4WjJkaGs5VEF4ekd6WHJhMkViR19TMlNxeU44VGZpNmZKODJFWWlWag=="
},
"seqno": 1
}
Message Flow
The resolver API uses a bidirectional gRPC stream. After connecting:
1. Incoming Events
You'll receive these events from the server:
message ResolverEvent {
oneof mux {
uint64 seqno = 1; // Server-generated event ID
uint64 reply_to = 2; // References your request seqno
}
oneof event {
QuoteRequestedEvent quote_requested = 10;
QuoteRequestCancelledEvent quote_request_cancelled = 11;
QuoteAcceptedEvent quote_accepted = 20;
QuoteRejectedEvent quote_rejected = 21;
QuoteInvalidatedEvent quote_invalidated = 22;
KeepAlive keep_alive = 100;
}
}
Key events:
QuoteRequestedEvent
: New quote request from a traderQuoteRequestCancelledEvent
: Trader cancelled their requestQuoteAcceptedEvent
: Your quote was acceptedQuoteRejectedEvent
: Your quote was rejectedQuoteInvalidatedEvent
: Confirmation of quote invalidation
2. Outgoing Requests
You can send these requests to the server:
message ResolverRequest {
oneof mux {
uint64 seqno = 1; // Your request ID
uint64 reply_to = 2; // References server event seqno
}
oneof request {
ConnectRequest connect = 10;
UpdateQuoteRequest update_quote = 11;
InvalidateQuoteRequest invalidate_quote = 12;
}
}
Key requests:
UpdateQuoteRequest
: Provide or update a quoteInvalidateQuoteRequest
: Cancel a previously provided quote
Quote Flow
1. Receiving Quote Requests
When you receive a QuoteRequestedEvent
:
message QuoteRequestedEvent {
string rfq_id = 1; // Quote request ID
QuoteRequest quote_request = 2; // The actual request
uint32 protocol_fee_bps = 3; // Protocol fee in basis points
sint64 request_timestamp = 4; // When request was made
sint64 quote_validity_timeout = 5; // How long quote should be valid
sint64 deposit_settling_delay = 6; // Min delay before settlement
sint64 resolve_timeout = 7; // Max time to complete trade
}
2. Providing Quotes
Respond with an UpdateQuoteRequest
:
message UpdateQuoteRequest {
string rfq_id = 1; // From QuoteRequestedEvent
string bid_units = 2; // Amount trader pays
string ask_units = 3; // Amount trader receives
uint64 trade_start_deadline = 4; // Quote expiration time
oneof params {
ResolverSwapParams swap = 20; // For swap settlement
ResolverEscrowParams escrow = 21; // For escrow settlement
ResolverHtlcParams htlc = 22; // For HTLC settlement
}
}
For swap settlement, include route information:
message ResolverSwapParams {
repeated SwapRoute routes = 1; // Possible swap paths
}
Important: When specifying the protocol in a SwapChunk, the gRPC API expects it as one of the strings "StonFiV1", "StonFiV2", "DeDust", or "TonCo". Currently, only extra_version = 1
is supported.
3. Quote Lifecycle
You receive
QuoteRequestedEvent
You send
UpdateQuoteRequest
The server validates your quote and responds with:
QuoteAcceptedEvent
: Your quote was validated and accepted, includesquote_id
.QuoteRejectedEvent
: Your quote was rejected. The event includes acode
field from theQuoteRejectedCode
enum. Possible reasons include:UNDEFINED
(0) - Uncategorized errorINVALID_PARAMETERS
(1) - Invalid value of a parameterINVALID_AMOUNTS
(2) - Asset amount restrictions don't pass RFQ requirementsROUTE_PROHIBITED
(3) - Route uses a prohibited intermediate assetPOOL_PROHIBITED
(4) - Route uses a unverified or prohibited liquidity poolEMULATION_RESULT_MISMATCH
(5) - Transaction emulation produced a result different from the quoteINTERNAL_ERROR
(101) - Server errors
At any time, you can:
Send new
UpdateQuoteRequest
to update the quoteSend
InvalidateQuoteRequest
to cancel the quote
Best Practices
Sequence Numbers
Use monotonically increasing
seqno
for your requestsMatch
reply_to
with eventseqno
when respondingTrack request/response correlation using
seqno
Quote Management
Honor your quotes until
trade_start_deadline
Invalidate quotes you can't fulfill
Include all fees in your quoted amounts
Error Handling
Reconnect on connection loss
Handle all event types
Log rejected quotes for monitoring
Performance
Maintain a single gRPC connection
Process events asynchronously
Buffer outgoing requests
API Endpoints
Production:
gRPC:
https://omni-grpc.ston.fi
Sandbox:
Demo site:
https://omniston-sandbox.ston.fi/
WS API:
wss://omni-ws-sandbox.ston.fi
gRPC API:
https://omni-grpc-sandbox.ston.fi
See Also
Last updated