Skip to content

Commit

Permalink
[compiler-v2]Move example (#4378)
Browse files Browse the repository at this point in the history
* move example for proxima

fix move examples defi resource_groups

update shared_account defi examples

* update address
  • Loading branch information
nkysg authored Jan 24, 2025
1 parent 530102e commit c59ff57
Show file tree
Hide file tree
Showing 11 changed files with 873 additions and 0 deletions.
9 changes: 9 additions & 0 deletions vm/move-examples/defi/Move.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "defi"
version = "0.0.1"

[addresses]
defi = "0xed9ea1f3533c14e1b52d9ff6475776ba"

[dependencies]
StarcoinFramework = { local = "../../framework/starcoin-framework" }
489 changes: 489 additions & 0 deletions vm/move-examples/defi/sources/locked_coins.move

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions vm/move-examples/hello_blockchain/Move.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "hello-blockchain"
version = "0.0.1"

[addresses]
hello_blockchain = "0xed9ea1f3533c14e1b52d9ff6475776ba"

[dependencies]
StarcoinFramework = { local = "../../framework/starcoin-framework" }
59 changes: 59 additions & 0 deletions vm/move-examples/hello_blockchain/sources/hello_blockchain.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
module hello_blockchain::message {
use std::error;
use std::signer;
use std::string;
use starcoin_framework::event;

//:!:>resource
struct MessageHolder has key {
message: string::String,
}
//<:!:resource

#[event]
struct MessageChange has drop, store {
account: address,
from_message: string::String,
to_message: string::String,
}

/// There is no message present
const ENO_MESSAGE: u64 = 0;

#[view]
public fun get_message(addr: address): string::String acquires MessageHolder {
assert!(exists<MessageHolder>(addr), error::not_found(ENO_MESSAGE));
borrow_global<MessageHolder>(addr).message
}

public entry fun set_message(account: signer, message: string::String)
acquires MessageHolder {
let account_addr = signer::address_of(&account);
if (!exists<MessageHolder>(account_addr)) {
move_to(&account, MessageHolder {
message,
})
} else {
let old_message_holder = borrow_global_mut<MessageHolder>(account_addr);
let from_message = old_message_holder.message;
event::emit(MessageChange {
account: account_addr,
from_message,
to_message: copy message,
});
old_message_holder.message = message;
}
}

#[test(account = @0x1)]
public entry fun sender_can_set_message(account: signer) acquires MessageHolder {
let addr = signer::address_of(&account);
starcoin_framework::account::create_account_for_test(addr);
set_message(account, string::utf8(b"Hello, Blockchain"));

assert!(
get_message(addr) == string::utf8(b"Hello, Blockchain"),
ENO_MESSAGE
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#[test_only]
module hello_blockchain::message_tests {
use std::signer;
use std::unit_test;
use std::vector;
use std::string;

use hello_blockchain::message;

fun get_account(): signer {
vector::pop_back(&mut unit_test::create_signers_for_testing(1))
}

#[test]
public entry fun sender_can_set_message() {
let account = get_account();
let addr = signer::address_of(&account);
starcoin_framework::account::create_account_for_test(addr);
message::set_message(account, string::utf8(b"Hello, Blockchain"));

assert!(
message::get_message(addr) == string::utf8(b"Hello, Blockchain"),
0
);
}
}
9 changes: 9 additions & 0 deletions vm/move-examples/resource_groups/primary/Move.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "ResourceGroupsPrimary"
version = "0.0.1"

[addresses]
resource_groups_primary = "0xed9ea1f3533c14e1b52d9ff6475776ba"

[dependencies]
StarcoinFramework = { local = "../../../framework/starcoin-framework" }
58 changes: 58 additions & 0 deletions vm/move-examples/resource_groups/primary/sources/primary.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/// This demonstrates how to use a resource group within a single module
/// See resource_groups_primary::secondary for cross module and multiple resources
module resource_groups_primary::primary {
use std::signer;

#[resource_group(scope = global)]
struct ResourceGroupContainer { }

#[resource_group_member(group = resource_groups_primary::primary::ResourceGroupContainer)]
struct Primary has drop, key {
value: u64,
}

public entry fun init(account: &signer, value: u64) {
move_to(account, Primary { value });
}

public entry fun set_value(account: &signer, value: u64) acquires Primary {
let primary = borrow_global_mut<Primary>(signer::address_of(account));
primary.value = value;
}

public fun read(account: address): u64 acquires Primary {
borrow_global<Primary>(account).value
}

public entry fun remove(account: &signer) acquires Primary {
move_from<Primary>(signer::address_of(account));
}

public fun exists_at(account: address): bool {
exists<Primary>(account)
}

fun init_module(owner: &signer) {
move_to(owner, Primary { value: 3 });
}

#[test(account = @0x3)]
fun test_multiple(account: &signer) acquires Primary {
// Do it once to verify normal flow
test_primary(account);

// Do it again to verify it can be recreated
test_primary(account);
}

public fun test_primary(account: &signer) acquires Primary {
let addr = signer::address_of(account);
assert!(!exists_at(addr), 0);
init(account, 5);
assert!(read(addr) == 5, 1);
set_value(account, 12);
assert!(read(addr) == 12, 1);
remove(account);
assert!(!exists_at(addr), 0);
}
}
11 changes: 11 additions & 0 deletions vm/move-examples/resource_groups/secondary/Move.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "ResourceGroupsSecondary"
version = "0.0.1"

[addresses]
resource_groups_primary = "0xed9ea1f3533c14e1b52d9ff6475776ba"
resource_groups_secondary = "0xed9ea1f3533c14e1b52d9ff6475776ba"

[dependencies]
StarcoinFramework = { local = "../../../framework/starcoin-framework" }
ResourceGroupsPrimary = { local = "../primary" }
58 changes: 58 additions & 0 deletions vm/move-examples/resource_groups/secondary/sources/secondary.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/// This demonstrates how to use a resource group accross modules
module resource_groups_secondary::secondary {
use std::signer;
use resource_groups_primary::primary;

#[resource_group_member(group = resource_groups_primary::primary::ResourceGroupContainer)]
struct Secondary has drop, key {
value: u32,
}

public entry fun init(account: &signer, value: u32) {
move_to(account, Secondary { value });
}

public entry fun set_value(account: &signer, value: u32) acquires Secondary {
let primary = borrow_global_mut<Secondary>(signer::address_of(account));
primary.value = value;
}

public fun read(account: address): u32 acquires Secondary {
borrow_global<Secondary>(account).value
}

public entry fun remove(account: &signer) acquires Secondary {
move_from<Secondary>(signer::address_of(account));
}

public fun exists_at(account: address): bool {
exists<Secondary>(account)
}

// This boiler plate function exists just so that primary is loaded with secondary
// We'll need to explore how to load resource_group_containers without necessarily
// having loaded their module via the traditional module graph.
public fun primary_exists(account: address): bool {
primary::exists_at(account)
}

#[test(account = @0x3)]
fun test_multiple(account: &signer) acquires Secondary {
let addr = signer::address_of(account);
assert!(!exists_at(addr), 0);
init(account, 7);
assert!(read(addr) == 7, 1);
set_value(account, 13);
assert!(read(addr) == 13, 1);

// Verify that primary can be added and removed without affecting secondary
primary::test_primary(account);
assert!(read(addr) == 13, 1);

remove(account);
assert!(!exists_at(addr), 0);

// Verify that primary can be re-added after secondary has been removed
primary::test_primary(account);
}
}
9 changes: 9 additions & 0 deletions vm/move-examples/shared_account/Move.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "shared_account"
version = "0.0.1"

[addresses]
shared_account = "0xed9ea1f3533c14e1b52d9ff6475776ba"

[dependencies]
StarcoinFramework = { local = "../../framework/starcoin-framework" }
136 changes: 136 additions & 0 deletions vm/move-examples/shared_account/sources/shared_account.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// This module demonstrates a basic shared account that could be used for NFT royalties
// Users can (1) create a shared account (2) disperse the coins to multiple creators
module shared_account::SharedAccount {
use std::error;
use std::signer;
use std::vector;
use starcoin_framework::account;
use starcoin_framework::coin;

// struct Share records the address of the share_holder and their corresponding number of shares
struct Share has store {
share_holder: address,
num_shares: u64,
}

// Resource representing a shared account
struct SharedAccount has key {
share_record: vector<Share>,
total_shares: u64,
signer_capability: account::SignerCapability,
}

struct SharedAccountEvent has key {
resource_addr: address,
}

const EACCOUNT_NOT_FOUND: u64 = 0;
const ERESOURCE_DNE: u64 = 1;
const EINSUFFICIENT_BALANCE: u64 = 2;

// Create and initialize a shared account
public entry fun initialize(source: &signer, seed: vector<u8>, addresses: vector<address>, numerators: vector<u64>) {
let total = 0;
let share_record = vector::empty<Share>();

vector::enumerate_ref(&addresses, |i, addr|{
let addr = *addr;
let num_shares = *vector::borrow(&numerators, i);

// make sure that the account exists, so when we call disperse() it wouldn't fail
// because one of the accounts does not exist
assert!(account::exists_at(addr), error::invalid_argument(EACCOUNT_NOT_FOUND));

vector::push_back(&mut share_record, Share { share_holder: addr, num_shares });
total = total + num_shares;
});

let (resource_signer, resource_signer_cap) = account::create_resource_account(source, seed);

move_to(
&resource_signer,
SharedAccount {
share_record,
total_shares: total,
signer_capability: resource_signer_cap,
}
);

move_to(source, SharedAccountEvent {
resource_addr: signer::address_of(&resource_signer)
});
}

// Disperse all available balance to addresses in the shared account
public entry fun disperse<CoinType>(resource_addr: address) acquires SharedAccount {
assert!(exists<SharedAccount>(resource_addr), error::invalid_argument(ERESOURCE_DNE));

let total_balance = coin::balance<CoinType>(resource_addr);
assert!(total_balance > 0, error::out_of_range(EINSUFFICIENT_BALANCE));

let shared_account = borrow_global<SharedAccount>(resource_addr);
let resource_signer = account::create_signer_with_capability(&shared_account.signer_capability);

vector::for_each_ref(&shared_account.share_record, |shared_record|{
let shared_record: &Share = shared_record;
let current_amount = shared_record.num_shares * total_balance / shared_account.total_shares;
coin::transfer<CoinType>(&resource_signer, shared_record.share_holder, current_amount);
});
}

#[test_only]
public fun set_up(user: signer, test_user1: signer, test_user2: signer) : address acquires SharedAccountEvent {
let addresses = vector::empty<address>();
let numerators = vector::empty<u64>();
let seed = x"01";
let user_addr = signer::address_of(&user);
let user_addr1 = signer::address_of(&test_user1);
let user_addr2 = signer::address_of(&test_user2);

starcoin_framework::starcoin_account::create_account(user_addr);
starcoin_framework::starcoin_account::create_account(user_addr1);
starcoin_framework::starcoin_account::create_account(user_addr2);

vector::push_back(&mut addresses, user_addr1);
vector::push_back(&mut addresses, user_addr2);

vector::push_back(&mut numerators, 1);
vector::push_back(&mut numerators, 4);

initialize(&user, seed, addresses, numerators);

assert!(exists<SharedAccountEvent>(user_addr), error::not_found(EACCOUNT_NOT_FOUND));
borrow_global<SharedAccountEvent>(user_addr).resource_addr
}

#[test(user = @0x1111, test_user1 = @0x1112, test_user2 = @0x1113, core_framework = @starcoin_framework)]
public entry fun test_disperse(user: signer, test_user1: signer, test_user2: signer, core_framework: signer) acquires SharedAccount, SharedAccountEvent {
use starcoin_framework::starcoin_coin::{Self, STC};
let user_addr1 = signer::address_of(&test_user1);
let user_addr2 = signer::address_of(&test_user2);
let (burn_cap, mint_cap) = starcoin_coin::initialize_for_test(&core_framework);
let resource_addr = set_up(user, test_user1, test_user2);

let shared_account = borrow_global<SharedAccount>(resource_addr);
let resource_signer = account::create_signer_with_capability(&shared_account.signer_capability);
coin::register<STC>(&resource_signer);
coin::deposit(resource_addr, coin::mint(1000, &mint_cap));
disperse<STC>(resource_addr);
coin::destroy_mint_cap<STC>(mint_cap);
coin::destroy_burn_cap<STC>(burn_cap);

assert!(coin::balance<STC>(user_addr1) == 200, 0);
assert!(coin::balance<STC>(user_addr2) == 800, 1);
}

#[test(user = @0x1111, test_user1 = @0x1112, test_user2 = @0x1113)]
#[expected_failure]
public entry fun test_disperse_insufficient_balance(user: signer, test_user1: signer, test_user2: signer) acquires SharedAccount, SharedAccountEvent {
use starcoin_framework::starcoin_coin::STC;
let resource_addr = set_up(user, test_user1, test_user2);
let shared_account = borrow_global<SharedAccount>(resource_addr);
let resource_signer = account::create_signer_with_capability(&shared_account.signer_capability);
coin::register<STC>(&resource_signer);
disperse<STC>(resource_addr);
}
}

0 comments on commit c59ff57

Please sign in to comment.