aere-contracts/contracts/season1/AereQuestRegistry.sol
Aere Network a13a649b77
Some checks are pending
contracts-ci / Install (lockfile) → compile → full test suite (push) Waiting to run
contracts-ci / PQC known-answer tests (NIST vectors) (push) Waiting to run
contracts-ci / Coverage (scoped, with artifacts) (push) Waiting to run
Initial public release
Aere Network public source. Everything here can be checked against the live
chain (chain id 2800, https://rpc.aere.network).

Scope note, stated up front rather than buried: consensus on chain 2800 is
classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at
the signature, precompile, account and transport layers. Nothing here makes the
consensus post-quantum, and no document in it should be read as claiming so.
2026-07-20 01:02:37 +03:00

130 lines
4.4 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "@openzeppelin/contracts/access/Ownable.sol";
import "./interfaces/ISeason1.sol";
/**
* @title AereQuestRegistry — AERE Season 1 "Altitude" quest catalogue
* @notice Foundation-owned registry describing every quest: its point weight,
* active window, per-user cap, active flag, and whether it requires a
* verified human. Fully TUNABLE without redeploying the ledger or the
* attestor — those read quest config live from here.
*
* A quest is keyed by a bytes32 questId (e.g. keccak256("create-passkey")).
*
* This contract holds NO funds and issues NO credits. It is pure config that
* the attestor and referral registry consult. Owner = Foundation multisig.
*/
contract AereQuestRegistry is Ownable, IAereQuestRegistry {
mapping(bytes32 => Quest) private _quests;
bytes32[] public questIds;
event QuestCreated(
bytes32 indexed questId,
uint256 weight,
uint64 startTime,
uint64 endTime,
uint256 perUserCap,
bool requiresVerifiedHuman
);
event QuestUpdated(
bytes32 indexed questId,
uint256 weight,
uint64 startTime,
uint64 endTime,
uint256 perUserCap,
bool requiresVerifiedHuman
);
event QuestActiveSet(bytes32 indexed questId, bool active);
error QuestExists();
error UnknownQuest();
error BadWindow();
error BadWeight();
/* ------------------------------- mutations ------------------------------ */
function createQuest(
bytes32 questId,
uint256 weight,
uint64 startTime,
uint64 endTime,
uint256 perUserCap,
bool requiresVerifiedHuman
) external onlyOwner {
if (questId == bytes32(0)) revert UnknownQuest();
if (_quests[questId].exists) revert QuestExists();
_validate(weight, startTime, endTime, perUserCap);
_quests[questId] = Quest({
weight: weight,
startTime: startTime,
endTime: endTime,
perUserCap: perUserCap,
active: true,
requiresVerifiedHuman: requiresVerifiedHuman,
exists: true
});
questIds.push(questId);
emit QuestCreated(questId, weight, startTime, endTime, perUserCap, requiresVerifiedHuman);
}
function updateQuest(
bytes32 questId,
uint256 weight,
uint64 startTime,
uint64 endTime,
uint256 perUserCap,
bool requiresVerifiedHuman
) external onlyOwner {
Quest storage q = _quests[questId];
if (!q.exists) revert UnknownQuest();
_validate(weight, startTime, endTime, perUserCap);
q.weight = weight;
q.startTime = startTime;
q.endTime = endTime;
q.perUserCap = perUserCap;
q.requiresVerifiedHuman = requiresVerifiedHuman;
emit QuestUpdated(questId, weight, startTime, endTime, perUserCap, requiresVerifiedHuman);
}
function setQuestActive(bytes32 questId, bool active) external onlyOwner {
if (!_quests[questId].exists) revert UnknownQuest();
_quests[questId].active = active;
emit QuestActiveSet(questId, active);
}
function _validate(uint256 weight, uint64 startTime, uint64 endTime, uint256 perUserCap) internal pure {
if (weight == 0) revert BadWeight();
// perUserCap must admit at least one completion.
if (perUserCap < weight) revert BadWeight();
// endTime 0 == open-ended; otherwise must be after start.
if (endTime != 0 && endTime <= startTime) revert BadWindow();
}
/* --------------------------------- views -------------------------------- */
function getQuest(bytes32 questId) external view returns (Quest memory) {
Quest memory q = _quests[questId];
if (!q.exists) revert UnknownQuest();
return q;
}
/// @notice True iff the quest exists, is active, and `atTime` is within its
/// window. endTime == 0 means open-ended.
function isOpen(bytes32 questId, uint256 atTime) external view returns (bool) {
Quest memory q = _quests[questId];
if (!q.exists || !q.active) return false;
if (atTime < q.startTime) return false;
if (q.endTime != 0 && atTime > q.endTime) return false;
return true;
}
function questCount() external view returns (uint256) {
return questIds.length;
}
}