Possible Solutions for Unsustainable Storage Growth

Table of Contents

Introduction

Background

Moonbeam and Moonriver have historically provided Web3 developers and users a low fee environment in which to experiment with Moonbeam’s connected contract capabilities. However, as the Moonbeam and Moonriver ecosystems continue to mature, so too must the fee model to ensure long term sustainability and scalability.

As discussed in this previous forum post, a Dynamic Fee model had already been in the process of making its way to Moonbeam but it became necessary to accelerate this effort due to a particular use case which was causing a rapid increase in the size of chain storage which was clearly not sustainable.

While the addition of Dynamic Fees resulted in the rate of growth of storage to decrease, it has only bought time - long term sustainability remains at risk.

A fundamental reason for this is that the fee model on Moonbeam and Moonriver was implemented in such a way as to maximize compatibility with Ethereum and this model disproportionately leans towards transaction compute costs over storage costs. When one considers that the current model is to pay once to store data forever, it becomes clear to the reader that this could be problematic and that fees related to data storage must be appropriately set.

Here is the rate of growth as of May 24th. The effect of Dynamic Fees is visible but storage continues to grow at an alarming rate :

(Blue: Accounts, Red: Smart Contracts)

Purpose of Post

Over the past few weeks, many of the Moonbeam core developers have been discussing various ways that the fee model could be modified in order to drive better alignment between user/developer behavior and long term sustainability.

The purpose of this post is to provide a high level summary of possible solutions to the community in order to provide the community with an opportunity to weigh in on these proposals, ask questions and provide feedback.

For each proposal, a link to a corresponding MBIP (Moonbeam Improvement Proposal) in GitHub is provided where the technical specifics are detailed. Engineering oriented discussions on specific implementation details should be discussed there.

Each of these proposals have potentially far reaching implications for the Moonriver and Moonbeam networks, users, developers and other stakeholders. The implementation and adoption of one or more of these solutions can only occur with the approval of the community through governance.

Timeline

Given the potential impact of these proposals on the core protocol, it is critical that the community is provided ample time to provide feedback. Moreover, the implementation of one or more of these solutions is a non-trivial amount of work and should only commence once the community has indicated its support.

Assuming the change would be introduced in Runtime 2500, here is a tentative timeline (subject to change based on feedback, additional research from core engineers, etc.):

  • Community Feedback: Today through June 14th
  • Snapshot Vote: June 14th to June 21st
  • Development: June 21st to July 5th
  • Runtime 2500 Deployment Cycle - July 5th through August 6th

Proposals (MBIPs)

List of MBIPs and Evaluation

As various approaches were considered, they were evaluated in the context of 3 scenarios on Moonbeam which impact storage size:

  1. Creation of a new account (EOAs as well as contracts)
  2. Storage of a Smart Contract
  3. Storage of data within a Smart Contract

At this time, there are 5 proposals presented for discussion:

  • MBIP-1: Smart Contract Creation Deposit
  • MBIP-2: Storage Data Deposit
  • MBIP-3: Storage Rent Mechanism
  • MBIP-4: Storage Base Fee
  • MBIP-5: Gas-Based Storage Limit

These proposals vary in terms of which of the 3 storage categories they address. In theory, some of these proposals could be used in combination with one another as part of a holistic approach.

A summary of each proposal is provided below and considered from the perspective of the following criteria:

  • Storage Categories Addressed: A, B and/or C as per above
  • Cost to Grief Network: Relative amount of cost in GLMR to grief the network from storage spam. (Based on a number of assumptions but should be useful to understand the order of magnitude)
  • Impact to Ethereum Compatibility: How much does this deviate from Ethereum Compatibility (low=good, high=bad)
  • Impact to End-User Experience: Extent to which end-user experience is negatively impacted (low=good, high=bad)
  • Impact to Developer Experience: Extent to which end-user experience is negatively impacted (low=good, high=bad)

MBIP-1: Smart Contract Creation Deposit

This solution proposes that a deposit would be required in order to deploy a Smart Contract. The deposit would be proportional to the size of the contract plus any overhead and would be taken from the account sending the transaction that resulted in the creation of the contract.

The deposit is linked to the contract itself. If and when the contract is destroyed, the deposit would be returned to the depositor account.

This proposal addresses:

  • Category B: Storage of a Smart Contract

