Request Types

ForwardCall

1
type ForwardCall = {
2
chainId: number;
3
target: string;
4
data: BytesLike;
5
feeToken: string;
6
gas: string;
7
};
Copied!
ForwardCall is designed to handle payments of type 0, as it requires no signatures.
ForwardCall requests require no signatures and can be submitted by calling the following method in Gelato Relay SDK:
1
/**
2
*
3
* @param {number} chainId - Chain ID.
4
* @param {string} target - Address of dApp's smart contract to call.
5
* @param {string} data - Payload for `target`.
6
* @param {string} feeToken - paymentToken for Gelato Executors. Use `0xeee...` for native token.
7
* @param {string} gas - Gas limit.
8
* @returns {PromiseLike<string>} taskId - Task ID.
9
*/
10
const sendCallRequest = async (
11
chainId: number,
12
target: string,
13
data: string,
14
feeToken: string,
15
gas: string
16
): Promise<string>;
Copied!
Upon Promise resolution, we get a unique taskId that identifies our request. taskId can then be used to query Gelato Status API in order to retrieve more information.

ForwardRequest

1
type ForwardRequest = {
2
chainId: number;
3
target: string;
4
data: BytesLike;
5
feeToken: string;
6
paymentType: number;
7
maxFee: string;
8
gas: string;
9
sponsor: string;
10
sponsorChainId: number;
11
nonce: number;
12
enforceSponsorNonce: boolean;
13
enforceSponsorNonceOrdering: boolean;
14
};
Copied!
ForwardRequest is designed to handle payments of type 1, 2 and 3, in cases where all meta-transaction related logic (or other kinds of replay protection mechanisms such as hash based commitments) is already implemented inside target smart contract. The sponsor is still required to EIP-712 sign this request, in order to ensure the integrity of payments. Optionally, nonce may or may not be enforced, by setting enforceSponsorNonce. Some dApps may not need to rely on a nonce for ForwardRequest if they already implement strong forms of replay protection.
Firstly, we build a ForwardRequest object using the following method:
1
/**
2
*
3
* @param {number} chainId - Chain ID.
4
* @param {string} target - Address of dApp's smart contract to call.
5
* @param {string} data - Payload for `target`.
6
* @param {string} feeToken - paymentToken for Gelato Executors. Use `0xeee...` for native token.
7
* @param {number} paymentType - Type identifier for Gelato's payment. Can be 1, 2 or 3.
8
* @param {string} maxFee - Maximum fee sponsor is willing to pay Gelato Executors.
9
* @param {string} gas - Gas limit.
10
* @param {number} nonce - Smart contract nonce for sponsor to sign.
11
* Can be 0 if enforceSponsorNonce is always false.
12
* @param {boolean} enforceSponsorNonce - Whether or not to enforce replay protection using sponsor's nonce.
13
* @param {string} sponsor - EOA address that pays Gelato Executors.
14
* @param {number} [sponsorChainId] - Chain ID of where sponsor holds a Gas Tank balance with Gelato.
15
* Relevant for paymentType=1. Defaults to `chainId` if not provided.
16
* @param {boolean} [enforceSponsorNonceOrdering] - Whether or not ordering matters for concurrently submitted transactions.
17
* Defaults to `true` if not provided.
18
* @returns {ForwardRequest}
19
*/
20
const forwardRequest = (
21
chainId: number,
22
target: string,
23
data: BytesLike,
24
feeToken: string,
25
paymentType: number,
26
maxFee: string,
27
gas: string,
28
nonce: number,
29
enforceSponsorNonce: boolean,
30
sponsor: string,
31
sponsorChainId?: number,
32
enforceSponsorNonceOrdering?: boolean
33
): ForwardRequest;
Copied!
Then we send request to Gelato Relay API. sponsorSignature is the EIP-712 signature from sponsor. Upon Promise resolution, we get a unique taskId that identifies our request. taskId can then be used to query Gelato Status API in order to retrieve more information.
1
/**
2
*
3
* @param {ForwardRequest} request - ForwardRequest to be relayed by Gelato Executors.
4
* @param {string} sponsorSignature - EIP-712 signature of sponsor (who pays Gelato Executors).
5
* @returns {PromiseLike<string>} taskId - Task ID.
6
*/
7
const sendForwardRequest = async (
8
request: ForwardRequest,
9
sponsorSignature: BytesLike
10
): Promise<string>;
Copied!

