PancakeSwap
Launch App
English
English
  • Welcome to PancakeSwap
    • Product Overview
    • ✨How-to Guides
      • Get Started (BSC)
        • Create a Wallet
        • Get BEP20 Tokens
        • Using PancakeSwap without Centralized Exchanges
        • Connect Your Wallet to PancakeSwap
      • Get Started (Aptos)
        • Create a Wallet
        • Get Aptos Coins
        • CAKE Bridging Guide
        • Connect Your Wallet to PancakeSwap
        • Aptos FAQ
      • V3/V2 Migration
        • How to migrate
        • How v3 APR is calculated
        • FAQ
        • V2 Migration
          • Migrate Your Stakings
          • MasterChef v2
            • List of Farms
          • CAKE Syrup Pool
    • 🗺️Roadmap
    • veCAKE Sunset
      • Staked Cake Redemption
      • 🔷[Archive] veCAKE
        • What is veCAKE?
        • How to get veCAKE?
        • Migrate from CAKE Pool
        • veCAKE Managers
        • Bridge your veCAKE
        • Earn CAKE Weekly
          • FAQ
    • 📈Analytics (Info Page)
    • ✅Audits
    • 🤙Contact Us
      • Social Accounts
      • Telegram & Discord Communities
      • 💼Business Partnerships
        • ⛽veCAKE / Gauges
        • 🧑‍🌾Initial Farm Offerings - IFOs
        • 🥞Syrup Pools and Farms
        • 🖼️NFT Marketplace
        • ❓Commonly Asked Business Partnerships Questions
      • Ambassador Program
        • What can you do as an Ambassador?
        • Benefits of a PancakeSwap Ambassador
        • Want to be an Ambassador?
        • ❓ FAQs Page
      • General FAQ
        • I need help
        • Troubleshooting Errors
        • What is an Approval Transaction?
        • Fixing Stuck Pending Transactions on MetaMask
    • 👨‍👨‍👦About Us
      • Team
        • The Kitchen Team
        • Become a Chef
          • Brand / Graphics Designer
          • Motion / Interaction Designer
          • Social Media & Content Manager
          • Developer Relations Manager
          • Product Designer (UX/I)
          • Business Development Manager
          • Senior Backend Engineer
      • Brand & Logos
  • 👴Archive
    • 📂Legacy Products
      • 🖼️[Archived] NFT Marketplace
      • 🎮[Archived] Gaming Platform
        • 📒Brand Kit
        • 🎓Pancake Mayor
        • 🛡️Pancake Protectors
          • FAQ
      • 🍯[Archived] Pottery
        • How to Play Pottery
        • Pottery FAQ
      • 🧈[Archived] Trading Reward
        • How to Participate
          • Top Traders Campaign
          • CAKE Stakers Campaign
        • FAQ
      • 🤝[Archived] Simple Staking
        • How to start Simple Staking
        • FAQ
        • Terms & Conditions
      • 🎯[Archived] PancakeSwap Quest
        • Getting Started
        • How to Join and Complete a Quest
        • Rewards
      • 💧[Archived] Liquid Staking Integration
        • How To Liquid Stake
        • WBETH
          • WBETH FAQ
        • SnBNB
          • SnBNB FAQ
      • 💸[Archived] Options
        • Options Primer
        • CLAMM Options
        • Options FAQ
      • [Archived] CAKE Syrup Pool
        • How to use the Flexible Staking option
        • How to use the Fixed-Term Staking option
    • Old Tokenomics
      • CAKE Tokenomics v1
      • Controlling CAKE supply v1
    • How to Use Farms with BscScan
    • [Archived] Affiliate Program
      • Dashboard & Link
      • Commission Structure and Payment
      • Program Rules & Guidelines
      • Terms & Conditions
  • Trade
    • 🔄Swap
      • Token Swaps
      • How to Trade
      • Fees and Routes
      • Market Maker Integration
      • Smart Router (V2)
        • How to trade using Smart Router
    • ♾️PancakeSwap Infinity
      • Key Features
      • Hooks
        • Dynamic Fee Hook
      • Pool Types
      • Farms
    • 🎯Limit Orders and Twap
      • How to use Limit Orders
      • How to use TWAP
    • 📈Perpetual Trading
      • What is Perpetual Trading?
      • Perpetual Trading V2
        • How can I use it?
        • Dumb Mode
          • Dumb Mode Guide
        • Degen Mode
          • Degen Mode Dynamic Fee
        • Perpetuals V2 Glossary
        • Forex
        • Perpetual Trading FAQ
        • Supported Chains, Modes & Markets
      • Perpetual Trading V1
        • Perpetuals V1 Glossary
        • How can I use it?
        • V1 Perpetual Trading FAQ
    • 🏦Stableswap
      • How to StableSwap
    • 💳Buy Crypto
    • ❓Trading FAQ
      • Swap FAQ
      • v4 FAQ
      • Limit Orders and TWAP FAQ
  • Bridge
    • 🌉Bridging
      • Between EVMs
      • Between EVM and Aptos
      • Wormhole Bridge Guide
    • ❓Bridging FAQ
      • Wormhole Bridge FAQ
  • Earn
    • 🌊Liquidity Pools
      • How to Add/Remove Liquidity
      • ⛏️Pancake Picks
      • Zap
        • Zap (V2)
    • 🚜Yield Farming
      • How to Use Farms
        • How to Use Farms (Legacy)
      • APR/ROI/IL Calculator
      • Farming on Aptos
        • FAQ
      • Crosschain Farming
        • FAQ
    • 🥩Cake Staking
      • 🍯Syrup Pools - Stake CAKE
        • Syrup Pools
    • 🛍️IFO (Initial Farm Offering)
      • How to Participate in an IFO
      • How to Participate in CrossChain IFOs
      • FAQ
      • NFT Profile System
        • How to Set Up an NFT Profile
        • Teams
      • Contract Details
    • ❓Earn FAQ
      • Cake Staking FAQ
        • Syrup Pool FAQ & Troubleshooting
  • Play
    • 🔮Prediction
      • How to Use Prediction
      • AI Prediction
      • Prediction FAQ
      • Prediction Troubleshooting
      • Prediction Mini App
    • 🎟️Lottery
      • How to Play Lottery
      • Lottery FAQ
  • ❓Play FAQ
  • Trading Tools
    • 🛡️PancakeSwap MEV Guard
    • PancakeSwap Auto Slippage
      • How to Set Slippage on PancakeSwap
    • 🔔Notifications
    • ❓Trading Tools FAQ
      • MEV Guard FAQ
  • Protocol
    • 🍰CAKE Tokenomics
    • 📔Governance
      • How to Vote
    • 💻Developers
      • Developer Docs
      • Contributing
        • Codebase Overview
