# 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:

1. Register by obtaining a Soul-Bound Token (SBT)
2. Connect to the gRPC API
3. 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:

{% hint style="success" %}
[JavaScript implementation](https://github.com/3nsoft/ecma-nacl)
{% endhint %}

{% hint style="success" %}
[Rust implementation](https://github.com/3nsoft/rust-nacl)
{% endhint %}

{% hint style="info" %}
For other languages, you can use any standard Ed25519 implementation library available in your preferred programming language
{% endhint %}

### Step 2: Prepare Metadata

Create a JSON document containing:

* Your resolver name
* Your public key(s)

Example:

```json
{
  "name": "MyResolver",
  "publicKeys": [
    "b951254b184fae6906a61ab01e37296fbb28961494cacf7befac9f638fcfe40c",
    "9397ddd52a0d6033da4a32e654b4afbddcc5d832575e396c0a6f5b213faa199f"
  ]
}
```

### Step 3: Submit Registration

{% hint style="success" %}
[Link to google form](https://forms.gle/LxY2GphQdyhLu3L39)
{% endhint %}

1. Your metadata will be stored in an SBT NFT collection
2. You'll receive your SBT stake address for API authentication

## Connecting to the API

### Authentication

1. Create a connection payload:

```protobuf
message ConnectPayload {
  uint64 connect_timestamp = 1;  // Current timestamp in milliseconds
  string stake_address = 2;      // Your SBT stake address
}
```

2. Sign the payload:

* Serialize the `ConnectPayload` to bytes
* Sign using your Ed25519 private key
* Base64 encode the signature

3. Send a `ConnectRequest`:

```protobuf
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:

```json
{
  "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:

```protobuf
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 trader
* `QuoteRequestCancelledEvent`: Trader cancelled their request
* `QuoteAcceptedEvent`: Your quote was accepted
* `QuoteRejectedEvent`: Your quote was rejected
* `QuoteInvalidatedEvent`: Confirmation of quote invalidation

### 2. Outgoing Requests

You can send these requests to the server:

```protobuf
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 quote
* `InvalidateQuoteRequest`: Cancel a previously provided quote

## Quote Flow

### 1. Receiving Quote Request Acknowledgment

When a trader sends a quote request, they will first receive a `QuoteRequestAck` containing the `rfq_id` that uniquely identifies their request.

### 2. Receiving Quote Requests

As a resolver, when you receive a `QuoteRequestedEvent`:

```protobuf
message QuoteRequestedEvent {
  string rfq_id = 1;                      // Quote request ID (SHA-256 hex string)
  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
}
```

### 3. Providing Quotes

Respond with an `UpdateQuoteRequest`:

```protobuf
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:

```protobuf
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.

### 4. Quote Lifecycle

1. You receive `QuoteRequestedEvent`
2. You send `UpdateQuoteRequest`
3. The server validates your quote and responds with:
   * `QuoteAcceptedEvent`: Your quote was validated and accepted, includes `quote_id`.
   * `QuoteRejectedEvent`: Your quote was rejected. The event includes a `code` field from the `QuoteRejectedCode` enum. Possible reasons include:
     * `UNDEFINED` (0) - Uncategorized error
     * `INVALID_PARAMETERS` (1) - Invalid value of a parameter
     * `INVALID_AMOUNTS` (2) - Asset amount restrictions don't pass RFQ requirements
     * `ROUTE_PROHIBITED` (3) - Route uses a prohibited intermediate asset
     * `POOL_PROHIBITED` (4) - Route uses a unverified or prohibited liquidity pool
     * `EMULATION_RESULT_MISMATCH` (5) - Transaction emulation produced a result different from the quote
     * `INTERNAL_ERROR` (101) - Server errors
4. At any time, you can:
   * Send new `UpdateQuoteRequest` to update the quote
   * Send `InvalidateQuoteRequest` to cancel the quote

## Best Practices

1. **Sequence Numbers**
   * Use monotonically increasing `seqno` for your requests
   * Match `reply_to` with event `seqno` when responding
   * Track request/response correlation using `seqno`
2. **Quote Management**
   * Honor your quotes until `trade_start_deadline`
   * Invalidate quotes you can't fulfill
   * Include all fees in your quoted amounts
3. **Error Handling**
   * Reconnect on connection loss
   * Handle all event types
   * Log rejected quotes for monitoring
4. **Performance**
   * Maintain a single gRPC connection
   * Process events asynchronously
   * Buffer outgoing requests

## API Endpoints

**Production**:

* WebSocket (JSON-RPC): `wss://omni-ws.ston.fi`
* gRPC (TLS): `omni-grpc.ston.fi:443`
* Demo site: [omniston.ston.fi](https://omniston.ston.fi)

**Sandbox**:

* WebSocket (JSON-RPC): `wss://omni-ws-sandbox.ston.fi`
* gRPC (TLS): `omni-grpc-sandbox.ston.fi:443`
* Demo site: [omniston-sandbox.ston.fi](https://omniston-sandbox.ston.fi)

> ⚠️ gRPC endpoints are **host:port** targets over TLS and are not HTTP REST URLs. Do not use `https://...` as if it were a REST API.

## See Also

* [Swap Protocol Documentation](/developer-section/omniston/swap.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.ston.fi/developer-section/omniston/resolvers/guide.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
