Skip to main content

Challenge 3: MintCoin Mechanics

In this challenge, you'll dive into the mechanics of "MintCoin," a coin that allows anyone to mint new tokens using a "Proof of Move" process. However, minting alone will not be enough to get the flag—you'll need to go a step further.

Your goal is to understand how the system works and figure out the extra steps needed to successfully retrieve the flag.

Deployed Contract Addresses:

CoinMetadata<0xc6f00a2b5ec2d161442b305dcb307ba914e20c5268ec931bd14d7ea3454b262b::mintcoin::MINTCOIN>: 0x90ab9599899828a27412b8f77644a5eec5da4b0632c3e53afab236eb7c211c54
Counter: 0xc3716689fa16bd8d8bf33ce1036b00740c8818ab9826dba846ef736501fd34b7
TreasuryCap<0xc6f00a2b5ec2d161442b305dcb307ba914e20c5268ec931bd14d7ea3454b262b::mintcoin::MINTCOIN>: 0x11d7aacb27eb65063dbb6ce0fa07f7807316c5e77763c6f2356d1bd3a34a2741
Package: 0xc6f00a2b5ec2d161442b305dcb307ba914e20c5268ec931bd14d7ea3454b262b

Contracts

mint.move

module ctf::mintcoin {
use iota::coin::{Self, Coin, TreasuryCap};
use ctf::counter::{Self, Counter};


public struct MINTCOIN has drop {}
public struct Flag has key, store {
id: UID,
user: address
}

#[allow(lint(share_owned))]
fun init(witness: MINTCOIN, ctx: &mut TxContext) {
counter::create_counter(ctx);
let (coincap, coindata) = coin::create_currency(witness, 0, b"MintCoin", b"Mint Coin", b"A coin that anyone can mint, mind blowing!", option::none(), ctx);
transfer::public_freeze_object(coindata);
transfer::public_share_object(coincap);
}

public entry fun mint_coin(cap: &mut TreasuryCap<MINTCOIN>, ctx: &mut TxContext) {
coin::mint_and_transfer<MINTCOIN>(cap, 2, tx_context::sender(ctx), ctx);
}

public entry fun burn_coin(cap: &mut TreasuryCap<MINTCOIN>, coins: Coin<MINTCOIN>) {
coin::burn(cap, coins);
}

public entry fun get_flag(user_counter: &mut Counter, coins: &mut Coin<MINTCOIN>, ctx: &mut TxContext) {
counter::increment(user_counter);
counter::is_within_limit(user_counter);

let limit = coin::value(coins);
assert!(limit == 5, 1);
transfer::public_transfer(Flag {
id: object::new(ctx),
user: tx_context::sender(ctx)
}, tx_context::sender(ctx));
}
}

counter.move

module ctf::counter {

const MaxCounter: u64 = 1000;
const ENoAttemptLeft: u64 = 0;

/// A shared counter.
public struct Counter has key {
id: UID,
owner: address,
value: u64
}

/// Create and share a Counter object.
public(package) fun create_counter(ctx: &mut TxContext) {
transfer::share_object(Counter {
id: object::new(ctx),
owner: tx_context::sender(ctx),
value: 0
})
}

public fun owner(counter: &Counter): address {
counter.owner
}

public fun value(counter: &Counter): u64 {
counter.value
}

/// Increment a counter by 1.
public fun increment(counter: &mut Counter) {
counter.value = counter.value + 1;
}

/// Set value (only runnable by the Counter owner)
public entry fun set_value(counter: &mut Counter, value: u64, ctx: &TxContext) {
assert!(counter.owner == tx_context::sender(ctx), 0);
counter.value = value;
}

/// Check whether the counter has reached the limit.
public fun is_within_limit(counter: &mut Counter) {
assert!(counter.value <= MaxCounter, ENoAttemptLeft);
}
}

This challenge's main contract is written using the Coin Standard. Having been familiar with the Object Model from the last challenge, you should now be able to understand the Coin Standard and how it works.

tip

Your starting point should be the function get_flag in the mint module to understand the steps required to capture the flag. To successfully complete the challenge, make sure to follow the contract's logic and requirements.

Good luck in capturing your third flag!