It seems like most folks understand (or are at least resigned to!) the idea that Flow’s approach to storage fees isn’t going to change, but I thought it would be worth sharing how I think about this subject.
I think the best way to present this is to list the ideas I’m aware of as a solution to this problem, and lay out the problems I’m aware of for each of them. And they all have problems! So it’s not really about selecting the best solution, it’s really about selecting the “least bad” solution…
-
Pay on Allocation, Global Price (The Ethereum Model)
The basic idea here is that you would pay extra tx fees when you allocate new data, and refund it (possibly at a reduced rate) when data is deleted, but everyone pays the same rate.
The worst issue with this approach is that it leads to state growth due to speculation (remember that the problem we’re trying to solve is to encourage minimal state growth!). If we refund tokens when you free up storage at a global rate, there is a huge incentive to allocate garbage storage to push up the storage price and sell at a profit! Very bad incentives.
The Ethereum designers seem aware of this issue and tried to solve this by only refunding gas, and not refunding ETH. Unfortunately, this kind of stuck them with the worst of both worlds. There’s a ton of low-value data on Ethereum that has far too small of an incentive to clean up, while there’s still things like Gas Token that store garbage data just to arbitrage gas prices.
I haven’t seen any variations on this approach that don’t create bad incentives: They don’t provide enough incentive to clean up stale data, or they provide incentives to create junk data, or they do both!
-
Pay on Allocation, Individual Pricing (The @bluesign Model)
The basic idea here is that you would pay extra tx fees when you allocate new data, and some (or all) of those funds would be returned when the data is deleted.
My first concern with this one is how to even track this. Imagine I have a Resource that stores a variable-length array of integers. If I grow that list, I pay the current allocation price, but how much gets refunded if I delete an integer from the array? Do we keep track of the price for each element in the array? The min? Max? Average?
My second issue is that this doesn’t create a strong incentive to delete old data. If I have a piece of data that was allocated when storage was cheap, but now storage is expensive, I don’t have the proper incentive to clean that up.
Indeed, as @AlexTheArchitect pointed out, it creates an incentive to create a bunch of junk data that you can use as storage again in the future. You think the price of storage is going to go up? Allocate a bunch of empty space to be used later. This is not the kind of optimization I want Flow dapp developers to be thinking about!
-
The Storage Token (The EOS Model)
You have a secondary token that is used for storage.
EOS tried this and it was a disaster. It’s another bad incentive: I buy up all of the storage tokens to increase the price, and I can hold you (and every other project) hostage because you either pay, or shut down your whole project.
(I’ll be honest, I don’t know all the details of what exactly happened on EOS with their storage token and how they addressed it. But once you see the possible attack, it’s hard to imagine how it wouldn’t be a problem.)
I liked @AlexTheArchitect’s idea that you can always just pay directly for storage with Flow, which prevents the “cornering the market” attack… until I realized that it has the opposite problem. That approach effectively lets me “mint” any number of storage tokens at the current price. If I suspect the price will go up at some point, I just mint a million or a billion storage tokens (allocate a new object, immediately delete it and stash the storage token. GOTO 10). Anyone who does this can sell them at the old price (or close to it) and the network can’t ever really increase the cost of storage. (And if you put a cap on how many storage tokens can be created, you’re back to the EOS problem.)
-
Storage Rent (The Model Solana Got Rid Of)
An account buys units of “spacetime”, a certain amount of storage over a certain period of time. If an account uses more “byte-years” than it has purchased, the contents of that account can be deleted.
I’m not sure how much I have to go into the disadvantages of this. It’s an approach that only an economist could love. To ask users to remember to pay their rent, and then delete their stuff if they forget isn’t just “not user friendly”, it’s downright user hostile…
-
Pay Once (The AR Weave Model)
When you allocate storage you pay a relatively large fee, but the data remains indefinitely. The economic theory here is that it’s kind of like paying into an endowment. Like, I pay $5 to store my file, and (conceptually) the “interest” on that $5 pays for the storage indefinitely. AR Weave also counts on the price of storage going down over time (a reasonable assumption).
I think this is a poor fit for data that can be processed. For something like AR Weave, where the data is stored once and retrieved rarely, I think this model can work. Inside a smart contract block chain, however, every byte of data needs to be in the “working set” of memory so it can be processed at any time by any transaction. The “ceiling” on data storage in a smart contract chain is much, much lower than in a data archive chain.
IMHO, the worst part of this is that there is zero incentive to remove stale data. From the standpoint of the chain, it’s just as valuable to create an incentive for someone to delete a MB of data as it is to incentivize someone to structure their data to take up a MB less space. A MB is a MB.
-
Storage Staking (The Flow Model)
Each account can store an amount of data dictated by the amount of tokens they have. If they have a lot of tokens, they can store a lot of data. Transactions fail if they result in too much data (or not enough tokens) in any account. If an account is “off-side” it doesn’t get deleted, but it can’t be used for anything until the imbalance is fixed. Any transaction from any account can deposit funds into the account to address the issue (provided they have a FLOW receiver).
For users that use FLOW as their primary currency, this approach is really pretty straightforward and maps very closely to something like DropBox. I have a storage limit, and if I want that limit to go up, I need more money. (Except that, unlike DropBox and Storage Rent, I don’t spend that money, I just stake that money until I stop needing the storage space.)
However, this approach works poorly in a world where most people’s balances are held in something OTHER than the native token. And, because of the example set by Dapper Wallet and NBA Top Shot, we have a lot of users on Flow who use USDC and/or Dapper Credit instead of the native FLOW currency.
In the end, we should be looking for the best balance of correct incentives and user experience. I would argue that options 1, 2, and 3 have counter-productive incentives, option 4 creates a bad UX, and option 5 would be more expensive and lacks a “clean-up” incentive. Option 6 has problems, but I would argue that the best solution to these problems would be to normalize the usage of FLOW for all users. That’s the token that provides the crypto-economic security protecting the contents of their account, after all!