Skip to content
Draft
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
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
135 changes: 135 additions & 0 deletions contracts/competition/Competition.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

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

contract Competition is Governable
Comment thread
morazzela marked this conversation as resolved.
Outdated
{
Comment thread
morazzela marked this conversation as resolved.
Outdated
struct Team {
address leader;
string name;
bytes32 referral;
address[] members;
address[] joinRequests;
}

uint public start;
uint public end;
uint public registrationStart;
uint public registrationEnd;
IReferralStorage private referralStorage;
address[] private leaders;
Comment thread
morazzela marked this conversation as resolved.
Outdated
mapping(address => Team) private teams;
mapping(string => bool) private teamNames;
mapping(address => address) private membersToTeam;
mapping(address => mapping(address => bool)) private requests;
Comment thread
morazzela marked this conversation as resolved.
Outdated

modifier registrationIsOpen()
Comment thread
morazzela marked this conversation as resolved.
Outdated
{
require(block.timestamp >= registrationStart && block.timestamp < registrationEnd, "Registration is closed.");
_;
}

modifier isNotMember()
{
require(membersToTeam[msg.sender] == address(0), "Team members are not allowed.");
_;
}

constructor(
uint start_,
uint end_,
uint registrationStart_,
uint registrationEnd_,
IReferralStorage referralStorage_
Comment thread
morazzela marked this conversation as resolved.
Outdated
) public {
start = start_;
end = end_;
registrationStart = registrationStart_;
registrationEnd = registrationEnd_;
referralStorage = referralStorage_;
}

function registerTeam(string calldata name, bytes32 referral) external registrationIsOpen {
require(referralStorage.codeOwners(referral) != address(0), "Referral code does not exist.");
require(teamNames[name] == false, "Team name already registered.");

Team storage team;
Comment thread
morazzela marked this conversation as resolved.
Outdated

team.leader = msg.sender;
team.name = name;
team.referral = referral;
team.members.push(msg.sender);

teams[msg.sender] = team;
leaders.push(msg.sender);
teamNames[name] = true;
}

function createJoinRequest(address leaderAddress) external registrationIsOpen isNotMember {
require(membersToTeam[msg.sender] == address(0), "You can't join multiple teams.");
require(teams[msg.sender].leader != address(0), "The team does not exist.");
require(requests[leaderAddress][msg.sender] == false, "You already applied for this team.");

teams[leaderAddress].joinRequests.push(msg.sender);
requests[leaderAddress][msg.sender] = true;
}

function approveJoinRequest(address memberAddress) external registrationIsOpen isNotMember {
require(requests[msg.sender][memberAddress] == false, "This member did not apply.");
require(membersToTeam[memberAddress] == address(0), "This member already joined a team.");

referralStorage.setTraderReferralCode(memberAddress, teams[msg.sender].referral);
teams[msg.sender].members.push(msg.sender);
membersToTeam[memberAddress] = msg.sender;
}

function getLeaders(uint start_, uint offset) external view returns (address[] memory) {
address[] memory res;

for (uint i = start_; i < leaders.length && i < start_ + offset; i++) {
res[i] = leaders[i];
}

return res;
}

function getTeam(address leaderAddr) external view returns (address leader, string memory name, bytes32 referral) {
Team memory team = teams[leaderAddr];
return (team.leader, team.name, team.referral);
}

function getTeamMembers(address leaderAddr) external view returns (address[] memory) {
return teams[leaderAddr].members;
}

function getTeamJoinRequests(address leaderAddr) external view returns (address[] memory) {
address[] memory res;

for (uint i = 0; i < teams[leaderAddr].joinRequests.length; i++) {
address jr = teams[leaderAddr].joinRequests[i];
if (membersToTeam[jr] == address(0)) {
res[i] = jr;
}
}

return res;
}

function setStart(uint start_) external onlyGov {
start = start_;
}

function setEnd(uint end_) external onlyGov {
end = end_;
}

function setRegistrationStart(uint registrationStart_) external onlyGov {
registrationStart = registrationStart_;
}

function setRegistrationEnd(uint registrationEnd_) external onlyGov {
registrationEnd = registrationEnd_;
}
}
52 changes: 52 additions & 0 deletions scripts/core/deployCompetition.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
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())

