Skip to content
Open
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
55 changes: 55 additions & 0 deletions hiero-enterprise-cli-sample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Hiero Enterprise CLI Sample

A command-line interface for interacting with the Hiero network using [picocli](https://picocli.info) and `hiero-enterprise-base`.

## Prerequisites

- Java 21+
- Maven 3.8+
- A Hedera testnet account ([get one free](https://portal.hedera.com))

## Build

```bash
mvn clean package -pl hiero-enterprise-cli-sample -am -DskipTests
```

## Usage

```bash
java -jar hiero-enterprise-cli-sample/target/hiero-enterprise-cli-sample-*.jar [command]
```

### Create Account
```bash
java -jar target/*.jar create-account \
--account-id 0.0.123 \
--private-key <YOUR_PRIVATE_KEY>
```

### Create Topic
```bash
java -jar target/*.jar create-topic \
--account-id 0.0.123 \
--private-key <YOUR_PRIVATE_KEY> \
--memo "My first topic"
```

### Send Message to Topic
```bash
java -jar target/*.jar send-message \
--account-id 0.0.123 \
--private-key <YOUR_PRIVATE_KEY> \
--topic-id 0.0.456 \
--message "Hello Hiero!"
```

## Configuration

Set the following environment variables or pass them as CLI options:

| Variable | Description |
|----------|-------------|
| `--account-id` | Your Hedera operator account ID (e.g. `0.0.123`) |
| `--private-key` | Your Hedera operator private key |
| `--network` | Network name (default: `hedera-testnet`) |
61 changes: 61 additions & 0 deletions hiero-enterprise-cli-sample/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.hiero</groupId>
<artifactId>hiero-enterprise</artifactId>
<version>0.20.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

<artifactId>hiero-enterprise-cli-sample</artifactId>
<name>Hiero Enterprise CLI Sample</name>
<description>Sample for Hiero via CLI using picocli</description>
<url>https://github.com/hiero-ledger/hiero-enterprise-java</url>

<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>hiero-enterprise-base</artifactId>
</dependency>
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>
<version>4.7.6</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-okhttp</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-inprocess</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>org.hiero.base.sample.HieroCli</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.hiero.base.sample;

import com.hedera.hashgraph.sdk.AccountId;
import com.hedera.hashgraph.sdk.Client;
import com.hedera.hashgraph.sdk.PrivateKey;
import com.hedera.hashgraph.sdk.PublicKey;
import java.util.HashMap;
import java.util.Map;
import org.hiero.base.HieroContext;
import org.hiero.base.config.NetworkSettings;
import org.hiero.base.data.Account;
import org.jspecify.annotations.NonNull;

public class CliHieroContext implements HieroContext {

private final Account operationalAccount;
private final Client client;

public CliHieroContext(final String accountIdStr, final String privateKeyStr, final String network) {
final AccountId accountId = AccountId.fromString(accountIdStr);
final PrivateKey privateKey = PrivateKey.fromString(privateKeyStr);
final PublicKey publicKey = privateKey.getPublicKey();
operationalAccount = new Account(accountId, publicKey, privateKey);

final NetworkSettings networkSettings =
NetworkSettings.forIdentifier(network)
.orElseThrow(
() -> new IllegalStateException("Unknown network: " + network));

final Map<String, AccountId> nodes = new HashMap<>();
networkSettings
.getConsensusNodes()
.forEach(n -> nodes.put(n.getAddress(), n.getAccountId()));

client = Client.forNetwork(nodes);
if (!networkSettings.getMirrorNodeAddresses().isEmpty()) {
try {
client.setMirrorNetwork(networkSettings.getMirrorNodeAddresses().stream().toList());
} catch (InterruptedException e) {
throw new RuntimeException("Error configuring Mirror Node", e);
}
}
client.setOperator(accountId, privateKey);
}

@Override
public @NonNull Account getOperatorAccount() {
return operationalAccount;
}

@Override
public @NonNull Client getClient() {
return client;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.hiero.base.sample;

import org.hiero.base.AccountClient;
import org.hiero.base.implementation.AccountClientImpl;
import org.hiero.base.implementation.ProtocolLayerClientImpl;
import org.hiero.base.protocol.ProtocolLayerClient;
import org.hiero.base.data.Account;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;

@Command(name = "create-account", description = "Create a new Hiero account", mixinStandardHelpOptions = true)
public class CreateAccountCommand implements Runnable {

@Option(names = {"-n", "--network"}, description = "Hiero network", defaultValue = "hedera-testnet")
private String network;

@Option(names = {"-a", "--account-id"}, description = "Operator account ID", required = true)
private String accountId;

@Option(names = {"-k", "--private-key"}, description = "Operator private key", required = true)
private String privateKey;

@Override
public void run() {
try {
final CliHieroContext context = new CliHieroContext(accountId, privateKey, network);
final ProtocolLayerClient protocolClient = new ProtocolLayerClientImpl(context);
final AccountClient accountClient = new AccountClientImpl(protocolClient);
System.out.println("Creating account on " + network + "...");
final Account account = accountClient.createAccount();
System.out.println("Account created: " + account.accountId());
} catch (final Exception e) {
System.err.println("Error: " + e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.hiero.base.sample;

import com.hedera.hashgraph.sdk.TopicId;
import org.hiero.base.TopicClient;
import org.hiero.base.implementation.ProtocolLayerClientImpl;
import org.hiero.base.implementation.TopicClientImpl;
import org.hiero.base.protocol.ProtocolLayerClient;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;

@Command(name = "create-topic", description = "Create a new Hiero topic", mixinStandardHelpOptions = true)
public class CreateTopicCommand implements Runnable {

@Option(names = {"-n", "--network"}, description = "Hiero network", defaultValue = "hedera-testnet")
private String network;

@Option(names = {"-a", "--account-id"}, description = "Operator account ID", required = true)
private String accountId;

@Option(names = {"-k", "--private-key"}, description = "Operator private key", required = true)
private String privateKey;

@Option(names = {"-m", "--memo"}, description = "Topic memo", defaultValue = "Created via Hiero CLI")
private String memo;

@Override
public void run() {
try {
final CliHieroContext context = new CliHieroContext(accountId, privateKey, network);
final ProtocolLayerClient protocolClient = new ProtocolLayerClientImpl(context);
final TopicClient topicClient = new TopicClientImpl(protocolClient, context.getOperatorAccount());
System.out.println("Creating topic on " + network + "...");
final TopicId topicId = topicClient.createTopic(memo);
System.out.println("Topic created: " + topicId);
} catch (final Exception e) {
System.err.println("Error: " + e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.hiero.base.sample;

import picocli.CommandLine;
import picocli.CommandLine.Command;

@Command(
name = "hiero",
mixinStandardHelpOptions = true,
version = "1.0",
description = "Hiero Enterprise CLI - interact with the Hiero network",
subcommands = {
CreateAccountCommand.class,
CreateTopicCommand.class,
SendMessageCommand.class
})
public class HieroCli implements Runnable {

public static void main(final String[] args) {
final int exitCode = new CommandLine(new HieroCli()).execute(args);
System.exit(exitCode);
}

@Override
public void run() {
CommandLine.usage(this, System.out);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.hiero.base.sample;

import org.hiero.base.TopicClient;
import org.hiero.base.implementation.ProtocolLayerClientImpl;
import org.hiero.base.implementation.TopicClientImpl;
import org.hiero.base.protocol.ProtocolLayerClient;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;

@Command(name = "send-message", description = "Send a message to a Hiero topic", mixinStandardHelpOptions = true)
public class SendMessageCommand implements Runnable {

@Option(names = {"-n", "--network"}, description = "Hiero network", defaultValue = "hedera-testnet")
private String network;

@Option(names = {"-a", "--account-id"}, description = "Operator account ID", required = true)
private String accountId;

@Option(names = {"-k", "--private-key"}, description = "Operator private key", required = true)
private String privateKey;

@Option(names = {"-t", "--topic-id"}, description = "Topic ID", required = true)
private String topicId;

@Option(names = {"-msg", "--message"}, description = "Message content", required = true)
private String message;

@Override
public void run() {
try {
final CliHieroContext context = new CliHieroContext(accountId, privateKey, network);
final ProtocolLayerClient protocolClient = new ProtocolLayerClientImpl(context);
final TopicClient topicClient = new TopicClientImpl(protocolClient, context.getOperatorAccount());
System.out.println("Sending message to topic " + topicId + "...");
topicClient.submitMessage(topicId, message);
System.out.println("Message sent successfully!");
} catch (final Exception e) {
System.err.println("Error: " + e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.hiero.microprofile;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Instance;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Inject;
import org.eclipse.microprofile.config.inject.ConfigProperties;
Expand All @@ -9,6 +10,7 @@
import org.hiero.base.FungibleTokenClient;
import org.hiero.base.HieroContext;
import org.hiero.base.HookClient;
import org.hiero.base.interceptors.ReceiveRecordInterceptor;
import org.hiero.base.NftClient;
import org.hiero.base.SmartContractClient;
import org.hiero.base.TopicClient;
Expand Down Expand Up @@ -70,8 +72,14 @@ HieroContext createHieroContext(@NonNull final HieroConfig hieroConfig) {
@NonNull
@Produces
@ApplicationScoped
ProtocolLayerClient createProtocolLayerClient(@NonNull final HieroContext hieroContext) {
return new ProtocolLayerClientImpl(hieroContext);
ProtocolLayerClient createProtocolLayerClient(
@NonNull final HieroContext hieroContext,
@NonNull final Instance<ReceiveRecordInterceptor> interceptor) {
final ProtocolLayerClientImpl client = new ProtocolLayerClientImpl(hieroContext);
if (!interceptor.isUnsatisfied()) {
client.setRecordInterceptor(interceptor.get());
}
return client;
}

@NonNull
Expand Down
Loading