def Main(op, args): context = GetContext() if op == 'registerDelegator': if len(args) == 2: return register_delegator(context, args) else: Notify(ARG_ERROR) return False elif op == 'registerWallet': if len(args) == 1: return register_wallet(context, args) else: Notify(ARG_ERROR) return False elif op == 'verifyClaim': if len(args) == 1: delegator = GetEntryScriptHash() return verify_claim(context, delegator, args) else: Notify(ARG_ERROR) return False else: Notify(INVALID_OPERATION) return False
def checkHash(): Notify(["111_checkHash"]) # to prevent hack from other contract callerHash = GetCallingScriptHash() entryHash = GetEntryScriptHash() Notify([callerHash, entryHash, ContractAddress]) return True
def invokeB(param): Notify(["111_invokeB", param]) # to prevent hack from other contract callerHash = GetCallingScriptHash() entryHash = GetEntryScriptHash() Notify([callerHash, entryHash, ContractAddress]) return True
def do_transfer(ctx, Caller, args): """Transfers a token at the specified id from the t_owner address to the t_to address :param StorageContext ctx: current store context :param list args: 0: bytearray t_to: transfer to address 1: int t_id: token id :return: transfer success :rtype: bool """ # we don't need the t_from because the token data stores the owner t_from = "" t_to = args[0] t_id = args[1] if len( args ) == 3: # use traditional from, to, id format if they want to send it t_from = args[0] t_to = args[1] t_id = args[2] if Caller != GetEntryScriptHash() and not is_whitelisted_dex(ctx, Caller): # non-whitelisted contracts can only approve their own funds for transfer, # even if they have the signature of the owner on the invocation t_from = Caller assert len(t_to) == 20, INVALID_ADDRESS_ERROR ownership = safe_deserialize(Get(ctx, concat('ownership/', t_id))) assert ownership, TOKEN_DNE_ERROR assert has_key(ownership, 'owner'), TOKEN_DNE_ERROR assert len(ownership['owner']) == 20, TOKEN_DNE_ERROR t_owner = ownership['owner'] if t_owner == t_to: print('transfer to self') return True assert authenticate(t_owner, Caller), PERMISSION_ERROR res = remove_token_from_owners_list(ctx, t_owner, t_id) assert res, 'unable to remove token from owner list' ownership['owner'] = t_to # update token's owner # remove any existing approvals for this token if has_key(ownership, 'approved'): ownership.remove('approved') Put(ctx, concat('ownership/', t_id), Serialize(ownership)) res = add_token_to_owners_list(ctx, t_to, t_id) # log this transfer event OnTransfer(t_owner, t_to, 1) OnNFTTransfer(t_owner, t_to, t_id) return True
def avoidToBeInvokedByContract(): callerHash = GetCallingScriptHash() entryHash = GetEntryScriptHash() if callerHash != entryHash: Notify(["You are not allowed to invoke this method through contract"]) return False else: Notify(["You can implement what you need to do here!"]) return True
def do_approve(ctx, caller, t_receiver, t_id, revoke): """Approve a token to eventually be transferred to the t_receiver :param StorageContext ctx: current store context :param byte[] caller: calling script hash :param byte[] t_receiver: address of the future token owner :param bytes t_id: int: token id :param bytes revoke: set to 1 to revoke previous approval :return: approval success :rtype: bool """ if len(t_receiver) != 20: Notify(INVALID_ADDRESS_ERROR) return False if len(revoke) == b'\x00': revoke = b'\x00' t_owner = Get(ctx, t_id) if len(t_owner) != 20: Notify(TOKEN_DNE_ERROR) return False if t_owner == t_receiver: Notify('approved spend to self') return True is_token_owner = CheckWitness(t_owner) if is_token_owner and GetEntryScriptHash() != caller: Notify('third party script is bouncing the signature to us') return False # if token owner is a smart contract and is the calling # script hash, continue elif GetContract(t_owner) and t_owner == caller: is_token_owner = True if is_token_owner: approval_key = concat('approved/', t_id) # revoke previous approval if revoke != 0 if revoke != b'\x00': Delete(ctx, approval_key) # log the revoking of previous approvals OnApprove(t_owner, t_receiver, b'\x00') OnNFTApprove(t_owner, '', t_id) return True # approve this transfer Put(ctx, approval_key, concat(t_owner, t_receiver)) # Log this approval event OnApprove(t_owner, t_receiver, 1) OnNFTApprove(t_owner, t_receiver, t_id) return True Notify(PERMISSION_ERROR) return False
def do_approve(ctx, Caller, args): """Approve a token to be transferred to a third party by an approved spender :param StorageContext ctx: current store context :param bytearray t_owner: current owner of the token :param bytearray t_spender: spender to approve :param int t_id: int: token id :param bool revoke: set to True to revoke previous approval :return: approval success :rtype: bool """ t_owner = args[0] t_spender = args[1] t_id = args[2] revoke = False if len(args) > 3: revoke = args[3] if Caller != GetEntryScriptHash() and not is_whitelisted_dex(ctx, Caller): # non-whitelisted contracts can only approve their own funds for transfer, # even if they have the signature of the owner on the invocation t_owner = Caller assert len(t_owner) == 20, INVALID_ADDRESS_ERROR assert len(t_spender) == 20, INVALID_ADDRESS_ERROR assert t_id, TOKEN_DNE_ERROR ownership_key = concat('ownership/', t_id) ownership = safe_deserialize(Get(ctx, ownership_key)) assert ownership, TOKEN_DNE_ERROR assert has_key(ownership, 'owner'), TOKEN_DNE_ERROR assert t_owner == ownership['owner'], PERMISSION_ERROR assert t_owner != t_spender, 'same owner and spender' assert authenticate(t_owner, Caller), PERMISSION_ERROR # revoke previous approval if revoke is True if revoke: if has_key(ownership, 'approved'): ownership.remove('approved') Put(ctx, ownership_key, Serialize(ownership)) # log the revoking of previous approvals OnApprove(t_owner, t_spender, 0) OnNFTApprove(t_owner, '', t_id) return True ownership['approved'] = concat(t_owner, t_spender) # approve this transfer Put(ctx, ownership_key, Serialize(ownership)) # Log this approval event OnApprove(t_owner, t_spender, 1) OnNFTApprove(t_owner, t_spender, t_id) return True
def do_transfer(ctx, t_from, t_to, amount, callingScriptHash): if amount <= 0: return False if len(t_to) != 20: return False # if the calling script hash is not the entry script hash # we force the `t_from` to be the address of the callingScriptHash if callingScriptHash != GetEntryScriptHash(): print("Cannot call from another contract on behalf of other addresses") print("Setting from address to callingScriptHash") t_from = callingScriptHash else: if t_from == GetExecutingScriptHash(): if not check_owners(ctx, 3): print("Must authenticate as owners") return False else: if not CheckWitness(t_from): print("Insufficient priveleges") return False from_balance_key = get_balance_key(t_from) to_balance_key = get_balance_key(t_to) from_balance = Get(ctx, from_balance_key) if from_balance < amount: Notify("insufficient funds") return False if t_from == t_to: return True if from_balance == amount: Delete(ctx, from_balance_key) else: difference = from_balance - amount Put(ctx, from_balance_key, difference) to_balance = Get(ctx, to_balance_key) to_total = to_balance + amount Put(ctx, to_balance_key, to_total) OnTransfer(t_from, t_to, amount) return True
def avoidContractCallAttack(guessNumber): randomNumber = getRandomNumber() callerHash = GetCallingScriptHash() entryHash = GetEntryScriptHash() Notify(["randomNumber:", randomNumber, "guessNumber:", guessNumber]) if callerHash != entryHash: Notify(["You are not allowed to invoke this method through contract!"]) return False else: Notify(["You can implement what you need to do here!"]) if guessNumber == randomNumber: Notify(["You have won the big prize!"]) return True
def do_approve(ctx, t_owner, t_spender, amount, callingScriptHash): if t_owner == GetExecutingScriptHash(): print("Cannot approve from contract address. Use transfer") return False # if the calling script hash is not the entry script hash # we force the `t_from` to be the address of the callingScriptHash if callingScriptHash != GetEntryScriptHash(): print("Cannot call from another contract on behalf of other addresses") print("Setting from address to callingScriptHash") t_owner = callingScriptHash else: if not CheckWitness(t_owner): print("Insufficient priveleges") return False if len(t_spender) != 20: return False if amount < 0: return False # cannot approve an amount that is # currently greater than the from balance owner_balance_key = get_balance_key(t_owner) if Get(ctx, owner_balance_key) >= amount: approval_key = get_allowance_key(t_owner, t_spender) if amount == 0: Delete(ctx, approval_key) else: Put(ctx, approval_key, amount) OnApprove(t_owner, t_spender, amount) return True print("not enough balance") return False
def do_transfer(ctx, caller, args): """Transfers a token at the specified id from the t_owner address to the t_to address :param StorageContext ctx: current store context :param bytes caller: calling script hash :param list args: 0: byte[] t_to: transfer to address 1: bytes t_id: token id 2: extra_arg: optional argument that can be passed (for use only with smart contracts) :return: transfer success :rtype: bool """ t_to = args[0] t_id = args[1] if len(t_to) != 20: Notify(INVALID_ADDRESS_ERROR) return False t_owner = Get(ctx, t_id) if len(t_owner) != 20: Notify(TOKEN_DNE_ERROR) return False if t_owner == t_to: Notify('transfer to self') return True # Verifies that the calling contract has verified the required # script hashes of the transaction/block is_token_owner = CheckWitness(t_owner) if is_token_owner and GetEntryScriptHash() != caller: Notify('third party script is bouncing the signature to us') return False # if token owner is a smart contract and is the calling # script hash, continue elif GetContract(t_owner) and t_owner == caller: is_token_owner = True if is_token_owner: # 1. Is t_to a smart contract? # If True, invoke the transfer_to_smart_contract # method, if transfer_to_smart_contract() returns False, # then reject the transfer if GetContract(t_to): success = transfer_to_smart_contract(ctx, t_owner, args, False) if success is False: return False else: if len(args) > 2: Notify(ARG_ERROR) return False res = remove_token_from_owners_list(ctx, t_owner, t_id) if res is False: Notify('unable to transfer token') return False Put(ctx, t_id, t_to) # update token's owner # remove any existing approvals for this token Delete(ctx, concat('approved/', t_id)) add_token_to_owners_list(ctx, t_to, t_id) # log this transfer event OnTransfer(t_owner, t_to, 1) OnNFTTransfer(t_owner, t_to, t_id) return True Notify(PERMISSION_ERROR) return False
def invokeA(operation, params): callerHash = GetCallingScriptHash() entryHash = GetEntryScriptHash() Notify(["111_invokeA",callerHash, entryHash, ContractAddress]) return ContractB(operation, params)
def do_transfer_from(ctx, Caller, args): """Transfers the approved token at the specified id from the t_from address to the t_to address Only the approved spender OR a whitelisted DEX can invoke this function and a whitelisted DEX will still need to pass the authentication of the spender :param StorageContext ctx: current store context :param list args: 0: bytearray t_spender: approved spender address 1: bytearray t_from: transfer from address (token owner) 2: bytearray t_to: transfer to address (token receiver) 3: int t_id: token id :return: transferFrom success :rtype: bool """ t_spender = args[0] t_from = args[1] t_to = args[2] t_id = args[3] if Caller != GetEntryScriptHash() and not is_whitelisted_dex(ctx, Caller): # non-whitelisted contracts can only approve their own funds for transfer, # even if they have the signature of the owner on the invocation t_from = Caller assert len(t_spender) == 20, INVALID_ADDRESS_ERROR assert len(t_from) == 20, INVALID_ADDRESS_ERROR assert len(t_to) == 20, INVALID_ADDRESS_ERROR assert authenticate(t_spender, Caller), PERMISSION_ERROR if t_from == t_to: print('transfer to self') return True ownership = safe_deserialize(Get(ctx, concat('ownership/', t_id))) assert ownership, TOKEN_DNE_ERROR assert has_key(ownership, 'owner'), TOKEN_DNE_ERROR assert has_key(ownership, 'approved'), 'no approval exists for this token' assert len(ownership['owner']) == 20, TOKEN_DNE_ERROR t_owner = ownership['owner'] assert t_from == t_owner, 'from address is not the owner of this token' assert len( ownership['approved']) == 40, 'malformed approval key for this token' # Finally check to see if the owner approved this spender assert ownership['approved'] == concat(t_from, t_spender), PERMISSION_ERROR res = remove_token_from_owners_list(ctx, t_from, t_id) assert res, 'unable to remove token from owner list' ownership['owner'] = t_to ownership.remove('approved') # remove previous approval Put(ctx, concat('ownership/', t_id), Serialize(ownership)) res = add_token_to_owners_list(ctx, t_to, t_id) # log this transfer event OnTransfer(t_from, t_to, 1) OnNFTTransfer(t_from, t_to, t_id) return True
def bet(account, ongAmount, number): """ :param account: :param ongAmount: :param number: :return: """ # RequireWitness(account) if CheckWitness(account) == False: # bet: Check witness failed! Notify(["Error", 601]) return False currentRound = getCurrentRound() # to prevent hack from other contract callerHash = GetCallingScriptHash() entryHash = GetEntryScriptHash() # Require(callerHash == entryHash) if callerHash != entryHash: # bet: Don't support bet method being invoked by another contract to prevent hacking Notify(["Error", 602]) return False if getRoundGameStatus(currentRound) != STATUS_ON: # bet: Current round game ended, please wait for the starting of the next round game and try later Notify(["Error", 603]) return False # make sure the contract has enough ong to pay to accont if account wins tryPayOutToWin = _calculatePayOutToWin(ongAmount, number) totalOngAmount = getTotalONG() realTimeRunVault = getRealTimeRunningVault(currentRound) # # Require(realTimeRunVault > tryPayOutToWin) if realTimeRunVault < Sub(tryPayOutToWin, ongAmount): # bet: Running pot/vault does not have enough asset to pay to the player, please try smaller bet Notify(["Error", 604]) return False # Require(_transferONG(account, ContractAddress, ongAmount)) res = _transferONG(account, ContractAddress, ongAmount) if res == False: # bet: Transfer ONG failed, please try later or again Notify(["Error", 605]) return False # Require(number < 97) if number >= 97: # bet: Please try to bet with a number less than 97 Notify(["Error", 606]) return False # Require(number > 1) if number <= 1: # bet: Please try to bet with a number greater than 1 Notify(["Error", 607]) return False theNumber = _rollANumber() payOutToWin = 0 if theNumber < number: payOutToWin = tryPayOutToWin Require(_transferONGFromContact(account, payOutToWin)) # update total ongAmount ongAmountToBeSub = Sub(payOutToWin, ongAmount) Put(GetContext(), TOTAL_ONG_KEY, Sub(totalOngAmount, ongAmountToBeSub)) # update real time running vault realTimeRunVaultKey = concatKey(concatKey(ROUND_PREFIX, currentRound), REAL_TIME_RUNNING_VAULT) Put(GetContext(), realTimeRunVaultKey, Sub(realTimeRunVault, ongAmountToBeSub)) realTimeRunVault = getRealTimeRunningVault(currentRound) # mark the game as end if real time running vault is less than 1/10 of running vault if realTimeRunVault <= Div(getIncreasingRunnVault(currentRound), 10): # mark this round of game end Put(GetContext(), concatKey(concatKey(ROUND_PREFIX, currentRound), ROUND_STATUS), STATUS_OFF) # update profit per investment for bankers runVaultLeft = getRunningVault(currentRound) if runVaultLeft > 0: profitPerRuningVaultShareToBeAdd = Div( Mul(realTimeRunVault, MagnitudeForProfitPerSth), runVaultLeft) Put( GetContext(), concatKey(concatKey(ROUND_PREFIX, currentRound), PROFIT_PER_RUNNING_VAULT_SHARE_KEY), Add(profitPerRuningVaultShareToBeAdd, getProfitPerRunningVaultShare(currentRound))) # update real time running vault Delete(GetContext(), realTimeRunVaultKey) Notify(["endCurrentRound", currentRound]) return True else: # update total ong amount Put(GetContext(), TOTAL_ONG_KEY, Add(totalOngAmount, ongAmount)) # update profit per investment for bankers runVaultLeft = getRunningVault(currentRound) if runVaultLeft > 0: profitPerRunNingVaultShareToBeAdd = Div( Mul(ongAmount, MagnitudeForProfitPerSth), runVaultLeft) Put( GetContext(), concatKey(concatKey(ROUND_PREFIX, currentRound), PROFIT_PER_RUNNING_VAULT_SHARE_KEY), Add(profitPerRunNingVaultShareToBeAdd, getProfitPerRunningVaultShare(currentRound))) Notify([ "bet", currentRound, account, number, theNumber, ongAmount, payOutToWin ]) return True
def fillPaper(account, guessNumberList): """ :param account: :param guessNumberList: can be a list of numbers :return: """ RequireWitness(account) currentRound = getCurrentRound() Require(getGameStatus(currentRound) == STATUS_ON) # to prevent hack from other contract callerHash = GetCallingScriptHash() entryHash = GetEntryScriptHash() Require(callerHash == entryHash) # Require(entryHash == ContractAddress) guessNumberLen = len(guessNumberList) Require(guessNumberLen >= 1) currentPaperBalance = getPaperBalance(account) # make sure his balance is greater or equal to guessNumberList length Require(currentPaperBalance >= guessNumberLen) numberListKey = concatKey(concatKey(ROUND_PREFIX, currentRound), FILLED_NUMBER_LIST_KEY) numberListInfo = Get(GetContext(), numberListKey) numberList = [] if numberListInfo: numberList = Deserialize(numberListInfo) for guessNumber in guessNumberList: # Require is need to raise exception Require(guessNumber < 100) Require(guessNumber >= 0) numberPlayersListKey = concatKey(concatKey(ROUND_PREFIX, currentRound), concatKey(FILLED_NUMBER_KEY, guessNumber)) numberPlayersListInfo = Get(GetContext(), numberPlayersListKey) numberPlayersList = [] if numberPlayersListInfo: numberPlayersList = Deserialize(numberPlayersListInfo) # make sure account has NOT filled the number before in this round for player in numberPlayersList: Require(player != account) else: numberList.append(guessNumber) # add account to the players list that filled the number in this round numberPlayersList.append(account) # Store the numberPlayers List numberPlayersListInfo = Serialize(numberPlayersList) Put(GetContext(), numberPlayersListKey, numberPlayersListInfo) # Store the numberList numberListInfo = Serialize(numberList) Put(GetContext(), numberListKey, numberListInfo) # update dividend updateDividendBalance(account) # update the paper balance of account -- destroy the filled papers Put(GetContext(), concatKey(PAPER_BALANCE_PREFIX, account), Sub(currentPaperBalance, guessNumberLen)) # update total paper amount Put(GetContext(), TOTAL_PAPER_KEY, Sub(getTotalPaper(), guessNumberLen)) # update the filled paper balance of account in current round key1 = concatKey(ROUND_PREFIX, currentRound) key2 = concatKey(FILLED_PAPER_BALANCE_PREFIX, account) key = concatKey(key1, key2) Put(GetContext(), key, Add(guessNumberLen, getFilledPaperBalance(account, currentRound))) # update the filled paper amount in current round key = concatKey(concatKey(ROUND_PREFIX, currentRound), FILLED_PAPER_AMOUNT) Put(GetContext(), key, Add(guessNumberLen, getFilledPaperAmount(currentRound))) Notify(["fillPaper", account, guessNumberList, GetTime()]) return True
def IsFromContract(): callerHash = GetCallingScriptHash() entryHash = GetEntryScriptHash() if callerHash != entryHash: return True return False