Skip to content

Latest commit

 

History

History
154 lines (124 loc) · 6.21 KB

File metadata and controls

154 lines (124 loc) · 6.21 KB

Sample SCORE

Before showing the usage of PythonSDK in Jupyter Notebook, we will require a SCORE to be interacted with. For this documentation we chose one of the SCORE having highest number of transactions in ICON blockchain ie Dice SCORE of ICONbet.

Some changes have been made to the actual SCORE to make it easier for demonstration purpose. You can access the project folder from Github.

Dice SCORE

The following is the code snippet for the dice SCORE:-

from iconservice import *

TAG = "DICE"
UPPER_LIMIT = 99
LOWER_LIMIT = 0
MAIN_BET_MULTIPLIER = 98.5
SIDE_BET_MULTIPLIER = 95
BET_MIN = 1000000000000000
SIDE_BET_TYPES = ["digits_match", "icon_logo1", "icon_logo2"]
SIDE_BET_MULTIPLIERS = {"digits_match": 9.5, "icon_logo1": 5, "icon_logo2": 95}
BET_LIMIT_RATIOS_SIDE_BET = {"digits_match": 1140, "icon_logo1": 540, "icon_logo2": 12548}
MINIMUM_TREASURY = 250

class Dice(IconScoreBase):
    _GAME_ON = "game_on"

    def __init__(self, db: IconScoreDatabase) -> None:
        super().__init__(db)
        self._game_on = VarDB(self._GAME_ON, db, value_type=bool)

    @eventlog(indexed=2)
    def BetSource(self, _from: Address, timestamp: int):
        pass

    @eventlog(indexed=3)
    def PayoutAmount(self, payout: int, main_bet_payout: int, side_bet_payout: int):
        pass

    @eventlog(indexed=3)
    def BetResult(self, spin: str, winningNumber: int, payout: int):
        pass

    @eventlog(indexed=2)
    def FundTransfer(self, recipient: Address, amount: int, note: str):
        pass

    def on_install(self) -> None:
        super().on_install()
        self._game_on.set(False)

    def on_update(self) -> None:
        super().on_update()


    @external
    def toggle_game_status(self) -> None:

        if self.msg.sender != self.owner:
            revert('Only the owner can call the toggle_game_status method')
        self._game_on.set(not self._game_on.get() )


    @external(readonly=True)
    def get_game_status(self) -> bool:
        return self._game_on.get()

    def get_random(self, user_seed: str = '') -> float:
        seed = (str(bytes.hex(self.tx.hash)) + str(self.now()) + user_seed)
        spin = (int.from_bytes(sha3_256(seed.encode()), "big") % 100000) / 100000.0
        return spin

    @payable
    @external
    def call_bet(self, upper: int, lower: int, user_seed: str = '', side_bet_amount: int = 0,
                 side_bet_type: str = '') -> None:
        return self.__bet(upper, lower, user_seed, side_bet_amount, side_bet_type)

    def __bet(self, upper: int, lower: int, user_seed: str, side_bet_amount: int, side_bet_type: str) -> None:
        side_bet_win = False
        side_bet_set = False
        side_bet_payout = 0
        self.BetSource(self.tx.origin, self.tx.timestamp)

        #condition checks
        if not self._game_on.get():
            revert(f'Game not active yet.')

        if not (0 <= upper <= 99 and 0 <= lower <= 99):
            revert(f'Invalid bet. Choose a number between 0 to 99')
        if not (0 <= upper - lower <= 95):
            revert(f'Invalid gap. Choose upper and lower values such that gap is between 0 to 95')
        if (side_bet_type == '' and side_bet_amount != 0) or (side_bet_type != '' and side_bet_amount == 0):
            revert(f'should set both side bet type as well as side bet amount')
        if side_bet_amount < 0:
            revert(f'Bet amount cannot be negative')
        if side_bet_type != '' and side_bet_amount != 0:
            side_bet_set = True
            if side_bet_type not in SIDE_BET_TYPES:
                revert(f'Invalid side bet type.')
            side_bet_limit = MINIMUM_TREASURY // BET_LIMIT_RATIOS_SIDE_BET[side_bet_type]
            if side_bet_amount < BET_MIN or side_bet_amount > side_bet_limit:
                revert(f'Betting amount {side_bet_amount} out of range ({BET_MIN} ,{side_bet_limit}).')
            side_bet_payout = int(SIDE_BET_MULTIPLIERS[side_bet_type] * 100) * side_bet_amount // 100


        main_bet_amount = self.msg.value - side_bet_amount
        gap = (upper - lower) + 1
        if main_bet_amount == 0:
            Logger.debug(f'No main bet amount provided', TAG)
            revert(f'No main bet amount provided')

        main_bet_limit = (MINIMUM_TREASURY * 1.5 * gap) // (68134 - 681.34 * gap)
        if main_bet_amount < BET_MIN or main_bet_amount > main_bet_limit:
            revert(f'Main Bet amount {main_bet_amount} out of range {BET_MIN},{main_bet_limit} ')
        main_bet_payout = int(MAIN_BET_MULTIPLIER * 100) * main_bet_amount // (100 * gap)
        payout = side_bet_payout + main_bet_payout
        if self.icx.get_balance(self.address) < payout:
            revert('Not enough in treasury to make the play.')
        spin = self.get_random(user_seed)
        winningNumber = int(spin * 100)
        if lower <= winningNumber <= upper:
            main_bet_win = True
        else:
            main_bet_win = False
        if side_bet_set:
            side_bet_win = self.check_side_bet_win(side_bet_type, winningNumber)
            if not side_bet_win:
                side_bet_payout = 0
        main_bet_payout = main_bet_payout * main_bet_win
        payout = main_bet_payout + side_bet_payout
        self.BetResult(str(spin), winningNumber, payout)
        self.PayoutAmount(payout, main_bet_payout, side_bet_payout)
        if main_bet_win or side_bet_win:
            try:
                self.icx.transfer(self.msg.sender,payout)
            except BaseException as e:
                revert('Network problem. Winnings not sent. Returning funds.')

    # check for bet limits and side limits
    def check_side_bet_win(self, side_bet_type: str, winning_number: int) -> bool:
        if side_bet_type == SIDE_BET_TYPES[0]:  # digits_match
            return winning_number % 11 == 0
        if side_bet_type == SIDE_BET_TYPES[1]:  # for icon logo1 ie for numbers having 1 zero in it
            return str(0) in str(winning_number) or winning_number in range(1, 10)
        if side_bet_type == SIDE_BET_TYPES[2]:  # for icon logo2 ie for 0
            return winning_number == 0

    @payable
    def fallback(self):
        if self.msg.sender != self.owner:
            revert("Treasury can only be filled by the SCORE owner")