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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,13 @@ For most of the part, the sample application is the same as the Spring Boot samp

A sample project can be found at https://github.com/hendrikebbers/quarkus-hiero-sample

## Helidon samples

This repository includes Helidon-based samples:

- `hiero-enterprise-helidon-me-sample` (MicroProfile-based)
- `hiero-enterprise-helidon-se-sample` (base module + Helidon SE routing)

## Managed services

The module provides a set of managed services that can be used to interact with a Hiero network.
Expand Down
3 changes: 3 additions & 0 deletions hiero-enterprise-helidon-me-sample/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
HEDERA_ACCOUNT_ID=0.0.123456
HEDERA_PRIVATE_KEY=302e020100300506032b657004220420YOUR_PRIVATE_KEY
HEDERA_NETWORK=hedera-testnet
26 changes: 26 additions & 0 deletions hiero-enterprise-helidon-me-sample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Hiero Enterprise Helidon ME Sample

This sample demonstrates concrete operations with `hiero-enterprise-microprofile` on Helidon MicroProfile:

- Create a topic
- Transfer an existing token
- Call a function on an existing smart contract

## Configuration

Set operator credentials via environment variables:

- `HEDERA_ACCOUNT_ID`
- `HEDERA_PRIVATE_KEY`
- `HEDERA_NETWORK` (default: `hedera-testnet`)

See `.env.example` in this module.

## Endpoints

- `POST /topics`
- body: `{"memo":"sample-topic"}`
- `POST /tokens/transfer`
- body: `{"tokenId":"0.0.x","toAccountId":"0.0.y","amount":1}`
- `POST /contracts/call`
- body: `{"contractId":"0.0.z","functionName":"getValue"}`
49 changes: 49 additions & 0 deletions hiero-enterprise-helidon-me-sample/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?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 https://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-helidon-me-sample</artifactId>

<name>Hiero Enterprise Helidon ME Sample</name>
<description>Sample for Hiero in Helidon MicroProfile</description>
<url>https://github.com/hiero-ledger/hiero-enterprise-java</url>

<dependencies>
<dependency>
<groupId>io.helidon.microprofile.server</groupId>
<artifactId>helidon-microprofile-server</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>hiero-enterprise-microprofile</artifactId>
</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-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,90 @@
package org.hiero.helidon.me.sample;

import com.hedera.hashgraph.sdk.TokenId;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.hiero.base.FungibleTokenClient;
import org.hiero.base.SmartContractClient;
import org.hiero.base.TopicClient;
import org.hiero.base.data.ContractCallResult;

