Test deploy on Moonbase Alpha
parent
066a1d9ddb
commit
8e122386b6
|
@ -1,3 +1,7 @@
|
|||
# DCTF
|
||||
# DCTF Beta
|
||||
|
||||
Decentralized CTF engine, used by CCTF
|
||||
|
||||
Testnet deployment on Moonbase Alpha: https://moonbase.moonscan.io/address/0x919f68cc35ce5d49a45c94dc44e7bf444f9a7531
|
||||
|
||||
Contributors: six, Silur, Anonz team, Metafaka team, BMEta team
|
|
@ -0,0 +1,138 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Authors: six and Silur
|
||||
pragma solidity ^0.8.16;
|
||||
|
||||
contract CCTF9 {
|
||||
address public admin;
|
||||
uint256 public volStart;
|
||||
uint256 public volMaxPoints;
|
||||
uint256 public powDiff;
|
||||
bool public started;
|
||||
|
||||
enum PlayerStatus {
|
||||
Unverified,
|
||||
Verified,
|
||||
Banned
|
||||
}
|
||||
|
||||
struct Player {
|
||||
PlayerStatus status;
|
||||
uint256 points;
|
||||
}
|
||||
|
||||
modifier onlyAdmin {
|
||||
require(msg.sender == admin, "Not admin");
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyActive {
|
||||
require(started == true, "CCTF not started.");
|
||||
_;
|
||||
}
|
||||
|
||||
struct Flag {
|
||||
address signer;
|
||||
bool onlyFirstSolver;
|
||||
uint256 points;
|
||||
string skill_name;
|
||||
}
|
||||
|
||||
mapping (address => Player) public players;
|
||||
mapping (uint256 => Flag) public flags;
|
||||
|
||||
event CCTFStarted(uint256 timestamp);
|
||||
event FlagAdded(uint256 indexed flagId, address flagSigner);
|
||||
event FlagRemoved(uint256 indexed flagId);
|
||||
event FlagSolved(uint256 indexed flagId, address indexed solver);
|
||||
|
||||
constructor(uint256 _volMaxPoints, uint256 _powDiff) {
|
||||
admin = msg.sender;
|
||||
volMaxPoints = _volMaxPoints;
|
||||
powDiff = _powDiff;
|
||||
started = false;
|
||||
}
|
||||
|
||||
function setAdmin(address _admin) external onlyAdmin {
|
||||
require(_admin != address(0));
|
||||
admin = _admin;
|
||||
}
|
||||
|
||||
function setCCTFStatus(bool _started) external onlyAdmin {
|
||||
started = _started;
|
||||
}
|
||||
|
||||
function setFlag(uint256 _flagId, address _flagSigner, bool _onlyFirstSolver, uint256 _points, string memory _skill) external onlyAdmin{
|
||||
flags[_flagId] = Flag(_flagSigner, _onlyFirstSolver, _points, _skill);
|
||||
emit FlagAdded(_flagId, _flagSigner);
|
||||
}
|
||||
|
||||
function setPowDiff(uint256 _powDiff) external onlyAdmin {
|
||||
powDiff = _powDiff;
|
||||
}
|
||||
|
||||
|
||||
function register(string memory _RTFM) external {
|
||||
require(players[msg.sender].status == PlayerStatus.Unverified, 'Already registered or banned');
|
||||
//uint256 pow = uint256(keccak256(abi.encodePacked("CCTF", msg.sender,"registration", nonce)));
|
||||
//require(pow < powDiff, "invalid pow");
|
||||
require(keccak256(abi.encodePacked('I_read_it')) == keccak256(abi.encodePacked(_RTFM))); // PoW can be used for harder challenges, this is Entry!
|
||||
players[msg.sender].status = PlayerStatus.Verified;
|
||||
}
|
||||
|
||||
function setPlayerStatus(address player, PlayerStatus status) external onlyAdmin {
|
||||
players[player].status = status;
|
||||
}
|
||||
|
||||
|
||||
////////// Submit flags
|
||||
mapping(bytes32 => bool) usedNs; // Against replay attack (we only check message signer)
|
||||
mapping (address => mapping (uint256 => bool)) Solves; // address -> challenge ID -> solved/not
|
||||
uint256 public submission_success_count = 0; // For statistics
|
||||
|
||||
function SubmitFlag(bytes32 _message, bytes memory signature, uint256 _submitFor) external onlyActive {
|
||||
require(players[msg.sender].status == PlayerStatus.Verified, "You are not even playing");
|
||||
require(bytes32(_message).length <= 256, "Too long message.");
|
||||
require(!usedNs[_message]);
|
||||
usedNs[_message] = true;
|
||||
require(recoverSigner(_message, signature) == flags[_submitFor].signer, "Not signed with the correct key.");
|
||||
require(Solves[msg.sender][_submitFor] == false);
|
||||
|
||||
Solves[msg.sender][_submitFor] = true;
|
||||
players[msg.sender].points += flags[_submitFor].points;
|
||||
players[msg.sender].points = players[msg.sender].points < volMaxPoints ? players[msg.sender].points : volMaxPoints;
|
||||
|
||||
if (flags[_submitFor].onlyFirstSolver) {
|
||||
flags[_submitFor].points = 0;
|
||||
}
|
||||
|
||||
submission_success_count = submission_success_count + 1;
|
||||
emit FlagSolved(_submitFor, msg.sender);
|
||||
}
|
||||
|
||||
function recoverSigner(bytes32 _ethSignedMessageHash, bytes memory _signature) public pure returns (address) {
|
||||
(bytes32 r, bytes32 s, uint8 v) = splitSignature(_signature);
|
||||
return ecrecover(_ethSignedMessageHash, v, r, s);
|
||||
}
|
||||
|
||||
function splitSignature(bytes memory sig) public pure returns (bytes32 r, bytes32 s, uint8 v){
|
||||
require(sig.length == 65, "Invalid signature length");
|
||||
assembly {
|
||||
r := mload(add(sig, 32))
|
||||
s := mload(add(sig, 64))
|
||||
v := byte(0, mload(add(sig, 96)))
|
||||
}
|
||||
}
|
||||
|
||||
////////// Check status, scores, etc
|
||||
function getPlayerStatus(address _player) external view returns (PlayerStatus) {
|
||||
return players[_player].status;
|
||||
}
|
||||
|
||||
function getPlayerPoints(address _player) external view returns (uint256) {
|
||||
return players[_player].points < volMaxPoints ? players[_player].points : volMaxPoints;
|
||||
}
|
||||
|
||||
function getSuccessfulSubmissionCount() external view returns (uint256){
|
||||
return submission_success_count;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Authors: Anonz team, developed at Polkadot Metaverse Championship, as part of their CCTF track solution.
|
||||
// Based on Six's and Silur's CCTF 2022 code.
|
||||
pragma solidity ^0.8.16;
|
||||
|
||||
contract CryptoCTFX {
|
||||
enum PlayerStatus {
|
||||
Unverified,
|
||||
Verified,
|
||||
Banned
|
||||
}
|
||||
|
||||
struct Player {
|
||||
PlayerStatus status;
|
||||
uint score;
|
||||
}
|
||||
|
||||
modifier onlyExistingContest(uint contestID) {
|
||||
require(contests[contestID].admin != address(0), "Unknown contest ID");
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyAdmin(uint contestID) {
|
||||
require(msg.sender == contests[contestID].admin, "You are not the admin of this contest");
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyOpen(uint contestID) {
|
||||
require(contests[contestID].submissionsOpen, "Submissions are not open for this contest at this time");
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyExistingChallenge(uint contestID, uint challengeID) {
|
||||
require(contests[contestID].challenges[challengeID].obscuredFlag != address(0), "Unknown challenge ID");
|
||||
_;
|
||||
}
|
||||
|
||||
struct Challenge {
|
||||
address obscuredFlag; // public key of the flag
|
||||
uint worth;
|
||||
uint256 descriptionFingerprint;
|
||||
bool onlyFirstSolver;
|
||||
string skill;
|
||||
}
|
||||
|
||||
struct Contest {
|
||||
address admin;
|
||||
mapping (uint => Challenge) challenges;
|
||||
mapping (address => Player) players;
|
||||
bool submissionsOpen;
|
||||
mapping (address => mapping (uint => bool)) solves; // address -> challengeID -> solved/not
|
||||
mapping (uint => bool) anySolves; // challengeID -> solved/not
|
||||
}
|
||||
|
||||
mapping (uint => Contest) public contests;
|
||||
|
||||
event ChallengeAddedOrUpdated(uint contestID, uint indexed challengeID);
|
||||
event ChallengeSolved(uint contestID, uint indexed challengeID, address indexed solver);
|
||||
|
||||
function createContest(uint contestID) external {
|
||||
require(contests[contestID].admin == address(0), "This contest ID has already been registered");
|
||||
contests[contestID].admin = msg.sender;
|
||||
contests[contestID].submissionsOpen = false;
|
||||
}
|
||||
|
||||
function setAdmin(uint contestID, address newAdmin) external onlyExistingContest(contestID) onlyAdmin(contestID) {
|
||||
require(newAdmin != address(0));
|
||||
contests[contestID].admin = newAdmin;
|
||||
}
|
||||
|
||||
function setSubmissionsStatus(uint contestID, bool open) external onlyExistingContest(contestID) onlyAdmin(contestID) {
|
||||
contests[contestID].submissionsOpen = open;
|
||||
}
|
||||
|
||||
function addOrUpdateChallenge(uint contestID, uint challengeID, address obscuredFlag, uint worth, uint256 descriptionFingerprint, bool onlyFirstSolver, string memory skill) external onlyExistingContest(contestID) onlyAdmin(contestID) {
|
||||
require(obscuredFlag != address(0), "The obscured flag value must not be 0");
|
||||
contests[contestID].challenges[challengeID] = Challenge(obscuredFlag, worth, descriptionFingerprint, onlyFirstSolver, skill);
|
||||
emit ChallengeAddedOrUpdated(contestID, challengeID);
|
||||
}
|
||||
|
||||
function register(uint contestID, string memory password) external onlyExistingContest(contestID) {
|
||||
require(contests[contestID].players[msg.sender].status == PlayerStatus.Unverified, "You are already registered or banned in this contest");
|
||||
require(keccak256(abi.encodePacked("I_read_it")) == keccak256(abi.encodePacked(password)));
|
||||
contests[contestID].players[msg.sender].status = PlayerStatus.Verified;
|
||||
}
|
||||
|
||||
function setPlayerStatus(uint contestID, address player, PlayerStatus status) external onlyExistingContest(contestID) onlyAdmin(contestID) {
|
||||
contests[contestID].players[player].status = status;
|
||||
}
|
||||
|
||||
function submitFlag(uint contestID, uint challengeID, bytes memory signature) external onlyExistingContest(contestID) onlyExistingChallenge(contestID, challengeID) onlyOpen(contestID) {
|
||||
require(contests[contestID].players[msg.sender].status == PlayerStatus.Verified, "You are unverified or banned in this contest");
|
||||
// the correct signature is an ECDSA signature where (1) the message (hash) is the sender address and (2) the private key is the flag;
|
||||
// (2) is checked by testing against the public key, which can then be public information
|
||||
address recoveredSigner = recoverSigner(bytes32(abi.encodePacked(msg.sender)), signature);
|
||||
require(recoveredSigner != address(0), "Invalid signature");
|
||||
require(recoveredSigner == contests[contestID].challenges[challengeID].obscuredFlag, "Wrong answer");
|
||||
require(!contests[contestID].solves[msg.sender][challengeID], "You have already solved this challenge of this contest");
|
||||
|
||||
if (!contests[contestID].anySolves[challengeID] || !contests[contestID].challenges[challengeID].onlyFirstSolver) {
|
||||
contests[contestID].players[msg.sender].score += contests[contestID].challenges[challengeID].worth;
|
||||
}
|
||||
contests[contestID].solves[msg.sender][challengeID] = true;
|
||||
contests[contestID].anySolves[challengeID] = true;
|
||||
|
||||
emit ChallengeSolved(contestID, challengeID, msg.sender);
|
||||
}
|
||||
|
||||
function recoverSigner(bytes32 messageHash, bytes memory signature) public pure returns (address) {
|
||||
(bytes32 r, bytes32 s, uint8 v) = splitSignature(signature);
|
||||
return ecrecover(messageHash, v, r, s);
|
||||
}
|
||||
|
||||
function splitSignature(bytes memory sig) public pure returns (bytes32 r, bytes32 s, uint8 v) {
|
||||
require(sig.length == 65, "Invalid signature length");
|
||||
assembly {
|
||||
r := mload(add(sig, 32))
|
||||
s := mload(add(sig, 64))
|
||||
v := byte(0, mload(add(sig, 96)))
|
||||
}
|
||||
}
|
||||
|
||||
function getPlayerStatus(uint contestID, address player) external view onlyExistingContest(contestID) returns (PlayerStatus) {
|
||||
return contests[contestID].players[player].status;
|
||||
}
|
||||
|
||||
function getPlayerScore(uint contestID, address player) external view onlyExistingContest(contestID) returns (uint) {
|
||||
return contests[contestID].players[player].score;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,798 @@
|
|||
myABI = [
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_volMaxPoints",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_powDiff",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "constructor"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "timestamp",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "CCTFStarted",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "uint256",
|
||||
"name": "flagId",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "address",
|
||||
"name": "flagSigner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "FlagAdded",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "uint256",
|
||||
"name": "flagId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "FlagRemoved",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "uint256",
|
||||
"name": "flagId",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "solver",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "FlagSolved",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "player",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "enum CCTF9.PlayerStatus",
|
||||
"name": "newStatus",
|
||||
"type": "uint8"
|
||||
}
|
||||
],
|
||||
"name": "PlayerStatusChanged",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "_message",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "signature",
|
||||
"type": "bytes"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_submitFor",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "SubmitFlag",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "TIME_DECAY_MAX",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint8",
|
||||
"name": "",
|
||||
"type": "uint8"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "admin",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "endTime",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "flagCount",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "flagList",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "flags",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "signer",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "enum CCTF9.FlagType",
|
||||
"name": "flagType",
|
||||
"type": "uint8"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "solveCount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "points",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "currentPoints",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "skill_name",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_idx",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_count",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getFlags",
|
||||
"outputs": [
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "signer",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "enum CCTF9.FlagType",
|
||||
"name": "flagType",
|
||||
"type": "uint8"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "solveCount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "points",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "currentPoints",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "skill_name",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"internalType": "struct CCTF9.Flag[]",
|
||||
"name": "flagListRet",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getFlags",
|
||||
"outputs": [
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "signer",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "enum CCTF9.FlagType",
|
||||
"name": "flagType",
|
||||
"type": "uint8"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "solveCount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "points",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "currentPoints",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "skill_name",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"internalType": "struct CCTF9.Flag[]",
|
||||
"name": "flagListRet",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "_player",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "getPlayerPoints",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "_player",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "getPlayerStatus",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "enum CCTF9.PlayerStatus",
|
||||
"name": "",
|
||||
"type": "uint8"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_idx",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_count",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getPlayers",
|
||||
"outputs": [
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "enum CCTF9.PlayerStatus",
|
||||
"name": "status",
|
||||
"type": "uint8"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "points",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "name",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256[2][]",
|
||||
"name": "pointsPerFlag",
|
||||
"type": "uint256[2][]"
|
||||
}
|
||||
],
|
||||
"internalType": "struct CCTF9.Player[]",
|
||||
"name": "playerListRet",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getPlayers",
|
||||
"outputs": [
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "enum CCTF9.PlayerStatus",
|
||||
"name": "status",
|
||||
"type": "uint8"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "points",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "name",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256[2][]",
|
||||
"name": "pointsPerFlag",
|
||||
"type": "uint256[2][]"
|
||||
}
|
||||
],
|
||||
"internalType": "struct CCTF9.Player[]",
|
||||
"name": "playerListRet",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getSuccessfulSubmissionCount",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "playerCount",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "playerList",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "players",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "enum CCTF9.PlayerStatus",
|
||||
"name": "status",
|
||||
"type": "uint8"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "points",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "name",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "powDiff",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "_ethSignedMessageHash",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "_signature",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"name": "recoverSigner",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "pure",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "_RTFM",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "_name",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"name": "register",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "_admin",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "setAdmin",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_endTime",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "setCCTFEndTime",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "_paused",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"name": "setCCTFPaused",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_startTime",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "setCCTFStartTime",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_flagId",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "_flagSigner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "enum CCTF9.FlagType",
|
||||
"name": "_flagType",
|
||||
"type": "uint8"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_points",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "_skill",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"name": "setFlag",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "player",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "enum CCTF9.PlayerStatus",
|
||||
"name": "status",
|
||||
"type": "uint8"
|
||||
}
|
||||
],
|
||||
"name": "setPlayerStatus",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_powDiff",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "setPowDiff",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "sig",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"name": "splitSignature",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "r",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "s",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"internalType": "uint8",
|
||||
"name": "v",
|
||||
"type": "uint8"
|
||||
}
|
||||
],
|
||||
"stateMutability": "pure",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "startTime",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "started",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "submission_success_count",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "volMaxPoints",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "volStart",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
Binary file not shown.
After Width: | Height: | Size: 6.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.8 KiB |
|
@ -0,0 +1,121 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>CCTF Frontend by Metafaka</title>
|
||||
<link rel="icon" type="image/png" href="cctf-icon.png" sizes="96x96">
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto Mono" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.4/css/all.css" integrity="sha384-DyZ88mC6Up2uqS4h/KRgHuoeGwBcD4Ng9SiP4dIRy0EXTlnuz47vAwmeGwVChigm" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="top">
|
||||
|
||||
<div class="top_logo">
|
||||
<img src="cctf-logo.png" id="logo" alt="CCTF logo" width="200" height="49">
|
||||
</div>
|
||||
|
||||
<div class="top_left">
|
||||
<div>
|
||||
<i style="margin: 10px;" class="fas fa-users"></i>
|
||||
</div>
|
||||
<div style="padding-right: 18px; line-height: 14px;">
|
||||
<span id="header_uppercase">PLAYERS</span><br>
|
||||
<span id="players">loading...</span>
|
||||
</div>
|
||||
<div>
|
||||
<i style="margin: 10px;" class="far fa-flag"></i>
|
||||
</div>
|
||||
<div style="line-height: 14px;">
|
||||
<span id="header_uppercase">FOUND FLAGS</span><br>
|
||||
<span id="submissions">loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="top_center" id="toplist">
|
||||
<div class="topitem">
|
||||
<span id="remaining_time" style="font-size: 28px; font-weight: 200">--:--:--</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="top_right">
|
||||
<div class="topitem">
|
||||
|
||||
<!-- Trigger/Open The Modal -->
|
||||
<div style="margin: 0 18px; cursor: pointer" id="myBtn">
|
||||
<i style="margin-right: 2px;" class="fas fa-bolt"></i>
|
||||
<span>Getting Started</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="left">
|
||||
<div class="left-content">
|
||||
<h1>My Account</h1>
|
||||
<div id="account_locked">
|
||||
<button id="account_enable"><img id="metamask" src="metamask.svg">Enable account</button>
|
||||
</div>
|
||||
|
||||
<div id="account_unlocked" style="display: none">
|
||||
<div id="user"></div>
|
||||
<div id="unverified" style="display: none">
|
||||
<h1>Register</h1>
|
||||
<input type="text" id="playerName" placeholder="Your name...">
|
||||
<br>
|
||||
<span style="font-size: 14px;" id="myBtn">I have read the f#%@ken manual: </span><input type="checkbox" id="myCheck">
|
||||
<button id="register"><i class="fab fa-ethereum" style="font-size: 16px; margin-right: 8px;"></i>Submit</button>
|
||||
</div>
|
||||
<div id="verified" style="display: none">
|
||||
<h1>My Points</h1>
|
||||
<span id="holding">0</span>
|
||||
<h1>Challenges</h1>
|
||||
<p>Challenge 1</p>
|
||||
<p>Challenge 2</p>
|
||||
<p>Challenge 3</p>
|
||||
<p>Challenge 4</p>
|
||||
<p>Challenge 5</p>
|
||||
<p>Challenge 6</p>
|
||||
|
||||
</div>
|
||||
<div id="banned" style="display: none">
|
||||
<h1>BANNED</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div id="loading">Loading...</div>
|
||||
<h2>Leaderboard</h2>
|
||||
<table id="rank_list"></table>
|
||||
</div>
|
||||
|
||||
<!-- The Modal -->
|
||||
<div id="myModal" class="modal">
|
||||
|
||||
<!-- Modal content -->
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<span class="close">×</span>
|
||||
<h2>Getting Started</h2>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<h3>The Game</h3>
|
||||
<p>Soooo much fun!</p>
|
||||
|
||||
<h3>Contract Address</h3>
|
||||
<p><a href="https://moonbase.moonscan.io/address/0x919f68cc35ce5d49a45c94dc44e7bf444f9a7531" target="_blank">0x919f68cc35ce5d49a45c94dc44e7bf444f9a7531</a></p>
|
||||
<h3>Questions?</h3>
|
||||
<p>Answers.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script language="javascript" type="text/javascript" src="https://cdn.jsdelivr.net/gh/ethereum/web3.js@1.8.0/dist/web3.min.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="abi.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="script.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 4.9 KiB |
|
@ -0,0 +1,330 @@
|
|||
"use strict";
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
|
||||
const contractAddress = '0xc8bd0A15E35Ddc1692D907583696443e2970779b';
|
||||
const nullAddress = '0x0000000000000000000000000000000000000000';
|
||||
var userAccount = nullAddress;
|
||||
|
||||
var btnRegister = document.getElementById("register");
|
||||
var btnAccountEnable = document.getElementById("account_enable");
|
||||
|
||||
btnRegister.addEventListener('click', register);
|
||||
btnAccountEnable.addEventListener('click', accountEnable);
|
||||
|
||||
var funcType;
|
||||
var mark;
|
||||
var expirationTime;
|
||||
var timerIsRunning = false;
|
||||
var players = [];
|
||||
|
||||
// Modern dapp browsers...
|
||||
if(window.ethereum) {
|
||||
console.log("Modern dapp browser detected");
|
||||
window.web3 = new Web3(window.ethereum);
|
||||
checkNetworkType();
|
||||
ethereum.on('accountsChanged', (accounts) => accountChange(accounts[0]));
|
||||
ethereum.on('chainChanged', (_chainId) => window.location.reload());
|
||||
}
|
||||
// Legacy dapp browsers...
|
||||
else if(window.web3) {
|
||||
console.log("Legacy dapp browser detected");
|
||||
window.web3 = new Web3(web3.currentProvider);
|
||||
// Acccounts always exposed
|
||||
checkNetworkType();
|
||||
}
|
||||
// Non-dapp browsers...
|
||||
else {
|
||||
console.log("Non-Ethereum browser detected. You should consider trying MetaMask!");
|
||||
window.web3 = new Web3(new Web3.providers.WebsocketProvider('wss://goerli.infura.io/ws/v3/d82552d5ad5841e5acbbe7a8417a23ad'));
|
||||
}
|
||||
|
||||
function switchToGoerli() {
|
||||
const iconUrl = "";
|
||||
const goerli = {
|
||||
chainId: "0x5",
|
||||
chainName: "Goerli test network",
|
||||
rpcUrls: ["https://goerli.infura.io/v3/"],
|
||||
nativeCurrency: {
|
||||
name: "GoerliETH",
|
||||
symbol: "ETH",
|
||||
decimals: 18,
|
||||
},
|
||||
blockExplorerUrls: ["https://goerli.etherscan.io"],
|
||||
iconUrls: [iconUrl],
|
||||
};
|
||||
|
||||
const { request } = window.ethereum;
|
||||
request({
|
||||
method: "wallet_switchEthereumChain",
|
||||
params: [{ chainId: goerli.chainId }],
|
||||
}).catch(e => {
|
||||
// This error code indicates that the chain has not been added to MetaMask.
|
||||
if (e.code === 4902) {
|
||||
return request({
|
||||
method: "wallet_addEthereumChain",
|
||||
params: [goerli],
|
||||
});
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function checkNetworkType() {
|
||||
web3.eth.net.getId()
|
||||
.then(function(result) {
|
||||
if(result != 5) { /* Goerli Mainnet */
|
||||
alert("Please select Goerli test network on your Metamask!");
|
||||
console.log('switchToGoerli');
|
||||
switchToGoerli();
|
||||
}
|
||||
console.log('Network Id:', result);
|
||||
});
|
||||
}
|
||||
|
||||
function displayShortAddress(address) {
|
||||
return(address.slice(0, 6) + "..." + address.slice(-4))
|
||||
}
|
||||
|
||||
function setUserAccount() {
|
||||
var x = userAccount.toString();
|
||||
document.getElementById("user").textContent = displayShortAddress(x);
|
||||
if(userAccount != nullAddress) {
|
||||
document.getElementById("account_locked").style.display = "none";
|
||||
document.getElementById("account_unlocked").style.display = "block";
|
||||
}
|
||||
else {
|
||||
document.getElementById("account_locked").style.display = "block";
|
||||
document.getElementById("account_unlocked").style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
function getPlayerStatus() {
|
||||
myContract.methods.getPlayerStatus(userAccount).call()
|
||||
.then(function(result) {
|
||||
console.log("Player status: " + result);
|
||||
if(result == 0) {
|
||||
document.getElementById("unverified").style.display = "block";
|
||||
document.getElementById("verified").style.display = "none";
|
||||
document.getElementById("banned").style.display = "none";
|
||||
}
|
||||
else if(result == 1) {
|
||||
document.getElementById("unverified").style.display = "none";
|
||||
document.getElementById("verified").style.display = "block";
|
||||
document.getElementById("banned").style.display = "none";
|
||||
}
|
||||
else {
|
||||
document.getElementById("unverified").style.display = "none";
|
||||
document.getElementById("verified").style.display = "none";
|
||||
document.getElementById("banned").style.display = "block";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function accountChange(newAccount) {
|
||||
if(web3.utils.isAddress(newAccount)) {
|
||||
userAccount = web3.utils.toChecksumAddress(newAccount);
|
||||
}
|
||||
else userAccount = nullAddress;
|
||||
console.log('ACCOUNT: ' + userAccount);
|
||||
console.log('CheckNetworkType');
|
||||
checkNetworkType();
|
||||
setUserAccount();
|
||||
getPlayerStatus();
|
||||
}
|
||||
|
||||
async function getAccount(type) {
|
||||
if (window.ethereum) { // for modern DApps browser
|
||||
try {
|
||||
await window.ethereum.request({ method: 'eth_requestAccounts' });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
if (window.web3){
|
||||
await window.web3.eth.getAccounts().then(it => {
|
||||
accountChange(it[0]);
|
||||
if (type == "REGISTER") registerPlayer();
|
||||
});
|
||||
}
|
||||
else {
|
||||
alert('No Metamask or Ethereum wallet found. Read "Getting Started" for more information');
|
||||
console.log('No Metamask or Ethereum wallet found. Read "Getting Started" for more information');
|
||||
}
|
||||
}
|
||||
|
||||
function register() {
|
||||
funcType = "REGISTER";
|
||||
getAccount(funcType);
|
||||
}
|
||||
|
||||
function accountEnable() {
|
||||
funcType = "NONE";
|
||||
getAccount(funcType);
|
||||
}
|
||||
|
||||
function setPlayers(data) {
|
||||
document.getElementById("players").textContent = data;
|
||||
}
|
||||
|
||||
function setSubmissions(data) {
|
||||
document.getElementById("submissions").textContent = data;
|
||||
}
|
||||
|
||||
function setExpiration(newExpiration) {
|
||||
if(! timerIsRunning) startTimer();
|
||||
expirationTime = newExpiration;
|
||||
updateClock();
|
||||
}
|
||||
|
||||
function setRank(data) {
|
||||
var text = "";
|
||||
text += "<tr><th>PlayerStatus</th><th>Name</th><th>Flags found</th><th>Points</th></tr>";
|
||||
|
||||
data.forEach(item => {
|
||||
console.log(item[2]);
|
||||
text += "<tr><td>" + item[0] + "</td><td>" + item[2] + "</td><td>" + item[3] + "</td><td>" + item[1] + "</td></tr>";
|
||||
});
|
||||
|
||||
document.getElementById("loading").style.display = "none";
|
||||
document.getElementById("rank_list").innerHTML = text;
|
||||
}
|
||||
|
||||
function registerPlayer() {
|
||||
var rtfm = 'I_read_it'; // todo: Checkbox check removed for easier testing
|
||||
var playerName = document.getElementById("playerName").value;
|
||||
myContract.methods.register(rtfm, playerName).send({from: userAccount})
|
||||
.then(function() {
|
||||
console.log("register()");
|
||||
rank();
|
||||
getPlayerStatus();
|
||||
getPlayers();
|
||||
});
|
||||
}
|
||||
|
||||
function isGameGoing() {
|
||||
myContract.methods.started().call()
|
||||
.then(function(result) {
|
||||
console.log("is going: " + result);
|
||||
return(result);
|
||||
});
|
||||
}
|
||||
|
||||
function startTimer() {
|
||||
mark = setInterval(updateClock, 1000);
|
||||
timerIsRunning = true;
|
||||
}
|
||||
|
||||
function stopTimer() {
|
||||
clearInterval(mark);
|
||||
timerIsRunning = false;
|
||||
}
|
||||
|
||||
function updateClock() {
|
||||
const remainingTime = expirationTime - Math.floor((new Date()).getTime() / 1000);
|
||||
|
||||
if(remainingTime > 0) {
|
||||
//var hour = Math.floor(remainingTime / (60 * 60)) % 24;
|
||||
var hour = Math.floor(remainingTime / (60 * 60));
|
||||
var min = Math.floor(remainingTime / 60) % 60;
|
||||
var sec = remainingTime % 60;
|
||||
if(hour < 10) hour = '0' + hour;
|
||||
if(min < 10) min = '0' + min;
|
||||
if(sec < 10) sec = '0' + sec;
|
||||
document.getElementById("remaining_time").textContent = hour + ':' + min + ':' + sec;
|
||||
}
|
||||
else gameOver();
|
||||
}
|
||||
|
||||
function gameOver() {
|
||||
stopTimer();
|
||||
document.getElementById("remaining_time").textContent = "--:--:--";
|
||||
if(isGameGoing()) {
|
||||
document.getElementById("remaining_time").textContent = "Game Over";
|
||||
}
|
||||
else {
|
||||
document.getElementById("remaining_time").textContent = "Not Started";
|
||||
}
|
||||
}
|
||||
|
||||
function expiration() {
|
||||
myContract.methods.endTime().call()
|
||||
.then(function(result) {
|
||||
console.log("expiration: " + result);
|
||||
setExpiration(result);
|
||||
});
|
||||
}
|
||||
|
||||
function getPlayers() {
|
||||
myContract.methods.playerCount().call()
|
||||
.then(function(result) {
|
||||
console.log("playerCount: " + result);
|
||||
setPlayers(result);
|
||||
});
|
||||
}
|
||||
|
||||
function submissions() {
|
||||
myContract.methods.getSuccessfulSubmissionCount().call()
|
||||
.then(function(result) {
|
||||
console.log("SuccessfulSubmissionCount: " + result);
|
||||
setSubmissions(result);
|
||||
});
|
||||
}
|
||||
|
||||
function rank() {
|
||||
myContract.methods.getPlayers().call()
|
||||
.then(function(result) {
|
||||
console.log("Players: " + result);
|
||||
setRank(result);
|
||||
});
|
||||
}
|
||||
|
||||
//===============================================================================================
|
||||
// Get the modal
|
||||
var modal = document.getElementById('myModal');
|
||||
|
||||
// Get the button that opens the modal
|
||||
var btn = document.getElementById("myBtn");
|
||||
|
||||
// Get the <span> element that closes the modal
|
||||
var span = document.getElementsByClassName("close")[0];
|
||||
|
||||
// When the user clicks the button, open the modal
|
||||
btn.onclick = function() {
|
||||
modal.style.display = "block";
|
||||
}
|
||||
|
||||
// When the user clicks on <span> (x), close the modal
|
||||
span.onclick = function() {
|
||||
modal.style.display = "none";
|
||||
}
|
||||
|
||||
// When the user clicks anywhere outside of the modal, close it
|
||||
window.onclick = function(event) {
|
||||
if (event.target == modal) {
|
||||
modal.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
//===============================================================================================
|
||||
var myContract;
|
||||
myContract = new web3.eth.Contract(myABI, contractAddress);
|
||||
|
||||
myContract.events.PlayerStatusChanged()
|
||||
.on("data", function(event) {
|
||||
var data = event.returnValues;
|
||||
console.log("PlayerStatusChanged: " + data);
|
||||
rank();
|
||||
})
|
||||
.on("error", function(error) {
|
||||
var text = error;
|
||||
console.log("PlayerStatusChanged: " + text);
|
||||
})
|
||||
.on("error", console.error);
|
||||
|
||||
expiration();
|
||||
getPlayers();
|
||||
submissions();
|
||||
rank();
|
||||
});
|
|
@ -0,0 +1,360 @@
|
|||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.top {
|
||||
background: #45484d;
|
||||
background: linear-gradient(to right, #000000 220px,#2D3436 50%,#2D3436 calc(50% + 220px),#000000 100%);
|
||||
position: absolute;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
.top_logo {
|
||||
width: 220px;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
font-size: 32px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.top_left {
|
||||
width: calc(50% - 205px);
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.top_center {
|
||||
width: 190px;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.top_right {
|
||||
width: calc(50% - 205px);
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.left {
|
||||
background-color: #000000;
|
||||
position: absolute;
|
||||
left: 0; top: 70px; bottom: 0;
|
||||
width: 220px;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* IE 10+ */
|
||||
}
|
||||
|
||||
.left::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.main {
|
||||
background: #bcbfbe;
|
||||
background: radial-gradient(ellipse at center, #2D3436 0%,#000000 100%);
|
||||
position: absolute;
|
||||
left: 220px; top: 70px; right: 0; bottom: 0;
|
||||
}
|
||||
|
||||
.left .activated {
|
||||
border: 1px solid #2FA232;
|
||||
background: #FFFFFF;
|
||||
color: #2FA232;
|
||||
}
|
||||
|
||||
.topitem {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#playerName {
|
||||
border: 0;
|
||||
border-radius: 0.25rem;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
line-height: 1.5;
|
||||
white-space: nowrap;
|
||||
text-decoration: none;
|
||||
margin: 10px 0;
|
||||
padding: 0.25rem 0.5rem;
|
||||
width: 93%;
|
||||
}
|
||||
|
||||
#register {
|
||||
border: 0;
|
||||
border-radius: 0.25rem;
|
||||
color: #FFFFFF;
|
||||
background: #2FA232;
|
||||
background: linear-gradient(225deg, #166D3B 0%, #2FA232 100%);
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
white-space: nowrap;
|
||||
text-decoration: none;
|
||||
margin: 10px 0;
|
||||
padding: 0.25rem 0.5rem;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#register:hover {
|
||||
background: #2FA232;
|
||||
background: linear-gradient(45deg, #166D3B 0%, #2FA232 100%);
|
||||
}
|
||||
|
||||
.left-content {
|
||||
margin: 14px;
|
||||
}
|
||||
|
||||
#header_uppercase {
|
||||
font-size: 13px;
|
||||
color: #CCCCCC;
|
||||
font-family: monospace;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#account_enable {
|
||||
border: 1px solid #dcdcdc;
|
||||
border-radius: 0.25rem;
|
||||
background: #f4f6f7;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
font-size: 1rem;
|
||||
white-space: nowrap;
|
||||
text-decoration: none;
|
||||
padding: 0.25rem 0.5rem;
|
||||
margin: 10px 0;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#account_enable:hover {
|
||||
background: #fafafb;
|
||||
border-color: #2FA232;
|
||||
color: #2FA232;
|
||||
}
|
||||
|
||||
#metamask {
|
||||
width: 20px;
|
||||
vertical-align: text-top;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* The Modal (background) */
|
||||
.modal {
|
||||
display: none; /* Hidden by default */
|
||||
position: fixed; /* Stay in place */
|
||||
z-index: 1; /* Sit on top */
|
||||
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%; /* Full width */
|
||||
height: 100%; /* Full height */
|
||||
overflow: auto; /* Enable scroll if needed */
|
||||
background-color: rgb(0,0,0); /* Fallback color */
|
||||
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
|
||||
}
|
||||
|
||||
/* Modal Content */
|
||||
.modal-content {
|
||||
position: relative;
|
||||
background-color: #000000;
|
||||
margin: 70px auto;
|
||||
padding: 0;
|
||||
max-width: 800px;
|
||||
border: 3px solid #2FA232;
|
||||
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);
|
||||
-webkit-animation-name: animatetop;
|
||||
-webkit-animation-duration: 0.4s;
|
||||
animation-name: animatetop;
|
||||
animation-duration: 0.4s
|
||||
}
|
||||
|
||||
/* Add Animation */
|
||||
@-webkit-keyframes animatetop {
|
||||
from {top:-300px; opacity:0}
|
||||
to {top:0; opacity:1}
|
||||
}
|
||||
|
||||
@keyframes animatetop {
|
||||
from {top:-300px; opacity:0}
|
||||
to {top:0; opacity:1}
|
||||
}
|
||||
|
||||
/* The Close Button */
|
||||
.close {
|
||||
float: right;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.close:hover,
|
||||
.close:focus {
|
||||
color: #BBBBBB;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
padding: 1px 12px 1px 24px;
|
||||
}
|
||||
|
||||
.modal-body {padding: 2px 16px;}
|
||||
|
||||
.modal-footer {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.top a:link, .top a:visited, .top a:hover, .top a:active {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
hr {
|
||||
display: block;
|
||||
height: 1px;
|
||||
border: 0;
|
||||
border-top: 1px solid #d8dadb;
|
||||
margin-top: 1em;
|
||||
padding: 0;
|
||||
}
|
||||
.buttons {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.buttons i {
|
||||
font-size: 24px;
|
||||
margin: 2px 0;
|
||||
background-color: #2FA232;
|
||||
background-image: linear-gradient(45deg, #8929AD 0%, #2FA232 50%, #43B7B8 100%);
|
||||
background-size: 100%;
|
||||
-webkit-background-clip: text;
|
||||
-moz-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
-moz-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
li {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.form-switch {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.form-switch i {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 46px;
|
||||
height: 26px;
|
||||
background-color: #e6e6e6;
|
||||
border-radius: 23px;
|
||||
vertical-align: text-bottom;
|
||||
transition: all 0.3s linear;
|
||||
}
|
||||
|
||||
.form-switch i::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 42px;
|
||||
height: 22px;
|
||||
background-color: #fff;
|
||||
border-radius: 11px;
|
||||
transform: translate3d(2px, 2px, 0) scale3d(1, 1, 1);
|
||||
transition: all 0.25s linear;
|
||||
}
|
||||
|
||||
.form-switch i::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
background-color: #fff;
|
||||
border-radius: 11px;
|
||||
box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.6);
|
||||
transform: translate3d(2px, 2px, 0);
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.form-switch:active i::after {
|
||||
width: 28px;
|
||||
transform: translate3d(2px, 2px, 0);
|
||||
}
|
||||
|
||||
.form-switch:active input:checked + i::after { transform: translate3d(16px, 2px, 0); }
|
||||
|
||||
.form-switch input { display: none; }
|
||||
|
||||
.form-switch input:checked + i { background-color: #2FA232; }
|
||||
|
||||
.form-switch input:checked + i::before { transform: translate3d(18px, 2px, 0) scale3d(0, 0, 0); }
|
||||
|
||||
.form-switch input:checked + i::after { transform: translate3d(22px, 2px, 0); }
|
||||
|
||||
h1 {
|
||||
font-size: 16px;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 0;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
color: #2FA232;
|
||||
}
|
||||
|
||||
#loading {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
-ms-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
text-align: center;
|
||||
text-shadow: 2px 2px 4px #FFFFFF;
|
||||
}
|
||||
|
||||
.dropdown-p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#rank_list {
|
||||
width: 90%;
|
||||
border-collapse: collapse;
|
||||
border-style: hidden;
|
||||
text-align: center;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
#rank_list th {
|
||||
border: 1px solid #5f5f5f;
|
||||
padding: 8px;
|
||||
min-width: 40px;
|
||||
}
|
||||
|
||||
#rank_list td {
|
||||
border: 1px solid #5f5f5f;
|
||||
padding: 8px;
|
||||
min-width: 40px;
|
||||
}
|
||||
|
||||
.center {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
#rules {
|
||||
color: #cccccc;
|
||||
font-size: 14px;
|
||||
}
|
Loading…
Reference in New Issue