Non-Sponsored calls are also known as Sync fee Calls. It is the simplest way to pay, but it delegates all security (reentrancy/replay protection etc.) and payment logic to the target smart contract. You can use ERC-2771 to achieve out-of-the-box security and authentication. Relay costs are covered in either native or ERC-20 tokens and they are paid synchronously during the relay call.
1. Deploy a GelatoRelayContext compatible contract
Import GelatoRelayContext in your target contract to inherit callWithSyncFee functionalities.
Copy
Ask AI
import { GelatoRelayContext} from "@gelatonetwork/relay-context/contracts/GelatoRelayContext.sol";contract TargetContract is GelatoRelayContext { function example() external onlyGelatoRelay { // _yourAuthenticationLogic() // Payment to Gelato // NOTE: be very careful here! // if you do not use the onlyGelatoRelay modifier, // anyone could encode themselves as the fee collector // in the low-level data and drain tokens from this contract. _transferRelayFee(); } function exampleFeeCapped(uint256 maxFee) external onlyGelatoRelay { // Remember to autheticate your call since you are not using ERC-2771 // _yourAuthenticationLogic() // Payment to Gelato // NOTE: be very careful here! // if you do not use the onlyGelatoRelay modifier, // anyone could encode themselves as the fee collector // in the low-level data and drain tokens from this contract. _transferRelayFeeCapped(maxFee); }}
Import GelatoRelayContextERC2771 in your target contract to inherit ERC2771 functionalities with callWithSyncFee. Learn more about ERC2771 here.
Copy
Ask AI
import { GelatoRelayContextERC2771} from "@gelatonetwork/relay-context/contracts/GelatoRelayContextERC2771.sol";contract TargetContractRelayContextERC2771 is GelatoRelayContextERC2771 { mapping (address=>bool) public caller; function increment() external onlyGelatoRelayERC2771 { // Payment to Gelato // NOTE: be very careful here! // if you do not use the onlyGelatoRelayERC2771 modifier, // anyone could encode themselves as the fee collector // in the low-level data and drain tokens from this contract. _transferRelayFee(); // _getMsgSender() will fetch the original user who signed the relay request. caller[_getMsgSender()] = true; } function incrementFeeCapped(uint256 maxFee) external onlyGelatoRelayERC2771 { // Payment to Gelato // NOTE: be very careful here! // if you do not use the onlyGelatoRelayERC2771 modifier, // anyone could encode themselves as the fee collector // in the low-level data and drain tokens from this contract. _transferRelayFeeCapped(maxFee); // _getMsgSender() will fetch the original user who signed the relay request. caller[_getMsgSender()] = true; }}
import { GelatoRelay, CallWithSyncFeeERC2771Request } from "@gelatonetwork/relay-sdk-viem";
Once we have imported the GelatoRelay class, when using ERC2771 methods, we must initialize it with the appropriate trustedForwarder.
The possible configurations are:
Copy
Ask AI
contract: { relay1BalanceERC2771: "trustedForwarder for method sponsoredCallERC2771", relayERC2771: "trustedForwarder for method callWithSyncFeeERC2771", relay1BalanceConcurrentERC2771: "trustedForwarder for method concurrent sponsoredCallERC2771", relayConcurrentERC2771:"trustedForwarder for method concurrent callWithSyncFeeERC2771", relay1BalanceConcurrentERC2771zkSync: "trustedForwarder for method concurrent sponsoredCallERC2771 on zkSync", relay1BalanceERC2771zkSync: "trustedForwarder for method sponsoredCallERC2771 on zkSync", relayConcurrentERC2771zkSync: "trustedForwarder for method concurrent callWithSyncFeeERC2771 on zkSync", relayERC2771zkSync: "trustedForwarder for method callWithSyncFeeERC2771 on zkSync",}
We will need to go to the Supported Networks and check the network and the contract addresses to identify the trustedForwarder associated with our method.
In the example below, we are using the method callWithSyncFeeERC2771 on Sepolia, the trustedForwarder associated is 0xb539068872230f20456CF38EC52EF2f91AF4AE49. We will initialize GelatoRelay with the following config:
Copy
Ask AI
const relay = new GelatoRelay({ contract: { relayERC2771:"0xb539068872230f20456CF38EC52EF2f91AF4AE49" }});
// set up target address and function signature abiconst targetContractAddress = "<your target contract address>"; const abi = ["function example()","function exampleFeeCapped(uint256)"];const [address] = await window.ethereum!.request({ method: "eth_requestAccounts", });// generate payload using front-end provider such as MetaMaskconst client = createWalletClient({ account: address, chain, transport: custom(window.ethereum!),});// address of the token to pay feesconst feeToken = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";const chainId = await client.getChainId();//encode function dataconst data = encodeFunctionData({ abi: abi, functionName: "example",});// -----------------------------------------------------------------// the following is an alternative example using Gelato Fee Oracle, // setting maxFee, and calling the exampleFeeCapped(maxFee) method// retrieve the estimate fee from the Gelato const fee = await relay.getEstimatedFee( BigInt(chainId), feeToken, gasLimit, false,)const maxFee = fee * 2 // you can use 2x or 3x to set your maxFee//encode function dataconst data = encodeFunctionData({ abi: abi, functionName: "exampleFeeCapped", args : [maxFee]});