Powered by GitBook
On this page
  • Contract roles:
  • Owner
  • Operator Address
  • Treasury Address
  • Injector Address (Currently the same as Owner)
  • Functions
  • injectFunds - Injector and Owner
  • startLottery - Operator
  • closeLottery - Operator
  • drawFinalNumberAndMakeLotteryClaimable - Operator
  • recoverWrongTokens - Owner
  • setMinAndMaxTicketPriceInCake - Owner
  • setMaxNumberTicketsPerBuy - Owner
  • setOperatorAndTreasuryAndInjectorAddresses - Owner
  • changeRandomGenerator - Owner

Was this helpful?

  1. To Delete
  2. Smart Contracts (EVM)
  3. Lottery

Lottery Contract

Contract roles:

Role
Description

injectorAddress (onlyInjector)

Injector is the address used to fund the lottery with periodic injections

operatorAddress (onlyOperator)

The lottery scheduler account used to run regular operations.

treasuryAddress (onlyTreasury)

The address in which the burn is sent

Owner (onlyOwner)

The contract owner

Owner

0xad9d97fc7bf0ac6dc68d478dcb3709454519b358

Address controlled by gnosis multisignature contract with a threshold of 3/6

Operator Address

0x566a7e38b300E903dE71389C2b801AcDBA5268dB

Scheduler address - entirely automated and no human interaction. Not on multisig and doesn't have access to sensitive contract operations.

Treasury Address

0xe2086f890e7bd20e07fc0036a437dc4813e88b09

Address controlled by gnosis multisignature contract with a threshold of 3/6

Injector Address (Currently the same as Owner)

0xaD9d97fc7BF0ac6dC68d478dcB3709454519b358

Address controlled by gnosis multisignature contract with a threshold of 3/6

Functions

injectFunds - Injector and Owner

    function injectFunds(uint256 _lotteryId, uint256 _amount) external override onlyOwnerOrInjector {
        require(_lotteries[_lotteryId].status == Status.Open, "Lottery not open");

        cakeToken.safeTransferFrom(address(msg.sender), address(this), _amount);
        _lotteries[_lotteryId].amountCollectedInCake += _amount;

        emit LotteryInjection(_lotteryId, _amount);
    }

The Injector or Owner can call this function to inject a specific lotteryId with a specified amount of CAKE.

