Aptos Mystery Box: Building a Random Reward Smart Contract with Move Lang
Learn How to Leverage Aptos Randomness and Object Model to Create Engaging DeFi Experiences
Hey there, MOVERS! 👋 Today, we're diving into something super cool - a mystery box contract on Aptos that lets you airdrop a random number of APT tokens to your frens. Exciting, right? ✨
You've probably seen design patterns like "scratch & win" on payment apps like GPay, Amazon Pay, and CRED. Well, guess what? We can build something similar on Aptos, and it's easier than you might think!
Setting the Stage: Imports and Structs
Before we jump into the fun stuff, let's take a quick look at how we set things up:
use std::string;
use std::signer;
use aptos_framework::account;
use aptos_framework::randomness;
use aptos_framework::coin::{Self, Coin};
use aptos_framework::aptos_coin::{Self, AptosCoin};
use aptos_framework::object::{Self, Object, ExtendRef, DeleteRef};
use aptos_framework::aptos_account;
These 'use' statements are like inviting friends to our party. We're bringing in some standard libraries and Aptos-specific modules to help us out. We've got `randomness` for our mystery factor, `coin` and `aptos_coin` for handling our APT tokens, and `object` for some cool Aptos-specific features we'll talk about in a bit.
Now, let's look at our main star - the MysteryBox struct:
#[resource_group_member(group = aptos_framework::object::ObjectGroup)]
struct MysteryBox has key {
coins: Coin<AptosCoin>,
extend_ref: ExtendRef,
delete_ref: DeleteRef
}
This struct is where the magic happens. It's part of the `ObjectGroup` (more on that later), and it holds our APT coins and some special references we'll use to manage our mystery box.
We've also got some error codes:
const ENOT_OWNER: u64 = 2001;
const ENOT_ENOUGH_COINS: u64 = 2002;
These are like our bouncers - they help us handle situations where someone's trying to open a box they don't own or if the creator doesn't have enough coins to create a box. Safety first, right? 🛡️
Objects in Aptos Move: The Cool Kids on the Block
Now, let's talk about something that makes Aptos special - its object model. In Aptos Move, objects are like digital containers that can hold resources (like our MysteryBox) and have their own address. They're super flexible and can be passed around or shared easily.
Two key players in the object game are `ExtendRef` and `DeleteRef`:
- `ExtendRef` is like a VIP pass. It lets you add more stuff to your object later if you want.
- `DeleteRef` is the self-destruct button. When you're done with your object, you use this to clean it up.
These refs give us fine-grained control over our mystery boxes. We can update them or delete them when they're opened. Pretty nifty, huh? 🎛️
Randomness in Aptos: Shake It Up!
Before we create our mystery box, let's talk about how we make it, well, mysterious! Aptos has a built-in `randomness` module that's like a cosmic dice roller. It uses some complex math magic to give us unpredictable numbers that no one can guess ahead of time.
In our contract, we use it like this:
let random_coin_value = randomness::u64_range(0, 100);
This line is asking our cosmic dice roller to give us a random number between 0 and 100. That's how many APT tokens will be in each mystery box. Exciting, right? 🎲✨
The Magic Behind the Mystery Box
Now that we've got our tools ready, let's break down how this mystery box actually works. It's all about two main functions:
1. Creating and airdropping tokens
2. Opening the box
Creating and Airdropping Tokens
Check out this piece of code:
#[randomness]
public(friend) entry fun create_and_airdrop(caller: &signer, receiver: address){
let caller_addr = signer::address_of(caller);
let constructor_ref = object::create_object(receiver);
let object_signer = object::generate_signer(&constructor_ref);
let extend_ref = object::generate_extend_ref(&constructor_ref);
let delete_ref = object::generate_delete_ref(&constructor_ref);
let random_coin_value = randomness::u64_range(0, 100);
assert!(coin::balance<AptosCoin>(caller_addr) >= random_coin_value, ENOT_ENOUGH_COINS);
let coins = coin::withdraw(caller, random_coin_value);
move_to(&object_signer, MysteryBox{
coins,
extend_ref,
delete_ref
});
}
1. We create a new object owned by the receiver. This is our mystery box container.
2. We generate those special refs we talked about earlier - the VIP pass and self-destruct button for our box.
3. We use our cosmic dice roller to decide how many coins go in the box.
4. We check if the caller has enough coins and withdraw them.
5. Finally, we pack everything into our MysteryBox and put it in its container.
It's like wrapping a gift, but the gift wraps itself and decides its own value. How cool is that? 🔮
Opening the Box
Now, let's look at how we open this mystery box:
public entry fun open_box(receiver: &signer, obj_addr: address) acquires MysteryBox {
let receiver_addr = signer::address_of(receiver);
let mystery_box = object::address_to_object<MysteryBox>(obj_addr);
assert!(object::is_owner<MysteryBox>(mystery_box, receiver_addr), ENOT_OWNER);
let MysteryBox {coins, extend_ref: _, delete_ref} = move_from<MysteryBox>(obj_addr);
aptos_account::deposit_coins(receiver_addr, coins);
object::delete(delete_ref);
}
This function is called by the receiver and allows them to:
1. Check if they're the rightful owner of the box. No sneaky peeking allowed!
2. Open the box and take out the coins.
3. Deposit the coins into their account. Cha-ching! 💰
4. Use that self-destruct button (delete_ref) to clean up the empty box.
Simple and effective, right? 💥
Making It Even Better
Now, I know what you're thinking - "This is great, but can we make it even cooler?" Of course we can! Here are some ideas to level up this contract:
1. Timelock: Add a timelock for the mystery box which expires if not opened. The funds could be released back to the deployer after expiration.
2. More Rewards: Why stop at APT tokens? We could add support for NFTs or other types of rewards. Sky's the limit! 🌈
3. Customizable Campaigns: Instead of hardcoding reward values (like our 0-100 range), we could make this a highly customizable service. Imagine enterprises setting up campaigns for their users! 🚀
A Special Shoutout
Before I wrap up, I've gotta give a v v special thnx to @Greg_Nazario, killer Aptos founding dev and a dear fren. His daily move repo has been a great inspo for writing this contract. You should totally check it out!
Wrapping Up
So there you have it, folks! A sample mystery box contract that shows off Aptos' native & instant randomness, cool object model, and Move language features. Pretty neat, huh?
If you want to dive deeper, you can find the complete code for this mystery box contract on my GitHub
And hey, if you need help with the frontend bit, hit me up - ice cream's on me! 🍦
Keep coding, keep creating, and who knows what awesome dApps you'll come up with next? The world of blockchain is your oyster! 🌊🦪
Happy hacking, everyone! 👩💻👨💻