Migration

Migrating from Dwellir

We serve Dwellir's gateway service — hyperliquid_l1_gateway.v2.HyperliquidL1Gateway — with identical wire semantics on our endpoint. Generated stubs, request messages, and decoding logic work unchanged; the migration is an endpoint string, an x-api-key header, and a validation run.

What stays the same

The full gateway surface, streaming and point-in-time. Nothing in this table requires a code change:

MethodTypeCarriesStatus
StreamBlocksstreamL1 blocks, Dwellir wire formatunchanged
StreamFillsstreamExecuted fills, Dwellir wire formatunchanged
StreamOrderbookSnapshotsstreamOrder-book snapshots, Dwellir wire formatunchanged
GetBlockunaryPoint-in-time block fetch by heightunchanged
GetFillsunaryPoint-in-time fills fetchunchanged
GetOrderBookSnapshotunaryPoint-in-time book snapshotunchanged

Reflection is enabled, so grpcurl … list will show the gateway alongside our native packages — see SDKs & protos.

The migration

  1. Swap the endpoint

    Replace the Dwellir endpoint with ours wherever the channel is built. TLS over HTTP/2, port 443:

    grpc endpointdiff
    removed: ENDPOINT = "your-current-dwellir-endpoint:443"
    added: ENDPOINT = "stream.hyperliquidrpc.com:443"
  2. Move auth to x-api-key metadata

    Authentication is an API key sent as gRPC metadata on each call — replace your previous credential mechanism with the x-api-key pair. Keys come from the console; see Authentication.

    client.pydiff
    removed: channel = grpc.secure_channel(ENDPOINT, grpc.ssl_channel_credentials())
    removed: gateway = HyperliquidL1GatewayStub(channel)
    removed: fills = gateway.StreamFills(request)
    added: channel = grpc.secure_channel(ENDPOINT, grpc.ssl_channel_credentials())
    added: gateway = HyperliquidL1GatewayStub(channel)
    added: fills = gateway.StreamFills(request, metadata=(("x-api-key", "YOUR_KEY"),))
  3. Check your channel options

    • Message size. StreamOrderbookSnapshots and GetOrderBookSnapshot responses can be large on liquid markets — set max receive message size to at least 256 MB.
    • Reconnects. Streams are long-lived but not eternal; reconnect with backoff. A DATA_LOSS status means your consumer fell behind — reconnect, and use the gateway's point-in-time GetFills / GetBlock (or native replay from height) to close the gap.
    channel = grpc.secure_channel(    "stream.hyperliquidrpc.com:443",    grpc.ssl_channel_credentials(),    options=[        # order-book snapshots can exceed default limits — allow >=256 MB        ("grpc.max_receive_message_length", 268_435_456),    ],)
  4. Validate side by side

    Run both endpoints in parallel before cutting over. Fills and blocks carry their height, so per-block equality is mechanical:

    validate.pypython
    # run both providers in parallel and diff fills per block.# uses the hyperliquid_l1_gateway.v2 stubs you already generated.import grpc OLD = "your-current-dwellir-endpoint:443"NEW = "stream.hyperliquidrpc.com:443" def fills(endpoint, metadata=()):    channel = grpc.secure_channel(        endpoint,        grpc.ssl_channel_credentials(),        options=[("grpc.max_receive_message_length", 268_435_456)],    )    gateway = HyperliquidL1GatewayStub(channel)    return gateway.StreamFills(StreamFillsRequest(), metadata=metadata) old = fills(OLD)                                          # existing authnew = fills(NEW, metadata=(("x-api-key", "YOUR_KEY"),))   # ours # align on a common block height, then compare per-block fill setsfor a, b in zip(old, new):    assert a.block_height == b.block_height, (a.block_height, b.block_height)    assert {(f.oid, f.px, f.sz) for f in a.fills} == \           {(f.oid, f.px, f.sz) for f in b.fills}    print("block", a.block_height, "ok:", len(a.fills), "fills") # point-in-time methods are easy to spot-check too:# gateway.GetOrderBookSnapshot(...) against both endpoints at one height

    Let it run through a busy period — funding intervals and a volatility burst are the interesting cases — then cut over.

What you gain

The gateway gets you migrated; the native packages on the same endpoint and key are why you move: