23rd Oct 2024
8 min read

Mainsail EVM - Transitioning to Full EVM Support

Over the past few months, we have been working on and experimenting with further improvements to Mainsail, most notably the integration of the EVM. After extensive testing and careful decisions, we’ve now moved all transaction handling into the EVM, including native ARK token transfers, Voting, and Validator Registrations & Resignations. Let’s go over the changes in more detail and outline the next steps for the upcoming months.

The previous blog posts explore performance improvements and additional changes as we work on integrating the EVM into the protocol.

After months of experimentation, we’ve decided to migrate all transaction handling to the EVM. This means that every action, whether it’s a transfer, vote, validator registration, or otherwise, now takes place within the EVM by calling equivalent smart contracts.

This approach offers several advantages, such as significantly reducing the effort needed to extend and maintain transaction logic, while also streamlining integration with the broader Web3 ecosystem.

Now, a single transaction type, called EvmCall, is exclusively used to replace all other transaction logic. It allows anyone to deploy new contracts, execute contract logic, and perform simple value transfers.

Furthermore, all wallet states are now part of the EVM and no longer separate. In EVM terms, wallets have now become accounts. More details on this will follow below.

Transaction Types Overview

To give a better overview of how the current transaction types will look like going forward:

Type Pre-EVM Post-EVM Smart Contract
Transfer Transfer EvmCall Native Token (Not ERC20)
Vote/Unvote Vote EvmCall Consensus.sol
ValidatorRegistration ValidatorRegistration EvmCall Consensus.sol
ValidatorResignation ValidatorResignation EvmCall Consensus.sol
MultiPayment MultiPayment EvmCall TBD*
MultiSignatureRegistration MultiSignatureRegistration EvmCall TBD*
UsernameRegistration UsernameRegistration EvmCall TBD*
UsernameResignation UsernameResignation EvmCall TBD*
  • *TBD = To Be Determined

We are currently evaluating Gnosis Safe or similar solutions for MultiSignature wallet support. As for UsernameRegistration and UsernameResignation, we are considering implementing them using ENS contracts, though no final decision has been made. MultiSignature support will be introduced at a later stage and is unlikely to be included in the initial public testnet of the Mainsail EVM.

Consensus Contract Integration

We recently merged our experimental fork containing changes to consolidate all transaction types into the Mainsail develop branch. Going forward, there is now a single transaction handler dedicated to processing only evm-call transactions.

For this purpose, a Consensus contract has been written and tightly integrated into the Mainsail protocol.

The contract’s primary purpose is to facilitate:

  • ValidatorRegistration
  • ValidatorResignation
  • Vote/Unvote

The ABI is available in the @mainsail/evm-contracts package. All Solidity source code (still experimental) is separated from the published packages due to differing CI requirements and can be found here .

As mentioned, Validators and Voters are now part of the EVM state. This means the main process (i.e., host) must call into the EVM during block commit to trigger rewards and update the active validator set.

// Validator registration transaction replacement
function registerValidator(bytes calldata bls12_381_public_key) external;
function deregisterValidator() external;// Vote transaction replacement
function vote(address addr) external;// Called on round change during block commit
function calculateTopValidators(uint8 n) external;
function getTopValidators() public view returns (Validator[] memory);

Disclaimer: The contract is still experimental and under development. We will provide a deep dive into this contract and others that are planned in an upcoming blog post.

Gas & Fees

All fees are calculated based on gasPrice and consumedGas. A transaction can only enter the transaction pool if the sender’s wallet has a balance of at least gasPrice * gasLimit. Since it’s impossible to know the exact gas consumption of a transaction beforehand, it is always assumed that a transaction may consume all available gas. After the transaction executes, the exact amount of gas consumed is deducted from the user’s balance, and any “overcharge” is adjusted accordingly in the transaction pool.

In a future blog post, we will provide a detailed explanation of how gas pricing interacts with various gas limits (e.g., block limit, transaction limit), as this is still under development.

Native Token

Currently, ARK’s Mainsail token functions as a native token, similar to ETH on Ethereum, and is not an ERC20 token. This creates the need for workarounds, such as wrapper tokens (like WETH), to use it in the same manner as an ERC20 token.

In contrast, Polygon treats its native token (MATIC/POL) like an ERC20 token, which offers several advantages. We are considering adopting a similar approach for the Mainsail native token and will evaluate the pros and cons. Depending on our research, we may follow Polygon’s route. For now, the token remains native, but a final decision will be made after further study to determine whether to go the ETH route or the MATIC/POL route.

State Changes

