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

Commit f88b934

Browse files
authored
feat: add websocket support (#35)
1 parent fc61f21 commit f88b934

9 files changed

Lines changed: 377 additions & 24 deletions

File tree

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@
3939
},
4040
"dependencies": {
4141
"axios": "0.27.2",
42-
"ethers": "6.7.0"
42+
"ethers": "6.7.0",
43+
"isomorphic-ws": "^5.0.0",
44+
"ws": "^8.5.0"
4345
},
4446
"lint-staged": {
4547
"*.{js,json,md,ts}": "yarn format",

src/index.ts

Lines changed: 106 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
GELATO_RELAY_CONCURRENT_ERC2771_ZKSYNC_ADDRESS,
3030
GELATO_RELAY_1BALANCE_CONCURRENT_ERC2771_ZKSYNC_ADDRESS,
3131
} from "./constants";
32+
import { WebsocketHandler } from "./utils/index.js";
3233

3334
export {
3435
CallWithSyncFeeRequest,
@@ -47,11 +48,14 @@ export {
4748
Config,
4849
SignerOrProvider,
4950
};
51+
5052
export class GelatoRelay {
5153
#config: Config;
54+
readonly #websocketHandler: WebsocketHandler;
5255

5356
constructor(config?: Partial<Config>) {
5457
this.#config = this._getConfiguration(config);
58+
this.#websocketHandler = new WebsocketHandler(this.#config.websocketUrl);
5559
}
5660

5761
/**
@@ -62,8 +66,10 @@ export class GelatoRelay {
6266
};
6367

6468
private _getConfiguration = (config?: Partial<Config>): Config => {
69+
const url = config?.url ?? GELATO_RELAY_URL;
6570
return {
66-
url: config?.url ?? GELATO_RELAY_URL,
71+
url,
72+
websocketUrl: url.replace(/^http/, "ws"),
6773
contract: {
6874
relayERC2771:
6975
config?.contract?.relayERC2771 ?? GELATO_RELAY_ERC2771_ADDRESS,
@@ -99,12 +105,22 @@ export class GelatoRelay {
99105
* @returns {Promise<RelayResponse>} Response object with taskId parameter
100106
*
101107
*/
102-
callWithSyncFee = (
108+
callWithSyncFee = async (
103109
request: CallWithSyncFeeRequest,
104110
options?: RelayRequestOptions,
105111
sponsorApiKey?: string
106-
): Promise<RelayResponse> =>
107-
library.relayWithSyncFee({ request, sponsorApiKey, options }, this.#config);
112+
): Promise<RelayResponse> => {
113+
const response = await library.relayWithSyncFee(
114+
{ request, sponsorApiKey, options },
115+
this.#config
116+
);
117+
118+
if (this.#websocketHandler.hasHandlers()) {
119+
await this.#websocketHandler.subscribe(response.taskId);
120+
}
121+
122+
return response;
123+
};
108124

109125
/**
110126
* @param {CallWithSyncFeeERC2771Request | CallWithSyncFeeConcurrentERC2771Request} request - Call with sync fee: Sequential ERC2771 or Concurrent ERC2771 request to be relayed by Gelato Executors
@@ -114,15 +130,15 @@ export class GelatoRelay {
114130
* @returns {Promise<RelayResponse>} Response object with taskId parameter
115131
*
116132
*/
117-
callWithSyncFeeERC2771 = (
133+
callWithSyncFeeERC2771 = async (
118134
request:
119135
| CallWithSyncFeeERC2771Request
120136
| CallWithSyncFeeConcurrentERC2771Request,
121137
signerOrProvider: SignerOrProvider,
122138
options?: RelayRequestOptions,
123139
sponsorApiKey?: string
124-
): Promise<RelayResponse> =>
125-
library.relayWithCallWithSyncFeeERC2771(
140+
): Promise<RelayResponse> => {
141+
const response = await library.relayWithCallWithSyncFeeERC2771(
126142
{
127143
request,
128144
signerOrProvider,
@@ -132,23 +148,37 @@ export class GelatoRelay {
132148
this.#config
133149
);
134150

151+
if (this.#websocketHandler.hasHandlers()) {
152+
await this.#websocketHandler.subscribe(response.taskId);
153+
}
154+
155+
return response;
156+
};
157+
135158
/**
136159
* @param {SponsoredCallRequest} request SponsoredCallRequest to be relayed by the Gelato Executors.
137160
* @param {string} sponsorApiKey Sponsor API key to be used for the call
138161
* @param {RelayRequestOptions} [options] Optional Relay configuration
139162
* @returns {Promise<RelayResponse>} Response object with taskId parameter
140163
*
141164
*/
142-
sponsoredCall = (
165+
sponsoredCall = async (
143166
request: SponsoredCallRequest,
144167
sponsorApiKey: string,
145168
options?: RelayRequestOptions
146-
): Promise<RelayResponse> =>
147-
library.relayWithSponsoredCall(
169+
): Promise<RelayResponse> => {
170+
const response = await library.relayWithSponsoredCall(
148171
{ request, sponsorApiKey, options },
149172
this.#config
150173
);
151174

175+
if (this.#websocketHandler.hasHandlers()) {
176+
await this.#websocketHandler.subscribe(response.taskId);
177+
}
178+
179+
return response;
180+
};
181+
152182
/**
153183
* @param {CallWithERC2771Request | CallWithConcurrentERC2771Request} request - Sponsored: Sequential ERC2771 or Concurrent ERC2771 request to be relayed by Gelato Executors
154184
* @param {SignerOrProvider} signerOrProvider - BrowserProvider [front-end] or Signer [back-end] to sign the payload
@@ -157,13 +187,13 @@ export class GelatoRelay {
157187
* @returns {Promise<RelayResponse>} Response object with taskId parameter
158188
*
159189
*/
160-
sponsoredCallERC2771 = (
190+
sponsoredCallERC2771 = async (
161191
request: CallWithERC2771Request | CallWithConcurrentERC2771Request,
162192
signerOrProvider: SignerOrProvider,
163193
sponsorApiKey: string,
164194
options?: RelayRequestOptions
165-
): Promise<RelayResponse> =>
166-
library.relayWithSponsoredCallERC2771(
195+
): Promise<RelayResponse> => {
196+
const response = await library.relayWithSponsoredCallERC2771(
167197
{
168198
request,
169199
signerOrProvider,
@@ -173,6 +203,13 @@ export class GelatoRelay {
173203
this.#config
174204
);
175205

206+
if (this.#websocketHandler.hasHandlers()) {
207+
await this.#websocketHandler.subscribe(response.taskId);
208+
}
209+
210+
return response;
211+
};
212+
176213
/**
177214
* @param {CallWithERC2771Request | CallWithConcurrentERC2771Request} request - Sequential ERC2771 or Concurrent ERC2771 request to be relayed by Gelato Executors
178215
* @param {SignerOrProvider} signerOrProvider - BrowserProvider [front-end] or Signer [back-end] to sign the payload
@@ -215,13 +252,13 @@ export class GelatoRelay {
215252
* @returns {Promise<RelayResponse>} Response object with taskId parameter
216253
*
217254
*/
218-
sponsoredCallERC2771WithSignature = (
255+
sponsoredCallERC2771WithSignature = async (
219256
struct: SignatureData["struct"],
220257
signature: SignatureData["signature"],
221258
sponsorApiKey: string,
222259
options?: RelayRequestOptions
223-
): Promise<RelayResponse> =>
224-
library.sponsoredCallERC2771WithSignature(
260+
): Promise<RelayResponse> => {
261+
const response = await library.sponsoredCallERC2771WithSignature(
225262
{
226263
struct,
227264
signature,
@@ -231,6 +268,13 @@ export class GelatoRelay {
231268
this.#config
232269
);
233270

271+
if (this.#websocketHandler.hasHandlers()) {
272+
await this.#websocketHandler.subscribe(response.taskId);
273+
}
274+
275+
return response;
276+
};
277+
234278
/**
235279
* @param {SignatureData["struct"]} struct - Struct that can be obtained from getSignatureDataERC2771
236280
* @param {BaseCallWithSyncFeeParams} syncFeeParams - Call with Sync Fee parameters
@@ -240,14 +284,14 @@ export class GelatoRelay {
240284
* @returns {Promise<RelayResponse>} Response object with taskId parameter
241285
*
242286
*/
243-
callWithSyncFeeERC2771WithSignature = (
287+
callWithSyncFeeERC2771WithSignature = async (
244288
struct: SignatureData["struct"],
245289
syncFeeParams: BaseCallWithSyncFeeParams,
246290
signature: SignatureData["signature"],
247291
options?: RelayRequestOptions,
248292
sponsorApiKey?: string
249-
): Promise<RelayResponse> =>
250-
library.callWithSyncFeeERC2771WithSignature(
293+
): Promise<RelayResponse> => {
294+
const response = await library.callWithSyncFeeERC2771WithSignature(
251295
{
252296
struct,
253297
syncFeeParams,
@@ -258,6 +302,13 @@ export class GelatoRelay {
258302
this.#config
259303
);
260304

305+
if (this.#websocketHandler.hasHandlers()) {
306+
await this.#websocketHandler.subscribe(response.taskId);
307+
}
308+
309+
return response;
310+
};
311+
261312
/**
262313
* @param {bigint} chainId - Chain Id
263314
* @returns {Promise<boolean>} Boolean to demonstrate if Relay V2 is supported on the provided chain
@@ -322,4 +373,40 @@ export class GelatoRelay {
322373
taskId: string
323374
): Promise<TransactionStatusResponse | undefined> =>
324375
library.getTaskStatus({ taskId }, this.#config);
376+
377+
/**
378+
* @param {callback} handler - Callback function to be called on every task status update
379+
*
380+
*/
381+
onTaskStatusUpdate = (
382+
handler: (taskStatus: TransactionStatusResponse) => void
383+
): void => {
384+
this.#websocketHandler.onUpdate(handler);
385+
};
386+
387+
/**
388+
* @param {callback} handler - Callback function to be unregistered from task status updates
389+
*
390+
*/
391+
offTaskStatusUpdate = (
392+
handler: (taskStatus: TransactionStatusResponse) => void
393+
): void => {
394+
this.#websocketHandler.offUpdate(handler);
395+
};
396+
397+
/**
398+
* @param {callback} handler - Callback function to be called on error
399+
*
400+
*/
401+
onError = (handler: (error: Error) => void): void => {
402+
this.#websocketHandler.onError(handler);
403+
};
404+
405+
/**
406+
* @param {callback} handler - Callback function to be unregistered as an error handler
407+
*
408+
*/
409+
offError = (handler: (error: Error) => void): void => {
410+
this.#websocketHandler.offError(handler);
411+
};
325412
}

src/lib/status/types/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ export type TransactionStatusResponse = {
88
transactionHash?: string;
99
blockNumber?: number;
1010
executionDate?: string;
11+
gasUsed?: string;
12+
effectiveGasPrice?: string;
1113
};
1214

13-
enum TaskState {
15+
export enum TaskState {
1416
CheckPending = "CheckPending",
1517
ExecPending = "ExecPending",
18+
WaitingForConfirmation = "WaitingForConfirmation",
1619
ExecSuccess = "ExecSuccess",
1720
ExecReverted = "ExecReverted",
18-
WaitingForConfirmation = "WaitingForConfirmation",
19-
Blacklisted = "Blacklisted",
2021
Cancelled = "Cancelled",
21-
NotFound = "NotFound",
2222
}

src/lib/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export type BaseCallWithSyncFeeParams = {
5555

5656
export type Config = {
5757
url: string;
58+
websocketUrl: string;
5859
contract: {
5960
relayERC2771: string;
6061
relay1BalanceERC2771: string;

src/utils/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@ export * from "./axios";
1313
export * from "./isConcurrentStruct";
1414
export * from "./isConcurrentRequest";
1515
export * from "./generateSalt";
16+
export * from "./isFinalTaskState";
17+
export * from "./websocketHandler";

src/utils/isFinalTaskState.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { TaskState } from "../lib/status/types/index.js";
2+
3+
export const isFinalTaskState = (taskState: TaskState): boolean => {
4+
switch (taskState) {
5+
case TaskState.ExecSuccess:
6+
case TaskState.ExecReverted:
7+
case TaskState.Cancelled:
8+
return true;
9+
default:
10+
return false;
11+
}
12+
};

0 commit comments

Comments
 (0)