Question on EntropyCallback

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.

  1. Is the way I generated the users random number okay? Or should that be passed in from the frontend?

  2. 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);
    }
2 Likes

Congratulations on being the first to kick off this forum!

  1. You’re currently generating a random number on-chain, which makes the random number deterministic and potentially predictable. While the final random number will be generated by the provider and won’t be deterministic, we recommend generating randomness off-chain whenever possible for better security.

  2. Yes, you’re using Entropy correctly. I suggest you thoroughly gas profile the entropyCallback method — under no circumstances should it exceed 500k gas. Please refer to this section in our docs for best practices.

2 Likes

thank you!

  1. That’s great to know, will change it to an input from the frontend.

  2. Are there any good ways to do this? If it’s working on testnet and it has the same callback gas limit as mainnet then it should be okay?

There are alot of ways to do it. If you are testing locally, use the foundry gas tracking.
Testnet and mainnet gas should be same. I would recommend you to test the gas limits on every possible flow of the callback method.

1 Like