We are going to learn how to create, update and remove global storage data structures using this stake pool concept.
Making a struct
The struct we are making here is called StakePool
which has only one attribute and that’s amount
struct StakePool has key {
amount: u64
}
Adding a user
Adding a user is essentially giving their account this data structure and then manipulating that data. To do that, we just
fun add_user(account: &signer) {
let amount: u64 = 0;
move_to(account, StakePool{amount});
}
The move_to function lets us move the struct to the signer’s account. Here we are giving the amount attribute to their stake pool data structure as 0. It basically means they have added 0 tokens to the stakepool.
Reading the stake pool amount of a user
We need to be able to read how much a user has staked, which basically means we need to retrieve the amount
attribute of the user. To do that,
fun read_pool(account: address): u64 acquires StakePool {
borrow_global<StakePool>(account).amount
}
Keep in mind that this borrow_global method only returns a ‘read only’ version of the data, so you cannot use this to edit the amount whatsoever. To do that you need to the borrow_global_mut<StakePool>()
function which will help you change the amount
value however you like.
Staking some tokens
For a user to stake some amount of tokens, their stake pool amount must increase by the amount of tokens they are willing to stake. In this example we assume that the user is willing to stake a total of 100 tokens, so essentially we are going to add 100 to their stake pool amount.
fun stake(account: address) acquires StakePool {
let entry = &mut borrow_global_mut<StakePool>(account).amount;
*entry = *entry + 100;
}
Here, we are making an object called entry
which is essentially a mutable version of the account
object inside the user’s stakepool.
We then dereference is using the asterisk, add 100 and then reassign it to the entry
object.
Unstaking tokens
We are now going to remove the 100 tokens previously staked using the same method, except we just subtract the 100 from the dereference.
fun unstake(account: address) acquires StakePool {
let entry = &mut borrow_global_mut<StakePool>(account).amount;
*entry = *entry - 100;
}
Confirming the existence of the user (checking if an account has the StakePool struct)
fun confirm_user(account: address): bool {
exists<StakePool>(account)
}
This function returns a boolean if the stake pool exists for a given account address.
Removing the struct from the account when it doesn’t have the drop attribute
fun remove_user(account: &signer): u64 acquires StakePool {
let entry = move_from<StakePool>(signer::address_of(account));
let StakePool{amount} = entry;
amount
}
The move_from
function removes the struct by default, but when you do not have the drop functionality baked into the struct, you will have to do this thing where you have to use the data you’ve received from it, or else the Aptos Move compiler will throw an error.
Removing the struct from the account when it does have the drop attribute
// Making sure the struct has the drop attribute
struct StakePool has key, drop {
amount: u64
}
// The remove function can simple be just the move_from function instead of all that jazz above
fun remove_user(account: &signer): u64 acquires StakePool {
move_from<StakePool>(signer::address_of(account));
}
Since the drop attribute exists for the struct you’re trying to purge from the account, you can simply just use the move_from
function to pop it right out and the move compiler won’t mind.
Testing
#[test_only]
use std::string::{utf8};
#[test_only]
use std::debug::print;
#[test(user = @0x123)]
fun test_function(user: signer) acquires StakePool {
add_user(&user);
assert!(read_pool(signer::address_of(&user)) == 0, 1);
print(&utf8(b"User has been added successfully"));
stake(signer::address_of(&user));
assert!(read_pool(signer::address_of(&user)) == 100, 1);
print(&utf8(b"User has staked a 100 tokens"));
unstake(signer::address_of(&user));
assert!(read_pool(signer::address_of(&user)) == 0, 1);
print(&utf8(b"User has unstaked a 100 tokens"));
remove_user(&user);
assert!(confirm_user(signer::address_of(&user)) == false, 1);
print(&utf8(b"User has been removed successfully"));
}