Can someone sign a transaction, maybe for sending funds or for smart contract interaction, and have someone else broadcast it and pay the transaction fees?
Or is it that the signer of the transaction also has to pay for that transaction?
gasmsg.sendersignaturetransactions
Can someone sign a transaction, maybe for sending funds or for smart contract interaction, and have someone else broadcast it and pay the transaction fees?
Or is it that the signer of the transaction also has to pay for that transaction?
There are 2 options with their pros and cons:
Use signatures
signature
parameter. 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.
A basic Ethereum transaction looks like this:
from – the address of the sender, that will be signing the transaction.
recipient – the receiving address. If a contract account, the transaction will execute the contract code)
signature – the identifier of the sender. This is generated when the sender's private key signs the transaction and confirms the sender has authorized this transaction
nonce -
value –
data –
gasLimit –
maxPriorityFeePerGas -
maxFeePerGas -
The gas fee is deducted from the sender account. And the signature needs to be generated using the sender's private key. Unfortunately, sender=wallet2 and signature=wallt1 simply doesn't work.
For tokens, you can use wallet2 to send a transaction on behalf of wallet1 to transfer x amount of tokens to another address. Firstly, you need to call the approve
method to set the allowance limit, then call the transferFrom
method. But calling the approve
method costs gas too. I guess the most gas-efficient way is to transfer some eth to wallet1 and have wallet1 transfer tokens directly.
In your sample code, you are calling the transfer
method, which is transferring from the msg.sender(wallet2 in this case) to the destination. If it can be successfully executed it may not work as you expect.
Best Answer
Theoretically anyone can broadcast signed transactions, yes.
The signed transaction contains the gas price (and gas limit) that the sender is willing to pay to for getting the transaction included in a block. Usually transactions with a gas price of 0 or below a certain threshold would get ignored by miners.
However, there are exceptions like Flashbots Auction, where bundles of transactions can be submitted to miners. If one of the transactions in the bundle yields enough reward for miners, they'd also include other transactions of that bundle, even if they have a gas price of 0.