Skip to content
This repository was archived by the owner on Mar 11, 2026. It is now read-only.

Commit 72526cd

Browse files
committed
feat: add order query fns and test
1 parent 4b1366f commit 72526cd

10 files changed

Lines changed: 666 additions & 50 deletions

File tree

packages/filler/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export { IntentFiller } from "./core/filler"
44
export { EventMonitor } from "./core/event-monitor"
55
export { BasicFiller } from "./strategies/basic"
66
export { ConfirmationPolicy } from "./config/confirmation-policy"
7+
export { ChainConfigService } from "./services/ChainConfigService"

packages/indexer/src/configs/schema.graphql

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -877,43 +877,6 @@ type OrderPlaced @entity {
877877
transactionHash: String! @index
878878
}
879879

880-
type OrderFilled @entity {
881-
"""
882-
Unique identifier for the order (commitment)
883-
"""
884-
id: ID!
885-
886-
"""
887-
Address of the user who filled the order
888-
"""
889-
filler: String! @index
890-
891-
"""
892-
Reference to the order that was filled
893-
"""
894-
order: OrderPlaced!
895-
896-
"""
897-
Timestamp when the order was filled
898-
"""
899-
createdAt: Date! @index
900-
901-
"""
902-
Block number where the order was filled
903-
"""
904-
blockNumber: BigInt! @index
905-
906-
"""
907-
Timestamp of the block where the order was filled
908-
"""
909-
blockTimestamp: BigInt! @index
910-
911-
"""
912-
Hash of the transaction that filled the order
913-
"""
914-
transactionHash: String! @index
915-
}
916-
917880
"""
918881
Metadata about the status of an Order
919882
"""

packages/indexer/src/services/intentGateway.service.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ export class IntentGatewayService {
182182
await orderPlaced.save()
183183
}
184184

