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 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.
const callWithSyncFeeERC2771 = async (
request: CallWithSyncFeeERC2771Request,
options?: RelayRequestOptions
): Promise<RelayResponse>
- options?:
relayRequestOptions
is an optional object.
type RelayResponse = {
taskId: string;
};
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 thetarget
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 requiredtarget
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 thefeeCollector
address, thefeeToken
address, and the uint256fee
to the calldata. - If set to
false
, Gelato Relay will only append thefeeCollector
address to the calldata. - As this is an optional parameter with a default value of
true
, explicit passing is only required if you are usingGelatoRelayFeeCollector
from our relay-context-contracts.
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
1
import { GelatoRelay, CallWithSyncFeeERC2771Request } from "@gelatonetwork/relay-sdk";
2
const relay = new GelatoRelay();
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);
Last modified 28d ago