async def respond_to_offer(self, offer: Offer, fee=uint64(0)) -> Tuple[bool, Optional[TradeRecord], Optional[str]]: take_offer_dict: Dict[Union[bytes32, int], int] = {} arbitrage: Dict[Optional[bytes32], int] = offer.arbitrage() for asset_id, amount in arbitrage.items(): if asset_id is None: wallet = self.wallet_state_manager.main_wallet key: Union[bytes32, int] = int(wallet.id()) else: wallet = await self.wallet_state_manager.get_wallet_for_asset_id(asset_id.hex()) if wallet is None and amount < 0: return False, None, f"Do not have a CAT of asset ID: {asset_id} to fulfill offer" elif wallet is None: key = asset_id else: key = int(wallet.id()) take_offer_dict[key] = amount # First we validate that all of the coins in this offer exist valid: bool = await self.check_offer_validity(offer) if not valid: return False, None, "This offer is no longer valid" success, take_offer, error = await self._create_offer_for_ids(take_offer_dict, fee=fee) if not success or take_offer is None: return False, None, error complete_offer = Offer.aggregate([offer, take_offer]) assert complete_offer.is_valid() final_spend_bundle: SpendBundle = complete_offer.to_valid_spend() await self.maybe_create_wallets_for_offer(complete_offer) tx_records: List[TransactionRecord] = await self.calculate_tx_records_for_offer(complete_offer, True) trade_record: TradeRecord = TradeRecord( confirmed_at_index=uint32(0), accepted_at_time=uint64(int(time.time())), created_at_time=uint64(int(time.time())), is_my_offer=False, sent=uint32(0), offer=bytes(complete_offer), taken_offer=bytes(offer), coins_of_interest=complete_offer.get_involved_coins(), trade_id=complete_offer.name(), status=uint32(TradeStatus.PENDING_CONFIRM.value), sent_to=[], ) await self.save_trade(trade_record) # Dummy transaction for the sake of the wallet push push_tx = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=bytes32([1] * 32), amount=uint64(0), fee_amount=uint64(0), confirmed=False, sent=uint32(0), spend_bundle=final_spend_bundle, additions=[], removals=[], wallet_id=uint32(0), sent_to=[], trade_id=bytes32([1] * 32), type=uint32(TransactionType.OUTGOING_TRADE.value), name=final_spend_bundle.name(), memos=[], ) await self.wallet_state_manager.add_pending_transaction(push_tx) for tx in tx_records: await self.wallet_state_manager.add_transaction(tx) return True, trade_record, None
async def test_complex_offer(self, setup_sim): sim, sim_client = setup_sim try: coins_needed: Dict[Optional[str], List[int]] = { None: [500, 400, 300], "red": [250, 100], "blue": [3000], } all_coins: Dict[Optional[str], List[Coin]] = await self.generate_coins( sim, sim_client, coins_needed) chia_coins: List[Coin] = all_coins[None] red_coins: List[Coin] = all_coins["red"] blue_coins: List[Coin] = all_coins["blue"] # Create an XCH Offer for RED chia_requested_payments: Dict[Optional[bytes32], List[Payment]] = { str_to_tail_hash("red"): [ Payment(acs_ph, 100, [b"memo"]), Payment(acs_ph, 200, [b"memo"]), ] } chia_requested_payments: Dict[ Optional[bytes32], List[NotarizedPayment]] = Offer.notarize_payments( chia_requested_payments, chia_coins) chia_announcements: List[ Announcement] = Offer.calculate_announcements( chia_requested_payments) chia_secured_bundle: SpendBundle = self.generate_secure_bundle( chia_coins, chia_announcements, 1000) chia_offer = Offer(chia_requested_payments, chia_secured_bundle) assert not chia_offer.is_valid() # Create a RED Offer for XCH red_requested_payments: Dict[Optional[bytes32], List[Payment]] = { None: [ Payment(acs_ph, 300, [b"red memo"]), Payment(acs_ph, 400, [b"red memo"]), ] } red_requested_payments: Dict[ Optional[bytes32], List[NotarizedPayment]] = Offer.notarize_payments( red_requested_payments, red_coins) red_announcements: List[ Announcement] = Offer.calculate_announcements( red_requested_payments) red_secured_bundle: SpendBundle = self.generate_secure_bundle( red_coins, red_announcements, 350, tail_str="red") red_offer = Offer(red_requested_payments, red_secured_bundle) assert not red_offer.is_valid() # Test aggregation of offers new_offer = Offer.aggregate([chia_offer, red_offer]) assert new_offer.get_offered_amounts() == { None: 1000, str_to_tail_hash("red"): 350 } assert new_offer.get_requested_amounts() == { None: 700, str_to_tail_hash("red"): 300 } assert new_offer.is_valid() # Create yet another offer of BLUE for XCH and RED blue_requested_payments: Dict[Optional[bytes32], List[Payment]] = { None: [ Payment(acs_ph, 200, [b"blue memo"]), ], str_to_tail_hash("red"): [ Payment(acs_ph, 50, [b"blue memo"]), ], } blue_requested_payments: Dict[ Optional[bytes32], List[NotarizedPayment]] = Offer.notarize_payments( blue_requested_payments, blue_coins) blue_announcements: List[ Announcement] = Offer.calculate_announcements( blue_requested_payments) blue_secured_bundle: SpendBundle = self.generate_secure_bundle( blue_coins, blue_announcements, 2000, tail_str="blue") blue_offer = Offer(blue_requested_payments, blue_secured_bundle) assert not blue_offer.is_valid() # Test a re-aggregation new_offer: Offer = Offer.aggregate([new_offer, blue_offer]) assert new_offer.get_offered_amounts() == { None: 1000, str_to_tail_hash("red"): 350, str_to_tail_hash("blue"): 2000, } assert new_offer.get_requested_amounts() == { None: 900, str_to_tail_hash("red"): 350 } assert new_offer.summary() == ( { "xch": 1000, str_to_tail_hash("red").hex(): 350, str_to_tail_hash("blue").hex(): 2000, }, { "xch": 900, str_to_tail_hash("red").hex(): 350 }, ) assert new_offer.get_pending_amounts() == { "xch": 1200, str_to_tail_hash("red").hex(): 350, str_to_tail_hash("blue").hex(): 3000, } assert new_offer.is_valid() # Test (de)serialization assert Offer.from_bytes(bytes(new_offer)) == new_offer # Test compression assert Offer.from_compressed(new_offer.compress()) == new_offer # Make sure we can actually spend the offer once it's valid arbitrage_ph: bytes32 = Program.to([3, [], [], 1]).get_tree_hash() offer_bundle: SpendBundle = new_offer.to_valid_spend(arbitrage_ph) result = await sim_client.push_tx(offer_bundle) assert result == (MempoolInclusionStatus.SUCCESS, None) self.cost["complex offer"] = cost_of_spend_bundle(offer_bundle) await sim.farm_block() finally: await sim.close()