Using NEAR with Ethereum wallets

This is a request for grant (RFG) for implementing a generic pattern.

Context

Each account on NEAR is also a smart contract. This also means that at any time an existing account or new account can be upgraded with smart contract that performs various functions.

At the same time, there are few millions of users who have Ethereum wallets at this point. They are used to them and have trouble figuring out NEAR initially (compared to new to blockchain users that feel like NEAR onboarding is nice :D).

This offers a way to onboard these users and provide them a way later to migrate into native key management later.

This also means that all the key management tools that are already available on Ethereum can be leveraged.

Spec

There are next parts:

  • User level smart contract
  • Relayer
  • Application level
  • Payment

User level smart contract

This is contract that accepts messages from relayer and forwards the actions to the system.

In design very similar to the multisig contract that exists - core-contracts/multisig at master · near/core-contracts · GitHub

Except instead of accepting transactions from keys attached to given account, it accepts transactions from anyone but they must be valid EIP-712 messages that will pass validation and will recover into address that matches Ethereum address this account was initialized.

To standardize these contracts and usage pattern, a factory is suggested.
E.g. Something like eth.near which creates 0x<eth address>.eth.near accounts with these contracts.

There should be also a way for user later to submit special message to remove this contract and replace it with a full access key themself. This would allow to transition to native key management later while preserving all the data and accesses on the account.

impl EthRequest {
   signature: EthSignature,
   /// Multisig-like request. One of the actions is to remove this contract and replace with a full access key.
   request: Request
}

impl EthProxy {
  /// Initialize with the 
  pub fn new(address: EthAddress);
  /// Executes given request on behalf of given account (e.g. predeccessor).
  /// This method verifies signature first, checks that recovered via EIP-712 address matches to the address in initialization, and executes the request similar how multisig `execute` method works.
  pub fn execute(request: EthRequest);
}

Relayer

Relayer is the service that known parties run.
They can be known either via social or on-chain consensus. E.g. there can be a contract for discovering operational relayers. This is similar to any other RPC endpoints.

The code itself for relayer is a web service with hot wallet that runs a loop:

  • accept a request via http
  • validate that request is indeed valid EIP-712, recover address and check that proxy contract exists for this address.
  • if accounts doesn’t exist - (consider) creating it first. Check if this user payed on Ethereum.
  • if account exists - send a transaction to this account calling execute.

Application level

We need to have a JS library that will provide an utility to send transactions in this form.
Ideally this will be combined with all the other App level Wallet code.

Suggestion is actually to move that code into Wallet SDK.
In such form, if it was selected or detected that there is Ethereum wallet present - it would actually leverage this path.

E.g. when Account object is created, it detects that “Relayer” is used.

Payment

Additionally, there is a need for payment to relayer. There are few ways how this can be done, but one way is to pre-pay relayer on Ethereum for a month of transacting in USDT. There is no guarantee that relayer won’t cheat but given a month of transactions on NEAR will be cheap - it’s probably going to be $1 per month, the Ethereum fee will be more expensive.

There might be easier and fully trust-less way when bridge is fully operational.
For example, in one batch proof, proving that all the relay tx have happened on NEAR and charging for them from some pool of money that was pre-authorized.

Notes

  • EVM runtime actually has the "user smart contract’ part embedded into the runtime itself via calling “meta” call. This is done for generic base runtime account.
  • This idea will work with any Ethereum wallet that support EIP-712 (this can be degraded to signing an arbitrary message, like many do - but than user doesn’t know what is going on at all… like on Ethereum right now).
  • Degrading is though important for things like Ledger, where EIP-712 either not supported or may not just fit into memory of the device. The best way to degrade is actually leveraging multisig approach - first submit the request to the contract and then confirm it with signature on ID of the request which is human readable.
4 Likes

Also @mikedotexe has been working on a relayer prototype, which can be directly leveraged here.

2 Likes

We probably should have a generic proxy SC standard / template, which can be customized by specific application logic.

Because if someone wants to write a custom proxy to pay with custom token - this would allow to customize one in minutes.

Given there are few standard charging models, it’s possible to also have generic relayers that support these messaes: ethereum meta-tx, paying with custom tokens, etc.

2 Likes