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<0x871d4332aeb888fafa0c90280a3b2d301213e3352c11493b2ac59002264c2d20::mintcoin::MINTCOIN>: 0xb24911470fcef420eb6774d8624f8ad8b329a0e3c1157fe3d5c25dfd3e812f10
Counter: 0x843737fabb25a83b9034764cbc06d2e0444397be4157d08fca0306004065c3db
TreasuryCap<0x871d4332aeb888fafa0c90280a3b2d301213e3352c11493b2ac59002264c2d20::mintcoin::MINTCOIN>: 0x9000629971a67ee3df540a54da1c8af5a67629e2aa479a515b5942d116288aa4
Package: 0x871d4332aeb888fafa0c90280a3b2d301213e3352c11493b2ac59002264c2d20

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 = 10;
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!