MetaTxRequest

1
type MetaTxRequest = {
2
chainId: number;
3
target: string;
4
data: BytesLike;
5
feeToken: string;
6
paymentType: number;
7
maxFee: string;
8
gas: string;
9
user: string;
10
sponsor: string;
11
sponsorChainId: number;
12
nonce: number;
13
deadline: number;
14
};
Copied!
MetaTxRequest is designed to handle payments of type 1, 2 and 3, in cases where the target contract does not have any meta-transaction nor replay protection logic. In this case, the appropriate Gelato Relay's smart contract already verifies user and sponsor signatures. user is the EOA address that wants to interact with the dApp, while sponsor is the account that pays fees.
Firstly we create MetaTxRequest object using the following SDK's method:
1
/**
2
*
3
* @param {number} chainId - Chain ID.
4
* @param {string} target - Address of dApp's smart contract to call.
5
* @param {string} data - Payload for `target`.
6
* @param {string} feeToken - paymentToken for Gelato Executors. Use `0xeee...` for native token.
7
* @param {number} paymentType - Type identifier for Gelato's payment. Can be 1, 2 or 3.
8
* @param {string} maxFee - Maximum fee sponsor is willing to pay Gelato Executors.
9
* @param {string} gas - Gas limit.
10
* @param {string} user - EOA of dApp's user
11
* @param {number} nonce - user's smart contract nonce.
12
* @param {string} [sponsor] - EOA that pays Gelato Executors.
13
* @param {number} [sponsorChainId] - Chain ID where sponsor holds a balance with Gelato.
14
* Relevant for paymentType=1.
15
* @param {number} [deadline] - Deadline for executing MetaTxRequest, UNIX timestamp in seconds.
16
* Can also be 0 (not enforced).
17
* @returns {MetaTxRequest}
18
*/
19
const metaTxRequest = (
20
chainId: number,
21
target: string,
22
data: BytesLike,
23
feeToken: string,
24
paymentType: number,
25
maxFee: string,
26
gas: string,
27
user: string,
28
nonce: number,
29
sponsor?: string,
30
sponsorChainId?: number,
31
deadline?: number
32
): MetaTxRequest;
Copied!
Then we send request to Gelato Relay API. userSignature is the EIP-712 signature from dApp's user. If sponsorSignature is not passed, we assume sponsor is also the user, so that we set it equal to sponsorSignature. Upon Promise resolution, we get a unique taskId that identifies our request. taskId can then be used to query Gelato Status API in order to retrieve more information.
1
/**
2
*
3
* @param {MetaTxRequest} request - MetaTxRequest to be relayed by Gelato Executors.
4
* @param {string} userSignature - EIP-712 signature from user:
5
* EOA that interacts with target dApp's address.
6
* @param {string} [sponsorSignature] - EIP-712 signature from sponsor:
7
* EOA that pays Gelato Executors, could be same as user.
8
* @returns {string} taskId - Task ID.
9
*/
10
const sendMetaTxRequest = async (
11
request: MetaTxRequest,
12
userSignature: BytesLike,
13
sponsorSignature?: BytesLike
14
): Promise<string>;
Copied!

Querying Task Status

