Comment on page
2⃣
callWithSyncFee
Permissionless transactions with on-chain payments
After reading this page:
- You'll see some code which will help you send a relay request within minutes.
- You'll learn how to pay for transactions using the provided values for
fee
,feeToken
andfeeCollector
.
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.
When using
callWithSyncFee
relay method the target contract assumes responsibility for transferring the fee to Gelato's fee collector during transaction execution. For this, the target contract needs to know:fee
: the transfer amountfeeToken
: the token to be transferredfeeCollector
: the destination address for the fee
- 1.By inheriting the GelatoRelayContext contract in your target contract, you have the ability to transfer the fee through one of two straightforward methods:
_transferRelayFee()
or_transferRelayFeeCapped(uint256 maxFee)
. In either case, the inherited contract takes care of decoding thefee
,feeToken
, andfeeCollector
behind the scenes. The Gelato Relay backend simplifies the process by automatically calculating the fee for you, using Gelato's Fee Oracle to perform the calculations in the background. - 2.Alternatively, you may choose to inherit the GelatoRelayFeeCollector contract. With this approach, Gelato only decodes the
feeCollector
. You must provide thefee
andfeeToken
on-chain, either by hardcoding them (which is not recommended) or embedding them within the payload to be executed. The suggested way to handle this is to calculate the fee with Gelato's Fee Oracle.
Setting a maximum fee, or
maxFee
, for your transactions is strongly advised. This practice enables you to ensure that transaction costs remain below a specific limit. The method _transferRelayFeeCapped(uint256 maxFee)
in the GelatoRelayContext contract provides a convenient way to set the maxFee
easily.If you are utilizing the GelatoRelayFeeCollector contract, the recommended way to pass the
maxFee
is by calculating the fee with Gelato's Fee Oracle, which is accessible in the relay-sdk. The getEstimatedFee()
method is provided to facilitate this calculation.const callWithSyncFee = async (
request: CallWithSyncFeeRequest,
options?: RelayRequestOptions,
apiKey?: string
): Promise<RelayResponse>
options
:RelayRequestOptions
is an optional object.apiKey
: this is an optional API key that links your request to your Gelato Relay account. As this pertains to the syncFee payment method, transaction costs won't be deducted from your 1Balance account. By using the API key, you can benefit from increased rate limits of your Gelato Relay account.
type RelayResponse = {
taskId: string;
};
const request = {
chainId: BigNumberish;
target: string;
data: BytesLike;
isRelayContext?: boolean;
feeToken: string;
};
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.isRelayContext
: an optional boolean (default:true
) denoting what data you would prefer appended to the end of the calldata.- If set to
true
(default), Gelato Relay will append thefeeCollector
address, thefeeToken
address, and the uint256fee
to the calldata. This requires the target contract to inherit the GelatoRelayContext contract. - If set to
false
, Gelato Relay will only append thefeeCollector
address to the calldata. In this case the contract to be inherit by the target contract is the GelatoRelayFeeCollector.
feeToken
: the address of the token that is to be used for payment. Please visit SyncFee Payment Tokens for the full list of supported payment tokens per network.
1
// SPDX-License-Identifier: MIT
2
pragma solidity 0.8.17;
3
4
import {
5
GelatoRelayContext
6
} from "@gelatonetwork/relay-context/contracts/GelatoRelayContext.sol";
7
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
8
9
// Inheriting GelatoRelayContext gives access to:
10
// 1. _getFeeCollector(): returns the address of Gelato's feeCollector
11
// 2. _getFeeToken(): returns the address of the fee token
12
// 3. _getFee(): returns the fee to pay
13
// 4. _transferRelayFee(): transfers the required fee to Gelato's feeCollector.abi
14
// 5. _transferRelayFeeCapped(uint256 maxFee): transfers the fee to Gelato
15
// only if fee < maxFee
16
// 6. _getMsgData(): returns the original msg.data without appended information
17
// 7. onlyGelatoRelay modifier: allows only Gelato Relay's smart contract
18
// to call the function
19
contract CounterRelayContext is GelatoRelayContext {
20
using Address for address payable;
21
22
uint256 public counter;
23
24
event IncrementCounter(uint256 newCounterValue);
25
26
// `increment` is the target function to call.
27
// This function increments a counter variable by 1
28
// IMPORTANT: with `callWithSyncFee` you need to implement
29
// your own smart contract security measures, as this
30
// function can be called by any third party and not only by
31
// Gelato Relay. If not done properly, funds kept in this
32
// smart contract can be stolen.
33
function increment() external onlyGelatoRelay {
34
// Remember to autheticate your call since you are not using ERC-2771
35
// _yourAuthenticationLogic()
36
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
counter++;
45
46
emit IncrementCounter(counter);
47
}
48
49
// `incrementFeeCapped` is the target function to call.
50
// This function uses `_transferRelayFeeCapped` method to ensure
51
// better control of gas fees. If gas fees are above the maxFee value
52
// the transaction will not be executed.
53
// This function increments a counter variable by 1
54
// IMPORTANT: with `callWithSyncFee` you need to implement
55
// your own smart contract security measures, as this
56
// function can be called by any third party and not only by
57
// Gelato Relay. If not done properly, funds kept in this
58
// smart contract can be stolen.
59
function incrementFeeCapped(uint256 maxFee) external onlyGelatoRelay {
60
// Remember to autheticate your call since you are not using ERC-2771
61
// _yourAuthenticationLogic()
62
63
// Payment to Gelato
64
// NOTE: be very careful here!
65
// if you do not use the onlyGelatoRelay modifier,
66
// anyone could encode themselves as the fee collector
67
// in the low-level data and drain tokens from this contract.
68
69
_transferRelayFeeCapped(maxFee);
70
71
counter++;
72
73
emit IncrementCounter(counter);
74
}
75
}
1
import { GelatoRelay, CallWithSyncFeeRequest } from "@gelatonetwork/relay-sdk";
2
const relay = new GelatoRelay();
1
// set up target address and function signature abi
2
const counter = "<your counter contract address>";
3
const abi = ["function increment()"];
4
5
// generate payload using front-end provider such as MetaMask
6
const provider = new ethers.BrowserProvider(window.ethereum);
7
const signer = provider.getSigner();
8
9
// address of the token to pay fees
10
const feeToken = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
11
12
// instantiate the target contract object
13
const contract = new ethers.Contract(counterAddress, abi, signer);
14
15
// example callig the increment() method
16
const { data } = await contract.populateTransaction.increment();
17
18
// populate the relay SDK request body
19
const request: CallWithSyncFeeRequest = {
20
chainId: (await provider.getNetwork()).chainId,
21
target: counter,
22
data: data,
23
feeToken: feeToken,
24
isRelayContext: true,
25
};
26
27
// send relayRequest to Gelato Relay API
28
const relayResponse = await relay.callWithSyncFee(request);
29
30
// -----------------------------------------------------------------
31
// the following is an alternative example using Gelato Fee Oracle,
32
// setting maxFee, and calling the incrementFeeCapped(maxFee) method
33
34
// retrieve the estimate fee from the Gelato
35
36
const fee = await relay.getEstimatedFee(
37
(await provider.getNetwork()).chainId,
38
feeToken,
39
gasLimit,
40
false,
41
)
42
43
const maxFee = fee * 2 // you can use 2x or 3x to set your maxFee
44
45
// example calling the incrementFeeCapped(maxFee) method
46
const { dataMaxFee } = await contract.incrementFeeCapped.populateTransaction(maxFee);
47
48
// populate the relay SDK request body
49
const requestMaxFee: CallWithSyncFeeRequest = {
50
chainId: (await provider.getNetwork()).chainId,
51
target: counter,
52
data: dataMaxFee,
53
feeToken: feeToken,
54
isRelayContext: true,
55
};
56
57
// send relayRequest to Gelato Relay API
58
const relayResponse = await relay.callWithSyncFee(requestMaxFee);
Last modified 2mo ago