This would primarily impact projects who are deploying smart contracts but it should be noted that this goes beyond dApp developers since in some use cases, a user may interact with a smart contract which turns around and creates one or more new contracts (eg. creating a multisig, launchpads, etc).

The deposit could either be debited from the overall account balance or, it could be “reserved” (ie. the deposit remains with the address but is not part of the transferable amount).

Either way, the deposit would not be visible via the Ethereum RPC which may confuse users. That is, it would not appear in EVM based Wallets or in Moonscan and a user may not realize when signing a transaction that a deposit will be deducted from their balance (for example, when creating a new Moonsafe multisig). Moreover, a user could interact with a contract that in turn creates many new contracts causing the entire user balance being used for deposit.

These compatibility issues could perhaps be mitigated by including the deposit amount as part of the overall “value” used in the transaction but would require developers to take this into consideration in developing/maintaining their dApps and it’s unclear if they’d follow this approach and would be a special case for their Moonbeam deployment (as compared to other EVM chains).

MBIP-1 Summary

  • Storage Categories Addressed:
  • Category B: Storage of a Smart Contract
  • Cost to Grief Network: Very High
  • Impact to Ethereum Compatibility: Medium
  • Impact to User Experience: Medium
  • Impact to Developer Experience: Medium
  • MBIP-1 Draft Technical Specifications

MBIP-2: Storage Data Deposit

In this proposal, whenever a user executes a transaction that increases the storage size of the chain, a deposit is taken (or reserved) from the sender proportional to the increase in storage size and added to the sender’s overall “deposit balance”. Unlike MBIP-1, the deposit is linked to the user (sender), not the contract.

Conversely, whenever a user executes a transaction that decreases the storage size of the chain, a portion of the sender’s “deposit balance” proportional to the decrease in size is returned/freed. (Note that the “deposit balance” can never go below zero so it is not possible for a user to receive/free more tokens than they had ever deposited/reserved.)

This proposal addresses:

  • Category C: Storage of data within a Smart Contract
  • Optionally, Category B: Storage of a Smart Contract

It should be noted that it is compatible with MBIP-1, “Smart Contract Creation Deposit”.

This would impact many if not most users in some form or another. For example, a user would incur a deposit when first using any smart contract which records balances or other information related to their account. If extended to cover contract creation, then developers would also be impacted.

There are many similarities to MBIP-1:

  • the deposit could either be debited from the overall account balance or, it could be “reserved” (ie. the deposit remains with the address but is not part of the transferable amount).
  • the deposit would not be visible via the Ethereum RPC which may confuse users (it would not appear in EVM based Wallets or in Moonscan).
  • a user could interact with a contract that generates a huge amount of storage causing the entire user balance being used for deposit.
  • compatibility issues could be mitigated by incorporating the deposit into the “value” of the transaction but would require developers to take this into consideration

A variant of this solution would be to make the deposit dynamic such that for a given transaction, a higher deposit would be required when the rate of storage growth is high and lower deposit when the rate of growth decreases. However, this would need to be carefully thought through in order to discourage any attempts at gaming based on speculation the price will go up or down.

MBIP-2 Summary

  • Storage Categories Addressed:
    • Category C: Storage of data within a Smart Contract
    • Optionally, Category B: Storage of a Smart Contract
  • Cost to Grief Network: High
  • Impact to Ethereum Compatibility: Medium
  • Impact to User Experience: Medium
  • Impact to Developer Experience: Medium
  • MBIP-2 Draft Technical Specifications

MBIP-3: Smart Contract Rent Mechanism

Similar to MBIP-1, this solution proposes that a deposit would be required in order to deploy a Smart Contract and the deposit remains linked to the contract.

However, this deposit would essentially act as prepaid rent for contract storage (the contract itself and any data stored). Rental fees are charged at a set rate of GLMR per Byte per Year (or MOVR on Moonriver). Rent would be deducted from the deposit balance over time and the amount of rent due would vary according to the data stored in the Smart Contract. (The exact mechanics of this are explained in detail in the proposal specifications).

If the contract is called by a transaction but the rent deposit balance has been exhausted (ie. the contract has been deployed for longer than for which rent has been paid), the transaction would fail and the transaction would be reverted.

