Debugging Basic Smart Contracts

This section introduces a few basic smart contracts from this repository as a starting point for programming activites.

💻 hello_world

This very simple program for Movement Move VM. You can find it at examples/movement/hello_world prints values to debug console. Note the differences in encodings between the b"" and string::utf8 versions of the string.

Learnings leveraged:

  • Basic Move syntax
  • Byte strings
#![allow(unused)]
fn main() {
module hello_world::hello_world {
    
    use std::string;
    use std::signer;
    use aptos_std::debug;

   
    #[test(account = @0x1)]
    public entry fun hello_world(account: signer) {
        let addr = signer::address_of(&account);
        debug::print<address>(&addr);
        let message = b"Hello, world!";
        debug::print(&message);
        let str_message = string::utf8(message);
        debug::print(&str_message);
    }

}
}

To test run...

movement move test
> [debug] @0x1
> [debug] 0x48656c6c6f2c20776f726c6421
> [debug] "Hello, world!"

If you want to double-check the output hex...

echo 48656c6c6f2c20776f726c6421 | xxd -r - p

💻 fib

The obligatory Move program that computes the nth Fibonacci number. We will refer to this later when we do 💻 MulticontractFib. You can find it and instructions to run it examples/movement/fib.

Learnings leveraged:

  • Basic Move syntax
  • Move recursion.

💻 data_structures

From scratch implementation of a priority queue, a couple variations of a hash map, and a binary tree. This may be a useful reference point for building more challenging projects that require custom data strucures. You can find it at examples/movement/data_structures.

Learnings leveraged:

  • Basic Move syntax
  • Vectors
  • BCS
  • Move idioms

💻 resource_roulette

A game of roulette on MoveVM. Place your address on an element in the vector. Contains methods public fun bid and public fun spin. Receive a payout if you placed your address on the correct cell. You can find it and instructions to run it at examples/movement/resource_roulette.

Learnings leveraged:

  • Basic Move syntax
  • Signer and address types
  • Borrows
  • Initialization
  • Move idioms
#![allow(unused)]
fn main() {
module resource_roulette::resource_roulette {
  use std::vector;
  use std::signer;

  const ENO_UNAUTHORIZED_ADDRESS : u64 = 0;

  // ResourceRoulette struct representing the contract state
  struct ResourceRoulette has key {
    bids: vector<vector<address>>,
    owner: address,
    state : u64
  }

  struct RouletteWinnings has key {
    amount : u64
  }

  // Initialization function for the ResourceRoulette contract
  public fun init(account: &signer) {

    assert!(signer::address_of(account) == @resource_roulette, ENO_UNAUTHORIZED_ADDRESS);

    let bids = vector::empty<vector<address>>();
    let i = 0;
    while (i < 32) {
      vector::push_back(&mut bids, vector::empty<address>());
      i = i + 1;
    };

    move_to(account, ResourceRoulette {
      bids,
      owner: @resource_roulette,
      state : 17203943403948
    });

  }

  // Initializes winnings for a signer
  public fun init_winnings(account: &signer) {
    move_to(account, RouletteWinnings {
      amount: 0,
    });
  }

  // Bid function to allow signers to bid on a specific slot
  public fun bid(account : &signer, slot: u8) acquires ResourceRoulette {

    if (!exists<RouletteWinnings>(signer::address_of(account))) {
      init_winnings(account);
    };

    let self = borrow_global_mut<ResourceRoulette>(@resource_roulette);
    roll_state(self);
    let bids_size = vector::length(&self.bids);
    assert!(slot < (bids_size as u8), 99);
    let slot_bids = vector::borrow_mut(&mut self.bids, (slot as u64));
    vector::push_back(slot_bids, signer::address_of(account));

  }

  public fun total_bid() : u64 {
    // Make this more complex to support actual bidding
    return 100
  }

  // rolls state using xoroshiro prng
  fun roll_state(self :&mut ResourceRoulette) {
    let state = (self.state as u256);
    let x = state;
    let y = state >> 64;

    let t = x ^ y;
    state = ((x << 55) | (x >> 9)) + y + t;

    y = y ^ x;
    state = state + ((y << 14) | (y >> 50)) + x + t;
    
    state = state + t;
    state = state % (2^128 - 1);
    self.state = (state as u64);

  }

  public fun get_noise() : u64 {
    1
  }

  fun empty_bids(self : &mut ResourceRoulette){

    // empty the slots
    let bids = vector::empty<vector<address>>();
    let i = 0;
    while (i < 32) {
      vector::push_back(&mut bids, vector::empty<address>());
      i = i + 1;
    };
    self.bids = bids;

  }

  // Roll function to select a pseudorandom slot and pay out all signers who selected that slot
  public fun spin() acquires ResourceRoulette, RouletteWinnings {

    let self = borrow_global_mut<ResourceRoulette>(@resource_roulette);

    // get the winning slot
    let bids_size = vector::length(&self.bids);
    roll_state(self);
    let winning_slot = (get_noise() * self.state % (bids_size as u64)) ;

    // pay out the winners
    let winners = vector::borrow(&self.bids, winning_slot);
    let num_winners = vector::length(winners);

    if (num_winners > 0){
      let balance_per_winner = total_bid()/( num_winners as u64);
      let i = 0;
      while (i < num_winners) {
        let winner = vector::borrow(winners, i);
        let winnings = borrow_global_mut<RouletteWinnings>(*winner);
        winnings.amount = winnings.amount + balance_per_winner;
        i = i + 1;
      };
    };

    empty_bids(self);

  }

  // tests...

}
}