Hello, I just have a few questions as its my first time using pyth entropy.
I’m building a slot machine plugin on Berachain that accumulates oBERO and BGT as its prize pool.
Players pay 0.1 BERA to spin the slot machine.
A single random output decides which payout tier they hit, giving them a portion of the accumulated oBERO and BGT according to these probabilities:
• 1% → 30% of the pool
• 2% → 15%
• 5% → 5%
• 42% → 0.5%
• 50% → no reward
The amount of oBERO and BGT in the contract will constantly be going up cause its being fed directly from Beradrome Incentives and Berachain POL.
-
Is the way I generated the users random number okay? Or should that be passed in from the frontend?
-
Am I using the entropy callback correctly? The payout is based on the whole pool’s balance so the order in which rewards are paid out is important, is there anyway this can be gamed as it is rn?
Here’s the code snippet for reference:
function play() external payable nonReentrant {
if (!initialized) revert Plugin__NotInitialized();
if (msg.value < playPrice) revert Plugin__InsufficientCost();
bytes32 userRandomNumber = keccak256(abi.encodePacked(block.timestamp, block.prevrandao, msg.sender));
if (address(entropy) != address(0)) {
address entropyProvider = entropy.getDefaultProvider();
uint256 fee = entropy.getFee(entropyProvider);
if (msg.value < playPrice + fee) revert Plugin__InsufficientFee();
uint64 sequenceNumber = entropy.requestWithCallback{value: fee}(entropyProvider, userRandomNumber);
pendingPlays[sequenceNumber] = msg.sender;
emit Plugin__SpinRequest(sequenceNumber, msg.sender);
} else {
mockCallback(msg.sender, userRandomNumber);
emit Plugin__SpinRequest(0, msg.sender);
}
IGauge(gauge).getReward(address(this));
uint256 bgtReward = IRewardVault(rewardVault).getReward(address(this), address(this));
if (bgtReward > 0) {
IBGT(BGT).redeem(address(this), bgtReward);
IWBERA(address(token)).deposit{value: bgtReward}();
}
}
function entropyCallback(
uint64 sequenceNumber,
address,
bytes32 randomNumber
) internal override {
address player = pendingPlays[sequenceNumber];
if (player == address(0)) revert Plugin__InvalidSequence();
uint256 randomValue = uint256(randomNumber) % 100;
uint256 oTokenBalance = IERC20(OTOKEN).balanceOf(address(this));
uint256 tokenBalance = IERC20(address(token)).balanceOf(address(this));
uint256 oTokenReward = 0;
uint256 tokenReward = 0;
// Calculate reward based on probability
if (randomValue < 1) { // 1% chance
oTokenReward = (oTokenBalance * 30) / 100;
tokenReward = (tokenBalance * 30) / 100;
} else if (randomValue < 3) { // 2% chance
oTokenReward = (oTokenBalance * 15) / 100;
tokenReward = (tokenBalance * 15) / 100;
} else if (randomValue < 8) { // 5% chance
oTokenReward = (oTokenBalance * 5) / 100;
tokenReward = (tokenBalance * 5) / 100;
} else if (randomValue < 50) { // 42% chance
oTokenReward = (oTokenBalance * 5) / 1000; // 0.5%
tokenReward = (tokenBalance * 5) / 1000; // 0.5%
}
// Transfer reward if won
if (oTokenReward > 0) {
IERC20(OTOKEN).safeTransfer(player, oTokenReward);
}
if (tokenReward > 0) {
IERC20(address(token)).safeTransfer(player, tokenReward);
}
delete pendingPlays[sequenceNumber];
emit Plugin__SpinResult(sequenceNumber, player, oTokenReward, tokenReward);
}