This mechanism has the advantage that, provided the rent is paid up, there’s no change in behavior for end users interacting with contracts. However, if/when there are failures, this could be confusing for end users.

However, App Developers would need to ensure that their rent is paid up and, depending on the implementation and/or use case, the rent could become prohibitively expensive over time.

Perhaps a clean up mechanism could be implemented whereby contracts that haven’t had rent paid for some long period of time are destroyed.

However, the implications of any such cleanup mechanism would require careful consideration since the contract may contain token balances, etc. There are also potential catastrophic risks associated with contracts being destroyed and re-created in a state where they could be subject to a replay attack (eg. a bridge contract).

MBIP-3 Summary

  • Storage Categories Addressed:
    • Category B: Storage of a Smart Contract
    • Category C: Storage of data within a Smart Contract
  • Cost to Grief Network: High
  • Impact to Ethereum Compatibility: Medium
  • Impact to User Experience: Low
  • Impact to Developer Experience: High
  • MBIP-3 Draft Technical Specifications

MBIP-4: Storage Base Fee

In this solution, “Storage Base Fee” is introduced (similar to the EIP-1559 idea of a “Base Fee”). The fee would be dynamic, computed at each block based on the previous change in storage size. The fee would be expressed in GWEI per byte (unit of storage).

The Storage Base Fee would be calculated based on the increase/decrease of storage from the previous block, and would be incorporated into the calculation of the Base Fee for the next block.

This proposal addresses:

  • Category B: Storage of a Smart Contract
  • Category C: Storage of data within a Smart Contract
  • Optionally, Category A: Creation of a new account

In this model, the cost to execute a transaction becomes:

total cost = units of gas used * gas base fee + units of storage used * storage base fee

This solution has the advantage that there are no complexities involving deposits and for the most part, doesn’t stray very far in terms of Ethereum compatibility.

However, when a client makes an RPC call to get the base fee, only one value can be returned so the question is how to incorporate the storage base fee into the overall base fee returned.

There are a few options for computing the Base Fee returned by this RPC call. In the simplest case, you could just add the gas base fee and storage base fee together. However, this may lead to users overpaying for transactions by a relatively large amount.

Another approach could take into account an amount of bytes (units) of storage per block that the community feels is sustainable in the long term, target max storage. Thus, we can derive what should be considered a sustainable ratio of units of storage to units of gas - target storage to gas ratio.

The RPC call could then be made to multiply the storage base fee by this ratio before adding it to the gas base fee. Assuming a target max storage in the order of 50,000 bytes, the weight of the storage fee is greatly reduced and the base fee returned by the RPC server will be much closer to the expected gas fee.

The idea is that for transactions where the ratio of storage increase to gas use is relatively low, the value returned by the RPC call will be sufficient. However, if a given transaction has a relatively high ratio of storage to gas, the user may have to manually bump the gas price as the value returned by the RPC call will not be enough.

The actual value used for target max storage would require some study to strike a good balance.

MBIP-4 Summary

  • Storage Categories Addressed:
    • Category B: Storage of a Smart Contract
    • Category C: Storage of data within a Smart Contract
    • Optionally, Category A: Creation of a new account
  • Cost to Grief Network: High
  • Impact to Ethereum Compatibility: Low
  • Impact to User Experience: Medium
  • Impact to Developer Experience: Low
  • MBIP-4 Draft Technical Specifications

MBIP-5: Gas-Based Storage Limit

In this solution, a transaction would consume additional gas based on the storage used. This will be reflected in the gas estimation call of the transaction, as returned by Moonbeam’s Ethereum JSON RPC.

This proposal addresses:

  • Category B: Storage of a Smart Contract
  • Category C: Storage of data within a Smart Contract

Similar to a variant of MBIP-4, the cost to execute a transaction incorporates the storage used and the community would decide upon an amount of bytes (units) of storage per block that the community feels is sustainable in the long term (target max storage) - perhaps something in the order of 40,000 bytes.

This value would be used to derive a healthy ratio of gas units to storage units per block, the target gas to storage ratio. (Note this is the inverse of the ratio in MBIP-4.)

In this model, the cost to execute a transaction becomes:

total cost = units of gas used * gas base fee + units of storage used * target gas to storage ratio

