Skip to content

API Reference

The Aspens Market Stack exposes three gRPC services. Source of truth is the protos repository:

ServiceProto fileUsed by
ArborterServicearborter.protoTrading: orders, cancels, streams
ConfigServicearborter_config.protoRead-only stack configuration
AuthServicearborter_auth.protoAdmin login, JWT issuance

This page documents the trading service. The full proto bundle is at github.com/aspensprotocol/protos.

ArborterService

service ArborterService {
  rpc SendOrder   (SendOrderRequest)   returns (SendOrderResponse);
  rpc CancelOrder (CancelOrderRequest) returns (CancelOrderResponse);
  rpc Trades      (TradeRequest)       returns (stream Trade);
  rpc Orderbook   (OrderbookRequest)   returns (stream OrderbookEntry);
}

SendOrder

message SendOrderRequest {
  Order order = 1;
  bytes signature_hash = 2;          // EIP-191 (EVM) / Ed25519 (Solana) over the encoded Order
  OrderAuthorization authorization = 3;  // required — see below
}

The arborter authenticates order entry by the signature_hash envelope and rejects requests without an authorization. (The legacy on-chain lock path — and the old GaslessAuthorization message — were removed.)

message OrderAuthorization {
  string order_id  = 1;  // 0x-prefixed 32-byte hex; SDK-derived canonical id
  string amount_in = 2;  // committed input amount, base units, decimal-string u128
}

order_id must match aspens::orders::derive_order_id(...) — the same SHA-256 derivation the arborter recomputes server-side. Drift here is silently fatal (the arborter uses this id throughout matching and settlement).

message Order {
  Side          side                  = 1;  // SIDE_BID | SIDE_ASK
  string        quantity              = 2;
  optional string price               = 3;  // present = LIMIT, absent = MARKET
  string        market_id             = 4;  // base_chain_id::token_addr::quote_chain_id::token_addr
  string        base_account_address  = 5;
  string        quote_account_address = 6;
  ExecutionType execution_type        = 7;  // _UNSPECIFIED (=DIRECT) | _DISCRETIONARY
  repeated uint64 matching_order_ids  = 8;  // discretionary only
}
 
enum Side          { SIDE_UNSPECIFIED = 0; SIDE_BID = 1; SIDE_ASK = 2; }
enum ExecutionType { EXECUTION_TYPE_UNSPECIFIED = 0; EXECUTION_TYPE_DISCRETIONARY = 1; }
message SendOrderResponse {
  bool                       order_in_book      = 1;
  optional Order             order              = 2;  // unfilled remainder, if any
  repeated Trade             trades             = 3;  // matches produced by this submit
  repeated TransactionHash   transaction_hashes = 4;
  repeated OrderbookEntry    current_orderbook  = 5;
  uint64                     order_id           = 6;  // engine-internal id
}

CancelOrder

message CancelOrderRequest {
  OrderToCancel order = 1;
  bytes signature_hash = 2;
}
 
message OrderToCancel {
  string market_id    = 1;
  Side   side         = 2;
  string token_address = 3;
  uint64 order_id     = 4;  // engine-internal id from SendOrderResponse.order_id
}

Trades / Orderbook (streams)

message TradeRequest {
  bool   continue_stream         = 1;
  string market_id               = 2;
  optional bool   historical_closed_trades = 3;
  optional string filter_by_trader        = 4;
}
 
message OrderbookRequest {
  bool   continue_stream         = 1;
  string market_id               = 2;
  optional bool   historical_open_orders = 3;
  optional string filter_by_trader      = 4;
}

Stream entries:

message OrderbookEntry {
  uint64 timestamp           = 1;
  uint64 order_id            = 2;
  string price               = 3;
  string quantity            = 4;
  Side   side                = 5;
  string maker_base_address  = 6;
  string maker_quote_address = 7;
  string market_id           = 8;
  OrderState state           = 9;  // PENDING | CONFIRMED | MATCHED | CANCELED | SETTLED
}
 
message Trade {
  uint64    timestamp            = 1;
  string    price                = 2;  // settled, net of fees
  string    qty                  = 3;
  string    maker_id             = 4;
  string    taker_id             = 5;
  string    maker_base_address   = 6;
  string    maker_quote_address  = 7;
  string    taker_base_address   = 8;
  string    taker_quote_address  = 9;
  TradeRole buyer_is             = 10; // MAKER | TAKER
  TradeRole seller_is            = 11;
  uint64    order_hit            = 12;
}

Order request flow

Arborter Send Order Request Flow

  1. Client signs the encoded Order (EIP-191 on EVM, Ed25519 on Solana) and sends SendOrderRequest with the OrderAuthorization { order_id, amount_in }. No on-chain transaction.
  2. Arborter verifies the envelope signature with the curve derived from the chain architecture and verifies authorization.order_id against its own derive_order_id.
  3. The engine reserves amount_in against the user's off-chain ledger balance; if matchable, Trades are produced and balances move in the ledger immediately (no on-chain tx, no block wait).
  4. Any unmatched remainder rests in the orderbook against that ledger reservation.
  5. SendOrderResponse returns immediately; Trades and Orderbook subscribers are notified in parallel.
  6. Separately, a background settler folds accumulated net deltas on-chain in batches (MidribV3.settleBatch on EVM, settle_batch on Solana).