Being a blockchain based on Ethereum 1.x ideas, the RSK blockchain executes transactions whose costs are covered in gas. More specifically, the sender account in the transaction SHOULD pay the used gas multiplied by the offered gas price, and that amount is subtracted from its balance. So, a new account, having no balance, cannot send a first transaction: it should get balance, using a testnet faucet or obtaining RBTC in mainnet.
This situation usually add friction to the onboarding of a new user, in Ethereum, RSK and other blockchains. In the Ethereum ecosystem, the idea of metatransactions was born some years ago. I wrote a personal project about implementing metatransactions, with many links that explain the concept, and I gave a talk (my slides).
In short, using metatransactions a new user could send a transaction to a relayer system (usually an specialized server) that then it executes the user transaction, but the cost is paid by the relayer account. There is a popular implementation of a gas station, and OpenZeppelin with others have developed contracts that use the relayer idea.
But one problem remains: usually, the transaction is sent to the blockchain USING THE RELAYER account as sender, so
msg.origin` does not points to the original sender. There are tricks to obtain the original sender, but it could be not compatible the existing ecosystem (ie, ERC20 tokens use a lot that information to allow the transfer of the sender tokens).
I think that RSK blockchain could be altered minimally, to allow the use of metatransactions but still honor the original sender. This posts tries to describe the proposal. As usual, I tried to MAXIMIZE SIMPLICITY, and to focus in solving the metatransaction + original sender problem, instead of writing a huge proposal.
The Solution using Smart Contract as Sponsor
The base idea is that the user should send this information to a RSK node:
Transaction: the usual raw transaction, signed by the external client software. No break of the ecosystem here, the user could still use software and hardware wallets.
Sponsor contract: the expected account (smart contract) that will cover the gas costs of the transaction.
Additional arguments: optional, only for some uses cases (in a first implementation it could be skipped)
Sender signature: of the extender message containing transaction, sponsor account and additional arguments.
The above information could be send to a new JSON RPC entry point,named
Usually, the transaction would be rejected, due to lack of balance in the sender account. But having a suggested sponsor, an additional step is invoked:
Call the sponsor (should be a contract) giving the original transaction data (sender, receiver, value, data, gas limit, gas price, nonce) and additional arguments (if needed), then if the call returns
true` the transaction is accepted and relayed to the rest of the network, using an extended transaction message, containing the transaction and the additional fields. Also, the sender signature is checked inside the sponsor contract, to be sure that the sender intention is to delegate the payment of the cost to the given sponsor account.
When mined, the call is executed again, and then, if the check returns
false the transaction IS NOT INCLUDED in a block. If the checks was successful, the original transaction is executed, and the sponsor balance is used at the end to cover the used gas multiplied by the gas price.
When executed, the same steps are invoked. If any of the steps fails, the block is considered invalid.
Optionally, after the original transaction execution, the sponsor contract could be called with the used gas (instead of the gas limit). But I think that in many implementations, this additional step is not needed.
Consider a company that develops a blockchain game. They are interested in user adoption, and having supporter companies (that use advertising along the game), it could fund an sponsor account, to allow new users to start using the game without using a faucet or manual funding of the new user account. Maybe the sponsor smart contract could check that the new account has low balances, and that the transaction to execute will call the game smart contract, and that the nonce of the transaction is reasonably low (ie, less than 1000).
Another interesting use case: to have a faucet contract (some initial code in my personal project): a contract that send funds to the sender account, but also acts as the sponsor for the first funding transaction. Or it could fund the account many times, only if the account has low balance.
Well, the main problem is that, in these cases, the sponsor should be funded to support a number of sponsored transactions. If the sponsor check is light, maybe there are possible attacks to the sponsor contract. If the sponsor check is detailed, maybe it adds execution costs or storage.
The Solution Using User Account as Sponsor
An alternative is to use an user account as sponsor. In this case, the original sender, probably from the DApp, should obtain an sponsor signature:
The sponsor signature cover the original transaction (maybe some additional fields, like adjusted gas price, or a field indicating the intention of covering the gas costs (in case there are other uses to sign an original transaction)).
The new message is validated and relayed to the rest of the network. At the end, the miner validates the message, checks that the sponsor balance is enough to cover the gas costs, and executes the original transaction USING THE ORIGINAL SENDER (this solves the
msg.sender problem). The gas costs are covered by the sponsor user account.
n the previous solution, this additional data could be sent to a new JSON RPC entry point. An alternative could be to send al the data as an RLP list directly to
eth_sendRawTransaction . In this latter case, a pattern could emerge: to have a general enveloped transaction: to be discussed in an upcoming post.
Possibly, the sponsor user account and the generation of its signature could be automated. A possible steps in a Dapp:
- Dapp generates the original transaction, obtaining the raw RLP with signature from the wallet (usually a web3 provider)
- Sends the raw transaction to an sponsor REST API, The sponsor server checks the transaction and if it is potable to be sponsored, the REST API returns the sponsor signature
- Dapp build the raw RLP (an envelope-like transaction) including: original transaction, sponsor signature, any additional fields
- Sends the raw full transaction to
Angel “Java” Lopez