Gelato Network
Search
⌃K
2⃣

callWithSyncFeeERC2771

Transactions with on-chain payments and ERC2771 authentication support
If you are using @gelatonetwork/relay-sdk v3 or contracts from the package @gelatonetwork/relay-context v2 please follow this migration guide to migrate to the new versions.
After reading this page:
  • You'll know how to use the callWithSyncFeeERC2771 SDK method, using the syncFee payment method.
  • You'll see some code which will help you send a relay request within minutes.
Please proceed to our Security Considerations page and read it thoroughly before advancing with your implementation. It is crucial to understand all potential security risks and measures to mitigate them.

Overview

callWithSyncFeeERC2771 method uses the syncFee payment method with ERC-2771 support.

SDK method: callWithSyncFee

const callWithSyncFeeERC2771 = async (
request: CallWithSyncFeeERC2771Request,
options?: RelayRequestOptions
): Promise<RelayResponse>

Arguments:

  • request: this is the request body used to send a request.
  • options?: relayRequestOptions is an optional object.

Return Object: RelayResponse

type RelayResponse = {
taskId: string;
};

Optional Parameters

Sending a Request

Request Body

const request = {
chainId: BigNumberish;
target: string;
data: BytesLike;
user: string;
userNonce?: BigNumberish;
userDeadline?: BigNumberish;
feeToken: string;
isRelayContext?: boolean;
};
  • chainId: the chain ID of the chain where the target smart contract is deployed.
  • target: the address of the target smart contract.
  • data: encoded payload data (usually a function selector plus the required arguments) used to call the required target address.
  • user: the address of the user's EOA.
  • userNonce: optional, this is a nonce similar to Ethereum nonces, stored in a local mapping on the relay contracts. It is used to enforce nonce ordering of relay calls, if the user requires it. Otherwise, this is an optional parameter and if not passed, the relay-SDK will automatically query on-chain for the current value.
  • userDeadline: optional, the amount of time in seconds that a user is willing for the relay call to be active in the relay backend before it is dismissed.
    • This way the user knows that if the transaction is not sent within a certain timeframe, it will expire. Without this, an adversary could pick up the transaction in the mempool and send it later. This could transfer money, or change state at a point in time which would be highly undesirable to the user.
  • feeToken: the address of the token that is to be used for payment.
  • isRelayContext: an optional boolean (default: true) denoting what data you would prefer appended to the end of the calldata.
    • If set to true, Gelato Relay will append the feeCollector address, the feeToken address, and the uint256 fee to the calldata.
    • If set to false, Gelato Relay will only append the feeCollector address to the calldata.
    • As this is an optional parameter with a default value of true, explicit passing is only required if you are using GelatoRelayFeeCollector from our relay-context-contracts.

Example Code

1. Deploy a GelatoRelayContextERC2771 compatible contract

1
// SPDX-License-Identifier: MIT
2
pragma solidity 0.8.17;
3
4
import {
5
GelatoRelayContextERC2771
6
} from "@gelatonetwork/relay-context/contracts/GelatoRelayContextERC2771.sol";
7
8
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
9
10
// Inheriting GelatoRelayContext gives access to:
11
// 1. _getFeeCollector(): returns the address of Gelato's feeCollector
12
// 2. _getFeeToken(): returns the address of the fee token
13
// 3. _getFee(): returns the fee to pay
14
// 4. _transferRelayFee(): transfers the required fee to Gelato's feeCollector.abi
15
// 5. _transferRelayFeeCapped(uint256 maxFee): transfers the fee to Gelato
16
// only if fee < maxFee
17
// 6. function _getMsgSender(): decodes and returns the user's address from the
18
// calldata, which can be used to refer to user safely instead of msg.sender
19
// (which is Gelato Relay in this case).
20
// 7. _getMsgData(): returns the original msg.data without appended information
21
// 8. onlyGelatoRelay modifier: allows only Gelato Relay's smart contract
22
// to call the function
23
contract CounterRelayContextERC2771 is GelatoRelayContextERC2771 {
24
using Address for address payable;
25
26
mapping(address => uint256) public contextCounter;
27
28
// emitting an event for testing purposes
29
event IncrementCounter(address msgSender);
30
31
// `increment` is the target function to call.
32
// This function increments a counter variable which is
33
// mapped to every _getMsgSender(), the address of the user.
34
// This way each user off-chain has their own counter
35
// variable on-chain.
36
function increment() external onlyGelatoRelayERC2771 {
37
// Payment to Gelato
38
// NOTE: be very careful here!
39
// if you do not use the onlyGelatoRelay modifier,
40
// anyone could encode themselves as the fee collector
41
// in the low-level data and drain tokens from this contract.
42
_transferRelayFee();
43
44
// Incrementing the counter mapped to the _getMsgSender()
45
contextCounter[_getMsgSender()]++;
46
47
emit IncrementCounter(_getMsgSender());
48
}
49
}
50

2. Import GelatoRelaySDK into your front-end .js project

1
import { GelatoRelay, CallWithSyncFeeERC2771Request } from "@gelatonetwork/relay-sdk";
2
const relay = new GelatoRelay();

3. Send the payload to Gelato

1
// target contract address
2
const counter = "<your counter contract address>";
3
4
// using a human-readable ABI for generating the payload
5
const abi = ["function increment()"];
6
7
// address of the token used to pay fees
8
const feeToken = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
9
10
// connect to the blockchain via a front-end provider
11
const provider = new ethers.providers.Web3Provider(window.ethereum);
12
const signer = provider.getSigner();
13
const user = signer.getAddress();
14
15
// generate the target payload
16
const contract = new ethers.Contract(counter, abi, signer);
17
const { data } = await contract.populateTransaction.increment();
18
19
// populate the relay SDK request body
20
const request: CallWithSyncFeeERC2771Request = {
21
chainId: provider.network.chainId,
22
target: counter,
23
data: data,
24
user: user,
25
feeToken: feeToken,
26
isRelayContext: true,
27
};
28
29
// send relayRequest to Gelato Relay API
30
const relayResponse = await relay.callWithSyncFeeERC2771(request, provider);