def opt_in( algod_client: algod.AlgodClient, user: Account, nft_id: int, ): nft_name = algod_client.asset_info(nft_id)['params']['name'] optin = '' while not optin: optin = str( input( f"Do you want to opt-in the {nft_name} (ID: {nft_id})? (Y/n) ") ) print("") if optin.lower() == 'y': params = algod_client.suggested_params() opt_in_txn = transaction.AssetOptInTxn( sender=user.address, sp=params, index=nft_id, ) return sign_send_wait(algod_client, user, opt_in_txn) elif optin.lower() == 'n': return else: optin = ''
def wait_for_confirmation(client: algod.AlgodClient, transaction_id: str, timeout: int = 100) -> Dict[str, str or int]: """ Check for when the transaction is confirmed by the network. Once confirmed, return the transaction information. :param client -> ``algod.AlgodClient``: an algorand client object. :param transaction_id -> ``str``: id for the transaction. """ start_round = client.status()["last-round"] + 1 current_round = start_round while current_round < start_round + timeout: try: pending_txn = client.pending_transaction_info(transaction_id) except Exception: return if pending_txn.get("confirmed-round", 0) > 0: return pending_txn elif pending_txn["pool-error"]: raise Exception('pool error: {}'.format(pending_txn["pool-error"])) client.status_after_block(current_round) current_round += 1 raise Exception( 'pending tx not found in timeout rounds, timeout value = : {}'.format( timeout))
def sign_send_wait(algod_client: algod.AlgodClient, account: Account, txn): """Sign a transaction, submit it, and wait for its confirmation.""" signed_txn = sign(account, txn) tx_id = signed_txn.transaction.get_txid() algod_client.send_transactions([signed_txn]) wait_for_confirmation(algod_client, tx_id) return algod_client.pending_transaction_info(tx_id)
def notify(algod_client: algod.AlgodClient, user: Account, seller: Account, trade_gtxn: list): params = algod_client.suggested_params() note = { 'buy_order': 'AlgoRealm Special Card', 'asset_id': CARD_ID, 'algo_amount': trade_gtxn[2].transaction.amt / 10**6, 'algo_royalty': (trade_gtxn[3].amt + trade_gtxn[4].amt) / 10**6, 'last_valid_block': trade_gtxn[2].transaction.last_valid_round } bytes_note = msgpack.packb(note) notification_txn = transaction.PaymentTxn( sender=user.address, sp=params, receiver=seller.address, amt=0, note=bytes_note, ) signed_txn = sign(user, notification_txn) tx_id = signed_txn.transaction.get_txid() print("✉️ Sending buy order notification to the Seller...\n") algod_client.send_transactions([signed_txn]) wait_for_confirmation(algod_client, tx_id) print("\n📄 Buy order notification:\n" "https://algoexplorer.io/tx/" + tx_id)
def check_from_algod(args): global graceful_stop token, addr = token_addr_from_args(args) algod = AlgodClient(token, addr) if args.genesis or args.algod: gpath = args.genesis or os.path.join(args.algod, 'genesis.json') getGenesisVars(gpath) else: process_genesis(algod.algod_request("GET", "/genesis")) status = algod.status() iloadstart = time.time() indexer_headers = None gtaddr = args.gtaddr maxaddr = None if args.shard: minaddr, maxaddr = shard_bounds(args.shard) if minaddr is not None: gtaddr = algosdk.encoding.encode_address(minaddr) if args.indexer_token: indexer_headers = {'X-Indexer-API-Token': token} lastlog = time.time() i2a_checker = CheckContext(sys.stderr) # for each account in indexer, get it from algod, and maybe re-get it from indexer at the round algod had ia_queue = queue.Queue(maxsize=10) threads = [] for i in range(args.threads): t = threading.Thread(target=compare_thread, args=(ia_queue, i2a_checker, algod, args.indexer, indexer_headers)) t.start() threads.append(t) lastaddr = None for indexer_account in indexerAccounts(args.indexer, addrlist=(args.accounts and args.accounts.split(',')), headers=indexer_headers, gtaddr=gtaddr): lastaddr = indexer_account['address'] if maxaddr is not None: la = algosdk.encoding.decode_address(lastaddr) if la > maxaddr: break ia_queue.put(indexer_account) if graceful_stop: break now = time.time() if (now - lastlog) > 5: with i2a_checker.lock: logger.info('%d match, %d neq, through about %s', i2a_checker.match, i2a_checker.neq, lastaddr) lastlog = now for t in threads: ia_queue.put(None) # signal end for t in threads: t.join() if graceful_stop: logger.info('last addr %s', lastaddr) return i2a_checker
def check_from_algod(args): gpath = args.genesis or os.path.join(args.algod, 'genesis.json') getGenesisVars(gpath) token, addr = token_addr_from_args(args) algod = AlgodClient(token, addr) status = algod.status() #logger.debug('status %r', status) iloadstart = time.time() if args.indexer: i2a = indexerAccounts(args.indexer, addrlist=(args.accounts and args.accounts.split(','))) i2a_checker = CheckContext(i2a, sys.stderr) else: logger.warn('no indexer, nothing to do') return None, None iloadtime = time.time() - iloadstart logger.info('loaded %d accounts from indexer in %.1f seconds, %.1f a/s', len(i2a), iloadtime, len(i2a) / iloadtime) aloadstart = time.time() rawad = [] for address in i2a.keys(): niceaddr = algosdk.encoding.encode_address(address) rawad.append(algod.account_info(niceaddr)) aloadtime = time.time() - aloadstart logger.info('loaded %d accounts from algod in %.1f seconds, %.1f a/s', len(rawad), aloadtime, len(rawad) / aloadtime) lastlog = time.time() count = 0 for ad in rawad: niceaddr = ad['address'] # fetch indexer account at round algod got it at na = indexerAccounts(args.indexer, blockround=ad['round'], addrlist=[niceaddr]) i2a.update(na) microalgos = ad['amount-without-pending-rewards'] address = algosdk.encoding.decode_address(niceaddr) i2a_checker.check(address, niceaddr, microalgos, ad.get('assets'), ad.get('created-assets'), ad.get('created-apps'), ad.get('apps-local-state')) count += 1 now = time.time() if (now - lastlog) > 5: logger.info('%d accounts checked, %d mismatches', count, len(i2a_checker.mismatches)) lastlog = now return i2a_checker
def claim_nft( algod_client: algod.AlgodClient, indexer_client: indexer.IndexerClient, claimer: Account, claim_arg: str, new_majesty: str, donation_amount: int, nft_id: int, ): params = algod_client.suggested_params() claim_txn = transaction.ApplicationNoOpTxn( sender=claimer.address, sp=params, index=ALGOREALM_APP_ID, app_args=[claim_arg.encode(), new_majesty.encode()]) donation_txn = transaction.PaymentTxn( sender=claimer.address, sp=params, receiver=REWARDS_POOL, amt=donation_amount, ) nft_transfer = transaction.AssetTransferTxn( sender=ALGOREALM_LAW.address, sp=params, receiver=claimer.address, amt=1, index=nft_id, revocation_target=current_owner(indexer_client, nft_id), ) signed_group = group_and_sign( [claimer, claimer, ALGOREALM_LAW], [claim_txn, donation_txn, nft_transfer], ) nft_name = algod_client.asset_info(nft_id)['params']['name'] print(f"Claiming the {nft_name} as {new_majesty}, " f"donating {donation_amount / 10 ** 6} ALGO...\n") try: gtxn_id = algod_client.send_transactions(signed_group) wait_for_confirmation(algod_client, gtxn_id) except AlgodHTTPError: quit("\n☹️ Were you too stingy? Only generous hearts will rule over " "Algorand Realm!\n️")
def sign_and_send(transaction: str, passphrase: str, client: algod.AlgodClient) -> Dict[str, str or int]: """ sign and send a transaction to the algorand network. Return the transaction info when the transaction is completed. :param transaction -> ``algosdk.transaction.AssetTransferTxn``: the transaction object. :param passphrase -> ``str``: the public key for the user involved in the transaction. :param client -> ``algod.AlgodClient``: an algorand client object. """ private_key = mnemonic.to_private_key(passphrase) signed_transaction = transaction.sign(private_key) transaction_id = signed_transaction.transaction.get_txid() client.send_transaction(signed_transaction, headers={'content-type': 'application/x-binary'}) transaction_info = wait_for_confirmation(client, transaction_id) return transaction_info
def order_summary(algod_client: algod.AlgodClient, trade_gtxn: list): current_round = algod_client.status()["last-round"] last_valid_round = trade_gtxn[2].transaction.last_valid_round remaning_rounds = last_valid_round - current_round if remaning_rounds <= 0: remaning_rounds = 'Buy order expired!' return f"""
def wait_for_confirmation(client: algod.AlgodClient, txid: str): """ Utility function to wait until the transaction is confirmed before proceeding. """ last_round = client.status().get("last-round") txinfo = client.pending_transaction_info(txid) while not txinfo.get("confirmed-round", -1) > 0: print(f"Waiting for transaction {txid} confirmation.") last_round += 1 client.status_after_block(last_round) txinfo = client.pending_transaction_info(txid) print( f"Transaction {txid} confirmed in round {txinfo.get('confirmed-round')}." ) return txinfo
def algod(self): "return an open algosdk.v2client.algod.AlgodClient" if self._algod is None: if self.algorand_data: token, addr = token_addr_from_algod(self.algorand_data) else: token = self.token addr = self.addr self._algod = AlgodClient(token, addr, headers=self.headers) return self._algod
def claim_card(algod_client: algod.AlgodClient, claimer: Account): params = algod_client.suggested_params() proof_crown_ownership = proof_asa_amount_eq_txn( algod_client=algod_client, sender=claimer, asa_id=CROWN_ID, asa_amount=1, ) proof_sceptre_ownership = proof_asa_amount_eq_txn( algod_client=algod_client, sender=claimer, asa_id=SCEPTRE_ID, asa_amount=1, ) nft_card_xfer = transaction.AssetTransferTxn( sender=CARD_CONTRACT.address, sp=params, receiver=claimer.address, amt=1, index=CARD_ID, revocation_target=CARD_CONTRACT.address, ) signed_group = group_and_sign( [claimer, claimer, CARD_CONTRACT], [proof_crown_ownership, proof_sceptre_ownership, nft_card_xfer], ) try: gtxn_id = algod_client.send_transactions(signed_group) wait_for_confirmation(algod_client, gtxn_id) except AlgodHTTPError: quit("\nOnly the generous heart of the Great Majesty of Algorand " "can break the spell!\n" "Conquer both the 👑 Crown of Entropy and the 🪄 Sceptre " "of Proof first!\n")
def add_network_params( client: algod.AlgodClient, tx_data: Dict[str, str or int]) -> Dict[str, str or int]: """ Adds network-related parameters to supplied transaction data. :param client -> ``algod.AlgodClient``: an algorand client object. :param tx_data -> ``Dict[str, str or int]``: data for the transaction. """ params = client.suggested_params() tx_data["first"] = params.first tx_data["last"] = params.last tx_data["gh"] = params.gh tx_data["gen"] = params.gen tx_data["fee"] = round(convert_algos_to_microalgo(.01)) tx_data["flat_fee"] = True return tx_data
def proof_asa_amount_eq_txn( algod_client: algod.AlgodClient, sender: Account, asa_id: int, asa_amount: int, ): params = algod_client.suggested_params() proof_txn = transaction.ApplicationNoOpTxn( sender=sender.address, sp=params, index=ASA_STATE_OBSERVER_APP_ID, app_args=["AsaAmountEq".encode(), asa_amount], foreign_assets=[asa_id], accounts=[sender.address], ) return proof_txn
def restart(self): """ This method restart the application from the point when it tries to connect to a node to display wallets. This method can also be used to do the first start. This is done by deleting any existing WalletFrame and creating a new one. This method also makes sure that new settings are refreshed into new rest endpoints. """ # This will be enabled in the future when it can be called. (i.e.: there exists at least one wallet) self.menuAction_NewTransaction.setEnabled(False) if self.queuedWidget.count() >= 1: self.queuedWidget.clear_queue() endpoints = SettingsWindow.calculate_rest_endpoints() # TODO: Check that those settings actually point to an online daemon. if "kmd" in endpoints: self.kmd_client = KMDClient( endpoints["kmd"]["token"], endpoints["kmd"]["address"] ) if "algod" in endpoints: self.algod_client = AlgodClient( endpoints["algod"]["token"], endpoints["algod"]["address"] ) if "indexer" in endpoints: self.indexer_client = IndexerClient( endpoints["indexer"]["token"], endpoints["indexer"]["address"] ) if self.kmd_client: try: self.kmd_client.versions() except Exception: QtWidgets.QMessageBox.critical(self, "kmd settings", "Please check kmd settings.\n" "Kmd daemon could be offline.") else: self.queuedWidget.add_widget( WalletsFrame(self, self.kmd_client, self.algod_client, self.indexer_client) ) else: QtWidgets.QMessageBox.critical(self, "kmd settings", "kmd settings not found.")
def sell_card(algod_client: algod.AlgodClient, user: Account): trade_gtxn = transaction.retrieve_from_file('trade_raw.gtxn') signed_crown_proof = trade_gtxn[0].sign(user.private_key) signed_sceptre_proof = trade_gtxn[1].sign(user.private_key) signed_royalty_1 = trade_gtxn[3].sign(user.private_key) signed_royalty_2 = trade_gtxn[4].sign(user.private_key) trade_gtxn[0] = signed_crown_proof trade_gtxn[1] = signed_sceptre_proof trade_gtxn[3] = signed_royalty_1 trade_gtxn[4] = signed_royalty_2 print(f"🤝 Selling the AlgoRealm Special Card for " f"{trade_gtxn[2].transaction.amt / 10 ** 6} ALGO:\n") try: gtxn_id = algod_client.send_transactions(trade_gtxn) except AlgodHTTPError: quit("You must hold the 👑 Crown and the 🪄 Scepter to sell the Card!\n") else: return wait_for_confirmation(algod_client, gtxn_id)
def card_order( algod_client: algod.AlgodClient, buyer: Account, seller: Account, price: int, ): params = algod_client.suggested_params() proof_crown_ownership = proof_asa_amount_eq_txn( algod_client=algod_client, sender=seller, asa_id=CROWN_ID, asa_amount=1, ) proof_sceptre_ownership = proof_asa_amount_eq_txn( algod_client=algod_client, sender=seller, asa_id=SCEPTRE_ID, asa_amount=1, ) nft_card_payment = transaction.PaymentTxn( sender=buyer.address, sp=params, receiver=seller.address, amt=price, ) royalty_amount = math.ceil(price * ROYALTY_PERC / 100) royalty_1_payment = transaction.PaymentTxn( sender=seller.address, sp=params, receiver=ROYALTY_COLLECTOR_1.address, amt=royalty_amount, ) royalty_2_payment = transaction.PaymentTxn( sender=seller.address, sp=params, receiver=ROYALTY_COLLECTOR_2.address, amt=royalty_amount, ) nft_card_xfer = transaction.AssetTransferTxn( sender=CARD_CONTRACT.address, sp=params, receiver=buyer.address, amt=1, index=CARD_ID, revocation_target=seller.address, ) trade_gtxn = [ proof_crown_ownership, proof_sceptre_ownership, nft_card_payment, royalty_1_payment, royalty_2_payment, nft_card_xfer ] transaction.assign_group_id(trade_gtxn) signed_nft_card_payment = trade_gtxn[2].sign(buyer.private_key) trade_gtxn[2] = signed_nft_card_payment trade_gtxn[5] = transaction.LogicSigTransaction(trade_gtxn[5], CARD_CONTRACT.lsig) transaction.write_to_file(trade_gtxn, 'trade_raw.gtxn', overwrite=True) print( "📝 Partially signed trade group transaction saved as: 'trade.gtxn'\n") return trade_gtxn