Once a task is submitted to Gelato Relay API, we can use its taskId in order to query Gelato Status API as follows:
1
/**
2
*
3
* @param taskId - Task ID.
4
* @returns {PromiseLike<TransactionStatus | undefined}
5
*/
6
const getStatus = async (
7
taskId: string
8
): Promise<TransactionStatus | undefined>;
Copied!
getStatus returns undefined in case any error has occurred, otherwise it returns an object of type TransactionStatus defined as follows:
1
interface TransactionStatus {
2
service: string; // Name of Gelato Service
3
chain: string; // Chain ID
4
taskId: string; // taskId
5
taskState: TaskState;
6
created_at: Date; // JS Date object
7
lastCheck?: Check;
8
execution?: Execution;
9
lastExecution: Date; // JS Date object
10
}
11
12
// TransactionStatus.taskState is of type:
13
enum TaskState {
14
CheckPending = "CheckPending",
15
ExecPending = "ExecPending",
16
ExecSuccess = "ExecSuccess",
17
ExecReverted = "ExecReverted",
18
WaitingForConfirmation = "WaitingForConfirmation",
19
Blacklisted = "Blacklisted",
20
Cancelled = "Cancelled",
21
NotFound = "NotFound",
22
}
23
24
// TransactionStatus.lastCheck is optional of type:
25
interface Check {
26
taskState: TaskState;
27
message?: string;
28
payload?: Payload;
29
reason?: string;
30
}
31
32
// TransactionStatus.lastExecution is optional of type:
33
interface Execution {
34
status: string;
35
transactionHash: string;
36
blockNumber: number;
37
created_at: Date;
38
}
Copied!

Estimating maxFee

maxFee denotes the maximum fee denominated in feeToken a sponsor is willing to pay, and is one of the required parameters in both ForwardRequest and MetaTxRequest.
Thanks to sponsorSignature and smart contract logic, Gelato Executors will be strongly discouraged to over-charge transaction sponsors. Moreover, maxFee also serves as a buffer to protect against gas price volatility spikes, meaning that transactions will still get mined on time and reliably under said adversarial circumstances.
At execution time, Gelato will charge the fair fee according to actual gas cost estimates and gas price used, not the whole maxFee. In the future, staked Gelato Executors will have a strong incentive to play by the fair rules described in this paragraph even if there is no way for the smart contract to enforce this rule (for instance, payments of Type 1 which use an Off-chain accounting system), as doing otherwise will result in their fee revenues not being paid by the Gelato DAO, and possibly have some or all of their GEL stake slashed.
Below is an example on how to use Gelato Relay SDK in order to calculate suitable maxFee values:
1
const estimateMaxFee = async (
2
chainId: number,
3
feeToken: string,
4
gasLimit: number
5
): Promise<string | undefined> => {
6
try {
7
// First we query all currently whitelisted feeTokens
8
const whitelistedFeeTokens: string[] = (
9
await GelatoRelaySDK.getFeeTokens(chainId)
10
).map((token) => {
11
return token.toLowerCase();
12
});
13
14
console.log(
15
`Whitelisted fee tokens for chainId ${chainId}: ${JSON.stringify(
16
whitelistedFeeTokens
17
)}`
18
);
19
20
if (!whitelistedFeeTokens.includes(feeToken.toLowerCase())) {
21
throw new Error(`feeToken ${feeToken} not whitelisted`);
22
}
23
24
// Add a constant buffer to gasLimit, since the tx will be routed through
25
// Gelato's smart contracts
26
const totalGasLimit = gasLimit + GelatoRelaySDK.GELATO_GAS_BUFFER;
27
28
// Estimate maxFee
29
const maxFee = await GelatoRelaySDK.getMaxFeeEstimate(
30
chainId,
31
feeToken,
32
totalGasLimit
33
);
34
35
console.log(`maxFee estimate for feeToken ${feeToken}: ${maxFee}`);
36
37
return maxFee;
38
} catch (error) {
39
const errorMsg = (error as Error).message ?? String(error);
40
41
console.log(`estimateMaxFee: Failed with error: ${errorMsg}`);
42
43
return undefined;
44
}
45
};
46
47
async function main() {
48
// Can use GelatoRelaySDK.isChainSupported(chainId) to see if it is supported
49
const chainId = 4;
50
// Native token
51
const feeToken = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
52
// gasLimit of the transaction being relayed.
53
// Note that, by construction, this is an upper bound on the actual gas cost,
54
// hence suitable for estimating a maxFee
55
const gasLimit = 100000;
56
57
await estimateMaxFee(chainId, feeToken, gasLimit);
58
}
59
60
main();
Copied!