loader image
Categories
Main Category

How to Implement Gasless Transactions?

Understanding Gas in Ethereum:

In the Ethereum world, gas is crucial. To use an example, it is the fuel that permits it to run, much as a car needs fuel to run. By definition, the term “gas” refers to the unit used to quantify the amount of computation required to complete a particular operation.

Each Ethereum transaction has a cost since they all need computing resources to complete. The native currency of Ethereum, ETH, is used to pay gas fees. Additionally, gas price is typically expressed in Gwei units, which are itself a denomination of ETH. One Gwei is equivalent to 0.000000001 ETH (or 109 ETH).

For instance, you would often state that your gas costs 1 Gwei rather than that it costs 0.000000001 ETH. Gwei, which stands for Giga-Wei and is equivalent to 1,000,000,000 Wei (1 Wei = 1018 ETH), is a unit of measurement. The lowest unit of ETH is called Wei and is named after Wei Dai, the creator of b-money.

Why Do Gas Fees Exist?

  • Ethereum miners are paid gas fees for their efforts in protecting the network and confirming transactions.
  • Gas charges also prevent fraudulent users from flooding the network with transactions and clogging it up.

The algorithm used to determine Ethereum gas costs is dynamic. Therefore they fluctuate. The Ethereum network is frequently criticized for its high fees and poor performance. For instance, the average transaction cost on the Ethereum network is often greater than the Bitcoin blockchain. Users of the Ethereum network that want to finish a transaction quicker can just pay more. Users might pick periods when network traffic has been relatively low to reduce gas costs. They can also lessen their tip if they don’t mind a slower transaction speed.

This raises the question of whether or not these gas costs can be eliminated.

Yes, we can do that, thanks to meta-transaction.

Meta-Transaction 

What Is It?

A meta-transaction is a standard Ethereum transaction that includes the actual transaction. There is no need for gas or blockchain involvement because the actual transaction is signed by a user and forwarded to an operator or something similar. The operator submits this signed transaction to the blockchain while covering the associated costs. The forwarding smart contract checks the actual transaction’s valid signature before executing it.

Limitations on Ethereum 

A crucial governance layer exists within the context of ERC-20 token transfers: The interaction between approve and transferFrom, which allows tokens to be transferred between externally owned accounts (EOA) and used in other contracts under application-specific conditions by abstracting away msg.sender as the defining mechanism for token access control, is arguably one of the primary reasons for the success of ERC-20 tokens.

Nonetheless, this solution is constrained by the fact that the ERC-20 approve function is specified in terms of msg.sender. An EOA must conduct the user’s initial activity utilizing ERC-20 tokens. If the user wants to engage 

with a smart contract, two transactions are required (approveand the smart contract call, which will internally call transferFrom). Users must own ETH to cover transaction gas expenses even in the most basic use case of paying another person.

How Can We Resolve This?

To address this issue, we may add a new function permit to the ERC-20 token contract that enables users to change the allowance mapping using a signed message (through secp256k1 signatures) rather than using msg.sender. In other words, by submitting a message that the account has signed, the permit method may be used to modify an account’s ERC-20 allowance (see IERC20.allowance). The token holder account does not need to make a transaction and is thus not necessary to retain any ETH because it does not rely on IERC20.approve.

The signed data is organized in accordance with EIP-712, which is already widely used by major RPC & wallet providers for an enhanced user experience.

Implement Gasless Transactions

Example

Our example smart contract denoted as Forwarder.sol implements the EIP-712 domain separator (_domainSeparatorV4) that is used as part of the encoding scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA (_hashTypedDataV4).

Forwarder Contract 

A Smart Contract for Extensible Meta-Transaction Forwarding on Ethereum

The smart contract Forwarder.sol extends the EIP-2770 and entails the following core functions:

verify: Verifies the signature based on the typed structured data.

/**
 * @dev Verifies the signature based on typed structured data. 
 * See https://eips.ethereum.org/EIPS/eip-712
 */
function verify(ForwardRequest calldata req, bytes calldata signature) public view returns (bool) {
    address signer = _hashTypedDataV4(keccak256(abi.encode(
        _TYPEHASH,
        req.from,
        req.to,
        req.value,
        req.gas,
        req.nonce,
        keccak256(req.data)
    ))).recover(signature);
    return _nonces[req.from] == req.nonce && signer == req.from;
}

execute: Executes the meta-transaction via a low-level call.

/**
 * @dev Main function; executes the meta-transaction via a low-level call.
 */
function execute(ForwardRequest calldata req, bytes calldata signature) public payable whenNotPaused() returns (bool, bytes memory) {
    require(_senderWhitelist[msg.sender], "AwlForwarder: sender of meta-transaction is not whitelisted");
    require(verify(req, signature), "AwlForwarder: signature does not match request");
    _nonces[req.from] = req.nonce + 1;
    // solhint-disable-next-line avoid-low-level-calls
    (bool success, bytes memory returndata) = req.to.call{gas: req.gas, value: req.value}(abi.encodePacked(req.data, req.from));
    
    if (!success) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
        returndatacopy(0, 0, returndatasize())
        revert(0, returndatasize())
        }
    }
    /**
     * @dev Validates that the relayer/forwarder EOA has sent enough gas for the call.
     * See https://ronan.eth.link/blog/ethereum-gas-dangers/.
     */
    assert(gasleft() > req.gas / 63);
    emit MetaTransactionExecuted(req.from, req.to, req.data);
    return (success, returndata);
}

Some Security Considerations:

We track a nonce mapping on-chain to provide replay prevention. Furthermore, the Forwarder prevents anybody from publicizing transactions with potentially nefarious purposes. The execute function of the sol smart contract is protected by a whitelist. Furthermore, the smart contract is Ownable, which offers a rudimentary access control mechanism in which an EOA (an owner) is allowed exclusive access to certain functionalities (i.e., addSenderToWhitelist, removeSenderFromWhitelist, killForwarder, pause, unpause). Furthermore, the smart contract function execute is Pausable, which means it incorporates an emergency stop mechanism that the owner can activate. Finally, a selfdestruct operation is built as an emergency backup using the function killForwarder.

Note 1: It is of utmost importance that the whitelisted EOAs carefully check the encoded (user-signed) calldata before sending the transaction.

Note 2: calldata is where data from external calls to functions are stored. Functions can be called internally, e.g., from within the contract, or externally when a function’s visibility is external. When such an external call happens, the data of that call is stored in calldata.

Note 3: For the functions addSenderToWhitelist and killForwarder we do not implement a dedicated strict policy to never allow the zero address 0x0000000000000000000000000000000000000000. The reason for this is that firstly, the functions are protected by being Ownable, and secondly, it can be argued that addresses like 0x00000000000000000000000000000000000001 are just as dangerous, but we do nothing about it.