startLottery - Operator

    function startLottery(
        uint256 _endTime,
        uint256 _priceTicketInCake,
        uint256 _discountDivisor,
        uint256[6] calldata _rewardsBreakdown,
        uint256 _treasuryFee
    ) external override onlyOperator {
        require(
            (currentLotteryId == 0) || (_lotteries[currentLotteryId].status == Status.Claimable),
            "Not time to start lottery"
        );

        require(
            ((_endTime - block.timestamp) > MIN_LENGTH_LOTTERY) && ((_endTime - block.timestamp) < MAX_LENGTH_LOTTERY),
            "Lottery length outside of range"
        );

        require(
            (_priceTicketInCake >= minPriceTicketInCake) && (_priceTicketInCake <= maxPriceTicketInCake),
            "Outside of limits"
        );

        require(_discountDivisor >= MIN_DISCOUNT_DIVISOR, "Discount divisor too low");
        require(_treasuryFee <= MAX_TREASURY_FEE, "Treasury fee too high");

        require(
            (_rewardsBreakdown[0] +
                _rewardsBreakdown[1] +
                _rewardsBreakdown[2] +
                _rewardsBreakdown[3] +
                _rewardsBreakdown[4] +
                _rewardsBreakdown[5]) == 10000,
            "Rewards must equal 10000"
        );

        currentLotteryId++;

        _lotteries[currentLotteryId] = Lottery({
            status: Status.Open,
            startTime: block.timestamp,
            endTime: _endTime,
            priceTicketInCake: _priceTicketInCake,
            discountDivisor: _discountDivisor,
            rewardsBreakdown: _rewardsBreakdown,
            treasuryFee: _treasuryFee,
            cakePerBracket: [uint256(0), uint256(0), uint256(0), uint256(0), uint256(0), uint256(0)],
            countWinnersPerBracket: [uint256(0), uint256(0), uint256(0), uint256(0), uint256(0), uint256(0)],
            firstTicketId: currentTicketId,
            firstTicketIdNextLottery: currentTicketId,
            amountCollectedInCake: pendingInjectionNextLottery,
            finalNumber: 0
        });

        emit LotteryOpen(
            currentLotteryId,
            block.timestamp,
            _endTime,
            _priceTicketInCake,
            currentTicketId,
            pendingInjectionNextLottery
        );

        pendingInjectionNextLottery = 0;
    }

The startLottery function is only callable by the Operator in order to start a new lottery round.

closeLottery - Operator

function closeLottery(uint256 _lotteryId) external override onlyOperator nonReentrant {
        require(_lotteries[_lotteryId].status == Status.Open, "Lottery not open");
        require(block.timestamp > _lotteries[_lotteryId].endTime, "Lottery not over");
        _lotteries[_lotteryId].firstTicketIdNextLottery = currentTicketId;

        // Request a random number from the generator based on a seed
        randomGenerator.getRandomNumber(uint256(keccak256(abi.encodePacked(_lotteryId, currentTicketId))));

        _lotteries[_lotteryId].status = Status.Close;

        emit LotteryClose(_lotteryId, currentTicketId);
    }

Callable by the Operator to close a round of the lottery.

drawFinalNumberAndMakeLotteryClaimable - Operator

function drawFinalNumberAndMakeLotteryClaimable(uint256 _lotteryId, bool _autoInjection)
        external
        override
        onlyOperator
        nonReentrant
    {
        require(_lotteries[_lotteryId].status == Status.Close, "Lottery not close");
        require(_lotteryId == randomGenerator.viewLatestLotteryId(), "Numbers not drawn");

        // Calculate the finalNumber based on the randomResult generated by ChainLink's fallback
        uint32 finalNumber = randomGenerator.viewRandomResult();

        // Initialize a number to count addresses in the previous bracket
        uint256 numberAddressesInPreviousBracket;

        // Calculate the amount to share post-treasury fee
        uint256 amountToShareToWinners = (
            ((_lotteries[_lotteryId].amountCollectedInCake) * (10000 - _lotteries[_lotteryId].treasuryFee))
        ) / 10000;

        // Initializes the amount to withdraw to treasury
        uint256 amountToWithdrawToTreasury;

        // Calculate prizes in CAKE for each bracket by starting from the highest one
        for (uint32 i = 0; i < 6; i++) {
            uint32 j = 5 - i;
            uint32 transformedWinningNumber = _bracketCalculator[j] + (finalNumber % (uint32(10)**(j + 1)));

            _lotteries[_lotteryId].countWinnersPerBracket[j] =
                _numberTicketsPerLotteryId[_lotteryId][transformedWinningNumber] -
                numberAddressesInPreviousBracket;

            // A. If number of users for this _bracket number is superior to 0
            if (
                (_numberTicketsPerLotteryId[_lotteryId][transformedWinningNumber] - numberAddressesInPreviousBracket) !=
                0
            ) {
                // B. If rewards at this bracket are > 0, calculate, else, report the numberAddresses from previous bracket
                if (_lotteries[_lotteryId].rewardsBreakdown[j] != 0) {
                    _lotteries[_lotteryId].cakePerBracket[j] =
                        ((_lotteries[_lotteryId].rewardsBreakdown[j] * amountToShareToWinners) /
                            (_numberTicketsPerLotteryId[_lotteryId][transformedWinningNumber] -
                                numberAddressesInPreviousBracket)) /
                        10000;

                    // Update numberAddressesInPreviousBracket
                    numberAddressesInPreviousBracket = _numberTicketsPerLotteryId[_lotteryId][transformedWinningNumber];
                }
                // A. No CAKE to distribute, they are added to the amount to withdraw to treasury address
            } else {
                _lotteries[_lotteryId].cakePerBracket[j] = 0;

                amountToWithdrawToTreasury +=
                    (_lotteries[_lotteryId].rewardsBreakdown[j] * amountToShareToWinners) /
                    10000;
            }
        }

        // Update internal statuses for lottery
        _lotteries[_lotteryId].finalNumber = finalNumber;
        _lotteries[_lotteryId].status = Status.Claimable;

        if (_autoInjection) {
            pendingInjectionNextLottery = amountToWithdrawToTreasury;
            amountToWithdrawToTreasury = 0;
        }

        amountToWithdrawToTreasury += (_lotteries[_lotteryId].amountCollectedInCake - amountToShareToWinners);

        // Transfer CAKE to treasury address
        cakeToken.safeTransfer(treasuryAddress, amountToWithdrawToTreasury);

        emit LotteryNumberDrawn(currentLotteryId, finalNumber, numberAddressesInPreviousBracket);
    }