Also similar to MBIP-4, this solution has the advantage that there are no complexities involving deposits and for the most part, doesn’t stray very far in terms of Ethereum compatibility. The current implementation of Moonbeam’s Ethereum JSON RPC, and therefore, Ethereum tooling should work out of the box.

However, this could lead to blocks being considered full due to storage limits leading to under utilization from a computational perspective.

Additionally, this could cause problems for contracts that estimate gas to forward on to other contracts/function calls to fail as the contract developer may have already put fixed gas estimates into their contracts. For example, a call hardcoded in a smart contract like:

address(_address).call{gas: 1000000}(…)

MBIP-5 Summary

  • Storage Categories Addressed:
    • Category B: Storage of a Smart Contract
    • Category C: Storage of data within a Smart Contract
  • Cost to Grief Network: Very High
  • Impact to Ethereum Compatibility: Low
  • Impact to User Experience: Low
  • Impact to Developer Experience: Low
  • MBIP-5 Draft Technical Specifications

MBIP Comparison Chart

1- SC Creation Deposit 2 - Data Storage Deposit 3 - Storage Rent Mechanism 4 - Storage Base Fee 5 - Gas-Based Storage Limit
A - Address Creation No No No Maybe No
B - Contract Code Storage Yes Maybe Yes Yes Yes
C -Contract Data Storage No Yes Yes Yes Yes
Cost to Grief Network Very High High High High Very High
Impact to Eth Compatibility Med Med Med Low Low
Impact to User Experience Med Med Low Med Low
Impact to Developer Experience Med Med High Low Low
5 Likes

hey, Aaron, thank you very much for bringing it up!

Given the technical nature of the solution, it is really important for devs building on Moonbeam & Moonriver to actively participate in this topic and ask questions / provide input. I believe that a collaborative approach allows us to better understand the merits of the proposal and the potential consequences.

By involving the development teams, they can contribute their expertise and insights, ensuring that the proposed changes align with the needs and expectations of the projects being built on Moonbeam & Moonriver. involvement of devs can also help identify any potential challenges or considerations that may arise during the implementation. their input can ensure that the solution is well-rounded, addresses the technical aspects effectively, and takes into account the needs and concerns of the ecosystem’s stakeholders

3 Likes

Although I understand approaches 1-3, I’m worried how they deviate from the typical deployment structure and interaction when compared to other EVM implementations. This will just create very complicated dynamics that are not standard and have a big impact in both user experience and developer experience. This might drive devs away from Moonbeam.

On the other hand, proposal 4 & 5 will also create some differences with typical EVM implementations, but at least they’ll be sort of hidden behind the Ethereum JSON RPC. Therefore, in theory, while these implementations are not standard, they should have minimal impact on developer and user experience. Generally it will just be higher gas fees on some scenarios.

I personally like option 5. Specially if EVM gas estimations can take into account the change in state the transaction is doing and reflect that by increasing the minimum gas requirements of that tx. It is true that this should mean tx throughput might be affected, but:

  1. With Async Backing and further enhancements down the road, the maximum gas per block could be easily increase by 4x - 10x. Therefore (hopefully) throughput should not be impacted
  2. Devs should store only the bare minimum info on the Blockchain, and rely on events that can be retrieved by indexers to store and retrieve data in a cheaper and more efficient way

The main problem I see with 5 is what was mentioned of contracts that might have some gas limit hard coded for some calls. But this is not widely used IMO.

Anyways, my two cents, but would love to see discussions on the different ptoposals!

6 Likes

Overall, I am leaning more towards MBIP-5 as it seems to offer the most transparency for EVM users and retains a mechanism that feels more in line with Ethereum’s approach to managing smart contracts. In addition to this, I feel like this MBIP encourage developers to optimize and take care of their contracts as the cost for a user to use their protocol may become a key difference with protocols that offer similar functionalities.

Appart from this I want to drop one question regarding the deposits on smart contract creation,

From MBIP-1

When the Smart Contract gets destroyed, the deposit is restored to the depositor.

How would the contract destruction work? The selfdestruct opcode got deprecated and will be replaced by a new OPCODE with a different functionality to fit better on Ethereum’s roadmap. Smart Contracts using an older compiler version should still be able to be destroyed but not the ones using the newest compiler versions. And a following up question, would we consider that the deposit is lost/locked in the case Ethereum finally replaces selfdestruct and we follow their path due to compatibility ?

