Test deploy on Moonbase Alpha
parent
066a1d9ddb
commit
8e122386b6
|
@ -1,3 +1,7 @@
|
||||||
# DCTF
|
# DCTF Beta
|
||||||
|
|
||||||
Decentralized CTF engine, used by CCTF
|
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