def generate_secure_bundle( self, selected_coins: List[Coin], announcements: List[Announcement], offered_amount: uint64, tail_str: Optional[str] = None, ) -> SpendBundle: announcement_assertions: List[List] = [[63, a.name()] for a in announcements] selected_coin_amount: int = sum([c.amount for c in selected_coins]) non_primaries: List[Coin] = [] if len( selected_coins) < 2 else selected_coins[1:] inner_solution: List[List] = [ [51, Offer.ph(), offered_amount], # Offered coin [51, acs_ph, uint64(selected_coin_amount - offered_amount)], # Change *announcement_assertions, ] if tail_str is None: bundle = SpendBundle( [ CoinSpend( selected_coins[0], acs, Program.to(inner_solution), ), *[ CoinSpend(c, acs, Program.to([])) for c in non_primaries ], ], G2Element(), ) else: spendable_cats: List[SpendableCAT] = [ SpendableCAT( c, str_to_tail_hash(tail_str), acs, Program.to([ [51, 0, -113, str_to_tail(tail_str), Program.to([])], # Use the TAIL rather than lineage *(inner_solution if c == selected_coins[0] else []), ]), ) for c in selected_coins ] bundle = unsigned_spend_bundle_for_spendable_cats( CAT_MOD, spendable_cats) return bundle
async def _create_offer_for_ids( self, offer_dict: Dict[Union[int, bytes32], int], fee: uint64 = uint64(0) ) -> Tuple[bool, Optional[Offer], Optional[str]]: """ Offer is dictionary of wallet ids and amount """ try: coins_to_offer: Dict[uint32, List[Coin]] = {} requested_payments: Dict[Optional[bytes32], List[Payment]] = {} for id, amount in offer_dict.items(): if amount > 0: if isinstance(id, int): wallet_id = uint32(id) wallet = self.wallet_state_manager.wallets[wallet_id] p2_ph: bytes32 = await wallet.get_new_puzzlehash() if wallet.type() == WalletType.STANDARD_WALLET: key: Optional[bytes32] = None memos: List[bytes] = [] elif wallet.type() == WalletType.CAT: key = bytes32(bytes.fromhex(wallet.get_asset_id())) memos = [p2_ph] else: raise ValueError(f"Offers are not implemented for {wallet.type()}") else: p2_ph = await self.wallet_state_manager.main_wallet.get_new_puzzlehash() key = id memos = [p2_ph] requested_payments[key] = [Payment(p2_ph, uint64(amount), memos)] elif amount < 0: assert isinstance(id, int) wallet_id = uint32(id) wallet = self.wallet_state_manager.wallets[wallet_id] balance = await wallet.get_confirmed_balance() if balance < abs(amount): raise Exception(f"insufficient funds in wallet {wallet_id}") coins_to_offer[wallet_id] = await wallet.select_coins(uint64(abs(amount))) elif amount == 0: raise ValueError("You cannot offer nor request 0 amount of something") all_coins: List[Coin] = [c for coins in coins_to_offer.values() for c in coins] notarized_payments: Dict[Optional[bytes32], List[NotarizedPayment]] = Offer.notarize_payments( requested_payments, all_coins ) announcements_to_assert = Offer.calculate_announcements(notarized_payments) all_transactions: List[TransactionRecord] = [] fee_left_to_pay: uint64 = fee for wallet_id, selected_coins in coins_to_offer.items(): wallet = self.wallet_state_manager.wallets[wallet_id] # This should probably not switch on whether or not we're spending a CAT but it has to for now if wallet.type() == WalletType.CAT: txs = await wallet.generate_signed_transaction( [abs(offer_dict[int(wallet_id)])], [Offer.ph()], fee=fee_left_to_pay, coins=set(selected_coins), puzzle_announcements_to_consume=announcements_to_assert, ) all_transactions.extend(txs) else: tx = await wallet.generate_signed_transaction( abs(offer_dict[int(wallet_id)]), Offer.ph(), fee=fee_left_to_pay, coins=set(selected_coins), puzzle_announcements_to_consume=announcements_to_assert, ) all_transactions.append(tx) fee_left_to_pay = uint64(0) transaction_bundles: List[Optional[SpendBundle]] = [tx.spend_bundle for tx in all_transactions] total_spend_bundle = SpendBundle.aggregate(list(filter(lambda b: b is not None, transaction_bundles))) offer = Offer(notarized_payments, total_spend_bundle) return True, offer, None except Exception as e: tb = traceback.format_exc() self.log.error(f"Error with creating trade offer: {type(e)}{tb}") return False, None, str(e)