3 Likes

Hi Guys,

I’ve spoken to the contracts developers within the project and from a genuine on-chain usecase, our preferred solution would be 1 and 3 as 2 and 4 i think it will affect our base transaction cost which is something our users care a lot about as we do a lot of interactions onchain and increasing tx cost would affect our user-base.

5 Likes

Updated the account growth chart in the main post so that it’s as of May 25th, 2023.

3 Likes

Great post with a lot of juicy detail @aaron.mbf. Reducing complexity for those that are experienced in using and deploying to Ethereum compatible chains seems like it should have a higher weighting, and that lends itself to 4/5. But as I am not a developer my own view should be weighted less, so I would rather support the consensus of those actually building the dApps (assuming they have user experience in mind when considering the options).

With regard to address creation, is it not such of an issue that an existential deposit has not been implemented or considered for this?

2 Likes

Hello everyone,

I’m part of the developer team at StellaSwap. Evaluating the effects of MBIPs 1-3, I find that they complicate matters, negatively affecting both the user and developer experience.

However, MBIP-5 presents a promising solution, where it minimizes the negative impact on both user and developer experiences. From a computational under utilization standpoint, it could be okay if it means simplifying the user experience.

When it comes to employing {gas:} in our smart contracts, we always pass gasLimit as a parameter. I propose we look into the quantity of contracts on moonscan that use hardcoded values, which I suspect will be few.

Overall, this approach promotes optimal practices in smart contract development.

5 Likes

Noah from DAM here!
Not to parrot exactly what Alberto has written down, but I also agree that 1-3 are complicated from a UI pov as well as straying from the well-known baselines of EVM contract usage. Barriers to entry should always be kept extremely low in my opinion, and these proposals would directly contradict that philosophy.

4 and 5 make the most sense to me, with 5 probably edging out the win in my head, again because of low barrier to entry from a user POV. I would also agree that hardcoded values on gas usage are (or at least shouldn’t be) common anyway, so that drawback doesn’t really hold much weight imo. Underutilization from a compute perspective makes the alarm bells start ringing a little bit, but I would need to know more in order to weigh it properly. Is it the case where blocks are getting to 80% usage and storage says no more, or one where they’re getting to 20% usage and storage says no more? Do we have any benchmarks on that?

6 Likes

My main issue with 4 and 5 is that they seem to be an ingress-based model that does not account for the time dimension of storage. If we want to store some data indefinitely like Arweave then it could work, but if we want to incentivize state size hygiene, then it’s not the right economic model.

Would it be possible to ask users to pay an extra “storage subsidy” when they use a contract? Something like…

total cost = (units of gas used + subsidy gas surcharge) * gas base fee

The subsidy would be based on

  • current storage rent payable = storage * time - previous paid subsidies
  • contract tx rate to split the subsidy among multiple users

At the end of the day, the economic model must find a way to balance contract utility with storage costs.

  • A contract with large state should be OK if users are OK with paying for its high TX fees (the assumption is that are OK because of its high utility).
  • Lots of transactions indicate higher utility and decrease gas costs because the subsidy is split among more (active) users.
  • As the contract state grows txs will become more expensive, so developers are incentivized to design contracts that keep state size under control.
  • The contract storage funding scales up with active users/transactions

Potential issues

  • Not sure if wallets would play along the contract-dependant gas fee calculation
  • A contract with many dormant users and a lot of state would become expensive for new users
2 Likes

total cost = (units of gas used + subsidy gas surcharge) * gas base fee

I’m intrigued by this idea, but I see some problems with it. I’ll mention just a couple here:

  • There’s sort of a “Tragedy of the Commons” problem because if I want to use some contract that is expensive, what I really want is to wait for someone else to use it first and then use it immediately afterward. If this is everyone’s strategy, then no one will use a contract and the longer this “game” goes on, the worse it gets.

  • The model seems clear when a transaction directly calls a contract, but what about various forms of subcalls? Should I have to pay this rent on contract B when contract A calls contract B? For that matter, not all accesses to a contract are equal. The approach could be modified to take the size of an operation into account, but this gets complicated quickly.

Concerning the state size hygiene, I feel it is acceptable to consider a constant growth, as our hardware and technology is evolving over time, however we have to make sure it doesn’t grow faster