Skip to content
Draft
Show file tree
Hide file tree
Changes from 16 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ data/snapshotBalance
data/holders
flattened
distribution-data*.json
.DS_Store

#Hardhat files
cache
Expand Down
177 changes: 177 additions & 0 deletions contracts/competition/Competition.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import "../referrals/interfaces/IReferralStorage.sol";
import "../access/Governable.sol";

contract Competition is Governable {
struct Team {
address leader;
string name;
bytes32 referralCode;
address[] members;
}

struct Competition {
uint start;
uint end;
uint maxTeamSize;
mapping(address => Team) teams;
mapping(string => bool) teamNames;
mapping(address => address) memberTeams;
mapping(address => address) joinRequests;
}

Competition[] public competitions;
IReferralStorage public referralStorage;

event TeamCreated(uint index, address leader, string name, bytes32 referral);
event JoinRequestCreated(uint index, address member, address leader);
event JoinRequestCanceled(uint index, address member);
event JoinRequestApproved(uint index, address member, address leader);
event MemberRemoved(uint index, address leader, address member);
event CompetitionCreated(uint index, uint start, uint end, uint maxTeamSize);
event CompetitionUpdated(uint index, uint start, uint end, uint maxTeamSize);
event CompetitionRemoved(uint index);

modifier registrationIsOpen(uint competitionIndex) {
require(competitions[competitionIndex].start > block.timestamp, "Competition: Registration is closed.");
_;
}

modifier isNotMember(uint competitionIndex) {
require(competitions[competitionIndex].memberTeams[msg.sender] == address(0), "Competition: Team members are not allowed.");
_;
}

modifier competitionExists(uint index) {
require(competitions.length > index && competitions[index].start > 0, "Competition: The competition does not exist.");
_;
}

constructor(IReferralStorage _referralStorage) public {
referralStorage = _referralStorage;
}

function createCompetition(uint start, uint end, uint maxTeamSize) external onlyGov {
_validateCompetitionParameters(start, end, maxTeamSize);

competitions.push(Competition(start, end, maxTeamSize));

emit CompetitionCreated(competitions.length - 1, start, end, maxTeamSize);
}

function updateCompetition(uint index, uint start, uint end, uint maxTeamSize) external onlyGov competitionExists(index) {
_validateCompetitionParameters(start, end, maxTeamSize);

competitions[index] = Competition(start, end, maxTeamSize);

emit CompetitionUpdated(index, start, end, maxTeamSize);
}

function removeCompetition(uint index) external onlyGov competitionExists(index) {
require(competitions[index].start > block.timestamp, "Competition: Competition is active.s");

delete competitions[index];

emit CompetitionRemoved(index);
}

function createTeam(uint competitionIndex, string calldata name, bytes32 referralCode) external registrationIsOpen(competitionIndex) isNotMember(competitionIndex) {
Competition storage competition = competitions[competitionIndex];

require(referralStorage.codeOwners(referralCode) != address(0), "Competition: Referral code does not exist.");
require(!competition.teamNames[name], "Competition: Team name already registered.");

Team storage team = competition.teams[msg.sender];
team.leader = msg.sender;
team.name = name;
team.referralCode = referralCode;
team.members.push(msg.sender);

competition.teamNames[name] = true;
competition.memberTeams[msg.sender] = msg.sender;

emit TeamCreated(competitionIndex, msg.sender, name, referralCode);
}

function createJoinRequest(uint competitionIndex, address leaderAddress) external registrationIsOpen(competitionIndex) isNotMember(competitionIndex) {
Competition storage competition = competitions[competitionIndex];

require(competition.memberTeams[msg.sender] == address(0), "Competition: You can't join multiple teams.");
require(competition.teams[leaderAddress].leader != address(0), "Competition: The team does not exist.");

competition.joinRequests[msg.sender] = leaderAddress;

emit JoinRequestCreated(competitionIndex, msg.sender, leaderAddress);
}

function approveJoinRequest(uint competitionIndex, address memberAddress) external registrationIsOpen(competitionIndex) {
Competition storage competition = competitions[competitionIndex];

require(competition.joinRequests[memberAddress] == msg.sender, "Competition: This member did not apply.");
require(competition.memberTeams[memberAddress] == address(0), "Competition: This member already joined a team.");
require(competition.teams[msg.sender].members.length < competition.maxTeamSize, "Competition: Team is full.");

// referralStorage.setTraderReferralCode(memberAddress, teams[msg.sender].referral);
competition.teams[msg.sender].members.push(memberAddress);
competition.memberTeams[memberAddress] = msg.sender;
competition.joinRequests[memberAddress] = address(0);

emit JoinRequestApproved(competitionIndex, memberAddress, msg.sender);
}

function cancelJoinRequest(uint competitionIndex) external registrationIsOpen(competitionIndex) {
competitions[competitionIndex].joinRequests[msg.sender] = address(0);
emit JoinRequestCanceled(competitionIndex, msg.sender);
}

function removeMember(uint competitionIndex, address leaderAddress, address memberAddress) external registrationIsOpen(competitionIndex) {
Competition storage competition = competitions[competitionIndex];

require(competition.memberTeams[memberAddress] == msg.sender || memberAddress == msg.sender, "Competition: You are not allowed to remove this member.");

address[] memory oldMembers = competition.teams[leaderAddress].members;
address[] memory newMembers = new address[](oldMembers.length - 1);
for (uint i = 0; i < oldMembers.length; i++) {
if (oldMembers[i] != memberAddress) {
newMembers[i] = oldMembers[i];
}
}

competition.teams[leaderAddress].members = newMembers;
competition.memberTeams[memberAddress] = address(0);

emit MemberRemoved(competitionIndex, leaderAddress, memberAddress);
}

function getTeam(uint competitionIndex, address leaderAddress) external view returns (address, string memory, bytes32) {
Team memory team = competitions[competitionIndex].teams[leaderAddress];
return (team.leader, team.name, team.referralCode);
}

function getTeamMembers(uint competitionIndex, address leaderAddress, uint start, uint offset) external view returns (address[] memory) {
address[] memory members = competitions[competitionIndex].teams[leaderAddress].members;
address[] memory result = new address[](offset);

for (uint i = start; i < start + offset && i < members.length; i++) {
result[i] = members[i];
}

return result;
}

function getJoinRequest(uint competitionIndex, address memberAddress) external view returns (address) {
return competitions[competitionIndex].joinRequests[memberAddress];
}

function validateName(uint competitionIndex, string calldata name) external view returns (bool) {
return !competitions[competitionIndex].teamNames[name];
}

function _validateCompetitionParameters(uint start, uint end, uint maxTeamSize) internal {
require(start > block.timestamp, "Competition: Start time must be in the future.");
require(end > start, "Competition: End time must be greater than start time.");
require(maxTeamSize > 0, "Competition: Max team size must be greater than zero.");
}
}
43 changes: 43 additions & 0 deletions scripts/core/deployCompetition.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const { deployContract, contractAt } = require("../shared/helpers")

const network = (process.env.HARDHAT_NETWORK || 'mainnet');

async function getArbValues() {
const positionRouter = await contractAt("PositionRouter", "0x3D6bA331e3D9702C5e8A8d254e5d8a285F223aba")

return { positionRouter }
}

async function getAvaxValues() {
const positionRouter = await contractAt("PositionRouter", "0x195256074192170d1530527abC9943759c7167d8")

return { positionRouter }
}

async function getValues() {
if (network === "arbitrum") {
return getArbValues()
}

if (network === "avax") {
return getAvaxValues()
}

if (network === "testnet") {
return getTestnetValues()
}
}

async function main() {
const { positionRouter } = await getValues()
const referralStorage = await contractAt("ReferralStorage", await positionRouter.referralStorage())

await deployContract("Competition", [ referralStorage.address ]);
}

main()
.then(() => process.exit(0))
.catch(error => {
console.error(error)
process.exit(1)
})
Loading