class DexCloseOrderTransaction extends DEX {

    @Override
    public void validateAttachment(Transaction tx) throws AplException.ValidationException {
        DexCloseOrderAttachment attachment = (DexCloseOrderAttachment) tx.getAttachment();
        DexService dexService = lookupDexService();
        ExchangeContract dexContract = dexService.getDexContractById(attachment.getContractId());
        if (dexContract == null) {
            throw new AplException.NotCurrentlyValidException("Contract does not exists, id - " + attachment.getContractId());
        }
        if (dexContract.getRecipient() != tx.getSenderId() && dexContract.getSender() != tx.getSenderId()) {
            throw new AplException.NotCurrentlyValidException("Account " + tx.getSenderId() + " is not a participant of the contract, id - " + dexContract.getId());
        }
        if (dexContract.getContractStatus() != ExchangeContractStatus.STEP_3) {
            throw new AplException.NotCurrentlyValidException("Wrong contract status, expected STEP_3, got " + dexContract.getContractStatus());
        }
        boolean isSender = dexContract.getSender() == tx.getSenderId();
        long orderId = isSender ? dexContract.getOrderId() : dexContract.getCounterOrderId();
        DexOrder order = dexService.getOrder(orderId);
        if (order == null) {
            throw new AplException.NotCurrentlyValidException("Order with id " + attachment.getContractId() + " does not exists");
        }
        if (order.getAccountId() != tx.getSenderId()) {
            throw new AplException.NotCurrentlyValidException(JSON.toString(incorrect("orderId", "You can close only your orders.")));
        }
        if (order.getType() == OrderType.BUY) {
            throw new AplException.NotCurrentlyValidException("APL buy orders are closing automatically");
        }
        if (!order.getStatus().isWaitingForApproval()) {
            throw new AplException.NotCurrentlyValidException(JSON.toString(incorrect("orderStatus", "You can close order in the status WaitingForApproval only, but got: " + order.getStatus().name())));
        }
        long transferId = Long.parseUnsignedLong(isSender ? dexContract.getTransferTxId() : dexContract.getCounterTransferTxId());
        Transaction transferTx = lookupBlockchain().getTransaction(transferId);
        if (transferTx == null) {
            throw new AplException.NotCurrentlyValidException("Transfer tx was not found: " + transferId);
        }
        if (transferTx.getType() != DEX_TRANSFER_MONEY_TRANSACTION) {
            throw new AplException.NotCurrentlyValidException("Wrong type of transfer tx: " + transferTx.getType());
        }
        if (transferTx.getSenderId() != tx.getSenderId()) {
            throw new AplException.NotCurrentlyValidException("Wrong transfer tx sender: " + tx.getSenderId() + ", expected -" + transferTx.getSenderId());
        }
        long transferContractId = ((DexControlOfFrozenMoneyAttachment) transferTx.getAttachment()).getContractId();
        if (transferContractId != attachment.getContractId()) {
            throw new AplException.NotCurrentlyValidException("Trasfer tx " + transferId + " refers to another contract " + transferContractId + ", expected " + attachment.getContractId());
        }
        PhasingPollService phasingPollService = lookupPhasingPollService();
        if (phasingPollService.getPoll(transferId) == null && phasingPollService.getResult(transferId) == null) {
            throw new AplException.NotCurrentlyValidException("Transfer tx " + transferId + " was not phased");
        }

    }

}