@Path("/")
public class HieroEndpoint {

private final TopicClient topicClient;
private final FungibleTokenClient tokenClient;
private final SmartContractClient smartContractClient;

@Inject
public HieroEndpoint(
final TopicClient topicClient,
final FungibleTokenClient tokenClient,
final SmartContractClient smartContractClient) {
this.topicClient = topicClient;
this.tokenClient = tokenClient;
this.smartContractClient = smartContractClient;
}

@GET
@Produces(MediaType.TEXT_PLAIN)
public String health() {
return "Hiero Helidon ME sample is running";
}

@POST
@Path("/topics")
@Produces(MediaType.APPLICATION_JSON)
public Response createTopic(final TopicCreateRequest request) {
try {
final String memo =
request.memo() == null || request.memo().isBlank() ? "sample-topic" : request.memo();
return Response.ok(new TopicCreateResponse(topicClient.createTopic(memo).toString())).build();
} catch (final Exception e) {
return Response.serverError().entity(e.getMessage()).build();
}
}

@POST
@Path("/tokens/transfer")
@Produces(MediaType.APPLICATION_JSON)
public Response transferToken(final TokenTransferRequest request) {
try {
tokenClient.transferToken(
TokenId.fromString(request.tokenId()), request.toAccountId(), request.amount());
return Response.ok(new TokenTransferResponse("Token transfer submitted")).build();
} catch (final Exception e) {
return Response.serverError().entity(e.getMessage()).build();
}
}

@POST
@Path("/contracts/call")
@Produces(MediaType.APPLICATION_JSON)
public Response callContract(final ContractCallRequest request) {
try {
final ContractCallResult result =
smartContractClient.callContractFunction(request.contractId(), request.functionName());
return Response.ok(new ContractCallResponse(result.gasUsed(), result.cost().toString()))
.build();
} catch (final Exception e) {
return Response.serverError().entity(e.getMessage()).build();
}
}

public record TopicCreateRequest(String memo) {}

public record TopicCreateResponse(String topicId) {}

public record TokenTransferRequest(String tokenId, String toAccountId, long amount) {}

public record TokenTransferResponse(String status) {}

public record ContractCallRequest(String contractId, String functionName) {}

public record ContractCallResponse(long gasUsed, String cost) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
hiero.accountId=${HEDERA_ACCOUNT_ID:0.0.123}
hiero.privateKey=${HEDERA_PRIVATE_KEY:302e020100300506032b657004220420d39413bc03867c293cf5975569429cf29759c878950c44186419e4871e9a2638}
hiero.network.name=${HEDERA_NETWORK:hedera-testnet}
4 changes: 4 additions & 0 deletions hiero-enterprise-helidon-se-sample/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
HEDERA_ACCOUNT_ID=0.0.123456
HEDERA_PRIVATE_KEY=302e020100300506032b657004220420YOUR_PRIVATE_KEY
HEDERA_NETWORK=hedera-testnet
SERVER_PORT=8082
30 changes: 30 additions & 0 deletions hiero-enterprise-helidon-se-sample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Hiero Enterprise Helidon SE Sample

This sample demonstrates concrete operations using `hiero-enterprise-base` with Helidon SE routing:

- Create a topic
- Transfer an existing token
- Call a function on an existing smart contract

## Configuration

Set operator credentials via environment variables:

- `HEDERA_ACCOUNT_ID`
- `HEDERA_PRIVATE_KEY`
- `HEDERA_NETWORK` (default: `hedera-testnet`)
- `SERVER_PORT` (optional, default `8082`)

See `.env.example` in this module.

## Run

```bash
../mvnw -pl hiero-enterprise-helidon-se-sample -am exec:java -Dexec.mainClass=org.hiero.helidon.se.sample.HelidonSeSampleMain
```

## Endpoints

- `POST /topics?memo=sample-topic`
- `POST /tokens/transfer?tokenId=0.0.x&toAccountId=0.0.y&amount=1`
- `POST /contracts/call?contractId=0.0.z&functionName=getValue`
50 changes: 50 additions & 0 deletions hiero-enterprise-helidon-se-sample/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?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 https://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-helidon-se-sample</artifactId>

<name>Hiero Enterprise Helidon SE Sample</name>
<description>Sample for Hiero in Helidon SE (base module)</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>io.helidon.webserver</groupId>
<artifactId>helidon-webserver</artifactId>
<version>${helidon.version}</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-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,85 @@
package org.hiero.helidon.se.sample;

import com.hedera.hashgraph.sdk.TokenId;
import io.helidon.webserver.WebServer;
import io.helidon.webserver.http.ServerRequest;
import io.helidon.webserver.http.ServerResponse;
import java.util.Objects;
import org.hiero.base.data.ContractCallResult;

public final class HelidonSeSampleMain {

private HelidonSeSampleMain() {}

public static void main(String[] args) {
final HieroClients clients = HieroClients.fromEnv();
final int port = Integer.parseInt(System.getenv().getOrDefault("SERVER_PORT", "8082"));

final WebServer server =
WebServer.builder()
.port(port)
.routing(
builder ->
builder
.get("/", (req, res) -> res.send("Hiero Helidon SE sample is running"))
.post("/topics", (req, res) -> createTopic(req, res, clients))
.post("/tokens/transfer", (req, res) -> transferToken(req, res, clients))
.post("/contracts/call", (req, res) -> callContract(req, res, clients)))
.build();

server.start();
}

private static void createTopic(
final ServerRequest request, final ServerResponse response, final HieroClients clients) {
try {
final String memo =
valueOrDefault(request.query().first("memo").orElse(null), "sample-topic");
final String topicId = clients.topicClient().createTopic(memo).toString();
response.send("{\"topicId\":\"" + topicId + "\"}");
} catch (final Exception e) {
response.status(500).send(e.getMessage());
}
}

private static void transferToken(
final ServerRequest request, final ServerResponse response, final HieroClients clients) {
try {
final String tokenId = requiredQuery(request, "tokenId");
final String toAccountId = requiredQuery(request, "toAccountId");
final long amount = Long.parseLong(requiredQuery(request, "amount"));
clients.tokenClient().transferToken(TokenId.fromString(tokenId), toAccountId, amount);
response.send("{\"status\":\"Token transfer submitted\"}");
} catch (final Exception e) {
response.status(500).send(e.getMessage());
}
}

private static void callContract(
final ServerRequest request, final ServerResponse response, final HieroClients clients) {
try {
final String contractId = requiredQuery(request, "contractId");
final String functionName = requiredQuery(request, "functionName");
final ContractCallResult result =
clients.smartContractClient().callContractFunction(contractId, functionName);
response.send("{\"gasUsed\":" + result.gasUsed() + ",\"cost\":\"" + result.cost() + "\"}");
} catch (final Exception e) {
response.status(500).send(e.getMessage());
}
}

private static String requiredQuery(final ServerRequest request, final String key) {
return request
.query()
.first(key)
.filter(value -> !value.isBlank())
.orElseThrow(() -> new IllegalArgumentException("Missing query parameter: " + key));
}

private static String valueOrDefault(final String value, final String fallback) {
if (Objects.isNull(value) || value.isBlank()) {
return fallback;
}
return value;
}
}
Loading