Skip to content

Instantly share code, notes, and snippets.

@0xfoobar
Created March 14, 2022 22:10
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save 0xfoobar/c7f620e62339b0e7d201cb64f5042eef to your computer and use it in GitHub Desktop.
Save 0xfoobar/c7f620e62339b0e7d201cb64f5042eef to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.12;
import {IERC20} from "../interfaces/IERC20.sol";
contract BetEscrow {
event BetOffered(uint betId);
enum Status {
Offered,
Accepted,
Settled
}
enum Outcome {
A,
B,
Neutral
}
struct Bet {
uint id;
address playerA;
address playerB;
address adjucator;
IERC20 token;
uint amount;
Status status;
}
mapping(uint => Bet) public bets;
uint public nextBetId = 0;
bool internal locked;
modifier noReentrant() {
require(!locked, "No re-entrancy");
locked = true;
_;
locked = false;
}
function offerBet(address _playerB, address _adjucator, IERC20 token, uint amount) external {
IERC20(token).transferFrom(msg.sender, address(this), amount);
Bet memory bet = Bet({
id: nextBetId,
playerA: msg.sender,
playerB: _playerB,
adjucator: _adjucator,
token: token,
amount: amount,
status: Status.Offered
});
bets[nextBetId] = bet;
emit BetOffered(nextBetId);
nextBetId += 1;
}
function withdrawOffer(uint betId) external noReentrant {
Bet memory bet = bets[betId];
require(msg.sender == bet.playerA, "Not the offer creator");
require(bet.status == Status.Offered, "Not offered");
// Cache values before deleting
IERC20 _token = bet.token;
uint _amount = bet.amount;
// Delete bet before refunding to prevent reentrancy
delete bets[betId];
// Refund
_token.transfer(msg.sender, _amount);
}
function acceptBet(uint betId) external {
Bet memory bet = bets[betId];
require(msg.sender == bet.playerB, "Not the offer recipient");
require(bet.status == Status.Offered, "Not offered");
bet.token.transferFrom(msg.sender, address(this), bet.amount);
bets[betId].status = Status.Accepted;
}
function settleBet(uint betId, Outcome outcome) external noReentrant {
Bet memory bet = bets[betId];
require(msg.sender == bet.adjucator, "Not the adjucator");
require(bet.status == Status.Accepted, "Not accepted");
if (outcome == Outcome.A) {
bet.token.transfer(bet.playerA, 2 * bet.amount);
} else if (outcome == Outcome.B) {
bet.token.transfer(bet.playerB, 2 * bet.amount);
} else if (outcome == Outcome.Neutral) {
bet.token.transfer(bet.playerA, bet.amount);
bet.token.transfer(bet.playerB, bet.amount);
}
bets[betId].status = Status.Settled;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment