Skip to content
This repository was archived by the owner on May 16, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.openelements.hiero.base.data;

import com.hedera.hashgraph.sdk.TransactionId;
import org.jspecify.annotations.NonNull;

import java.util.Objects;

public record ChunkInfo(
@NonNull TransactionId initialTransactionId,
int nonce,
int number,
int total,
boolean scheduled
) {
public ChunkInfo {
Objects.requireNonNull(initialTransactionId, "initialTransactionId must not be null");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.openelements.hiero.base.data;

import com.hedera.hashgraph.sdk.AccountId;
import com.hedera.hashgraph.sdk.PublicKey;
import com.hedera.hashgraph.sdk.TopicId;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;

import java.time.Instant;
import java.util.List;
import java.util.Objects;

public record Topic(
@NonNull TopicId topicId,
@Nullable PublicKey adminKey,
@Nullable AccountId autoRenewAccount,
int autoRenewPeriod,
@NonNull Instant createdTimestamp,
@NonNull List<FixedFee> fixedFees,
@Nullable List<PublicKey> feeExemptKeyList,
@Nullable PublicKey feeScheduleKey,
@Nullable PublicKey submitKey,
boolean deleted,
String memo,
@NonNull Instant fromTimestamp,
@NonNull Instant toTimestamp
) {
public Topic {
Objects.requireNonNull(topicId, "topicId must not be null");
Objects.requireNonNull(createdTimestamp, "createdTimestamp must not be null");
Objects.requireNonNull(fixedFees, "fixedFees must not be null");
Objects.requireNonNull(fromTimestamp, "fromTimestamp must not be null");
Objects.requireNonNull(toTimestamp, "toTimestamp must not be null");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.openelements.hiero.base.data;

import com.hedera.hashgraph.sdk.AccountId;
import com.hedera.hashgraph.sdk.TopicId;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;

import java.time.Instant;
import java.util.Objects;

public record TopicMessage(
@Nullable ChunkInfo chunkInfo,
@NonNull Instant consensusTimestamp,
@NonNull String message,
@NonNull AccountId payerAccountId,
byte[] runningHash,
int runningHashVersion,
long sequenceNumber,
@NonNull TopicId topicId
) {
public TopicMessage {
Objects.requireNonNull(consensusTimestamp, "consensusTimestamp must not be null");
Objects.requireNonNull(message, "message must not be null");
Objects.requireNonNull(payerAccountId, "payerAccountId must not be null");
Objects.requireNonNull(topicId, "topicId must not be null");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.hedera.hashgraph.sdk.AccountId;
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.ExchangeRates;
Expand All @@ -12,6 +13,8 @@
import com.openelements.hiero.base.data.NftMetadata;
import com.openelements.hiero.base.data.TokenInfo;
import com.openelements.hiero.base.data.TransactionInfo;
import com.openelements.hiero.base.data.Topic;
import com.openelements.hiero.base.data.TopicMessage;
import com.openelements.hiero.base.mirrornode.MirrorNodeClient;
import java.util.List;
import java.util.Objects;
Expand Down Expand Up @@ -83,6 +86,20 @@ public final Optional<TransactionInfo> queryTransaction(@NonNull String transact
return getJsonConverter().toTransactionInfo(json);
}

@Override
@NonNull
public final Optional<Topic> queryTopicById(TopicId topicId) throws HieroException {
final JSON json = getRestClient().queryTopicById(topicId);
return getJsonConverter().toTopic(json);
}

@Override
@NonNull
public final Optional<TopicMessage> queryTopicMessageBySequenceNumber(TopicId topicId, long sequenceNumber) throws HieroException {
final JSON json = getRestClient().queryTopicMessageBySequenceNumber(topicId, sequenceNumber);
return getJsonConverter().toTopicMessage(json);
}

@Override
public @NonNull Optional<NftMetadata> getNftMetadata(TokenId tokenId) throws HieroException {
throw new UnsupportedOperationException("Not yet implemented");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import com.openelements.hiero.base.data.Token;
import com.openelements.hiero.base.data.TokenInfo;
import com.openelements.hiero.base.data.Balance;
import com.openelements.hiero.base.data.Topic;
import com.openelements.hiero.base.data.TopicMessage;

import java.util.List;
import java.util.Optional;
Expand Down Expand Up @@ -48,4 +50,13 @@ public interface MirrorNodeJsonConverter<JSON> {
List<Balance> toBalances(JSON node);

List<Token> toTokens(JSON node);

@NonNull
Optional<Topic> toTopic(JSON json);

@NonNull
Optional<TopicMessage> toTopicMessage(JSON json);

@NonNull
List<TopicMessage> toTopicMessages(JSON json);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.hedera.hashgraph.sdk.AccountId;
import com.hedera.hashgraph.sdk.TokenId;
import com.hedera.hashgraph.sdk.TopicId;
import com.openelements.hiero.base.HieroException;
import java.util.Objects;
import org.jspecify.annotations.NonNull;
Expand Down Expand Up @@ -55,6 +56,16 @@ default JSON queryTokenById(TokenId tokenId) throws HieroException {
return doGetCall("/api/v1/tokens/" + tokenId);
}

@NonNull
default JSON queryTopicById(TopicId topicId) throws HieroException {
return doGetCall("/api/v1/topics/" + topicId);
}

@NonNull
default JSON queryTopicMessageBySequenceNumber(TopicId topicId, long sequenceNumber) throws HieroException {
return doGetCall("/api/v1/topics/" + topicId + "/messages/" + sequenceNumber);
}

@NonNull
JSON doGetCall(@NonNull String path) throws HieroException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.openelements.hiero.base.implementation;

import com.hedera.hashgraph.sdk.TopicId;
import com.openelements.hiero.base.HieroException;
import com.openelements.hiero.base.data.Page;
import com.openelements.hiero.base.data.Topic;
import com.openelements.hiero.base.data.TopicMessage;
import com.openelements.hiero.base.mirrornode.MirrorNodeClient;
import com.openelements.hiero.base.mirrornode.TopicRepository;
import org.jspecify.annotations.NonNull;

import java.util.Objects;
import java.util.Optional;

public class TopicRepositoryImpl implements TopicRepository {
private final MirrorNodeClient mirrorNodeClient;

public TopicRepositoryImpl(@NonNull final MirrorNodeClient mirrorNodeClient) {
this.mirrorNodeClient = Objects.requireNonNull(mirrorNodeClient, "mirrorNodeClient must not be null");
}

@Override
public @NonNull Optional<Topic> findTopicById(TopicId topicId) throws HieroException {
Objects.requireNonNull(topicId, "topicId must not be null");
return mirrorNodeClient.queryTopicById(topicId);
}

@Override
public @NonNull Page<TopicMessage> getMessages(TopicId topicId) throws HieroException {
Objects.requireNonNull(topicId, "topicId must not be null");
return mirrorNodeClient.queryTopicMessages(topicId);
}

@Override
public @NonNull Optional<TopicMessage> getMessageBySequenceNumber(TopicId topicId, long sequenceNumber) throws HieroException {
Objects.requireNonNull(topicId, "topicId must not be null");
if (sequenceNumber < 1) {
throw new IllegalArgumentException("sequenceNumber must be greater than 0");
}
return mirrorNodeClient.queryTopicMessageBySequenceNumber(topicId, sequenceNumber);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.hedera.hashgraph.sdk.AccountId;
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;
Expand All @@ -15,6 +16,8 @@
import com.openelements.hiero.base.data.Token;
import com.openelements.hiero.base.data.TokenInfo;
import com.openelements.hiero.base.data.TransactionInfo;
import com.openelements.hiero.base.data.Topic;
import com.openelements.hiero.base.data.TopicMessage;
import com.openelements.hiero.base.data.Result;
import com.openelements.hiero.base.data.BalanceModification;
import java.util.List;
Expand Down Expand Up @@ -377,6 +380,77 @@ default Page<Balance> queryTokenBalancesForAccount(@NonNull String tokenId, @Non
return queryTokenBalancesForAccount(TokenId.fromString(tokenId), AccountId.fromString(accountId));
}

/**
* Return Topic for given topicId.
*
* @param topicId id of the topic
* @return Optional of Topic
* @throws HieroException if the search fails
*/
@NonNull
Optional<Topic> queryTopicById(TopicId topicId) throws HieroException;

/**
* Return Topic for given topicId.
*
* @param topicId id of the topic
* @return Optional of Topic
* @throws HieroException if the search fails
*/
@NonNull
default Optional<Topic> queryTopicById(String topicId) throws HieroException {
Objects.requireNonNull(topicId, "topicId must not be null");
return queryTopicById(TopicId.fromString(topicId));
}

/**
* Return TopicMessages for given topicId.
*
* @param topicId id of the topic
* @return Page of TopicMessage
* @throws HieroException if the search fails
*/
@NonNull
Page<TopicMessage> queryTopicMessages(TopicId topicId) throws HieroException;

/**
* Return TopicMessages for given topicId.
*
* @param topicId id of the topic
* @return Page of TopicMessage
* @throws HieroException if the search fails
*/
@NonNull
default Page<TopicMessage> queryTopicMessages(String topicId) throws HieroException {
Objects.requireNonNull(topicId, "topicId must not be null");
return queryTopicMessages(TopicId.fromString(topicId));
}

/**
* Return TopicMessage for given topicId.
*
* @param topicId id of the topic
* @param sequenceNumber sequenceNumber of the message
* @return Optional of TopicMessage
* @throws HieroException if the search fails
*/
@NonNull
Optional<TopicMessage> queryTopicMessageBySequenceNumber(TopicId topicId, long sequenceNumber) throws HieroException;

/**
* Return TopicMessage for given topicId.
*
* @param topicId id of the topic
* @param sequenceNumber sequenceNumber of the message
* @return Optional of TopicMessage
* @throws HieroException if the search fails
*/
@NonNull
default Optional<TopicMessage> queryTopicMessageBySequenceNumber(String topicId, long sequenceNumber) throws HieroException {
Objects.requireNonNull(topicId, "topicId must not be null");
return queryTopicMessageBySequenceNumber(TopicId.fromString(topicId), sequenceNumber);
}

@NonNull
Optional<NftMetadata> getNftMetadata(@NonNull TokenId tokenId) throws HieroException;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.openelements.hiero.base.mirrornode;

import com.hedera.hashgraph.sdk.TopicId;
import com.openelements.hiero.base.HieroException;
import com.openelements.hiero.base.data.Page;
import com.openelements.hiero.base.data.Topic;
import com.openelements.hiero.base.data.TopicMessage;
import org.jspecify.annotations.NonNull;

import java.util.Objects;
import java.util.Optional;

/**
* Interface for interacting with a Hiero network. This interface provides methods
* for searching Topic and TopicMessages.
*/
public interface TopicRepository {
/**
* Return Topic for given topicId.
*
* @param topicId id of the topic
* @return Optional of Topic
* @throws HieroException if the search fails
*/
@NonNull
Optional<Topic> findTopicById(TopicId topicId) throws HieroException;

/**
* Return Topic for given topicId.
*
* @param topicId id of the topic
* @return Optional of Topic
* @throws HieroException if the search fails
*/
@NonNull
default Optional<Topic> findTopicById(String topicId) throws HieroException {
Objects.requireNonNull(topicId, "topicId must not be null");
return findTopicById(TopicId.fromString(topicId));
}

/**
* Return TopicMessages for given topicId.
*
* @param topicId id of the topic
* @return Page of TopicMessage
* @throws HieroException if the search fails
*/
@NonNull
Page<TopicMessage> getMessages(TopicId topicId) throws HieroException;

/**
* Return TopicMessages for given topicId.
*
* @param topicId id of the topic
* @return Page of TopicMessage
* @throws HieroException if the search fails
*/
@NonNull
default Page<TopicMessage> getMessages(String topicId) throws HieroException {
Objects.requireNonNull(topicId, "topicId must not be null");
return getMessages(TopicId.fromString(topicId));
};

/**
* Return TopicMessage for given topicId.
*
* @param topicId id of the topic
* @param sequenceNumber sequenceNumber of the message
* @return Optional of TopicMessage
* @throws HieroException if the search fails
*/
@NonNull
Optional<TopicMessage> getMessageBySequenceNumber(TopicId topicId, long sequenceNumber) throws HieroException;

/**
* Return TopicMessage for given topicId.
*
* @param topicId id of the topic
* @param sequenceNumber sequenceNumber of the message
* @return Optional of TopicMessage
* @throws HieroException if the search fails
*/
@NonNull
default Optional<TopicMessage> getMessageBySequenceNumber(String topicId, long sequenceNumber) throws HieroException {
Objects.requireNonNull(topicId, "topicId must not be null");
return getMessageBySequenceNumber(TopicId.fromString(topicId), sequenceNumber);
};
}
Loading
Loading