Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NEP-491: Non-refundable storage #491

Merged
merged 14 commits into from
Oct 1, 2024
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Changes to the protocol specification and standards are called NEAR Enhancement
| [0399](https://github.com/near/NEPs/blob/master/neps/nep-0399.md) | Flat Storage | @Longarithm @mzhangmzz | Review |
| [0448](https://github.com/near/NEPs/blob/master/neps/nep-0448.md) | Zero-balance Accounts | @bowenwang1996 | Final |
| [0455](https://github.com/near/NEPs/blob/master/neps/nep-0455.md) | Parameter Compute Costs | @akashin @jakmeier | Final |
| [0491](https://github.com/near/NEPs/blob/master/neps/nep-0491.md) | Non-Refundable Storage Staking | @jakmeier | Review |
| [0514](https://github.com/near/NEPs/blob/master/neps/nep-0514.md) | Fewer Block Producer Seats in `testnet` | @nikurt | Final |


Expand Down
381 changes: 381 additions & 0 deletions neps/nep-0491.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,381 @@
---
NEP: 491
Title: Non-Refundable Storage Staking
Authors: Jakob Meier <jakob@near.org>
Status: Draft
DiscussionsTo: https://gov.near.org/t/proposal-locking-account-storage-refunds-to-avoid-faucet-draining-attacks/34155
Type: Protocol Track
Version: 1.0.0
Created: 2023-07-24
LastUpdated: 2023-07-26
---


## Summary

Non-refundable storage allows to create accounts with arbitrary state for users,
without being susceptible to refund abuse.

This is done by tracking non-refundable balance in a separate field of the
account. This balance is only useful for storage staking and otherwise can be
considered burned.


## Motivation

Creating new accounts on chain costs a gas fee and a storage staking fee. The
more state is added to the account, the higher the storage staking fees. When
deploying a contract on the account, it can quickly go above 1 NEAR per account.

Some business models are okay with paying that fee for users upfront, just to
get them onboarded. However, if a business does that today, their users can
delete their new accounts and spend the tokens intended for storage staking in
other ways. Since this is free for the user, they are financially incentivized
to repeat this action for as long as the business has funds left in the faucet.

The protocol should allow to create accounts in a way that is not susceptible to
such refund abuse. This would at least change the incentives such that creating
fake users is no longer profitable.
Comment on lines +26 to +38
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative solution to this problem that doesn't involve a change to the protocol.

The business creates an account with a deployed contract that will work in two modes.

  • Restricted mode: The user doesn't have a FullAccessKey, and can't delete the account
  • Regular mode: The user has a FullAccessKey

On restricted mode, the user has a FunctionCallKey that allows calling into some methods of the contract that grants similar permissions to a FullAccessKey, but not all: See all permissions here. In particular, it must not have permission to delete the account and get a refund.

To upgrade from restricted mode to regular mode, there is a method upgrade that will send the initial amount of NEAR deposited by the creator of the account (the business) and will attach a new FullAccessKey to the contract (the same as the initial FunctionCallKey). Anyone can call this method.

With this proposal, there will be no incentives to steal money from the business (by creating and deleting accounts), since the user can only delete the account in normal mode, after depositing the initial amount.

The business can upgrade accounts for those users that they already trust to not be acting maliciously. (i.e their goal is not to recover the money, but to avoid incentives for malicious users to deplete their funds).

Drawback

The accounts in restricted mode don't behave in the same way as owning a regular account, since all actions need to be done through a special method (i.e. to do any Transfer or FunctionCall to another contract).

This might be acceptable if the contract is a multi-sig (or similar interface) where the user is not expected to do Transfers or FunctionCalls directly anyway; or if the account is expected to be used on a specific application only, that is aware of this mode.

To support using restricted accounts for arbitrary (allowed) use cases, wallets should be aware of it, which requires creating a standard about it. An alternative (that requires a protocol update), is creating a new type of key, in addition to FullAccessKey and FunctionCallKey, we can have GranularAccessKey, which works similarly to a FullAccessKey but doesn't have permission to do some actions (in this case it won't have permission to Delete the contract).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc. @jbajic

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does a user interact with a fungible token contract without a Full Access Key?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does a user interact with a fungible token contract without a Full Access Key?

@DavidM-D As I understand the idea, it goes through a contract deployed on the account that forwards arbitrary calls.

On restricted mode, the user has a FunctionCallKey that allows calling into some methods of the contract that grants similar permissions to a FullAccessKey, but not all

So for example, you call user.near::proxy([{action: "Function Call", "receiver": "ft.near", "method": "ft_transfer", "arg": "100", "balance": "1"}]) which will be unpacked by the contract and create the actual request as ft.near::ft_transfer("100") to the FT contract as a cross-contract call.

I think we discussed this as the "proxy account model" before, in the variation where it's deployed on every account. The increased storage and gas costs were one of the problems we saw in this idea. Also the latency is increased by at least one block time.

But more importantly, I think we assumed we want to give full access to users, which leads again to the storage refund problem. Now, the proxy contract gets around this problem by stripping the rights to delete the account or redeploy the contract code from the user. That's fine and I think we considered this already but assumed for the upgrade path we need to hold a full access key on the sponsor, which we didn't like for potential abuse issues. However, this seems to solve it:

To upgrade from restricted mode to regular mode, there is a method upgrade that will send the initial amount of NEAR deposited by the creator of the account (the business) and will attach a new FullAccessKey to the contract (the same as the initial FunctionCallKey). Anyone can call this method.

So the upgrade path is also part of the contract. Nobody has full access for the account, except the contract code. I really like this idea by @mfornet

Of course it's possible I missed something. WDYT think @DavidM-D, could it work for your use case?

Copy link

@firatNEAR firatNEAR Oct 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder how this new proposal is different from Keypom Trial Accounts. AFAIK Keypom Trial accounts also has the option of refunding the sponsor if the $NEAR attached to the account is not fully used when the free trial account owner wants to graduate to an account with FullAccessKey.

Copy link
Member

@mfornet mfornet Oct 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@walnut-the-cat I can join a call next week as well. Use the email mfornet94 at gmail for an invite.

One question I'd ask is, ignoring the contract cost, could we have implemented Zero Balance Accounts entirely on the application level using a KeyPomesque solution?

@DavidM-D Zero Balance Accounts no, but maybe you could have achieved your goal anyway (which is not to avoid paying but avoid getting scammed by the users as I understand).

How are the users expected to use those accounts, or interact with the contract you are deploying? (i.e what is the goal of the contract).

If the contract is somehow "similar" in spirit to a multi-sig, where the user needs to go through a contract method to perform any action, then KeyPom contract will fit you. Otherwise drawbacks from the original comment in this thread applies.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mfornet , if you can share some of your availability (e.g. timezone), I will send out an invitation! I can check schedule for @DavidM-D and @firatNEAR from my end

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@walnut-the-cat I can do any time between (6:00 - 14:00) UTC, I could do later, but not every day. I recommend you create an event, and we follow up in email.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the conclusion of this discussion? @mfornet @DavidM-D @jakmeier

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


Non-refundable storage staking is a further improvement over
[NEP-448](https://github.com/near/NEPs/pull/448) (Zero Balance Accounts) which
addressed the same issue but is limited to 770 bytes per account. By lifting the
limit, sponsored accounts can be used in combination with smart contracts.
Comment on lines +40 to +43
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps the following part could use a bit more details (expanded on, in some section below):

By lifting the limit

with respect to how it interacts with "free 770 bytes per account" concept. Because strictly speaking, the way I understand this prop, it's not exactly a generalization of #448 but rather a standalone feature that extends it conceptually.

It seems both features interplay nicely together, just want to make sure it doesn't create unnecessary burden for developers when they decide which one to go with.


## Specification

Users can opt-in to nonrefundable storage when creating new accounts. For that,
we use the new action `ReserveStorage`.

```rust
pub enum Action {
...
ReserveStorage(ReserveStorageAction),
...
}
```

To create a named account today, the typical pattern is a transaction with
`CreateAccount`, `Transfer`, and `AddKey`. To make the funds nonrefundable, we
can use action `ReserveStorage` like this:

```json
"Actions": {
"CreateAccount": {},
"ReserveStorage": { "deposit": "1000000000000000000000000" },
"AddKey": { "public_key": "...", "access_key": "..." }
}
```

Adding a `Transfer` action allows the combination of nonrefundable balance and
refundable balance. This allows the user to make calls where they need to attach
balance, for example an FT transfer which requires 1 yocto NEAR.

```json
"Actions": {
"CreateAccount": {},
"ReserveStorage": { "deposit": "1000000000000000000000000" },
"Transfer": { "deposit": "100" },
"AddKey": { "public_key": "...", "access_key": "..." }
}
```

To create implicit accounts, the current protocol requires a single `Transfer`
action without further actions in the same transaction and this has not changed
with this proposal:
Comment on lines +83 to +85
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am confused here. It seems like it does change with this proposal?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The creation of implicit accounts without reserved storage does not change, the request stays the same, I will remove the ReserveStorage action below


```json
"Actions": {
"CreateAccount": {},
"ReserveStorage": { "deposit": "1000000000000000000000000" },
"Transfer": { "deposit": "0" },
}
```

If a non-refundable transfer arrives at an account that already exists, it will
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why failing? Why not allowing to top-up a non-refundable balance?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the non-refundable balance exists only for storage staking and it would be confusing if somebody would top up non-refundable balance and then it all gets burned when accounts gets deleted.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fail and the funds are returned to the predecessor.

Finally, when querying an account for its balance, there will be an additional
field `nonrefundable` in the output. Wallets will need to decide how they want
to show it. They could, for example, add a new field called "non-refundable
storage credits".

```js
// Account near
{
"amount": "68844924385676812880674962949",
"block_hash": "3d6SisRc5SuwrkJnLwQb3W5pWitZKCjGhiKZuc6tPpao",
"block_height": 97314513,
"code_hash": "Dmi6UTRYTT3eNirp8ndgDNh8kYk2T9SZ6PJZDUXB1VR3",
"locked": "0",
"storage_paid_at": 0,
"storage_usage": 2511772,
"formattedAmount": "68,844.924385676812880674962949",
// this is new
"nonrefundable": "0"
}
```


## Reference Implementation

On the protocol side, we need to add new action:

```rust
enum Action {
CreateAccount(CreateAccountAction),
DeployContract(DeployContractAction),
FunctionCall(FunctionCallAction),
Transfer(TransferAction),
Stake(StakeAction),
AddKey(AddKeyAction),
DeleteKey(DeleteKeyAction),
DeleteAccount(DeleteAccountAction),
Delegate(super::delegate_action::SignedDelegateAction),
// this gets added in the end
ReserveStorage(ReserveStorageAction),
}
```

and handle the new action in the `apply_action` call.

Further, we have to update the account meta data representation in the state
trie to track the non-refundable storage.

```rust
pub struct Account {
amount: Balance,
locked: Balance,
// this field is new
nonrefundable: Balance,
code_hash: CryptoHash,
storage_usage: StorageUsage,
// the account version will be increased from 1 to 2
version: AccountVersion,
}
```

The field `nonrefundable` must be added to the normal `amount` and the `locked`
balance calculate how much state the account is allowed to use. The new formula
to check storage balance therefore becomes

```rust
amount + locked + nonrefundable >= storage_usage * storage_amount_per_byte
```

For old accounts that don't have the new field, the non-refundable balance is
always zero. Adding non-refundable balance later is not allowed. If a transfer
is made to an account that already existed before the receipt's actions are
applied, execution must fail with
`ActionErrorKind::OnlyReserveStorageOnAccountCreation{ account_id: AccountId }`.

Conceptually, these are all changes on the protocol level. However,
unfortunately, the account version field is not currently serialized, hence not
included in the on-chain state.

Therefore, as the last change necessary for this NEP, we also introduce a new
serialization format for new accounts.

```rust
// new serialization format for `struct Account`

// new: prefix with a sentinel value to detect V1 accounts, they will have
// a real balance here which is smaller than u128::MAX
writer.serialize(u128::MAX)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we add a comment that the near supply is orders of magnitude smaller than u128::MAX?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that explanation is in the Alternatives section. Let's add a link here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Markdown does not support links to paragraphs from code sections. I think the explanation is not that far away from this part, I could add it as a comment in code if you think it is valuable?

// new: include version number (u8) for accounts with version 2 or more
writer.serialize(version)?;
writer.serialize(amount)?;
writer.serialize(locked)?;
writer.serialize(code_hash)?;
writer.serialize(storage_usage)?;
// new: this is the field we added, the type is u128 like other balances
writer.serialize(nonrefundable)?;
```

Note that we are not migrating old accounts. Accounts created as version 1 will
remain at version 1.

A proof of concept implementation for nearcore is available in this PR:
https://github.com/near/nearcore/pull/9346


## Security Implications

We were not able to come up with security relevant implications.

## Alternatives

There are small variations in the implementation, and then there are completely
different ways to look at the problem. Let's start with the variations.

### Variation: Allow adding nonrefundable balance to existing accounts

Instead of failing when a non-refundable transfer arrives at an existing
account, we could add the balance to the existing non-refundable balance. This
would be more flexible to use. A business could easily add more funds for
storage even after account creation.

The problems are in the implementation details. It would allow to add
non-refundable storage to existing accounts, which would require some form of
migration of the all accounts in the state trie. This is impractical, as we have
to iterate over all existing accounts and re-merklize. That's infeasible within
a single block time and stopping the chain would be disruptive.

We could maybe migrate lazily, i.e. read account version 1 and automatically
convert it to version 2. However, that would break the assumption that every
logical value in the merkle trie has a unique borsh representation, as there
would be a account version 1 and a version 2 borsh serialization that both map
to the same logical version 2 value. This could lead to different
representations of the same chunk in memory, which might be used in attacks to
force a double-sign by innocent validators.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I think double sign is not possible, because Account v1 and v2 has different physical representation, hence will produce different Merkle Root. So it will require chain fork.
  2. "assumption that every logical value in the merkle trie has a unique borsh representation". This is already the case: Account v1 is logically equal to Account v2 with zero non-refundable balance.

Migrating Account v1 to v2 during the first transaction by that account should solve the issue.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issues that arise with converting accounts from v1 to v2 are due to the Borsch serialization, currently we have a guarantee that there is no two binary representations that deserialize into the same object of same type, but with different types we can have two same binary representation (or as you refer to it as physical representation)


It is not 100% clear to me, the author, if this is a problem we could work
around. However, the complications it would involve do not seem to be worth it,
given that in the feature discussions nobody saw it as critical to add
non-refundable balance to existing accounts.

### Variation: Allow refunds to original sponsor

Instead of complete non-refundability, the tokens reserved for storage staking
could be returned to the original account that created the account when an
account is deleted.

The community discussions ended with the conclusion that this feature would
probably not be used and we should not implement it until there is real demand
for it.

### Alternative: Don't use smart contracts on user accounts

Instead of deploying contracts on the user account, one could build a similar
solution that uses zero balance accounts and a single master contract that
performs all smart contract functionality required. This master contract can
implement the [Storage Management]
(https://nomicon.io/Standards/StorageManagement) standard to limit storage usage
per user.

This solution is not as flexible. The master account cannot make cross-contract
function calls with the user id as the predecessor.

### Alternative: Move away from storage staking

We could also abandon the concept of storage staking entirely. However, coming
up with a scalable, sustainable solution that does not suffer from the same
refund problems is hard.

One proposed design is a combination of zero balance accounts and code sharing
between contracts. Basically, if somehow the deployed code is stored in a way
that does not require storage staking by the user themself, maybe the per-user
state is small enough to fit in the 770 bytes limit of zero balance accounts.
(Questionable for non-trivial use cases.)

This alternative is much harder to design and implement. The proposal that has
gotten the furthest so far is [Ephemeral
Storage](https://github.com/near/NEPs/pull/485), which is pretty complicated and
does not have community consensus yet. Nobody is currently working on moving it
forward. While we could wait for that to eventually make progress, in the
meantime, the community is held back in their innovation because of the refund
problem.

### Alternative: Using a proxy account

As suggested by [@mfornet](https://github.com/near/NEPs/pull/491#discussion_r1349496234)
another alternative is using a proxy account approach where the business creates
an account with a deployed contract that has Regular (user has full access key)
and Restricted mode (user doesn't have full access key and cannot delete
account).

In restricted mode, the user has a `FunctionCallKey` which allows the user to
call methods of the contract that controls the `FullAccessKey` and allows the
user some functionality but not all, e.g. not allowing account deletion. The
user in restricted mode could also upgrade an account by sending the initial
amount of NEAR deposited by the account creator and will attach a new
`FullAccessKey`.

The downside of this idea is additional complexity on the tooling side because
actions like adding access keys to the account need to be converted to function
calls instead of being direct actions. And the complexity on the business side
is that it needs to include the proxy logic with their business logic in the
same contract, increasing the complexity of development.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @mfornet 's GranularAccessKey proposal should still be included here as an another alternative. This alternative includes a protocol change which introduces a new kind of access key which is not allowed to do all actions (in this particular case we are interested in forbidding the DeleteAccount action) and the user is given such an access key as part of the business' on-boarding process. The business could provide the option to the user to upgrade their GranularAccessKey to a FullAccessKey with a smart contract method on the account. That method would require the user to buy out the original deposit which funded the account creation, and could even restore the business its funds.

The main drawback of this approach (in my opinion) is complexity. As with the proxy contract alternative above, without a way to compose Wasm modules, the business needs to include the GranularAccessKey upgrade logic into the same smart contract as their business logic, which created development complexity for them. I also think the GranularAccessKey protocol change is significantly more complex than the proposed ReserveStorageAction change and I do not know if there is benefit to that additional complexity. Perhaps if I am wrong on this point then someone from the core protocol team would weigh in.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with your assessment on the complexity of the GranularAccessKey change. I think Marcelo's proposal of proxy smart contracts is definitely worth considering though

## Future possibilities

- We might want to add the possibility to make non-refundable balance transfers
from within a smart contract. This would require changes to the WASM smart
contract to host interface. Since removing anything from there is virtually
impossible, we shouldn't be too eager in adding it there but if there is
demand for it, we certainly can do it without much trouble.
- We could later add the possibility to refund the non-refundable tokens to the
account who sent the tokens initially.
- We could allow sending non-refundable balance to existing accounts.
- If (cheap) code sharing between contracts is implemented in the future, this
proposal will most likely work well in combination with that. Per-user data
will still need to be paid for by the user, which could be sponsored as
non-refundable balance without running into refund abuse.


## Consequences


### Positive

- Businesses can sponsor new user accounts without the user being able to steal
any tokens.

### Neutral

- Non-refundable tokens are removed from the circulating supply, i.e. burnt.

### Negative

- Understanding a user's balance become even more complicated than it already
is. Instead of only `amount` and `locked`, there will be a third component.
- There is no incentive anymore to delete an account and its state when the
backing tokens are not refundable.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't' this be positive or neutral?

Copy link
Contributor

@jbajic jbajic Nov 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the negative aspects of:

  • Understanding a user's balance become even more complicated than it already
    is. (Instead of only amount and locked, there will be a third component.)

is the users experience, since the user must now know the nature of another balance in their account and have and understanding of what is possible with it and what is not.

and for the second statement:

  • There is no incentive anymore to delete an account and its state when the
    backing tokens are not refundable.

the negative side is that we can have a lot of accounts with their state and no reason for their owners to delete them, since they get nothing out of it, which leaves "dead" accounts in circulation.


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more negative consequence:

  • no incentive or impossibility to delete unused accounts with a non-refundable balance.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't that the same as the last one?


### Backwards Compatibility

We believe this can be implemented with full backwards compatibility.

## Unresolved Issues (Optional)

All of these issues already have a proposed solution above. But nevertheless,
these points are likely to be challenged / discussed:

- Should we allow adding non-refundable balance to existing accounts? (proposal:
no)
- Should we allow adding more non-refundable balance after account creation?
(proposal: no)
- Should this NEP include a host function to send non-refundable balance from
smart contracts? (proposal: no)
- How should a wallet display non-refundable balances? (proposal: up to wallet
providers, probably a new separate field)
Comment on lines +366 to +367
Copy link

@norwnd norwnd Aug 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do wallets even need to display non-refundable balance to users (what for) ? My impression was "non-refundable balance" is working specifically towards hiding the complexity of storage-staking from users, is this even an achievable goal ? Blockchain explorers would probably need to display "non-refundable balance" though at least for info/debugging purposes.

On a similar topic, the name "non-refundable balance" might be unnecessarily vague given the intent of this proposal, wouldn't something like "dedicated storage balance" or "storage-tied balance"/"storage-restricted balance" better describe the concept ? Since it's gonna show up in blockchain explorers this makes it a bit more self-explanatory, I think. Unless it does become a more generic concept eventually (in which case more generic name is warranted ofc).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @norwnd , thanks for your input, it's very much welcome! :)

Do wallets even need to display non-refundable balance to users (what for) ?

Not displaying it is a valid choice for a wallet IMO. But right now, I assume most wallets are doing some kind of computation to show how much of the balance is currently open for withdrawal, vs the amount that's reserved to cover storage staking. If they have this computation, they need to update how it works once this proposal gets through.

My impression was "non-refundable balance" is working specifically towards hiding the complexity of storage-staking from users, is this even an achievable goal ?

Interesting question. The original motivation here isn't so much to simplify things. It's more about enabling a certain use case. But I can see how, if presented correctly, we can maybe make it simpler from a user point of view, as they don't even have to know there is storage staking for their account. I like that!

wouldn't something like "dedicated storage balance" or "storage-tied balance"/"storage-restricted balance" better describe the concept ?

I'm open to name changes. I very much dislike "non-refundable balance". But I would like to have a clear distinction between "refundable balance that's currently locked due to storage usage" (which we already have today) and the new concept of "balance that can only ever be used for storage in this account and will be burnt when the account is deleted". In your suggestions, it seems like the line between the two is blurry.

I've considered "storage credits", but that sounds like you can trade them between accounts.

Another idea: Perhaps we should just "purchase" an amount of bytes, and add a field like free_bytes, as in, no storage staking required for these bytes. Sounds a bit crazy to me but maybe that would make the concept easier to grasp. WDYT?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In your suggestions, it seems like the line between the two is blurry.

Yep, it was just to illustrate that point, your free_bytes concept feels much better (alternative names to consider: purchased/prepayed/bought+_+bytes/storage perhaps).

Copy link

@norwnd norwnd Aug 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original motivation here isn't so much to simplify things. It's more about enabling a certain use case. But I can see how, if presented correctly, we can maybe make it simpler from a user point of view, as they don't even have to know there is storage staking for their account.

Long term though, UX-wise both for users and devs the "Alternative: Move away from storage staking" is still probably desirable, if achievable in sustainable manner.


## Changelog

### 1.0.0 - Initial Version

> Placeholder for the context about when and who approved this NEP version.

#### Benefits

> List of benefits filled by the Subject Matter Experts while reviewing this
> version:

- Benefit 1
- Benefit 2

#### Concerns

> Template for Subject Matter Experts review for this version: Status: New |
> Ongoing | Resolved

| # | Concern | Resolution | Status |
| --: | :------ | :--------- | -----: |
| 1 | | | |
| 2 | | | |

## Copyright

Copyright and related rights waived via
[CC0](https://creativecommons.org/publicdomain/zero/1.0/).