Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ const unsubscribe = sdk.subscriptions.onEventsCreated(

## Edge Subscriptions

Pass `{ useEdge: true }` as the third argument to a generated subscription method (or the fourth argument to `sdk.subscribe`) to use a regional edge node. Solana (`1399811149`) routes to `wss://fra.edge.codex.io/graphql`; Robinhood Chain (`4663`) routes to `wss://iad.edge.codex.io/graphql`. Connections are created on demand and reused per endpoint.
Pass `{ useEdge: true }` as the third argument to a generated subscription method (or the fourth argument to `sdk.subscribe`) to use a regional edge node. Solana (`1399811149`) routes to `wss://fra.edge.codex.io/graphql`; Robinhood Chain (`4663`) and Base (`8453`) route to `wss://iad.edge.codex.io/graphql`. Connections are created on demand and reused per endpoint.

```typescript
import { Codex, EventCommitmentLevel } from "@codex-data/sdk";
Expand All @@ -330,7 +330,7 @@ const unsubscribe = sdk.subscriptions.onTokenEventsCreated(

Edge supports `onEventsCreated`, `onTokenEventsCreated`, `onBarsUpdated`, and `onTokenBarsUpdated`. The commitment levels must include `Processed`, and `onBarsUpdated` requires an explicit `quoteToken`. Each subscription must target one supported chain. Subscriptions without `useEdge` use the main endpoint.

The lightweight `withEdge` helper from `@codex-data/sdk/edge` uses the same routing. For a standalone Robinhood connection, pass `url: ROBINHOOD_EDGE_URL` to `createEdgeClient`, importing both from `@codex-data/sdk/edge`. The SDK's `apiEdgeUrl` and the helper's `edgeUrl` override routing for all chains.
The lightweight `withEdge` helper from `@codex-data/sdk/edge` uses the same routing. For a standalone Virginia connection, pass `url: ROBINHOOD_EDGE_URL` or `url: BASE_EDGE_URL` to `createEdgeClient`, importing both from `@codex-data/sdk/edge`. The SDK's `apiEdgeUrl` and the helper's `edgeUrl` override routing for all chains.

## Custom Queries: Request Only the Fields You Need

Expand Down
1 change: 1 addition & 0 deletions src/edge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export {
createClient,
createEdgeClient,
DEFAULT_EDGE_URL,
BASE_EDGE_URL,
ROBINHOOD_EDGE_URL,
withEdge,
} from "./sdk/edge";
Expand Down
26 changes: 23 additions & 3 deletions src/sdk/edge.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { type ClientOptions, createClient } from "graphql-ws";

import {
BASE_EDGE_URL,
createEdgeClient,
DEFAULT_EDGE_URL,
ROBINHOOD_EDGE_URL,
Expand Down Expand Up @@ -276,6 +277,7 @@ it.each([
for (const [networkId, url] of [
[1399811149, DEFAULT_EDGE_URL],
[4663, ROBINHOOD_EDGE_URL],
[8453, BASE_EDGE_URL],
]) {
const client = createClientWithEdge({
url: "wss://main.example/graphql",
Expand Down Expand Up @@ -305,7 +307,7 @@ it.each([
{ levels: ["Processed", "Finalized"] },
{ levels: ["Processed", null] },
{ levels: ["Processed", 1] },
{ input: { networkId: 8453 } },
{ input: { networkId: 1 } },
{ input: { networkId: "1399811149" } },
{ input: { tokenAddress: "token" } },
])(
Expand All @@ -330,7 +332,7 @@ it.each([
`query { onEventsCreated(networkId: 1399811149, commitmentLevel: [Processed]) { events { transactionHash } } }`,
`subscription { onPriceUpdated(networkId: 1399811149, commitmentLevel: [Processed]) { priceUsd } }`,
`subscription { onEventsCreatedByMaker(input: {networkId: 1399811149}, commitmentLevel: [Processed]) { events { transactionHash } } }`,
`subscription { onEventsCreated(id: "pair:8453", commitmentLevel: [Processed]) { events { transactionHash } } }`,
`subscription { onEventsCreated(id: "pair:1", commitmentLevel: [Processed]) { events { transactionHash } } }`,
`subscription { onEventsCreated(id: "pair:1399811149", networkId: 1, commitmentLevel: [Processed]) { events { transactionHash } } }`,
`subscription { onEventsCreated(id: "pair:4663", networkId: 1399811149, commitmentLevel: [Processed]) { events { transactionHash } } }`,
`subscription { onEventsCreated(id: "pair:1399811149", networkId: 4663, commitmentLevel: [Processed]) { events { transactionHash } } }`,
Expand All @@ -350,7 +352,7 @@ it.each([
expect(transports).toHaveLength(1);
});

it.each([1399811149, 4663])(
it.each([1399811149, 4663, 8453])(
"rejects implicit pair quote selection for network %s instead of letting edge relay it through Oregon",
(networkId) => {
const client = createClientWithEdge({ url: "wss://main.example/graphql" });
Expand Down Expand Up @@ -452,3 +454,21 @@ it("validates serialized variables rather than a misleading toJSON value", () =>
).toThrow("useEdge requires");
expect(transports).toHaveLength(1);
});

it("reuses the Virginia transport for separate Base and Robinhood operations", async () => {
const client = createClientWithEdge({ url: "wss://main.example/graphql" });
for (const networkId of [4663, 8453]) {
client.subscribe(
{
query: events,
variables: { ...variables, input: { ...variables.input, networkId } },
},
sink,
{ useEdge: true },
);
}
expect(transports).toHaveLength(2);
expect(transports[1].options.url).toBe(BASE_EDGE_URL);
expect(transports[1].subscribe).toHaveBeenCalledTimes(2);
await client.dispose();
});
8 changes: 5 additions & 3 deletions src/sdk/edge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,24 @@ import {

import {
assertEdgeSubscription,
BASE_NETWORK_ID,
ROBINHOOD_NETWORK_ID,
} from "./edgeSubscription";

export { createClient };

export const DEFAULT_EDGE_URL = "wss://fra.edge.codex.io/graphql";
export const ROBINHOOD_EDGE_URL = "wss://iad.edge.codex.io/graphql";
export const BASE_EDGE_URL = ROBINHOOD_EDGE_URL;

export function edgeUrlForNetwork(networkId: number): string {
return networkId === ROBINHOOD_NETWORK_ID
return networkId === ROBINHOOD_NETWORK_ID || networkId === BASE_NETWORK_ID
? ROBINHOOD_EDGE_URL
: DEFAULT_EDGE_URL;
}

export interface SubscriptionOptions {
/** Experimental: use edge for supported Solana or Robinhood streams whose commitments include Processed. */
/** Experimental: use edge for supported Solana, Robinhood or Base streams whose commitments include Processed. */
useEdge?: boolean;
}

Expand All @@ -45,7 +47,7 @@ export interface ClientWithEdge extends Client {
}

export interface EdgeClientOptions extends Omit<ClientOptions, "url"> {
/** Defaults to the Solana edge endpoint. Use ROBINHOOD_EDGE_URL for Robinhood. */
/** Defaults to the Solana edge endpoint. Use ROBINHOOD_EDGE_URL or BASE_EDGE_URL for Virginia. */
url?: ClientOptions["url"];
}

Expand Down
7 changes: 5 additions & 2 deletions src/sdk/edgeSubscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { SubscribePayload } from "graphql-ws";

const SOLANA_NETWORK_ID = 1399811149;
export const ROBINHOOD_NETWORK_ID = 4663;
export const BASE_NETWORK_ID = 8453;
const SUPPORTED_OPERATIONS = new Set([
"onEventsCreated",
"onTokenEventsCreated",
Expand All @@ -14,7 +15,7 @@ const SUPPORTED_OPERATIONS = new Set([
export function assertEdgeSubscription(payload: SubscribePayload): number {
function fail(): never {
throw new Error(
"useEdge requires a supported Solana or Robinhood subscription with commitmentLevel including Processed",
"useEdge requires a supported Solana, Robinhood or Base subscription with commitmentLevel including Processed",
);
}
const document = parse(payload.query);
Expand Down Expand Up @@ -74,7 +75,9 @@ export function assertEdgeSubscription(payload: SubscribePayload): number {
collect(args);
const networkId = networks[0];
if (
(networkId !== SOLANA_NETWORK_ID && networkId !== ROBINHOOD_NETWORK_ID) ||
(networkId !== SOLANA_NETWORK_ID &&
networkId !== ROBINHOOD_NETWORK_ID &&
networkId !== BASE_NETWORK_ID) ||
networks.some((network) => network !== networkId)
)
fail();
Expand Down
Loading