Repository: hashgraph/hedera-services
Issue: ApproveForAll in Approve/Adjust TransitionLogic doesn't check null value

### Description

Since the protobufs used for `NftAllowance` has `BoolValue` and not `boolean` , we need to check if the value is set or not before checking the value using `allowance.hasApproveForAll`
.
If this is not checked, evn if the value is not set it is considered as false and in `CryptoAdjustAllowanceTransitionLogic` we will remove the entity from the `approveForAllSet`


### Version

v0.25.0-alpha.1

### Operating system

_No response_

Changed files:
hedera-node/src/main/java/com/hedera/services/store/AccountStore.java
hedera-node/src/test/java/com/hedera/services/store/models/AccountTest.java
hedera-node/src/main/java/com/hedera/services/txns/crypto/CryptoApproveAllowanceTransitionLogic.java
hedera-node/src/main/java/com/hedera/services/store/models/Account.java
hedera-node/src/test/java/com/hedera/services/txns/crypto/CryptoAdjustAllowanceTransitionLogicTest.java
hedera-node/src/main/java/com/hedera/services/txns/crypto/CryptoAdjustAllowanceTransitionLogic.java
hedera-node/src/test/java/com/hedera/services/txns/crypto/CryptoApproveAllowanceTransitionLogicTest.java

