Relayer-compatible instadapp wallet

Hi all,

I was looking at the smart contract wallets used by instadapp.

There are two methods:

  • spell: performs a single delegatecall
  • cast: performs a batch delegatecall.

It looks like the wallet contract delegatecalls into a connector (kyber.sol) and then it executes the logic.

As a result, the wallet contract is the user’s identity for other contracts on Ethereum and of course it should hold the ERC20 tokens.

Unfortunately, the wallet contracts rely on msg.sender to authenticate the owner of the wallet contract. This means the owner must sign an Ethereum Transaction and pay for their own gas in order to use instadapp. As we have seen with the volatility gas prices recently, it can lead to the user’s transaction getting stuck and for them to manually bump the gas price several times to get it in.

If the Instadapp wallet contract offered an alternative method to authenticate the user by verifying sign messages (e.g. ecrecover), then the user can use third party relay infrastructure that specialises in sending transactions to Ethereum (i.e., meta-transactions). Several wallets already do this for their users including argent / gnosis safe, either to pay for the user’s gas or just to improve the user experience. Disclaimer: I work on https://www.anydot.dev/

It looks pretty easy to add meta-transaction support to instadapp. I put together Simple Wallet to demonstrate the easiest approach. It just checks that the message hash is unique and the user has signed it. To apply it for instadapp, you just need to modify cast/spell to check the signed message before executing the delegatecall.

As a side note, I recommend adding a function that just performs a “.call” to another smart contract - so its just as expressive as an Ethereum Transaction. Maybe you can call it wand :rofl:

p.s. there are more exotic ways to do replay protection like MultiNonce, but the simple wallet has minimal gas overhead, it is easier to build client-side tooling & verify that it works as you expect.

1 Like

Thank you @stonecoldpat for the suggestion. I like meta-transaction but relying on third-party relayer is too risky. And maintaining a relayer is also costly. Maybe in the future when there’s more funding to have our own relayer then we can add meta-transaction support.

You don’t need to enforce that a relayer (third party or in-house) is used when interacting with instadapp. If it is natively supported in the contract, then you can just make it optional for users who would prefer that. It also enables other use cases like limit orders. e.g. the pre-signed transaction is only sent when the price is at X. Only reliable way to implement that for the user is with a meta-tx setup.

Can I ask what’s the point of just adding check with signature without adding relayer? Have a look here https://docs.openzeppelin.com/contracts/2.x/gsn#pre_and_postrelayedcall. It defines the relay address in order to acceptRelayedCall. Only with relayer then it can becomes meta-tx

I would ignore the gas station network setup for understanding meta-transactions.

A meta-transaction is really just separating who can pay for the gas and who is authorising the execution.

A really good example is the makerdao permit() function:

Screen Shot 2020-07-04 at 17.41.08

That contract is meta-transaction native, so the user can pre-sign the permit() function and someone else can send that to the network on their behalf. It can also be sent via a batch contract.

If the instadapp wallet contract natively supports meta-transactions, then in the future you can easily just turn on relay infrastructure. (e.g., it may be your own, an integrated relayer like any.sender which is really simple to use, or the user’s choice for dealing with ethereum transactions).

For example right now, Argent is doing a promotion where they sponsor the network fees for some types of transactions. That isn’t possible unless the wallet contract natively supports meta-transactions.

Meta-TX is allowing the contract or relayer to do something for the user on their behave. For your example the user is allowing the contract to use Dai on their behave. And in the case of Instadapp it would be the user to allow the relayer to cast spells on their behave. So when you say natively support meta-tx it needs to define who’s the relayer to cast spells for the user. And without the relayer there’s no natively support meta-tx.

But it is a good suggestion that maybe Instadapp would have their own token and relayer then user can pay gas using the token. Not sure, just guessing, anything is possible :slight_smile:

Oh no! That isn’t right.

A relayer can only send a pre-signed message from the user. It cannot access their funds or perform any action without their approval.

The wallet contract does not need to define who is the relayer and in fact it should not know who paid for the gas. As long as the wallet contract receives a signed message from the owner, then it should execute the spell/cast.

Why the relayer is willing to pay for the gas is a different problem. There is no requirement for the relayer to be paid in DAI, a token, or anything really. The relayer could be paid off-chain in $$ and then relay transactions for the user.

I’d recommend our walkthrough here (sorry I don’t want to shill any.sender on the message board, but it is an easy walkthrough to demonstrate that the wallet contract does not need to be aware of the relayer):

In the example, the user has a wallet contract and wants to broadcast a message via the echo contract.:

  • The user signs a message and sends it up to any.sender (our relayer).
  • any.sender packs the signed message into an Ethereum Transaction and broadcasts that to the network.
  • any.sender gradually bumps the fee until it is accepted. (e.g. so the user always pays the best price)

When the transaction is confirmed - it is sent to the user’s wallet contract, it checks the signed message, and then it executes the echo contract. The wallet contract and the echo contract is not aware of any.sender at all - anyone could have paid for the gas and it doesn’t matter.

Thanks for your suggestion again, but it only make sense to add this support when there’s a relayer so that people will be willing to sign the message. Otherwise most people would be just signing it and still sending the request by themselves, which would confused the user.