We have successfully implemented etherless token transfers for dApp UX improvements.
by Caleb Lau
On the Ethereum network, all actions on the network incurs some amount of gas, payable in the native network currency ETH. This introduces a barrier to entry for the layperson, as for on-chain token transfers a person needs to also have some ETH to pay for gas cost on the network. This article introduces the concept of delegable token transfers, which allow tokens to be transferred via signed messages through a third party, where gas cost could be subsidised by this third party in return for some tokens, providing the experience of “token transfer paid with tokens”.
The general idea is this: Upon initiating a token transfer, the application will sign a message with the user’s private keys, which effectively says “I will send x number of tokens to another address for a fee amounting to y, payable in tokens”.
This is done by introducing the below interface and implementing a delegableToken contract to a standard ERC20 token:
Interface and Constants Being Implemented
Here we introduce two constants – signingPrefix as the prefix for message signing (which in this case is a hash of 32 characters hence ending it with \n32); and signedTransferSig, which is the function selector for signedTransfer (first four bytes of the keccak256 hash of the function signature).
Three main functions are introduced – signedTransferHash, signedTransfer, and signedTransferCheck, which is implemented in the delegableToken contract.
The delegableToken contract inherits from StandardToken which allows it to work with ERC20 based tokens. This only covers the “transfer” feature of a standard ERC20, however is extensible also to “transferFrom” and subsequently “approval”.
Implemented delegableToken contract. Click for a larger image.
signedTransferHash allows recreating of the keccak256 hash using the transfer parameters, so we could use ecRecover to recover the signing address. This provides a validation function ensuring the message is signed by the owner with all the matching parameters, and you will notice this being used by both signedTransfer and signedTransferCheck.
signedTransfer executes the transaction itself. First it runs a validation to ensure the message was indeed from the original sender, with the correct nonce. Once that’s done, increment nonce, transfer the tokens, and execute the fee transfer.
signedTransferCheck consist of various pre-validation returning a corresponding error message, used before signedTransferHash is called.
With these in place, we could call our token contract to use the delegableToken functions. Generally, the process would be as such:
- Specify parameters:
- Function signature, first four bytes of keccak256(“signedTransfer(address,address,uint256,uint256,uint256,bytes,address)”).
- Token Contract Address
- Token Transfer Amount
- Transfer Fees
- Generate hash offchain using these parameters. Remember to pad the function arguments according to its datatype encoding, which in this case the uint256 datatypes – token transfer amount, transfer fees, and nonce should be zero padded up to 32 bytes.
- Sign the generated offchain hash with the private keys of the sender.
- An optional step which could be ran as a local test is to match the generated offchain hash with the onchain hash using the same parameters. This can be done using function signedTransferHash.
- Pass parameters to signedTransferCheck to validate and ensure everything is fine with the values and signing process.
- If everything is successful, call signedTransfer and deploy the intended parameters.
A sample implementation can be found on the below Github repo:
An example transaction on Rinkeby:
And there you have it: The token holder maintains full custody of his/her tokens. A transaction is done where the holder does not have any ETH, instead, routes the signed transaction to a third party and the third party will be responsible for deploying the transaction and incurring gas cost, for a nominal fee in tokens. A third party could offer this as a service, and with enough traction a marketplace of third party deployers could exist and compete with one another, potentially offering this service in different forms and quality for different fee structures.