Skip to content
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,34 @@
package com.openelements.hedera.base;

import com.hedera.hashgraph.sdk.AccountId;
import com.openelements.hedera.base.mirrornode.AccountInfo;
import org.jspecify.annotations.NonNull;

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

/**
* Interface for interacting with a Hedera network. This interface provides methods for searching for Accounts.
*/
public interface AccountRepository {
/**
* Return the AccountInfo of a given accountId.
*
* @param accountId id of the account
* @return {@link Optional} containing the found AccountInfo or null
* @throws HederaException if the search fails
*/
Optional<AccountInfo> findById(@NonNull AccountId accountId) throws HederaException;

/**
* Return the AccountInfo of a given accountId.
*
* @param accountId id of the account
* @return {@link Optional} containing the found AccountInfo or null
* @throws HederaException if the search fails
*/
default Optional<AccountInfo> findById(@NonNull String accountId) throws HederaException {
Objects.requireNonNull(accountId, "accountId must not be null");
return findById(AccountId.fromString(accountId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.openelements.hedera.base.implementation;

import com.hedera.hashgraph.sdk.AccountId;
import com.openelements.hedera.base.AccountRepository;
import com.openelements.hedera.base.HederaException;
import com.openelements.hedera.base.mirrornode.AccountInfo;
import com.openelements.hedera.base.mirrornode.MirrorNodeClient;
import org.jspecify.annotations.NonNull;

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

public class AccountRepositoryImpl implements AccountRepository {
private final MirrorNodeClient mirrorNodeClient;

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

@Override
public Optional<AccountInfo> findById(@NonNull AccountId accountId) throws HederaException {
return mirrorNodeClient.queryAccount(accountId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.openelements.hedera.base.mirrornode;

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

import java.util.Objects;

public record AccountInfo(@NonNull AccountId accountId, @NonNull String evmAddress, long balance, long ethereumNonce, long pendingReward) {
public AccountInfo {
Objects.requireNonNull(accountId, "accountId must not be null");
Objects.requireNonNull(evmAddress, "evmAddress must not be null");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,27 @@ default Optional<Nft> queryNftsByAccountAndTokenIdAndSerial(@NonNull String acco
*/
@NonNull
Optional<TransactionInfo> queryTransaction(@NonNull String transactionId) throws HederaException;

/**
* Queries the account information for a specific account ID.
*
* @param accountId the account ID
* @return the account information for the account ID
* @throws HederaException if an error occurs
*/
@NonNull
Optional<AccountInfo> queryAccount(@NonNull AccountId accountId) throws HederaException;

/**
* Queries the account information for a specific account ID.
*
* @param accountId the account ID
* @return the account information for the account ID
* @throws HederaException if an error occurs
*/
@NonNull
default Optional<AccountInfo> queryAccount(@NonNull String accountId) throws HederaException {
Objects.requireNonNull(accountId, "accountId must not be null");
return queryAccount(AccountId.fromString(accountId));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.openelements.hedera.base.FileClient;
import com.openelements.hedera.base.NftClient;
import com.openelements.hedera.base.NftRepository;
import com.openelements.hedera.base.AccountRepository;
import com.openelements.hedera.base.SmartContractClient;
import com.openelements.hedera.base.implementation.AccountClientImpl;
import com.openelements.hedera.base.implementation.FileClientImpl;
Expand All @@ -17,6 +18,7 @@
import com.openelements.hedera.base.implementation.NftRepositoryImpl;
import com.openelements.hedera.base.implementation.ProtocolLayerClientImpl;
import com.openelements.hedera.base.implementation.SmartContractClientImpl;
import com.openelements.hedera.base.implementation.AccountRepositoryImpl;
import com.openelements.hedera.base.mirrornode.MirrorNodeClient;
import com.openelements.hedera.base.protocol.ProtocolLayerClient;
import java.net.URI;
Expand Down Expand Up @@ -200,6 +202,13 @@ NftRepository nftRepository(final MirrorNodeClient mirrorNodeClient) {
return new NftRepositoryImpl(mirrorNodeClient);
}

@Bean
@ConditionalOnProperty(prefix = "spring.hedera", name = "mirrorNodeSupported",
havingValue = "true", matchIfMissing = true)
AccountRepository accountRepository(final MirrorNodeClient mirrorNodeClient) {
return new AccountRepositoryImpl(mirrorNodeClient);
}

@Bean
ContractVerificationClient contractVerificationClient(HederaNetwork hederaNetwork) {
return new ContractVerificationClientImplementation(hederaNetwork);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.hedera.hashgraph.sdk.TokenId;
import com.openelements.hedera.base.HederaException;
import com.openelements.hedera.base.Nft;
import com.openelements.hedera.base.mirrornode.AccountInfo;
import com.openelements.hedera.base.mirrornode.MirrorNodeClient;
import com.openelements.hedera.base.mirrornode.Page;
import com.openelements.hedera.base.mirrornode.TransactionInfo;
Expand Down Expand Up @@ -98,6 +99,13 @@ public Optional<TransactionInfo> queryTransaction(@NonNull final String transact
return Optional.of(new TransactionInfo(transactionId));
}

@Override
public @NonNull Optional<AccountInfo> queryAccount(@NonNull AccountId accountId) throws HederaException {
Objects.requireNonNull(accountId, "accountId must not be null");
final JsonNode jsonNode = doGetCall("/api/v1/accounts/" + accountId);
return jsonNodeToOptionalAccountINfo(jsonNode);
}

private JsonNode doGetCall(String path, Map<String, ?> params) throws HederaException {
return doGetCall(builder -> {
UriBuilder uriBuilder = builder.path(path);
Expand Down Expand Up @@ -172,6 +180,30 @@ private Nft jsonNodeToNft(final JsonNode jsonNode) throws IOException {
}
}

private @NonNull Optional<AccountInfo> jsonNodeToOptionalAccountINfo(JsonNode jsonNode) throws HederaException {
if (jsonNode == null || !jsonNode.fieldNames().hasNext()) {
return Optional.empty();
}
try {
return Optional.of(jsonNodeToAccountInfo(jsonNode));
} catch (final Exception e) {
throw new HederaException("Error parsing AccountInfo from JSON '" + jsonNode + "'", e);
}
}

private AccountInfo jsonNodeToAccountInfo(JsonNode jsonNode) {
try {
final AccountId accountId = AccountId.fromString(jsonNode.get("account").asText());
final String evmAddress = jsonNode.get("evm_address").asText();
final long ethereumNonce = jsonNode.get("ethereum_nonce").asLong();
final long pendingReward = jsonNode.get("pending_reward").asLong();
final long balance = jsonNode.get("balance").get("balance").asLong();
return new AccountInfo(accountId, evmAddress, balance, ethereumNonce, pendingReward);
} catch (final Exception e) {
throw new IllegalArgumentException("Error parsing NFT from JSON '" + jsonNode + "'", e);
}
}

private List<Nft> getNfts(final JsonNode jsonNode) {
if (!jsonNode.has("nfts")) {
return List.of();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.openelements.hedera.spring.test;

import com.hedera.hashgraph.sdk.AccountId;
import com.openelements.hedera.base.Account;
import com.openelements.hedera.base.AccountClient;
import com.openelements.hedera.base.AccountRepository;
import com.openelements.hedera.base.mirrornode.AccountInfo;
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;

import java.util.Optional;

@SpringBootTest(classes = TestConfig.class)
public class AccountRepositoryTest {
@Autowired
private AccountRepository accountRepository;

@Autowired
private HederaTestUtils hederaTestUtils;

@Autowired
private AccountClient accountClient;

@Test
void findById() throws Exception {
//given
final Account account = accountClient.createAccount();
final AccountId newOwner = account.accountId();
hederaTestUtils.waitForMirrorNodeRecords();

//when
final Optional<AccountInfo> result = accountRepository.findById(newOwner);

//then
Assertions.assertNotNull(result);
Assertions.assertTrue(result.isPresent());

}
}