本節學習智慧合約的開發,主要包括:
- ink!智慧合約的結構;
- 儲存單個的值和hash map;
- 安全的設定或獲取這些值;
- 編寫public和private函式;
- 配置Rust使用安全的數學庫。
- ink!
ink!是一種嵌入式領域特定語言,可以用Rust來編寫基於webassembly的智慧合約。
ink!實際上是一種使用#[ink(…)]屬性宏標準的Rust寫法。
- 開始一個新的專案
從建立到編譯,執行命令如下:
cargo contract new incrementer
cd incrementer/
cargo +nightly test
cargo +nightly contract build
substrate合約可以儲存使用Parity Codec編碼和解碼的型別,例如像bool, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, string, 元組,陣列等。
另外ink!還提供substrate特定型別如AccountId,Balance,Hash等作為原始型別。
- 合約函式
寫法如下:
impl MyContract {
// Public and Private functions go here
}
- 建構函式
每個ink!智慧合約必須有在建立時執行一次的建構函式。每個ink!智慧合約可以有多個建構函式。 建構函式寫法如下:
use ink_lang as ink;
#[ink::contract]
mod mycontract {
#[ink(storage)]
pub struct MyContract {
number: u32,
}
impl MyContract {
/// Constructor that initializes the `u32` value to the given `init_value`.
#[ink(constructor)]
pub fn new(init_value: u32) -> Self {
Self {
number: init_value,
}
}
/// Constructor that initializes the `u32` value to the `u32` default.
///
/// Constructors can delegate to other constructors.
#[ink(constructor)]
pub fn default() -> Self {
Self {
number: Default::default(),
}
}
/* --snip-- */
}
}
- 公有函式和私有函式
寫法如下:
impl MyContract {
/// Public function
#[ink(message)]
pub fn my_public_function(&self) {
/* --snip-- */
}
/// Private function
fn my_private_function(&self) {
/* --snip-- */
}
/* --snip-- */
}
- 獲取合約上的值
寫法如下:
impl MyContract {
#[ink(message)]
pub fn my_getter(&self) -> u32 {
self.number
}
}
- 可變和不可變的函式
寫法舉例如下:
impl MyContract {
#[ink(message)]
pub fn my_getter(&self) -> u32 {
self.my_number
}
#[ink(message)]
pub fn my_setter(&mut self, new_value: u32) {
self.my_number = new_value;
}
}
- 惰性儲存值
使用示例如下:
#[ink(storage)]
pub struct MyContract {
// Store some number
my_number: ink_storage::Lazy<u32>,
}
impl MyContract {
#[ink(constructor)]
pub fn new(init_value: u32) -> Self {
Self {
my_number: ink_storage::Lazy::<u32>::new(init_value),
}
}
#[ink(message)]
pub fn my_setter(&mut self, new_value: u32) {
ink_storage::Lazy::<u32>::set(&mut self.my_number, new_value);
}
#[ink(message)]
pub fn my_adder(&mut self, add_value: u32) {
let my_number = &mut self.my_number;
let cur = ink_storage::Lazy::<u32>::get(my_number);
ink_storage::Lazy::<u32>::set(my_number, cur + add_value);
}
}
- HashMap
使用示例如下:
#[ink(storage)]
pub struct MyContract {
// Store a mapping from AccountIds to a u32
my_number_map: ink_storage::collections::HashMap<AccountId, u32>,
}
- HashMap API
HashMap常用的API如下:
/// Inserts a key-value pair into the map.
///
/// Returns the previous value associated with the same key if any.
/// If the map did not have this key present, `None` is returned.
pub fn insert(&mut self, key: K, new_value: V) -> Option<V> {/* --snip-- */}
/// Removes the key/value pair from the map associated with the given key.
///
/// - Returns the removed value if any.
pub fn take<Q>(&mut self, key: &Q) -> Option<V> {/* --snip-- */}
/// Returns a shared reference to the value corresponding to the key.
///
/// The key may be any borrowed form of the map's key type,
/// but `Hash` and `Eq` on the borrowed form must match those for the key type.
pub fn get<Q>(&self, key: &Q) -> Option<&V> {/* --snip-- */}
/// Returns a mutable reference to the value corresponding to the key.
///
/// The key may be any borrowed form of the map's key type,
/// but `Hash` and `Eq` on the borrowed form must match those for the key type.
pub fn get_mut<Q>(&mut self, key: &Q) -> Option<&mut V> {/* --snip-- */}
/// Returns `true` if there is an entry corresponding to the key in the map.
pub fn contains_key<Q>(&self, key: &Q) -> bool {/* --snip-- */}
/// Converts the OccupiedEntry into a mutable reference to the value in the entry
/// with a lifetime bound to the map itself.
pub fn into_mut(self) -> &'a mut V {/* --snip-- */}
/// Gets the given key's corresponding entry in the map for in-place manipulation.
pub fn entry(&mut self, key: K) -> Entry<K, V> {/* --snip-- */}
- 初始化一個HashMap
初始化示例如下:
#![cfg_attr(not(feature = "std"), no_std)]
use ink_lang as ink;
#[ink::contract]
mod mycontract {
#[ink(storage)]
pub struct MyContract {
// Store a mapping from AccountIds to a u32
my_number_map: ink_storage::collections::HashMap<AccountId, u32>,
}
impl MyContract {
/// Public function.
/// Default constructor.
#[ink(constructor)]
pub fn default() -> Self {
Self {
my_number_map: Default::default(),
}
}
/// Private function.
/// Returns the number for an AccountId or 0 if it is not set.
fn my_number_or_zero(&self, of: &AccountId) -> u32 {
let balance = self.my_number_map.get(of).unwrap_or(&0);
*balance
}
}
}
- 合約呼叫者
返回合約呼叫者的示例如下:
#![cfg_attr(not(feature = "std"), no_std)]
use ink_lang as ink;
#[ink::contract]
mod mycontract {
#[ink(storage)]
pub struct MyContract {
// Store a contract owner
owner: AccountId,
}
impl MyContract {
#[ink(constructor)]
pub fn new() -> Self {
Self {
owner: Self::env().caller();
}
}
/* --snip-- */
}
}
- 修改hash map
示例1如下:
impl MyContract {
/* --snip-- */
// Set the value for the calling AccountId
#[ink(message)]
pub fn set_my_number(&mut self, value: u32) {
let caller = self.env().caller();
self.my_number_map.insert(caller, value);
}
// Add a value to the existing value for the calling AccountId
#[ink(message)]
pub fn add_my_number(&mut self, value: u32) {
let caller = self.env().caller();
let my_number = self.my_number_or_zero(&caller);
self.my_number_map.insert(caller, my_number + value);
}
/// Returns the number for an AccountId or 0 if it is not set.
fn my_number_or_zero(&self, of: &AccountId) -> u32 {
*self.my_number_map.get(of).unwrap_or(&0)
}
}
示例2如下:
let caller = self.env().caller();
self.my_number_map
.entry(caller)
.and_modify(|old_value| *old_value += by)
.or_insert(by);
總的來說,寫ink!合約和直接用Rust編碼沒有太大的區別,只要能使用Rust都能很快的編寫合約。
docs.substrate.io/tutorials/v3/ink...
本作品採用《CC 協議》,轉載必須註明作者和本文連結