Skip to content

Commit

Permalink
feat: add websocket support (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
denis-pingin committed Oct 5, 2023
1 parent fc61f21 commit f88b934
Show file tree
Hide file tree
Showing 9 changed files with 377 additions and 24 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@
},
"dependencies": {
"axios": "0.27.2",
"ethers": "6.7.0"
"ethers": "6.7.0",
"isomorphic-ws": "^5.0.0",
"ws": "^8.5.0"
},
"lint-staged": {
"*.{js,json,md,ts}": "yarn format",
Expand Down
125 changes: 106 additions & 19 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
GELATO_RELAY_CONCURRENT_ERC2771_ZKSYNC_ADDRESS,
GELATO_RELAY_1BALANCE_CONCURRENT_ERC2771_ZKSYNC_ADDRESS,
} from "./constants";
import { WebsocketHandler } from "./utils/index.js";

export {
CallWithSyncFeeRequest,
Expand All @@ -47,11 +48,14 @@ export {
Config,
SignerOrProvider,
};

export class GelatoRelay {
#config: Config;
readonly #websocketHandler: WebsocketHandler;

constructor(config?: Partial<Config>) {
this.#config = this._getConfiguration(config);
this.#websocketHandler = new WebsocketHandler(this.#config.websocketUrl);
}

/**
Expand All @@ -62,8 +66,10 @@ export class GelatoRelay {
};

private _getConfiguration = (config?: Partial<Config>): Config => {
const url = config?.url ?? GELATO_RELAY_URL;
return {
url: config?.url ?? GELATO_RELAY_URL,
url,
websocketUrl: url.replace(/^http/, "ws"),
contract: {
relayERC2771:
config?.contract?.relayERC2771 ?? GELATO_RELAY_ERC2771_ADDRESS,
Expand Down Expand Up @@ -99,12 +105,22 @@ export class GelatoRelay {
* @returns {Promise<RelayResponse>} Response object with taskId parameter
*
*/
callWithSyncFee = (
callWithSyncFee = async (
request: CallWithSyncFeeRequest,
options?: RelayRequestOptions,
sponsorApiKey?: string
): Promise<RelayResponse> =>
library.relayWithSyncFee({ request, sponsorApiKey, options }, this.#config);
): Promise<RelayResponse> => {
const response = await library.relayWithSyncFee(
{ request, sponsorApiKey, options },
this.#config
);

if (this.#websocketHandler.hasHandlers()) {
await this.#websocketHandler.subscribe(response.taskId);
}

return response;
};

/**
* @param {CallWithSyncFeeERC2771Request | CallWithSyncFeeConcurrentERC2771Request} request - Call with sync fee: Sequential ERC2771 or Concurrent ERC2771 request to be relayed by Gelato Executors
Expand All @@ -114,15 +130,15 @@ export class GelatoRelay {
* @returns {Promise<RelayResponse>} Response object with taskId parameter
*
*/
callWithSyncFeeERC2771 = (
callWithSyncFeeERC2771 = async (
request:
| CallWithSyncFeeERC2771Request
| CallWithSyncFeeConcurrentERC2771Request,
signerOrProvider: SignerOrProvider,
options?: RelayRequestOptions,
sponsorApiKey?: string
): Promise<RelayResponse> =>
library.relayWithCallWithSyncFeeERC2771(
): Promise<RelayResponse> => {
const response = await library.relayWithCallWithSyncFeeERC2771(
{
request,
signerOrProvider,
Expand All @@ -132,23 +148,37 @@ export class GelatoRelay {
this.#config
);

if (this.#websocketHandler.hasHandlers()) {
await this.#websocketHandler.subscribe(response.taskId);
}

return response;
};

/**
* @param {SponsoredCallRequest} request SponsoredCallRequest to be relayed by the Gelato Executors.
* @param {string} sponsorApiKey Sponsor API key to be used for the call
* @param {RelayRequestOptions} [options] Optional Relay configuration
* @returns {Promise<RelayResponse>} Response object with taskId parameter
*
*/
sponsoredCall = (
sponsoredCall = async (
request: SponsoredCallRequest,
sponsorApiKey: string,
options?: RelayRequestOptions
): Promise<RelayResponse> =>
library.relayWithSponsoredCall(
): Promise<RelayResponse> => {
const response = await library.relayWithSponsoredCall(
{ request, sponsorApiKey, options },
this.#config
);

if (this.#websocketHandler.hasHandlers()) {
await this.#websocketHandler.subscribe(response.taskId);
}

return response;
};

/**
* @param {CallWithERC2771Request | CallWithConcurrentERC2771Request} request - Sponsored: Sequential ERC2771 or Concurrent ERC2771 request to be relayed by Gelato Executors
* @param {SignerOrProvider} signerOrProvider - BrowserProvider [front-end] or Signer [back-end] to sign the payload
Expand All @@ -157,13 +187,13 @@ export class GelatoRelay {
* @returns {Promise<RelayResponse>} Response object with taskId parameter
*
*/
sponsoredCallERC2771 = (
sponsoredCallERC2771 = async (
request: CallWithERC2771Request | CallWithConcurrentERC2771Request,
signerOrProvider: SignerOrProvider,
sponsorApiKey: string,
options?: RelayRequestOptions
): Promise<RelayResponse> =>
library.relayWithSponsoredCallERC2771(
): Promise<RelayResponse> => {
const response = await library.relayWithSponsoredCallERC2771(
{
request,
signerOrProvider,
Expand All @@ -173,6 +203,13 @@ export class GelatoRelay {
this.#config
);

if (this.#websocketHandler.hasHandlers()) {
await this.#websocketHandler.subscribe(response.taskId);
}

return response;
};

/**
* @param {CallWithERC2771Request | CallWithConcurrentERC2771Request} request - Sequential ERC2771 or Concurrent ERC2771 request to be relayed by Gelato Executors
* @param {SignerOrProvider} signerOrProvider - BrowserProvider [front-end] or Signer [back-end] to sign the payload
Expand Down Expand Up @@ -215,13 +252,13 @@ export class GelatoRelay {
* @returns {Promise<RelayResponse>} Response object with taskId parameter
*
*/
sponsoredCallERC2771WithSignature = (
sponsoredCallERC2771WithSignature = async (
struct: SignatureData["struct"],
signature: SignatureData["signature"],
sponsorApiKey: string,
options?: RelayRequestOptions
): Promise<RelayResponse> =>
library.sponsoredCallERC2771WithSignature(
): Promise<RelayResponse> => {
const response = await library.sponsoredCallERC2771WithSignature(
{
struct,
signature,
Expand All @@ -231,6 +268,13 @@ export class GelatoRelay {
this.#config
);

if (this.#websocketHandler.hasHandlers()) {
await this.#websocketHandler.subscribe(response.taskId);
}

return response;
};

/**
* @param {SignatureData["struct"]} struct - Struct that can be obtained from getSignatureDataERC2771
* @param {BaseCallWithSyncFeeParams} syncFeeParams - Call with Sync Fee parameters
Expand All @@ -240,14 +284,14 @@ export class GelatoRelay {
* @returns {Promise<RelayResponse>} Response object with taskId parameter
*
*/
callWithSyncFeeERC2771WithSignature = (
callWithSyncFeeERC2771WithSignature = async (
struct: SignatureData["struct"],
syncFeeParams: BaseCallWithSyncFeeParams,
signature: SignatureData["signature"],
options?: RelayRequestOptions,
sponsorApiKey?: string
): Promise<RelayResponse> =>
library.callWithSyncFeeERC2771WithSignature(
): Promise<RelayResponse> => {
const response = await library.callWithSyncFeeERC2771WithSignature(
{
struct,
syncFeeParams,
Expand All @@ -258,6 +302,13 @@ export class GelatoRelay {
this.#config
);

if (this.#websocketHandler.hasHandlers()) {
await this.#websocketHandler.subscribe(response.taskId);
}

return response;
};

/**
* @param {bigint} chainId - Chain Id
* @returns {Promise<boolean>} Boolean to demonstrate if Relay V2 is supported on the provided chain
Expand Down Expand Up @@ -322,4 +373,40 @@ export class GelatoRelay {
taskId: string
): Promise<TransactionStatusResponse | undefined> =>
library.getTaskStatus({ taskId }, this.#config);

/**
* @param {callback} handler - Callback function to be called on every task status update
*
*/
onTaskStatusUpdate = (
handler: (taskStatus: TransactionStatusResponse) => void
): void => {
this.#websocketHandler.onUpdate(handler);
};

/**
* @param {callback} handler - Callback function to be unregistered from task status updates
*
*/
offTaskStatusUpdate = (
handler: (taskStatus: TransactionStatusResponse) => void
): void => {
this.#websocketHandler.offUpdate(handler);
};

/**
* @param {callback} handler - Callback function to be called on error
*
*/
onError = (handler: (error: Error) => void): void => {
this.#websocketHandler.onError(handler);
};

/**
* @param {callback} handler - Callback function to be unregistered as an error handler
*
*/
offError = (handler: (error: Error) => void): void => {
this.#websocketHandler.offError(handler);
};
}
8 changes: 4 additions & 4 deletions src/lib/status/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ export type TransactionStatusResponse = {
transactionHash?: string;
blockNumber?: number;
executionDate?: string;
gasUsed?: string;
effectiveGasPrice?: string;
};

enum TaskState {
export enum TaskState {
CheckPending = "CheckPending",
ExecPending = "ExecPending",
WaitingForConfirmation = "WaitingForConfirmation",
ExecSuccess = "ExecSuccess",
ExecReverted = "ExecReverted",
WaitingForConfirmation = "WaitingForConfirmation",
Blacklisted = "Blacklisted",
Cancelled = "Cancelled",
NotFound = "NotFound",
}
1 change: 1 addition & 0 deletions src/lib/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export type BaseCallWithSyncFeeParams = {

export type Config = {
url: string;
websocketUrl: string;
contract: {
relayERC2771: string;
relay1BalanceERC2771: string;
Expand Down
2 changes: 2 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ export * from "./axios";
export * from "./isConcurrentStruct";
export * from "./isConcurrentRequest";
export * from "./generateSalt";
export * from "./isFinalTaskState";
export * from "./websocketHandler";
12 changes: 12 additions & 0 deletions src/utils/isFinalTaskState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { TaskState } from "../lib/status/types/index.js";

export const isFinalTaskState = (taskState: TaskState): boolean => {
switch (taskState) {
case TaskState.ExecSuccess:
case TaskState.ExecReverted:
case TaskState.Cancelled:
return true;
default:
return false;
}
};
Loading

0 comments on commit f88b934

Please sign in to comment.