Online debugging:
https://playground.pontem.network/
0x00 Aptos
Aptos is a new Layer 1 public chain.
ff: "diem was formerly libra, libra changed its name to diem due to regulatory issues, diem was sold to meta, and then those guys came out with aptos and sui. " ff: "This is the kind of Web3 dog drama that should be in a hundred episodes."
0x01 Why Move?
- resource-oriented programming
- Original Avoid Double Flowers
- Security and formal validation
Move believes that Token assets are a very special and important kind of data that should not be defined and represented by ordinary numeric types, so it creates a separate Resource to define on-chain assets. This approach presents three characteristics:
- Resource still exists as a value in Move, and can be stored as a data structure or passed and returned as a parameter.
- Resource can securely represent a digital asset, it is special in that it cannot be copied, discarded or reused, but it can be securely stored and transferred, and the value of the Resource type can only be created and destroyed by the module that defines it, so it implements the meaning of an asset rather than a number.
- Resource is adapted to the characteristics of blockchain applications, such as being tied to an account; Resource data must be stored under an account, so the corresponding Resource asset only exists if an account has been assigned to it, and the Resource must be "used" whenever it is taken out of the account, which means that it is either passed as a return value, i.e., it must go somewhere, or it is destroyed directly, using the built-in Once an asset is removed from an account using the built-in Move_form method, it is either passed as a return value that has to go somewhere, or it is destroyed, which means that the asset is used as much as it is removed. Remember how Solidity works? It decreases the balance of one address, then increases it to another address, and then the code makes the number of decrease and increase the same, so in Solidity is completely rely on the logic of the code to realize the use of assets, but Resource is the concept of asset encapsulation at the bottom instead of adding and subtracting, avoiding the creation of assets out of thin air and random access, greatly improving security! The Token movement of Move can be regarded as moving bricks from one place to another, while Solidity is adding and subtracting, subtracting from one place and adding to another.
In summary, Move is a more native and tailored programming language dedicated to the issuance of digital assets, which enables the direct integration of programs with digital assets.
0x02 Structure of Move
- Modules
- Structured definitions
- function function
- global storage
- Scripts
- A tentative code snippet
- Modules functions can be called
- There can only be 1 function in a Script.
About -- public(script)
public fun
The : method can be called from any module.public(script) fun
: script function is the entry method in the module, indicating that the method can be invoked by initiating a transaction through the console, as if it were a locally executed script.- The next version of Move willexpense or outlay
public entry fun
substitute forpublic(script) fun
- Self is the self-module.
Self is the self-module.
public(script) fun init_counter(account: signer){
Self::init(&account)
}
public(script) fun incr_counter(account: signer) acquires Counter {
Self::incr(&account)
}
0x03 Variables & First demo
Move in Chinese comments will report an error, so write comments in English.
script{
use 0x1::Debug // import other modules, 0x1 is an offical modules library.
fun main(){
let num:u64 = 1024; }; Debug::print(&num); }
Debug::print(&num); }
}
}
Script execution:
- Find "Run Script" on the left sidebar.
- Type main()
- console outputs "debug:1024", and Gas cost."
Numeric, bool, address
Move does not support string types, only "numeric, bool, and address " .
- Plastic surgery (unsigned plastic surgery)
- u8 : unsigned 8 bits (2^8=) 256)
- u64 : unsigned 64 bits (2^64 = 18,446,744,073,709,551,616)
- u128
- bool : true or false
- address.
- Std / 0x1 / Sender etc. are address types.
signer
- Native datatype, only one ability: drop
struct signer has drop { a: address }
- Signer represents the person who sent the transaction
- Cannot be created in code, must be passed via script call
use StarcoinFramework::Signer
Instead, it uses the Signer module from the standard library, which is a native Resource-like non-copyable type that contains the address of the sender of the transaction. One of the reasons for introducing the signer type is to explicitly show which functions require sender privileges and which do not. Thus, a function cannot trick a user into unauthorized access to its Resource. see the source code for details.
API:
address_of ( &Signer ): address
Returns the address in the signerborrow_address(&Signer): &address
Returns a reference to an address in SIgner
script {
use 0x1::Signer;
use 0x1::Debug;
fun main_signer( sn: signer) { // if multi params, place signer first
// let a: signer = @0x42; // wrong, no constructor for signer
let addr = Signer::address_of(&sn);
Debug::print(&addr); }
}
}
Signer
Yes, library.signer
is a data type- If you want to wear more than one parameter, put the signer type in the first one.
- signer cannot be created (there is no constructor), it can only be called by passing a value.
public fun init(account: &signer){
move_to(account, Counter{value:0});
}
As above.init
The method argument is a &signer
, meaning that the method must be legally signed by an account before it can be called.
Resource
- Resource is a struct with restricted ability
- Resource has only 2 abilities: key and store.
- The Resource must be stored under the account.
- An account can only hold one resource at a time
API :
name | description | abort |
---|---|---|
move_ro(&Signer, T) | Publish Resource T to Signer | If Signer already has a T |
move_from(address): T | Deletes Signer's T resource and returns | if Signer does not have T |
borrow_global_mut(address): &mut T | Returns a mutable reference to T under Signer | if Signer does not have T |
borrow_global(address): &T | Returns an immutable reference to T under Signer | if Signer does not have T |
exists (address): bool | Returns whether a resource under address has type T. | Never |
create, move, Query Resource
① move_to(&signer, T)
: Publish and add a resource of type T to the address of the signer. ② exists(address): bool
: Determine whether there is a resource of type T under the address. ③ move_from(addr: address): T
-- Removes a resource of type T from under the address and returns this resource. ④ borrow_global(addr: address): &T
-- An immutable reference to a resource of type T under the return address. ⑤ borrow_global_mut(addr: address): &mut T
-- A mutable reference to a resource of type T under the return address.
// resouces/collection.move
module 0x1::collection{
use 0x1::Vector;
use 0x1::Signer;
struct Item has store, drop {}
// define resouce, // abilities of resouce only store & kkry
// abilities of resouce only store & kkry
struct Collection has store, key {} // define resouce, // abilities of resouce only store & kkry
items: vector, // vector not Vector
}
// move resource to account.
public fun start_collection(account: &signer){
move_to(account, Collection{
items: Vector::empty()
})
}
// judge exists ?
public fun exists_account(at: address): bool {
exists(at)
}
}
vector
Indicates that the container holds the Item
Type;
// scripts/test-resource.move
script {
use 0x1::collection as cl;
fun test_resource(account: signer) {
cl::start_collection(&account);
let addr = Signer::address_of(&account);
let isExists = cl::exists_account(addr);
Debug::print(&isExists); }
}
}
Modify Resource
module 0x1::collection{
use 0x1::Vector;
struct Item has store, drop {}
// define resouce, // abilities of resouce only store & kkry
// abilities of resouce only store & kkry
struct Collection has store, key {} // define resouce, // abilities of resouce only store & kkry
items: vector, // vector not Vector
}
// move resource
public fun start_collection(account: &signer){
move_to(account, Collection{
items: Vector::empty()
})
}
// judge exists ?
public fun exists_account(at: address): bool {
exists(at)
}
// modify
// acquires return resource list
public fun add_item(account: &signer) acquires Collection{
// get the resource mutable quote
let addr = Signer::address_of(account); let collection = borrow_global_mut(addr)
let collection = borrow_global_mut(addr);
Vector::push_back(&mut collection.items, Item{}); }
}
// return resources length
public fun size(account: &signer): u64 acquires Collection {
let addr = Signer::address_of(account);
let collection = borrow_global(addr);
Vector::length(&collection.items)
}
}
Testing:
① Cancel cl::start_collection(&account).
annotations, first create the
② Note off cl::start_collection(&account).
The following code will be executed.
script {
use 0x1::collection as cl;
fun test_resource(account: signer) {
cl::start_collection(&account);
// let addr = Signer::address_of(&account);
// let isExists = cl::exists_account(addr); // let addr = Signer::address_of(&account); }
// Debug::print(&isExists); // let isExists = cl::exists_account(addr); } }
let addr = Signer::address_of(&account); // Debug::print(&isExists); } }
cl::add_item(&account);
let lsize = cl::size(&account);
Debug::print(&lsize);
}
}
Destroy Resource
module 0x1::collection{
use 0x1::Vector;
struct Item has store, drop {}
// ...
// Destroy
public fun destroy(account: &signer) acquires Collection{
let addr = Signer::address_of(account); // remove collection from address_of(account).
// remove collection from address
let collection = move_from(addr); // remove collection from address.
// DESTROY.
let Collection{ items: _ } = collection; }
}
}
struct
is a new data type that can be specified as "ability ability "
struct Name [has ${ability}] { // initial capitalization
filed3: TYPE3, }
}
- The number of fields can range from 0 to 65535.
- Types can be native, customized, but type recursion is not allowed.
Example - Student Type:
// sources/student.move
address 0x1 {
module student {
struct Empty {}
struct Student {
id: u64,
struct Student { id: u64, age: u8,
sex: bool,
}
public fun newStudent(id: u64, age: u8, sex: bool): Student { //
return Student {
id: id, // or just write id, as in js
age: age, // age: age, means age , // public fun newStudent(id: u64)
sex: sex, // sex
}
}
}
}
// scripts/05-struct.move
script {
use 0x1::student;
use 0x1::Debug;
use 0x1::student; use 0x1::Debug; fun main() {
let stu1 = student::newStudent(10001, 24, true);
let id = student::getId(stu1);
Debug::print(&id); }
}
}
address 0x1 is another way to write struct Empty {} which means you can build an empty Struct, ps: no semicolon after it ;)
0x04 Function ( fun ~)
**Don't put a semicolon in the return line ; **
Function prototype:
[public] fun funcName(param1: Type, ... ): ReturnType{
// body ...
}
[public]
is access to the function, without Public access is limited to the module /ˈmɒdjuːl/.- The return value can be more than one, so you have to wrap it with ().
Define an address space Sender at Address, which is automatically converted to address 0x1 during subsequent compilation.
// module 0x01::Math{
module Sender::Math{
public fun sum(a:u64, b:u64): u64 {
// return a+b // it's ok .
a + b // `return` can be omitted.
}
}
script{
use 0x1::Debug; // use 0x1::Math; }
// use 0x1::Math; } } script{ use Sender::Math; } }
use Sender::Math; }
fun testSum(a:u64,b:u64){
let c:u64 = Math::sum(a, b); } }; let c:u64 = Math::sum(a, b); }
Debug::print(&c); }
}
}
I realized that the parameters passed in are (a:u8, b:u8) and the return needs to be of type u8 as well.
0x05 Loop & break
if, else
public fun max(a:u64, b:u64): u64 {
// a>b ? a : b
if (a >= b){
if (a >= b){ a
} else {
b
}
}
// 03-text-func.move
script {
use 0x1::Debug;
fun textMax(a:u64, b:u64) {
Debug::print(&MM::max(a, b)); }
}
}
while :
- continue ; jump out of this loop
- break; just pick the whole loop
// Calculate 1 +3 +5 ...
public fun sum99() :u64 {
let idx:u64 = 0;
let idx:u64 = 0; let res:u64 = 0; while ( idx <= 99) {
while ( idx <= 99) {
idx = idx + 1; // not support `+=` ?
if ( idx % 2 == 0) continue; res = res + idx + idx ?
res = res + idx
}; // Attention `;`
}; // Attention `;` return res
}
0x06 as Type Conversion
// sources/Math.move
module Sender::Math {
public fun sum(a:u64, b:u64):u64 {
a + b // `+` has higher priority than `as`
}
public fun sum2(a:u64, b:u8):u64 {
a + (b as u64) // `+` has higher priority than `as` }
}
}
// scripts/02-test-as.move
script {
use 0x1::Debug;
use Sender::Math;
fun testSum(a:u64, b:u8) {
let c:u64 = Math::sum2(a, b); } }
Debug::print(&c)
}
}
as can also be used for module command naming:
💡 use Sender::Math as SM;
script {
use 0x1::Debug;
use Sender::Math as SM;
fun testSum(a:u64, b:u8) {
let c:u64 = SM::sum2(a, b); } }; let c:u64 = SM::sum2(a, b); }
Debug::print(&c)
}
}
0x07 Scope and Lifecycle
Scope and Life Cycle
Global constants:
- const
- Inside a module or inside a script
- first letter A-Z
- Constant names must start with 'A...Z'
Local Variables:
- let, inside functions, initials a-z
address type, which, if put inside an expression, must be expressed with the @
notation
script {
use 0x1::Debug;
use Sender::Math as SM;
fun testSum() {
// let c:u64 = SM::sum2(a, b); //Debug::print(&SM::sum99();)
//Debug::print(&SM::sum99());
let dd:address = @Sender;
Debug::print(&dd);
}
}
0x08 Abort Assert Control
abort abort v. to quit, abandon.
- Usage:
abort 0
- Interrupts the execution of the current script. When the interruption is complete, the script will be restored to its initial state.
- i.e., suspension of execution, resumption of business
assert
- Macro definition, call:**
assert!(condition, code)
**
As an example, theDebug::print(&temp);
Not in abort(1)
After that, it will continue to be implemented.
let temp:u8 = 10; if (temp == 10) abort(1)
if (temp == 10) abort(1);
Debug::print(&temp);
You can also use assert to achieve the same effect as above:
- If the user's login status
loginStatus == false
, then throwscode == 401 Unauthorized
// let loginStatus:bool = false;
assert!(loginStatus, 401);
Debug::print(&loginStatus);
0x09 Tuple & quote Tuples and quotes
Tuple:
- Use parentheses to combine multiple variables, e.g. (x, y) (10, false)
- Definition and acceptance of multiple return values for functions
- Simultaneous definition of multiple variables
let (a, _) = (1, false);
// _ is a placeholder
// let (a:u8, b:bool) = (1, false); // Error doesn't need to write types... Confusion ...
let (a, b) = (1, false); // Error No need to write types...
C++ Frequently used &
References to replace pointers *
Because pointers expose the address directly. Because pointers expose addresses directly, but quotes don't.
Quote:
- quote Defines an alias for a variable
- The quote avoids exposing the address directly.
There are 2 ways to refer to Move:
- Immutable references & (read-only, unmodifiable -- secure.)
- variable reference
&mut
- Left values are used for assignments (required)
&mut
- The right value is used to read the
*
represents dereferencing, pointing to the value corresponding to the reference
- Left values are used for assignments (required)
Use quote to implement the exchange of values:
Maximum use: indirect assignment
// modules/Math.move
module Sender::Math {
// ...
public fun swap(a:&mut u64, b:&mut u64) { // no return
let temp = *a; *a = *b; // no return
*a = *b.
*b = temp.
}
}
// scripts/02-test.move
script {
use 0x1::Debug;
use Sender::Math as SM; }
fun testSum() {
SM::swap(&mut a, &mut b);
Debug::print(&a);
Debug::print(&b);
}
}
&mut a
Deconstruct a into variable referencesfun swap
utilization( a:&mut u64, ... )
to acceptvariable reference
*a
Dereference, at which point*a
Points to the value corresponding to the reference i.e. 1- Exchange, over.
0x0A generices generic
- Generic logic is independent of type;
- Generics allow coders to use types that are specified "later" in strongly typed languages, and to specify those types as parameters in instantiations.
- That is, the type is not determined in advance, but rather the execution phase is when we know what the type is, so there is uncertainty, insecurity
**fun funcName(x: Type)**
Here is a lower level implementation of the show function that prints different types:
module Sender::Math {
use 0x1::Debug;
// ...
public fun show(a: T) {
Debug::print(&a); }
}
}
After execution, I found that an error was reported:The type 'T' does not have the ability 'drop'
Plus drop ability, Compile passed:
public fun show(a: T) {
Debug::print(&a);
}
Subsequent use of structural generalization implements the advanced version of the capability.
Generic struct struct
// modules/user.move
address 0x1 {
module user {
struct Student has drop {
id: u64,
age: u8, sex: bool, id: u64,
sex: bool, }
}
struct User has drop { id: u64, age: u8, sex: bool, }
id: T1, age: T2, sex: bool, } struct User has drop {
age: T2, } struct User has drop { id: T1, age: T2, }
}
public fun newUser(id: T1, age: T2): User {
return User { id , age }
}
}
}
// scripts/test.move
script {
use 0x1::user;
use 0x1::Debug;
fun main() {
// let user1 = user::newUser(10001, 23); // auto cognize
let user1 = user::newUser(10001: u64, 23: u8); // good
Debug::print(&user1)
}
}
0x0B Vector
vector is a generic container provided by Move
let str_ = b "hello"; // cast "hello" as Vector of Ascii
let v2 = Vector::empty(); Vector::push_back(&mut v2, 10); // cast "hello" as Vector of Ascii
Vector::push_back(&mut v2, 10); // cast "hello" as Vector of Ascii let v2 = Vector::empty()
Print "String"
let str_ = b "Hello World";
Debug::print(&str_); // Attention No &str_ , but str_ , why ?
SM::show(str_); // Attention No &str_ , but str_ , why ?
Push Value to Vector:
script {
use 0x1::Debug;
use Sender::Math as SM; use 0x1::Vector; // maybe lowcase as 'vector', need test ?
use 0x1::Vector; // maybe lowcase as 'vector', need test ?
fun testSum() {
let v2 = Vector::empty(); Vector::push_back()
Vector::push_back(&mut v2, 1);
Vector::push_back(&mut v2, 10);
Debug::print(&v2);
}
}
Vector API
Termination: i.e., will an abort type termination exception be thrown?
empty(): vector | |
---|---|
singleton(t: T):vector | |
borrow(v: &vector, i:u64): &T | Return an immutable reference to T at index i. |
0x0C Type Capability
Several capabilities of the Move type
- Copy The modified value can be copied.
- Drop Modified values can be dropped at the end of the scope.
- The value modified by Key can be used to access the global state as a key value. (corresponding to another value, store, the key-store is a pair of
- Store The modified value can be stored to the global state.
A key,store qualifier means that it cannot be copied, discarded or reused, but it can be stored and transferred securely.
struct Counter has key, store {
value:u64, }
}
Blue: Native types such as integers, booleans, etc. naturally have copy, drop, and store capabilities.
Drop
public fun show(val: T) {
Debug::print(&val);
}
When the show function is called, the variable val is attributed to the function, and after the function is executed, the variable val has reached the end of its life cycle and needs to be discarded, so the compiler will require that the type of show T Ability to be droppedThe
0x0D Ownership issues
- Each variable has ownership
- The owner of the ownership (genus) is the scope
- move operation: owner (belonger) transfer operation
- copy Copy the value.
fun main() {
let tmp = 10; Math::show(move tmp);
Math::show(move tmp);
Math::show(tmp);
The main function transfers ownership of tmp to the show function, and main no longer owns the variable.
offside Math::show(tmp);
Calling it again will report an error.
There is no problem with either of the following 2 ways of writing.
- Debug::print(&id); : passes id as an immutable reference. This avoids copying.
- Use move to control resources.
- MOVE Design Philosophy: An asset is designed not to be copied, but only to be passed on.
fun main() {
let tmp = 10; Math::show(move tmp);
Math::show(move tmp);
Math::show(tmp);
Math::show(copy tmp); Math::show(copy tmp); Math::show(copy tmp)
Math::show(copy tmp); let tmp = 10; Math::show(copy tmp); Math::show(copy tmp)
0x0F Storage Analysis
That is, the sample code of https://playground.pontem.network/.
scripts
folder contains examples of using the storage module.
sources/Storage.move
Storage.move
Use Move language generics to store any type of data under a user account as a resource.
module Sender::Storage {
use Std::Signer;
//The resource would store `T` (generic) kind of data under `val` field. struct Storage has key { `val` field.
struct Storage has key {
val: T, }
}
// Store the `val` under user account in `Storage` resource.
// `signer` - the transaction sender.
// `val` - the value to store.
public fun store(account: &signer, val: T){
// Get address of `signer` by utilizing `Signer` module of Standard Library.
let addr = Signer::address_of(account); // Check if resource isn't exempt.
// Check if resource isn't exists already, otherwise throw error with code 101.
assert!(!exists<Storage>(addr), 101); // Check if resource isn't exists already, otherwise throw error with code 101.
// Create `Storage` resource contains provided value.
let to_store = Storage{
val, }; }; // Create `Storage` resource containing provided value.
}; // Create `Storage` resource containing provided value.
// 'Move' the Storage resource under user account, // so the resource will be placed into storage under user account.
// so the resource will be placed into storage under user account.
move_to(account, to_store); // 'Move' the Storage resource under user account, // so the resource will be placed into storage under user account.
// Get stored value under signer account.
// `signer` - transaction sender, which stored value.
public fun get(account: &signer): T acquires Storage{
let addr = Signer::address_of(account);
assert! (exists<Storage>(addr), 102);
let Storage { val } = move_from<(Storage)>(addr);
return val
}
}
public fun store :
use Std::Signer;
- Use the Signer data type from the standard library.
struct Storage
:- This resource will store data of the "T" generic type under the "val" field.
- Generic structs have key - store capability.
public fun store
:- Because the data is to be stored on the account, only one resource can be stored under an account, so if there is already a resource under the account, an error code should be run.
- Store "val" under the user account in the "Storage" structure resource.
T: store
: Requires the type T to have the ability to store.signer
is the sender of the transaction and val is the data to be stored;- Utilizing the standard library of
Signer
Module Acquisitionsigner
the address of the - Checks to see if the resource no longer exists, otherwise throws a Code 101 error.
let to_store = Storage{ val, };
assume (office){val: val ,}
Same as js Object
move_to(account, to_store).
Resource storage.
public fun get
- If a function wants to take out a resource and return it, it needs to use the
acquires
Keywords. let Storage { val }
Not primarily.let { val }
Test it
Test the Storage.move written above
exist . /scripts/store_bytes.move
This script can store bytes of data:
script {
use Sender::Storage;
// Script to store `vector` (bytes).
fun store_bytes(account: signer, val: vector) {
Storage::store(&account, val);
}
}
sources/Coins.move
This is an example of implementing Balance and Coin logic in Move without the Aptos framework.
module Sender::Coins {
use Std::Signer;
// In Move, all struct objects can have "abilities" from a hardcoded set of {copy, key, store, drop}. struct Coins has store { val: u64 }.
struct Coins has store { val: u64 }
// resource object which is marked by `key` ability. It can be added to the Move Storage directly. struct Balance has key { val: u64 }
It can be added to the Move Storage directly. struct Balance has key { val: u64 }
It can be added to the Move Storage directly. struct Balance has key { val: u64 } // It contains an amount of `Coins` inside.
It can be added to the Move Storage directly. struct Balance has key { // It contains an amount of `Coins` inside.
coins: Coins. coins: Coins. coins: Coins.}
// Error Code Definition
// 1. when `Balance` doesn't exist on account.
const ERR_BALANCE_NOT_EXISTS: u64 = 101; // 2. Error when `Balance` already exists on account.
const ERR_BALANCE_NOT_EXISTS: u64 = 101; // 2. Error when `Balance` already exists on account.
const ERR_BALANCE_EXISTS: u64 = 102; // 2. Error when `Balance` already exists on account.
// In Move you cannot directly create an instance of `Coin` from script, // Instead you need to use available scripts.
// Instead you need to use available constructor methods. In general case, those methods could have some permission
// restrictions, i.e. `mint(acc, val: u64)` method would require `&signer` of coin creator as the first argument
// which is only available in transactions signed by that account.
// In the current example anyone can mint(acc, val: u64)` method.
// In the current example anyone can mint as many coins as want, but usually you can add restrictions (MintCapabilities), // for details look at standard library (MintCapabilities).
// for details look at standard library (links in the herd of the file).
public fun mint(val: u64): Coins {
let new_coin = Coins{ val }; new_coin = Coins{ val }; new_coin = Coins{ val }
new_coin
new_coin = Coins{ val }; new_coin }
/// If struct object does not have `drop` ability, it cannot be destroyed at the end of the script scope, /// and needs explicit desctruct.
/// and needs explicit desctructuring method.
public fun burn(coin: Coins) {
public fun burn(coin: Coins) { let Coins{ val: _ } = coin.
}
public fun create_balance(acc: &signer) {
let acc_addr = Signer::address_of(acc);
assert!(!balance_exists(acc_addr), ERR_BALANCE_EXISTS);
let zero_coins = Coins{ val: 0 };
move_to(acc, Balance { coins: zero_coins });
}
// Check if `Balance` resource exists on account.
public fun balance_exists(acc_addr: address): bool {
exists(acc_addr)
}
// Create `Balance` resource to account.
// In Move to store resource under account you have to provide user signature (`acc: &signer`).
// So before starting work with balances (use `deposit`, `withdraw`), account should add Balance resource
// on it's own account.
public fun create_balance(acc: &signer) {
let acc_addr = Signer::address_of(acc);
assert!(!balance_exists(acc_addr), ERR_BALANCE_EXISTS);
let zero_coins = Coins{ val: 0 };
move_to(acc, Balance { coins: zero_coins });
}
// Check if `Balance` resource exists on account.
public fun balance_exists(acc_addr: address): bool {
exists(acc_addr)
}
// Deposit coins to user's balance (to `acc` balance). public fun deposit(acc_addr: address: bool { exists(acc_addr) }
public fun deposit(acc_addr: address, coin: Coins) acquires Balance {
public fun deposit(acc_addr: address, coin: Coins); public fun deposit(acc_addr: address, coin: `acc` balance); public fun deposit(acc_addr: address, coin: Coins)
let Coins { val } = coin; let balance = borrow_global_exists(acc_addr, ERR_BALANCE_NOT_EXISTS)
let balance = borrow_global_mut(acc_addr);
balance.coins.val = balance.coins.val + val;
}
// Withdraw coins from user's balance (withdraw from `acc` balance).
public fun withdraw(acc: &signer, val: u64): Coins acquires Balance {
let acc_addr = Signer::address_of(acc);
assert!(balance_exists(acc_addr), ERR_BALANCE_NOT_EXISTS);
let balance = borrow_global_mut(acc_addr);
balance.coins.val = balance.coins.val - val;
Coins{ val }
}
// Get balance of an account.
public fun balance(acc_addr: address): u64 acquires Balance {
borrow_global(acc_addr).coins.val
}
}
- In Move, all struct objects can be retrieved from the hardcoded set's
{copy, key, store, drop}
Get "abilities" in the Balance Resource
Represents the user balance stored under the account by thekey
Capability Marker. It can be added directly to removable storage.- As you can see, the Balance struct calls the Coin it generated in the previous step (maybe for extensibility?).
fun burn
: If the struct object does not havedrop
capability, it would not be able to be destroyed at the end of the script scope, so an explicit deconstruction method is required.let Coins{ val: _} = coin
: here with_
To accept the original coin information, it is also a disguised destruction of the original Coin.
create_balance
: CreateBalance
resources to the account, and when storing resources under the account, you must provide the user signature (acc: &signer
), so there is no need to use thedeposit
,withdraw
The account should add Balance resource on it's own account.move_to(acc, Balance { coins: Coins{ val: 0 }});
: Create Balance This completes the operation of creating a balance by adding an object with a balance of zero.
public fun deposit
: Deposits.borrow_global_mut(acc_addr).
Retrieve a mutable object to make changes - increase the balance.
public fun withdraw
: Withdrawals (withdrawals)return Coins{ val }
: Amount withdrawnNeed to returnThe
Contrast deposit and withdraw:
deposit(acc_addr: address)
Passed in is an addresswithdraw(acc: &signer, val: u64)
Passed in is a signer
Test it!
mint.move
script {
use Std::Signer;
use Sender::Coins;
// Mint new coins and deposit to account.
fun mint(acc: signer, amount: u64) {
let acc_addr = Signer::address_of(&acc);
let coins = Coins::mint(amount);
if (!Coins::balance_exists(acc_addr)) {
Coins::create_balance(&acc);
};
Coins::deposit(acc_addr, coins);
assert!(Coins::balance(acc_addr) == amount, 1);;
}
}
- Mint Coins
- Creating a Balance for an Account
- Adds a balance to the account Balance.
sources/Transfer.move
Transfer: withdraw Coins from the acc account and deposit them to the recipient account.
script{
use Sender::Coins;
// Script to mint coins and deposit coins to account.
// Recipient should have created `Balance` resources on his account.
fun mint_and_deposit(acc: signer, recipient: address, amount: u64){
let coins = Coins::withdraw(&acc, amount);
Coins::deposit(recipient, coins);
}
}
Test Functions:mint_and_deposit(0x41, 0x51, 25)
But rashly executing it this way will report an error:
Because 0x51 does not have a Balance Balance yet.
mint_coins(0x51, 0)
Add Balance
The transfer can then be executed:
mint_and_deposit(0x41, 0x51, 25)
0x10 AptosCoin.move Analyze
sources/AptosCoin.move
It's a bit difficult. I'll stop for now.
0x11 Example - Star Card
Ballerina Card Info - Features:
- establish
- drop
- ferret out
- Set price (if you want to sell)
- Transfers (in conjunction with Coins)
address 0x1 {
module football {
use Std::Signer;
// error code
const STAR_ALREADY_EXISTS: u64 = 100; const STAR_NOT_EXISTS: u64 = 101; // error code.
struct FootBallStar has key {
country: vector, position: u8, footBallStar_NOT_EXISTS.
value: u64; struct FootBallStar has key { name: vector, country: vector, position: u8, value: u64
country: vector, position: u8, value: u64
}
public fun newStar(
name: vector, country: vector, position: u8
country: vector, position: u8
): FootBallStar {
FootBallStar {
name, country, position.
value: 0
}
}
public fun mint(recipient: &signer, card: FootBallStar) {
let addr = Signer::address_of(recipient);
assert!(!exists(addr), STAR_ALREADY_EXISTS);
move_to(recipient, card);
}
// Query, dont need signer, address is ok.
public fun get(owner: address, ): (vector, u64) acquires FootBallStar {
// Query dont need assert
let card = borrow_global(owner); (card.name, card.value, u64, u64, u64, u64, u64, u64, u64)
(card.name, card.value) // return a tuple
}
// modify need to get the real data, so acquires ...
public fun setPrice(owner: address, price: u64) acquires FootBallStar {
assert!(exists(owner), STAR_NOT_EXISTS);
let card = borrow_global_mut(owner);
card.value = price; }
}
// in every transaction, price will go rise $20
public fun transfer(owner: address, recipient: &signer) acquires FootBallStar {
assert!(exists(owner), STAR_NOT_EXISTS); // is owner hold it ?
let card = move_from(owner);
let card = move_from(owner); card.value = card.value + 20; // is owner hold it ?
let reci_address = Signer::address_of(recipient);
assert!(!exists(owner), STAR_ALREADY_EXISTS); // is recipient hold it ?
move_to(recipient, card);
}
}
}
All of the above content is reproduced from the Internet, does not represent the position of AptosNews, is not investment advice, investment risk, the market need to be cautious, such as infringement, please contact the administrator to delete.