@gibs/bridge-indexer 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2026 Gibs Finance
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # @gibs/bridge-indexer
2
+
3
+ Reads a Gibs Finance bridge indexer. It gives you the transfer archive, delivery receipts, and live crossing status. It only reads. It never signs or broadcasts.
4
+
5
+ ## Endpoint
6
+
7
+ The package reads `https://next-indexer.gibs.finance` by default. This is the owner's choice "for now". The next indexer follows the current schema.
8
+
9
+ Importing the package has no side effects. The package creates its GraphQL client on first use.
10
+
11
+ To read another indexer, call `setIndexerEndpoint` before the first query:
12
+
13
+ ```ts
14
+ import { setIndexerEndpoint, indexerEndpoint, DEFAULT_INDEXER_ENDPOINT } from '@gibs/bridge-indexer'
15
+
16
+ setIndexerEndpoint('http://localhost:42069/graphql')
17
+ indexerEndpoint() // the address now in force
18
+ ```
19
+
20
+ The indexer serves GraphQL at `/` and at `/graphql`. It serves `GET /version` with the indexer package version, the Ponder schema name, and the git commit when the deploy sets `GIT_SHA`. The indexer no longer serves raw SQL.
21
+
22
+ Production `https://indexer.gibs.finance` may lag the schema in this package. If a query fails there with an unknown field, point the package at the next indexer.
23
+
24
+ ## Known defects in the production data
25
+
26
+ A batched re-index will fix these. Until it runs, work around them.
27
+
28
+ - Five relations are always `null`. Do not read them:
29
+ - `AMBBridge.validatorContract`
30
+ - `Omnibridge.ambBridge`
31
+ - `Omnibridge.validatorContract`
32
+ - `UserRequest.destinationAMBBridge`
33
+ - `UserRequest.destinationOmnibridge`
34
+ - The joins between a request and its `Completion` or `Delivery` work in one direction only. Query `Completion` or `Delivery` by `messageHash` instead of expecting the join on the request.
35
+ - A message for a native pathway records the ERC-20 omnibridge, not the native one.
36
+ - Read `delivered` (a boolean on the request). Do not read `delivery`. The `delivery` relation can be `null` for a request that landed.
37
+ - Hex filters are lowercase. A checksummed address in a `where` clause matches nothing. Lowercase every address and hash first.
38
+
39
+ ## Units of fee fields
40
+
41
+ - `FeeUpdate.fee` is a rate. It is a fraction scaled by 1e18. `10n ** 18n` means 100 percent. The amount is `amount * fee / 10n ** 18n`.
42
+ - `UserRequest.feeAmount` is the fee the omnibridge took. It is in base units of the origin token. `null` means no fee log was seen, which in practice means the fee was zero.
43
+ - Read `feeAmount` for the fee actually taken. `feeUpdate.fee` is the rate that was configured at the time. That is a different fact.
44
+ - `BigInt` fields arrive as decimal strings. `Numeric` fields arrive as a number or a string.
45
+
46
+ ## Schema snapshot and codegen
47
+
48
+ `schema.graphql` is a committed snapshot of the indexer schema. `src/graphql.ts` is generated from it. Codegen never contacts a live host.
49
+
50
+ | Script | What it does |
51
+ | --- | --- |
52
+ | `yarn codegen` | Regenerates `src/graphql.ts` from `schema.graphql`. Offline. |
53
+ | `yarn schema:update` | Introspects the live next indexer and rewrites `schema.graphql`. |
54
+ | `yarn schema:check` | Exits 1 when the live schema differs from the snapshot. Not run in continuous integration. Run it by hand before a release. |
55
+
56
+ To follow a schema change, run `yarn schema:update`, then `yarn codegen`, then the tests. A test fails when the snapshot and the generated types disagree. A schema change cannot ship without regenerating.
57
+
58
+ `schema:update` and `schema:check` accept an address as a second word, for example `node scripts/schema-snapshot.mjs check https://indexer.gibs.finance/graphql`.
59
+
60
+ ## License
61
+
62
+ ISC. See `LICENSE`.
@@ -0,0 +1,58 @@
1
+ import type { BridgeKey } from '@gibs/bridge-sdk/types';
2
+ import type { Block, Hex } from 'viem';
3
+ export declare const bridgeStatuses: {
4
+ readonly SUBMITTED: "SUBMITTED";
5
+ readonly MINED: "MINED";
6
+ readonly FINALIZED: "FINALIZED";
7
+ readonly VALIDATING: "VALIDATING";
8
+ readonly AFFIRMED: "AFFIRMED";
9
+ readonly DELIVERED: "DELIVERED";
10
+ };
11
+ export type BridgeStatus = keyof typeof bridgeStatuses;
12
+ export type LiveBridgeStatusParams = {
13
+ hash: Hex;
14
+ ticker: Block;
15
+ bridgeKey: BridgeKey;
16
+ };
17
+ export type ContinuedLiveBridgeStatusParams = LiveBridgeStatusParams & {
18
+ status: BridgeStatus;
19
+ statusIndex: number;
20
+ receipt?: {
21
+ blockNumber: bigint;
22
+ transactionHash: Hex;
23
+ };
24
+ finalizedBlock: {
25
+ number: bigint;
26
+ };
27
+ messageId?: Hex;
28
+ deliveredHash?: Hex;
29
+ count?: number;
30
+ requiredSignatures?: number | null;
31
+ /** null = main bridge; 'gas+' | 'fixed' | 'percentage' = gibs-routed */
32
+ deliveryType?: string | null;
33
+ /**
34
+ * The fields a manual release needs to build its calldata, present only
35
+ * once the live poll has resolved a `messageId` (stage two). A history row
36
+ * replayed through `createBridgeStatusFromHistory` never sets these — that
37
+ * path only feeds `bridgeETA.calculateETA`, which reads `status` alone.
38
+ */
39
+ release?: {
40
+ requestType: string;
41
+ encodedData: Hex;
42
+ signatures: string[];
43
+ destinationAmbAddress: Hex;
44
+ };
45
+ };
46
+ /**
47
+ * The ordered list of bridge statuses, mirroring insertion order of
48
+ * {@link bridgeStatuses}. Its index positions encode lifecycle progression so a
49
+ * later status compares greater than an earlier one.
50
+ */
51
+ export declare const statusList: ("SUBMITTED" | "MINED" | "FINALIZED" | "VALIDATING" | "AFFIRMED" | "DELIVERED")[];
52
+ /**
53
+ * Returns the lifecycle position of a bridge status within {@link statusList}.
54
+ * Used to compare two statuses by progression rather than by name.
55
+ * @param status - the status to locate
56
+ * @returns the zero-based index of the status, or -1 when unknown
57
+ */
58
+ export declare const statusToIndex: (status: BridgeStatus) => number;
@@ -0,0 +1,23 @@
1
+ export const bridgeStatuses = {
2
+ SUBMITTED: 'SUBMITTED',
3
+ MINED: 'MINED',
4
+ FINALIZED: 'FINALIZED',
5
+ VALIDATING: 'VALIDATING',
6
+ AFFIRMED: 'AFFIRMED',
7
+ DELIVERED: 'DELIVERED',
8
+ };
9
+ /**
10
+ * The ordered list of bridge statuses, mirroring insertion order of
11
+ * {@link bridgeStatuses}. Its index positions encode lifecycle progression so a
12
+ * later status compares greater than an earlier one.
13
+ */
14
+ export const statusList = Object.values(bridgeStatuses);
15
+ /**
16
+ * Returns the lifecycle position of a bridge status within {@link statusList}.
17
+ * Used to compare two statuses by progression rather than by name.
18
+ * @param status - the status to locate
19
+ * @returns the zero-based index of the status, or -1 when unknown
20
+ */
21
+ export const statusToIndex = (status) => {
22
+ return statusList.indexOf(status);
23
+ };
@@ -0,0 +1,49 @@
1
+ import { GraphQLClient } from 'graphql-request';
2
+ /**
3
+ * WHICH INDEXER THIS PACKAGE READS, AND HOW A CONSUMER CHANGES IT.
4
+ *
5
+ * The interface this code came from held the address as a build-time constant
6
+ * substituted at bundle time, which is the right shape for one application and
7
+ * the wrong shape for a library: a package cannot be rebuilt per consumer, and
8
+ * somebody running against their own indexer, a fork, or a local Ponder has no
9
+ * way to say so.
10
+ *
11
+ * So the address is state with a setter rather than a constant. ONE client is
12
+ * held rather than one per call because `GraphQLClient` is a thin wrapper whose
13
+ * construction is not free, and every query in this package goes to the same
14
+ * host.
15
+ *
16
+ * THE DEFAULT IS THE NEXT INDEXER, "for now" (owner decision), so a consumer who
17
+ * configures nothing still reads real data rather than failing against an empty
18
+ * address. It tracks the current schema; production `indexer.gibs.finance` may
19
+ * lag it.
20
+ *
21
+ * NOTHING RUNS AT MODULE LOAD. The client is created on first use, so importing
22
+ * this package has no side effects.
23
+ */
24
+ export declare const DEFAULT_INDEXER_ENDPOINT = "https://next-indexer.gibs.finance";
25
+ /**
26
+ * Points this package at an indexer.
27
+ *
28
+ * Call it once, before any query. Calling it again drops the cached client, which
29
+ * abandons nothing — a request already in flight keeps the client that issued
30
+ * it and settles normally.
31
+ *
32
+ * Returns early on an unchanged address so a consumer may call this from a
33
+ * render or an effect without discarding a warm client on every pass.
34
+ *
35
+ * @param url - the indexer's GraphQL address.
36
+ */
37
+ export declare const setIndexerEndpoint: (url: string) => void;
38
+ /** The indexer address currently in force. */
39
+ export declare const indexerEndpoint: () => string;
40
+ /**
41
+ * The client every query in this package uses.
42
+ *
43
+ * A FUNCTION RATHER THAN THE CLIENT ITSELF, so a module that imports this at
44
+ * load time still sees a later {@link setIndexerEndpoint}. Exporting the value
45
+ * would freeze whichever host happened to be configured at the moment the
46
+ * importing module was first evaluated — which, for a consumer that configures
47
+ * the address during startup, is reliably the wrong one.
48
+ */
49
+ export declare const indexerClient: () => GraphQLClient;
@@ -0,0 +1,59 @@
1
+ import { GraphQLClient } from 'graphql-request';
2
+ /**
3
+ * WHICH INDEXER THIS PACKAGE READS, AND HOW A CONSUMER CHANGES IT.
4
+ *
5
+ * The interface this code came from held the address as a build-time constant
6
+ * substituted at bundle time, which is the right shape for one application and
7
+ * the wrong shape for a library: a package cannot be rebuilt per consumer, and
8
+ * somebody running against their own indexer, a fork, or a local Ponder has no
9
+ * way to say so.
10
+ *
11
+ * So the address is state with a setter rather than a constant. ONE client is
12
+ * held rather than one per call because `GraphQLClient` is a thin wrapper whose
13
+ * construction is not free, and every query in this package goes to the same
14
+ * host.
15
+ *
16
+ * THE DEFAULT IS THE NEXT INDEXER, "for now" (owner decision), so a consumer who
17
+ * configures nothing still reads real data rather than failing against an empty
18
+ * address. It tracks the current schema; production `indexer.gibs.finance` may
19
+ * lag it.
20
+ *
21
+ * NOTHING RUNS AT MODULE LOAD. The client is created on first use, so importing
22
+ * this package has no side effects.
23
+ */
24
+ export const DEFAULT_INDEXER_ENDPOINT = 'https://next-indexer.gibs.finance';
25
+ let endpoint = DEFAULT_INDEXER_ENDPOINT;
26
+ let client = null;
27
+ /**
28
+ * Points this package at an indexer.
29
+ *
30
+ * Call it once, before any query. Calling it again drops the cached client, which
31
+ * abandons nothing — a request already in flight keeps the client that issued
32
+ * it and settles normally.
33
+ *
34
+ * Returns early on an unchanged address so a consumer may call this from a
35
+ * render or an effect without discarding a warm client on every pass.
36
+ *
37
+ * @param url - the indexer's GraphQL address.
38
+ */
39
+ export const setIndexerEndpoint = (url) => {
40
+ if (url === endpoint)
41
+ return;
42
+ endpoint = url;
43
+ client = null;
44
+ };
45
+ /** The indexer address currently in force. */
46
+ export const indexerEndpoint = () => endpoint;
47
+ /**
48
+ * The client every query in this package uses.
49
+ *
50
+ * A FUNCTION RATHER THAN THE CLIENT ITSELF, so a module that imports this at
51
+ * load time still sees a later {@link setIndexerEndpoint}. Exporting the value
52
+ * would freeze whichever host happened to be configured at the moment the
53
+ * importing module was first evaluated — which, for a consumer that configures
54
+ * the address during startup, is reliably the wrong one.
55
+ */
56
+ export const indexerClient = () => {
57
+ client ??= new GraphQLClient(endpoint);
58
+ return client;
59
+ };