526 days ago
Enabling Gasless Transactions with Gelato & Safe's Account Abstraction SDK
Navigating a multitude of dApps can be daunting for newcomers. The onboarding process is often riddled with complex concepts and steps, creating a barrier that hinders mass adoption.
One of these challenges is the concept of "gas" - the fee required to perform transactions on different chains.
To acquire native assets (like ETH, MATIC, etc.) to pay gas fees, you must create an account on an exchange, go through a potentially lengthy verification process, purchase the assets, and then transfer it to your wallet. This process can be intimidating and cause friction for new users. Even if you've made it past this hurdle and have any ERC20 tokens in your wallet, but no Native assets, further challenges await. You can't move or swap those ERC20 tokens because you can't pay gas. You can't swap the USDC for say native assets like ETH either, because that in itself costs gas. So, you're back to square one - needing Native token to perform any action.
Simplifying Transactions with Account Abstraction and Safe Account Abstraction SDK
One such project making strides in this direction is the Safe.The Safe{Core} Account Abstraction SDK offers developers an opportunity to integrate account abstraction capabilities into their applications, thereby facilitating a smoother user experience.
The Safe Account Abstraction Relay kit, powered by the Gelato relay SDK, pioneers a user-friendly environment. It allows users to cover gas fees with ERC-20 tokens or paves the way for providing gasless transactions.
Mint NFTs Without Paying Gas Using Gelato's Safe Relay Kit
In this section, we will walk you through how you can mint NFTs without bearing any gas costs using the Safe Relay Kit powered by Gelato.
To begin, let's take a look at a simple NFT minting contract. Our aim is to make the mintGelato
function of this contract gasless.
Here's a NFT minting contract, implementing the ERC721 standard:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.14;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract GelatoNFT is ERC721, ERC721URIStorage {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
string public constant gelatoInitialURI = "https://bafkreid6qtmeanbpfygu3e5bbw5fxfexas4dimaigsm6nbigji3naxtz74.ipfs.nftstorage.link/";
constructor() ERC721("GelatoNFT", "GNFT") {}
function mintGelato(address to) public {
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(to, newItemId);
_setTokenURI(newItemId, gelatoInitialURI);
}
function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
super._burn(tokenId);
}
function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) {
return super.tokenURI(tokenId);
}
function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721URIStorage) returns (bool) {
return super.supportsInterface(interfaceId);
}
}
Now let’s see step by step guide to make the mintGelato
function gasless-
1. Create a Safe
Navigate to the safe{Wallet} website and create a new safe. For this tutorial, create a 1/1 safe.
2. Verify Your Contract
Copy your new safe address and paste it into a block explorer to verify the contract. Go to your contract's tab in the block explorer and click on "Is this a proxy?".
Verify your contract and save it. The verification step will also give you an implementation address. The ABI of this implementation smart contract can also be used to add this smart contract to Gelato
3. Deposit funds Into Gelato 1Balance
Visit Gelato 1Balance and deposit either USDC on Polygon for the mainnet or geth on Goerli for testnets. Gelato 1Balance allows you to cover relay costs across multiple chains from this deposit.
4. Create your Relay App from Gelato UI
In your Relay App, paste your safe contract address. This will reveal all of your available functions. Enable the execTransaction
function to make it gasless.
5. Prepare Your Project Directory and Dependencies
Open your terminal and run the following commands:
mkdir gasless-nft-minting
cd gasless-nft-minting
yarn init -y
yarn add ethers @safe-global/relay-kit @safe-global/protocol-kit @safe-global/safe-core-sdk-types
6. Import Packages and Initialize Transaction Settings
In your project, import the necessary packages:
import { ethers } from 'ethers'
import { GelatoRelayPack } from '@safe-global/relay-kit'
import Safe, { EthersAdapter, getSafeContract } from '@safe-global/protocol-kit'
import { MetaTransactionData, MetaTransactionOptions, OperationType } from '@safe-global/safe-core-sdk-types'
7. Create Transaction Object
Create a transaction object for the NFT Minting contract. This includes the to address (NFT Minting contract address), the data which includes encoded function data to call the mintGelato
function in the NFT contract, and value which is set to zero because minting NFTs typically doesn't require sending Ether. The operation is set to OperationType.Call as we're making a simple call to the mintGelato function in the NFT contract.
The options object tells whether the transaction is sponsored or not. If sponsored is true
then the platform is paying gas fee for users and if not then users are paying via any native or ERC20 token present in their safe.
const : MetaTransactionData = {
to: targetAddress,
data: nftContract.interface.encodeFunctionData("mintGelato", [
"0x68B38f944d2689537f8ed8A2F006b4597eE42218",
]),
value: "0",
operation: OperationType.Call,
};
const options: MetaTransactionOptions = {
gasLimit,
isSponsored: true,
};
8. Create the Protocol and Relay Kits instances
const ethAdapter = new EthersAdapter({
ethers,
signerOrProvider: signer,
});
const safeSDK = await Safe.create({
ethAdapter,
safeAddress,
});
console.log(safeSDK);
const relayKit = new GelatoRelayPack(GELATO_RELAY_API_KEY);
9. Encoding the Transaction
In this we will be encoding the execTransaction
function, which is part of the Gnosis Safe contracts. This function takes multiple parameters, including the contract address (to), the value of Ether to send (value), and other operational parameters.
const safeTransaction = await safeSDK.createTransaction({
safeTransactionData,
});
const signedSafeTx = await safeSDK.signTransaction(safeTransaction);
const safeSingletonContract = await getSafeContract({
ethAdapter,
safeVersion: await safeSDK.getContractVersion(),
});
const encodedTx = safeSingletonContract.encode("execTransaction", [
signedSafeTx.data.to,
signedSafeTx.data.value,
signedSafeTx.data.data,
signedSafeTx.data.operation,
signedSafeTx.data.safeTxGas,
signedSafeTx.data.baseGas,
signedSafeTx.data.gasPrice,
signedSafeTx.data.gasToken,
signedSafeTx.data.refundReceiver,
signedSafeTx.encodedSignatures(),
]);
10. Send the Transaction to the Relayer
The above encoded data along with other params will be sent to Gelato Relay based on chosen logic of paying gas fees(sponsored or paying gas fee via token).
const relayTransaction: RelayTransaction = {
target: safeAddress,
encodedTransaction: encodedTx,
chainId: chainId,
options,
};
const response = await relayKit.relayTransaction(relayTransaction);
console.log(response);
console.log(
`Relay Transaction Task ID: https://relay.gelato.digital/tasks/status/${response.taskId}`
);
Conclusion
Safe's Account Abstraction SDK Relay Kit powered by Gelato Relay offers an innovative solution for facilitating gasless transactions, making dApp interactions simpler and more user-friendly. By enabling a streamlined process and greater accessibility, these tools hold immense potential to boost the mass adoption of blockchain technology.
About Gelato Relay
Gelato handles blockchain complexities for you every step of the way, enabling secure gasless transactions for a seamless user experience and onboarding.
Check out Relay as well as other Gelato services like Web3 Functions, Automate, and the Account Abstraction SDK.
While you're at it, join our community and our developer discussions on Discord!