For Operator to draw the final number using ChainLink VRF function.

recoverWrongTokens - Owner

   function recoverWrongTokens(address _tokenAddress, uint256 _tokenAmount) external onlyOwner {
        require(_tokenAddress != address(cakeToken), "Cannot be CAKE token");

        IERC20(_tokenAddress).safeTransfer(address(msg.sender), _tokenAmount);

        emit AdminTokenRecovery(_tokenAddress, _tokenAmount);
    }

In the case of tokens other than CAKE mistakenly being sent to the lottery contract, this function is used to recover them and is only callable by the Owner


setMinAndMaxTicketPriceInCake - Owner

    function setMinAndMaxTicketPriceInCake(uint256 _minPriceTicketInCake, uint256 _maxPriceTicketInCake)
        external
        onlyOwner
    {
        require(_minPriceTicketInCake <= _maxPriceTicketInCake, "minPrice must be < maxPrice");

        minPriceTicketInCake = _minPriceTicketInCake;
        maxPriceTicketInCake = _maxPriceTicketInCake;
    }

To prevent the Operator setting the tickets to arbitrary prices during the event of a flash crash/pump.

setMaxNumberTicketsPerBuy - Owner

function setMaxNumberTicketsPerBuy(uint256 _maxNumberTicketsPerBuy) external onlyOwner {
        require(_maxNumberTicketsPerBuy != 0, "Must be > 0");
        maxNumberTicketsPerBuyOrClaim = _maxNumberTicketsPerBuy;
    }

The Owner can modify the maximum number of tickets per transaction. This may be modified in the case of BSC block size increasing or decreasing.

setOperatorAndTreasuryAndInjectorAddresses - Owner

function setOperatorAndTreasuryAndInjectorAddresses(
        address _operatorAddress,
        address _treasuryAddress,
        address _injectorAddress
    ) external onlyOwner {
        require(_operatorAddress != address(0), "Cannot be zero address");
        require(_treasuryAddress != address(0), "Cannot be zero address");
        require(_injectorAddress != address(0), "Cannot be zero address");

        operatorAddress = _operatorAddress;
        treasuryAddress = _treasuryAddress;
        injectorAddress = _injectorAddress;

        emit NewOperatorAndTreasuryAndInjectorAddresses(_operatorAddress, _treasuryAddress, _injectorAddress);
    }

Function used to set the Operator, Treasury, and Injector addresses.

changeRandomGenerator - Owner

    function changeRandomGenerator(address _randomGeneratorAddress) external onlyOwner {
        require(
            (currentLotteryId == 0) || (_lotteries[currentLotteryId].status == Status.Claimable),
            "Lottery not in claimable"
        );

        // Request a random number from the generator based on a seed
        IRandomNumberGenerator(_randomGeneratorAddress).getRandomNumber(
            uint256(keccak256(abi.encodePacked(currentLotteryId, currentTicketId)))
        );

        // Calculate the finalNumber based on the randomResult generated by ChainLink's fallback
        IRandomNumberGenerator(_randomGeneratorAddress).viewRandomResult();

        randomGenerator = IRandomNumberGenerator(_randomGeneratorAddress);

        emit NewRandomGenerator(_randomGeneratorAddress);
    }

For the Owner to update the RandomNumberGenerator contract in case we need to update the drawing logic, or release an update.

Last updated 3 years ago

Was this helpful?