🍧
Relay Context Contracts
Getting your smart contracts compatible with Gelato Relay's relayWithSyncFee
WARNING: 22nd December 2022
callWithSyncFee
is only available with the latestrelay-context
2.1.0 npm package version and only on supported v2 networks. Please read the migration guide and reach out on our Discord if you have any questions. After reading this page:
- You'll learn how to use our helper functions to get useful information from directly within your target contract.
- These allow you access to the
feeCollector
,feeToken
address , andfee
amount from within your target contract.
When using
callWithSyncFee
, you need to pay Gelato's fee collecting contract when your target function is called, otherwise your relay request will not execute. To carry out this payment, your target contract needs to know the address of the fee collector so that you can transfer funds during the call. Furthermore, you need to know in what token to pay the fee in, and how much to pay the fee collector. Gelato Relay appends this useful information to the end of the calldata, when using the
callWithSyncFee
SDK method. Gelato Relay's Context contracts give you helper functions which you can use via inheritance in your target contract allowing you to decode information from the relay calldata, giving you access to:- uint256
fee
: a value denoting how much fee to pay. - address
feeToken
: the address of the token the fee is paid in. - address
feeCollector
: the address to which to send your payment
NOTE:
- If you need target function needs to know all three variables from the relay calldata, see GelatoRelayContext.
- If you only need the
feeCollector
address (i.e. you already encode thefee
andfeeToken
inside your own function parameters), see GelatoRelayFeeCollector.
relay-context is an extremely simple way to create a Gelato Relay compatible smart contract, with just one import.
Terminal
Note: please make sure to use version v2.1.0 and above.
npm install --save-dev @gelatonetwork/relay-context
or
yarn add -D @gelatonetwork/relay-context
1
import {
2
GelatoRelayContext
3
} from "@gelatonetwork/relay-context/contracts/GelatoRelayContext.sol";
OR:
import {
GelatoRelayFeeCollector
} from "@gelatonetwork/relay-context/contracts/GelatoRelayFeeCollector.sol";
GelatoRelayContext allows your smart contract to retrieve the following information from the relay calldata :
- 1.Gelato's fee collector address, a contract specifically deployed for collecting relay fees securely. This allows a smart contract to transfer fees directly if you are using the syncFee payment method.
- 2.The fee token address specifying which address the fee will be paid in, which Gelato resolved from the original relay-SDK request body.
- 3.The fee itself, which includes the gas cost + Gelato's 10% fee on top.
Below is an example:
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
8
// Inheriting GelatoRelayContext gives access to:
9
// 1. onlyGelatoRelay modifier
10
// 2. payment methods, i.e. _transferRelayFee
11
// 3. _getFeeCollector(), _getFeeToken(), _getFee()
12
contract Counter is GelatoRelayContext {
13
uint256 public counter;
14
15
event IncrementCounter();
16
17
// `increment` is the target function to call
18
// this function increments a counter variable after payment to Gelato
19
function increment() external onlyGelatoRelay {
20
// Payment to Gelato
21
// NOTE: be very careful here!
22
// if you do not use the onlyGelatoRelay modifier,
23
// anyone could encode themselves as the fee collector
24
// in the low-level data and drain tokens from this contract.
25
_transferRelayFee();
26
27
counter += 1;
28
emit IncrementCounter();
29
}
30
}
Line 12 inherits the GelatoRelayContext contract, giving access to these features:
_isGelatoRelay(address _forwarder)
: a function which returns true if the address matches Gelato Relay's address.
_getFeeCollector()
: a function to retrieve the fee collector address._getFee()
: a function to retrieve the fee that Gelato will charge._getFeeToken()
: a function to retrieve the address of the token used for fee payment.
As you are using the callWithSyncFee SDK method, you can use the below helper functions to pay directly to Gelato:
_transferRelayFee()
: a function which transfers the fee amount to Gelato, with no cap._transferRelayFeeCapped(uint256 _maxFee)
: a function which transfers the fee amount to Gelato which a set cap from the argumentmaxFee
in wei. This helps limit fees on function calls in case of gas volatility or just for general budgeting.
You can choose to inherit either GelatoRelayContext or GelatoRelayFeeCollector. GelatoRelayContext gives you access to all three pieces of information:
feeCollector
, feeToken
, and fee
; whereas GelatoRelayFeeCollector assumes only the feeCollector
address is appended to the calldata.If you know the fee a priori; for example, if you have queried our fee oracle for the fee amount and a user has already signed off, via a front-end, on this amount and which token to pay in you are only interested in allowing your smart contract to know where to pay this fee, you only require the
feeCollector
address. In this case, please inherit GelatoRelayFeeCollector
, see below.The fee oracle allows you to query and display a fee to your users before sending their transaction via Gelato Relay. Therefore, you could also give them the option to sign off on a certain fee. In this case, you might want to pass the fee you receive from the oracle directly to your target function as an argument.
This makes sense, but be wary that due to gas price volatility, a smoother UX might be to query the fee oracle and calculate a maximum fee by adding some overhead, and displaying this maximum fee to the user. This way, if gas prices do fluctuate more than normal, you can be certain that your user's transaction is executed. You can choose to set a very large overhead, or a smaller one, depending on your own trade-off between execution likelihood and cost to the user. This way, you can also integrate a fee check from inside target function to avoid any overpayments.
GelatoRelayFeeCollector allows your smart contract to retrieve the following information from the relay calldata :
- 1.Gelato's fee collector address, a contract specifically deployed for collecting relay fees securely. This allows a smart contract to transfer fees directly if you are using the syncFee payment method.
Below is an example:
1
// SPDX-License-Identifier: MIT
2
pragma solidity 0.8.17;
3
4
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
5
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
6
import {
7
SafeERC20
8
} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
9
import {
10
GelatoRelayFeeCollector
11
} from "@gelatonetwork/relay-context/contracts/GelatoRelayFeeCollector.sol";
12
13
// Inheriting GelatoFeeCollector gives access to:
14
// 1. onlyGelatoRelay modifier
15
// 2. _getFeeCollector()
16
contract Counter is GelatoRelayFeeCollector {
17
using SafeERC20 for IERC20;
18
uint256 public counter;
19
20
event IncrementCounter();
21
22
// `increment` is the target function to call
23
// this function increments a counter variable after payment to Gelato
24
function increment() external onlyGelatoRelay {
25
// Retreiving the feeCollector address, using the nativeToken
26
address feeCollector = _getFeeCollector();
27
address nativeToken = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
28
// Hardcoding the fee to 100 wei - NOTE: this is just for example
29
// If you do not pay enough to feeCollector,
30
// your relay request will not go through
31
// In reality, you should pass in user signatures TODO
32
uint256 fee = 100;
33
34
// Payment to Gelato
35
// NOTE: be very careful here!
36
// if you do not use the onlyGelatoRelay modifier,
37
// anyone could encode themselves as the fee collector
38
// in the low-level data and drain tokens from this contract.
39
transfer(nativeToken, feeCollector, fee);
40
41
counter += 1;
42
emit IncrementCounter();
43
}
44
45
46
function transfer(
47
address _token,
48
address _to,
49
uint256 _amount
50
) internal {
51
if (_amount == 0) return;
52
_token == NATIVE_TOKEN
53
? Address.sendValue(payable(_to), _amount)
54
: IERC20(_token).safeTransfer(_to, _amount);
55
}
56
}
Line 16 inherits the GelatoRelayFeeCollector contract, giving access to these features:
_isGelatoRelay(address _forwarder)
: a function which returns true if the address matches Gelato Relay's address.
_getFeeCollector()
: a function to retrieve the fee collector address.
Last modified 3mo ago