24

My understanding is that msg.sender pays the gas fee. Can a smart contract be written in such a way that it's always the contract owner who pay the fees?

As an example, in an ERC20 token when someone transfers the tokens, he pays the gas fee but what if the gas fee for every transfer has to be paid by smart contract owner?

2
  • In one of the coming ethereum updates you'll be able to let your contract pay for the gas.
    – Orry
    Mar 11, 2018 at 10:53
  • @Orry we're 3 years after your comment, has that update come around yet?
    – vrwim
    Jan 23, 2022 at 10:59

5 Answers 5

22

There are 2 options with their pros and cons:

  1. Use signatures

    • Every function in your smart contract must have signature parameter.
    • People who want to interact with the smart contract must sign the function parameters with their account's private key and send it to the smart contract owner (via any communication channel).
    • The owner then submits the parameters along with the signature to the blockchain, paying for gas. The signature guarantees that the message was approved by the user.
  2. Refund used gas at the end of the transaction. A modifier refundGasCost can be used for this (see below).

Below are more details for each option:


Using signatures

Here is a simple ReceiverPays contract that allows making the receiver of the payment pay for gas:

pragma solidity ^0.4.20;

contract ReceiverPays {
    address owner = msg.sender;

    mapping(uint256 => bool) usedNonces;

    // Funds are sent at deployment time.
    function ReceiverPays() public payable { }


    function claimPayment(uint256 amount, uint256 nonce, bytes sig) public {
        require(!usedNonces[nonce]);
        usedNonces[nonce] = true;

        // This recreates the message that was signed on the client.
        bytes32 message = prefixed(keccak256(msg.sender, amount, nonce, this));

        require(recoverSigner(message, sig) == owner);

        msg.sender.transfer(amount);
    }

    // Destroy contract and reclaim leftover funds.
    function kill() public {
        require(msg.sender == owner);
        selfdestruct(msg.sender);
    }


    // Signature methods

    function splitSignature(bytes sig)
        internal
        pure
        returns (uint8, bytes32, bytes32)
    {
        require(sig.length == 65);

        bytes32 r;
        bytes32 s;
        uint8 v;

        assembly {
            // first 32 bytes, after the length prefix
            r := mload(add(sig, 32))
            // second 32 bytes
            s := mload(add(sig, 64))
            // final byte (first byte of the next 32 bytes)
            v := byte(0, mload(add(sig, 96)))
        }

        return (v, r, s);
    }

    function recoverSigner(bytes32 message, bytes sig)
        internal
        pure
        returns (address)
    {
        uint8 v;
        bytes32 r;
        bytes32 s;

        (v, r, s) = splitSignature(sig);

        return ecrecover(message, v, r, s);
    }

    // Builds a prefixed hash to mimic the behavior of eth_sign.
    function prefixed(bytes32 hash) internal pure returns (bytes32) {
        return keccak256("\x19Ethereum Signed Message:\n32", hash);
    }
}

More details can be found in this article https://programtheblockchain.com/posts/2018/02/17/signing-and-verifying-messages-in-ethereum/

The limitation of this approach is that if your smart contract needs to interact with other contracst, then they must also implement the same pattern with adding signatures to every method.


Refunding used gas to the transaction sender

It's not an ideal solution but you can refund gas cost to the transaction sender. You can do this with a modifier:

pragma solidity^0.4.11;

contract SomeContract {

    event SomeEvent(address sender);

    // Need to allow depositing ether to the contract
    function() public payable {
    }

    modifier refundGasCost()
    {
        uint remainingGasStart = msg.gas;

        _;

        uint remainingGasEnd = msg.gas;
        uint usedGas = remainingGasStart - remainingGasEnd;
        // Add intrinsic gas and transfer gas. Need to account for gas stipend as well.
        usedGas += 21000 + 9700;
        // Possibly need to check max gasprice and usedGas here to limit possibility for abuse.
        uint gasCost = usedGas * tx.gasprice;
        // Refund gas cost
        tx.origin.transfer(gasCost);
    }

    function doSomething() external refundGasCost {
        SomeEvent(msg.sender);  
    }
}

