static void publishOffer(
     Transaction transaction, Attachment.MonetarySystemPublishExchangeOffer attachment) {
   CurrencyBuyOffer previousOffer =
       CurrencyBuyOffer.getOffer(attachment.getCurrencyId(), transaction.getSenderId());
   if (previousOffer != null) {
     removeOffer(previousOffer);
   }
   CurrencyBuyOffer.addOffer(transaction, attachment);
   CurrencySellOffer.addOffer(transaction, attachment);
 }
  static void exchangeCurrencyForNXT(
      Transaction transaction,
      Account account,
      final long currencyId,
      final long rateNQT,
      long units) {
    long extraAmountNQT = 0;
    long remainingUnits = units;

    List<CurrencyBuyOffer> currencyBuyOffers = new ArrayList<>();
    try (DbIterator<CurrencyBuyOffer> offers =
        CurrencyBuyOffer.getOffers(
            new ValidOffersDbClause(currencyId, rateNQT, true),
            0,
            -1,
            " ORDER BY rate DESC, creation_height ASC, transaction_height ASC, transaction_index ASC ")) {
      for (CurrencyBuyOffer offer : offers) {
        currencyBuyOffers.add(offer);
      }
    }

    for (CurrencyBuyOffer offer : currencyBuyOffers) {
      if (remainingUnits == 0) {
        break;
      }
      long curUnits = Math.min(Math.min(remainingUnits, offer.getSupply()), offer.getLimit());
      long curAmountNQT = Math.multiplyExact(curUnits, offer.getRateNQT());

      extraAmountNQT = Math.addExact(extraAmountNQT, curAmountNQT);
      remainingUnits = Math.subtractExact(remainingUnits, curUnits);

      offer.decreaseLimitAndSupply(curUnits);
      long excess = offer.getCounterOffer().increaseSupply(curUnits);

      Account counterAccount = Account.getAccount(offer.getAccountId());
      counterAccount.addToBalanceNQT(-curAmountNQT);
      counterAccount.addToCurrencyUnits(currencyId, curUnits);
      counterAccount.addToUnconfirmedCurrencyUnits(currencyId, excess);
      Exchange.addExchange(
          transaction, currencyId, offer, account.getId(), offer.getAccountId(), curUnits);
    }

    account.addToBalanceAndUnconfirmedBalanceNQT(extraAmountNQT);
    account.addToCurrencyUnits(currencyId, -(units - remainingUnits));
    account.addToUnconfirmedCurrencyUnits(currencyId, remainingUnits);
  }
  static void removeOffer(CurrencyBuyOffer buyOffer) {
    CurrencySellOffer sellOffer = buyOffer.getCounterOffer();

    CurrencyBuyOffer.remove(buyOffer);
    CurrencySellOffer.remove(sellOffer);

    Account account = Account.getAccount(buyOffer.getAccountId());
    account.addToUnconfirmedBalanceNQT(
        Math.multiplyExact(buyOffer.getSupply(), buyOffer.getRateNQT()));
    account.addToUnconfirmedCurrencyUnits(buyOffer.getCurrencyId(), sellOffer.getSupply());
  }
 static {
   Nxt.getBlockchainProcessor()
       .addListener(
           block -> {
             if (block.getHeight() <= Constants.MONETARY_SYSTEM_BLOCK) {
               return;
             }
             List<CurrencyBuyOffer> expired = new ArrayList<>();
             try (DbIterator<CurrencyBuyOffer> offers =
                 CurrencyBuyOffer.getOffers(
                     new DbClause.IntClause("expiration_height", block.getHeight()), 0, -1)) {
               for (CurrencyBuyOffer offer : offers) {
                 expired.add(offer);
               }
             }
             expired.forEach(CurrencyExchangeOffer::removeOffer);
           },
           BlockchainProcessor.Event.AFTER_BLOCK_APPLY);
 }