Skip to content

Commit 89ef5e5

Browse files
committed
Merge branch 'main' into mirror-node-support
# Conflicts: # hiero-enterprise-microprofile/src/main/java/com/openelements/hiero/microprofile/ClientProvider.java # hiero-enterprise-microprofile/src/main/java/com/openelements/hiero/microprofile/implementation/MirrorNodeClientImpl.java # hiero-enterprise-spring/src/main/java/com/openelements/hiero/spring/implementation/MirrorNodeClientImpl.java
2 parents 9a7bf15 + a898b04 commit 89ef5e5

File tree

31 files changed

+1406
-145
lines changed

31 files changed

+1406
-145
lines changed

hiero-enterprise-base/src/main/java/com/openelements/hiero/base/HieroContext.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,25 @@
44
import com.openelements.hiero.base.data.Account;
55
import org.jspecify.annotations.NonNull;
66

7+
/**
8+
* Context for a specific Hiero connection to a network.
9+
*/
710
public interface HieroContext {
811

12+
/**
13+
* Get the account that is used to pay for the transactions that are sent to the network. This account is called the
14+
* 'operator account'.
15+
*
16+
* @return the operator account
17+
*/
918
@NonNull
1019
Account getOperatorAccount();
1120

21+
/**
22+
* Get the 'native' client that is used to interact with the hiero network.
23+
*
24+
* @return the client
25+
*/
1226
@NonNull
1327
Client getClient();
1428
}

hiero-enterprise-base/src/main/java/com/openelements/hiero/base/TokenClient.java

Lines changed: 380 additions & 0 deletions
Large diffs are not rendered by default.

hiero-enterprise-base/src/main/java/com/openelements/hiero/base/config/ConsensusNode.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44
import java.util.Objects;
55
import org.jspecify.annotations.NonNull;
66

