There is no reason for ERC20 to be changed provided an alternative that does not add significant trade-offs beyond more engineering complexity (rather than usability trade-offs). Which can be argued to be the case for this proposal. In fact, the user is already expected to use relays using submitSignedPayments(). Therefore, the same relays can be used to deploy smart contract wallets without much additional complexity. Of course, the wallet frontend will hide this process from the user the same way it would with submitSignedPayments().
Replacing external accounts with a smart contract wallet architecture can also allow for easier upgradability/extensibility in the future by moving standardization from the token side to the wallet side; something that external accounts do not allow. Multisigs are an example of this. They are not going to be standardized into token contracts because people want to have a wide variety of token multisigs that no token side standard can accommodate for. While upgrading wallet contracts only requires a decision to be made by the wallet owner. Token standardization can be a double-edged sword. And even if ERC20 can be “upgraded” across most relevant token contracts soon (which I believe to be a strong assumption), it will only become more and more difficult as these contracts gain more adoption.
You’re right. The ability to negotiate the fee with the relay on contract deployment would solve the problem. From the clarification and code example that I found, the constructor parameters seem to be included in init_code. Therefore, changing the parameters would also change the computed contract address. That said, there is a work around. The wallet contract stores the factory address in its constructor using msg.sender. We add a transferRelayFee(address relay, uint256 fee, address tokenContract) function to the contract code that only allows the factory to transfer any amount of tokens to any address. As you proposed, the relay can then submit the init_code to the factory contract alongside a signed message from the wallet owner allowing the relay a fee in tokens provided they deploy the init_code.
In this case, the factory deployment function header becomes:
deployCreate2(bytes memory code, uint salt, uint fee, address tokenContract, uint8 v, bytes32 r, bytes32 s)
Factory implementation:
- Use create2 to deploy
code - Query the deployed contract’s
owneraddress - Recover the signer address from
v,r,sandkeccak256(salt, fee, tokenContract)and compare it toowner. - Call
transferRelayFee(msg.sender, fee, tokenContract)on the new contract - If any of above steps fails, revert transaction and reverse contract deployment.
It’s worth mentioning that the reason transferRelayFee() is called by the factory instead of a separate transaction by the relay is to avoid an attack where the owner quickly empties their token balance before the relay has a chance to withdraw their fee after deployment.