|
18 | 18 |
|
19 | 19 | import static com.hedera.hapi.node.base.ResponseCodeEnum.AMOUNT_EXCEEDS_ALLOWANCE;
|
20 | 20 | import static com.hedera.hapi.node.base.ResponseCodeEnum.INSUFFICIENT_ACCOUNT_BALANCE;
|
| 21 | +import static com.hedera.hapi.node.base.ResponseCodeEnum.INSUFFICIENT_SENDER_ACCOUNT_BALANCE_FOR_CUSTOM_FEE; |
21 | 22 | import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_ACCOUNT_ID;
|
22 | 23 | import static com.hedera.hapi.node.base.ResponseCodeEnum.SPENDER_DOES_NOT_HAVE_ALLOWANCE;
|
23 | 24 | import static com.hedera.node.app.service.token.impl.util.TokenHandlerHelper.getIfUsable;
|
24 | 25 | import static com.hedera.node.app.spi.workflows.HandleException.validateTrue;
|
| 26 | +import static java.util.Collections.emptyList; |
25 | 27 | import static java.util.Objects.requireNonNull;
|
26 | 28 |
|
27 | 29 | import com.hedera.hapi.node.base.AccountID;
|
28 | 30 | import com.hedera.hapi.node.base.TransferList;
|
29 | 31 | import com.hedera.hapi.node.token.CryptoTransferTransactionBody;
|
| 32 | +import com.hedera.hapi.node.transaction.AssessedCustomFee; |
30 | 33 | import com.hedera.node.app.service.token.impl.WritableAccountStore;
|
31 | 34 | import com.hedera.node.app.service.token.impl.handlers.BaseTokenHandler;
|
| 35 | +import com.hedera.node.app.spi.workflows.HandleException; |
32 | 36 | import edu.umd.cs.findbugs.annotations.NonNull;
|
33 | 37 | import java.util.ArrayList;
|
34 | 38 | import java.util.Collections;
|
35 | 39 | import java.util.LinkedHashMap;
|
| 40 | +import java.util.List; |
36 | 41 | import java.util.Map;
|
37 | 42 |
|
38 | 43 | public class AdjustHbarChangesStep extends BaseTokenHandler implements TransferStep {
|
@@ -133,9 +138,35 @@ private void modifyAggregatedTransfers(
|
133 | 138 | accountId, accountStore, transferContext.getHandleContext().expiryValidator(), INVALID_ACCOUNT_ID);
|
134 | 139 | final var currentBalance = account.tinybarBalance();
|
135 | 140 | final var newBalance = currentBalance + amount;
|
| 141 | + if (newBalance < 0) { |
| 142 | + final var assessedCustomFees = transferContext.getAssessedCustomFees(); |
| 143 | + // Whenever mono-service assessed a fixed fee to an account, it would |
| 144 | + // update the "metadata" of that pending balance change to use |
| 145 | + // INSUFFICIENT_SENDER_ACCOUNT_BALANCE_FOR_CUSTOM_FEE instead of |
| 146 | + // INSUFFICIENT_ACCOUNT_BALANCE in the case of an insufficient balance. |
| 147 | + // We don't have an equivalent place to store such "metadata" in the |
| 148 | + // mod-service implementation; so instead if INSUFFICIENT_ACCOUNT_BALANCE |
| 149 | + // happens, we check if there were any custom fee payments that could |
| 150 | + // have contributed to the insufficient balance, and translate the |
| 151 | + // error to INSUFFICIENT_SENDER_ACCOUNT_BALANCE_FOR_CUSTOM_FEE if so. |
| 152 | + if (effectivePaymentWasMade(accountId, assessedCustomFees)) { |
| 153 | + throw new HandleException(INSUFFICIENT_SENDER_ACCOUNT_BALANCE_FOR_CUSTOM_FEE); |
| 154 | + } |
| 155 | + } |
136 | 156 | validateTrue(newBalance >= 0, INSUFFICIENT_ACCOUNT_BALANCE);
|
137 | 157 | final var copy = account.copyBuilder();
|
138 | 158 | accountStore.put(copy.tinybarBalance(newBalance).build());
|
139 | 159 | }
|
140 | 160 | }
|
| 161 | + |
| 162 | + private boolean effectivePaymentWasMade( |
| 163 | + @NonNull final AccountID payer, @NonNull final List<AssessedCustomFee> assessedCustomFees) { |
| 164 | + for (final var fee : assessedCustomFees) { |
| 165 | + if (fee.tokenId() == null |
| 166 | + && fee.effectivePayerAccountIdOrElse(emptyList()).contains(payer)) { |
| 167 | + return true; |
| 168 | + } |
| 169 | + } |
| 170 | + return false; |
| 171 | + } |
141 | 172 | }
|
0 commit comments