diff --git a/hedera-node/src/main/java/com/hedera/services/store/AccountStore.java b/hedera-node/src/main/java/com/hedera/services/store/AccountStore.java
index 3dca51dd86..d2dcadf300 100644
--- a/hedera-node/src/main/java/com/hedera/services/store/AccountStore.java
+++ b/hedera-node/src/main/java/com/hedera/services/store/AccountStore.java
@@ -198,7 +198,7 @@ public class AccountStore {
 		mutableAccount.setSmartContract(model.isSmartContract());
 		mutableAccount.setCryptoAllowances(model.getMutableCryptoAllowances());
 		mutableAccount.setFungibleTokenAllowances(model.getMutableFungibleTokenAllowances());
-		mutableAccount.setApproveForAllNfts(model.getMutableApprovedForAllNftsAllowances());
+		mutableAccount.setApproveForAllNfts(model.getMutableApprovedForAllNfts());
 		final var tokenAssociationMetadata = new TokenAssociationMetadata(
 				model.getNumAssociations(), model.getNumZeroBalances(), model.getLastAssociatedToken());
 		mutableAccount.setTokenAssociationMetadata(tokenAssociationMetadata);
diff --git a/hedera-node/src/main/java/com/hedera/services/store/models/Account.java b/hedera-node/src/main/java/com/hedera/services/store/models/Account.java
index 259872ec5c..7e1ef3ec7a 100644
--- a/hedera-node/src/main/java/com/hedera/services/store/models/Account.java
+++ b/hedera-node/src/main/java/com/hedera/services/store/models/Account.java
@@ -489,7 +489,7 @@ public class Account {
 		return approveForAllNfts == null ? Collections.emptySet() : approveForAllNfts;
 	}
 
-	public SortedSet<FcTokenAllowanceId> getMutableApprovedForAllNftsAllowances() {
+	public SortedSet<FcTokenAllowanceId> getMutableApprovedForAllNfts() {
 		if (approveForAllNfts == null) {
 			approveForAllNfts = new TreeSet<>();
 		}
diff --git a/hedera-node/src/main/java/com/hedera/services/txns/crypto/CryptoAdjustAllowanceTransitionLogic.java b/hedera-node/src/main/java/com/hedera/services/txns/crypto/CryptoAdjustAllowanceTransitionLogic.java
index ad79bb6464..333955944c 100644
--- a/hedera-node/src/main/java/com/hedera/services/txns/crypto/CryptoAdjustAllowanceTransitionLogic.java
+++ b/hedera-node/src/main/java/com/hedera/services/txns/crypto/CryptoAdjustAllowanceTransitionLogic.java
@@ -197,7 +197,7 @@ public class CryptoAdjustAllowanceTransitionLogic implements TransitionLogic {
 	 * @param payerAccount
 	 * 		account of the payer for this adjustAllowance txn
 	 */
-	private void adjustNftAllowances(final List<NftAllowance> nftAllowances, final Account payerAccount) {
+	void adjustNftAllowances(final List<NftAllowance> nftAllowances, final Account payerAccount) {
 		if (nftAllowances.isEmpty()) {
 			return;
 		}
@@ -206,27 +206,25 @@ public class CryptoAdjustAllowanceTransitionLogic implements TransitionLogic {
 			final var owner = allowance.getOwner();
 
 			final var accountToAdjust = fetchOwnerAccount(owner, payerAccount, accountStore, entitiesChanged);
-			final var mutableApprovedForAllNftsAllowances = accountToAdjust.getMutableApprovedForAllNftsAllowances();
-
-			final var spenderAccount = allowance.getSpender();
-			final var approvedForAll = allowance.getApprovedForAll();
-			final var serialNums = allowance.getSerialNumbersList();
-			final var tokenID = allowance.getTokenId();
-			final var tokenId = Id.fromGrpcToken(tokenID);
-			final var spender = Id.fromGrpcAccount(spenderAccount);
+			final var mutableApprovedForAllNfts = accountToAdjust.getMutableApprovedForAllNfts();
+
+			final var tokenId = Id.fromGrpcToken(allowance.getTokenId());
+			final var spender = Id.fromGrpcAccount(allowance.getSpender());
+
 			accountStore.loadAccountOrFailWith(spender, INVALID_ALLOWANCE_SPENDER_ID);
-			final var key = FcTokenAllowanceId.from(tokenId.asEntityNum(),
-					spender.asEntityNum());
+			final var key = FcTokenAllowanceId.from(tokenId.asEntityNum(), spender.asEntityNum());
 
-			if (approvedForAll.getValue()) {
-				mutableApprovedForAllNftsAllowances.add(key);
-			} else {
-				mutableApprovedForAllNftsAllowances.remove(key);
+			if (allowance.hasApprovedForAll()) {
+				if (allowance.getApprovedForAll().getValue()) {
+					mutableApprovedForAllNfts.add(key);
+				} else {
+					mutableApprovedForAllNfts.remove(key);
+				}
 			}
 
 			validateAllowanceLimitsOn(accountToAdjust, dynamicProperties.maxAllowanceLimitPerAccount());
 
-			final var nfts = updateSpender(tokenStore, accountToAdjust.getId(), spender, tokenId, serialNums);
+			final var nfts = updateSpender(tokenStore, accountToAdjust.getId(), spender, tokenId, allowance.getSerialNumbersList());
 			for (var nft : nfts) {
 				nftsTouched.put(nft.getNftId(), nft);
 			}
diff --git a/hedera-node/src/main/java/com/hedera/services/txns/crypto/CryptoApproveAllowanceTransitionLogic.java b/hedera-node/src/main/java/com/hedera/services/txns/crypto/CryptoApproveAllowanceTransitionLogic.java
index e21d3f8e90..2d4387349d 100644
--- a/hedera-node/src/main/java/com/hedera/services/txns/crypto/CryptoApproveAllowanceTransitionLogic.java
+++ b/hedera-node/src/main/java/com/hedera/services/txns/crypto/CryptoApproveAllowanceTransitionLogic.java
@@ -184,30 +184,28 @@ public class CryptoApproveAllowanceTransitionLogic implements TransitionLogic {
 	 * @param nftAllowances
 	 * @param payerAccount
 	 */
-	private void applyNftAllowances(final List<NftAllowance> nftAllowances, final Account payerAccount) {
+	void applyNftAllowances(final List<NftAllowance> nftAllowances, final Account payerAccount) {
 		if (nftAllowances.isEmpty()) {
 			return;
 		}
 		for (var allowance : nftAllowances) {
 			final var owner = allowance.getOwner();
 			final var accountToApprove = fetchOwnerAccount(owner, payerAccount, accountStore, entitiesChanged);
-			final var approveForAllNftsSet = accountToApprove.getMutableApprovedForAllNftsAllowances();
+			final var approveForAllNftsSet = accountToApprove.getMutableApprovedForAllNfts();
 
 			final var spenderId = Id.fromGrpcAccount(allowance.getSpender());
 			accountStore.loadAccountOrFailWith(spenderId, INVALID_ALLOWANCE_SPENDER_ID);
 
-			final var approvedForAll = allowance.getApprovedForAll();
-			final var serialNums = allowance.getSerialNumbersList();
 			final var tokenId = Id.fromGrpcToken(allowance.getTokenId());
 
-			if (approvedForAll.getValue()) {
+			if (allowance.hasApprovedForAll() && allowance.getApprovedForAll().getValue()) {
 				final var key = FcTokenAllowanceId.from(tokenId.asEntityNum(), spenderId.asEntityNum());
 				approveForAllNftsSet.add(key);
 			}
 
 			validateAllowanceLimitsOn(accountToApprove, dynamicProperties.maxAllowanceLimitPerAccount());
 
-			final var nfts = updateSpender(tokenStore, accountToApprove.getId(), spenderId, tokenId, serialNums);
+			final var nfts = updateSpender(tokenStore, accountToApprove.getId(), spenderId, tokenId, allowance.getSerialNumbersList());
 			for (var nft : nfts) {
 				nftsTouched.put(nft.getNftId(), nft);
 			}
diff --git a/hedera-node/src/test/java/com/hedera/services/store/models/AccountTest.java b/hedera-node/src/test/java/com/hedera/services/store/models/AccountTest.java
index 7e8df0b0db..4b2014cba3 100644
--- a/hedera-node/src/test/java/com/hedera/services/store/models/AccountTest.java
+++ b/hedera-node/src/test/java/com/hedera/services/store/models/AccountTest.java
@@ -161,7 +161,7 @@ class AccountTest {
 
 		assertTrue(subject.getMutableCryptoAllowances().isEmpty());
 		assertTrue(subject.getMutableFungibleTokenAllowances().isEmpty());
-		assertTrue(subject.getMutableApprovedForAllNftsAllowances().isEmpty());
+		assertTrue(subject.getMutableApprovedForAllNfts().isEmpty());
 	}
 
 	@Test
diff --git a/hedera-node/src/test/java/com/hedera/services/txns/crypto/CryptoAdjustAllowanceTransitionLogicTest.java b/hedera-node/src/test/java/com/hedera/services/txns/crypto/CryptoAdjustAllowanceTransitionLogicTest.java
index b74ca1a2ef..9af4e2ab78 100644
--- a/hedera-node/src/test/java/com/hedera/services/txns/crypto/CryptoAdjustAllowanceTransitionLogicTest.java
+++ b/hedera-node/src/test/java/com/hedera/services/txns/crypto/CryptoAdjustAllowanceTransitionLogicTest.java
@@ -64,6 +64,7 @@ import java.util.TreeSet;
 
 import static com.hedera.test.utils.IdUtils.asAccount;
 import static com.hedera.test.utils.IdUtils.asToken;
+import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_ALLOWANCE_SPENDER_ID;
 import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.MAX_ALLOWANCES_EXCEEDED;
 import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.OK;
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -295,6 +296,53 @@ class CryptoAdjustAllowanceTransitionLogicTest {
 		verify(txnCtx).setStatus(ResponseCodeEnum.SUCCESS);
 	}
 
+	@Test
+	void checkIfApproveForAllIsSetBeforeDeleting(){
+		var ownerAcccount = new Account(ownerId);
+		setUpOwnerWithSomeKeys(ownerAcccount);
+		nftAllowances.clear();
+		var spenderAccount = new Account(Id.fromGrpcAccount(spender1));
+
+		given(accountStore.loadAccountOrFailWith(Id.fromGrpcAccount(spender1), INVALID_ALLOWANCE_SPENDER_ID)).willReturn(spenderAccount);
+		ownerAcccount.setCryptoAllowances(new TreeMap<>());
+		ownerAcccount.setFungibleTokenAllowances(new TreeMap<>());
+		ownerAcccount.setApproveForAllNfts(new TreeSet<>());
+		given(dynamicProperties.maxAllowanceLimitPerAccount()).willReturn(100);
+		given(tokenStore.loadUniqueToken(tokenId2, serial1)).willReturn(nft1);
+		given(tokenStore.loadUniqueToken(tokenId2, serial2)).willReturn(nft2);
+		given(tokenStore.loadUniqueToken(tokenId1, serial1)).willReturn(nft1);
+		given(tokenStore.loadUniqueToken(tokenId1, serial2)).willReturn(nft2);
+		given(tokenStore.loadUniqueToken(tokenId1, serial3)).willReturn(nft3);
+		given(tokenStore.loadUniqueToken(tokenId2, serial1)).willReturn(nft4);
+		given(tokenStore.loadUniqueToken(tokenId2, serial2)).willReturn(nft5);
+
+		givenValidTxnCtx();
+
+		// ApproveForALl is not set. It should not be considered as false
+		final NftAllowance nftAllowance = NftAllowance.newBuilder()
+				.setSpender(spender1)
+				.setOwner(owner)
+				.setTokenId(token2)
+				.addAllSerialNumbers(List.of(serial1)).build();
+		nftAllowances.add(nftAllowance);
+
+		subject.adjustNftAllowances(nftAllowances, ownerAcccount);
+
+		assertEquals(1, ownerAcccount.getApprovedForAllNftsAllowances().size());
+
+		final NftAllowance nftAllowance1 = NftAllowance.newBuilder()
+				.setSpender(spender1)
+				.setOwner(owner)
+				.setTokenId(token2)
+				.setApprovedForAll(BoolValue.of(false))
+				.addAllSerialNumbers(List.of(serial1)).build();
+		nftAllowances.add(nftAllowance1);
+
+		subject.adjustNftAllowances(nftAllowances, ownerAcccount);
+
+		assertEquals(0, ownerAcccount.getApprovedForAllNftsAllowances().size());
+	}
+
 	private void setUpOwnerWithSomeKeys(final Account ownerAcccount) {
 		Map<EntityNum, Long> cryptoAllowances = new TreeMap<>();
 		Map<FcTokenAllowanceId, Long> tokenAllowances = new TreeMap<>();
diff --git a/hedera-node/src/test/java/com/hedera/services/txns/crypto/CryptoApproveAllowanceTransitionLogicTest.java b/hedera-node/src/test/java/com/hedera/services/txns/crypto/CryptoApproveAllowanceTransitionLogicTest.java
index 0ea40c3099..dc288e2425 100644
--- a/hedera-node/src/test/java/com/hedera/services/txns/crypto/CryptoApproveAllowanceTransitionLogicTest.java
+++ b/hedera-node/src/test/java/com/hedera/services/txns/crypto/CryptoApproveAllowanceTransitionLogicTest.java
@@ -64,6 +64,7 @@ import java.util.TreeSet;
 import static com.hedera.test.utils.IdUtils.asAccount;
 import static com.hedera.test.utils.IdUtils.asToken;
 import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_ALLOWANCE_OWNER_ID;
+import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_ALLOWANCE_SPENDER_ID;
 import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.MAX_ALLOWANCES_EXCEEDED;
 import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.OK;
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -297,6 +298,40 @@ class CryptoApproveAllowanceTransitionLogicTest {
 		verify(txnCtx).setStatus(ResponseCodeEnum.SUCCESS);
 	}
 
+	@Test
+	void checkIfApproveForAllIsSet(){
+		final NftAllowance nftAllowance = NftAllowance.newBuilder()
+				.setSpender(spender1)
+				.setOwner(ownerId)
+				.setTokenId(token2)
+				.addAllSerialNumbers(List.of(serial1)).build();
+		final NftAllowance nftAllowance1 = NftAllowance.newBuilder()
+				.setSpender(spender1)
+				.setOwner(ownerId)
+				.setTokenId(token2)
+				.setApprovedForAll(BoolValue.of(false))
+				.addAllSerialNumbers(List.of(serial1)).build();
+		nftAllowances.add(nftAllowance);
+		nftAllowances.add(nftAllowance1);
+
+		var ownerAcccount = new Account(Id.fromGrpcAccount(ownerId));
+
+		givenValidTxnCtx();
+
+		given(accountStore.loadAccountOrFailWith(spenderId1, INVALID_ALLOWANCE_SPENDER_ID))
+				.willReturn(payerAcccount);
+		ownerAcccount.setCryptoAllowances(new TreeMap<>());
+		ownerAcccount.setFungibleTokenAllowances(new TreeMap<>());
+		ownerAcccount.setApproveForAllNfts(new TreeSet<>());
+		given(dynamicProperties.maxAllowanceLimitPerAccount()).willReturn(100);
+		given(tokenStore.loadUniqueToken(tokenId2, serial1)).willReturn(nft1);
+		given(tokenStore.loadUniqueToken(tokenId2, serial2)).willReturn(nft2);
+
+		subject.applyNftAllowances(nftAllowances, ownerAcccount);
+
+		assertEquals(1, ownerAcccount.getApprovedForAllNftsAllowances().size());
+	}
+
 	private void setUpOwnerWithExistingKeys(final Account ownerAcccount) {
 		Map<EntityNum, Long> cryptoAllowances = new TreeMap<>();
 		Map<FcTokenAllowanceId, Long> tokenAllowances = new TreeMap<>();