Some things to note

  • The table datatype is not native to the Move smart contract language.
  • Tables is a type that is a part of the Aptos framework.
  • You will have to import the Aptos framework to work with this

Lets make a real estate management smart contract that stores properties under sellers.

Imports

	use aptos_framework::table::{Self, Table};
    use std::signer;
    use std::string::String;

We gotta import the table::{Self, Table} from the aptos framework to be able to use it. The signer is needed to be able to use the move_to functionality to push the table to the sellers account. The string import is for storing the address strings in the property struct.

Making our structure to store property data

struct Property has store, copy, drop {
        baths: u16,
        beds: u16,
        sqm: u16,
        phy_addr: String,
        price: u64,
        available: bool
    }

Pretty self explanatory. We are making a struct Property that describes the house.

PropList for storing multiple properties under a single seller

Since there are going to be multiple houses under a single seller, we need a key to be able to access them. To do that, we need another struct with the key attribute, like this:

struct PropList has key {
        info: Table<u64, Property>,
        prop_id: u64
    }

We just need to use the prop_id to be able to traverse through the multiple properties a seller might have.

Now, moving on to the functions:

Registering a seller

fun register_seller(account: &signer) {
        let init_property = PropList {
            info: table::new(),
            prop_id: 0
        };
  
        move_to(account, init_property);
    }

This bit of code takes the reference to some account’s signer, makes a new PropList for them and then moves the PropList into their account.

Now, to list new properties:

Listing new properties under a seller

fun list_property(account: &signer, prop_info: Property)
    acquires PropList {
        let account_addr = signer::address_of(account);
        assert!(exists<PropList>(account_addr) == true, 404);
        let prop_list = borrow_global_mut<PropList>(account_addr);
        let new_id = prop_list.prop_id + 1;
        table::upsert(&mut prop_list.info, new_id, prop_info);
        prop_list.prop_id = new_id;
    }

We take the account (another reference to their signer), and a Property type as the input for this function. It needs to acquire PropList, since we are working with it. We are then acquiring the address of the seller using the signer::address_of(account); function. We check if the PropList exists using the assert. We then retrieve a mutable version of the PropList from the seller using the borrow_global_mut method. prop_id is set to 0 by default, when we initialize a user. We need to increment it by 1 every time we add a new property to a seller’s list. We then use the table::upsert(&mut prop_list.info, prop_info) to add a new entry to the PropList.

Reading Property info

fun read_property(account: &signer, prop_id: u64): (u16, u16, u16, String, u64, bool) //Gives you the info as a tuple
    acquires PropList {
        let account_addr = signer::address_of(account);
        assert!(exists<PropList>(account_addr) == true, 404);
        let prop_list = borrow_global<PropList>(account_addr);
        let info = table::borrow(&prop_list.info, prop_id);
        return (info.beds, info.baths, info.sqm, info.phy_addr, info.price, info.available)
    }

Function takes in the signer as a reference and the prop_id that you need to check, and then returns a bunch of data as a tuple as described. It needs to acquire PropList. First, you need to borrow the table from the seller’s PropList immutably into a variable called prop_list. Then you need to borrow the data under the id into the info variable using the table::borrow function. The info variable is now a Property struct. You can unpack it into a tuple in the return statement as shown.

Testing

#[test_only]
    use std::debug::print;
  
    #[test_only]
    use std::string::{utf8};
  
    #[test(seller1 = @0x123, seller2 = @0x124)]
    fun test_function(seller1: &signer, seller2: &signer) acquires PropList {
        register_seller(seller1);
        let prop_info = Property {
            baths: 2,
            beds: 3,
            sqm: 110,
            phy_addr: utf8(b"123 Main St, Montevideo, Uruguay"),
            price: 120000,
            available: true
            };
  
            list_property(seller1, prop_info);
            let (_,_,_,location,_,_) = read_property(seller1, 1);
            print(&location);
  
            register_seller(seller2);
            let prop_info2 = Property {
            baths: 3,
            beds: 3,
            sqm: 140,
            phy_addr: utf8(b"14 Ave, Berlin, Germany"),  
            price: 150000,
            available: true
            };
  
            list_property(seller2, prop_info2);
            let (_,_,_,_,price,_) = read_property(seller2, 1);
            print(&price);
  
            let (_,_,_,location,_,_) = read_property(seller2, 1);
            print(&location);
        }

The important thing to note here is this notation here near the end where you are acquiring only the information you need using the (_,_,_,_,price,_) notation. This will now load only price with the information, and then you could print it with the print(&price) function and be able to see that exact piece of information.