module chapter_5::vote {
use sui::coin::{Self, Coin, TreasuryCap};
use sui::transfer::{public_transfer, share_object, public_freeze_object};
use sui::vec_map::{Self, VecMap};
use sui::object_table::{Self, ObjectTable};
use std::string::String;
use sui::balance::Balance;
use sui::balance::zero;
use sui::coin::into_balance;
use sui::coin::from_balance;
public struct VOTE has drop {}
public struct Votecap has key {
id: UID,
cap: TreasuryCap<VOTE>,
}
public struct Mintlist has key {
id: UID,
mintlist: VecMap<address, u64>,
}
public struct VoteStore has key {
id: UID,
proposals: ObjectTable<String, Proposal>,
voters: VecMap<address, u64>,
}
public struct Proposal has key, store {
id: UID,
owner: address,
votes: u64,
locked_tokens: Balance<VOTE>,
closed: bool,
}
const E_INVALID_AMOUNT: u64 = 1;
const E_INVALID_PROPOSAL: u64 = 2;
const E_ALREADY_MINTED: u64 = 3;
const E_ALREADY_VOTE: u64 = 4;
const E_PROPOSAL_CLOSED: u64 = 5;
fun init(witness: VOTE, ctx: &mut TxContext) {
let (treasury_cap, meta) = coin::create_currency(
witness, 6, b"VOTE", b"VOTE", b"", option::none(), ctx
);
let vote_cap = Votecap { id: object::new(ctx), cap: treasury_cap };
let mintlist = Mintlist { id: object::new(ctx), mintlist: vec_map::empty() };
let store = VoteStore {
id: object::new(ctx),
proposals: object_table::new(ctx),
voters: vec_map::empty(),
};
public_freeze_object(meta);
share_object(vote_cap);
share_object(mintlist);
share_object(store);
}
public entry fun mint(
vote_cap: &mut Votecap,
mint_list: &mut Mintlist,
ctx: &mut TxContext
) {
let addr = tx_context::sender(ctx);
assert!(!vec_map::contains(&mint_list.mintlist, &addr), E_ALREADY_MINTED);
let coin = coin::mint(&mut vote_cap.cap, 100, ctx);
vec_map::insert(&mut mint_list.mintlist, addr, 100);
public_transfer(coin, addr);
}
public entry fun create_proposal(
store: &mut VoteStore,
name: String,
ctx: &mut TxContext
) {
assert!(!object_table::contains(&store.proposals, name), E_INVALID_PROPOSAL);
let proposal = Proposal {
id: object::new(ctx),
owner: tx_context::sender(ctx),
votes: 0,
locked_tokens: zero<VOTE>(),
closed: false,
};
object_table::add(&mut store.proposals, name, proposal);
}
public entry fun vote(
store: &mut VoteStore,
vote_coin: Coin<VOTE>,
proposal_name: String,
ctx: &mut TxContext
) {
assert!(vote_coin.value() > 0, E_INVALID_AMOUNT);
assert!(object_table::contains(&store.proposals, proposal_name), E_INVALID_PROPOSAL);
let proposal = object_table::borrow_mut(&mut store.proposals, proposal_name);
assert!(!proposal.closed, E_PROPOSAL_CLOSED);
let sender = tx_context::sender(ctx);
assert!(!vec_map::contains(&store.voters, &sender),E_ALREADY_VOTE);
vec_map::insert(&mut store.voters, sender, vote_coin.value());
let amount = into_balance(vote_coin);
proposal.votes = proposal.votes + amount.value();
proposal.locked_tokens.join(amount);
}
public entry fun close_proposal(
store: &mut VoteStore,
proposal_name: String,
ctx: &mut TxContext
) {
assert!(object_table::contains(&store.proposals, proposal_name), E_INVALID_PROPOSAL);
let proposal = object_table::borrow_mut(&mut store.proposals, proposal_name);
assert!(!proposal.closed, E_PROPOSAL_CLOSED);
proposal.closed = true;
let coin = from_balance(proposal.locked_tokens.withdraw_all(), ctx);
public_transfer(coin, tx_context::sender(ctx));
}
}