Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 139 additions & 44 deletions src/Fullcount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@ import { IERC721 } from "../lib/openzeppelin-contracts/contracts/token/ERC721/IE
import { SignatureChecker } from "../lib/openzeppelin-contracts/contracts/utils/cryptography/SignatureChecker.sol";

import {
PlayerType,
PitchSpeed,
SwingType,
VerticalLocation,
HorizontalLocation,
Session,
AtBat,
AtBatOutcome,
HorizontalLocation,
NFT,
Outcome,
Pitch,
PitchSpeed,
PlayerType,
Session,
Swing,
Outcome,
AtBatOutcome
SwingType,
VerticalLocation
} from "./data.sol";

/*
Expand Down Expand Up @@ -95,7 +96,14 @@ contract Fullcount is EIP712 {
mapping(uint256 => uint256[]) public AtBatSessions;
mapping(uint256 => uint256) public SessionAtBat;

// Player address => executor address => bool. Whether or not the
// executor is able to submit at-bats on behalf of the player.
mapping(address => mapping(address => bool)) public TrustedExecutors;

event FullcountDeployed(string indexed version, uint256 SecondsPerPhase);

event ExecutorChange(address indexed player, address indexed executor, bool approved);

event SessionStarted(
uint256 indexed sessionID, address indexed nftAddress, uint256 indexed tokenID, PlayerType role
);
Expand All @@ -104,6 +112,14 @@ contract Fullcount is EIP712 {
);
event SessionExited(uint256 indexed sessionID, address indexed nftAddress, uint256 indexed tokenID);
event SessionAborted(uint256 indexed sessionID, address indexed nftAddress, uint256 indexed tokenID);
event SessionResolved(
uint256 indexed sessionID,
Outcome indexed outcome,
address pitcherAddress,
uint256 pitcherTokenID,
address batterAddress,
uint256 batterTokenID
);

event AtBatStarted(
uint256 indexed atBatID,
Expand All @@ -113,28 +129,13 @@ contract Fullcount is EIP712 {
PlayerType role,
bool requiresSignature
);

event AtBatJoined(
uint256 indexed atBatID,
address indexed nftAddress,
uint256 indexed tokenID,
uint256 firstSessionID,
PlayerType role
);

event PitchCommitted(uint256 indexed sessionID);
event SwingCommitted(uint256 indexed sessionID);
event PitchRevealed(uint256 indexed sessionID, Pitch pitch);
event SwingRevealed(uint256 indexed sessionID, Swing swing);
event SessionResolved(
uint256 indexed sessionID,
Outcome indexed outcome,
address pitcherAddress,
uint256 pitcherTokenID,
address batterAddress,
uint256 batterTokenID
);

event AtBatProgress(
uint256 indexed atBatID,
AtBatOutcome indexed outcome,
Expand All @@ -146,6 +147,11 @@ contract Fullcount is EIP712 {
uint256 batterTokenID
);

event PitchCommitted(uint256 indexed sessionID);
event SwingCommitted(uint256 indexed sessionID);
event PitchRevealed(uint256 indexed sessionID, Pitch pitch);
event SwingRevealed(uint256 indexed sessionID, Swing swing);

constructor(uint256 secondsPerPhase) EIP712("Fullcount", FullcountVersion) {
SecondsPerPhase = secondsPerPhase;
emit FullcountDeployed(FullcountVersion, secondsPerPhase);
Expand All @@ -164,6 +170,19 @@ contract Fullcount is EIP712 {
return AtBatSessions[atBatID].length;
}

function _isExecutorForPlayer(address executor, address player) internal view returns (bool) {
return TrustedExecutors[player][executor];
}

function isExecutorForPlayer(address executor, address player) external view returns (bool) {
return _isExecutorForPlayer(executor, player);
}

function setTrustedExecutor(address executor, bool approved) external virtual {
TrustedExecutors[msg.sender][executor] = approved;
emit ExecutorChange(msg.sender, executor, approved);
}

/**
* Return values:
* 0 - session does not exist
Expand Down Expand Up @@ -387,7 +406,7 @@ contract Fullcount is EIP712 {
return NumAtBats;
}

function _progressAtBat(uint256 finishedSessionID) internal {
function _progressAtBat(uint256 finishedSessionID, bool updateStakedTokens) internal {
uint256 atBatID = SessionAtBat[finishedSessionID];
if (atBatID == 0) return;

Expand All @@ -400,19 +419,36 @@ contract Fullcount is EIP712 {
atBat.outcome = AtBatOutcome.Strikeout;
} else {
atBat.strikes++;
_startNextAtBatSession(
atBatID,
finishedSession.pitcherNFT.nftAddress,
finishedSession.pitcherNFT.tokenID,
finishedSession.batterNFT.nftAddress,
finishedSession.batterNFT.tokenID
);
if (updateStakedTokens) {
_startNextAtBatSession(
atBatID,
finishedSession.pitcherNFT.nftAddress,
finishedSession.pitcherNFT.tokenID,
finishedSession.batterNFT.nftAddress,
finishedSession.batterNFT.tokenID
);
}
}
} else if (finishedSession.outcome == Outcome.Ball) {
if (atBat.balls >= 3) {
atBat.outcome = AtBatOutcome.Walk;
} else {
atBat.balls++;
if (updateStakedTokens) {
_startNextAtBatSession(
atBatID,
finishedSession.pitcherNFT.nftAddress,
finishedSession.pitcherNFT.tokenID,
finishedSession.batterNFT.nftAddress,
finishedSession.batterNFT.tokenID
);
}
}
} else if (finishedSession.outcome == Outcome.Foul) {
if (atBat.strikes < 2) {
atBat.strikes++;
}
if (updateStakedTokens) {
_startNextAtBatSession(
atBatID,
finishedSession.pitcherNFT.nftAddress,
Expand All @@ -421,17 +457,6 @@ contract Fullcount is EIP712 {
finishedSession.batterNFT.tokenID
);
}
} else if (finishedSession.outcome == Outcome.Foul) {
if (atBat.strikes < 2) {
atBat.strikes++;
}
_startNextAtBatSession(
atBatID,
finishedSession.pitcherNFT.nftAddress,
finishedSession.pitcherNFT.tokenID,
finishedSession.batterNFT.nftAddress,
finishedSession.batterNFT.tokenID
);
} else if (finishedSession.outcome == Outcome.Single) {
atBat.outcome = AtBatOutcome.Single;
} else if (finishedSession.outcome == Outcome.Double) {
Expand Down Expand Up @@ -826,7 +851,7 @@ contract Fullcount is EIP712 {
StakedSession[session.pitcherNFT.nftAddress][session.pitcherNFT.tokenID] = 0;
session.pitcherLeftSession = true;

_progressAtBat(sessionID);
_progressAtBat(sessionID, true);
}
}

Expand Down Expand Up @@ -883,7 +908,77 @@ contract Fullcount is EIP712 {
StakedSession[session.pitcherNFT.nftAddress][session.pitcherNFT.tokenID] = 0;
session.pitcherLeftSession = true;

_progressAtBat(sessionID);
_progressAtBat(sessionID, true);
}
}

function submitAtBat(
NFT memory pitcherNFT,
NFT memory batterNFT,
Pitch[] memory pitches,
Swing[] memory swings,
AtBatOutcome proposedOutcome
)
external
{
require(
pitches.length == swings.length, "Fullcount.submitAtBat: number of pitches does not match number of swings."
);

address pitcherOwner = IERC721(pitcherNFT.nftAddress).ownerOf(pitcherNFT.tokenID);
require(
_isExecutorForPlayer(msg.sender, pitcherOwner),
"Fullcount.submitAtBat: sender is not an executor for pitcher."
);

address batterOwner = IERC721(batterNFT.nftAddress).ownerOf(batterNFT.tokenID);
require(
_isExecutorForPlayer(msg.sender, batterOwner),
"Fullcount.submitAtBat: sender is not an executor for batter."
);

// Create at-bat
NumAtBats++;

AtBatState[NumAtBats].pitcherNFT = pitcherNFT;
AtBatState[NumAtBats].batterNFT = batterNFT;

uint256[] storage sessionList = AtBatSessions[NumAtBats];

for (uint256 i = 0; i < pitches.length; i++) {
if (AtBatState[NumAtBats].outcome != AtBatOutcome.InProgress) {
revert("Fullcount.submitAtBat: invalid at-bat - invalid at-bat");
}

Outcome sessionOutcome = resolve(pitches[i], swings[i]);

NumSessions++;
SessionState[NumSessions].pitcherNFT = pitcherNFT;
SessionState[NumSessions].batterNFT = batterNFT;
SessionState[NumSessions].outcome = sessionOutcome;

emit SessionResolved(
NumSessions,
sessionOutcome,
pitcherNFT.nftAddress,
pitcherNFT.tokenID,
batterNFT.nftAddress,
batterNFT.tokenID
);

// Add session to at-bat
sessionList.push(NumSessions);
SessionAtBat[NumSessions] = NumAtBats;

_progressAtBat(NumSessions, false);
}

if (AtBatState[NumAtBats].outcome == AtBatOutcome.InProgress) {
revert("Fullcount.submitAtBat: invalid at-bat - inconclusive");
}

if (AtBatState[NumAtBats].outcome != proposedOutcome) {
revert("Fullcount.submitAtBat: at-bat outcome does not match executor proposed outcome");
}
}
}
Loading