Flipper Contract
This is step-by-step explanation of the process behind building an ink! smart contract, using a simple app called Flipper. The examples provided within this guide will help you develop an understanding of the basic elements and structure of ink! smart contracts.
What is Flipper?
Flipper is a basic smart contract that allows the user to toggle a boolean value located in storage to either true
or false
. When the flip function is called, the value will change from one to the other.
Prerequisites
Please refer to the previous section for the list of prerequisites.
Flipper Smart Contract
In a new project folder, execute the following:
$ cargo contract new flipper # flipper is introduced from the beginning.
$ cd flipper/
$ cargo contract build #build flipper app
💡 If you receive an error such as:
ERROR: cargo-contract cannot build using the "stable" channel. Switch to nightly.
Execute:
$ rustup default nightly
to reconfigure the default Rust toolchain to use the nightly build, or
$ cargo +nightly contract build
to use the nightly build explicitly, which may be appropriate for developers working exclusively with ink!
Once the operation has finished, and the Flipper project environment has been initialized, we can perform an examination of the file and folder structure. Let’s dive a bit deeper into the project structure:
The File Structure of Flipper
target
: Contains build / binary information.Cargo.lock
: The lock file for dependency package.Cargo.toml
: The package configuration.lib.rs
: The contract logic.
Flipper Contract lib.rs
#![cfg_attr(not(feature = "std"), no_std)]
#[ink::contract]
mod flipper {
/// Defines the storage of your contract.
/// Add new fields to the below struct in order
/// to add new static storage fields to your contract.
#[ink(storage)]
pub struct Flipper {
/// Stores a single `bool` value on the storage.
value: bool,
}
impl Flipper {
/// Constructor that initializes the `bool` value to the given `init_value`.
#[ink(constructor)]
pub fn new(init_value: bool) -> Self {
Self { value: init_value }
}
/// Constructor that initializes the `bool` value to `false`.
///
/// Constructors can delegate to other constructors.
#[ink(constructor)]
pub fn default() -> Self {
Self::new(Default::default())
}
/// A message that can be called on instantiated contracts.
/// This one flips the value of the stored `bool` from `true`
/// to `false` and vice versa.
#[ink(message)]
pub fn flip(&mut self) {
self.value = !self.value;
}
/// Simply returns the current value of our `bool`.
#[ink(message)]
pub fn get(&self) -> bool {
self.value
}
}
/// Unit tests in Rust are normally defined within such a `#[cfg(test)]`
/// module and test functions are marked with a `#[test]` attribute.
/// The below code is technically just normal Rust code.
#[cfg(test)]
mod tests {
/// Imports all the definitions from the outer scope so we can use them here.
use super::*;
/// We test if the default constructor does its job.
#[ink::test]
fn default_works() {
let flipper = Flipper::default();
assert_eq!(flipper.get(), false);
}
/// We test a simple use case of our contract.
#[ink::test]
fn it_works() {
let mut flipper = Flipper::new(false);
assert_eq!(flipper.get(), false);
flipper.flip();
assert_eq!(flipper.get(), true);
}
}
}
Contract Structure lib.rs
#![cfg_attr(not(feature = "std"), no_std)]
use ink_lang as ink;
#[ink::contract]
mod flipper {
// This section defines storage for the contract.
#[ink(storage)]
pub struct Flipper {
}
// This section defines the functional logic of the contract.
impl Flipper {
}
// This section is used for testing, in order to verify contract validity.
#[cfg(test)]
mod tests {
}
}
Storage
#[ink(storage)]
pub struct Flipper {
}
This annotates a struct that represents the contract's internal state. (details):
#[ink(storage)]
Storage types:
- Rust primitives types
bool
u{8,16,32,64,128}
i{8,16,32,64,128}
String
- Substrate specific types
AccountId
Balance
Hash
- ink! storage type
Mapping
- Custom data structure details
This means the contract (Flipper) stores a single bool
value in storage.
#[ink(storage)]
pub struct Flipper {
value: bool,
}