From 882fd3cffd1fb9b35fd85fc4b1eaed2d7607894f Mon Sep 17 00:00:00 2001 From: Ndacyayisenga-droid Date: Fri, 31 Oct 2025 06:50:05 +0300 Subject: [PATCH 1/2] feat: create ContractRepository interface, implementation Signed-off-by: Ndacyayisenga-droid --- .../hiero/base/data/Contract.java | 41 +++++ .../AbstractMirrorNodeClient.java | 40 ++++- .../ContractRepositoryImpl.java | 58 +++++++ .../MirrorNodeJsonConverter.java | 12 +- .../implementation/MirrorNodeRestClient.java | 30 ++++ .../base/mirrornode/ContractRepository.java | 77 +++++++++ .../base/mirrornode/MirrorNodeClient.java | 68 +++++++- .../hiero/microprofile/ClientProvider.java | 9 ++ .../MirrorNodeJsonConverterImpl.java | 145 +++++++++++++++++ .../HieroAutoConfiguration.java | 22 ++- .../MirrorNodeJsonConverterImpl.java | 148 +++++++++++++++++- .../spring/test/ContractRepositoryTest.java | 88 +++++++++++ 12 files changed, 724 insertions(+), 14 deletions(-) create mode 100644 hiero-enterprise-base/src/main/java/com/openelements/hiero/base/data/Contract.java create mode 100644 hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/ContractRepositoryImpl.java create mode 100644 hiero-enterprise-base/src/main/java/com/openelements/hiero/base/mirrornode/ContractRepository.java create mode 100644 hiero-enterprise-spring/src/test/java/com/openelements/hiero/spring/test/ContractRepositoryTest.java diff --git a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/data/Contract.java b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/data/Contract.java new file mode 100644 index 00000000..e0f06cfc --- /dev/null +++ b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/data/Contract.java @@ -0,0 +1,41 @@ +package com.openelements.hiero.base.data; + +import com.hedera.hashgraph.sdk.AccountId; +import com.hedera.hashgraph.sdk.ContractId; +import com.hedera.hashgraph.sdk.PublicKey; +import java.time.Instant; +import java.util.Objects; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +/** + * Represents a smart contract on the Hiero network. + */ +public record Contract( + @NonNull ContractId contractId, + @Nullable PublicKey adminKey, + @Nullable AccountId autoRenewAccount, + int autoRenewPeriod, + @NonNull Instant createdTimestamp, + boolean deleted, + @Nullable Instant expirationTimestamp, + @Nullable String fileId, + @Nullable String evmAddress, + @Nullable String memo, + @Nullable Integer maxAutomaticTokenAssociations, + @Nullable Long nonce, + @Nullable String obtainerId, + boolean permanentRemoval, + @Nullable String proxyAccountId, + @NonNull Instant fromTimestamp, + @NonNull Instant toTimestamp, + @Nullable String bytecode, + @Nullable String runtimeBytecode +) { + public Contract { + Objects.requireNonNull(contractId, "contractId must not be null"); + Objects.requireNonNull(createdTimestamp, "createdTimestamp must not be null"); + Objects.requireNonNull(fromTimestamp, "fromTimestamp must not be null"); + Objects.requireNonNull(toTimestamp, "toTimestamp must not be null"); + } +} diff --git a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/AbstractMirrorNodeClient.java b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/AbstractMirrorNodeClient.java index cc796daf..8e64cdb7 100644 --- a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/AbstractMirrorNodeClient.java +++ b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/AbstractMirrorNodeClient.java @@ -1,16 +1,19 @@ package com.openelements.hiero.base.implementation; import com.hedera.hashgraph.sdk.AccountId; +import com.hedera.hashgraph.sdk.ContractId; import com.hedera.hashgraph.sdk.TokenId; import com.hedera.hashgraph.sdk.TopicId; import com.openelements.hiero.base.HieroException; +import com.openelements.hiero.base.data.Contract; +import com.openelements.hiero.base.data.Nft; +import com.openelements.hiero.base.data.NftMetadata; import com.openelements.hiero.base.data.AccountInfo; import com.openelements.hiero.base.data.ExchangeRates; import com.openelements.hiero.base.data.NetworkFee; import com.openelements.hiero.base.data.NetworkStake; import com.openelements.hiero.base.data.NetworkSupplies; -import com.openelements.hiero.base.data.Nft; -import com.openelements.hiero.base.data.NftMetadata; +import com.openelements.hiero.base.data.Page; import com.openelements.hiero.base.data.TokenInfo; import com.openelements.hiero.base.data.TransactionInfo; import com.openelements.hiero.base.data.Topic; @@ -105,4 +108,37 @@ public final Optional queryTopicMessageBySequenceNumber(TopicId to throw new UnsupportedOperationException("Not yet implemented"); } + @Override + public @NonNull Page queryContracts() throws HieroException { + final JSON json = getRestClient().queryContracts(); + return getJsonConverter().toContractPage(json); + } + + @Override + public @NonNull Optional queryContractById(@NonNull final ContractId contractId) throws HieroException { + Objects.requireNonNull(contractId, "contractId must not be null"); + final JSON json = getRestClient().queryContractById(contractId); + return getJsonConverter().toContract(json); + } + + @Override + public @NonNull Page queryContractsByEvmAddress(@NonNull final String evmAddress) throws HieroException { + Objects.requireNonNull(evmAddress, "evmAddress must not be null"); + final JSON json = getRestClient().queryContractsByEvmAddress(evmAddress); + return getJsonConverter().toContractPage(json); + } + + @Override + public @NonNull Page queryContractsByFileId(@NonNull final String fileId) throws HieroException { + Objects.requireNonNull(fileId, "fileId must not be null"); + final JSON json = getRestClient().queryContractsByFileId(fileId); + return getJsonConverter().toContractPage(json); + } + + @Override + public @NonNull Page queryContractsByProxyAccountId(@NonNull final String proxyAccountId) throws HieroException { + Objects.requireNonNull(proxyAccountId, "proxyAccountId must not be null"); + final JSON json = getRestClient().queryContractsByProxyAccountId(proxyAccountId); + return getJsonConverter().toContractPage(json); + } } diff --git a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/ContractRepositoryImpl.java b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/ContractRepositoryImpl.java new file mode 100644 index 00000000..83432e98 --- /dev/null +++ b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/ContractRepositoryImpl.java @@ -0,0 +1,58 @@ +package com.openelements.hiero.base.implementation; + +import com.hedera.hashgraph.sdk.ContractId; +import com.openelements.hiero.base.HieroException; +import com.openelements.hiero.base.data.Contract; +import com.openelements.hiero.base.data.Page; +import com.openelements.hiero.base.mirrornode.ContractRepository; +import com.openelements.hiero.base.mirrornode.MirrorNodeClient; +import java.util.Objects; +import java.util.Optional; +import org.jspecify.annotations.NonNull; + +/** + * Implementation of ContractRepository that uses MirrorNodeClient to query contract data. + */ +public class ContractRepositoryImpl implements ContractRepository { + + private final MirrorNodeClient mirrorNodeClient; + + /** + * Creates a new ContractRepositoryImpl with the given MirrorNodeClient. + * + * @param mirrorNodeClient the mirror node client to use for queries + */ + public ContractRepositoryImpl(@NonNull final MirrorNodeClient mirrorNodeClient) { + this.mirrorNodeClient = Objects.requireNonNull(mirrorNodeClient, "mirrorNodeClient must not be null"); + } + + @NonNull + @Override + public Page findAll() throws HieroException { + return mirrorNodeClient.queryContracts(); + } + + @NonNull + @Override + public Optional findById(@NonNull final ContractId contractId) throws HieroException { + return mirrorNodeClient.queryContractById(contractId); + } + + @NonNull + @Override + public Page findByEvmAddress(@NonNull final String evmAddress) throws HieroException { + return mirrorNodeClient.queryContractsByEvmAddress(evmAddress); + } + + @NonNull + @Override + public Page findByFileId(@NonNull final String fileId) throws HieroException { + return mirrorNodeClient.queryContractsByFileId(fileId); + } + + @NonNull + @Override + public Page findByProxyAccountId(@NonNull final String proxyAccountId) throws HieroException { + return mirrorNodeClient.queryContractsByProxyAccountId(proxyAccountId); + } +} diff --git a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/MirrorNodeJsonConverter.java b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/MirrorNodeJsonConverter.java index 442d5671..50c91651 100644 --- a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/MirrorNodeJsonConverter.java +++ b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/MirrorNodeJsonConverter.java @@ -12,13 +12,14 @@ import com.openelements.hiero.base.data.Balance; import com.openelements.hiero.base.data.Topic; import com.openelements.hiero.base.data.TopicMessage; +import com.openelements.hiero.base.data.Contract; +import com.openelements.hiero.base.data.Page; import java.util.List; import java.util.Optional; import org.jspecify.annotations.NonNull; public interface MirrorNodeJsonConverter { - @NonNull Optional toNft(@NonNull JSON json); @@ -59,4 +60,13 @@ public interface MirrorNodeJsonConverter { @NonNull List toTopicMessages(JSON json); + + @NonNull + Optional toContract(@NonNull JSON json); + + @NonNull + Page toContractPage(@NonNull JSON json); + + @NonNull + List toContracts(@NonNull JSON json); } diff --git a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/MirrorNodeRestClient.java b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/MirrorNodeRestClient.java index da2a0351..3cfd46f8 100644 --- a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/MirrorNodeRestClient.java +++ b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/MirrorNodeRestClient.java @@ -1,6 +1,7 @@ package com.openelements.hiero.base.implementation; import com.hedera.hashgraph.sdk.AccountId; +import com.hedera.hashgraph.sdk.ContractId; import com.hedera.hashgraph.sdk.TokenId; import com.hedera.hashgraph.sdk.TopicId; import com.openelements.hiero.base.HieroException; @@ -68,4 +69,33 @@ default JSON queryTopicMessageBySequenceNumber(TopicId topicId, long sequenceNum @NonNull JSON doGetCall(@NonNull String path) throws HieroException; + + @NonNull + default JSON queryContracts() throws HieroException { + return doGetCall("/api/v1/contracts"); + } + + @NonNull + default JSON queryContractById(@NonNull final ContractId contractId) throws HieroException { + Objects.requireNonNull(contractId, "contractId must not be null"); + return doGetCall("/api/v1/contracts/" + contractId); + } + + @NonNull + default JSON queryContractsByEvmAddress(@NonNull final String evmAddress) throws HieroException { + Objects.requireNonNull(evmAddress, "evmAddress must not be null"); + return doGetCall("/api/v1/contracts?evm.address=" + evmAddress); + } + + @NonNull + default JSON queryContractsByFileId(@NonNull final String fileId) throws HieroException { + Objects.requireNonNull(fileId, "fileId must not be null"); + return doGetCall("/api/v1/contracts?file.id=" + fileId); + } + + @NonNull + default JSON queryContractsByProxyAccountId(@NonNull final String proxyAccountId) throws HieroException { + Objects.requireNonNull(proxyAccountId, "proxyAccountId must not be null"); + return doGetCall("/api/v1/contracts?proxy.account.id=" + proxyAccountId); + } } diff --git a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/mirrornode/ContractRepository.java b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/mirrornode/ContractRepository.java new file mode 100644 index 00000000..b2a77b35 --- /dev/null +++ b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/mirrornode/ContractRepository.java @@ -0,0 +1,77 @@ +package com.openelements.hiero.base.mirrornode; + +import com.hedera.hashgraph.sdk.ContractId; +import com.openelements.hiero.base.HieroException; +import com.openelements.hiero.base.data.Contract; +import com.openelements.hiero.base.data.Page; +import java.util.Objects; +import java.util.Optional; +import org.jspecify.annotations.NonNull; + +/** + * Interface for interacting with smart contracts on a Hiero network. This interface provides methods for searching for contracts. + */ +public interface ContractRepository { + + /** + * Return all contracts. + * + * @return first page of contracts + * @throws HieroException if the search fails + */ + @NonNull + Page findAll() throws HieroException; + + /** + * Return a contract by its contract ID. + * + * @param contractId id of the contract + * @return {@link Optional} containing the found contract or null + * @throws HieroException if the search fails + */ + @NonNull + Optional findById(@NonNull ContractId contractId) throws HieroException; + + /** + * Return a contract by its contract ID. + * + * @param contractId id of the contract + * @return {@link Optional} containing the found contract or null + * @throws HieroException if the search fails + */ + @NonNull + default Optional findById(@NonNull String contractId) throws HieroException { + Objects.requireNonNull(contractId, "contractId must not be null"); + return findById(ContractId.fromString(contractId)); + } + + /** + * Return contracts by EVM address. + * + * @param evmAddress the EVM address of the contract + * @return first page of contracts + * @throws HieroException if the search fails + */ + @NonNull + Page findByEvmAddress(@NonNull String evmAddress) throws HieroException; + + /** + * Return contracts by file ID. + * + * @param fileId the file ID associated with the contract + * @return first page of contracts + * @throws HieroException if the search fails + */ + @NonNull + Page findByFileId(@NonNull String fileId) throws HieroException; + + /** + * Return contracts by proxy account ID. + * + * @param proxyAccountId the proxy account ID + * @return first page of contracts + * @throws HieroException if the search fails + */ + @NonNull + Page findByProxyAccountId(@NonNull String proxyAccountId) throws HieroException; +} diff --git a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/mirrornode/MirrorNodeClient.java b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/mirrornode/MirrorNodeClient.java index c2eac423..a5329031 100644 --- a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/mirrornode/MirrorNodeClient.java +++ b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/mirrornode/MirrorNodeClient.java @@ -1,11 +1,13 @@ package com.openelements.hiero.base.mirrornode; import com.hedera.hashgraph.sdk.AccountId; +import com.hedera.hashgraph.sdk.ContractId; import com.hedera.hashgraph.sdk.TokenId; import com.hedera.hashgraph.sdk.TopicId; import com.openelements.hiero.base.HieroException; import com.openelements.hiero.base.data.AccountInfo; import com.openelements.hiero.base.data.Balance; +import com.openelements.hiero.base.data.Contract; import com.openelements.hiero.base.data.ExchangeRates; import com.openelements.hiero.base.data.NetworkFee; import com.openelements.hiero.base.data.NetworkStake; @@ -143,7 +145,7 @@ default Optional queryNftsByTokenIdAndSerial(@NonNull String tokenId, long */ @NonNull default Optional queryNftsByAccountAndTokenIdAndSerial(@NonNull AccountId accountId, @NonNull TokenId tokenId, - long serialNumber) throws HieroException { + long serialNumber) throws HieroException { Objects.requireNonNull(accountId, "newAccountId must not be null"); return queryNftsByTokenIdAndSerial(tokenId, serialNumber) .filter(nft -> Objects.equals(nft.owner(), accountId)); @@ -160,7 +162,7 @@ default Optional queryNftsByAccountAndTokenIdAndSerial(@NonNull AccountId a */ @NonNull default Optional queryNftsByAccountAndTokenIdAndSerial(@NonNull String accountId, @NonNull String tokenId, - long serialNumber) throws HieroException { + long serialNumber) throws HieroException { Objects.requireNonNull(accountId, "accountId must not be null"); Objects.requireNonNull(tokenId, "tokenId must not be null"); return queryNftsByAccountAndTokenIdAndSerial(AccountId.fromString(accountId), TokenId.fromString(tokenId), @@ -459,4 +461,66 @@ default Optional queryTopicMessageBySequenceNumber(String topicId, @NonNull Page findAllNftTypes(); + + /** + * Queries all contracts. + * + * @return the contracts + * @throws HieroException if an error occurs + */ + @NonNull + Page queryContracts() throws HieroException; + + /** + * Queries a contract by its contract ID. + * + * @param contractId the contract ID + * @return the contract information + * @throws HieroException if an error occurs + */ + @NonNull + Optional queryContractById(@NonNull ContractId contractId) throws HieroException; + + /** + * Queries a contract by its contract ID. + * + * @param contractId the contract ID + * @return the contract information + * @throws HieroException if an error occurs + */ + @NonNull + default Optional queryContractById(@NonNull String contractId) throws HieroException { + Objects.requireNonNull(contractId, "contractId must not be null"); + return queryContractById(ContractId.fromString(contractId)); + } + + /** + * Queries contracts by EVM address. + * + * @param evmAddress the EVM address + * @return the contracts + * @throws HieroException if an error occurs + */ + @NonNull + Page queryContractsByEvmAddress(@NonNull String evmAddress) throws HieroException; + + /** + * Queries contracts by file ID. + * + * @param fileId the file ID + * @return the contracts + * @throws HieroException if an error occurs + */ + @NonNull + Page queryContractsByFileId(@NonNull String fileId) throws HieroException; + + /** + * Queries contracts by proxy account ID. + * + * @param proxyAccountId the proxy account ID + * @return the contracts + * @throws HieroException if an error occurs + */ + @NonNull + Page queryContractsByProxyAccountId(@NonNull String proxyAccountId) throws HieroException; } diff --git a/hiero-enterprise-microprofile/src/main/java/com/openelements/hiero/microprofile/ClientProvider.java b/hiero-enterprise-microprofile/src/main/java/com/openelements/hiero/microprofile/ClientProvider.java index 8ed8ea38..e6c99a6d 100644 --- a/hiero-enterprise-microprofile/src/main/java/com/openelements/hiero/microprofile/ClientProvider.java +++ b/hiero-enterprise-microprofile/src/main/java/com/openelements/hiero/microprofile/ClientProvider.java @@ -18,12 +18,14 @@ import com.openelements.hiero.base.implementation.SmartContractClientImpl; import com.openelements.hiero.base.implementation.TokenRepositoryImpl; import com.openelements.hiero.base.implementation.TransactionRepositoryImpl; +import com.openelements.hiero.base.implementation.ContractRepositoryImpl; import com.openelements.hiero.base.mirrornode.AccountRepository; import com.openelements.hiero.base.mirrornode.MirrorNodeClient; import com.openelements.hiero.base.mirrornode.NetworkRepository; import com.openelements.hiero.base.mirrornode.NftRepository; import com.openelements.hiero.base.mirrornode.TokenRepository; import com.openelements.hiero.base.mirrornode.TransactionRepository; +import com.openelements.hiero.base.mirrornode.ContractRepository; import com.openelements.hiero.base.protocol.ProtocolLayerClient; import com.openelements.hiero.base.verification.ContractVerificationClient; import com.openelements.hiero.microprofile.implementation.ContractVerificationClientImpl; @@ -159,4 +161,11 @@ TransactionRepository createTransactionRepository(@NonNull final MirrorNodeClien TokenRepository createTokenRepository(@NonNull final MirrorNodeClient mirrorNodeClient) { return new TokenRepositoryImpl(mirrorNodeClient); } + + @NonNull + @Produces + @ApplicationScoped + ContractRepository createContractRepository(@NonNull final MirrorNodeClient mirrorNodeClient) { + return new ContractRepositoryImpl(mirrorNodeClient); + } } diff --git a/hiero-enterprise-microprofile/src/main/java/com/openelements/hiero/microprofile/implementation/MirrorNodeJsonConverterImpl.java b/hiero-enterprise-microprofile/src/main/java/com/openelements/hiero/microprofile/implementation/MirrorNodeJsonConverterImpl.java index 86547503..fef6fdaa 100644 --- a/hiero-enterprise-microprofile/src/main/java/com/openelements/hiero/microprofile/implementation/MirrorNodeJsonConverterImpl.java +++ b/hiero-enterprise-microprofile/src/main/java/com/openelements/hiero/microprofile/implementation/MirrorNodeJsonConverterImpl.java @@ -1,6 +1,7 @@ package com.openelements.hiero.microprofile.implementation; import com.hedera.hashgraph.sdk.AccountId; +import com.hedera.hashgraph.sdk.ContractId; import com.hedera.hashgraph.sdk.TokenId; import com.hedera.hashgraph.sdk.TokenSupplyType; import com.hedera.hashgraph.sdk.TokenType; @@ -8,12 +9,14 @@ import com.hedera.hashgraph.sdk.TransactionId; import com.hedera.hashgraph.sdk.PublicKey; import com.openelements.hiero.base.data.AccountInfo; +import com.openelements.hiero.base.data.Contract; import com.openelements.hiero.base.data.ExchangeRate; import com.openelements.hiero.base.data.ExchangeRates; import com.openelements.hiero.base.data.NetworkFee; import com.openelements.hiero.base.data.NetworkStake; import com.openelements.hiero.base.data.NetworkSupplies; import com.openelements.hiero.base.data.Nft; +import com.openelements.hiero.base.data.Page; import com.openelements.hiero.base.data.TransactionInfo; import com.openelements.hiero.base.data.Token; import com.openelements.hiero.base.data.TokenInfo; @@ -606,5 +609,147 @@ private Optional toBalance(JsonObject jsonObject) { throw new IllegalStateException("Can not parse JSON: " + jsonObject, e); } } + + // Contract-related methods + + @Override + public @NonNull Optional toContract(@NonNull JsonObject jsonObject) { + Objects.requireNonNull(jsonObject, "jsonObject must not be null"); + if (jsonObject.isEmpty()) { + return Optional.empty(); + } + + try { + final ContractId contractId = ContractId.fromString(jsonObject.getString("contract_id")); + final PublicKey adminKey = jsonObject.get("admin_key") == null ? null + : PublicKey.fromString(jsonObject.get("admin_key").asJsonObject().getString("key")); + final AccountId autoRenewAccount = jsonObject.get("auto_renew_account") == null ? null + : AccountId.fromString(jsonObject.getString("auto_renew_account")); + final int autoRenewPeriod = jsonObject.get("auto_renew_period") == null ? 0 + : jsonObject.getJsonNumber("auto_renew_period").intValue(); + final Instant createdTimestamp = jsonObject.get("created_timestamp") == null + ? Instant.ofEpochSecond(0) + : Instant.ofEpochSecond(Long.parseLong(jsonObject.get("created_timestamp").toString().replaceAll("[^0-9].*$", ""))); + final boolean deleted = jsonObject.get("deleted") != null && jsonObject.getBoolean("deleted"); + final Instant expirationTimestamp = jsonObject.get("expiration_timestamp") == null ? null + : Instant.ofEpochSecond(Long.parseLong(jsonObject.getString("expiration_timestamp").split("\\.")[0])); + final String fileId = jsonObject.getString("file_id", null); + final String evmAddress = jsonObject.getString("evm_address", null); + final String memo = jsonObject.getString("memo", null); + final Integer maxAutomaticTokenAssociations = jsonObject.get("max_automatic_token_associations") == null ? null + : jsonObject.getJsonNumber("max_automatic_token_associations").intValue(); + final Long nonce = jsonObject.get("nonce") == null ? null + : jsonObject.getJsonNumber("nonce").longValue(); + final String obtainerId = jsonObject.getString("obtainer_id", null); + final boolean permanentRemoval = jsonObject.get("permanent_removal") != null && jsonObject.getBoolean("permanent_removal"); + final String proxyAccountId = jsonObject.getString("proxy_account_id", null); + final Instant fromTimestamp = Instant.ofEpochSecond(jsonObject.getJsonObject("timestamp").getJsonNumber("from").longValue()); + final Instant toTimestamp = Instant.ofEpochSecond(jsonObject.getJsonObject("timestamp").getJsonNumber("to").longValue()); + final String bytecode = jsonObject.getString("bytecode", null); + final String runtimeBytecode = jsonObject.getString("runtime_bytecode", null); + + return Optional.of(new Contract( + contractId, + adminKey, + autoRenewAccount, + autoRenewPeriod, + createdTimestamp, + deleted, + expirationTimestamp, + fileId, + evmAddress, + memo, + maxAutomaticTokenAssociations, + nonce, + obtainerId, + permanentRemoval, + proxyAccountId, + fromTimestamp, + toTimestamp, + bytecode, + runtimeBytecode + )); + } catch (final Exception e) { + throw new IllegalStateException("Can not parse JSON: " + jsonObject, e); + } + } + + @Override + public @NonNull Page toContractPage(@NonNull JsonObject jsonObject) { + Objects.requireNonNull(jsonObject, "jsonObject must not be null"); + if (jsonObject.isEmpty()) { + return new SimplePage<>(List.of()); + } + + try { + final List contracts = toContracts(jsonObject); + return new SimplePage<>(contracts); + } catch (final Exception e) { + throw new IllegalStateException("Can not parse JSON: " + jsonObject, e); + } + } + + @Override + public @NonNull List toContracts(@NonNull JsonObject jsonObject) { + Objects.requireNonNull(jsonObject, "jsonObject must not be null"); + if (!jsonObject.containsKey("contracts")) { + return List.of(); + } + final JsonArray contractsArray = jsonObject.getJsonArray("contracts"); + if (contractsArray == null) { + throw new IllegalArgumentException("Contracts array is not an array: " + contractsArray); + } + Spliterator spliterator = Spliterators.spliteratorUnknownSize(contractsArray.iterator(), + Spliterator.ORDERED); + return StreamSupport.stream(spliterator, false) + .map(n -> toContract(n.asJsonObject())) + .filter(optional -> optional.isPresent()) + .map(optional -> optional.get()) + .toList(); + } + + // Simple Page implementation for converter methods + private static class SimplePage implements Page { + private final List data; + + public SimplePage(List data) { + this.data = data; + } + + @Override + public int getPageIndex() { + return 0; + } + + @Override + public int getSize() { + return data.size(); + } + + @Override + public List getData() { + return data; + } + + @Override + public boolean hasNext() { + return false; + } + + @Override + public Page next() { + throw new IllegalStateException("No next page"); + } + + @Override + public Page first() { + return this; + } + + @Override + public boolean isFirst() { + return true; + } + } } diff --git a/hiero-enterprise-spring/src/main/java/com/openelements/hiero/spring/implementation/HieroAutoConfiguration.java b/hiero-enterprise-spring/src/main/java/com/openelements/hiero/spring/implementation/HieroAutoConfiguration.java index 35879b71..7dd04468 100644 --- a/hiero-enterprise-spring/src/main/java/com/openelements/hiero/spring/implementation/HieroAutoConfiguration.java +++ b/hiero-enterprise-spring/src/main/java/com/openelements/hiero/spring/implementation/HieroAutoConfiguration.java @@ -9,25 +9,27 @@ import com.openelements.hiero.base.TopicClient; import com.openelements.hiero.base.config.HieroConfig; import com.openelements.hiero.base.implementation.AccountClientImpl; -import com.openelements.hiero.base.implementation.AccountRepositoryImpl; import com.openelements.hiero.base.implementation.FileClientImpl; import com.openelements.hiero.base.implementation.FungibleTokenClientImpl; -import com.openelements.hiero.base.implementation.NetworkRepositoryImpl; import com.openelements.hiero.base.implementation.NftClientImpl; -import com.openelements.hiero.base.implementation.NftRepositoryImpl; import com.openelements.hiero.base.implementation.ProtocolLayerClientImpl; import com.openelements.hiero.base.implementation.SmartContractClientImpl; -import com.openelements.hiero.base.implementation.TokenRepositoryImpl; import com.openelements.hiero.base.implementation.TopicClientImpl; +import com.openelements.hiero.base.implementation.ContractRepositoryImpl; +import com.openelements.hiero.base.implementation.NftRepositoryImpl; +import com.openelements.hiero.base.implementation.NetworkRepositoryImpl; +import com.openelements.hiero.base.implementation.TokenRepositoryImpl; +import com.openelements.hiero.base.implementation.AccountRepositoryImpl; import com.openelements.hiero.base.implementation.TopicRepositoryImpl; import com.openelements.hiero.base.implementation.TransactionRepositoryImpl; import com.openelements.hiero.base.interceptors.ReceiveRecordInterceptor; -import com.openelements.hiero.base.mirrornode.AccountRepository; import com.openelements.hiero.base.mirrornode.MirrorNodeClient; +import com.openelements.hiero.base.mirrornode.TopicRepository; import com.openelements.hiero.base.mirrornode.NetworkRepository; +import com.openelements.hiero.base.mirrornode.AccountRepository; +import com.openelements.hiero.base.mirrornode.ContractRepository; import com.openelements.hiero.base.mirrornode.NftRepository; import com.openelements.hiero.base.mirrornode.TokenRepository; -import com.openelements.hiero.base.mirrornode.TopicRepository; import com.openelements.hiero.base.mirrornode.TransactionRepository; import com.openelements.hiero.base.protocol.ProtocolLayerClient; import com.openelements.hiero.base.verification.ContractVerificationClient; @@ -38,7 +40,6 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -145,6 +146,13 @@ NftRepository nftRepository(final MirrorNodeClient mirrorNodeClient) { return new NftRepositoryImpl(mirrorNodeClient); } + @Bean + @ConditionalOnProperty(prefix = "spring.hiero", name = "mirrorNodeSupported", + havingValue = "true", matchIfMissing = true) + ContractRepository contractRepository(final MirrorNodeClient mirrorNodeClient) { + return new ContractRepositoryImpl(mirrorNodeClient); + } + @Bean @ConditionalOnProperty(prefix = "spring.hiero", name = "mirrorNodeSupported", havingValue = "true", matchIfMissing = true) diff --git a/hiero-enterprise-spring/src/main/java/com/openelements/hiero/spring/implementation/MirrorNodeJsonConverterImpl.java b/hiero-enterprise-spring/src/main/java/com/openelements/hiero/spring/implementation/MirrorNodeJsonConverterImpl.java index 99903ed0..1cb247ed 100644 --- a/hiero-enterprise-spring/src/main/java/com/openelements/hiero/spring/implementation/MirrorNodeJsonConverterImpl.java +++ b/hiero-enterprise-spring/src/main/java/com/openelements/hiero/spring/implementation/MirrorNodeJsonConverterImpl.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.hedera.hashgraph.sdk.AccountId; +import com.hedera.hashgraph.sdk.ContractId; import com.hedera.hashgraph.sdk.TokenId; import com.hedera.hashgraph.sdk.TokenSupplyType; import com.hedera.hashgraph.sdk.TokenType; @@ -9,12 +10,14 @@ import com.hedera.hashgraph.sdk.TransactionId; import com.hedera.hashgraph.sdk.PublicKey; import com.openelements.hiero.base.data.AccountInfo; +import com.openelements.hiero.base.data.Contract; import com.openelements.hiero.base.data.ExchangeRate; import com.openelements.hiero.base.data.ExchangeRates; import com.openelements.hiero.base.data.NetworkFee; import com.openelements.hiero.base.data.NetworkStake; import com.openelements.hiero.base.data.NetworkSupplies; import com.openelements.hiero.base.data.Nft; +import com.openelements.hiero.base.data.Page; import com.openelements.hiero.base.data.TransactionInfo; import com.openelements.hiero.base.data.Token; import com.openelements.hiero.base.data.TokenInfo; @@ -388,9 +391,9 @@ private CustomFee getCustomFee(JsonNode node) { .map(n ->{ final long amount = n.get("amount").asLong(); final AccountId accountId = n.get("collector_account_id").isNull()? - null : AccountId.fromString(n.get("collector_account_id").asText()); + null : AccountId.fromString(n.get("collector_account_id").asText()); final TokenId tokenId = n.get("denominating_token_id").isNull()? - null : TokenId.fromString(n.get("denominating_token_id").asText()); + null : TokenId.fromString(n.get("denominating_token_id").asText()); return new FixedFee(amount, accountId, tokenId); }) .toList(); @@ -623,4 +626,145 @@ private Stream jsonArrayToStream(@NonNull final JsonNode node) { return StreamSupport .stream(Spliterators.spliteratorUnknownSize(node.iterator(), Spliterator.ORDERED), false); } + + @Override + public @NonNull Optional toContract(@NonNull JsonNode node) { + Objects.requireNonNull(node, "jsonNode must not be null"); + if (node.isNull() || node.isEmpty()) { + return Optional.empty(); + } + + try { + final ContractId contractId = ContractId.fromString(node.get("contract_id").asText()); + final PublicKey adminKey = node.get("admin_key").isNull() ? null + : PublicKey.fromString(node.get("admin_key").get("key").asText()); + final AccountId autoRenewAccount = node.get("auto_renew_account").isNull() ? null + : AccountId.fromString(node.get("auto_renew_account").asText()); + final int autoRenewPeriod = node.get("auto_renew_period").isNull() ? 0 + : node.get("auto_renew_period").asInt(); + final Instant createdTimestamp = Instant.ofEpochSecond( + node.get("created_timestamp").isNumber() + ? node.get("created_timestamp").asLong() + : Long.parseLong(node.get("created_timestamp").asText().split("\\.")[0]) + ); + final boolean deleted = !node.get("deleted").isNull() && node.get("deleted").asBoolean(); + final Instant expirationTimestamp = node.get("expiration_timestamp").isNull() ? null + : Instant.ofEpochSecond(Long.parseLong(node.get("expiration_timestamp").asText().split("\\.")[0])); + final String fileId = node.get("file_id").isNull() ? null : node.get("file_id").asText(); + final String evmAddress = node.get("evm_address").isNull() ? null : node.get("evm_address").asText(); + final String memo = node.get("memo").isNull() ? null : node.get("memo").asText(); + final Integer maxAutomaticTokenAssociations = node.get("max_automatic_token_associations").isNull() ? null + : node.get("max_automatic_token_associations").asInt(); + final Long nonce = node.get("nonce").isNull() ? null : node.get("nonce").asLong(); + final String obtainerId = node.get("obtainer_id").isNull() ? null : node.get("obtainer_id").asText(); + final boolean permanentRemoval = !node.get("permanent_removal").isNull() && node.get("permanent_removal").asBoolean(); + final String proxyAccountId = node.get("proxy_account_id").isNull() ? null : node.get("proxy_account_id").asText(); + final Instant fromTimestamp = Instant.ofEpochSecond(node.get("timestamp").get("from").asLong()); + final Instant toTimestamp = Instant.ofEpochSecond(node.get("timestamp").get("to").asLong()); + final String bytecode = node.get("bytecode").isNull() ? null : node.get("bytecode").asText(); + final String runtimeBytecode = node.get("runtime_bytecode").isNull() ? null : node.get("runtime_bytecode").asText(); + + return Optional.of(new Contract( + contractId, + adminKey, + autoRenewAccount, + autoRenewPeriod, + createdTimestamp, + deleted, + expirationTimestamp, + fileId, + evmAddress, + memo, + maxAutomaticTokenAssociations, + nonce, + obtainerId, + permanentRemoval, + proxyAccountId, + fromTimestamp, + toTimestamp, + bytecode, + runtimeBytecode + )); + } catch (final Exception e) { + throw new JsonParseException(node, e); + } + } + + @Override + public @NonNull Page toContractPage(@NonNull JsonNode node) { + Objects.requireNonNull(node, "jsonNode must not be null"); + if (node.isNull() || node.isEmpty()) { + return new SimplePage<>(List.of()); + } + + try { + final List contracts = toContracts(node); + return new SimplePage<>(contracts); + } catch (final Exception e) { + throw new JsonParseException(node, e); + } + } + + @Override + public @NonNull List toContracts(@NonNull JsonNode node) { + Objects.requireNonNull(node, "jsonNode must not be null"); + if (!node.has("contracts")) { + return List.of(); + } + final JsonNode contractsNode = node.get("contracts"); + if (!contractsNode.isArray()) { + throw new IllegalArgumentException("Contracts node is not an array: " + contractsNode); + } + Spliterator spliterator = Spliterators.spliteratorUnknownSize(contractsNode.iterator(), + Spliterator.ORDERED); + return StreamSupport.stream(spliterator, false) + .map(n -> toContract(n)) + .filter(optional -> optional.isPresent()) + .map(optional -> optional.get()) + .toList(); + } + + // Simple Page implementation for converter methods + private static class SimplePage implements Page { + private final List data; + + public SimplePage(List data) { + this.data = data; + } + + @Override + public int getPageIndex() { + return 0; + } + + @Override + public int getSize() { + return data.size(); + } + + @Override + public List getData() { + return data; + } + + @Override + public boolean hasNext() { + return false; + } + + @Override + public Page next() { + throw new IllegalStateException("No next page"); + } + + @Override + public Page first() { + return this; + } + + @Override + public boolean isFirst() { + return true; + } + } } diff --git a/hiero-enterprise-spring/src/test/java/com/openelements/hiero/spring/test/ContractRepositoryTest.java b/hiero-enterprise-spring/src/test/java/com/openelements/hiero/spring/test/ContractRepositoryTest.java new file mode 100644 index 00000000..ea1f28a7 --- /dev/null +++ b/hiero-enterprise-spring/src/test/java/com/openelements/hiero/spring/test/ContractRepositoryTest.java @@ -0,0 +1,88 @@ +package com.openelements.hiero.spring.test; + +import com.openelements.hiero.base.HieroException; +import com.openelements.hiero.base.data.Contract; +import com.openelements.hiero.base.data.Page; +import com.openelements.hiero.base.mirrornode.ContractRepository; +import java.util.Optional; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(classes = HieroTestConfig.class) +public class ContractRepositoryTest { + + @Autowired + private ContractRepository contractRepository; + + @Test + void testNullParam() { + Assertions.assertThrows(NullPointerException.class, () -> contractRepository.findById((String) null)); + Assertions.assertThrows(NullPointerException.class, () -> contractRepository.findByEvmAddress(null)); + Assertions.assertThrows(NullPointerException.class, () -> contractRepository.findByFileId(null)); + Assertions.assertThrows(NullPointerException.class, () -> contractRepository.findByProxyAccountId(null)); + } + + @Test + void testFindAll() throws HieroException { + // when + final Page contracts = contractRepository.findAll(); + + // then + Assertions.assertNotNull(contracts); + Assertions.assertNotNull(contracts.getData()); + } + + @Test + void testFindByIdWithNonExistentContract() throws HieroException { + // given + final String nonExistentContractId = "0.0.999999"; + + // when + final Optional result = contractRepository.findById(nonExistentContractId); + + // then + Assertions.assertNotNull(result); + Assertions.assertFalse(result.isPresent()); + } + + @Test + void testFindByEvmAddressWithNonExistentAddress() throws HieroException { + // given + final String nonExistentEvmAddress = "0x0000000000000000000000000000000000000000"; + + // when + final Page result = contractRepository.findByEvmAddress(nonExistentEvmAddress); + + // then + Assertions.assertNotNull(result); + Assertions.assertNotNull(result.getData()); + } + + @Test + void testFindByFileIdWithNonExistentFileId() throws HieroException { + // given + final String nonExistentFileId = "0.0.999999"; + + // when + final Page result = contractRepository.findByFileId(nonExistentFileId); + + // then + Assertions.assertNotNull(result); + Assertions.assertNotNull(result.getData()); + } + + @Test + void testFindByProxyAccountIdWithNonExistentAccountId() throws HieroException { + // given + final String nonExistentProxyAccountId = "0.0.999999"; + + // when + final Page result = contractRepository.findByProxyAccountId(nonExistentProxyAccountId); + + // then + Assertions.assertNotNull(result); + Assertions.assertNotNull(result.getData()); + } +} From f04f9d74f78b1db12f23b727ce4e63a15c43c937 Mon Sep 17 00:00:00 2001 From: Ndacyayisenga-droid Date: Fri, 14 Nov 2025 13:54:57 +0300 Subject: [PATCH 2/2] Remove none existent endpoints Signed-off-by: Ndacyayisenga-droid --- .../hiero/base/data/SinglePage.java | 54 +++++++++++++++++++ .../AbstractMirrorNodeClient.java | 20 ------- .../ContractRepositoryImpl.java | 18 ------- .../implementation/MirrorNodeRestClient.java | 18 ------- .../base/mirrornode/ContractRepository.java | 29 ---------- .../base/mirrornode/MirrorNodeClient.java | 29 ---------- .../MirrorNodeJsonConverterImpl.java | 53 ++---------------- .../MirrorNodeJsonConverterImpl.java | 52 ++---------------- .../spring/test/ContractRepositoryTest.java | 41 -------------- 9 files changed, 62 insertions(+), 252 deletions(-) create mode 100644 hiero-enterprise-base/src/main/java/com/openelements/hiero/base/data/SinglePage.java diff --git a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/data/SinglePage.java b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/data/SinglePage.java new file mode 100644 index 00000000..b77a89a5 --- /dev/null +++ b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/data/SinglePage.java @@ -0,0 +1,54 @@ +package com.openelements.hiero.base.data; + +import java.util.List; +import java.util.Objects; + +/** + * Basic {@link Page} implementation backed by an in-memory list. + * + * @param element type for the page + */ +public final class SinglePage implements Page { + + private final List data; + + public SinglePage(final List data) { + this.data = List.copyOf(Objects.requireNonNull(data, "data must not be null")); + } + + @Override + public int getPageIndex() { + return 0; + } + + @Override + public int getSize() { + return data.size(); + } + + @Override + public List getData() { + return data; + } + + @Override + public boolean hasNext() { + return false; + } + + @Override + public Page next() { + throw new IllegalStateException("No next page"); + } + + @Override + public Page first() { + return this; + } + + @Override + public boolean isFirst() { + return true; + } +} + diff --git a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/AbstractMirrorNodeClient.java b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/AbstractMirrorNodeClient.java index 8e64cdb7..0669b4f9 100644 --- a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/AbstractMirrorNodeClient.java +++ b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/AbstractMirrorNodeClient.java @@ -121,24 +121,4 @@ public final Optional queryTopicMessageBySequenceNumber(TopicId to return getJsonConverter().toContract(json); } - @Override - public @NonNull Page queryContractsByEvmAddress(@NonNull final String evmAddress) throws HieroException { - Objects.requireNonNull(evmAddress, "evmAddress must not be null"); - final JSON json = getRestClient().queryContractsByEvmAddress(evmAddress); - return getJsonConverter().toContractPage(json); - } - - @Override - public @NonNull Page queryContractsByFileId(@NonNull final String fileId) throws HieroException { - Objects.requireNonNull(fileId, "fileId must not be null"); - final JSON json = getRestClient().queryContractsByFileId(fileId); - return getJsonConverter().toContractPage(json); - } - - @Override - public @NonNull Page queryContractsByProxyAccountId(@NonNull final String proxyAccountId) throws HieroException { - Objects.requireNonNull(proxyAccountId, "proxyAccountId must not be null"); - final JSON json = getRestClient().queryContractsByProxyAccountId(proxyAccountId); - return getJsonConverter().toContractPage(json); - } } diff --git a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/ContractRepositoryImpl.java b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/ContractRepositoryImpl.java index 83432e98..ac2bc042 100644 --- a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/ContractRepositoryImpl.java +++ b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/ContractRepositoryImpl.java @@ -37,22 +37,4 @@ public Page findAll() throws HieroException { public Optional findById(@NonNull final ContractId contractId) throws HieroException { return mirrorNodeClient.queryContractById(contractId); } - - @NonNull - @Override - public Page findByEvmAddress(@NonNull final String evmAddress) throws HieroException { - return mirrorNodeClient.queryContractsByEvmAddress(evmAddress); - } - - @NonNull - @Override - public Page findByFileId(@NonNull final String fileId) throws HieroException { - return mirrorNodeClient.queryContractsByFileId(fileId); - } - - @NonNull - @Override - public Page findByProxyAccountId(@NonNull final String proxyAccountId) throws HieroException { - return mirrorNodeClient.queryContractsByProxyAccountId(proxyAccountId); - } } diff --git a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/MirrorNodeRestClient.java b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/MirrorNodeRestClient.java index 3cfd46f8..8c0d3077 100644 --- a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/MirrorNodeRestClient.java +++ b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/MirrorNodeRestClient.java @@ -80,22 +80,4 @@ default JSON queryContractById(@NonNull final ContractId contractId) throws Hier Objects.requireNonNull(contractId, "contractId must not be null"); return doGetCall("/api/v1/contracts/" + contractId); } - - @NonNull - default JSON queryContractsByEvmAddress(@NonNull final String evmAddress) throws HieroException { - Objects.requireNonNull(evmAddress, "evmAddress must not be null"); - return doGetCall("/api/v1/contracts?evm.address=" + evmAddress); - } - - @NonNull - default JSON queryContractsByFileId(@NonNull final String fileId) throws HieroException { - Objects.requireNonNull(fileId, "fileId must not be null"); - return doGetCall("/api/v1/contracts?file.id=" + fileId); - } - - @NonNull - default JSON queryContractsByProxyAccountId(@NonNull final String proxyAccountId) throws HieroException { - Objects.requireNonNull(proxyAccountId, "proxyAccountId must not be null"); - return doGetCall("/api/v1/contracts?proxy.account.id=" + proxyAccountId); - } } diff --git a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/mirrornode/ContractRepository.java b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/mirrornode/ContractRepository.java index b2a77b35..0a99f731 100644 --- a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/mirrornode/ContractRepository.java +++ b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/mirrornode/ContractRepository.java @@ -45,33 +45,4 @@ default Optional findById(@NonNull String contractId) throws HieroExce return findById(ContractId.fromString(contractId)); } - /** - * Return contracts by EVM address. - * - * @param evmAddress the EVM address of the contract - * @return first page of contracts - * @throws HieroException if the search fails - */ - @NonNull - Page findByEvmAddress(@NonNull String evmAddress) throws HieroException; - - /** - * Return contracts by file ID. - * - * @param fileId the file ID associated with the contract - * @return first page of contracts - * @throws HieroException if the search fails - */ - @NonNull - Page findByFileId(@NonNull String fileId) throws HieroException; - - /** - * Return contracts by proxy account ID. - * - * @param proxyAccountId the proxy account ID - * @return first page of contracts - * @throws HieroException if the search fails - */ - @NonNull - Page findByProxyAccountId(@NonNull String proxyAccountId) throws HieroException; } diff --git a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/mirrornode/MirrorNodeClient.java b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/mirrornode/MirrorNodeClient.java index a5329031..7c167ee9 100644 --- a/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/mirrornode/MirrorNodeClient.java +++ b/hiero-enterprise-base/src/main/java/com/openelements/hiero/base/mirrornode/MirrorNodeClient.java @@ -494,33 +494,4 @@ default Optional queryContractById(@NonNull String contractId) throws return queryContractById(ContractId.fromString(contractId)); } - /** - * Queries contracts by EVM address. - * - * @param evmAddress the EVM address - * @return the contracts - * @throws HieroException if an error occurs - */ - @NonNull - Page queryContractsByEvmAddress(@NonNull String evmAddress) throws HieroException; - - /** - * Queries contracts by file ID. - * - * @param fileId the file ID - * @return the contracts - * @throws HieroException if an error occurs - */ - @NonNull - Page queryContractsByFileId(@NonNull String fileId) throws HieroException; - - /** - * Queries contracts by proxy account ID. - * - * @param proxyAccountId the proxy account ID - * @return the contracts - * @throws HieroException if an error occurs - */ - @NonNull - Page queryContractsByProxyAccountId(@NonNull String proxyAccountId) throws HieroException; } diff --git a/hiero-enterprise-microprofile/src/main/java/com/openelements/hiero/microprofile/implementation/MirrorNodeJsonConverterImpl.java b/hiero-enterprise-microprofile/src/main/java/com/openelements/hiero/microprofile/implementation/MirrorNodeJsonConverterImpl.java index fef6fdaa..64e69762 100644 --- a/hiero-enterprise-microprofile/src/main/java/com/openelements/hiero/microprofile/implementation/MirrorNodeJsonConverterImpl.java +++ b/hiero-enterprise-microprofile/src/main/java/com/openelements/hiero/microprofile/implementation/MirrorNodeJsonConverterImpl.java @@ -17,6 +17,7 @@ import com.openelements.hiero.base.data.NetworkSupplies; import com.openelements.hiero.base.data.Nft; import com.openelements.hiero.base.data.Page; +import com.openelements.hiero.base.data.SinglePage; import com.openelements.hiero.base.data.TransactionInfo; import com.openelements.hiero.base.data.Token; import com.openelements.hiero.base.data.TokenInfo; @@ -678,12 +679,12 @@ private Optional toBalance(JsonObject jsonObject) { public @NonNull Page toContractPage(@NonNull JsonObject jsonObject) { Objects.requireNonNull(jsonObject, "jsonObject must not be null"); if (jsonObject.isEmpty()) { - return new SimplePage<>(List.of()); + return new SinglePage<>(List.of()); } try { final List contracts = toContracts(jsonObject); - return new SimplePage<>(contracts); + return new SinglePage<>(contracts); } catch (final Exception e) { throw new IllegalStateException("Can not parse JSON: " + jsonObject, e); } @@ -697,9 +698,9 @@ private Optional toBalance(JsonObject jsonObject) { } final JsonArray contractsArray = jsonObject.getJsonArray("contracts"); if (contractsArray == null) { - throw new IllegalArgumentException("Contracts array is not an array: " + contractsArray); + throw new IllegalArgumentException("No contracts array in JSON"); } - Spliterator spliterator = Spliterators.spliteratorUnknownSize(contractsArray.iterator(), + final Spliterator spliterator = Spliterators.spliteratorUnknownSize(contractsArray.iterator(), Spliterator.ORDERED); return StreamSupport.stream(spliterator, false) .map(n -> toContract(n.asJsonObject())) @@ -708,48 +709,4 @@ private Optional toBalance(JsonObject jsonObject) { .toList(); } - // Simple Page implementation for converter methods - private static class SimplePage implements Page { - private final List data; - - public SimplePage(List data) { - this.data = data; - } - - @Override - public int getPageIndex() { - return 0; - } - - @Override - public int getSize() { - return data.size(); - } - - @Override - public List getData() { - return data; - } - - @Override - public boolean hasNext() { - return false; - } - - @Override - public Page next() { - throw new IllegalStateException("No next page"); - } - - @Override - public Page first() { - return this; - } - - @Override - public boolean isFirst() { - return true; - } - } } - diff --git a/hiero-enterprise-spring/src/main/java/com/openelements/hiero/spring/implementation/MirrorNodeJsonConverterImpl.java b/hiero-enterprise-spring/src/main/java/com/openelements/hiero/spring/implementation/MirrorNodeJsonConverterImpl.java index 1cb247ed..0db47391 100644 --- a/hiero-enterprise-spring/src/main/java/com/openelements/hiero/spring/implementation/MirrorNodeJsonConverterImpl.java +++ b/hiero-enterprise-spring/src/main/java/com/openelements/hiero/spring/implementation/MirrorNodeJsonConverterImpl.java @@ -18,6 +18,7 @@ import com.openelements.hiero.base.data.NetworkSupplies; import com.openelements.hiero.base.data.Nft; import com.openelements.hiero.base.data.Page; +import com.openelements.hiero.base.data.SinglePage; import com.openelements.hiero.base.data.TransactionInfo; import com.openelements.hiero.base.data.Token; import com.openelements.hiero.base.data.TokenInfo; @@ -46,13 +47,9 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; import org.jspecify.annotations.NonNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class MirrorNodeJsonConverterImpl implements MirrorNodeJsonConverter { - private static final Logger log = LoggerFactory.getLogger(MirrorNodeJsonConverterImpl.class); - @Override public Optional toNft(final JsonNode node) { Objects.requireNonNull(node, "jsonNode must not be null"); @@ -694,12 +691,12 @@ private Stream jsonArrayToStream(@NonNull final JsonNode node) { public @NonNull Page toContractPage(@NonNull JsonNode node) { Objects.requireNonNull(node, "jsonNode must not be null"); if (node.isNull() || node.isEmpty()) { - return new SimplePage<>(List.of()); + return new SinglePage<>(List.of()); } try { final List contracts = toContracts(node); - return new SimplePage<>(contracts); + return new SinglePage<>(contracts); } catch (final Exception e) { throw new JsonParseException(node, e); } @@ -724,47 +721,4 @@ private Stream jsonArrayToStream(@NonNull final JsonNode node) { .toList(); } - // Simple Page implementation for converter methods - private static class SimplePage implements Page { - private final List data; - - public SimplePage(List data) { - this.data = data; - } - - @Override - public int getPageIndex() { - return 0; - } - - @Override - public int getSize() { - return data.size(); - } - - @Override - public List getData() { - return data; - } - - @Override - public boolean hasNext() { - return false; - } - - @Override - public Page next() { - throw new IllegalStateException("No next page"); - } - - @Override - public Page first() { - return this; - } - - @Override - public boolean isFirst() { - return true; - } - } } diff --git a/hiero-enterprise-spring/src/test/java/com/openelements/hiero/spring/test/ContractRepositoryTest.java b/hiero-enterprise-spring/src/test/java/com/openelements/hiero/spring/test/ContractRepositoryTest.java index ea1f28a7..f00ae68b 100644 --- a/hiero-enterprise-spring/src/test/java/com/openelements/hiero/spring/test/ContractRepositoryTest.java +++ b/hiero-enterprise-spring/src/test/java/com/openelements/hiero/spring/test/ContractRepositoryTest.java @@ -19,9 +19,6 @@ public class ContractRepositoryTest { @Test void testNullParam() { Assertions.assertThrows(NullPointerException.class, () -> contractRepository.findById((String) null)); - Assertions.assertThrows(NullPointerException.class, () -> contractRepository.findByEvmAddress(null)); - Assertions.assertThrows(NullPointerException.class, () -> contractRepository.findByFileId(null)); - Assertions.assertThrows(NullPointerException.class, () -> contractRepository.findByProxyAccountId(null)); } @Test @@ -47,42 +44,4 @@ void testFindByIdWithNonExistentContract() throws HieroException { Assertions.assertFalse(result.isPresent()); } - @Test - void testFindByEvmAddressWithNonExistentAddress() throws HieroException { - // given - final String nonExistentEvmAddress = "0x0000000000000000000000000000000000000000"; - - // when - final Page result = contractRepository.findByEvmAddress(nonExistentEvmAddress); - - // then - Assertions.assertNotNull(result); - Assertions.assertNotNull(result.getData()); - } - - @Test - void testFindByFileIdWithNonExistentFileId() throws HieroException { - // given - final String nonExistentFileId = "0.0.999999"; - - // when - final Page result = contractRepository.findByFileId(nonExistentFileId); - - // then - Assertions.assertNotNull(result); - Assertions.assertNotNull(result.getData()); - } - - @Test - void testFindByProxyAccountIdWithNonExistentAccountId() throws HieroException { - // given - final String nonExistentProxyAccountId = "0.0.999999"; - - // when - final Page result = contractRepository.findByProxyAccountId(nonExistentProxyAccountId); - - // then - Assertions.assertNotNull(result); - Assertions.assertNotNull(result.getData()); - } }