Next, we'll introduce the storage block. This block is where you store state variables in your contract that need to be persistent. Any Sway primitive type can be placed in the storage block.
Variables declared within a function and not saved in the storage block will be discarded once the function completes its execution.
contract;
use std::{
auth::msg_sender,
call_frames::msg_asset_id,
constants::BASE_ASSET_ID,
context::{
msg_amount,
this_balance,
},
token::transfer,
hash::Hash
};
struct Item {
id: u64,
price: u64,
owner: Identity,
metadata: str[20],
total_bought: u64,
}
abi SwayStore {
// a function to list an item for sale
// takes the price and metadata as args
#[storage(read, write)]
fn list_item(price: u64, metadata: str[20]);
// a function to buy an item
// takes the item id as the arg
#[storage(read, write), payable]
fn buy_item(item_id: u64);
// a function to get a certain item
#[storage(read)]
fn get_item(item_id: u64) -> Item;
// a function to set the contract owner
#[storage(read, write)]
fn initialize_owner() -> Identity;
// a function to withdraw contract funds
#[storage(read)]
fn withdraw_funds();
// return the number of items listed
#[storage(read)]
fn get_count() -> u64;
}
storage {
// counter for total items listed
item_counter: u64 = 0,
// map of item IDs to Items
item_map: StorageMap<u64, Item> = StorageMap {},
// owner of the contract
owner: Option<Identity> = Option::None,
}
enum InvalidError {
IncorrectAssetId: AssetId,
NotEnoughTokens: u64,
OnlyOwner: Identity,
}
impl SwayStore for Contract {
#[storage(read, write)]
fn list_item(price: u64, metadata: str[20]) {
// increment the item counter
storage.item_counter.write(storage.item_counter.try_read().unwrap() + 1);
// get the message sender
let sender = msg_sender().unwrap();
// configure the item
let new_item: Item = Item {
id: storage.item_counter.try_read().unwrap(),
price: price,
owner: sender,
metadata: metadata,
total_bought: 0,
};
// save the new item to storage using the counter value
storage.item_map.insert(storage.item_counter.try_read().unwrap(), new_item);
}
#[storage(read, write), payable]
fn buy_item(item_id: u64) {
// get the asset id for the asset sent
let asset_id = msg_asset_id();
// require that the correct asset was sent
require(asset_id == BASE_ASSET_ID, InvalidError::IncorrectAssetId(asset_id));
// get the amount of coins sent
let amount = msg_amount();
// get the item to buy
let mut item = storage.item_map.get(item_id).try_read().unwrap();
// require that the amount is at least the price of the item
require(amount >= item.price, InvalidError::NotEnoughTokens(amount));
// update the total amount bought
item.total_bought += 1;
// update the item in the storage map
storage.item_map.insert(item_id, item);
// only charge commission if price is more than 0.1 ETH
if amount > 100_000_000 {
// keep a 5% commission
let commission = amount / 20;
let new_amount = amount - commission;
// send the payout minus commission to the seller
transfer(item.owner, asset_id, new_amount);
} else {
// send the full payout to the seller
transfer(item.owner, asset_id, amount);
}
}
#[storage(read)]
fn get_item(item_id: u64) -> Item {
// returns the item for the given item_id
storage.item_map.get(item_id).try_read().unwrap()
}
#[storage(read, write)]
fn initialize_owner() -> Identity {
let owner = storage.owner.try_read().unwrap();
// make sure the owner has NOT already been initialized
require(owner.is_none(), "owner already initialized");
// get the identity of the sender
let sender = msg_sender().unwrap();
// set the owner to the sender's identity
storage.owner.write(Option::Some(sender));
// return the owner
sender
}
#[storage(read)]
fn withdraw_funds() {
let owner = storage.owner.try_read().unwrap();
// make sure the owner has been initialized
require(owner.is_some(), "owner not initialized");
let sender = msg_sender().unwrap();
// require the sender to be the owner
require(sender == owner.unwrap(), InvalidError::OnlyOwner(sender));
// get the current balance of this contract for the base asset
let amount = this_balance(BASE_ASSET_ID);
// require the contract balance to be more than 0
require(amount > 0, InvalidError::NotEnoughTokens(amount));
// send the amount to the owner
transfer(owner.unwrap(), BASE_ASSET_ID, amount);
}
#[storage(read)]
fn get_count() -> u64 {
storage.item_counter.try_read().unwrap()
}
}
The first variable we've stored is item_counter
, a number initialized to 0. This counter can be used to track the total number of items listed.
A StorageMap
is a unique type that permits the saving of key-value pairs within a storage block.
To define a storage map, you need to specify the types for both the key and the value. For instance, in the example below, the key type is u64
, and the value type is an Item
struct.
contract;
use std::{
auth::msg_sender,
call_frames::msg_asset_id,
constants::BASE_ASSET_ID,
context::{
msg_amount,
this_balance,
},
token::transfer,
hash::Hash
};
struct Item {
id: u64,
price: u64,
owner: Identity,
metadata: str[20],
total_bought: u64,
}
abi SwayStore {
// a function to list an item for sale
// takes the price and metadata as args
#[storage(read, write)]
fn list_item(price: u64, metadata: str[20]);
// a function to buy an item
// takes the item id as the arg
#[storage(read, write), payable]
fn buy_item(item_id: u64);
// a function to get a certain item
#[storage(read)]
fn get_item(item_id: u64) -> Item;
// a function to set the contract owner
#[storage(read, write)]
fn initialize_owner() -> Identity;
// a function to withdraw contract funds
#[storage(read)]
fn withdraw_funds();
// return the number of items listed
#[storage(read)]
fn get_count() -> u64;
}
storage {
// counter for total items listed
item_counter: u64 = 0,
// map of item IDs to Items
item_map: StorageMap<u64, Item> = StorageMap {},
// owner of the contract
owner: Option<Identity> = Option::None,
}
enum InvalidError {
IncorrectAssetId: AssetId,
NotEnoughTokens: u64,
OnlyOwner: Identity,
}
impl SwayStore for Contract {
#[storage(read, write)]
fn list_item(price: u64, metadata: str[20]) {
// increment the item counter
storage.item_counter.write(storage.item_counter.try_read().unwrap() + 1);
// get the message sender
let sender = msg_sender().unwrap();
// configure the item
let new_item: Item = Item {
id: storage.item_counter.try_read().unwrap(),
price: price,
owner: sender,
metadata: metadata,
total_bought: 0,
};
// save the new item to storage using the counter value
storage.item_map.insert(storage.item_counter.try_read().unwrap(), new_item);
}
#[storage(read, write), payable]
fn buy_item(item_id: u64) {
// get the asset id for the asset sent
let asset_id = msg_asset_id();
// require that the correct asset was sent
require(asset_id == BASE_ASSET_ID, InvalidError::IncorrectAssetId(asset_id));
// get the amount of coins sent
let amount = msg_amount();
// get the item to buy
let mut item = storage.item_map.get(item_id).try_read().unwrap();
// require that the amount is at least the price of the item
require(amount >= item.price, InvalidError::NotEnoughTokens(amount));
// update the total amount bought
item.total_bought += 1;
// update the item in the storage map
storage.item_map.insert(item_id, item);
// only charge commission if price is more than 0.1 ETH
if amount > 100_000_000 {
// keep a 5% commission
let commission = amount / 20;
let new_amount = amount - commission;
// send the payout minus commission to the seller
transfer(item.owner, asset_id, new_amount);
} else {
// send the full payout to the seller
transfer(item.owner, asset_id, amount);
}
}
#[storage(read)]
fn get_item(item_id: u64) -> Item {
// returns the item for the given item_id
storage.item_map.get(item_id).try_read().unwrap()
}
#[storage(read, write)]
fn initialize_owner() -> Identity {
let owner = storage.owner.try_read().unwrap();
// make sure the owner has NOT already been initialized
require(owner.is_none(), "owner already initialized");
// get the identity of the sender
let sender = msg_sender().unwrap();
// set the owner to the sender's identity
storage.owner.write(Option::Some(sender));
// return the owner
sender
}
#[storage(read)]
fn withdraw_funds() {
let owner = storage.owner.try_read().unwrap();
// make sure the owner has been initialized
require(owner.is_some(), "owner not initialized");
let sender = msg_sender().unwrap();
// require the sender to be the owner
require(sender == owner.unwrap(), InvalidError::OnlyOwner(sender));
// get the current balance of this contract for the base asset
let amount = this_balance(BASE_ASSET_ID);
// require the contract balance to be more than 0
require(amount > 0, InvalidError::NotEnoughTokens(amount));
// send the amount to the owner
transfer(owner.unwrap(), BASE_ASSET_ID, amount);
}
#[storage(read)]
fn get_count() -> u64 {
storage.item_counter.try_read().unwrap()
}
}
Here, we are creating a mapping from the item's ID to the Item
struct. Using this, we can retrieve information about an item using its ID.
Here, we are defining the owner
variable as one that can either be None
or hold an Identity
.
contract;
use std::{
auth::msg_sender,
call_frames::msg_asset_id,
constants::BASE_ASSET_ID,
context::{
msg_amount,
this_balance,
},
token::transfer,
hash::Hash
};
struct Item {
id: u64,
price: u64,
owner: Identity,
metadata: str[20],
total_bought: u64,
}
abi SwayStore {
// a function to list an item for sale
// takes the price and metadata as args
#[storage(read, write)]
fn list_item(price: u64, metadata: str[20]);
// a function to buy an item
// takes the item id as the arg
#[storage(read, write), payable]
fn buy_item(item_id: u64);
// a function to get a certain item
#[storage(read)]
fn get_item(item_id: u64) -> Item;
// a function to set the contract owner
#[storage(read, write)]
fn initialize_owner() -> Identity;
// a function to withdraw contract funds
#[storage(read)]
fn withdraw_funds();
// return the number of items listed
#[storage(read)]
fn get_count() -> u64;
}
storage {
// counter for total items listed
item_counter: u64 = 0,
// map of item IDs to Items
item_map: StorageMap<u64, Item> = StorageMap {},
// owner of the contract
owner: Option<Identity> = Option::None,
}
enum InvalidError {
IncorrectAssetId: AssetId,
NotEnoughTokens: u64,
OnlyOwner: Identity,
}
impl SwayStore for Contract {
#[storage(read, write)]
fn list_item(price: u64, metadata: str[20]) {
// increment the item counter
storage.item_counter.write(storage.item_counter.try_read().unwrap() + 1);
// get the message sender
let sender = msg_sender().unwrap();
// configure the item
let new_item: Item = Item {
id: storage.item_counter.try_read().unwrap(),
price: price,
owner: sender,
metadata: metadata,
total_bought: 0,
};
// save the new item to storage using the counter value
storage.item_map.insert(storage.item_counter.try_read().unwrap(), new_item);
}
#[storage(read, write), payable]
fn buy_item(item_id: u64) {
// get the asset id for the asset sent
let asset_id = msg_asset_id();
// require that the correct asset was sent
require(asset_id == BASE_ASSET_ID, InvalidError::IncorrectAssetId(asset_id));
// get the amount of coins sent
let amount = msg_amount();
// get the item to buy
let mut item = storage.item_map.get(item_id).try_read().unwrap();
// require that the amount is at least the price of the item
require(amount >= item.price, InvalidError::NotEnoughTokens(amount));
// update the total amount bought
item.total_bought += 1;
// update the item in the storage map
storage.item_map.insert(item_id, item);
// only charge commission if price is more than 0.1 ETH
if amount > 100_000_000 {
// keep a 5% commission
let commission = amount / 20;
let new_amount = amount - commission;
// send the payout minus commission to the seller
transfer(item.owner, asset_id, new_amount);
} else {
// send the full payout to the seller
transfer(item.owner, asset_id, amount);
}
}
#[storage(read)]
fn get_item(item_id: u64) -> Item {
// returns the item for the given item_id
storage.item_map.get(item_id).try_read().unwrap()
}
#[storage(read, write)]
fn initialize_owner() -> Identity {
let owner = storage.owner.try_read().unwrap();
// make sure the owner has NOT already been initialized
require(owner.is_none(), "owner already initialized");
// get the identity of the sender
let sender = msg_sender().unwrap();
// set the owner to the sender's identity
storage.owner.write(Option::Some(sender));
// return the owner
sender
}
#[storage(read)]
fn withdraw_funds() {
let owner = storage.owner.try_read().unwrap();
// make sure the owner has been initialized
require(owner.is_some(), "owner not initialized");
let sender = msg_sender().unwrap();
// require the sender to be the owner
require(sender == owner.unwrap(), InvalidError::OnlyOwner(sender));
// get the current balance of this contract for the base asset
let amount = this_balance(BASE_ASSET_ID);
// require the contract balance to be more than 0
require(amount > 0, InvalidError::NotEnoughTokens(amount));
// send the amount to the owner
transfer(owner.unwrap(), BASE_ASSET_ID, amount);
}
#[storage(read)]
fn get_count() -> u64 {
storage.item_counter.try_read().unwrap()
}
}
If you want a value to be potentially null or undefined under specific conditions, you can employ the Option
type. It's an enum that can take on either Some(value)
or None
. The keyword None
indicates the absence of a value, while Some
signifies the presence of a stored value.
Was this page helpful?