# 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](https://docs.ston.fi/developer-section/omniston/swap)