Refunding in this way implies some overhead: at least 9700 gas has to be payed extra for the transfer function call inside refundGasCost modifier. Also gas for other opcodes in refundGasCost should be added to usedGas.

Also the above code is potentially vulnerable to reentrancy and other attacks. I provided it only as an example and didn't test it thoroughly.


https://github.com/ethereum/wiki/wiki/Design-Rationale

Requiring transaction senders to pay for gas instead of contracts substantially increases developer usability. Very early versions of Ethereum had contracts pay for gas, but this led to the rather ugly problem that every contract had to implement "guard" code that would make sure that every incoming message compensated the contract with enough ether to pay for the gas that it consumed.

...

ORIGIN: the primary use of the ORIGIN opcode, which provides the sender of a transaction, is to allow contracts to make refund payments for gas.

7
  • Thanks! I was looking for something which wouldn't require paying gas by the smart contract user in first place. Having said that, your solution is really interesting :)
    – eth.block
    Feb 5, 2018 at 3:19
  • Interesting. Has anyone tried that? Feb 6, 2018 at 21:02
  • I tried it in Remix only. The calculated usedGas is not exactly equal to actually used gas but pretty close to it. Feb 7, 2018 at 7:03
  • Relying on tx.origin is not recommended since some time ethereum.stackexchange.com/a/200
    – Ismael
    Feb 25, 2018 at 18:24
  • @Ismael is it a real Vitalik Buterin or a troll? I didn't find that tx.origin can't be relied upon (except it can't be relied on for authorization) anywhere in the official docs. Feb 25, 2018 at 19:29
7

Just published a tiny library to add an ability to delegate transaction creation (fees payment): https://github.com/bitclave/Feeless

You need just:

  1. Inherit your smart contract from Feeless smart contract
  2. Add feeless modifier for any methods you wanna allow to call indirectly
  3. Use msgSender instead of msg.sender in these methods and methods internally called by them

And anyone will be able to pay fees for any other in a fully trustless way.

1
4

The question was asked in February 2018, that seems like an ancient history in the Etheruem space.


Key terms "meta transactions" and "gas station network":

Recent examples of gasless token sends:

USDC

https://medium.com/centre-blog/centre-consortium-announces-release-of-usd-coin-version-2-0-37ee8b27e09b

USDC 2.0 introduces “gasless sends,” which enables wallet and app developers to abstract away the complexity of gas fees (and the need for the customer to hold a balance of ether) and instead to delegate the payment of the gas fees to another address. This allows developers to either provide that service themselves, or allows a third party service to pay the related fees.

ANTv2

https://aragon.org/blog/antv2

There have also been other amazing developments in the world of Ethereum tokens, including gasless transfers. Gasless transfers enable relayers or other third parties to subsidize token transfers for users

EIP-3009 that they use: https://eips.ethereum.org/EIPS/eip-3009

2

Unfortunately not, it's not possible for another one user other than the user originating the transaction to pay for the gas. About msg.sender see the edit below.

There's been some discussion to abstract the signature out of the protocol and allow then to have self-paying smart-contracts.

However, those were only discussions and this feature is not even drafted in a technical implementation.

You'll can find the beginning of the discussion here and the current continuing discussion there

EDIT :

For information, note that msg.sender is not necessarily the user who originated the transaction.

Let's take Two contract A & B and let's imagine that A calls a Method in B contract. Then The method called in B will have for msg.sender A contract's address.

Original address is mostly referred as tx.origin which is the address who originated the transaction wherever we point in the transaction.

Also note that when coding a smart-contract it's not recommended to use tx.origin to determine who sent the transaction but rather use a parameter in the method signature to identify user.

0
2

Check out Fuel Web3 Provider: https://github.com/ahmb84/fuel-web3-provider

"Fuel enable developers to add a funding system to his Dapp and so make transactions feesless for the end user."

Here is a reference implementation: https://github.com/ahmb84/fuel-node-example

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.