From 72e337c7368a9badc0d84a1b091e8958fa549853 Mon Sep 17 00:00:00 2001 From: Michael Birch Date: Mon, 22 Jul 2024 19:57:56 +0200 Subject: [PATCH 1/6] NEP-518 description from #518 --- neps/nep-0518.md | 337 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 337 insertions(+) create mode 100644 neps/nep-0518.md diff --git a/neps/nep-0518.md b/neps/nep-0518.md new file mode 100644 index 000000000..34c1d5dcf --- /dev/null +++ b/neps/nep-0518.md @@ -0,0 +1,337 @@ +--- +NEP: 518 +Title: Web3-Compatible Wallets Support +Authors: Aleksandr Shevchenko +Status: New +DiscussionsTo: https://github.com/near/NEPs/issues/518 +Type: Protocol +Version: 1.0.0 +Created: 2023-11-15 +LastUpdated: 2024-07-22 +--- + +## Summary + +This proposal introduces a significant enhancement to the NEAR Protocol, aiming to make its ecosystem closer with Ethereum by integrating Web3 wallet support. The primary goal is to resolve the incompatibility issues between Ethereum and NEAR standards, thus facilitating a seamless user experience for Ethereum users on the NEAR platform. Central to this initiative are several components: the Wallet Contract, Wallet Selector Extension, RPC-Translator, Transaction Relayer, and the Ethereum Translation Contract. Together, these elements enable Ethereum-compatible transactions within NEAR, ensuring that users familiar with Ethereum's environment face minimal friction in using NEAR dApps. + +Key features include a protocol change that embeds the Wallet Contract for implicit Ethereum-style accounts as a fundamental feature of NEAR and simulating Ethereum standards for transaction compatibility on the level of this contract. + +Proposal also acknowledges inherent limitations and risks, which, however, are manageable. The proposal outlines future opportunities for extending its capabilities to include more Ethereum standards and exploring further integrations. The project's scope is vast, requiring rigorous development, testing, and collaboration across various components and entities of the NEAR ecosystem. + +In essence, this proposal strives to enhance NEAR's interoperability with Ethereum, significantly improving accessibility and user experience for a broader audience in the blockchain community. + +The proposal has core and advanced topics. Pieces that are marked with **[COMPLEX]** may be omitted during the first read. + +## Problem Statement + +Currently, the Ethereum ecosystem is a leading force in the smart contract blockchain space, boasting a large user base and extensive installations of Ethereum-compatible tooling and wallets. However, a significant challenge arises due to the incompatibility of these tools and wallets with NEAR Protocol. This incompatibility necessitates a complete onboarding process for users to interact with NEAR contracts and accounts, leading to confusion, decreased adoption, and the marginalization of NEAR Protocol. + +Implementing Web3 wallet support in NEAR Protocol, with an emphasis on user experience continuity, would significantly benefit the entire NEAR Ecosystem. + +## Goals / Deliverables + +The primary goal is to develop a solution enabling Web3 wallet users to seamlessly interact with NEAR Protocol while retaining their user experience with other EVM-compatible networks. This solution should be straightforward, requiring minimal changes to the NEAR protocol, avoiding potential reversals of protocol changes, and minimizing the need for extensive user education. Additionally, it should prioritize minimizing the risk of phishing attacks that trick users into signing indecipherable data. It's crucial that transactions signed by users are processed unambiguously at the NEAR blockchain level, eliminating the need for a trusted intermediary. + +## Prior work + +Previous efforts have been made in this area. Notable examples include the [nEth](https://github.com/neardefi/neth) project and the [Metamask Snap for NEAR](https://github.com/here-wallet/near-snap). These projects enable Metamask compatibility with NEAR natively but fall short in supporting arbitrary Ethereum wallets. Moreover, neither project provides a user experience comparable to that of EVM-compatible chains. + +With nEth, users must sign [EIP-712](https://eips.ethereum.org/EIPS/eip-712) messages, which differs from executing standard transactions in Metamask. The Metamask Snap project effectively creates a distinct application within Metamask, separate from typical Metamask workflows. This application cannot interact with Metamask keys, nor can it display token balances or transaction histories, and it lacks integration with Ledger. Both projects also necessitate funding new accounts from external sources. + +[Aurora XCC](https://dev.aurora.dev/posts/cross-ecosystem-communication) offers another avenue for Ethereum wallet users to interact with Near native. However, this approach has drawbacks, such as the requirement for WNEAR (NEP-141 wrapped NEAR bridged into Aurora from Near native) for many applications. This necessity complicates users' understanding of transaction costs. Additionally, XCC introduces a gas overhead compared to direct Near usage, potentially hindering user interactions with some applications due to Near's 300 Tgas transaction limit. + +## Technical Description + +Ethereum and NEAR exhibit several fundamental incompatibilities that impact areas such as transaction formats, signature algorithms, addressing schemes, public RPC APIs, and interaction workflows. This proposal seeks to effectively conceal and/or resolve these incompatibilities to facilitate standard wallet operations like balance inquiries, account top-ups, transfers of fungible tokens, and smart contract function calls on the NEAR blockchain. + +### Solution Overview + +The proposed solution comprises five key components: + +1. **Wallet Contract (WC)**: An on-chain smart contract designed to receive, validate, and execute Ethereum-compatible transactions on the NEAR blockchain. It functions as a user account. + +2. **Wallet Selector Extension**: A frontend module that connects NEAR-compatible wallets to dApps. This project aims to develop add-ons supporting Ethereum wallets, primarily to create Ethereum-compatible transactions from NEAR-compatible inputs. + +3. **RPC-Translator (RPCT)**: Given that Ethereum wallets access blockchain state via a specific Web3 API, the RPC-Translator is designed to provide Ethereum methods implementations using the NEAR RPC as a data source. This component, along with the Transaction Relayer, is publicly hosted and accessible to all users. While the RPC-Translator and Transaction Relayer are operated together, they are listed separately to highlight their distinct functions. + +4. **Transaction Relayer (TR)**: Ethereum wallets cannot generate NEAR-compatible transactions. Instead, Ethereum-compatible transactions produced by them are processed by the Transaction Relayer, which embeds it into a NEAR transaction and forwards it to the user’s Wallet Contract. Again, while operated in conjunction with the RPC-Translator, the Transaction Relayer is distinct in its role. + +5. **[COMPLEX] Ethereum Translation Contract (ETC)**: Addressing the incompatibility between NEAR's human-readable account names and Ethereum's cryptographic hash-based addresses, the ETC functions as an on-chain mapping system. This system records NEAR-compatible input values (like NEAR account names and smart contract function names) and maps them to their corresponding Ethereum-compatible cryptographic hashes. This feature is vital for preserving familiar user experiences, such as recognizing ft_transfer operations in NEP-141 as fungible token (ERC20) transfers, rather than generic contract calls, and ensuring that fungible token balances are displayed in Web3 wallets. + +### Transaction Flow + +The transaction flow between components is outlined as follows: + +- The dApp frontend initiates a call to the Wallet Selector. +- The Wallet Selector then interacts with the Ethereum Wallet. +- The Ethereum Wallet communicates with the Transaction Relayer. +- The Transaction Relayer submits the transaction to the NEAR RPC. +- The NEAR protocol processes the transaction through the Wallet Contract. +- Finally, the Wallet Contract interacts with the dApp contract. + +Below are detailed examples, with implementation specifics provided subsequently. + +#### Example Transaction Flow: Incoming $NEAR Transfer + +- Alice, a NEAR compatible wallet user, controls the alice.near account on NEAR. +- Bob, an Ethereum compatible wallet user, controls the Ethereum address (EOA) `0xb794f5ea0ba39494ce839613fffba74279579268`. +- Alice directs her NEAR wallet to transfer 2 $NEAR to `0xb794f5ea0ba39494ce839613fffba74279579268`. +- On NEAR, an implicit account named `0xb794f5ea0ba39494ce839613fffba74279579268` is created, with a Wallet Contract deployed to it by the NEAR Protocol. +- Bob’s wallet periodically queries the `eth_getBalance` RPC method via the RPC-Translator. Once Alice’s transaction is confirmed on-chain, the RPC-Translator reports the balance of the implicit account, which is then displayed to Bob by his wallet. + +This process aligns with the user experience expectations of both NEAR and Ethereum users. + +#### Example Transaction Flow: Outgoing Function Call + +- Bob, using an Ethereum compatible wallet, controls the Ethereum address `0xb794f5ea0ba39494ce839613fffba74279579268`. +- DefiEx, a dApp on NEAR, consists of a web frontend and a smart contract at `defiex.near`. +- Bob uses DefiEx’s web frontend, which employs the **Wallet Selector** to connect his Ethereum compatible wallet. The frontend invokes the `signAndSendTransaction` method, typical in NEAR wallet interactions. +- Wallet Selector addon constructs an Ethereum transaction, which is then sent to Bob's wallet and signed. +- The Ethereum Wallet communicates this transaction to the **Transaction Relayer** via `eth_sendRawTransaction`. +- The Transaction Relayer validates the Ethereum transaction, converts it into a NEAR-compatible format, and sends it to Bob’s Wallet Contract at `0xb794f5ea0ba39494ce839613fffba74279579268`. +- The **Wallet Contract** decodes the Ethereum transaction, verifies the signature against the Ethereum address, and executes the corresponding action on the DefiEx smart contract. + +This method meets the expectations of both NEAR developers and Ethereum users. + +#### [COMPLEX] Example Transaction Flow: NEP-141 Transfer from Metamask + +- Charlie, a Metamask user, controls the Ethereum address `0xb794f5ea0ba39494ce839613fffba74279579268`. +- Declan, another Metamask user, controls the Ethereum address `0xf977814e90da44bfa03b6295a0616a897441acec`. +- Charlie, having acquired USDT on a centralized exchange, wishes to transfer 10 NEAR-native USDT to Declan’s address. +- Charlie adds NEAR-native USDT to Metamask, using an address derived from the `keccak-256` hash of the NEAR account `usdt.tether-token.near`. +- Metamask, via RPC Translator, displays USDT as a standard ERC20 token. +- Charlie initiates a transfer from Metamask to Declan. This is processed as a standard ERC20 `transfer` method call. +- The Transaction Relayer converts this into a NEAR transaction and sends it to the NEAR Blockchain. +- The Wallet Contract identifies this as a special ERC-20 transfer, queries the **Ethereum Translation Contract** for a corresponding NEAR contract, and constructs an action for the `usdt.tether-token.near` contract. + - First, it checks if Declan’s account is registered with USDT, funding the storage if necessary. + - Next, it processes the `ft_transfer` action, attaching `1 yoctoNEAR` as per [NEP-141](https://nomicon.io/Standards/Tokens/FungibleToken/Core). +- Post-transaction, the RPC Translator provides ERC-20 transaction logs to Metamask, confirming the transfer to Charlie. + +This transaction flow adheres to the expectations of Ethereum users. + +### Wallet Selector and Transaction Transformation + +Ethereum transactions, [RLP encoded](https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/), encapsulate the following information: + +- `To` address of the transaction, +- `From` address (derived from the signature), +- `Gas price`, +- `Gas limit`, +- `Nonce`, +- Base token transfer amount within a transaction (`value`), +- `Chain ID`, +- `Data field`, +- `Signature` (yielding the public key and `From` address). + +For function calls to a smart contract, the data field contains: + +- The `Function Selector`, +- Function call parameters. + +Both `To` and `From` addresses, encoded in hexadecimal and prefixed with “0x”, comprise 20 bytes. The `From` address is the right-most 20 bytes of the **Keccak-256** hash of the binary public key of the transaction sender (an **EOA**, externally owned address). The To address signifies either the recipient EOA or an on-chain contract address. + +The `Function Selector` is the first 4 bytes of the **Keccak-256 hash** of the full function signature intended for the contract specified by the To address. For instance, the ERC-20 standard function `transfer(address _to, uint256 _value)` has the Function Selector `keccak-256("transfer(address,uint256)")[0,4]`, equal to `0xa9059cbb`. + +#### Ethereum Transaction Construction + +- Set the `chain ID` to a public constant (equal to [397](https://chainlist.org/chain/397) for mainnet and [398](https://chainlist.org/chain/398) for testnet). +- The `gas price`, fetched by the Ethereum Wallet from the RPC Translator (sourced from NEAR RPC), reflects the NEAR gas price. + - [COMPLEX]: Align Ethereum's gas consumption (usually in thousands or millions) with NEAR's TGas denomination. For instance, a 50k Ethereum gas consumption for ERC20 transfers aligns with 2-10 TGas in NEP-141 transfers. A **100,000x multiplier** is proposed for translating NEAR TGas to Ethereum gas. For example, a 10 TGas NEAR transaction would appear as 1M Ethereum Gas in an Ethereum wallet. Similarly, apply the inverse multiplier to gas prices. A typical NEAR gas price of `1e-4` $NEAR per TGas translates to `1e-9` $NEAR or `1 gwei` for Ethereum wallets, a familiar unit for Ethereum users. +- The `gas limit` is determined by the dApp-developer or through `eth_estimateGas` by the Ethereum Wallet, applying the aforementioned recalculation rules. +- The `nonce` and `signature` are managed by the Ethereum wallet, in line with Ethereum protocol. +- The `From` address adheres to Ethereum protocol too. + +#### Special Considerations for To and Data Field + +The `To` address and `data field` creation vary based on the dApp's input to the Wallet Selector: + +- **[COMPLEX]** If the `receiverID` matches `^0x[a-f0-9]{40}$` (indicating another Ethereum account), it's treated as an EOA and used as-is. This case is restricted to $NEAR transfers (excluding smart contract calls) and is integral to the **Ethereum standards simulation**. + - Note: **NEAR protocol will not host Ethereum smart contracts on Ethereum-style accounts** post-implementation of this proposal. +- Otherwise, the `receiverID` refers to a non-EOA account on NEAR. Here `keccak-256(receiverID)[12,32]` is used as the `To` address. + +For the `data field`: + +- When initiated by the Wallet Selector, function selectors and parameters mirror NEAR actions, encoded as native Ethereum types. For example: + - `FunctionCall(to: string, method: string, args: bytes, yoctoNear: uint32)` + - `Transfer(to: string, yoctoNear: uint32)` + - `Stake(public_key: bytes, yoctoNear: uint32)` + - `AddKey(public_key: bytes, nonce: uint64, is_full_access: bool, allowance: uint256, receiver_id: string, method_names: string[])` + - etc. +- **[COMPLEX]** The `yoctoNear` values are 32-bit, accommodating values under `1e6`. The total `yoctoNEAR` attached to a NEAR Action is `1e6 * + `, enabling: + - Accurate display of $NEAR balances in Ethereum wallets (assuming 18 decimals). + - Compatibility with NEAR standards like NEP-141, requiring 1 yoctoNEAR for ft_transfer and ft_transfer_call methods. +- **[COMPLEX]** If initiated by the Ethereum wallet (limited use cases like native or fungible token transfers), the data field is retained as-is. For instance, ERC-20 `transfer(to address, amount uint256)` calls map to NEAR actions in the Wallet Contract. + +### Wallet Contract + +The Wallet Contract is designed to process Ethereum transactions through the method `executeRLP(target: AccountId, rlp_transaction: Vec)`. The following steps outline its operation: + +1. **Parse the Transaction**: It starts by parsing the incoming Ethereum transaction. + +2. **Verify Target Address**: It checks if the `target` equals the transaction's `To` address, or if `keccak256(target)[12,32]` matches the `To` address. + +3. **Signature Verification and Extraction**: The contract verifies the signature to extract the `Public key` and `chainID`, ensuring the integrity of the transaction. + +4. **`From` Address Creation and Verification**: It generates the `From` address from the public key and confirms that it aligns with the `current_account_id` (the ID of the Wallet Contract). + +5. **`ChainID` Validation**: The contract validates that the `chainID` corresponds to the constant defined for NEAR. + +6. **`Nonce` Verification and Update**: It ensures the transaction nonce matches the stored nonce and then increments the stored nonce by one. + +7. **[COMPLEX] Value Calculation**: The contract sets the `Value` by multiplying the Ethereum transaction value by 1e6 and adding the `yoctoNear` value from the data field. It then confirms the attached deposit is either greater than or equal to this value or identifies it as a self-transaction (refer to **TR and Gas Payment** section for details). + +8. **[COMPLEX] ETC Lookup and Execution for Ethereum Transfers**: + + - If the `To` Address equals the `target` and the `value` is more than zero (indicating an Ethereum transfer), the Wallet Contract consults the ETC ("Ethereum Translation Contract") for the `To` address. + - If the ETC lacks an entry for the `To` address, it implies the recipient is an Ethereum EOA. In this case, the transaction is an ordinary transfer and should be executed as such. + - If the ETC contains an entry for the `To` address, indicating the recipient is a NEAR smart contract simulating an Ethereum standard (like NEP-141), the data field of the transaction is parsed against supported ERC standard calls to form the appropriate NEAR action. + +9. **NEAR Native Account Transfer**: If the data field is empty, it transfers the value to the target, typical for a $NEAR transfer to a NEAR-native account. Values below `1e6 yoctoNEAR` cannot be transferred. + +10. **Function Selector and Parameter Verification**: In cases where the data field is not empty, the contract verifies the function selector against matching NEAR actions. + +11. **Execution of NEAR Action**: After parameter verification, the Wallet Contract executes the corresponding NEAR action by creating a promise. + +These steps ensure that the Wallet Contract accurately processes and executes transactions, bridging the gap between Ethereum and NEAR protocols. + +### Transaction Relayer and Gas Payment + +The Transaction Relayer (TR) serves as an HTTP-JSONRPC endpoint compatible with Ethereum, primarily implementing the eth_sendRawTransaction method. Its validation process involves: + +- Ensuring correct encoding and signing of the transaction. +- Verifying the existence of the sender as an account on NEAR. +- Checking if the nonce is greater than the stored value in the Wallet Contract. + +Upon receiving a transaction, the TR assesses the `To` Address: + +- If it exists on-chain, the TR crafts a transaction with a FunctionCall to `executeRLP` on the `From` address account, setting `target` to the `To` Address. +- **[COMPLEX]** If the `To` Address is not on-chain, the TR consults the ETC (Ethereum Translation Contract) for the preimage of the `To` Address. If retrievable, it creates a FunctionCall to `executeRLP` on the `From` address account, directing `target` to the ETC result. + +#### Approaches to Gas Payment + +This project's challenge lies in appropriately assigning gas costs to the Ethereum-compatible account. Our proposed solutions are: + +1. **Simplified Gas Calculation**: By default, all transactions use the maximum gas limit, or a limit specified by the dApp developer, to bypass the complexity of gas amount computation. + +2. Gas Payment Scheme: + + - **Register the account with TR**. The Ethereum wallet sets up a `functionCall` key on its account, authorizing the `executeRLP` method call with a public key from the Transaction Relayer. This setup, compatible with the Transaction Translation model, employs the `AddKey` Action. Received transactions under this scheme are forwarded and paid for by the Transaction Relayer. Alternatively, the Wallet Contract could reimburse the Transaction Relayer via a `Transfer` promise. + - Once an Ethereum wallet is registered with the Transaction Relayer, this `functionCall` key is utilized for all subsequent transactions. Effectively, **the Transaction Relayer operates on the account's behalf, with the account bearing all gas costs.** + - To mitigate potential abuse by the Transaction Relayer, the Wallet Contract can revoke the `functionCall` key in cases of non-parseable transactions, invalid signatures, or nonce replay (subject to specific time and value deltas). This limitation restricts a dishonest Transaction Relayer to a single invalid transaction's NEAR gas cost (typically not exceeding 0.03 $NEAR under normal conditions). + +**[COMPLEX]**: This approach avoids unnecessary cross-contract calls, maintaining NEAR protocol interactivity for Ethereum users. + +**Optional Free Transactions**: Some relayers may offer a number of free transactions as an incentive for users to engage with NEAR. This can be achieved by the relayer's account directly calling the Wallet Contract's `executeRLP` function, instead of using the user’s account's `functionCall` access key. Some of these transactions may require attachment of $NEAR for covering storage staking or other requirements of NEAR protocol standards. + +### RPC Translator + +The RPC-Translator, functioning as an HTTP-JSONRPC endpoint compatible with Ethereum, implements a set of methods crucial for supporting Ethereum wallets. These include: + +- `eth_chainId`: Returns a constant value. +- `eth_gasPrice`: Fetches and recalculates respective data from NEAR RPC. +- `eth_estimateGas`: Returns a constant `30M`, aligned with the gas recalculation considerations previously mentioned. +- `eth_blockNumber`: Provides the current NEAR block height. +- `eth_getBalance`: Delivers the account's $NEAR balance, adjusted to Ethereum's decimal count (omitting the last 6 digits). +- `eth_getTransactionCount`: Retrieves the nonce value from the account's Wallet Contract. +- `eth_getTransactionByHash` and `eth_getTransactionReceipt`: Present transaction details by indexing the NEAR chain, with values recalculated for Ethereum's decimal count. +- `get_logs`: Offers log information from the NEAR chain, reformatted and recalculated for Ethereum compatibility. +- `eth_call`: Conducts a view call against NEAR RPC, translating parameters as per the **Ethereum standards simulation**. + +These methods enable Ethereum wallets to create transactions and access transaction status. The proposal also accommodates direct $NEAR and ERC20 transfers from Ethereum wallets. + +### [COMPLEX] Ethereum Standards Simulation and Ethereum Translation Contract + +To bridge the gap between NEAR's and Ethereum's differing addressing schemes, especially for interactions initiated directly from web3 wallets, the **Ethereum Translation Contract (ETC)** is pivotal. It maintains a simple mapping between Ethereum transaction hashes and corresponding NEAR account names: + +- `key`: `keccak256()[12,32]` +- `value`: `` + +The ETC provides two core functions: + +- `record(account_id: AccountId)`: Calculates the hash and records it in the map, requiring a deposit for storage staking. It is not applicable for Ethereum-style accounts. +- `lookup(hash: [u8; 32]) -> Option`: A view function that returns the associated NEAR account for a given hash, if available. + +NEAR accounts intending to interact with Ethereum wallets must register with the ETC. This registration is permissionless, allowing third-party on-the-fly registrations if necessary. + +It's important to note that ETC registration is not mandatory for most NEAR applications. For instance, applications like ref.finance, typically not called directly from Metamask, would route their contract calls through the frontend, thereby utilizing the Wallet Selector's transaction translation. + +In the initial phase, the focus will be on $NEAR token transfers and ERC-20 transfers, and approval management in the **Ethereum Standards Simulation**. Future expansions may include additional standards in the Wallet Contract (refer to the testing and upgrades section). The ETC itself does not require upgrades to support these enhancements. + +## Implementation Notes + +The proposed solution necessitates a protocol change for **deploying the Wallet Contract on implicit Ethereum accounts**. To facilitate this, we suggest integrating the Wallet Contract as a core feature of the NEAR protocol. This change would treat all Ethereum accounts as if they already have the Wallet Contract deployed. + +Additionally, to enhance security and align with Ethereum's operational flows, we propose prohibiting the deployment of other contracts on Ethereum accounts. This can be achieved by restricting Ethereum users from adding full access keys to these accounts, thereby significantly reducing the attack surface. + +## Testing and Upgrades + +Integrating the Wallet Contract as a protocol feature requires synchronized testing and upgrade processes between `nearcore` and the Wallet Contract. Fortunately, due to the absence of cross-dependencies, the Wallet Contract can be developed, maintained, and tested independently. Its latest stable version would then be incorporated into the `nearcore` Testnet release, followed by integration into the Mainnet release. + +We recommend that any modifications to the Wallet Contract's functionality, such as adding support for new Ethereum standards over time, should be incorporated into the NEAR enhancement process. This approach ensures a structured and systematic update mechanism. + +The development and testing of off-chain components, like the RPC-Translator, will precede those of on-chain components. This sequential approach allows for thorough testing and refinement of each component before integration into the larger system. + +## Limitations and Risks + +- **[COMPLEX] Dependence on Ethereum Standards Simulation**: The solution heavily depends on simulating Ethereum standards in the Wallet Contract and RPC-Translator. While standards like ERC20 are manageable, others may be too complex or impossible to emulate accurately. + +- **[COMPLEX] Ambiguity in Transaction Conversion**: The emulation of Ethereum standards may lead to confusion in indexing or displaying transactions due to the dual paths of NEP-141 transfer encoding – either through standard conversion via Wallet Selector or originating directly from the wallet. This does not introduce new attack vectors. Users in general should exercise caution and avoid transactions with unfamiliar contracts or frontends. + +- **RPC-Translator Limitations**: The RPC-Translator is tailored for wallet interactions and simple chain functions, but it's not equipped for advanced use cases requiring complete block data. Extending its capabilities could be challenging and resource-intensive. + +- **[COMPLEX] Challenges with the Ethereum Translation Contract (ETC)**: The ETC, vital for integrating NEAR accounts with Ethereum wallets, adds complexity and can lead to user experience issues if registration is overlooked. Automating ETC population through indexing services could mitigate this risk. Future developments might explore on-chain contract lists and function signatures for introspection. + +- **Power and Opacity of Wallet Contract**: The Wallet Contract, pivotal for executing NEAR Actions, may be obscure to Ethereum Wallet users. This necessitates stringent checks and limitations on action parameters, as noted in the proposal to restrict full access key additions. + +- **Lack of Batch Transaction Support**: The proposal currently does not accommodate batch transactions available in Wallet Selector, primarily due to limited utility and the complexity it would introduce, along with associated security concerns. + +- **[COMPLEX] Potential for inclusion NEAR account functionality in Wallet Contract**: An expanded version of the Wallet Contract, which includes NEAR account functionality, could retain valuable NEAR user patterns. For example, allowing using BOS frontend functionCall keys for Ethereum accounts. This would facilitate simple actions without needing to sign each transaction in the Ethereum wallet. While promising, this approach requires careful management. + +- **Wallet Contract Upgrades**: Positioning the Wallet Contract as a protocol feature could limit its upgradability and inadvertently enable smart contract deployments with unbilled storage allocation. Careful implementation can mitigate potential storage growth on NEAR. + +- **Exclusion of Native Ethereum Contract Deployment on NEAR**: The proposal does not address the deployment of Ethereum contracts natively on NEAR. Although this is possible via the Aurora network, interactions between NEAR-onboarded Ethereum users and Aurora contracts would necessitate additional steps, like asset bridging or using the [Aurora XCC SDK](https://dev.aurora.dev/posts/cross-ecosystem-communication). + +## Future Opportunities + +- **Expansion of Ethereum Standards**: A key future development involves simulating other Ethereum standards such as ERC-721, ERC-1155, or ERC-3643. This would enhance NEAR's frontend compatibility with various Ethereum use cases. +- **Account Abstraction and Extended Features**: This proposal lays the groundwork for account abstraction, potentially leading to the introduction of advanced features in the Wallet Contract. These could include native support for multi-signatures, time-locks, social recovery, and alternative signature algorithms compatible with secure elements in widespread devices. +- **Aurora Integration**: Exploring integration with Aurora to enable interactions with Aurora-hosted contracts through NEAR, utilizing Ethereum transaction formats and processes. +- **Bridging and Oracle Onboarding**: This model could facilitate simplified bridging and oracle integration, contributing to the expansion of NEAR's infrastructure. + +## Scope of the Project + +The scope of this project is extensive, encompassing several critical components: + +1. Core Technology: + + - **Wallet Contract**: A crucial, security-sensitive component requiring detailed audits and testing. + - **`nearcore` Protocol Upgrade**: Modification to treat Ethereum-style accounts as implicit and deploy the Wallet Contract on them. + - **Wallet Selector Add-Ons**: Ensuring compatibility with Metamask and WalletConnect-compatible wallets. + - **Transaction Relayer and RPC Translator**: Key elements of the NEAR Web3 RPC middleware, working alongside the NEAR node. + - **Ethereum Translation Contract (ETC)**: A simpler component in comparison to others, yet vital for the system. + +2. **Integration Testing**: Given the solution's distributed nature, comprehensive integration testing is imperative. + +3. **Documentation Development**: Detailed documentation is necessary, enabling third-party developers to understand and potentially replicate parts of the system, except for the Wallet Contract. + +4. **Node Operators Awareness and Public Infrastructure**: Launching and maintaining the NEAR Web3 RPC as a public service, similar to the existing NEAR RPC, and informing third-party node operators about the update and maintenance protocols. + +5. **Integration with Key Platforms**: For effective Ethereum user engagement, integration with the following is essential: + + - **Centralized Exchanges**: Permitting withdrawals to Ethereum-style addresses. + - **Bridges**: Facilitating login on the NEAR side with Ethereum-compatible wallets. + - **NEAR-native Projects**: Updating Wallet Selector configurations to support Ethereum-compatible wallet logins. Anticipated challenges include wallet address display issues, requiring adjustments. + - **Products to allow for key NEAR Wallet functionality**. Allowing Ethereum users to exercise core features of the NEAR protocol in a dApp: staking, key management, work with lockup contracts, etc. These may be implemented as BOS components. + +6. **Bug Bounty Program**: Establishing a program to address potential vulnerabilities, especially for on-chain components like the Wallet Contract and ETC. + +7. **Ongoing Development and Bug Fixes**: The project's complexity necessitates a proactive approach to bug fixes and future enhancements. + +## Reference implementation + +The protocol changes necessary for the project include: + +- Creating Ethereum-like (0x) implicit accounts using `Transfer` action, +- Automatically deploying the wallet contract to those 0x implicit accounts. + +These protocol changes are implemented in nearcore ([eth-implicit accounts PR 10224](https://github.com/near/nearcore/pull/10224), [wallet contract implementation](https://github.com/near/nearcore/tree/1ab9b42c3d723604a214e685d8ed39f7d6434ae2/runtime/near-wallet-contract/implementation)) and have been stabilized in protocol version 70 ([PR 11765](https://github.com/near/nearcore/pull/11765)). From e55e96a5ecc452f3273e7614a904d4656b2af6f6 Mon Sep 17 00:00:00 2001 From: Michael Birch Date: Wed, 24 Jul 2024 17:21:41 +0200 Subject: [PATCH 2/6] Rewrite NEP to match template --- neps/nep-0518.md | 341 +++++++++++------------------------------------ 1 file changed, 81 insertions(+), 260 deletions(-) diff --git a/neps/nep-0518.md b/neps/nep-0518.md index 34c1d5dcf..7b60bb668 100644 --- a/neps/nep-0518.md +++ b/neps/nep-0518.md @@ -12,326 +12,147 @@ LastUpdated: 2024-07-22 ## Summary -This proposal introduces a significant enhancement to the NEAR Protocol, aiming to make its ecosystem closer with Ethereum by integrating Web3 wallet support. The primary goal is to resolve the incompatibility issues between Ethereum and NEAR standards, thus facilitating a seamless user experience for Ethereum users on the NEAR platform. Central to this initiative are several components: the Wallet Contract, Wallet Selector Extension, RPC-Translator, Transaction Relayer, and the Ethereum Translation Contract. Together, these elements enable Ethereum-compatible transactions within NEAR, ensuring that users familiar with Ethereum's environment face minimal friction in using NEAR dApps. +This NEP describes the protocol changes needed to support the usage of Ethereum-compatible wallets (Web3 wallets), for example Metamask, on Near native applications. That is to say, with this protocol change all Metamask users can become Near users without installing any additional software; from their perspective Near will appear as just another network they can choose from (similar to Aurora today). -Key features include a protocol change that embeds the Wallet Contract for implicit Ethereum-style accounts as a fundamental feature of NEAR and simulating Ethereum standards for transaction compatibility on the level of this contract. +This is accomplished through two key protocol changes: -Proposal also acknowledges inherent limitations and risks, which, however, are manageable. The proposal outlines future opportunities for extending its capabilities to include more Ethereum standards and exploring further integrations. The project's scope is vast, requiring rigorous development, testing, and collaboration across various components and entities of the NEAR ecosystem. +1. Ethereum-like addresses (i.e. account IDs of the form `^0x[a-f0-9]{40}$`) are implicit accounts on Near (i.e. can be created via a `Transfer` action). We call these "eth-implicit accounts". +2. Unlike the current implicit accounts (64-character hex-encoded), eth-implicit accounts do not have any access keys added to them on creation. Instead, these accounts will have a special contract deployed to them automatically called the "wallet contract". This wallet contract enables the owner of the Ethereum address corresponding to the eth-implicit account ID to sign transactions with their Ethereum private key, thus providing similar functionality to the default access key of 64-character implicit accounts. -In essence, this proposal strives to enhance NEAR's interoperability with Ethereum, significantly improving accessibility and user experience for a broader audience in the blockchain community. +The nature of this NEP requires the reader to know some concepts from the Ethereum ecosystem. However, since this is a document for readers only familiar with the Near network, we include appendices with definitions and descriptions of the Ethereum concepts needed to understand this proposal. Terms in bold, for example **EOA**, are defined in the glossary (Appendix A). -The proposal has core and advanced topics. Pieces that are marked with **[COMPLEX]** may be omitted during the first read. +The protocol changes described here are a part of the overall eb3-Compatible Wallets Support solution. The full solution (including the protocol changes described here) are detailed in the original [NEP-518 issue description](https://github.com/near/NEPs/issues/518). -## Problem Statement +## Motivation Currently, the Ethereum ecosystem is a leading force in the smart contract blockchain space, boasting a large user base and extensive installations of Ethereum-compatible tooling and wallets. However, a significant challenge arises due to the incompatibility of these tools and wallets with NEAR Protocol. This incompatibility necessitates a complete onboarding process for users to interact with NEAR contracts and accounts, leading to confusion, decreased adoption, and the marginalization of NEAR Protocol. Implementing Web3 wallet support in NEAR Protocol, with an emphasis on user experience continuity, would significantly benefit the entire NEAR Ecosystem. -## Goals / Deliverables +## Specification -The primary goal is to develop a solution enabling Web3 wallet users to seamlessly interact with NEAR Protocol while retaining their user experience with other EVM-compatible networks. This solution should be straightforward, requiring minimal changes to the NEAR protocol, avoiding potential reversals of protocol changes, and minimizing the need for extensive user education. Additionally, it should prioritize minimizing the risk of phishing attacks that trick users into signing indecipherable data. It's crucial that transactions signed by users are processed unambiguously at the NEAR blockchain level, eliminating the need for a trusted intermediary. +### Eth-implicit accounts -## Prior work +**Definition**: An eth-implicit account is a top-level Near account with ID of the form `^0x[a-f0-9]{40}$` (i.e. 42-characters with `0x` as a prefix followed by 40 characters of hex-encoded data which represents a 20-byte address). -Previous efforts have been made in this area. Notable examples include the [nEth](https://github.com/neardefi/neth) project and the [Metamask Snap for NEAR](https://github.com/here-wallet/near-snap). These projects enable Metamask compatibility with NEAR natively but fall short in supporting arbitrary Ethereum wallets. Moreover, neither project provides a user experience comparable to that of EVM-compatible chains. +Eth-implicit accounts, as the name suggests, are implicit accounts on Near. This means MUST be automatically created by a `Transfer` action if the target account ID does not exist. This includes being created even if the amount being transferred is zero (per the prior [NEP on zero balance accounts](https://github.com/near/NEPs/blob/master/neps/nep-0448.md)). Eth-implicit accounts represent an Ethereum **EOA** and therefore are controlled via the Ethereum private key corresponding to the address contained in the account ID (see Appendix B for a description of how 20-byte addresses are derived from a private key in the Ethereum ecosystem). To enable this control, eth-implicit accounts all have a smart contract deploy to them called the wallet contract (specification in the next section). -With nEth, users must sign [EIP-712](https://eips.ethereum.org/EIPS/eip-712) messages, which differs from executing standard transactions in Metamask. The Metamask Snap project effectively creates a distinct application within Metamask, separate from typical Metamask workflows. This application cannot interact with Metamask keys, nor can it display token balances or transaction histories, and it lacks integration with Ledger. Both projects also necessitate funding new accounts from external sources. +When an eth-implicit account is created the runtime MUST set the contract code equal to specific "magic bytes". These bytes come from a UTF-8 encoded string which is equal to the constant `near` appended with the base-58 encoding of the sha2 hash of the wallet contract code. This constant allows the contract runtime to lookup the full contract code without needing it to be stored multiple times in the state. As well as being more efficient for the protocol, this setting the code equal to a hash of the contract instead of the contract itself is what keeps the storage requirements of a new eth-implicit account small enough to be a zero balance account. -[Aurora XCC](https://dev.aurora.dev/posts/cross-ecosystem-communication) offers another avenue for Ethereum wallet users to interact with Near native. However, this approach has drawbacks, such as the requirement for WNEAR (NEP-141 wrapped NEAR bridged into Aurora from Near native) for many applications. This necessity complicates users' understanding of transaction costs. Additionally, XCC introduces a gas overhead compared to direct Near usage, potentially hindering user interactions with some applications due to Near's 300 Tgas transaction limit. +The magic bytes depend on the Near network chain id because the wallet contract (and therefore its hash) depends on the Near chain id. The magic bytes (UTF-8 encoded) for each Near chain id are listed below: -## Technical Description +- `mainnet`: `near83PPBGX9KNgC2TRJgX7mvZfFPx92bFkdYvZNARQjRt8G` +- `testnet`: `near3Za8tfLX6nKa2k4u2Aq5CRrM7EmTVSL9EERxymfnSFKd` +- any other id (e.g. `localnet`): `near2dQzuvePVCmkXwe1oF3AgY9pZvqtDtq43nFHph928CU4` -Ethereum and NEAR exhibit several fundamental incompatibilities that impact areas such as transaction formats, signature algorithms, addressing schemes, public RPC APIs, and interaction workflows. This proposal seeks to effectively conceal and/or resolve these incompatibilities to facilitate standard wallet operations like balance inquiries, account top-ups, transfers of fungible tokens, and smart contract function calls on the NEAR blockchain. +When the runtime is executing a `FunctionCall` action on an account with these magic bytes as code then it MUST act as if the wallet contract code were stored there instead (i.e. the wallet contract Wasm module ends up being executed). -### Solution Overview +### Wallet contract -The proposed solution comprises five key components: +This smart contract is automatically deployed to all eth-implicit accounts (see prior section). The purpose of this contract is to accept transactions encoded in an Ethereum style and create Near actions which are executed in subsequent receipts. In this way, the owner of the Ethereum private key associated with the eth-implicit account (the address contained in its account ID) controls what actions the account takes. Thus that Ethereum key effectively becomes the only access key for the account, emulating the behavior of an Ethereum **EOA**. -1. **Wallet Contract (WC)**: An on-chain smart contract designed to receive, validate, and execute Ethereum-compatible transactions on the NEAR blockchain. It functions as a user account. +#### API -2. **Wallet Selector Extension**: A frontend module that connects NEAR-compatible wallets to dApps. This project aims to develop add-ons supporting Ethereum wallets, primarily to create Ethereum-compatible transactions from NEAR-compatible inputs. +The wallet contract has two public functions: -3. **RPC-Translator (RPCT)**: Given that Ethereum wallets access blockchain state via a specific Web3 API, the RPC-Translator is designed to provide Ethereum methods implementations using the NEAR RPC as a data source. This component, along with the Transaction Relayer, is publicly hosted and accessible to all users. While the RPC-Translator and Transaction Relayer are operated together, they are listed separately to highlight their distinct functions. +- `get_nonce` is a view function which takes no arguments and returns a 64-bit number (encoded as a base-10 string). +- `rlp_execute` is the main entry point for executing user transactions. It takes two inputs (encoded as a JSON object): `target` is an account ID (i.e. string) which indicates the account that is supposed to be the target of the Near action; and `tx_bytes_b64` is a string which is the base-64 encoding of the raw bytes of an Ethereum-like transaction. The process by which a Near action is derived from the Ethereum transaction is described below. -4. **Transaction Relayer (TR)**: Ethereum wallets cannot generate NEAR-compatible transactions. Instead, Ethereum-compatible transactions produced by them are processed by the Transaction Relayer, which embeds it into a NEAR transaction and forwards it to the user’s Wallet Contract. Again, while operated in conjunction with the RPC-Translator, the Transaction Relayer is distinct in its role. +The wallet contract has two state variables: the nonce, a 64-bit number; and a boolean flag indicating if a transaction is currently in progress. As with nonce values on Near access keys, the purpose of the wallet contract nonce is to prevent replaying the same Ethereum transaction more than once. The boolean flag prevents multiple transactions from being in-flight at the same time. The reason this is needed is because of the asynchronous nature of Near as compared with the synchronous nature of the **EVM**. On Ethereum if two transactions are sent (they must have sequential nonces per the Ethereum standard) then the all actions of the first will always happen before all the actions of the second. However, on Near there is no guarantee of the order of execution for receipts in different shards. Therefore, the only way to ensure that all actions from the first transaction are executed before all the actions of the second transaction is to prevent the second transaction from starting its execution until after the first one entirely finishes. -5. **[COMPLEX] Ethereum Translation Contract (ETC)**: Addressing the incompatibility between NEAR's human-readable account names and Ethereum's cryptographic hash-based addresses, the ETC functions as an on-chain mapping system. This system records NEAR-compatible input values (like NEAR account names and smart contract function names) and maps them to their corresponding Ethereum-compatible cryptographic hashes. This feature is vital for preserving familiar user experiences, such as recognizing ft_transfer operations in NEP-141 as fungible token (ERC20) transfers, rather than generic contract calls, and ensuring that fungible token balances are displayed in Web3 wallets. +#### Details of `rlp_execute` -### Transaction Flow +This function is named after the **RLP** standard in Ethereum. In particular, the `tx_bytes_b64` argument is be parsed into bytes from base-64 then those bytes are parsed into structured data assuming it is RLP encoded. The structure the data is parsed into is an Ethereum transaction. Ethereum transactions can have multiple different forms since the Ethereum protocol has evolved over time (there are "legacy" transactions, [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) type transactions, [EIP-2930](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2930.md) transactions). All these different forms are supported by the wallet contract (they are distinguished based on the "type byte" which starts the encoding as per [EIP-2718](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2718.md)) and are ultimately all transformed into a common data structure with the following fields: -The transaction flow between components is outlined as follows: +- `from`: the address associated with the private key that signed the transaction. +- `chain_id`: a numerical ID that is unique per **EVM**-chain. The Near chain ID values are discussed below. +- `nonce`: the nonce associated with this transaction. It must be equal to the wallet contracts's currently stored nonce for the transaction to be executed. +- `gas_limit`: the maximum amount of **EVM** gas the user is willing to spend on this transaction. +- `max_fee_per_gas`: the gas price the user is willing to pay. `gas_limit * max_fee_per_gas` gives the maximum amount of **Wei** the user is willing to pay for the transaction. +- `to`: the address of the account the transaction is targeting. This could be another **EOA** in the case of a base token transfer or the address of a smart contract in the case of what Near would refer to as a function call. In the Ethereum standard this field is allowed to be empty to indicate a new contract is being created, however that is forbidden by the wallet contract because Near currently does not support **EVM** bytecode, so there is not a reasonable way to emulate an Ethereum contract deployment. +- `value`: the amount of **Wei** attached to the transaction. +- `data`: the raw bytes which will be sent as a payload to the target address. If the target address is a contract it will use these bytes as input. -- The dApp frontend initiates a call to the Wallet Selector. -- The Wallet Selector then interacts with the Ethereum Wallet. -- The Ethereum Wallet communicates with the Transaction Relayer. -- The Transaction Relayer submits the transaction to the NEAR RPC. -- The NEAR protocol processes the transaction through the Wallet Contract. -- Finally, the Wallet Contract interacts with the dApp contract. +Note: some Ethereum transaction fields are intentionally omitted because they are unused by the wallet contract. -Below are detailed examples, with implementation specifics provided subsequently. +These fields are used to validate the transaction and derive Near actions that the wallet contract will create as receipts. The details of this process are described below. -#### Example Transaction Flow: Incoming $NEAR Transfer +**Ethereum transaction validation** -- Alice, a NEAR compatible wallet user, controls the alice.near account on NEAR. -- Bob, an Ethereum compatible wallet user, controls the Ethereum address (EOA) `0xb794f5ea0ba39494ce839613fffba74279579268`. -- Alice directs her NEAR wallet to transfer 2 $NEAR to `0xb794f5ea0ba39494ce839613fffba74279579268`. -- On NEAR, an implicit account named `0xb794f5ea0ba39494ce839613fffba74279579268` is created, with a Wallet Contract deployed to it by the NEAR Protocol. -- Bob’s wallet periodically queries the `eth_getBalance` RPC method via the RPC-Translator. Once Alice’s transaction is confirmed on-chain, the RPC-Translator reports the balance of the implicit account, which is then displayed to Bob by his wallet. +The following validation conditions MUST pass for the wallet contract to accept a transaction. -This process aligns with the user experience expectations of both NEAR and Ethereum users. +1. `from` address when formatted as hex-encoded with `0x` prefix MUST match the current account ID (i.e. the wallet contract's account ID). +2. `chain_id` MUST match one of the following values depending on the Near chain the wallet contract is deployed to: mainnet -> 397; testnet -> 398; any other chain -> 399. The mainnet and testnet values are registered with the [official Ethereum ecosystem registry of chain IDs](https://github.com/ethereum-lists/chains). +3. `nonce` MUST match the nonce value currently stored in the contract state. +4. `to` address MUST either (a) be equal to `keccak256(target)[12,32]` (where `target` is other argument passed to the `rlp_execute` function) or (b) when `to` is formatted as hex-encoded with `0x` prefix it MUST be equal to `target`. In case (b) there is an additional validation check that the `to` address is not registered in the "Ethereum Translation Contract" (ETC). The details of this check and why it is needed are discussed in Appendix C. +5. `value` MUST be less than or equal to `(2**128 - 1) // 1_000_000`. This condition arises from the mismatch is decimal places between Ether and NEAR which is discussed in the definition of **Wei** in Appendix A. Essentially, we must ensure the `value` can be mapped into a valid amount of yoctoNEAR, which means `value * 1_000_000 <= u128::MAX`. -#### Example Transaction Flow: Outgoing Function Call +**Converting Ethereum transaction into Near actions** -- Bob, using an Ethereum compatible wallet, controls the Ethereum address `0xb794f5ea0ba39494ce839613fffba74279579268`. -- DefiEx, a dApp on NEAR, consists of a web frontend and a smart contract at `defiex.near`. -- Bob uses DefiEx’s web frontend, which employs the **Wallet Selector** to connect his Ethereum compatible wallet. The frontend invokes the `signAndSendTransaction` method, typical in NEAR wallet interactions. -- Wallet Selector addon constructs an Ethereum transaction, which is then sent to Bob's wallet and signed. -- The Ethereum Wallet communicates this transaction to the **Transaction Relayer** via `eth_sendRawTransaction`. -- The Transaction Relayer validates the Ethereum transaction, converts it into a NEAR-compatible format, and sends it to Bob’s Wallet Contract at `0xb794f5ea0ba39494ce839613fffba74279579268`. -- The **Wallet Contract** decodes the Ethereum transaction, verifies the signature against the Ethereum address, and executes the corresponding action on the DefiEx smart contract. +TODO -This method meets the expectations of both NEAR developers and Ethereum users. +#### Interaction with Web3 relayers -#### [COMPLEX] Example Transaction Flow: NEP-141 Transfer from Metamask +TODO -- Charlie, a Metamask user, controls the Ethereum address `0xb794f5ea0ba39494ce839613fffba74279579268`. -- Declan, another Metamask user, controls the Ethereum address `0xf977814e90da44bfa03b6295a0616a897441acec`. -- Charlie, having acquired USDT on a centralized exchange, wishes to transfer 10 NEAR-native USDT to Declan’s address. -- Charlie adds NEAR-native USDT to Metamask, using an address derived from the `keccak-256` hash of the NEAR account `usdt.tether-token.near`. -- Metamask, via RPC Translator, displays USDT as a standard ERC20 token. -- Charlie initiates a transfer from Metamask to Declan. This is processed as a standard ERC20 `transfer` method call. -- The Transaction Relayer converts this into a NEAR transaction and sends it to the NEAR Blockchain. -- The Wallet Contract identifies this as a special ERC-20 transfer, queries the **Ethereum Translation Contract** for a corresponding NEAR contract, and constructs an action for the `usdt.tether-token.near` contract. - - First, it checks if Declan’s account is registered with USDT, funding the storage if necessary. - - Next, it processes the `ft_transfer` action, attaching `1 yoctoNEAR` as per [NEP-141](https://nomicon.io/Standards/Tokens/FungibleToken/Core). -- Post-transaction, the RPC Translator provides ERC-20 transaction logs to Metamask, confirming the transfer to Charlie. - -This transaction flow adheres to the expectations of Ethereum users. - -### Wallet Selector and Transaction Transformation - -Ethereum transactions, [RLP encoded](https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/), encapsulate the following information: - -- `To` address of the transaction, -- `From` address (derived from the signature), -- `Gas price`, -- `Gas limit`, -- `Nonce`, -- Base token transfer amount within a transaction (`value`), -- `Chain ID`, -- `Data field`, -- `Signature` (yielding the public key and `From` address). - -For function calls to a smart contract, the data field contains: - -- The `Function Selector`, -- Function call parameters. - -Both `To` and `From` addresses, encoded in hexadecimal and prefixed with “0x”, comprise 20 bytes. The `From` address is the right-most 20 bytes of the **Keccak-256** hash of the binary public key of the transaction sender (an **EOA**, externally owned address). The To address signifies either the recipient EOA or an on-chain contract address. - -The `Function Selector` is the first 4 bytes of the **Keccak-256 hash** of the full function signature intended for the contract specified by the To address. For instance, the ERC-20 standard function `transfer(address _to, uint256 _value)` has the Function Selector `keccak-256("transfer(address,uint256)")[0,4]`, equal to `0xa9059cbb`. - -#### Ethereum Transaction Construction - -- Set the `chain ID` to a public constant (equal to [397](https://chainlist.org/chain/397) for mainnet and [398](https://chainlist.org/chain/398) for testnet). -- The `gas price`, fetched by the Ethereum Wallet from the RPC Translator (sourced from NEAR RPC), reflects the NEAR gas price. - - [COMPLEX]: Align Ethereum's gas consumption (usually in thousands or millions) with NEAR's TGas denomination. For instance, a 50k Ethereum gas consumption for ERC20 transfers aligns with 2-10 TGas in NEP-141 transfers. A **100,000x multiplier** is proposed for translating NEAR TGas to Ethereum gas. For example, a 10 TGas NEAR transaction would appear as 1M Ethereum Gas in an Ethereum wallet. Similarly, apply the inverse multiplier to gas prices. A typical NEAR gas price of `1e-4` $NEAR per TGas translates to `1e-9` $NEAR or `1 gwei` for Ethereum wallets, a familiar unit for Ethereum users. -- The `gas limit` is determined by the dApp-developer or through `eth_estimateGas` by the Ethereum Wallet, applying the aforementioned recalculation rules. -- The `nonce` and `signature` are managed by the Ethereum wallet, in line with Ethereum protocol. -- The `From` address adheres to Ethereum protocol too. - -#### Special Considerations for To and Data Field - -The `To` address and `data field` creation vary based on the dApp's input to the Wallet Selector: - -- **[COMPLEX]** If the `receiverID` matches `^0x[a-f0-9]{40}$` (indicating another Ethereum account), it's treated as an EOA and used as-is. This case is restricted to $NEAR transfers (excluding smart contract calls) and is integral to the **Ethereum standards simulation**. - - Note: **NEAR protocol will not host Ethereum smart contracts on Ethereum-style accounts** post-implementation of this proposal. -- Otherwise, the `receiverID` refers to a non-EOA account on NEAR. Here `keccak-256(receiverID)[12,32]` is used as the `To` address. - -For the `data field`: - -- When initiated by the Wallet Selector, function selectors and parameters mirror NEAR actions, encoded as native Ethereum types. For example: - - `FunctionCall(to: string, method: string, args: bytes, yoctoNear: uint32)` - - `Transfer(to: string, yoctoNear: uint32)` - - `Stake(public_key: bytes, yoctoNear: uint32)` - - `AddKey(public_key: bytes, nonce: uint64, is_full_access: bool, allowance: uint256, receiver_id: string, method_names: string[])` - - etc. -- **[COMPLEX]** The `yoctoNear` values are 32-bit, accommodating values under `1e6`. The total `yoctoNEAR` attached to a NEAR Action is `1e6 * + `, enabling: - - Accurate display of $NEAR balances in Ethereum wallets (assuming 18 decimals). - - Compatibility with NEAR standards like NEP-141, requiring 1 yoctoNEAR for ft_transfer and ft_transfer_call methods. -- **[COMPLEX]** If initiated by the Ethereum wallet (limited use cases like native or fungible token transfers), the data field is retained as-is. For instance, ERC-20 `transfer(to address, amount uint256)` calls map to NEAR actions in the Wallet Contract. - -### Wallet Contract - -The Wallet Contract is designed to process Ethereum transactions through the method `executeRLP(target: AccountId, rlp_transaction: Vec)`. The following steps outline its operation: - -1. **Parse the Transaction**: It starts by parsing the incoming Ethereum transaction. - -2. **Verify Target Address**: It checks if the `target` equals the transaction's `To` address, or if `keccak256(target)[12,32]` matches the `To` address. - -3. **Signature Verification and Extraction**: The contract verifies the signature to extract the `Public key` and `chainID`, ensuring the integrity of the transaction. - -4. **`From` Address Creation and Verification**: It generates the `From` address from the public key and confirms that it aligns with the `current_account_id` (the ID of the Wallet Contract). - -5. **`ChainID` Validation**: The contract validates that the `chainID` corresponds to the constant defined for NEAR. - -6. **`Nonce` Verification and Update**: It ensures the transaction nonce matches the stored nonce and then increments the stored nonce by one. - -7. **[COMPLEX] Value Calculation**: The contract sets the `Value` by multiplying the Ethereum transaction value by 1e6 and adding the `yoctoNear` value from the data field. It then confirms the attached deposit is either greater than or equal to this value or identifies it as a self-transaction (refer to **TR and Gas Payment** section for details). - -8. **[COMPLEX] ETC Lookup and Execution for Ethereum Transfers**: - - - If the `To` Address equals the `target` and the `value` is more than zero (indicating an Ethereum transfer), the Wallet Contract consults the ETC ("Ethereum Translation Contract") for the `To` address. - - If the ETC lacks an entry for the `To` address, it implies the recipient is an Ethereum EOA. In this case, the transaction is an ordinary transfer and should be executed as such. - - If the ETC contains an entry for the `To` address, indicating the recipient is a NEAR smart contract simulating an Ethereum standard (like NEP-141), the data field of the transaction is parsed against supported ERC standard calls to form the appropriate NEAR action. - -9. **NEAR Native Account Transfer**: If the data field is empty, it transfers the value to the target, typical for a $NEAR transfer to a NEAR-native account. Values below `1e6 yoctoNEAR` cannot be transferred. - -10. **Function Selector and Parameter Verification**: In cases where the data field is not empty, the contract verifies the function selector against matching NEAR actions. - -11. **Execution of NEAR Action**: After parameter verification, the Wallet Contract executes the corresponding NEAR action by creating a promise. - -These steps ensure that the Wallet Contract accurately processes and executes transactions, bridging the gap between Ethereum and NEAR protocols. - -### Transaction Relayer and Gas Payment - -The Transaction Relayer (TR) serves as an HTTP-JSONRPC endpoint compatible with Ethereum, primarily implementing the eth_sendRawTransaction method. Its validation process involves: - -- Ensuring correct encoding and signing of the transaction. -- Verifying the existence of the sender as an account on NEAR. -- Checking if the nonce is greater than the stored value in the Wallet Contract. - -Upon receiving a transaction, the TR assesses the `To` Address: - -- If it exists on-chain, the TR crafts a transaction with a FunctionCall to `executeRLP` on the `From` address account, setting `target` to the `To` Address. -- **[COMPLEX]** If the `To` Address is not on-chain, the TR consults the ETC (Ethereum Translation Contract) for the preimage of the `To` Address. If retrievable, it creates a FunctionCall to `executeRLP` on the `From` address account, directing `target` to the ETC result. - -#### Approaches to Gas Payment - -This project's challenge lies in appropriately assigning gas costs to the Ethereum-compatible account. Our proposed solutions are: - -1. **Simplified Gas Calculation**: By default, all transactions use the maximum gas limit, or a limit specified by the dApp developer, to bypass the complexity of gas amount computation. - -2. Gas Payment Scheme: - - - **Register the account with TR**. The Ethereum wallet sets up a `functionCall` key on its account, authorizing the `executeRLP` method call with a public key from the Transaction Relayer. This setup, compatible with the Transaction Translation model, employs the `AddKey` Action. Received transactions under this scheme are forwarded and paid for by the Transaction Relayer. Alternatively, the Wallet Contract could reimburse the Transaction Relayer via a `Transfer` promise. - - Once an Ethereum wallet is registered with the Transaction Relayer, this `functionCall` key is utilized for all subsequent transactions. Effectively, **the Transaction Relayer operates on the account's behalf, with the account bearing all gas costs.** - - To mitigate potential abuse by the Transaction Relayer, the Wallet Contract can revoke the `functionCall` key in cases of non-parseable transactions, invalid signatures, or nonce replay (subject to specific time and value deltas). This limitation restricts a dishonest Transaction Relayer to a single invalid transaction's NEAR gas cost (typically not exceeding 0.03 $NEAR under normal conditions). - -**[COMPLEX]**: This approach avoids unnecessary cross-contract calls, maintaining NEAR protocol interactivity for Ethereum users. - -**Optional Free Transactions**: Some relayers may offer a number of free transactions as an incentive for users to engage with NEAR. This can be achieved by the relayer's account directly calling the Wallet Contract's `executeRLP` function, instead of using the user’s account's `functionCall` access key. Some of these transactions may require attachment of $NEAR for covering storage staking or other requirements of NEAR protocol standards. - -### RPC Translator - -The RPC-Translator, functioning as an HTTP-JSONRPC endpoint compatible with Ethereum, implements a set of methods crucial for supporting Ethereum wallets. These include: - -- `eth_chainId`: Returns a constant value. -- `eth_gasPrice`: Fetches and recalculates respective data from NEAR RPC. -- `eth_estimateGas`: Returns a constant `30M`, aligned with the gas recalculation considerations previously mentioned. -- `eth_blockNumber`: Provides the current NEAR block height. -- `eth_getBalance`: Delivers the account's $NEAR balance, adjusted to Ethereum's decimal count (omitting the last 6 digits). -- `eth_getTransactionCount`: Retrieves the nonce value from the account's Wallet Contract. -- `eth_getTransactionByHash` and `eth_getTransactionReceipt`: Present transaction details by indexing the NEAR chain, with values recalculated for Ethereum's decimal count. -- `get_logs`: Offers log information from the NEAR chain, reformatted and recalculated for Ethereum compatibility. -- `eth_call`: Conducts a view call against NEAR RPC, translating parameters as per the **Ethereum standards simulation**. - -These methods enable Ethereum wallets to create transactions and access transaction status. The proposal also accommodates direct $NEAR and ERC20 transfers from Ethereum wallets. - -### [COMPLEX] Ethereum Standards Simulation and Ethereum Translation Contract - -To bridge the gap between NEAR's and Ethereum's differing addressing schemes, especially for interactions initiated directly from web3 wallets, the **Ethereum Translation Contract (ETC)** is pivotal. It maintains a simple mapping between Ethereum transaction hashes and corresponding NEAR account names: - -- `key`: `keccak256()[12,32]` -- `value`: `` - -The ETC provides two core functions: - -- `record(account_id: AccountId)`: Calculates the hash and records it in the map, requiring a deposit for storage staking. It is not applicable for Ethereum-style accounts. -- `lookup(hash: [u8; 32]) -> Option`: A view function that returns the associated NEAR account for a given hash, if available. - -NEAR accounts intending to interact with Ethereum wallets must register with the ETC. This registration is permissionless, allowing third-party on-the-fly registrations if necessary. - -It's important to note that ETC registration is not mandatory for most NEAR applications. For instance, applications like ref.finance, typically not called directly from Metamask, would route their contract calls through the frontend, thereby utilizing the Wallet Selector's transaction translation. - -In the initial phase, the focus will be on $NEAR token transfers and ERC-20 transfers, and approval management in the **Ethereum Standards Simulation**. Future expansions may include additional standards in the Wallet Contract (refer to the testing and upgrades section). The ETC itself does not require upgrades to support these enhancements. - -## Implementation Notes - -The proposed solution necessitates a protocol change for **deploying the Wallet Contract on implicit Ethereum accounts**. To facilitate this, we suggest integrating the Wallet Contract as a core feature of the NEAR protocol. This change would treat all Ethereum accounts as if they already have the Wallet Contract deployed. - -Additionally, to enhance security and align with Ethereum's operational flows, we propose prohibiting the deployment of other contracts on Ethereum accounts. This can be achieved by restricting Ethereum users from adding full access keys to these accounts, thereby significantly reducing the attack surface. - -## Testing and Upgrades - -Integrating the Wallet Contract as a protocol feature requires synchronized testing and upgrade processes between `nearcore` and the Wallet Contract. Fortunately, due to the absence of cross-dependencies, the Wallet Contract can be developed, maintained, and tested independently. Its latest stable version would then be incorporated into the `nearcore` Testnet release, followed by integration into the Mainnet release. - -We recommend that any modifications to the Wallet Contract's functionality, such as adding support for new Ethereum standards over time, should be incorporated into the NEAR enhancement process. This approach ensures a structured and systematic update mechanism. - -The development and testing of off-chain components, like the RPC-Translator, will precede those of on-chain components. This sequential approach allows for thorough testing and refinement of each component before integration into the larger system. +## Reference implementation -## Limitations and Risks +The protocol changes necessary for the project include: -- **[COMPLEX] Dependence on Ethereum Standards Simulation**: The solution heavily depends on simulating Ethereum standards in the Wallet Contract and RPC-Translator. While standards like ERC20 are manageable, others may be too complex or impossible to emulate accurately. +- Creating Ethereum-like (0x) implicit accounts using `Transfer` action, +- Automatically deploying the wallet contract to those 0x implicit accounts. -- **[COMPLEX] Ambiguity in Transaction Conversion**: The emulation of Ethereum standards may lead to confusion in indexing or displaying transactions due to the dual paths of NEP-141 transfer encoding – either through standard conversion via Wallet Selector or originating directly from the wallet. This does not introduce new attack vectors. Users in general should exercise caution and avoid transactions with unfamiliar contracts or frontends. +These protocol changes are implemented in nearcore ([eth-implicit accounts PR 10224](https://github.com/near/nearcore/pull/10224), [wallet contract implementation](https://github.com/near/nearcore/tree/1ab9b42c3d723604a214e685d8ed39f7d6434ae2/runtime/near-wallet-contract/implementation)) and have been stabilized in protocol version 70 ([PR 11765](https://github.com/near/nearcore/pull/11765)). -- **RPC-Translator Limitations**: The RPC-Translator is tailored for wallet interactions and simple chain functions, but it's not equipped for advanced use cases requiring complete block data. Extending its capabilities could be challenging and resource-intensive. +## Security Implications -- **[COMPLEX] Challenges with the Ethereum Translation Contract (ETC)**: The ETC, vital for integrating NEAR accounts with Ethereum wallets, adds complexity and can lead to user experience issues if registration is overlooked. Automating ETC population through indexing services could mitigate this risk. Future developments might explore on-chain contract lists and function signatures for introspection. +The wallet contract must uphold the invariant that only the owner of the private key can make the wallet contract create Near actions. The wallet contract has been audited and is believed to be secure. -- **Power and Opacity of Wallet Contract**: The Wallet Contract, pivotal for executing NEAR Actions, may be obscure to Ethereum Wallet users. This necessitates stringent checks and limitations on action parameters, as noted in the proposal to restrict full access key additions. +## Alternatives -- **Lack of Batch Transaction Support**: The proposal currently does not accommodate batch transactions available in Wallet Selector, primarily due to limited utility and the complexity it would introduce, along with associated security concerns. +See the "Prior work" section of the [original NEP-518 issue](https://github.com/near/NEPs/issues/518). -- **[COMPLEX] Potential for inclusion NEAR account functionality in Wallet Contract**: An expanded version of the Wallet Contract, which includes NEAR account functionality, could retain valuable NEAR user patterns. For example, allowing using BOS frontend functionCall keys for Ethereum accounts. This would facilitate simple actions without needing to sign each transaction in the Ethereum wallet. While promising, this approach requires careful management. +## Future possibilities -- **Wallet Contract Upgrades**: Positioning the Wallet Contract as a protocol feature could limit its upgradability and inadvertently enable smart contract deployments with unbilled storage allocation. Careful implementation can mitigate potential storage growth on NEAR. +See the "Future Opportunities" section of the [original NEP-518 issue](https://github.com/near/NEPs/issues/518). -- **Exclusion of Native Ethereum Contract Deployment on NEAR**: The proposal does not address the deployment of Ethereum contracts natively on NEAR. Although this is possible via the Aurora network, interactions between NEAR-onboarded Ethereum users and Aurora contracts would necessitate additional steps, like asset bridging or using the [Aurora XCC SDK](https://dev.aurora.dev/posts/cross-ecosystem-communication). +## Consequences -## Future Opportunities +### Positive -- **Expansion of Ethereum Standards**: A key future development involves simulating other Ethereum standards such as ERC-721, ERC-1155, or ERC-3643. This would enhance NEAR's frontend compatibility with various Ethereum use cases. -- **Account Abstraction and Extended Features**: This proposal lays the groundwork for account abstraction, potentially leading to the introduction of advanced features in the Wallet Contract. These could include native support for multi-signatures, time-locks, social recovery, and alternative signature algorithms compatible with secure elements in widespread devices. -- **Aurora Integration**: Exploring integration with Aurora to enable interactions with Aurora-hosted contracts through NEAR, utilizing Ethereum transaction formats and processes. -- **Bridging and Oracle Onboarding**: This model could facilitate simplified bridging and oracle integration, contributing to the expansion of NEAR's infrastructure. +- All Ethereum users can easily onboard to Near -## Scope of the Project +### Neutral -The scope of this project is extensive, encompassing several critical components: +- New implicit account type with a protocol-level smart contract deployed by default. -1. Core Technology: +### Backwards Compatibility - - **Wallet Contract**: A crucial, security-sensitive component requiring detailed audits and testing. - - **`nearcore` Protocol Upgrade**: Modification to treat Ethereum-style accounts as implicit and deploy the Wallet Contract on them. - - **Wallet Selector Add-Ons**: Ensuring compatibility with Metamask and WalletConnect-compatible wallets. - - **Transaction Relayer and RPC Translator**: Key elements of the NEAR Web3 RPC middleware, working alongside the NEAR node. - - **Ethereum Translation Contract (ETC)**: A simpler component in comparison to others, yet vital for the system. +As pointed out in [PR 11606](https://github.com/near/nearcore/pull/11606) there are 5552 accounts on mainnet today with account IDs that would classify them as eth-implicit accounts. For backwards compatibility, these accounts will not be changed in any way (their access keys and contract code will be left in place) and therefore will in fact still be normal Near accounts as opposed to eth-implicit accounts because they have full access keys and possibly a contract different from the protocol-sanctioned wallet contract. -2. **Integration Testing**: Given the solution's distributed nature, comprehensive integration testing is imperative. +## Appendix A - Glossary -3. **Documentation Development**: Detailed documentation is necessary, enabling third-party developers to understand and potentially replicate parts of the system, except for the Wallet Contract. +Below is a list of Ethereum-related terms and their definitions. -4. **Node Operators Awareness and Public Infrastructure**: Launching and maintaining the NEAR Web3 RPC as a public service, similar to the existing NEAR RPC, and informing third-party node operators about the update and maintenance protocols. +- **Ethereum Virtual Machine (EVM)**: the virtual machine used to execute smart contracts on the Ethereum blockchain. "EVM-compatible" is often used interchangeably with "Ethereum compatible". +- **Externally owned account (EOA)**: An Ethereum account for which a user has the private key. Unlike Near, on Ethereum there is a distinction between contracts and user accounts. User accounts cannot have contract code and contract accounts cannot initiate a transaction. +- **Recursive Length Prefix (RLP) serialization**: An Ethereum ecosystem standard for encoding structured data as bytes. It plays a similar role to `borsh` in the Near ecosystem. +- **Wei**: the smallest unit of the base token for Ethereum. It plays a similar role to yoctoNEAR in the Near ecosystem. An important difference between Wei and yoctoNEAR is that 1 Ether (the typical unit for the base token on Ethereum) is equal to 10^18 Wei, while 1 NEAR is equal to 10^24 yoctoNEAR. Phrased another way, Ether has 18 decimal places while NEAR has 24. This difference in precision creates minor complexities in the wallet contract. -5. **Integration with Key Platforms**: For effective Ethereum user engagement, integration with the following is essential: +## Appendix B - How addresses are derived in Ethereum - - **Centralized Exchanges**: Permitting withdrawals to Ethereum-style addresses. - - **Bridges**: Facilitating login on the NEAR side with Ethereum-compatible wallets. - - **NEAR-native Projects**: Updating Wallet Selector configurations to support Ethereum-compatible wallet logins. Anticipated challenges include wallet address display issues, requiring adjustments. - - **Products to allow for key NEAR Wallet functionality**. Allowing Ethereum users to exercise core features of the NEAR protocol in a dApp: staking, key management, work with lockup contracts, etc. These may be implemented as BOS components. +On Ethereum all accounts are identified by a 20-byte address. The address of a user account is derived from a user's private key in the following way: -6. **Bug Bounty Program**: Establishing a program to address potential vulnerabilities, especially for on-chain components like the Wallet Contract and ETC. +1. Compute the user's public key from the private key (this step can be omitted if you already, indeed only, know the public key). +2. Compute the keccak256 hash of the public key. +3. Return the rightmost 20 bytes of this hash. -7. **Ongoing Development and Bug Fixes**: The project's complexity necessitates a proactive approach to bug fixes and future enhancements. +This is summarized by the following formula: `address = keccak256(public_key)[12,32]`. -## Reference implementation +## Appendix C - Ethereum Translation Contract (ETC) -The protocol changes necessary for the project include: +TODO -- Creating Ethereum-like (0x) implicit accounts using `Transfer` action, -- Automatically deploying the wallet contract to those 0x implicit accounts. +## Copyright -These protocol changes are implemented in nearcore ([eth-implicit accounts PR 10224](https://github.com/near/nearcore/pull/10224), [wallet contract implementation](https://github.com/near/nearcore/tree/1ab9b42c3d723604a214e685d8ed39f7d6434ae2/runtime/near-wallet-contract/implementation)) and have been stabilized in protocol version 70 ([PR 11765](https://github.com/near/nearcore/pull/11765)). +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From da5b9024950a7dde5133b388ca7015e1da10cf5a Mon Sep 17 00:00:00 2001 From: Michael Birch Date: Mon, 29 Jul 2024 15:52:45 +0200 Subject: [PATCH 3/6] Fix typo --- neps/nep-0518.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neps/nep-0518.md b/neps/nep-0518.md index 7b60bb668..d37aede00 100644 --- a/neps/nep-0518.md +++ b/neps/nep-0518.md @@ -35,7 +35,7 @@ Implementing Web3 wallet support in NEAR Protocol, with an emphasis on user expe **Definition**: An eth-implicit account is a top-level Near account with ID of the form `^0x[a-f0-9]{40}$` (i.e. 42-characters with `0x` as a prefix followed by 40 characters of hex-encoded data which represents a 20-byte address). -Eth-implicit accounts, as the name suggests, are implicit accounts on Near. This means MUST be automatically created by a `Transfer` action if the target account ID does not exist. This includes being created even if the amount being transferred is zero (per the prior [NEP on zero balance accounts](https://github.com/near/NEPs/blob/master/neps/nep-0448.md)). Eth-implicit accounts represent an Ethereum **EOA** and therefore are controlled via the Ethereum private key corresponding to the address contained in the account ID (see Appendix B for a description of how 20-byte addresses are derived from a private key in the Ethereum ecosystem). To enable this control, eth-implicit accounts all have a smart contract deploy to them called the wallet contract (specification in the next section). +Eth-implicit accounts, as the name suggests, are implicit accounts on Near. This means if the target account ID does not exist during a `Transfer` action then it MUST be automatically created. This includes being created even if the amount being transferred is zero (per the prior [NEP on zero balance accounts](https://github.com/near/NEPs/blob/master/neps/nep-0448.md)). Eth-implicit accounts represent an Ethereum **EOA** and therefore are controlled via the Ethereum private key corresponding to the address contained in the account ID (see Appendix B for a description of how 20-byte addresses are derived from a private key in the Ethereum ecosystem). To enable this control, eth-implicit accounts all have a smart contract deploy to them called the wallet contract (specification in the next section). When an eth-implicit account is created the runtime MUST set the contract code equal to specific "magic bytes". These bytes come from a UTF-8 encoded string which is equal to the constant `near` appended with the base-58 encoding of the sha2 hash of the wallet contract code. This constant allows the contract runtime to lookup the full contract code without needing it to be stored multiple times in the state. As well as being more efficient for the protocol, this setting the code equal to a hash of the contract instead of the contract itself is what keeps the storage requirements of a new eth-implicit account small enough to be a zero balance account. From c2975313314d24f7890a4a8f17c5746aa9a4f665 Mon Sep 17 00:00:00 2001 From: Michael Birch Date: Mon, 29 Jul 2024 15:55:23 +0200 Subject: [PATCH 4/6] Do not use emphasis as heading --- neps/nep-0518.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neps/nep-0518.md b/neps/nep-0518.md index d37aede00..81063a1be 100644 --- a/neps/nep-0518.md +++ b/neps/nep-0518.md @@ -77,7 +77,7 @@ Note: some Ethereum transaction fields are intentionally omitted because they ar These fields are used to validate the transaction and derive Near actions that the wallet contract will create as receipts. The details of this process are described below. -**Ethereum transaction validation** +##### Ethereum transaction validation The following validation conditions MUST pass for the wallet contract to accept a transaction. @@ -87,7 +87,7 @@ The following validation conditions MUST pass for the wallet contract to accept 4. `to` address MUST either (a) be equal to `keccak256(target)[12,32]` (where `target` is other argument passed to the `rlp_execute` function) or (b) when `to` is formatted as hex-encoded with `0x` prefix it MUST be equal to `target`. In case (b) there is an additional validation check that the `to` address is not registered in the "Ethereum Translation Contract" (ETC). The details of this check and why it is needed are discussed in Appendix C. 5. `value` MUST be less than or equal to `(2**128 - 1) // 1_000_000`. This condition arises from the mismatch is decimal places between Ether and NEAR which is discussed in the definition of **Wei** in Appendix A. Essentially, we must ensure the `value` can be mapped into a valid amount of yoctoNEAR, which means `value * 1_000_000 <= u128::MAX`. -**Converting Ethereum transaction into Near actions** +##### Converting Ethereum transaction into Near actions TODO From c090b75f93044a45759ebb1cc1f70446a1b0004a Mon Sep 17 00:00:00 2001 From: Michael Birch Date: Mon, 29 Jul 2024 21:26:21 +0200 Subject: [PATCH 5/6] Complete missing TODOs --- neps/nep-0518.md | 73 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 5 deletions(-) diff --git a/neps/nep-0518.md b/neps/nep-0518.md index 81063a1be..8a256f1e9 100644 --- a/neps/nep-0518.md +++ b/neps/nep-0518.md @@ -1,7 +1,7 @@ --- NEP: 518 Title: Web3-Compatible Wallets Support -Authors: Aleksandr Shevchenko +Authors: Aleksandr Shevchenko , Michael Birch Status: New DiscussionsTo: https://github.com/near/NEPs/issues/518 Type: Protocol @@ -89,15 +89,66 @@ The following validation conditions MUST pass for the wallet contract to accept ##### Converting Ethereum transaction into Near actions -TODO +Each Ethereum transaction is converted to a single Near action (batch transactions are not supported) based on the `data` field. Following the Solidity convention of the first four bytes of the data being a **method selector**, the wallet contract checks the first four bytes of the `data` to see if it is a known Near action. The **method selectors** for Near actions supported by the wallet contract are determined by mapping the actions to an equivalent Solidity function signature as follows: + +- `functionCall(string,string,bytes,uint64,uint32)` +- `transfer(string,uint32)` +- `addKey(uint8,bytes,uint64,bool,bool,uint128,string,string[])` +- `deleteKey(uint8,bytes)` + +Note that the `uint32` fields in `functionCall` and `transfer` contain the amount of yoctoNEAR tha cannot be included in the Ethereum transaction's `value` field due to the difference in decimal places (see **Wei** definition in Appendix A), therefore the value there is always less than `1_000_000` so it will easily fit in a 32-bit number. These type signatures then hash to the **method selectors**: + +- FunctionCall: `0x6179b707` +- Transfer: `0x3ed64124` +- AddKey: `0x753ce5ab` +- DeleteKey: `0x3fc6d404` + +If the first four bytes of the `data` field matches one of these **method selectors** then the wallet contract will try to parse the remainder of the `data` into the corresponding type signature (assuming the data is Solidity ABI encoded). If this parsing succeeds then the resulting tuple of values can be converted to the corresponding Near action. Some additional validation is done in this case, depending on the action: + +- FunctionCall/Transfer: `target` MUST equal the first `string` parameter (interpreted as the receiver ID), the `uint32` parameter value MUST be less than `1_000_000`. +- AddKey/DeleteKey: the `uint8` parameter value MUST be 0 (corresponding to an ED25519 access key) or 1 (corresponding to a Secp256k1 access key), the `bytes` MUST be the appropriate length depending on the key type, `target` MUST equal the current account ID (since these actions can only act on the current account). + +Additionally, the first `bool` value of `addKey` must be `false` because adding a full access key is currently not supported by the wallet contract. The reason for this is to prevent users from changing the contract code deployed to the eth-implicit contract, as it could break the account's intended functionality. However, this restriction may be lifted in the future. + +If the first four bytes of `data` does not match one of these known selectors then the contract tries another set of known **method selectors** which come from the Ethereum ERC-20 standard: + +- `balanceOf(address)` -> `0x70a08231` +- `transfer(address,uint256)` -> `0xa9059cbb` +- `totalSupply()` -> `0x18160ddd` + +These **method selectors** are included because some Web3 wallets (for example MetaMask) allow a user to transfer tokens directly within the wallet interface. This interface produces an Ethereum transaction with Solidity ABI encoded data following the ERC-20 standard rather than the encoding of the Near actions outlined above. Therefore the wallet contract also knows how to parse these ERC-20 standard methods into Near actions so that the wallet interfaces still work according to the user's expectations. This feature of the wallet contract is called Ethereum Standards Emulation because it emulates the execution of an Ethereum standard. Currently ERC-20 is the only supported standard for emulation, but perhaps more will be added in the future. + +ERC-20 is Ethereum's fungible token standard, thus these calls are mapped to the corresponding NEP-141 `FunctionCall` actions: + +- `balanceOf` -> `ft_balance_of` +- `transfer` -> `ft_transfer` +- `totalSupply` -> `ft_total_supply` + +Note: it is intentional that not all the ERC-20 functions are emulated (in particular related to `approve`/`allowance`) because there is not the corresponding functionality in NEP-141. There is additional validation in the case of `transfer` that the amount is less than `u128::MAX` because the ERC-20 standard allows 256-bit amounts while the NEP-141 standard only allows 128-bit. The NEP-141 standard also has additional complexity that ERC-20 does not have because of the storage deposit requirement (a consequence of Near's storage staking). On Ethereum a user can transfer tokens to another account that has never held that kind of token before. On Near that is only possible if the user pays for the recipient's storage deposit first. Therefore, as part of the `transfer` emulation the wallet contract includes a call to `storage_balance_of` to check if a call to `storage_storage_deposit` is also needed before calling `ft_transfer`. + +If none of the known selectors match the first four bytes of `data` or the remainder of `data` fails to parse into the appropriate type signature then there is one more possible emulation that the wallet contract checks for. On Ethereum base token transfers are allowed to have arbitrary data included and some wallets use this feature as a sort of messaging protocol between addresses. Therefore, if the `data` is not processed and the `target` is another eth-implicit account, then we assume this is meant to emulate a base token transfer and thus a Near `Transfer` action is created. Otherwise, the wallet contract returns an error that the transaction could not be parsed. #### Interaction with Web3 relayers -TODO +Typically users will not be constructing the `rlp_execute` action themselves because the target user group are those who only have a Web3 wallet like MetaMask, not a Near wallet to sign Near transactions. Therefore, the Near transactions will be constructed and sent to the Near network on a user's behalf by relayers. These relayers expose the Ethereum standard JSON RPC so that Web3 wallets know how to as the relayer to send an Ethereum-like transaction and to query the status of that transaction. More details about relayers and the RPC they expose is found in the [NEP-518 issue description](https://github.com/near/NEPs/issues/518), but it out of scope for this document because they operated separately from the Near protocol itself. + +The relevant fact for the wallet contact specification is that relayers can ask their users to add a function call access key to their eth-implicit account which the relayer uses to call `rlp_execute`. By using an access key on the eth-implicit account itself, the relayer does not need to cover any gas costs for the user because the transaction originates from the wallet contract account itself. However, for this mechanism to be safe for users, relayers must be prevented from sending transactions to the wallet contract that the user did not intend. Otherwise relayers could maliciously burn the $NEAR of their users on excess calls to `rlp_execute` (even if those transactions return an error, gas is still spent in the process). + +For this reason, the wallet contract separates possible errors in the `rlp_execute` input into two categories: user errors and relayer errors. User errors are errors that arise from data signed by the user's private key and therefore cannot be spoofed by the relayer. Relayer errors arise from input that should not have been sent by an honest relayer in the first place. These relayer errors include: + +- Invalid Ethereum transaction nonce: if the nonce check fails then the relayer is at fault because it should have checked the nonce before sending the transaction. This prevents a malicious relayer from sending the same user-signed transaction over and over to burn the user's $NEAR unnecessarily. +- Invalid base-64 encoding in `tx_bytes_b64`: an honest relayer should only send valid arguments. If a relayer sends garbage input then it is faulty. +- Invalid Ethereum transaction encoding: similar to the error above, but with the issue occurring in the RLP-encoding instead of in the base-64 encoding. +- Invalid sender: if the address extracted from the signature on the Ethereum transaction does not match the wallet contract account ID then the relayer is faulty because it sent an incorrectly signed transaction. +- Invalid target: if the `target` validation relative to the `to` field in the user's signed Ethereum transaction fails then the relayer is faulty because it tried to misdirect the transaction to a different account than the user intended. +- Invalid chain id: similar to the invalid sender error, the relayer should only send transaction with a valid signature, including with the correct chain id. +- Insufficient gas: if the relayer does not attach as much Near gas to the transaction as the user asked for in the `gas_limit` field of their signed Ethereum transaction then it is faulty. This prevents a malicious relayer from intentionally making user transactions fail by not attaching enough gas to complete the action. + +If a relayer error happens then the wallet contract creates a callback to remove the relayers access key. This prevents them from repeatedly sending incorrect input. ## Reference implementation -The protocol changes necessary for the project include: +Summarizing the above, the protocol changes necessary for the Web3 wallets project include: - Creating Ethereum-like (0x) implicit accounts using `Transfer` action, - Automatically deploying the wallet contract to those 0x implicit accounts. @@ -136,6 +187,7 @@ Below is a list of Ethereum-related terms and their definitions. - **Ethereum Virtual Machine (EVM)**: the virtual machine used to execute smart contracts on the Ethereum blockchain. "EVM-compatible" is often used interchangeably with "Ethereum compatible". - **Externally owned account (EOA)**: An Ethereum account for which a user has the private key. Unlike Near, on Ethereum there is a distinction between contracts and user accounts. User accounts cannot have contract code and contract accounts cannot initiate a transaction. +- **Method selector**: By convention in Solidity contracts, the first four bytes of the input to a smart contract determine which method is executed (unlike Near where a method is explicitly specified as part of the `FunctionCall` action). These bytes are obtained by taking the first four bytes of the keccak256 hash of the type signature of the function. - **Recursive Length Prefix (RLP) serialization**: An Ethereum ecosystem standard for encoding structured data as bytes. It plays a similar role to `borsh` in the Near ecosystem. - **Wei**: the smallest unit of the base token for Ethereum. It plays a similar role to yoctoNEAR in the Near ecosystem. An important difference between Wei and yoctoNEAR is that 1 Ether (the typical unit for the base token on Ethereum) is equal to 10^18 Wei, while 1 NEAR is equal to 10^24 yoctoNEAR. Phrased another way, Ether has 18 decimal places while NEAR has 24. This difference in precision creates minor complexities in the wallet contract. @@ -151,7 +203,18 @@ This is summarized by the following formula: `address = keccak256(public_key)[12 ## Appendix C - Ethereum Translation Contract (ETC) -TODO +There is an additional contract which is tangentially related to the wallet contract. The [original NEP-518 issue](https://github.com/near/NEPs/issues/518) refers to it as the Ethereum Translation Contract (ETC), though perhaps a more descriptive name is the Ethereum address registrar. The implementation of this contract is not part of the protocol, however its account ID is because the account ID is hardcoded into the wallet contract. The reason is because the wallet contract occasionally needs the ETC to verify if the `target` argument to `rlp_execute` is properly set relative to the `to` field of the user's signed Ethereum transaction. The details of why ETC is needed and how it is used is described below. + +Recall that the user is signing an Ethereum transaction because the whole point of this project is to allow Web3 wallets like MetaMask to be used on Near. An Ethereum transaction specifies the target of a transaction using a 20-byte address because there are no named accounts on Ethereum. Therefore the user only signs over a 20-byte address to indicate their intent of what account is meant to receive this transaction. However, this is obviously insufficient information on Near because most accounts are named ones, not addresses. The purpose of the `target` argument to `rlp_execute` is to communicate the account ID of the receiver of the transaction and it must be consistent with the user's signed Ethereum transaction according to the validation conditions described in the "Ethereum transaction validation" section. + +Most of the time that means checking `to == keccak256(target)[12,32]` because the `target` will be some named Near account. However, it is possible that `target` be another eth-implicit account; this is the case for "emulated" base token transfers (emulated Ethereum standards are discussed in the section "Converting Ethereum transaction into Near actions"). Thus, we must also allow the possibility that `to == target`. Yet, this poses a problem because it means `target` could be set incorrectly if it was meant to be a named account satisfying the hash condition instead. The ETC closes the loophole by providing a reverse lookup from 20-byte address to named Near accounts where the association comes from the hash condition. + +To fully validate `target` in the case that `to == target` the wallet contract makes the following additional checks: + +- If the `data` field of the user's signed Ethereum transaction can be parsed into a Near action then confirm `target` matches the `receiver_id` of the corresponding action (this is statically known to be the current account ID in the case of `AddKey` and `DeleteKey`, and it is encoded with the action in the case of `FunctionCall` and `Transfer`). +- If the `data` field can be parsed as ERC-20 action then call the `lookup` method of the ETC to see if the `target` is registered. If it is registered then the `target` field is set incorrectly because the relayer should have set `target` equal to the named account returned from `ETC::lookup(to)`. This validity check ensures that emulated ERC-20 transactions are sent to the correct NEP-141 token account. If `target` is not registered then the transaction is interpreted as an emulated base token transfer with a message that happens to parse like an ERC-20 function call. + +Notably, for this security measure to be effective all widely used NEP-141 token accounts will need to be registered with ETC. ETC has a public method `register` which permissionlessly allows anyone to add an account ID they think is important. This openness id not a feasible attack vector for the system because of the one-way nature of the keccak256 hash function preventing an attacker from coming up with a Near account ID that corresponds to an address of their choosing. ## Copyright From 1f64d1fec30ef7e70a6a0675eb459a3eae1a6315 Mon Sep 17 00:00:00 2001 From: Michael Birch Date: Fri, 9 Aug 2024 16:30:07 +0200 Subject: [PATCH 6/6] Review suggestions --- neps/nep-0518.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/neps/nep-0518.md b/neps/nep-0518.md index 8a256f1e9..7ea39b676 100644 --- a/neps/nep-0518.md +++ b/neps/nep-0518.md @@ -37,7 +37,7 @@ Implementing Web3 wallet support in NEAR Protocol, with an emphasis on user expe Eth-implicit accounts, as the name suggests, are implicit accounts on Near. This means if the target account ID does not exist during a `Transfer` action then it MUST be automatically created. This includes being created even if the amount being transferred is zero (per the prior [NEP on zero balance accounts](https://github.com/near/NEPs/blob/master/neps/nep-0448.md)). Eth-implicit accounts represent an Ethereum **EOA** and therefore are controlled via the Ethereum private key corresponding to the address contained in the account ID (see Appendix B for a description of how 20-byte addresses are derived from a private key in the Ethereum ecosystem). To enable this control, eth-implicit accounts all have a smart contract deploy to them called the wallet contract (specification in the next section). -When an eth-implicit account is created the runtime MUST set the contract code equal to specific "magic bytes". These bytes come from a UTF-8 encoded string which is equal to the constant `near` appended with the base-58 encoding of the sha2 hash of the wallet contract code. This constant allows the contract runtime to lookup the full contract code without needing it to be stored multiple times in the state. As well as being more efficient for the protocol, this setting the code equal to a hash of the contract instead of the contract itself is what keeps the storage requirements of a new eth-implicit account small enough to be a zero balance account. +When an eth-implicit account is created the runtime MUST set the contract code equal to specific "magic bytes". These bytes come from a UTF-8 encoded string which is equal to the constant `near` appended with the base-58 encoding of the sha2 hash of the wallet contract code. This constant allows the contract runtime to lookup the full contract code without needing it to be stored multiple times in the state. As well as being more efficient for the protocol, setting the code equal to a hash of the contract instead of the contract itself keeps the storage requirements of a new eth-implicit account small enough to be a zero balance account. The magic bytes depend on the Near network chain id because the wallet contract (and therefore its hash) depends on the Near chain id. The magic bytes (UTF-8 encoded) for each Near chain id are listed below: @@ -58,11 +58,11 @@ The wallet contract has two public functions: - `get_nonce` is a view function which takes no arguments and returns a 64-bit number (encoded as a base-10 string). - `rlp_execute` is the main entry point for executing user transactions. It takes two inputs (encoded as a JSON object): `target` is an account ID (i.e. string) which indicates the account that is supposed to be the target of the Near action; and `tx_bytes_b64` is a string which is the base-64 encoding of the raw bytes of an Ethereum-like transaction. The process by which a Near action is derived from the Ethereum transaction is described below. -The wallet contract has two state variables: the nonce, a 64-bit number; and a boolean flag indicating if a transaction is currently in progress. As with nonce values on Near access keys, the purpose of the wallet contract nonce is to prevent replaying the same Ethereum transaction more than once. The boolean flag prevents multiple transactions from being in-flight at the same time. The reason this is needed is because of the asynchronous nature of Near as compared with the synchronous nature of the **EVM**. On Ethereum if two transactions are sent (they must have sequential nonces per the Ethereum standard) then the all actions of the first will always happen before all the actions of the second. However, on Near there is no guarantee of the order of execution for receipts in different shards. Therefore, the only way to ensure that all actions from the first transaction are executed before all the actions of the second transaction is to prevent the second transaction from starting its execution until after the first one entirely finishes. +The wallet contract has two state variables: the nonce, a 64-bit number; and a boolean flag indicating if a transaction is currently in progress. As with nonce values on Near access keys, the purpose of the wallet contract nonce is to prevent replaying the same Ethereum transaction more than once. The boolean flag prevents multiple transactions from being in-flight at the same time. The reason this is needed is because of the asynchronous nature of Near as compared with the synchronous nature of the **EVM**. On Ethereum if two transactions are sent (they must have sequential nonces per the Ethereum standard) all actions of the first will happen before all actions of the second. However, on Near there is no guarantee of the order of execution for receipts in different shards. Therefore, the only way to ensure that all actions from the first transaction are executed before all the actions of the second transaction is to prevent the second transaction from starting its execution until after the first one entirely finishes. #### Details of `rlp_execute` -This function is named after the **RLP** standard in Ethereum. In particular, the `tx_bytes_b64` argument is be parsed into bytes from base-64 then those bytes are parsed into structured data assuming it is RLP encoded. The structure the data is parsed into is an Ethereum transaction. Ethereum transactions can have multiple different forms since the Ethereum protocol has evolved over time (there are "legacy" transactions, [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) type transactions, [EIP-2930](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2930.md) transactions). All these different forms are supported by the wallet contract (they are distinguished based on the "type byte" which starts the encoding as per [EIP-2718](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2718.md)) and are ultimately all transformed into a common data structure with the following fields: +This function is named after the **RLP** standard in Ethereum. In particular, the `tx_bytes_b64` argument is parsed into bytes from base-64; then the bytes are parsed into structured data assuming it is RLP encoded; then the structured data is parsed into an Ethereum transaction. Ethereum transactions can have multiple different forms since the Ethereum protocol has evolved over time (there are "legacy" transactions, [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) type transactions, [EIP-2930](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2930.md) transactions). All these different forms are supported by the wallet contract (they are distinguished based on the "type byte" which starts the encoding as per [EIP-2718](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2718.md)) and are ultimately all transformed into a common data structure with the following fields: - `from`: the address associated with the private key that signed the transaction. - `chain_id`: a numerical ID that is unique per **EVM**-chain. The Near chain ID values are discussed below. @@ -96,7 +96,7 @@ Each Ethereum transaction is converted to a single Near action (batch transactio - `addKey(uint8,bytes,uint64,bool,bool,uint128,string,string[])` - `deleteKey(uint8,bytes)` -Note that the `uint32` fields in `functionCall` and `transfer` contain the amount of yoctoNEAR tha cannot be included in the Ethereum transaction's `value` field due to the difference in decimal places (see **Wei** definition in Appendix A), therefore the value there is always less than `1_000_000` so it will easily fit in a 32-bit number. These type signatures then hash to the **method selectors**: +Note that the `uint32` fields in `functionCall` and `transfer` contain the amount of yoctoNEAR that cannot be included in the Ethereum transaction's `value` field due to the difference in decimal places (see **Wei** definition in Appendix A), therefore the value there is always less than `1_000_000` so it will easily fit in a 32-bit number. These type signatures then hash to the **method selectors**: - FunctionCall: `0x6179b707` - Transfer: `0x3ed64124`