---
id: registering-accounts
title: Registering Accounts
sidebar_label: Registering Accounts
---
import {Github} from "@site/src/components/codetabs"

In the previous tutorial, you looked at how to mint an initial circulating supply of tokens and how you could log events as per the [events standard](https://nomicon.io/Standards/Tokens/FungibleToken/Event). You then deployed the contract and saw the FTs in your wallet balance. In this tutorial, you'll learn about the [storage management](https://nomicon.io/Standards/StorageManagement) standard and how you can register accounts in your FT contract in order to prevent a malicious party from draining your contract of all its funds.

## Introduction

Whenever a new person receives any fungible tokens, they're added to the `accounts` lookup map on the contract. By doing this, you're adding bytes to the contract. If you made it so any user could receive FTs for free, that system could easily be abused. Users could essentially "drain" the contract of all it's funds by sending small amounts of FTs to many accounts. For this reason, you'll want to charge users for the information they're storing and the bytes they're using on the contract. This way of charging users, however, should be standardized so it works across all contracts.

*Enter the [storage management](https://nomicon.io/Standards/StorageManagement) standard*

<img width="65%" src="/docs/assets/fts/storage-standard-meme.png" />

### Storage Management Standard

The storage management standard is a set of rules that govern how a contract should charge users for storage. It outlines functions and behaviors such that all contracts implementing the standard are interoperable with each other. The 3 functions you'll need to implement are:
- **`storage_deposit`** - Allows a user to deposit some amount of storage to the contract. If the user over deposits, they're refunded for the excess $NEAR.
- **`storage_balance_of`** - Query for the storage paid by a given user
- **`storage_balance_bounds`** - Query for the minimum and maximum amount of storage needed to interact with a given contract.

With these functions outlined, you could make a reasonable assumption that the flow would be:
1. If a user doesn't exist on the contract, they need to deposit some storage to cover the bytes they use.
2. Once the user deposits $NEAR via the `storage_deposit` function, they're free to interact with the contract.

You might be asking yourself what the deposit amount should be. There are two ways you can go about getting this information:
- Dynamically calculate the bytes every individual user would take up in the `storage_deposit` function by inserting them into `accounts` map, measuring the bytes, and then removing them from the map after.
- Calculate the bytes for inserting a largest possible account ID once upon initializing the contract and simply charge every user the same amount.

For the purpose of simplicity, we'll assume the second method.

## Modifications to the Contract

This "bytes for longest account ID" should be stored in the contract's state such that we can pull the value during the `storage_deposit` function and ensure the user attaches enough $NEAR. Open the `src/lib.rs` file and add the following code to the `Contract` struct. If you're just joining us now, you can find the skeleton code for this tutorial in the `3.initial-supply` folder.

<Github language="rust" start="21" end="35" url="https://github.com/near-examples/ft-tutorial/blob/main/4.storage/src/lib.rs" />

You'll now need a way to calculate this amount which will be done in the initialization function. Move to the `src/internal.rs` file and add the following private function `measure_bytes_for_longest_account_id` which will add the longest possible account ID and remove it while measuring how many bytes the operation took. It will then set the `bytes_for_longest_account_id` field to the result.

<Github language="rust" start="36" end="45" url="https://github.com/near-examples/ft-tutorial/blob/main/4.storage/src/internal.rs" />

You'll also want to create a function for "registering" an account after they've paid for storage. To do this, you can simply insert them into the `accounts` map with a balance of 0. This way, you know that any account currently in the map is considered "registered" and have paid for storage. Any account that attempts to receive FTs must be in the map with a balance of 0 or greater. If they aren't, the contract should throw.

<Github language="rust" start="29" end="34" url="https://github.com/near-examples/ft-tutorial/blob/main/4.storage/src/internal.rs" />

Let's also create a function to panic with a custom message if the user doesn't exist yet.

<Github language="rust" start="5" end="14" url="https://github.com/near-examples/ft-tutorial/blob/main/4.storage/src/internal.rs" />

Now when you call the `internal_deposit` function, rather than defaulting the user's balance to `0` if they don't exist yet via:
```rust
let balance = self.accounts.get(&account_id).unwrap_or(0);
```
You can replace it with the following:
<Github language="rust" start="16" end="27" url="https://github.com/near-examples/ft-tutorial/blob/main/4.storage/src/internal.rs#L16-L27" />

With this finished, your `internal.rs` should look as follows:

```rust
use near_sdk::{require};

use crate::*;

impl Contract {
    pub(crate) fn internal_unwrap_balance_of(&self, account_id: &AccountId) -> Balance {
      ...
    }

    pub(crate) fn internal_deposit(&mut self, account_id: &AccountId, amount: Balance) {
      ...
    }

    pub(crate) fn internal_register_account(&mut self, account_id: &AccountId) {
      ...
    }

    pub(crate) fn measure_bytes_for_longest_account_id(&mut self) {
      ...
    }
}
```

There's only one problem you need to solve with this. When initializing the contract, the implementation will throw. This is because you call `internal_deposit` and the owner doesn't have a balance yet. To fix this, let's modify the initialization function to register the owner before depositing the FTs in their account. In addition, you should measure the bytes for the registration in this function.

<Github language="rust" start="66" end="106" url="https://github.com/near-examples/ft-tutorial/blob/main/4.storage/src/lib.rs" />

### Implementing Storage Standard

With this finished, you're now ready to implement the storage management standard. If you remember, the three functions you'll be implementing, we can break each down into their core functionality and decide how to proceed.

- **`storage_balance_bounds`** - Query for the minimum and maximum amount of storage needed to interact with a given contract.

Since you're creating a fungible token contract and the storage price won't change (unless the $NEAR cost per byte changes), the minimum and maximum storage costs should be the same. These values should be equal to the amount of bytes for the longest account ID you calculated in the `measure_bytes_for_longest_account_id` function multiplied by the current $NEAR price per byte. Switch to the `src/storage.rs` file to get started.

<Github language="rust" start="119" end="129" url="https://github.com/near-examples/ft-tutorial/blob/main/4.storage/src/storage.rs" />

- **`storage_balance_of`** - Query for the storage paid by a given user.

As we mentioned earlier, you can tell if somebody has paid for storage by checking if they're in the `accounts` map. If they are, you know that they've paid the amount returned by `storage_balance_bounds`.

<Github language="rust" start="131" end="138" url="https://github.com/near-examples/ft-tutorial/blob/main/4.storage/src/storage.rs" />

- **`storage_deposit`** - Allows a user to deposit some amount of storage to the contract. If the user over deposits, they're refunded for the excess $NEAR.

In order to implement this logic, you first need to get the attached deposit. You'll also need to ensure that the user hasn't already paid for storage (i.e. they're in the `accounts` map). If they are, simply refund the caller for the $NEAR they attached to the call.

If the user isn't registered yet, you should get the storage cost by calling `storage_balance_bounds` and make sure they've attached enough to cover that amount. Once this if finished, you can register them and refund any excess $NEAR.

<Github language="rust" start="78" end="117" url="https://github.com/near-examples/ft-tutorial/blob/main/4.storage/src/storage.rs" />

With this finished, you're ready to build and deploy the contract!

## Deploying the contract {#redeploying-contract}

Since the current contract you have is already initialized, let's create a sub-account and deploy to again.

### Creating a sub-account

Run the following command to create a sub-account `storage` of your main account with an initial balance of 25 NEAR which will be transferred from the original to your new account.

```bash
near create-account storage.$FT_CONTRACT_ID --masterAccount $FT_CONTRACT_ID --initialBalance 25
```

Next, you'll want to export an environment variable for ease of development:

```bash
export STORAGE_FT_CONTRACT_ID=storage.$FT_CONTRACT_ID
```

Using the build script, build the deploy the contract as you did in the previous tutorials:

```bash
cd 3.initial-supply && ./build.sh && cd .. && near deploy $STORAGE_FT_CONTRACT_ID out/contract.wasm
```

### Initialization {#initialization}

Now that the contract is deployed, it's time to initialize it and mint the total supply. Let's once again create an initial supply of 1000 `gtNEAR`.

```bash
near call $STORAGE_FT_CONTRACT_ID new_default_meta '{"owner_id": "'$STORAGE_FT_CONTRACT_ID'", "total_supply": "1000000000000000000000000000"}' --accountId $STORAGE_FT_CONTRACT_ID
```

If you now query for the storage paid by the owner, you should see that they're registered!
  
```bash
near view $STORAGE_FT_CONTRACT_ID storage_balance_of '{"account_id": "'$STORAGE_FT_CONTRACT_ID'"}'
```

This should return a struct. The Total amount is roughly `0.00125 $NEAR` to register and the user has 0 available $NEAR since it's all being used up to pay for registration.

```bash
{ total: '1250000000000000000000', available: '0' }
```

You can also query for the storage balance required to interact with the contract:

```bash
near view $STORAGE_FT_CONTRACT_ID storage_balance_bounds
```

You'll see that it returns the same amount as the `storage_balance_of` query above with the min equal to the max:

```bash
{ min: '1250000000000000000000', max: '1250000000000000000000' }
```

## Conclusion

Today you went through and created the logic for registering users on the contract. This logic adheres to the [storage management standard](https://nomicon.io/Standards/StorageManagement) and is customized to meet our needs when writing a FT contract. You then built, deployed, and tested those changes. In the [next tutorial](5.transfers.md), you'll look at the basics of how to transfer FTs to other users.