7+
/**
8+
* Configuration for a consensus node in a Hiero network.
9+
*
10+
* @param ip the IP address of the consensus node
11+
* @param port the port of the consensus node
12+
* @param account the account ID of the consensus node
13+
*/
714
public record ConsensusNode(@NonNull String ip, @NonNull String port, @NonNull String account) {
815

916
public ConsensusNode {
@@ -12,10 +19,20 @@ public record ConsensusNode(@NonNull String ip, @NonNull String port, @NonNull S
1219
Objects.requireNonNull(account, "account must not be null");
1320
}
1421

22+
/**
23+
* Get the address of the consensus node. The address is the IP address and port of the consensus node.
24+
*
25+
* @return the address
26+
*/
1527
public String getAddress() {
1628
return ip + ":" + port;
1729
}
1830

31+
/**
32+
* Get the account ID of the consensus node.
33+
*
34+
* @return the account ID
35+
*/
1936
public AccountId getAccountId() {
2037
return AccountId.fromString(account);
2138
}

hiero-enterprise-base/src/main/java/com/openelements/hiero/base/config/HieroConfig.java

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,58 @@
1414
import org.slf4j.Logger;
1515
import org.slf4j.LoggerFactory;
1616

17+
/**
18+
* Hiero configuration for one network connection.
19+
*/
1720
public interface HieroConfig {
1821

1922
final static Logger log = LoggerFactory.getLogger(HieroConfig.class);
2023

24+
/**
25+
* Returns the operator account for the network.
26+
*
27+
* @return the operator account
28+
*/
2129
@NonNull
2230
Account getOperatorAccount();
2331

32+
/**
33+
* Returns the network name.
34+
*
35+
* @return the network name
36+
*/
2437
@NonNull
2538
Optional<String> getNetworkName();
2639

40+
/**
41+
* Returns the mirror node addresses.
42+
*
43+
* @return the mirror node addresses
44+
*/
2745
@NonNull
28-
List<String> getMirrornodeAddresses();
46+
List<String> getMirrorNodeAddresses();
2947

48+
/**
49+
* Returns the consensus nodes.
50+
*
51+
* @return the consensus nodes
52+
*/
3053
@NonNull
3154
Set<ConsensusNode> getConsensusNodes();
3255

56+
/**
57+
* Returns the network.
58+
*
59+
* @return
60+
*/
3361
@NonNull
3462
HieroNetwork getNetwork();
3563

64+
/**
65+
* Creates a Hiero context. Calling this method multiple times will return a new instance each time.
66+
*
67+
* @return the Hiero context
68+
*/
3669
@NonNull
3770
default HieroContext createHieroContext() {
3871
final Account operatorAccount = getOperatorAccount();
@@ -50,6 +83,11 @@ default HieroContext createHieroContext() {
5083
};
5184
}
5285

86+
/**
87+
* Creates a new client for the network. Calling this method multiple times will return a new instance each time.
88+
*
89+
* @return the client
90+
*/
5391
@NonNull
5492
default Client createClient() {
5593
final HieroNetwork hieroNetwork = getNetwork();
@@ -70,7 +108,7 @@ default Client createClient() {
70108
final Map<String, AccountId> nodes = getConsensusNodes().stream()
71109
.collect(Collectors.toMap(n -> n.getAddress(), n -> n.getAccountId()));
72110
final Client client = Client.forNetwork(nodes);
73-
client.setMirrorNetwork(getMirrornodeAddresses());
111+
client.setMirrorNetwork(getMirrorNodeAddresses());
74112
client.setOperator(getOperatorAccount().accountId(), getOperatorAccount().privateKey());
75113
return client;
76114
} catch (Exception e) {
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package com.openelements.hiero.base.implementation;
2+
3+
import com.hedera.hashgraph.sdk.AccountId;
4+
import com.hedera.hashgraph.sdk.TokenId;
5+
import com.openelements.hiero.base.HieroException;
6+
import com.openelements.hiero.base.data.AccountInfo;
7+
import com.openelements.hiero.base.data.ExchangeRates;
8+
import com.openelements.hiero.base.data.NetworkFee;
9+
import com.openelements.hiero.base.data.NetworkStake;
10+
import com.openelements.hiero.base.data.NetworkSupplies;
11+
import com.openelements.hiero.base.data.Nft;
12+
import com.openelements.hiero.base.mirrornode.MirrorNodeClient;
13+
import java.util.List;
14+
import java.util.Objects;
15+
import java.util.Optional;
16+
import org.jspecify.annotations.NonNull;
17+
18+
public abstract class AbstractMirrorNodeClient<JSON> implements MirrorNodeClient {
19+
20+
@NonNull
21+
protected abstract MirrorNodeRestClient<JSON> getRestClient();
22+
23+
@NonNull
24+
protected abstract MirrorNodeJsonConverter<JSON> getJsonConverter();
25+
26+
@Override
27+
public @NonNull
28+
final Optional<Nft> queryNftsByTokenIdAndSerial(@NonNull final TokenId tokenId, final long serialNumber)
29+
throws HieroException {
30+
final JSON json = getRestClient().queryNftsByTokenIdAndSerial(tokenId, serialNumber);
31+
return getJsonConverter().toNft(json);
32+
}
33+
34+
@Override
35+
public @NonNull
36+
final Optional<AccountInfo> queryAccount(@NonNull final AccountId accountId) throws HieroException {
37+
Objects.requireNonNull(accountId, "accountId must not be null");
38+
final JSON json = getRestClient().queryAccount(accountId);
39+
return getJsonConverter().toAccountInfo(json);
40+
}
41+
42+
@Override
43+
public @NonNull
44+
final Optional<ExchangeRates> queryExchangeRates() throws HieroException {
45+
final JSON json = getRestClient().queryExchangeRates();
46+
return getJsonConverter().toExchangeRates(json);
47+
}
48+
49+
@Override
50+
public @NonNull
51+
final List<NetworkFee> queryNetworkFees() throws HieroException {
52+
final JSON json = getRestClient().queryNetworkFees();
53+
return getJsonConverter().toNetworkFees(json);
54+
}
55+
56+
@Override
57+
public @NonNull
58+
final Optional<NetworkStake> queryNetworkStake() throws HieroException {
59+
final JSON json = getRestClient().queryNetworkStake();
60+
return getJsonConverter().toNetworkStake(json);
61+
}
62+
63+
@Override
64+
public @NonNull
65+
final Optional<NetworkSupplies> queryNetworkSupplies() throws HieroException {
66+
final JSON json = getRestClient().queryNetworkSupplies();
67+
return getJsonConverter().toNetworkSupplies(json);
68+
}
69+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.openelements.hiero.base.implementation;
2+
3+
import com.openelements.hiero.base.data.AccountInfo;
4+
import com.openelements.hiero.base.data.ExchangeRates;
5+
import com.openelements.hiero.base.data.NetworkFee;
6+
import com.openelements.hiero.base.data.NetworkStake;
7+
import com.openelements.hiero.base.data.NetworkSupplies;
8+
import com.openelements.hiero.base.data.Nft;
9+
import com.openelements.hiero.base.data.TransactionInfo;
10+
import java.util.List;
11+
import java.util.Optional;
12+
import org.jspecify.annotations.NonNull;
13+
14+
public interface MirrorNodeJsonConverter<JSON> {
15+
16+
@NonNull
17+
Optional<Nft> toNft(@NonNull JSON json);
18+
19+
@NonNull
20+
Optional<NetworkSupplies> toNetworkSupplies(@NonNull JSON json);
21+
22+
@NonNull
23+
Optional<NetworkStake> toNetworkStake(@NonNull JSON json);
24+
25+
@NonNull
26+
Optional<ExchangeRates> toExchangeRates(@NonNull JSON json);
27+
28+
@NonNull
29+
Optional<AccountInfo> toAccountInfo(@NonNull JSON jsonNode);
30+
31+
@NonNull
32+
List<NetworkFee> toNetworkFees(@NonNull JSON json);
33+
34+
@NonNull
35+
List<TransactionInfo> toTransactionInfos(@NonNull JSON json);
36+
37+
List<Nft> toNfts(@NonNull JSON json);
38+
39+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.openelements.hiero.base.implementation;
2+
3+
import com.hedera.hashgraph.sdk.AccountId;
4+
import com.hedera.hashgraph.sdk.TokenId;
5+
import com.openelements.hiero.base.HieroException;
6+
import java.util.Objects;
7+
import org.jspecify.annotations.NonNull;
8+
9+
public interface MirrorNodeRestClient<JSON> {
10+
11+
@NonNull
12+
default JSON queryNftsByTokenIdAndSerial(@NonNull final TokenId tokenId, @NonNull final long serialNumber)
13+
throws HieroException {
14+
Objects.requireNonNull(tokenId, "tokenId must not be null");
15+
if (serialNumber <= 0) {
16+
throw new IllegalArgumentException("serialNumber must be positive");
17+
}
18+
return doGetCall("/api/v1/tokens/" + tokenId + "/nfts/" + serialNumber);
19+
}
20+
21+
@NonNull
22+
default JSON queryTransaction(@NonNull final String transactionId) throws HieroException {
23+
Objects.requireNonNull(transactionId, "transactionId must not be null");
24+
return doGetCall("/api/v1/transactions/" + transactionId);
25+
}
26+
27+
@NonNull
28+
default JSON queryAccount(@NonNull AccountId accountId) throws HieroException {
29+
Objects.requireNonNull(accountId, "accountId must not be null");
30+
return doGetCall("/api/v1/accounts/" + accountId);
31+
}
32+
33+
@NonNull
34+
default JSON queryExchangeRates() throws HieroException {
35+
return doGetCall("/api/v1/network/exchangerate");
36+
}
37+
38+
@NonNull
39+
default JSON queryNetworkFees() throws HieroException {
40+
return doGetCall("/api/v1/network/fees");
41+
}
42+
43+
@NonNull
44+
default JSON queryNetworkStake() throws HieroException {
45+
return doGetCall("/api/v1/network/stake");
46+
}
47+
48+
@NonNull
49+
default JSON queryNetworkSupplies() throws HieroException {
50+
return doGetCall("/api/v1/network/supply");
51+
}
52+
53+
@NonNull
54+
JSON doGetCall(@NonNull String path) throws HieroException;
55+
}

hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/NftClientImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ public void transferNfts(@NonNull final TokenId tokenId, @NonNull final List<Lon
116116
@NonNull final PrivateKey fromAccountKey, @NonNull final AccountId toAccountId) throws HieroException {
117117
final TokenTransferRequest request = TokenTransferRequest.of(tokenId, serialNumber, fromAccountId, toAccountId,
118118
fromAccountKey);
119-
client.executeTransferTransactionForNft(request);
119+
client.executeTransferTransaction(request);
120120
}
121121

122122

hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/ProtocolLayerClientImpl.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ public TokenBurnResult executeBurnTokenTransaction(@NonNull final TokenBurnReque
430430
}
431431

432432
final TransactionReceipt receipt = executeTransactionAndWaitOnReceipt(transaction);
433-
return new TokenBurnResult(receipt.transactionId, receipt.status);
433+
return new TokenBurnResult(receipt.transactionId, receipt.status, receipt.totalSupply);
434434
} catch (final Exception e) {
435435
throw new HieroException("Failed to execute burn token transaction", e);
436436
}
@@ -452,22 +452,29 @@ public TokenMintResult executeMintTokenTransaction(@NonNull final TokenMintReque
452452
}
453453
sign(transaction, request.supplyKey());
454454
final TransactionReceipt receipt = executeTransactionAndWaitOnReceipt(transaction);
455-
return new TokenMintResult(receipt.transactionId, receipt.status, receipt.serials);
455+
return new TokenMintResult(receipt.transactionId, receipt.status, receipt.serials, receipt.totalSupply);
456456
} catch (final Exception e) {
457457
throw new HieroException("Failed to execute mint token transaction", e);
458458
}
459459
}
460460

461-
public TokenTransferResult executeTransferTransactionForNft(@NonNull final TokenTransferRequest request)
461+
public TokenTransferResult executeTransferTransaction(@NonNull final TokenTransferRequest request)
462462
throws HieroException {
463463
Objects.requireNonNull(request, "request must not be null");
464464
try {
465465
final TransferTransaction transaction = new TransferTransaction()
466466
.setMaxTransactionFee(request.maxTransactionFee())
467467
.setTransactionValidDuration(request.transactionValidDuration());
468-
request.serials().forEach(
469-
serial -> transaction.addNftTransfer(new NftId(request.tokenId(), serial), request.sender(),
470-
request.receiver()));
468+
if (!request.serials().isEmpty()) {
469+
request.serials().forEach(
470+
serial -> transaction.addNftTransfer(new NftId(request.tokenId(), serial), request.sender(),
471+
request.receiver()));
472+
} else if (request.amount() != null) {
473+
transaction.addTokenTransfer(request.tokenId(), request.sender(), request.amount() * -1);
474+
transaction.addTokenTransfer(request.tokenId(), request.receiver(), request.amount());
475+
} else {
476+
throw new IllegalArgumentException("either amount or serial must be provided");
477+
}
471478
sign(transaction, request.senderKey());
472479
final TransactionReceipt receipt = executeTransactionAndWaitOnReceipt(transaction);
473480
return new TokenTransferResult(receipt.transactionId, receipt.status);

0 commit comments

Comments
 (0)