编译器
0.8.28+commit.7893614a
文件 1 的 7:BasePool.sol
pragma solidity 0.8.28;
import {IEntropy} from "@pythnetwork/entropy-sdk-solidity/IEntropy.sol";
import {IEntropyConsumer} from "@pythnetwork/entropy-sdk-solidity/IEntropyConsumer.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
struct Pool {
uint256 nextNumberToAssign;
mapping(uint256 explicitNumber => address participant) explicitNumbers;
}
contract BasePool is IEntropyConsumer, Ownable {
error InvalidAmount();
error TransferFailed();
error TargetExceeded();
error EmergencyWithdrawNotYetRequested();
error EmergencyWithdrawNotYetAvailable();
event Participation(
uint256 indexed poolId,
address indexed participant,
uint256 startNumber,
uint256 endNumber
);
event ConquerorSelected(
uint256 indexed poolId,
address indexed conqueror,
uint256 amount
);
event EmergencyWithdrawRequested(uint256 emergencyWithdrawTimestamp);
IEntropy public constant ENTROPY = IEntropy(0x6E7D74FA7d5c90FEF9F0512987605a6d546181Bb);
address public constant PROVIDER = 0x52DeaA1c84233F7bb8C8A45baeDE41091c616506;
mapping(uint256 poolId => Pool pool) public pools;
mapping(uint256 poolId => uint256) public conqueredNumbers;
uint256 public currentPoolId;
uint256 public emergencyWithdrawTimestamp;
address private immutable FEE_WALLET;
uint256 private constant ENTRY_COST = 0.0005 ether;
uint256 private constant POOL_TARGET = 0.5 ether;
uint256 public constant FEE_RATE = 50;
constructor() Ownable(msg.sender) {
FEE_WALLET = msg.sender;
}
receive() external payable {
_enter();
}
function requestEmergencyWithdraw() external onlyOwner {
emergencyWithdrawTimestamp = block.timestamp + 365 days;
emit EmergencyWithdrawRequested(emergencyWithdrawTimestamp);
}
function fulfillEmergencyWithdraw() external onlyOwner {
if (emergencyWithdrawTimestamp == 0) revert EmergencyWithdrawNotYetRequested();
if (block.timestamp < emergencyWithdrawTimestamp) revert EmergencyWithdrawNotYetAvailable();
(bool success,) = msg.sender.call{value: address(this).balance}("");
if (!success) revert TransferFailed();
}
function entropyCallback(uint64, address, bytes32 randomNumber)
internal
override
{
if (address(this).balance < POOL_TARGET) return;
uint256 random = uint256(randomNumber);
uint256 selectedNumber = random % pools[currentPoolId].nextNumberToAssign;
conqueredNumbers[currentPoolId] = selectedNumber;
address participant = _findParticipant(selectedNumber);
if (participant == address(0)) return;
uint256 totalAmount = address(this).balance;
uint256 commission = (totalAmount * FEE_RATE) / 1000;
uint256 prize = totalAmount - commission;
(bool successConqueror,) = participant.call{value: prize}("");
if (!successConqueror) revert TransferFailed();
(bool successFee,) = FEE_WALLET.call{value: commission}("");
if (!successFee) revert TransferFailed();
emit ConquerorSelected(currentPoolId, participant, prize);
emergencyWithdrawTimestamp = 0;
unchecked {
++currentPoolId;
}
}
function _enter() internal {
if (msg.value == 0 || msg.value % ENTRY_COST != 0) revert InvalidAmount();
uint256 numNumbers = msg.value / ENTRY_COST;
_assignNumber(numNumbers);
if (address(this).balance >= POOL_TARGET) {
bytes32 pseudoRandomNumber =
keccak256(abi.encode(block.timestamp, block.number, msg.sender, address(this).balance));
uint128 requestFee = ENTROPY.getFee(PROVIDER);
ENTROPY.requestWithCallback{value: requestFee}(PROVIDER, pseudoRandomNumber);
}
}
function getEntropy() internal pure override returns (address) {
return address(ENTROPY);
}
function _assignNumber(uint256 numNumbers) internal {
uint256 startNumber = pools[currentPoolId].nextNumberToAssign;
for (uint256 i = 0; i < numNumbers; i++) {
pools[currentPoolId].explicitNumbers[startNumber + i] = msg.sender;
}
uint256 nextNumberToAssign = startNumber + numNumbers;
pools[currentPoolId].nextNumberToAssign = nextNumberToAssign;
emit Participation(currentPoolId, msg.sender, startNumber, nextNumberToAssign - 1);
}
function _findParticipant(uint256 number) internal view returns (address) {
for (uint256 i = number; i >= 0; --i) {
address participant = pools[currentPoolId].explicitNumbers[i];
if (participant != address(0)) {
return participant;
}
if (i == 0) {
return address(0);
}
}
return address(0);
}
function getPoolStatus() external view returns (
uint256 poolId,
uint256 totalNumbers,
uint256 currentBalance,
uint256 threshold
) {
return (
currentPoolId,
pools[currentPoolId].nextNumberToAssign,
address(this).balance,
POOL_TARGET
);
}
function getParticipantNumbers(address participant) external view returns (uint256[] memory) {
uint256[] memory numbers = new uint256[](pools[currentPoolId].nextNumberToAssign);
uint256 count = 0;
for (uint256 i = 0; i < pools[currentPoolId].nextNumberToAssign; i++) {
if (pools[currentPoolId].explicitNumbers[i] == participant) {
numbers[count] = i;
count++;
}
}
uint256[] memory result = new uint256[](count);
for (uint256 i = 0; i < count; i++) {
result[i] = numbers[i];
}
return result;
}
function getParticipantNumberCount(address participant) external view returns (uint256) {
uint256 count = 0;
Pool storage pool = pools[currentPoolId];
for (uint256 i = 0; i < pool.nextNumberToAssign; i++) {
if (pool.explicitNumbers[i] == participant) {
count++;
}
}
return count;
}
function getConqueredNumber(uint256 poolId) external view returns (uint256) {
return conqueredNumbers[poolId];
}
function getAllConqueredNumbers() external view returns (uint256[] memory) {
uint256[] memory numbers = new uint256[](currentPoolId);
for (uint256 i = 0; i < currentPoolId; i++) {
numbers[i] = conqueredNumbers[i];
}
return numbers;
}
}
文件 2 的 7:Context.sol
pragma solidity ^0.8.20;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
文件 3 的 7:EntropyEvents.sol
pragma solidity ^0.8.0;
import "./EntropyStructs.sol";
interface EntropyEvents {
event Registered(EntropyStructs.ProviderInfo provider);
event Requested(EntropyStructs.Request request);
event RequestedWithCallback(
address indexed provider,
address indexed requestor,
uint64 indexed sequenceNumber,
bytes32 userRandomNumber,
EntropyStructs.Request request
);
event Revealed(
EntropyStructs.Request request,
bytes32 userRevelation,
bytes32 providerRevelation,
bytes32 blockHash,
bytes32 randomNumber
);
event RevealedWithCallback(
EntropyStructs.Request request,
bytes32 userRandomNumber,
bytes32 providerRevelation,
bytes32 randomNumber
);
event ProviderFeeUpdated(address provider, uint128 oldFee, uint128 newFee);
event ProviderUriUpdated(address provider, bytes oldUri, bytes newUri);
event ProviderFeeManagerUpdated(
address provider,
address oldFeeManager,
address newFeeManager
);
event Withdrawal(
address provider,
address recipient,
uint128 withdrawnAmount
);
}
文件 4 的 7:EntropyStructs.sol
pragma solidity ^0.8.0;
contract EntropyStructs {
struct ProviderInfo {
uint128 feeInWei;
uint128 accruedFeesInWei;
bytes32 originalCommitment;
uint64 originalCommitmentSequenceNumber;
bytes commitmentMetadata;
bytes uri;
uint64 endSequenceNumber;
uint64 sequenceNumber;
bytes32 currentCommitment;
uint64 currentCommitmentSequenceNumber;
address feeManager;
}
struct Request {
address provider;
uint64 sequenceNumber;
uint32 numHashes;
bytes32 commitment;
uint64 blockNumber;
address requester;
bool useBlockhash;
bool isRequestWithCallback;
}
}
文件 5 的 7:IEntropy.sol
pragma solidity ^0.8.0;
import "./EntropyEvents.sol";
interface IEntropy is EntropyEvents {
function register(
uint128 feeInWei,
bytes32 commitment,
bytes calldata commitmentMetadata,
uint64 chainLength,
bytes calldata uri
) external;
function withdraw(uint128 amount) external;
function withdrawAsFeeManager(address provider, uint128 amount) external;
function request(
address provider,
bytes32 userCommitment,
bool useBlockHash
) external payable returns (uint64 assignedSequenceNumber);
function requestWithCallback(
address provider,
bytes32 userRandomNumber
) external payable returns (uint64 assignedSequenceNumber);
function reveal(
address provider,
uint64 sequenceNumber,
bytes32 userRevelation,
bytes32 providerRevelation
) external returns (bytes32 randomNumber);
function revealWithCallback(
address provider,
uint64 sequenceNumber,
bytes32 userRandomNumber,
bytes32 providerRevelation
) external;
function getProviderInfo(
address provider
) external view returns (EntropyStructs.ProviderInfo memory info);
function getDefaultProvider() external view returns (address provider);
function getRequest(
address provider,
uint64 sequenceNumber
) external view returns (EntropyStructs.Request memory req);
function getFee(address provider) external view returns (uint128 feeAmount);
function getAccruedPythFees()
external
view
returns (uint128 accruedPythFeesInWei);
function setProviderFee(uint128 newFeeInWei) external;
function setProviderFeeAsFeeManager(
address provider,
uint128 newFeeInWei
) external;
function setProviderUri(bytes calldata newUri) external;
function setFeeManager(address manager) external;
function constructUserCommitment(
bytes32 userRandomness
) external pure returns (bytes32 userCommitment);
function combineRandomValues(
bytes32 userRandomness,
bytes32 providerRandomness,
bytes32 blockHash
) external pure returns (bytes32 combinedRandomness);
}
文件 6 的 7:IEntropyConsumer.sol
pragma solidity ^0.8.0;
abstract contract IEntropyConsumer {
function _entropyCallback(
uint64 sequence,
address provider,
bytes32 randomNumber
) external {
address entropy = getEntropy();
require(entropy != address(0), "Entropy address not set");
require(msg.sender == entropy, "Only Entropy can call this function");
entropyCallback(sequence, provider, randomNumber);
}
function getEntropy() internal view virtual returns (address);
function entropyCallback(
uint64 sequence,
address provider,
bytes32 randomNumber
) internal virtual;
}
文件 7 的 7:Ownable.sol
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
{
"compilationTarget": {
"contracts/BasePool/BasePool.sol": "BasePool"
},
"evmVersion": "cancun",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"EmergencyWithdrawNotYetAvailable","type":"error"},{"inputs":[],"name":"EmergencyWithdrawNotYetRequested","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"TargetExceeded","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"poolId","type":"uint256"},{"indexed":true,"internalType":"address","name":"conqueror","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ConquerorSelected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"emergencyWithdrawTimestamp","type":"uint256"}],"name":"EmergencyWithdrawRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"poolId","type":"uint256"},{"indexed":true,"internalType":"address","name":"participant","type":"address"},{"indexed":false,"internalType":"uint256","name":"startNumber","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endNumber","type":"uint256"}],"name":"Participation","type":"event"},{"inputs":[],"name":"ENTROPY","outputs":[{"internalType":"contract IEntropy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_RATE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PROVIDER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"sequence","type":"uint64"},{"internalType":"address","name":"provider","type":"address"},{"internalType":"bytes32","name":"randomNumber","type":"bytes32"}],"name":"_entropyCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"conqueredNumbers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentPoolId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencyWithdrawTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fulfillEmergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAllConqueredNumbers","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"getConqueredNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"participant","type":"address"}],"name":"getParticipantNumberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"participant","type":"address"}],"name":"getParticipantNumbers","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPoolStatus","outputs":[{"internalType":"uint256","name":"poolId","type":"uint256"},{"internalType":"uint256","name":"totalNumbers","type":"uint256"},{"internalType":"uint256","name":"currentBalance","type":"uint256"},{"internalType":"uint256","name":"threshold","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"pools","outputs":[{"internalType":"uint256","name":"nextNumberToAssign","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requestEmergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]