def launcher_conditions_and_spend_bundle( parent_coin_id: bytes32, launcher_amount: uint64, initial_singleton_inner_puzzle: Program, metadata: List[Tuple[str, str]], launcher_puzzle: Program = LAUNCHER_PUZZLE, ) -> Tuple[Program, bytes32, List[Program], SpendBundle]: launcher_puzzle_hash = launcher_puzzle.get_tree_hash() launcher_coin = Coin(parent_coin_id, launcher_puzzle_hash, launcher_amount) singleton_full_puzzle = SINGLETON_MOD.curry( SINGLETON_MOD_HASH, launcher_coin.name(), launcher_puzzle_hash, initial_singleton_inner_puzzle) singleton_full_puzzle_hash = singleton_full_puzzle.get_tree_hash() message_program = Program.to( [singleton_full_puzzle_hash, launcher_amount, metadata]) expected_announcement = Announcement(launcher_coin.name(), message_program.get_tree_hash()) expected_conditions = [] expected_conditions.append( Program.to( binutils.assemble( f"(0x{ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT.hex()} 0x{expected_announcement.name()})" ))) expected_conditions.append( Program.to( binutils.assemble( f"(0x{ConditionOpcode.CREATE_COIN.hex()} 0x{launcher_puzzle_hash} {launcher_amount})" ))) launcher_solution = Program.to( [singleton_full_puzzle_hash, launcher_amount, metadata]) coin_spend = CoinSpend(launcher_coin, launcher_puzzle, launcher_solution) spend_bundle = SpendBundle([coin_spend], G2Element()) lineage_proof = Program.to([parent_coin_id, launcher_amount]) return lineage_proof, launcher_coin.name( ), expected_conditions, spend_bundle
async def get_unspent_coins_for_wallet( self, wallet_id: int) -> Set[WalletCoinRecord]: """ Returns set of CoinRecords that have not been spent yet for a wallet. """ async with self.wallet_cache_lock: if wallet_id in self.coin_wallet_record_cache: wallet_coins: Dict[ bytes32, WalletCoinRecord] = self.coin_wallet_record_cache[ wallet_id] return set(wallet_coins.values()) coin_set = set() cursor = await self.db_connection.execute( "SELECT * from coin_record WHERE spent=0 and wallet_id=?", (wallet_id, ), ) rows = await cursor.fetchall() await cursor.close() cache_dict = {} for row in rows: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) coin_record = WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9]) coin_set.add(coin_record) cache_dict[coin.name()] = coin_record self.coin_wallet_record_cache[wallet_id] = cache_dict return coin_set
def claim_p2_singleton( p2_singleton_coin: Coin, singleton_inner_puzhash: bytes32, launcher_id: bytes32, delay_time: Optional[uint64] = None, delay_ph: Optional[bytes32] = None, ) -> Tuple[Program, Program, CoinSpend]: assertion = Program.to([ ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, std_hash(p2_singleton_coin.name() + b"$") ]) announcement = Program.to( [ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT, p2_singleton_coin.name()]) if delay_time is None or delay_ph is None: puzzle: Program = pay_to_singleton_puzzle(launcher_id) else: puzzle = pay_to_singleton_or_delay_puzzle( launcher_id, delay_time, delay_ph, ) claim_coinsol = CoinSpend( p2_singleton_coin, puzzle, solution_for_p2_singleton(p2_singleton_coin, singleton_inner_puzhash), ) return assertion, announcement, claim_coinsol
async def generate_eve_spend(self, coin: Coin, full_puzzle: Program, innerpuz: Program): assert self.did_info.origin_coin is not None # innerpuz solution is (mode amount message my_id my_puzhash parent_innerpuzhash_amounts_for_recovery_ids) innersol = Program.to([ 0, coin.amount, coin.puzzle_hash, coin.name(), coin.puzzle_hash, [] ]) # full solution is (parent_info my_amount innersolution) fullsol = Program.to([ [ self.did_info.origin_coin.parent_coin_info, self.did_info.origin_coin.amount ], coin.parent_coin_info, coin.amount, innersol, ]) list_of_solutions = [CoinSolution(coin, full_puzzle, fullsol)] # sign for AGG_SIG_ME message = ( Program.to([coin.amount, coin.puzzle_hash]).get_tree_hash() + coin.name() + self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA) pubkey = did_wallet_puzzles.get_pubkey_from_innerpuz(innerpuz) index = await self.wallet_state_manager.puzzle_store.index_for_pubkey( pubkey) private = master_sk_to_wallet_sk(self.wallet_state_manager.private_key, index) signature = AugSchemeMPL.sign(private, message) sigs = [signature] aggsig = AugSchemeMPL.aggregate(sigs) spend_bundle = SpendBundle(list_of_solutions, aggsig) return spend_bundle
def coin_spend_for_lock_coin( prev_coin: Coin, subtotal: int, coin: Coin, ) -> CoinSpend: puzzle_reveal = LOCK_INNER_PUZZLE.curry(prev_coin.as_list(), subtotal) coin = Coin(coin.name(), puzzle_reveal.get_tree_hash(), uint64(0)) coin_spend = CoinSpend(coin, puzzle_reveal, Program.to(0)) return coin_spend
async def check_all_there(): spendable = await cc_wallet.get_cc_spendable_coins() spendable_name_set = set() for record in spendable: spendable_name_set.add(record.coin.name()) puzzle_hash = cc_puzzle_hash_for_inner_puzzle_hash(CC_MOD, cc_wallet.cc_info.my_genesis_checker, cc_2_hash) for i in range(1, 50): coin = Coin(spent_coint.name(), puzzle_hash, i) if coin.name() not in spendable_name_set: return False return True
async def generate_new_decentralised_id( self, amount: uint64) -> Optional[SpendBundle]: """ This must be called under the wallet state manager lock """ coins = await self.standard_wallet.select_coins(amount) if coins is None: return None origin = coins.copy().pop() did_inner: Program = await self.get_new_innerpuz() did_inner_hash = did_inner.get_tree_hash() did_puz = did_wallet_puzzles.create_fullpuz(did_inner, origin.puzzle_hash) did_puzzle_hash = did_puz.get_tree_hash() tx_record: Optional[ TransactionRecord] = await self.standard_wallet.generate_signed_transaction( amount, did_puzzle_hash, uint64(0), origin.name(), coins) eve_coin = Coin(origin.name(), did_puzzle_hash, amount) future_parent = CCParent( eve_coin.parent_coin_info, did_inner_hash, eve_coin.amount, ) eve_parent = CCParent( origin.parent_coin_info, origin.puzzle_hash, origin.amount, ) await self.add_parent(eve_coin.parent_coin_info, eve_parent, False) await self.add_parent(eve_coin.name(), future_parent, False) if tx_record is None or tx_record.spend_bundle is None: return None # Only want to save this information if the transaction is valid did_info: DIDInfo = DIDInfo( origin, self.did_info.backup_ids, self.did_info.num_of_backup_ids_needed, self.did_info.parent_info, did_inner, None, None, None, ) await self.save_info(did_info, False) eve_spend = await self.generate_eve_spend(eve_coin, did_puz, did_inner) full_spend = SpendBundle.aggregate([tx_record.spend_bundle, eve_spend]) return full_spend
async def check_all_there(): spendable = await cat_wallet.get_cat_spendable_coins() spendable_name_set = set() for record in spendable: spendable_name_set.add(record.coin.name()) puzzle_hash = construct_cat_puzzle( CAT_MOD, cat_wallet.cat_info.limitations_program_hash, cat_2).get_tree_hash() for i in range(1, 50): coin = Coin(spent_coint.name(), puzzle_hash, i) if coin.name() not in spendable_name_set: return False return True
async def coin_removed(self, coin: Coin, height: uint32, wallet_id: int): """ Called when coin gets spent """ await self.coin_store.set_spent(coin.name(), height) unconfirmed_record: List[ TransactionRecord] = await self.tx_store.unconfirmed_with_removal_coin( coin.name()) for unconfirmed in unconfirmed_record: await self.tx_store.set_confirmed(unconfirmed.name, height) self.state_changed("coin_removed", wallet_id)
async def generate_eve_spend(self, coin: Coin, full_puzzle: Program, innerpuz: Program): assert self.did_info.origin_coin is not None # innerpuz solution is (mode amount message new_puzhash) innersol = Program.to([1, coin.amount, [], innerpuz.get_tree_hash()]) # full solution is (lineage_proof my_amount inner_solution) fullsol = Program.to( [ [self.did_info.origin_coin.parent_coin_info, self.did_info.origin_coin.amount], coin.amount, innersol, ] ) list_of_solutions = [CoinSpend(coin, full_puzzle, fullsol)] # sign for AGG_SIG_ME message = ( Program.to([innerpuz.get_tree_hash(), coin.amount, []]).get_tree_hash() + coin.name() + self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA ) pubkey = did_wallet_puzzles.get_pubkey_from_innerpuz(innerpuz) record: Optional[DerivationRecord] = await self.wallet_state_manager.puzzle_store.record_for_pubkey(pubkey) assert record is not None private = master_sk_to_wallet_sk_unhardened(self.wallet_state_manager.private_key, record.index) signature = AugSchemeMPL.sign(private, message) sigs = [signature] aggsig = AugSchemeMPL.aggregate(sigs) spend_bundle = SpendBundle(list_of_solutions, aggsig) return spend_bundle
async def fetch_puzzle_solution(self, peer, height: uint32, coin: Coin) -> CoinSpend: solution_response = await peer.request_puzzle_solution( wallet_protocol.RequestPuzzleSolution(coin.name(), height) ) if solution_response is None or not isinstance(solution_response, wallet_protocol.RespondPuzzleSolution): raise ValueError(f"Was not able to obtain solution {solution_response}") return CoinSpend(coin, solution_response.response.puzzle, solution_response.response.solution)
async def create_signed_transaction(self, request): if "additions" not in request or len(request["additions"]) < 1: raise ValueError("Specify additions list") additions: List[Dict] = request["additions"] amount_0: uint64 = uint64(additions[0]["amount"]) assert amount_0 <= self.service.constants.MAX_COIN_AMOUNT puzzle_hash_0 = hexstr_to_bytes(additions[0]["puzzle_hash"]) if len(puzzle_hash_0) != 32: raise ValueError(f"Address must be 32 bytes. {puzzle_hash_0}") additional_outputs = [] for addition in additions[1:]: receiver_ph = hexstr_to_bytes(addition["puzzle_hash"]) if len(receiver_ph) != 32: raise ValueError(f"Address must be 32 bytes. {receiver_ph}") amount = uint64(addition["amount"]) if amount > self.service.constants.MAX_COIN_AMOUNT: raise ValueError(f"Coin amount cannot exceed {self.service.constants.MAX_COIN_AMOUNT}") additional_outputs.append({"puzzlehash": receiver_ph, "amount": amount}) fee = uint64(0) if "fee" in request: fee = uint64(request["fee"]) coins = None if "coins" in request and len(request["coins"]) > 0: coins = set([Coin.from_json_dict(coin_json) for coin_json in request["coins"]]) signed_tx = await self.service.wallet_state_manager.main_wallet.generate_signed_transaction( amount_0, puzzle_hash_0, fee, coins=coins, ignore_max_send_amount=True, primaries=additional_outputs ) return {"signed_tx": signed_tx}
async def get_coin_records_by_parent_ids( self, include_spent_coins: bool, parent_ids: List[bytes32], start_height: uint32 = uint32(0), end_height: uint32 = uint32((2 ** 32) - 1), ) -> List[CoinRecord]: if len(parent_ids) == 0: return [] coins = set() parent_ids_db = tuple([pid.hex() for pid in parent_ids]) cursor = await self.coin_record_db.execute( f'SELECT * from coin_record WHERE coin_parent in ({"?," * (len(parent_ids_db) - 1)}?) ' f"AND confirmed_index>=? AND confirmed_index<? " f"{'' if include_spent_coins else 'AND spent=0'}", parent_ids_db + (start_height, end_height), ) rows = await cursor.fetchall() await cursor.close() for row in rows: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) coins.add(CoinRecord(coin, row[1], row[2], row[3], row[4], row[8])) return list(coins)
async def coin_added(self, coin: Coin, _: uint32): """Notification from wallet state manager that wallet has been received.""" self.log.info("DID wallet has been notified that coin was added") inner_puzzle = await self.inner_puzzle_for_did_puzzle(coin.puzzle_hash) if self.did_info.temp_coin is not None: self.wallet_state_manager.state_changed("did_coin_added", self.wallet_info.id) new_info = DIDInfo( self.did_info.origin_coin, self.did_info.backup_ids, self.did_info.num_of_backup_ids_needed, self.did_info.parent_info, inner_puzzle, None, None, None, False, ) await self.save_info(new_info, True) future_parent = LineageProof( coin.parent_coin_info, inner_puzzle.get_tree_hash(), coin.amount, ) await self.add_parent(coin.name(), future_parent, True)
def to_spend_bundle(self) -> SpendBundle: # Before we serialze this as a SpendBundle, we need to serialze the `requested_payments` as dummy CoinSpends additional_coin_spends: List[CoinSpend] = [] for tail_hash, payments in self.requested_payments.items(): puzzle_reveal: Program = construct_cat_puzzle( CAT_MOD, tail_hash, OFFER_MOD) if tail_hash else OFFER_MOD inner_solutions = [] nonces: List[bytes32] = [p.nonce for p in payments] for nonce in list( dict.fromkeys(nonces)): # dedup without messing with order nonce_payments: List[NotarizedPayment] = list( filter(lambda p: p.nonce == nonce, payments)) inner_solutions.append( (nonce, [np.as_condition_args() for np in nonce_payments])) additional_coin_spends.append( CoinSpend( Coin( ZERO_32, puzzle_reveal.get_tree_hash(), uint64(0), ), puzzle_reveal, Program.to(inner_solutions), )) return SpendBundle.aggregate([ SpendBundle(additional_coin_spends, G2Element()), self.bundle, ])
def claim_p2_singleton( puzzle_db: PuzzleDB, singleton_wallet: SingletonWallet, p2_singleton_coin: Coin) -> Tuple[CoinSpend, List[Program]]: inner_puzzle = singleton_wallet.inner_puzzle(puzzle_db) assert inner_puzzle inner_puzzle_hash = inner_puzzle.get_tree_hash() p2_singleton_puzzle = puzzle_db.puzzle_for_hash( p2_singleton_coin.puzzle_hash) assert p2_singleton_puzzle is not None p2_singleton_coin_name = p2_singleton_coin.name() p2_singleton_solution = solve_puzzle( puzzle_db, p2_singleton_puzzle, p2_singleton_spend_type="claim-p2-nft", singleton_inner_puzzle_hash=inner_puzzle_hash, p2_singleton_coin_name=p2_singleton_coin_name, ) p2_singleton_coin_spend = CoinSpend( p2_singleton_coin, p2_singleton_puzzle.to_serialized_program(), p2_singleton_solution, ) expected_p2_singleton_announcement = Announcement(p2_singleton_coin_name, bytes(b"$")).name() singleton_conditions = [ Program.to([ ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT, p2_singleton_coin_name ]), Program.to([ConditionOpcode.CREATE_COIN, inner_puzzle_hash, 1]), Program.to([ ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, expected_p2_singleton_announcement ]), ] return p2_singleton_coin_spend, singleton_conditions
def create_spend_for_message(parent_of_message, recovering_coin, newpuz, pubkey): puzzle = create_recovery_message_puzzle(recovering_coin, newpuz, pubkey) coin = Coin(parent_of_message, puzzle.get_tree_hash(), uint64(0)) solution = Program.to([]) coinsol = CoinSolution(coin, puzzle, solution) return coinsol
def do_inspect_coin_cmd(ctx, coins, print_results=True, **kwargs): if kwargs and all([kwargs[key] for key in kwargs.keys()]): coin_objs = [ Coin(bytes.fromhex(kwargs['parent_id']), bytes.fromhex(kwargs['puzzle_hash']), uint64(kwargs['amount'])) ] elif not kwargs or not any([kwargs[key] for key in kwargs.keys()]): coin_objs = [] try: if type(coins[0]) == str: coin_objs = streamable_load(Coin, coins) else: coin_objs = coins except: print("One or more of the specified objects was not a coin") else: print("Invalid arguments specified.") return if print_results: inspect_callback(coin_objs, ctx, id_calc=(lambda e: e.name()), type='Coin') else: return coin_objs
def launcher_conditions_and_spend_bundle( puzzle_db: PuzzleDB, parent_coin_id: bytes32, launcher_amount: uint64, initial_singleton_inner_puzzle: Program, metadata: List[Tuple[str, str]], launcher_puzzle: Program, ) -> Tuple[bytes32, List[Program], SpendBundle]: puzzle_db.add_puzzle(launcher_puzzle) launcher_puzzle_hash = launcher_puzzle.get_tree_hash() launcher_coin = Coin(parent_coin_id, launcher_puzzle_hash, launcher_amount) # TODO: address hint error and remove ignore # error: Argument 1 to "singleton_puzzle" has incompatible type "bytes32"; expected "Program" [arg-type] singleton_full_puzzle = singleton_puzzle( launcher_coin.name(), # type: ignore[arg-type] launcher_puzzle_hash, initial_singleton_inner_puzzle, ) puzzle_db.add_puzzle(singleton_full_puzzle) singleton_full_puzzle_hash = singleton_full_puzzle.get_tree_hash() message_program = Program.to( [singleton_full_puzzle_hash, launcher_amount, metadata]) expected_announcement = Announcement(launcher_coin.name(), message_program.get_tree_hash()) expected_conditions = [] expected_conditions.append( Program.to( binutils.assemble( f"(0x{ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT.hex()} 0x{expected_announcement.name()})" ))) expected_conditions.append( Program.to( binutils.assemble( f"(0x{ConditionOpcode.CREATE_COIN.hex()} 0x{launcher_puzzle_hash} {launcher_amount})" ))) solution = solve_puzzle( puzzle_db, launcher_puzzle, destination_puzzle_hash=singleton_full_puzzle_hash, launcher_amount=launcher_amount, metadata=metadata, ) coin_spend = CoinSpend(launcher_coin, SerializedProgram.from_program(launcher_puzzle), solution) spend_bundle = SpendBundle([coin_spend], G2Element()) return launcher_coin.name(), expected_conditions, spend_bundle
def farm_coin(self, puzzle_hash: bytes32, birthday: CoinTimestamp, amount: int = 1024) -> Coin: parent = birthday.height.to_bytes(32, "big") coin = Coin(parent, puzzle_hash, uint64(amount)) self._add_coin_entry(coin, birthday) return coin
def make_fake_coin(index: int, puzzle_hash_db: dict) -> Coin: """ Make a fake coin with parent id equal to the index (ie. a genesis block coin) """ parent = index.to_bytes(32, "big") puzzle_hash = puzzle_hash_for_index(index, puzzle_hash_db) amount = 100000 return Coin(parent, puzzle_hash, uint64(amount))
def compute_memos(bundle: SpendBundle) -> Dict[bytes32, List[bytes]]: """ Retrieves the memos for additions in this spend_bundle, which are formatted as a list in the 3rd parameter of CREATE_COIN. If there are no memos, the addition coin_id is not included. If they are not formatted as a list of bytes, they are not included. This is expensive to call, it should not be used in full node code. """ memos: Dict[bytes32, List[bytes]] = {} for coin_spend in bundle.coin_spends: _, result = coin_spend.puzzle_reveal.run_with_cost(INFINITE_COST, coin_spend.solution) for condition in result.as_python(): if condition[0] == ConditionOpcode.CREATE_COIN and len(condition) >= 4: # If only 3 elements (opcode + 2 args), there is no memo, this is ph, amount coin_added = Coin(coin_spend.coin.name(), bytes32(condition[1]), int_from_bytes(condition[2])) if type(condition[3]) != list: # If it's not a list, it's not the correct format continue memos[coin_added.name()] = condition[3] return memos
async def get_coins_added_at_height(self, height: uint32) -> List[CoinRecord]: cursor = await self.coin_record_db.execute("SELECT * from coin_record WHERE confirmed_index=?", (height,)) rows = await cursor.fetchall() await cursor.close() coins = [] for row in rows: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) coins.append(CoinRecord(coin, row[1], row[2], row[3], row[4], row[8])) return coins
def claim_p2_singleton( p2_singleton_coin: Coin, singleton_inner_puzhash: bytes32, launcher_id: bytes32, ) -> Tuple[Program, Program, CoinSolution]: assertion = Program.to([ ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, std_hash(p2_singleton_coin.name() + b"$") ]) announcement = Program.to( [ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT, p2_singleton_coin.name()]) claim_coinsol = CoinSolution( p2_singleton_coin, pay_to_singleton_puzzle(launcher_id), solution_for_p2_singleton(p2_singleton_coin, singleton_inner_puzhash), ) return assertion, announcement, claim_coinsol
def generate_farmed_coin( block_index: int, puzzle_hash: bytes32, amount: int, ) -> Coin: """ Generate a (fake) coin which can be used as a starting point for a chain of coin tests. """ return Coin(int_as_bytes32(block_index), puzzle_hash, uint64(amount))
async def get_coin_records_by_puzzle_hash(self, puzzle_hash: bytes32) -> List[WalletCoinRecord]: """Returns a list of all coin records with the given puzzle hash""" coins = set() cursor = await self.db_connection.execute("SELECT * from coin_record WHERE puzzle_hash=?", (puzzle_hash.hex(),)) rows = await cursor.fetchall() await cursor.close() for row in rows: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) coins.add(WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9])) return list(coins)
async def get_coin_record(self, coin_name: bytes32) -> Optional[CoinRecord]: if coin_name.hex() in self.coin_record_cache: return self.coin_record_cache[coin_name.hex()] cursor = await self.coin_record_db.execute("SELECT * from coin_record WHERE coin_name=?", (coin_name.hex(),)) row = await cursor.fetchone() await cursor.close() if row is not None: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) return CoinRecord(coin, row[1], row[2], row[3], row[4], row[8]) return None
async def get_coin_record_by_coin_id(self, coin_id: bytes32) -> Optional[WalletCoinRecord]: """Returns a coin records with the given name, if it exists""" cursor = await self.db_connection.execute("SELECT * from coin_record WHERE coin_name=?", (coin_id.hex(),)) row = await cursor.fetchone() await cursor.close() if row is None: return None coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) coin_record = WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9]) return coin_record
async def get_all_coins(self) -> Set[WalletCoinRecord]: """ Returns set of all CoinRecords.""" coins = set() cursor = await self.db_connection.execute("SELECT * from coin_record") rows = await cursor.fetchall() await cursor.close() for row in rows: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) coins.add(WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9])) return coins
async def get_coin_record(self, coin_name: bytes32) -> Optional[WalletCoinRecord]: """ Returns CoinRecord with specified coin id. """ if coin_name in self.coin_record_cache: return self.coin_record_cache[coin_name] cursor = await self.db_connection.execute("SELECT * from coin_record WHERE coin_name=?", (coin_name.hex(),)) row = await cursor.fetchone() await cursor.close() if row is not None: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) return WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9]) return None