185-
await OrderStatusMetadata.create({
185+
const orderStatusMetadata = await OrderStatusMetadata.create({
186186
id: `${commitment}.${status}`,
187187
orderId: commitment,
188188
status,
@@ -193,6 +193,8 @@ export class IntentGatewayService {
193193
transactionHash,
194194
createdAt: timestampToDate(timestamp),
195195
})
196+
197+
await orderStatusMetadata.save()
196198
}
197199

198200
static bytes32ToBytes20(bytes32: string): string {

packages/sdk/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,15 @@
4949
"test:concurrent": "vitest --watch=false --exclude=./src/tests/sequential",
5050
"test:sequence": "vitest --watch=false --sequence.concurrent=false ./src/tests/sequential/**",
5151
"test:requests": "vitest --watch=false --sequence.concurrent=false ./src/tests/sequential/requests.test.ts",
52+
"test:intent-gateway": "vitest --watch=false --sequence.concurrent=false ./src/tests/intentGateway.test.ts",
5253
"test:watch": "vitest --exclude=./src/tests/sequential",
5354
"lint": "biome lint .",
5455
"lint:fix": "biome lint --write .",
5556
"format": "prettier --write \"src/**/*.ts\""
5657
},
5758
"dependencies": {
5859
"@async-generator/merge-race": "^1.0.3",
60+
"@hyperbridge/filler": "workspace:*",
5961
"@polkadot/api": "^15.7.1",
6062
"@polkadot/types": "14.0.1",
6163
"@polkadot/util": "13.3.1",

packages/sdk/pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/sdk/src/client.ts

Lines changed: 104 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import {
2525
type RequestStatusKey,
2626
type TimeoutStatusKey,
2727
type PostRequestStatus,
28+
type OrderWithStatus,
29+
OrderStatus,
2830
} from "@/types"
2931
import {
3032
STATE_MACHINE_UPDATES_BY_HEIGHT,
@@ -45,7 +47,7 @@ import {
4547
waitForChallengePeriod,
4648
} from "@/utils"
4749
import { getChain, type IChain, type SubstrateChain } from "@/chain"
48-
import { _queryGetRequestInternal, _queryRequestInternal } from "./query-client"
50+
import { _queryGetRequestInternal, _queryRequestInternal, _queryOrderInternal } from "./query-client"
4951

5052
/**
5153
* IndexerClient provides methods for interacting with the Hyperbridge indexer.
@@ -1316,16 +1318,6 @@ export class IndexerClient {
13161318
}
13171319
}
13181320

1319-
/**
1320-
* Executes an async operation with exponential backoff retry
1321-
* @param operation - Async function to execute
1322-
* @param retryConfig - Optional retry configuration
1323-
* @returns Result of the operation
1324-
* @throws Last encountered error after all retries are exhausted
1325-
*
1326-
* @example
1327-
* const result = await this.withRetry(() => this.queryStatus(hash));
1328-
*/
13291321
/**
13301322
* Query for asset teleported events by sender, recipient, and destination chain
13311323
* @param from - The sender address
@@ -1367,6 +1359,107 @@ export class IndexerClient {
13671359
...retryConfig,
13681360
})
13691361
}
1362+
1363+
/**
1364+
* Query for an order by its commitment hash
1365+
* @param commitment - The commitment hash of the order
1366+
* @returns The order with its status if found, undefined otherwise
1367+
*/
1368+
async queryOrder(commitment: HexString): Promise<OrderWithStatus | undefined> {
1369+
return _queryOrderInternal({
1370+
commitmentHash: commitment,
1371+
queryClient: this.client,
1372+
logger: this.logger,
1373+
})
1374+
}
1375+
1376+
/**
1377+
* Create a Stream of status updates for an order.
1378+
* Stream ends when the order reaches a terminal state (FILLED, REDEEMED, or REFUNDED).
1379+
* @param commitment - The commitment hash of the order
1380+
* @returns AsyncGenerator that emits status updates until a terminal state is reached
1381+
* @example
1382+
*
1383+
* let client = new IndexerClient(config)
1384+
* let stream = client.orderStatusStream(commitment)
1385+
*
1386+
* // you can use a for-await-of loop
1387+
* for await (const status of stream) {
1388+
* console.log(status)
1389+
* }
1390+
*
1391+
* // you can also use a while loop
1392+
* while (true) {
1393+
* const status = await stream.next()
1394+
* if (status.done) {
1395+
* break
1396+
* }
1397+
* console.log(status.value)
1398+
* }
1399+
*/
1400+
async *orderStatusStream(commitment: HexString): AsyncGenerator<
1401+
{
1402+
status: OrderStatus
1403+
metadata: {
1404+
blockHash: string
1405+
blockNumber: number
1406+
transactionHash: string
1407+
timestamp: bigint
1408+
filler?: string
1409+
}
1410+
},
1411+
void
1412+
> {
1413+
const logger = this.logger.withTag("[orderStatusStream]")
1414+
1415+
let order: OrderWithStatus | undefined
1416+
1417+
while (!order) {
1418+
await this.sleep_for_interval()
1419+
order = await _queryOrderInternal({
1420+
commitmentHash: commitment,
1421+
queryClient: this.client,
1422+
logger: this.logger,
1423+
})
1424+
}
1425+
1426+
logger.trace("`Order` found")
1427+
// Yield initial status
1428+
const latestStatus = order.statuses[order.statuses.length - 1]
1429+
yield {
1430+
status: latestStatus.status,
1431+
metadata: latestStatus.metadata,
1432+
}
1433+
1434+
// If we're already in a terminal state, end the stream
1435+
if ([OrderStatus.FILLED, OrderStatus.REDEEMED, OrderStatus.REFUNDED].includes(latestStatus.status)) {
1436+
return
1437+
}
1438+
1439+
while (true) {
1440+
await this.sleep_for_interval()
1441+
const updatedOrder = await _queryOrderInternal({
1442+
commitmentHash: commitment,
1443+
queryClient: this.client,
1444+
logger: this.logger,
1445+
})
1446+
1447+
if (!updatedOrder) continue
1448+
1449+
const newLatestStatus = updatedOrder.statuses[updatedOrder.statuses.length - 1]
1450+
1451+
if (newLatestStatus.status !== latestStatus.status) {
1452+
yield {
1453+
status: newLatestStatus.status,
1454+
metadata: newLatestStatus.metadata,
1455+
}
1456+
1457+
if ([OrderStatus.FILLED, OrderStatus.REDEEMED, OrderStatus.REFUNDED].includes(newLatestStatus.status)) {
1458+
return
1459+
}
1460+
}
1461+
}
1462+
}
13701463
}
13711464

13721465
interface PartialClientConfig extends Omit<ClientConfig, "pollInterval"> {

packages/sdk/src/queries.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,44 @@ query GetResponseByRequestId($requestId: String!) {
156156
}
157157
}
158158
`
159+
160+
export const ORDER_STATUS = `
161+
query OrderStatus($commitment: String!) {
162+
orderPlaceds(
163+
filter: { commitment: { equalTo: $commitment } }
164+
) {
165+
nodes {
166+
id
167+
user
168+
sourceChain
169+
destChain
170+
commitment
171+
deadline
172+
nonce
173+
fees
174+
inputTokens
175+
inputAmounts
176+
inputValuesUSD
177+
inputUSD
178+
outputTokens
179+
outputAmounts
180+
outputBeneficiaries
181+
calldata
182+
status
183+
createdAt
184+
blockNumber
185+
blockTimestamp
186+
transactionHash
187+
statusMetadata {
188+
nodes {
189+
status
190+
chain
191+
timestamp
192+
blockNumber
193+
transactionHash
194+
filler
195+
}
196+
}
197+
}
198+
}
199+
}`

packages/sdk/src/query-client.ts

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ import type {
77
RequestResponse,
88
RequestStatusKey,
99
PostRequestWithStatus,
10+
OrderResponse,
11+
OrderWithStatus,
1012
} from "./types"
1113
import type { ConsolaInstance } from "consola"
12-
import { GET_REQUEST_STATUS, POST_REQUEST_STATUS } from "./queries"
14+
import { GET_REQUEST_STATUS, POST_REQUEST_STATUS, ORDER_STATUS } from "./queries"
1315

1416
export function createQueryClient(config: { url: string }) {
1517
return new GraphQLClient(config.url)
@@ -47,6 +49,22 @@ export function queryGetRequest(params: { commitmentHash: string; queryClient: I
4749
return _queryGetRequestInternal(params)
4850
}
4951

52+
/**
53+
* Queries an order by CommitmentHash
54+
*
55+
* @example
56+
* import { createQueryClient, queryOrder } from "hyperbridge-sdk"
57+
*
58+
* const queryClient = createQueryClient({
59+
* url: "http://localhost:3000", // URL of the Hyperbridge indexer API
60+
* })
61+
* const commitmentHash = "0x...."
62+
* const order = await queryOrder({ commitmentHash, queryClient })
63+
*/
64+
export function queryOrder(params: { commitmentHash: string; queryClient: IndexerQueryClient }) {
65+
return _queryOrderInternal(params)
66+
}
67+
5068
type InternalQueryParams = {
5169
commitmentHash: string
5270
queryClient: IndexerQueryClient
@@ -161,3 +179,65 @@ export async function _queryGetRequestInternal(params: InternalQueryParams): Pro
161179
statuses: sorted,
162180
}
163181
}
182+
183+
/**
184+
* Internal function to query an order by CommitmentHash
185+
*
186+
* @param params - Parameters for querying the order
187+
* @returns Latest status and block metadata of the order
188+
*/
189+
export async function _queryOrderInternal(params: InternalQueryParams): Promise<OrderWithStatus | undefined> {
190+
const { commitmentHash, queryClient: client, logger = DEFAULT_LOGGER } = params
191+
192+
const response = await retryPromise(
193+
() => {
194+
return client.request<OrderResponse>(ORDER_STATUS, {
195+
commitment: commitmentHash,
196+
})
197+
},
198+
{
199+
maxRetries: 3,
200+
backoffMs: 1000,
201+
logger,
202+
logMessage: `querying 'Order' with Statuses by CommitmentHash(${commitmentHash})`,
203+
},
204+
)
205+
206+
const first_record = response.orderPlaceds.nodes[0]
207+
if (!first_record) return
208+
209+
logger.trace("`Order` found")
210+
const { statusMetadata, ...first_node } = first_record
211+
212+
const statuses = structuredClone(statusMetadata.nodes).map((item) => ({
213+
status: item.status,
214+
metadata: {
215+
blockHash: item.blockHash,
216+
blockNumber: Number.parseInt(item.blockNumber),
217+
transactionHash: item.transactionHash,
218+
timestamp: BigInt(item.timestamp),
219+
filler: item.filler,
220+
},
221+
}))
222+
223+
// sort by ascending order
224+
const sorted = statuses.sort((a, b) => {
225+
// Since OrderStatus and RequestStatus are different enums, we'll just sort by timestamp
226+
return Number(a.metadata.timestamp) - Number(b.metadata.timestamp)
227+
})
228+
229+
const order: OrderWithStatus = {
230+
...first_node,
231+
deadline: BigInt(first_node.deadline),
232+
nonce: BigInt(first_node.nonce),
233+
fees: BigInt(first_node.fees),
234+
inputAmounts: first_node.inputAmounts.map(BigInt),
235+
outputAmounts: first_node.outputAmounts.map(BigInt),
236+
blockNumber: BigInt(first_node.blockNumber),
237+
blockTimestamp: BigInt(first_node.blockTimestamp),
238+
createdAt: new Date(first_node.createdAt),
239+
statuses: sorted,
240+
}
241+
242+
return order
243+
}

0 commit comments

Comments
 (0)