(Looking for feedback here, I might create a NEP out of it.)
Storage refunds make it very tricky to offer any service that creates Near accounts for users for free. Malicious users can just create many accounts, delete them, and collect the storage refunds.
Zero balance accounts partially address this problem but they only work in limited cases where <= 770 bytes are required. It does not work when a WASM contract needs to be deployed on the users account.
I propose a protocol change that would prevent such faucet-draining attacks even with larger storage requirements, while still allowing dApp providers to pay for their users’ storage.
Summary
- When creating an account, the creator can define a
sponsor_id
. This is an immutable field on the account. - When the account is deleted, we split the storage refund into two parts.
- The same amount of tokens as provided for account creation is refunded to the
sponsor_id
. - The remaining balance goes to
beneficiary_id
inside the delete action. (Same as today)
- The same amount of tokens as provided for account creation is refunded to the
note: By default, the sponsor has no way to enforce they get the balance back. Only if the user decides to delete their account will the amount be refunded to the sponsor.
However, using the magic of smart contracts, one could build in a dead-men-switch that allows deleting the account through a smart contract call IF the user becomes inactive for a long time, or even just after a free-tier trial period.
Simpler Alternative
Instead of defining a sponsor, we could also decide the amount of tokens is simply burned, while the user is still allowed to use the storage. This would be slightly simpler to implement.
But making it refundable to a specific sponsor could be useful. In combination with some kind of dead-men-switch to enforce the refund if the user doesn’t convert into a paying user, I believe this could be a much more sustainable business model than just burning the near tokens for all potential users.
But hey, I’m a protocol engineer and not familiar with running a business! So really any thoughts on this alternative are very much welcome. If we can get always with the “always burn” solution, I’m happy to take that route as well.
Detailed Changes Proposed
I will now describe the version wit the sponsor_id
in more details to show you the implementation challenges.
The biggest change will be an additional field on CreateAccountAction
to specify the sponsor.
This needs to be changed in tools, in the SDK, and even in existing smart contracts.
The most notable smart contract that is relevant here is the linkdrop account deployed on near
. It would have to read the sponsor ID as an argument in create_account()
and use it when creating a new account.
(Note: If we don’t define a sponsor_id
and go with burning tokens instead, we would still need a way to opt-in in the SDK and in the linkdrop contract, so basically we need to change tooling eitherway.)
Then we need to track the sponsor id and the initial balance on the account. We can do it by adding new fields to the account metadata struct.
Populating the initial amount is going to be a bit gimmicky because funding the account happens in a transfer action after the account creation action. (After just the CreateAccountAction
the balance is always 0)
To solve this, I propose that we define the initial amount as the amount of tokens on the account at the end of the account creation receipt, which would typically be after the initial transfer and inserting access keys.
You might wonder, why even track the initial balance? As opposed to refund whatever is left? Because the storage staking required can change all the time, up or down. If it goes down (for example because the user deploys a smaller contract) and we don’t keep the initial amount locked, we are back into faucet-draining territory.
Therefore it is important that the initial amount of tokens would remain locked even after state is removed! Which means we have to keep track of what that initial balance was.
Lastly, when deleting an account, we would create two refunds instead of one. Now you might wonder, why not send the entire refund to the sponsor? That would also be a valid choice. I am very curious to hear opinions on this choice. Just remember that the amount refunded covers ALL the Near tokens left on the account, whether they were locked for initial storage, storage added later, or not locked at all.
Please let me know what you think!