Skip to content

Commit

Permalink
Merge pull request #37 from JetonDAO/implement-smart-contracts
Browse files Browse the repository at this point in the history
Implement smart contracts
  • Loading branch information
arssly authored Sep 27, 2024
2 parents 2c9468f + bc6d2e2 commit 2cff6ee
Show file tree
Hide file tree
Showing 30 changed files with 8,811 additions and 11,151 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,7 @@ dist
# Misc
.DS_Store
*.pem

# Aptos
.aptos

17,043 changes: 6,289 additions & 10,754 deletions package-lock.json

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions packages/smart-contracts/Move.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "Jeton"
version = "0.0.0"

[addresses]
std = "0x1"
aptos_framework = "0x1"
zk_deck = "0x05a973c774644f1d27beab9308fc05d4a35d49e5108411fff5dad3105190178f"
jeton = "_"

[dev-addresses]

[dependencies.AptosFramework]
git = "https://github.com/aptos-labs/aptos-core.git"
rev = "mainnet"
subdir = "aptos-move/framework/aptos-framework"

[dependencies.ZKDeck]
local = "../zk-deck/contracts"

[dev-dependencies]
17 changes: 17 additions & 0 deletions packages/smart-contracts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "@jeton/smart-contracts",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"move:build": "aptos move compile --move-2 --named-addresses jeton=default",
"move:test": "aptos move test --move-2 --named-addresses jeton=default",
"move:publish": "aptos move create-resource-account-and-publish-package --address-name jeton --seed jeton --move-2 --assume-yes"
},
"devDependencies": {
"@aptos-labs/aptos-cli": "^0.2.0"
},
"dependencies": {
"@jeton/zk-deck": "^0.0.0"
}
}
292 changes: 292 additions & 0 deletions packages/smart-contracts/sources/texas_holdem.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
module jeton::texas_holdem {
use aptos_framework::account;
use aptos_framework::aptos_account;
use aptos_framework::event;
use aptos_framework::object;
use aptos_framework::object::Object;
use aptos_framework::option;
use aptos_framework::option::Option;
use aptos_framework::resource_account;
use std::signer;
use std::vector;
use zk_deck::zk_deck;

const ETABLE_HAS_NO_EMPTY_SEAT: u64 = 1;
const EPLAYER_HAS_SEAT_ON_TABLE: u64 = 2;
const EINVALID_ACTION: u64 = 3;

#[event]
struct TableCreatedEvent has drop, store {
table_object: Object<Table>,
}

struct Table has key {
info: TableInfo,
seats: vector<Option<Player>>,
dealer_index: u8,
phase: Phase,
time_out: u64,
aggregated_public_key: vector<u8>,
deck: vector<u8>,
private_cards_share: vector<vector<u8>>,
public_cards_share: vector<vector<u8>>,
}

struct TableInfo has drop, store {
action_timeout: u64,
min_buy_in_amount: u64,
max_buy_in_amount: u64,
small_blind: u64,
num_raises: u8,
start_at_player: u8,
}

enum Phase has drop, store {
AwaitingStart,
Shuffle {
seat_index: u8,
},
DrawPrivateCards {
contributors_index: vector<u8>,
},
BetPreFlop {
seat_index: u8,
last_raise_index: u8,
num_raises: u8,
},
DrawFlopCards {
contributors_index: vector<u8>,
},
BetFlop {
seat_index: u8,
last_raise_index: u8,
num_raises: u8,
},
DrawTurnCard {
contributors_index: vector<u8>,
},
BetTurn {
seat_index: u8,
last_raise_index: u8,
num_raises: u8,
},
DrawRiverCard {
contributors_index: vector<u8>,
},
BetRiver {
seat_index: u8,
last_raise_index: u8,
num_raises: u8,
},
ShowDown {
contributors_index: vector<u8>,
}
}

struct Player has drop, store {
addr: address,
public_key: vector<u8>,
balance: u64,
bet: u64,
is_folded: bool,
is_playing: bool,
is_last_hand: bool,
}

public entry fun create_table(
sender: signer,
action_timeout: u64,
min_buy_in_amount: u64,
max_buy_in_amount: u64,
small_blind: u64,
num_raises: u8,
start_at_player: u8,
num_seats: u8,
public_key: vector<u8>,
buy_in_amount: u64,
) acquires Table, SignerCapability {
let table_constructor_ref = object::create_object(@jeton);
let table_signer = object::generate_signer(&table_constructor_ref);

let info = TableInfo {
action_timeout,
min_buy_in_amount,
max_buy_in_amount,
small_blind,
num_raises,
start_at_player,
};

let seats = vector::empty();
for (i in 0..num_seats) {
vector::push_back(&mut seats, option::none<Player>());
};

let table = Table {
info,
seats,
dealer_index: 0,
phase: Phase::AwaitingStart,
time_out: 0,
aggregated_public_key: vector::empty(),
deck: vector::empty(),
private_cards_share: vector::empty(),
public_cards_share: vector::empty(),
};

move_to(&table_signer, table);
let table_object = object::object_from_constructor_ref(&table_constructor_ref);
event::emit(TableCreatedEvent {
table_object,
});
check_in(sender, table_object, public_key, buy_in_amount);
}

public entry fun check_in(
sender: signer,
table_object: Object<Table>,
public_key: vector<u8>,
buy_in_amount: u64,
) acquires Table, SignerCapability {
let table_addr = object::object_address<Table>(&table_object);
let table = borrow_global_mut<Table>(table_addr);

zk_deck::check_public_key(&public_key);

let player_addr = signer::address_of(&sender);
for (i in 0..vector::length(&table.seats)) {
let seat = vector::borrow(&table.seats, i);
if (option::is_none(seat)) {
continue
};
let player = option::borrow(seat);
if (player.addr == player_addr) {
abort EPLAYER_HAS_SEAT_ON_TABLE
};
};

let (has_empty_seat, empty_seat_index) =
vector::find(&table.seats, |seat| option::is_none(seat));
assert!(has_empty_seat, ETABLE_HAS_NO_EMPTY_SEAT);
let player = Player {
addr: player_addr,
public_key: public_key,
balance: buy_in_amount,
bet: 0,
is_folded: false,
is_playing: false,
is_last_hand: false,
};
let empty_set = vector::borrow_mut(&mut table.seats, empty_seat_index);
option::fill(empty_set, player);

let stake_amount = get_stake_amount(table);
let total_amount = buy_in_amount + stake_amount;
aptos_account::transfer(
&sender,
table_addr,
total_amount,
);

if (
(table.phase is Phase::AwaitingStart) &&
get_num_players(table) >= table.info.start_at_player
) {
start_game(table);
}
}

public entry fun check_out(
sender: signer,
table_object: Object<Table>,
) acquires Table, SignerCapability {
let player_addr = signer::address_of(&sender);
let table_addr = object::object_address<Table>(&table_object);
let table = borrow_global_mut<Table>(table_addr);
let num_seats = vector::length(&table.seats);
for (i in 0..num_seats) {
let seat = vector::borrow_mut(&mut table.seats, i);
if (option::is_none(seat)) {
continue;
};
let player = option::borrow_mut(seat);
if (player.addr != player_addr) {
continue;
};
if (table.phase is Phase::AwaitingStart) {
check_out_player(table, i);
} else {
player.is_last_hand = true;
};
return;
};
abort EINVALID_ACTION
}

fun start_game(table: &mut Table) acquires SignerCapability {
let num_seats = vector::length(&table.seats);
let public_keys = vector::empty<vector<u8>>();
for (i in 0..num_seats) {
let seat = vector::borrow_mut(&mut table.seats, i);
if (option::is_none(seat)) {
continue;
};
let player = option::borrow_mut(seat);
if (player.is_last_hand) {
check_out_player(table, i);
};
};
}

fun check_out_player(table: &mut Table, seat_index: u64) acquires SignerCapability {
let seat = vector::borrow_mut(&mut table.seats, seat_index);
let player = option::extract(seat);

let stake_amount = get_stake_amount(table);
let total_amount = player.balance + stake_amount;

let jeton_signer = get_jeton_signer();
aptos_account::transfer(
&jeton_signer,
player.addr,
total_amount,
);
}

fun get_stake_amount(table: &Table): u64 {
let num_seats = vector::length(&table.seats);
12 * table.info.small_blind * (table.info.num_raises as u64) * num_seats
}

fun get_num_players(table: &Table): u8 {
let is_empty = vector::map_ref(&table.seats, |seat| option::is_none(seat));
vector::fold(is_empty, 0, |count, e| if (e) { count } else { count + 1 })
}

fun get_next_player_index(table: &Table, seat_index: u8): u8 {
let num_seats = vector::length(&table.seats) as u8;
let i = (seat_index + 1 ) % num_seats;
let seat = vector::borrow(&table.seats, i as u64);
while(option::is_none(seat)) {
i = (i + 1) % num_seats;
seat = vector::borrow(&table.seats, i as u64)
};
return i
}

struct SignerCapability (account::SignerCapability) has key;

fun init_module(resource: &signer) {
let capability = resource_account::retrieve_resource_account_cap(
resource,
@jeton,
);
let capability = SignerCapability(capability);
move_to(resource, capability);
}

fun get_jeton_signer(): signer acquires SignerCapability{
let capability = borrow_global<SignerCapability>(@jeton);
account::create_signer_with_capability(&capability.0)
}
}
13 changes: 13 additions & 0 deletions packages/smart-contracts/turbo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "https://turbo.build/schema.json",
"extends": ["//"],
"tasks": {
"move:build": {
"inputs": ["sources/**", "contracts/Move.toml"],
"outputs": ["build/**"]
},
"move:test": {
"inputs": ["sources/**", "contracts/Move.toml"]
}
}
}
Loading

0 comments on commit 2cff6ee

Please sign in to comment.