const startTime = Math.round(Date.now() / 1000)
const endTime = startTime + 100000000000

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

main()
.then(() => process.exit(0))
.catch(error => {
console.error(error)
process.exit(1)
})
88 changes: 88 additions & 0 deletions test/competition/Competition.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
const { expect, use } = require("chai")
const { solidity } = require("ethereum-waffle")
const { deployContract } = require("../shared/fixtures")
const { getBlockTime } = require("../shared/utilities")

use(solidity)

const { keccak256 } = ethers.utils

// Tier0 (5% discount, 5% rebate) = Tier {totalRebate = 1000, defaultTradersDiscountShare = 5000}
// Tier1 (12% discount, 8% rebate) = Tier {totalRebate = 2000, defaultTradersDiscountShare = 6000}
// Tier2 (12% discount, 15% rebate) = Tier {totalRebate = 2700, defaultTradersDiscountShare = 4444}
// for the last tier extra EsGMX incentives will be handled off-chain
describe("Competition", function () {
const provider = waffle.provider
const [wallet, user0, user1, user2] = provider.getWallets()
let competition
let referralStorage

beforeEach(async () => {
const ts = await getBlockTime(provider)

referralStorage = await deployContract("ReferralStorage", [])
competition = await deployContract("Competition", [
ts + 60, // start
ts + 120, // end
ts - 60, // registrationStart
ts + 60, // registrationEnd
referralStorage.address
]);
})

it("allows owner to set times", async () => {
await competition.connect(wallet).setStart(1)
await competition.connect(wallet).setEnd(1)
await competition.connect(wallet).setRegistrationStart(1)
await competition.connect(wallet).setRegistrationEnd(1)
})

it("disable non owners to set times", async () => {
await expect(competition.connect(user0).setStart(1)).to.be.revertedWith("Governable: forbidden")
await expect(competition.connect(user0).setEnd(1)).to.be.revertedWith("Governable: forbidden")
await expect(competition.connect(user0).setRegistrationStart(1)).to.be.revertedWith("Governable: forbidden")
await expect(competition.connect(user0).setRegistrationEnd(1)).to.be.revertedWith("Governable: forbidden")
})

it("disable people to register teams before registration time", async () => {
const code = keccak256("0xFF")
await referralStorage.connect(user0).registerCode(code)
await competition.connect(wallet).setRegistrationStart((await getBlockTime(provider)) + 10)
await expect(competition.connect(user0).registerTeam("1", code)).to.be.revertedWith("Registration is closed.")
})

it("disable people to register teams after registration time", async () => {
const code = keccak256("0xFF")
await referralStorage.connect(user0).registerCode(code)
await competition.connect(wallet).setRegistrationEnd((await getBlockTime(provider)) - 10)
await expect(competition.connect(user0).registerTeam("1", code)).to.be.revertedWith("Registration is closed.")
})

it("allows people to register teams in times", async () => {
const code = keccak256("0xFF")
await referralStorage.connect(user0).registerCode(code)
try {
await competition.connect(user0).registerTeam("1", code)
} catch (e) {
console.log(e)
}
})

it("disabled people to register multiple teams", async () => {
const code = keccak256("0xFF")
await referralStorage.connect(user0).registerCode(code)
await competition.connect(user0).registerTeam("1", code)
await expect(competition.connect(user0).registerTeam("1", code)).to.be.revertedWith("Team members are not allowed.")
})

it("disabled people to register a team with non existing referral code", async () => {
const code = keccak256("0xFF")
await expect(competition.connect(user0).registerTeam("1", code)).to.be.revertedWith("Referral code does not exist.")
})

it("disabled multiple teams with the same name", async () => {
const code = keccak256("0xFF")
await competition.connect(user0).registerTeam("1", code)
await expect(competition.connect(user1).registerTeam("1", code)).to.be.revertedWith("Team name already registered.")
})
});