static void exchangeNXTForCurrency(
      Transaction transaction,
      Account account,
      final long currencyId,
      final long rateNQT,
      long units) {
    long extraUnits = 0;
    long remainingAmountNQT = Math.multiplyExact(units, rateNQT);

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

    for (CurrencySellOffer offer : currencySellOffers) {
      if (remainingAmountNQT == 0) {
        break;
      }
      long curUnits =
          Math.min(
              Math.min(remainingAmountNQT / offer.getRateNQT(), offer.getSupply()),
              offer.getLimit());
      if (curUnits == 0) {
        continue;
      }
      long curAmountNQT = Math.multiplyExact(curUnits, offer.getRateNQT());

      extraUnits = Math.addExact(extraUnits, curUnits);
      remainingAmountNQT = Math.subtractExact(remainingAmountNQT, curAmountNQT);

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

      Account counterAccount = Account.getAccount(offer.getAccountId());
      counterAccount.addToBalanceNQT(curAmountNQT);
      counterAccount.addToUnconfirmedBalanceNQT(
          Math.addExact(
              Math.multiplyExact(
                  curUnits - excess, offer.getRateNQT() - offer.getCounterOffer().getRateNQT()),
              Math.multiplyExact(excess, offer.getRateNQT())));
      counterAccount.addToCurrencyUnits(currencyId, -curUnits);
      Exchange.addExchange(
          transaction, currencyId, offer, offer.getAccountId(), account.getId(), curUnits);
    }

    account.addToCurrencyAndUnconfirmedCurrencyUnits(currencyId, extraUnits);
    account.addToBalanceNQT(-(Math.multiplyExact(units, rateNQT) - remainingAmountNQT));
    account.addToUnconfirmedBalanceNQT(remainingAmountNQT);
  }
 @Override
 void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) {
   Attachment.MonetarySystemExchangeBuy attachment =
       (Attachment.MonetarySystemExchangeBuy) transaction.getAttachment();
   senderAccount.addToUnconfirmedBalanceNQT(
       Math.multiplyExact(attachment.getUnits(), attachment.getRateNQT()));
 }
  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());
  }
 @Override
 boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) {
   Attachment.MonetarySystemExchangeBuy attachment =
       (Attachment.MonetarySystemExchangeBuy) transaction.getAttachment();
   if (senderAccount.getUnconfirmedBalanceNQT()
       >= Math.multiplyExact(attachment.getUnits(), attachment.getRateNQT())) {
     senderAccount.addToUnconfirmedBalanceNQT(
         -Math.multiplyExact(attachment.getUnits(), attachment.getRateNQT()));
     return true;
   }
   return false;
 }
 @Override
 void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) {
   Attachment.MonetarySystemPublishExchangeOffer attachment =
       (Attachment.MonetarySystemPublishExchangeOffer) transaction.getAttachment();
   senderAccount.addToUnconfirmedBalanceNQT(
       Math.multiplyExact(attachment.getInitialBuySupply(), attachment.getBuyRateNQT()));
   Currency currency = Currency.getCurrency(attachment.getCurrencyId());
   if (currency != null) {
     senderAccount.addToUnconfirmedCurrencyUnits(
         attachment.getCurrencyId(), attachment.getInitialSellSupply());
   }
 }
 @Override
 boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) {
   Attachment.MonetarySystemReserveIncrease attachment =
       (Attachment.MonetarySystemReserveIncrease) transaction.getAttachment();
   Currency currency = Currency.getCurrency(attachment.getCurrencyId());
   if (senderAccount.getUnconfirmedBalanceNQT()
       >= Math.multiplyExact(
           currency.getReserveSupply(), attachment.getAmountPerUnitNQT())) {
     senderAccount.addToUnconfirmedBalanceNQT(
         -Math.multiplyExact(currency.getReserveSupply(), attachment.getAmountPerUnitNQT()));
     return true;
   }
   return false;
 }
 @Override
 boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) {
   Attachment.MonetarySystemPublishExchangeOffer attachment =
       (Attachment.MonetarySystemPublishExchangeOffer) transaction.getAttachment();
   if (senderAccount.getUnconfirmedBalanceNQT()
           >= Math.multiplyExact(
               attachment.getInitialBuySupply(), attachment.getBuyRateNQT())
       && senderAccount.getUnconfirmedCurrencyUnits(attachment.getCurrencyId())
           >= attachment.getInitialSellSupply()) {
     senderAccount.addToUnconfirmedBalanceNQT(
         -Math.multiplyExact(attachment.getInitialBuySupply(), attachment.getBuyRateNQT()));
     senderAccount.addToUnconfirmedCurrencyUnits(
         attachment.getCurrencyId(), -attachment.getInitialSellSupply());
     return true;
   }
   return false;
 }
 @Override
 void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) {
   Attachment.MonetarySystemReserveIncrease attachment =
       (Attachment.MonetarySystemReserveIncrease) transaction.getAttachment();
   long reserveSupply;
   Currency currency = Currency.getCurrency(attachment.getCurrencyId());
   if (currency != null) {
     reserveSupply = currency.getReserveSupply();
   } else { // currency must have been deleted, get reserve supply from the original issuance
            // transaction
     Transaction currencyIssuance =
         Nxt.getBlockchain().getTransaction(attachment.getCurrencyId());
     Attachment.MonetarySystemCurrencyIssuance currencyIssuanceAttachment =
         (Attachment.MonetarySystemCurrencyIssuance) currencyIssuance.getAttachment();
     reserveSupply = currencyIssuanceAttachment.getReserveSupply();
   }
   senderAccount.addToUnconfirmedBalanceNQT(
       Math.multiplyExact(reserveSupply, attachment.getAmountPerUnitNQT()));
 }