Wallets no longer exist inside the Mainsail state; they are now maintained within the EVM state. As a result, most of the logic previously housed in the @mainsail/state package has been removed. This includes all wallet-related logic, such as:

  • Wallet repositories
  • Wallet attributes
  • Wallet indexes
  • Wallet balance mutators
  • Wallets
  • State snapshots

The state now only retains basic logic like lastBlock, genesisBlock, height, and totalRound values.

Transaction Pool Changes

The transaction pool has also been simplified. It no longer executes transactions to verify whether they can be included in the pool, as the EVM includes transactions in blocks even if they are reverted. Consequently, the transaction handler no longer implements the throwIfCannotEnterPool and throwIfCannotBeApplied methods.

The transaction pool now processes transactions as follows:

1. Initialize the sender’s state

Retrieve the sender’s account from the EVM state, which consists of the current nonce and balance. If the sender’s state is already active due to unconfirmed transactions, this step is skipped.

2. Perform basic checks

The transaction pool performs basic checks before a transaction can be included. First, the transaction’s structure and signature are verified. The transaction must pass deserialization and schema validation before the sender’s signature is confirmed.

Next, the nonce and balance are checked. The nonce should match the existing nonce and be incremented when the transaction is included in the pool. The sender must also have sufficient funds, with the balance being at least gasLimit × gasFee. This amount, plus the transaction amount, is deducted from the sender’s current balance.

3. Collect transactions

Before a transaction is included in a block, the Mainsail main process collects candidate transactions from the transaction pool worker and executes them.

4. Remove transactions

Transactions included in a block are removed from the transaction pool. Since included transactions were at the bottom of the sender’s transaction stack, any remaining transactions are reprocessed based on the updated sender state (Step 1 & Step 2).

Transactions can also be removed if they expire. In this case, the sender’s state (nonce & balance) is not expected to change. The expired transaction is removed from the top of the sender’s transaction stack, the nonce is decremented, and the balance is restored.

JSON-RPC API

The JSON-RPC API is still a work in progress but is already available at localhost:4008 if you enable the @mainsail/api-evm package.

It currently supports a limited subset of methods tailored to Mainsail use cases, with the primary goal of integrating with existing wallets and tooling (e.g., MetaMask).

We will share more updates in the future.

HTTP API

The HTTP API can be used to query transaction receipts.

When API synchronization is enabled, receipts are synced to PostgreSQL and stored in the receipts table. You can query these receipts using the /receipts endpoint.

A receipt contains the logs and output of a transaction.

Upcoming Transaction Changes

Now that Mainsail only supports a single transaction type, achieving compatibility with existing Ethereum wallets and tooling (MetaMask, Ledger App, etc.) has become easier.

As a result, the transaction structure is being updated, along with the way transactions are signed.

We will explore this further in an upcoming blog post, but the most important changes include:

  • Transactions will be signed using Ethereum-style ECDSA signatures, enabling Mainsail to recover the public key and its signature from the transaction.
  • Transaction hash calculation will be based on RLP encoding.
  • Obsolete transaction fields like asset, version, vendorField, and expiration will be removed so that signing works the same way as Ethereum Type 2 (EIP1559) transactions.

However, much of this is an implementation detail and is abstracted away when using the EvmCallBuilder. For end users, the transaction JSON payload will still look very similar to previous versions when comparing them at a glance.

Some aspects are still under development, and the final transaction format may change, but this should give you an idea of what to expect.

What’s Next?

Last week, we successfully deployed the Mainsail EVM network with all of the changes outlined above, and it has been running smoothly without major issues. However, some decisions are still pending, particularly regarding certain transaction types and the final approach to the native token.

Over the next few weeks, we will focus on polishing the network, integrating changes based on ongoing research, and addressing any issues that arise on the internal testnet. We will likely reset the internal testnet a few times to accommodate these changes, as it’s easier to do now before more public users start running nodes. For this reason, we are not releasing the public testnet yet.

Once we have a more finalized version, we will replace the current Mainsail network with the new Mainsail EVM network. At that point, we will invite the community to help us test, familiarize themselves with the changes, register Validators, and assist in moving toward the final release.

In the coming weeks, we’ll also be publishing more technical blog posts that provide examples of the changes, as well as details on how the new contracts and transactions will work.

We hope this blog post answers some of your questions and gives you insight into what we’ve been working on over the past few months.

Follow Us on X (formerly Twitter)

Stay updated on all our latest releases by following us on X (formerly Twitter). Be sure to check our blog regularly for updates, including our weekly development report, where you can track our progress and join us on the journey toward a decentralized future.

Share:

Get in Touch!

Whether you want to learn more about ARK Ecosystem, want to apply for developer bounty, become our partner or just want to say Hello, get in touch and we will get back to you.



An Ecosystem of Developers

Join us on our journey to create the future of Web3.