add one contract for airdrop reserve
This commit is contained in:
parent
f8e7069439
commit
b656d3ae3b
230
contracts/activity/AirdropReserve.sol
Normal file
230
contracts/activity/AirdropReserve.sol
Normal file
@ -0,0 +1,230 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.10;
|
||||
|
||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
|
||||
import "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2.sol";
|
||||
import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
|
||||
/**
|
||||
* @title AirdropReserve
|
||||
* @dev AirdropReserve is a contract for airdrop reserve.
|
||||
* It allows users to reserve airdrop and participate in the raffle.
|
||||
// TODO: Modify the logic of the executeRaffle method.
|
||||
*/
|
||||
contract AirdropReserve is Ownable, ReentrancyGuard, VRFConsumerBaseV2 {
|
||||
uint256 public reservePrice = 0.19527 ether;
|
||||
// 1714492800 -> 2024-05-01 00:00:00 UTC+8
|
||||
uint256 public priorityTime = 1714492800;
|
||||
|
||||
// reserver raffle weight before priorityTime
|
||||
uint256 public constant PRIORITY_WEIGHT = 9527;
|
||||
// reserver raffle weight after priorityTime
|
||||
uint256 public constant NORMAL_WEIGHT = 1000;
|
||||
|
||||
bool public isWhitelistReserveActive = false;
|
||||
bool public isPublicReserveActive = false;
|
||||
bool public isRefundActive = false;
|
||||
|
||||
address private immutable _VRFCoordinator;
|
||||
uint64 private _VRFSubscriptionId;
|
||||
uint256[] private _randomWords;
|
||||
|
||||
uint256 public totalReserved;
|
||||
uint256 private _totalWeight;
|
||||
uint256 private _priorityCount;
|
||||
address[] private _publicReservers;
|
||||
|
||||
// Don't tell me about using merkel tree to save gas
|
||||
// In order to prohibit whitelist addresses from participating in the public reserve,
|
||||
// all whitelist addresses need to be on-chain
|
||||
mapping(address => bool) public whitelist;
|
||||
mapping(address => bool) public reserved;
|
||||
mapping(address => bool) public raffleWon;
|
||||
mapping(address => bool) public refunded;
|
||||
|
||||
// ============ Events ============
|
||||
event WhitelistReserved(address indexed reserver);
|
||||
event PublicReserved(address indexed reserver);
|
||||
event RandomWordsRequested(uint256 requestId);
|
||||
event RandomWordsFulfilled(uint256 requestId, uint256[] randomWords);
|
||||
event RaffleWon(address indexed reserver);
|
||||
event Refunded(address indexed reserver);
|
||||
|
||||
// ============ Error ============
|
||||
error OnlyEOA();
|
||||
error RserveNotActive();
|
||||
error InvalidAddress();
|
||||
error InvalidValue();
|
||||
error AlreadyReserved();
|
||||
error InvalidRaffle();
|
||||
error RefundNotActive();
|
||||
error NotRefundable();
|
||||
error AlreadyRefund();
|
||||
error InsufficientBalance();
|
||||
error TransferFailed();
|
||||
|
||||
// ============ Modifier ============
|
||||
modifier onlyEOA() {
|
||||
if (msg.sender != tx.origin) revert OnlyEOA();
|
||||
_;
|
||||
}
|
||||
|
||||
// ============ Constructor ============
|
||||
constructor(address VRFCoordinator_, uint64 VRFSubscriptionId_) VRFConsumerBaseV2(VRFCoordinator_) {
|
||||
_VRFCoordinator = VRFCoordinator_;
|
||||
_VRFSubscriptionId = VRFSubscriptionId_;
|
||||
}
|
||||
|
||||
function setReservePrice(uint256 _reservePrice) external onlyOwner {
|
||||
reservePrice = _reservePrice;
|
||||
}
|
||||
|
||||
function setPriorityTime(uint256 _priorityTime) external onlyOwner {
|
||||
priorityTime = _priorityTime;
|
||||
}
|
||||
|
||||
function setIsWhitelistReserveActive(bool _isWhitelistReserveActive) external onlyOwner {
|
||||
isWhitelistReserveActive = _isWhitelistReserveActive;
|
||||
}
|
||||
|
||||
function setIsPublicReserveActive(bool _isPublicReserveActive) external onlyOwner {
|
||||
isPublicReserveActive = _isPublicReserveActive;
|
||||
}
|
||||
|
||||
function setIsRefundActive(bool _isRefundActive) external onlyOwner {
|
||||
isRefundActive = _isRefundActive;
|
||||
}
|
||||
|
||||
function setVRFSubscriptionId(uint64 VRFSubscriptionId_) external onlyOwner {
|
||||
_VRFSubscriptionId = VRFSubscriptionId_;
|
||||
}
|
||||
|
||||
function setWhitelist(address[] calldata addresses, bool status) external onlyOwner {
|
||||
for (uint256 i = 0; i < addresses.length; i++) {
|
||||
whitelist[addresses[i]] = status;
|
||||
}
|
||||
}
|
||||
|
||||
function withdraw(uint256 amount) external onlyOwner {
|
||||
if (amount == 0) {
|
||||
amount = address(this).balance;
|
||||
}
|
||||
_sendValue(payable(owner()), amount);
|
||||
}
|
||||
|
||||
function requestRaffleRandomWords(
|
||||
bytes32 keyHash,
|
||||
uint16 requestConfirmations,
|
||||
uint32 callbackGasLimit,
|
||||
uint32 numWords
|
||||
) external onlyOwner {
|
||||
uint256 requestId = VRFCoordinatorV2Interface(_VRFCoordinator).requestRandomWords(
|
||||
keyHash,
|
||||
_VRFSubscriptionId,
|
||||
requestConfirmations,
|
||||
callbackGasLimit,
|
||||
numWords
|
||||
);
|
||||
|
||||
emit RandomWordsRequested(requestId);
|
||||
}
|
||||
|
||||
function executeRaffle(uint256 raffleRound, uint256 raffleCount) external onlyOwner {
|
||||
if (raffleRound > _randomWords.length) revert InvalidRaffle();
|
||||
if (raffleCount > _publicReservers.length) revert InvalidRaffle();
|
||||
|
||||
uint256 raffleIndex = raffleRound - 1;
|
||||
uint256 randomWord = _randomWords[raffleIndex];
|
||||
uint256 priorityTotalWeight = _priorityCount * PRIORITY_WEIGHT;
|
||||
|
||||
while (raffleCount > 0) {
|
||||
uint256 index;
|
||||
uint256 randomWeight = uint256(keccak256(abi.encodePacked(randomWord))) % _totalWeight;
|
||||
|
||||
if (randomWeight <= priorityTotalWeight) {
|
||||
index = randomWeight / PRIORITY_WEIGHT;
|
||||
} else {
|
||||
index = _priorityCount + (randomWeight - priorityTotalWeight) / NORMAL_WEIGHT;
|
||||
}
|
||||
|
||||
address winner = _publicReservers[index];
|
||||
// select the next reserver as the winner if the current reserver has won
|
||||
while (raffleWon[winner]) {
|
||||
index = index < _publicReservers.length - 1 ? index + 1 : 0;
|
||||
winner = _publicReservers[index];
|
||||
}
|
||||
// set reserver as winner
|
||||
raffleWon[winner] = true;
|
||||
|
||||
unchecked {
|
||||
randomWord++;
|
||||
}
|
||||
raffleCount--;
|
||||
|
||||
emit RaffleWon(winner);
|
||||
}
|
||||
}
|
||||
|
||||
function whitelistReserve() external payable onlyEOA nonReentrant {
|
||||
if (!isWhitelistReserveActive) revert RserveNotActive();
|
||||
if (!whitelist[msg.sender]) revert InvalidAddress();
|
||||
if (reserved[msg.sender]) revert AlreadyReserved();
|
||||
if (msg.value != reservePrice) revert InvalidValue();
|
||||
|
||||
reserved[msg.sender] = true;
|
||||
totalReserved++;
|
||||
|
||||
emit WhitelistReserved(msg.sender);
|
||||
}
|
||||
|
||||
function publicReserve() external payable onlyEOA nonReentrant {
|
||||
if (!isPublicReserveActive) revert RserveNotActive();
|
||||
// whitelist address is not allowed to participate in the public reserve
|
||||
if (whitelist[msg.sender]) revert InvalidAddress();
|
||||
if (reserved[msg.sender]) revert AlreadyReserved();
|
||||
if (msg.value != reservePrice) revert InvalidValue();
|
||||
|
||||
reserved[msg.sender] = true;
|
||||
totalReserved++;
|
||||
_publicReservers.push(msg.sender);
|
||||
|
||||
if (block.timestamp < priorityTime) {
|
||||
_totalWeight += PRIORITY_WEIGHT;
|
||||
_priorityCount++;
|
||||
} else {
|
||||
_totalWeight += NORMAL_WEIGHT;
|
||||
}
|
||||
|
||||
emit PublicReserved(msg.sender);
|
||||
}
|
||||
|
||||
function refund() external onlyEOA nonReentrant {
|
||||
if (!isRefundActive) revert RefundNotActive();
|
||||
if (!reserved[msg.sender]) revert NotRefundable();
|
||||
// whitelist address is not allowed to refund
|
||||
// only address participate in the public reserve could refund
|
||||
if (whitelist[msg.sender]) revert NotRefundable();
|
||||
// raffle winner is not allowed to refund
|
||||
if (raffleWon[msg.sender]) revert NotRefundable();
|
||||
if (refunded[msg.sender]) revert AlreadyRefund();
|
||||
|
||||
refunded[msg.sender] = true;
|
||||
|
||||
_sendValue(payable(msg.sender), reservePrice);
|
||||
|
||||
emit Refunded(msg.sender);
|
||||
}
|
||||
|
||||
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
|
||||
_randomWords = randomWords;
|
||||
|
||||
emit RandomWordsFulfilled(requestId, randomWords);
|
||||
}
|
||||
|
||||
function _sendValue(address payable recipient, uint256 amount) private {
|
||||
if (address(this).balance < amount) revert InsufficientBalance();
|
||||
|
||||
(bool success, ) = recipient.call{value: amount}("");
|
||||
if (!success) revert TransferFailed();
|
||||
}
|
||||
}
|
2299
package-lock.json
generated
2299
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -41,6 +41,7 @@
|
||||
"truffle-plugin-verify": "^0.5.25"
|
||||
},
|
||||
"dependencies": {
|
||||
"@chainlink/contracts": "^0.8.0",
|
||||
"@openzeppelin/contracts": "4.9.2",
|
||||
"fs-jetpack": "^5.1.0"
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user