Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solidity ttt #1

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
55 changes: 54 additions & 1 deletion evm/src/solidity/ResourceMarket.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;

interface IERC20 {
function transfer(address recipient, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}
/// There are three resources needed to survive: Water, Food, and Wood.
enum Resource {
Water,
Expand Down Expand Up @@ -51,4 +56,52 @@ abstract contract ResourceMarket {
// And all the resources are subject to robbery. But if we are talking about virtual assets,
// there are no such risks. And depositing funds into the market comes with an opportunity cost.
// Design a reward system where there is a small fee on every withdrawal, and that fee is paid to
// liquidity providers.
// liquidity providers.



contract ResourceMarketLogic is ResourceMarket {
mapping(address => mapping(Resource => uint)) public balance;

// ERC20 tokens addresses
address[] public tokens;

// Fee percentage on withdrawals
uint public withdrawalFeePercentage = 1; // 1% fee

event Contribution(address indexed account, uint amount, Resource resource);
event Withdrawal(address indexed account, uint amount, Resource resource);

// Constructor to initialize ERC20 token addresses
constructor(address[] memory _tokens) {
require(_tokens.length == 3, "Invalid number of tokens");
tokens = _tokens;
}

/// Contribute some of your own private resources to the market.
/// Contributions are made one asset at a time.

function contribute(uint amount, Resource resource) public override {
require(amount > 0, "Invalid amount");
address tokenAddress = tokens[uint(resource)];
require(IERC20(tokenAddress).transferFrom(msg.sender, address(this), amount), "Transfer failed");
balance[msg.sender][resource] += amount;
emit Contribution(msg.sender, amount, resource);
}

/// Withdraw some resources from the market into your own private reserves.
function withdraw(uint amount, Resource resource) public override {
require(balance[msg.sender][resource] >= amount, "Insufficient balance");
uint fee = (amount * withdrawalFeePercentage) / 100;
uint netAmount = amount - fee;
require(IERC20(tokens[uint(resource)]).transfer(msg.sender, netAmount), "Transfer failed");
balance[msg.sender][resource] -= amount;
emit Withdrawal(msg.sender, netAmount, resource);
}

/// Set the withdrawal fee percentage
function setWithdrawalFeePercentage(uint _percentage) external {
require(_percentage <= 100, "Invalid fee percentage");
withdrawalFeePercentage = _percentage;
}
}
72 changes: 58 additions & 14 deletions evm/src/solidity/Roulette.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ contract Roulette {
/// The address of the "house" that operates this table.
/// This is initialized to the address that deployed the contract.
address house;
uint256 public constant MIN_BLOCKS_BETWEEN_SPINS = 20; // Minimum number of blocks between spins

uint256 private lastSpinBlockNumber; // Last block number when a spin occurred


constructor() {
house = msg.sender;
Expand All @@ -49,40 +53,80 @@ contract Roulette {
/// accidentally depositing funds that do not back any bet.
/// Because we have this explicit function, we do not include a fallback function.
function fund_table() external payable {
//TODO
require(msg.sender == house, "Only the house can fund the table");
emit Funded(msg.value);
}

/// Place a bet on a specific color
function place_color_bet(Color color) external payable {
//TODO
require(msg.value > 0, "Bet amount must be greater than zero");
color_bets.push(ColorBet(color, msg.value, msg.sender));
emit ColorBetPlaced(ColorBet(color, msg.value, msg.sender));
}

/// Helper function to determine the color of a roulette tile
function color_of(uint n) internal pure returns(Color) {
//TODO
function color_of(uint n) internal pure returns (Color) {
if (n == 0) {
return Color.Green;
} else if (n >= 1 && n <= 10) {
return (n % 2 == 0) ? Color.Black : Color.Red;
} else if (n >= 11 && n <= 18) {
return (n % 2 == 0) ? Color.Red : Color.Black;
} else if (n >= 19 && n <= 28) {
return (n % 2 == 0) ? Color.Black : Color.Red;
} else if (n >= 29 && n <= 36) {
return (n % 2 == 0) ? Color.Red : Color.Black;
} else {
revert("Invalid number");
}
}

/// Spin the wheel to determine the winning number.
/// Also calls settle_bets to kick off settlement of all bets.
function spin() external {

// Get the "random" result and convert it into a roulette table value.
// This is not _quite_ perfectly fair because 2^256 % 38 != 0.
// We could handle that if we want, but we won't yet.
uint r = uint(blockhash(block.number - 1)); //FIXME we could have an interface for getting randomness and one contract assignment could be to implement a better randomness.
uint winning_number = r % 38;


function spin() external {
require(color_bets.length > 0, "No bets placed yet");
require(block.number >= lastSpinBlockNumber + MIN_BLOCKS_BETWEEN_SPINS, "Too soon to spin again");

uint winning_number = get_random_number();
settle_bets(winning_number);

lastSpinBlockNumber = block.number;
}


/// Helper function to settle all bets given a winning number
/// The payouts can be complex.
/// https://www.omnicalculator.com/statistics/roulette-payout
function settle_bets(uint winning_number) internal {
//TODO
Color winning_color = color_of(winning_number);
for (uint i = 0; i < color_bets.length; i++) {
if (color_bets[i].color == winning_color) {
// Player wins
// Convert the beneficiary address to payable
address payable payableBeneficiary = payable(color_bets[i].beneficiary);

payableBeneficiary.transfer(color_bets[i].amount * 2); // Double the bet amount
emit ColorBetWon(color_bets[i]);
} else {
// Player loses
emit ColorBetLost(color_bets[i]);
}
}
delete color_bets;
}

/// Helper function to generate a random number between 0 and 37
function get_random_number() internal view returns (uint) {
return uint(keccak256(abi.encodePacked(block.timestamp, block.difficulty, blockhash(block.number - 1)))) % 38;
}
}







// Enhancement: Allow all kinds of other bets like:
// * even / odd
// * one-spot
Expand Down
98 changes: 90 additions & 8 deletions evm/src/solidity/SocialismDAO.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,28 +72,64 @@ contract SocialismDAO {

/// Join as a new member of the society
function join() external {
//TODO
// Make sure the caller is not already a member
require(!members[msg.sender], "Already a member");

// Add the caller to the members mapping
members[msg.sender] = true;

// Increment the number of members
num_members++;

// Emit an event to indicate that a new member has joined
emit MemberJoined(msg.sender);
}

/// Exit the society.
function exit() external {
//TODO
// Make sure the caller is a member
require(members[msg.sender], "Not a member");

// Remove the caller from the members mapping
delete members[msg.sender];

// Decrement the number of members
num_members--;

// Emit an event to indicate that a member has exited
emit MemberExited(msg.sender);
}


/// Helper function to remove a member's needs
/// This helper is useful because removal involves three storage items
function remove_need(address a) internal {
//TODO
// Find the index of the member in the array of members with needs
uint indexToRemove = needy_member_index[a];

// Swap the member to remove with the last member in the array
address lastMember = members_with_needs[members_with_needs.length - 1];
members_with_needs[indexToRemove] = lastMember;
needy_member_index[lastMember] = indexToRemove;

// Remove the last element from the array
members_with_needs.pop();

// Remove the member's need from the needs mapping
delete needs[a];

// Remove the member's index from the needy_member_index mapping
delete needy_member_index[a];
}

/// Check whether an account is a member of the DAO
function is_member(address x) external view returns(bool) {
//TODO
return members[x];
}

/// Check the currently registered need of the given user.
function user_need(address x) external view returns(uint256) {
//TODO
return needs[x];
}

/// Claim the amount of tokens you need on a roughly 100 block basis.
Expand All @@ -104,15 +140,29 @@ contract SocialismDAO {
///
/// In times of plenty your needs will be met; in times of scarcity they may not be.
function claim_need(uint need) external {
//TODO
// Update the caller's need in the `needs` mapping
needs[msg.sender] = need;

// If the caller has previously claimed a need, update the members_with_needs array
if (needy_member_index[msg.sender] != 0 || members_with_needs.length == 0) {
if (needs[msg.sender] == 0) {
remove_need(msg.sender);
} else if (needy_member_index[msg.sender] == 0) {
members_with_needs.push(msg.sender);
needy_member_index[msg.sender] = members_with_needs.length;
}
}

// Emit an event indicating that a need has been claimed
emit NeedClaimed(msg.sender, need);
}

/// Contribute some of your private funds to the socialism.
/// As a member of the society, it is your civic duty to call this method as often as you can.
///
/// Although we expect only members to contribute, we will allow donations from non-members too
function contribute() external payable {
//TODO
emit ResourcesContributed(msg.sender, msg.value);
}

/// Cause the members to be paid according to their needs.
Expand All @@ -134,8 +184,40 @@ contract SocialismDAO {
/// People who know their typical weekly expense will not have to re-submit each time.
/// This helps save gas fees, but makes it easier to ignore your civic duty to decrease your claim when you need less.
function trigger_payouts() external {
//TODO
// Check if the payout period has elapsed
require(block.number >= last_payout_block + 100, "Payout period has not elapsed yet");

// Update the last payout block
last_payout_block = block.number;

// Calculate the total amount available for payout
uint total_available_funds = address(this).balance;

// Loop through the members and pay them according to their needs
for (uint i = 0; i < members_with_needs.length; i++) {
address member = members_with_needs[i];
uint member_need = needs[member];

// Calculate the even split amount based on the remaining funds
uint even_split_amount = total_available_funds / (members_with_needs.length - i);

// Determine the actual payout amount for this member
uint payout_amount = (even_split_amount < member_need) ? even_split_amount : member_need;

// Transfer the funds to the member
payable(member).transfer(payout_amount);

// Subtract the paid amount from the total available funds
total_available_funds -= payout_amount;

// Remove the member's need since it has been satisfied
remove_need(member);
}

// Emit an event indicating that payouts have been performed
emit PayoutsPerformed(block.number, address(this).balance, total_available_funds);
}


}

Expand Down
53 changes: 49 additions & 4 deletions evm/src/solidity/TicTacToe.sol
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,18 @@ contract TicTacToe {
/// Internal helper function to actually do the turn taking logic
/// This is called by both take_turn and take_winning_turn
function do_take_turn(uint cell_index) internal {
//TODO
require(cell_index >= 0 && cell_index < 9, "Invalid cell index");
require(board[cell_index] == address(0), "Cell already taken");

address player = msg.sender;
require(player == player1 || player == player2, "Wrong player");

require(player == (num_turns % 2 == 0 ? player1 : player2), "Wrong player's turn");

board[cell_index] = player;
num_turns++;

emit TurnTaken(player, cell_index);
}

/// @dev Take a winning turn in a tic-tac-toe game
Expand All @@ -74,17 +85,51 @@ contract TicTacToe {
/// rather the user is forced to point out exactly where they have won, and the chain
/// just confirms it.
function take_winning_turn(uint cell_index, WinLocation win_location) external {
//TODO
do_take_turn(cell_index);

address winner = msg.sender;
require(verify_win(winner, win_location), "Invalid win claimed");

emit GameWon(winner, win_location);
}

/// Internal helper function to verify whether a win is valid.
function verify_win(address winner, WinLocation location) view internal returns(bool) {
//TODO
address player = winner;
address opponent = (player == player1) ? player2 : player1;

// Check if the claimed win location is valid
if (location == WinLocation.LeftColumn) {
return (board[0] == player && board[3] == player && board[6] == player);
} else if (location == WinLocation.CenterColumn) {
return (board[1] == player && board[4] == player && board[7] == player);
} else if (location == WinLocation.RightColumn) {
return (board[2] == player && board[5] == player && board[8] == player);
} else if (location == WinLocation.TopRow) {
return (board[0] == player && board[1] == player && board[2] == player);
} else if (location == WinLocation.MiddleRow) {
return (board[3] == player && board[4] == player && board[5] == player);
} else if (location == WinLocation.BottomRow) {
return (board[6] == player && board[7] == player && board[8] == player);
} else if (location == WinLocation.UphillDiagonal) {
return (board[0] == player && board[4] == player && board[8] == player);
} else if (location == WinLocation.DownhillDiagonal) {
return (board[2] == player && board[4] == player && board[6] == player);
} else {
return false;
}
}

/// Give up on the game allowing the other player to win.
function surrender() external {
//TODO
address player = msg.sender;
require(player == player1 || player == player2, "Invalid player");

// Determine the opponent of the player who surrenders
address opponent = (player == player1) ? player2 : player1;

// Emit the event indicating the surrender
emit GameWon(opponent, WinLocation.OpponentSurrender);
}
}

Expand Down
Loading