Skip to content

Commit 9547821

Browse files
Add tutorial to docs
1 parent 8e47def commit 9547821

File tree

14 files changed

+835
-676
lines changed

14 files changed

+835
-676
lines changed

docs/docs/tutorial.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Getting Started
2+
3+
This tutorial walks through the steps to initialize and interact with a Hydra Head using the `kuber-hydra` relay server and client libraries. We’ll demonstrate the process using two Hydra nodes—**Alice** and **Bob**—on the Cardano testnet.
4+
5+
## **1. Hydra Node Setup**
6+
7+
To set up a Hydra Head on the testnet, follow the official Hydra protocol tutorial:
8+
👉 [Hydra Head Protocol Documentation](https://hydra.family/head-protocol/docs/tutorial)
9+
10+
In our example setup:
11+
12+
- **Alice's Hydra Node** runs on `172.16.238.10:4001`
13+
- **Bob's Hydra Node** runs on `172.16.238.20:4002`
14+
15+
## **2. Kuber-Hydra Relay Server**
16+
17+
### **Repository**
18+
19+
- GitHub: [kuber](https://github.com/dquadrant/kuber)
20+
21+
### **Configuration**
22+
23+
Create a `.env` file with the following variables (example for Alice):
24+
25+
```env
26+
HYDRA_IP=172.16.238.10
27+
HYDRA_PORT=4001
28+
SERVER_PORT=8081
29+
CARDANO_NODE_SOCKET_PATH=/path/to/cardano-node/preview/node.socket
30+
NETWORK=2
31+
```
32+
33+
> The Kuber-Hydra relay API will be accessible at `http://localhost:8081`.
34+
35+
## **3. Kuber Client**
36+
37+
Example repository: [kuber-client-example](https://github.com/cardanoapi/kuber-client-example)
38+
39+
### Hydra Service Initialization
40+
41+
Assuming that the hydra node is running and kuber-hdra server is started on localhost:8081, we can pass the host url to this class constructor to create the service:
42+
43+
```ts
44+
import { KuberHydraService } from "kuber-client/service/kuberHydraService";
45+
46+
const hydraService = new KuberHydraService("http://localhost:8081");
47+
```

docs/docs/tutorials/build_tx.md

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
2+
# Building Transactions in Hydra
3+
4+
**1. Create a Sample Transaction JSON**
5+
6+
```ts
7+
const createSampleOutputTx = (
8+
selectionAddress: string,
9+
outputAddress: string
10+
) => ({
11+
selections: [selectionAddress],
12+
outputs: [
13+
{
14+
address: outputAddress,
15+
value: 2_000_000,
16+
datum: { constructor: 1, fields: [] },
17+
},
18+
{
19+
address: outputAddress,
20+
value: 2_000_000,
21+
datum: { constructor: 2, fields: [] },
22+
},
23+
],
24+
});
25+
```
26+
27+
**2. Generate Address from Key**
28+
29+
```ts
30+
import { setup } from "libcardano/lib/cardano/crypto";
31+
import { Ed25519Key } from "libcardano/cardano/primitives/keys";
32+
import { ShelleyWallet } from "libcardano/cardano/primitives/address";
33+
34+
await setup(); //this is necessary to run Ed25519Key funcitons
35+
36+
const testWalletSigningKey = await Ed25519Key.fromCardanoCliFile(
37+
path.join("src", "example.sk")
38+
);
39+
const testWalletAddress = new ShelleyWallet(testWalletSigningKey).addressBech32(
40+
0
41+
); // 0 = testnet
42+
```
43+
44+
**3. Create a Hydra Wallet**
45+
46+
```ts
47+
async function createHydraWallet(
48+
service: KuberHydraService,
49+
ed25519Key: Ed25519Key,
50+
network: 0 | 1
51+
): Promise<HydraWallet> {
52+
try {
53+
const shelleyWallet = new ShelleyWallet(ed25519Key);
54+
const hydraWallet = new HydraWallet(service, shelleyWallet, network);
55+
return hydraWallet;
56+
} catch (error: any) {
57+
return respondWithError(error);
58+
}
59+
}
60+
61+
const myWallet = await createHydraWallet(hydraService, testWalletSigningKey, 0);
62+
```
63+
64+
> The `HydraWallet` supports CIP-30 functions such as `getUTxO`, `signData`, `signTx`, `submitTx`, etc.
65+
66+
**4. Build the Transaction**
67+
68+
```ts
69+
// For the sake of simplicity, we can fund our own address since the protocol fees are `0`.
70+
const txBody = createSampleOutputTx(testWalletAddress, testWalletAddress);
71+
const buildTxResponse = await hydraService.buildTx(txBody, false); // false = don't submit as we have not signed the transaction yet
72+
```
73+
74+
The response from this function will provide the transaction details in the following format:
75+
76+
```json
77+
{
78+
"cborHex": "84a400d90102818258204051dd270c1a51da8645b6c91d23053273547f1f853929fbec5519527e18266d0d0183a300581d60182aeee511419facd4bf4eab7538187288a55a633f579be0cf36897b011a001e8480028201d81843d87b80a300581d60182aeee511419facd4bf4eab7538187288a55a633f579be0cf36897b011a001e8480028201d81843d87b8082581d60182aeee511419facd4bf4eab7538187288a55a633f579be0cf36897b1a00f4240002000ed9010281581c182aeee511419facd4bf4eab7538187288a55a633f579be0cf36897ba0f5f6",
79+
"description": "Ledger Cddl Format",
80+
"hash": "ff6de11af9d0998a85c3eb5333f4afd173a904164630fc0717be8ee819900d4f",
81+
"type": "Unwitnessed Tx ConwayEra"
82+
}
83+
```
84+
85+
**5. Sign and Submit**
86+
87+
```ts
88+
const txCborHex = buildTxResponse.cborHex;
89+
const signature = await myWallet.signTx(txCborHex);
90+
```
91+
92+
The wallet's signTx function returns a witness set in cbor hex format. To add this witness set to the transaction, we need to merge the witnesses.
93+
94+
```ts
95+
const signedTxHex = txWithMergedSignature(txCborHex, signature);
96+
```
97+
98+
Now, we have the signed transaction that we can submit to the hydra head.
99+
100+
```ts
101+
const submissionResult = await myWallet.submitTx(signedTxHex);
102+
console.log("Tx Submitted: ", submissionResult);
103+
```
104+
105+
The response from the wallet is the signed transction cbor hex upon successful submission.

docs/docs/tutorials/close_head.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Closing the Hydra Head
2+
3+
```ts
4+
const closeResponse = await hydraService.close(true);
5+
console.log(closeResponse);
6+
```
7+
8+
**Example Response:**
9+
10+
```json
11+
{
12+
"contestationDeadline": "2025-05-30T08:11:34Z",
13+
"headId": "84673dfc0cfd3cf404251fa730fbbfef8d8229b9e2f283e59bca2236",
14+
"seq": 17,
15+
"snapshotNumber": 1,
16+
"tag": "HeadIsClosed",
17+
"timestamp": "2025-05-30T08:07:54.34265662Z"
18+
}
19+
```
20+
21+
> 🛠 If you encounter errors on closing, refer to: [Hydra Issue #1039](https://github.com/cardano-scaling/hydra/issues/1039)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
# Committing UTxOs
3+
4+
Once the Hydra head is initialized, both participating parties must submit their commitments. To do this, define a commit object specifying the UTxOs to be included. If a signing key is provided, the Kuber-Hydra server will generate a signed transaction that can be submitted to the blockchain. The `submit` parameter determines the desired behavior—set it to `true` to submit the transaction immediately, or `false` to receive an unsigned transaction for manual submission later.
5+
6+
```ts
7+
const commitment: Commit = {
8+
utxos: [
9+
"4051dd270c1a51da8645b6c91d23053273547f1f853929fbec5519527e18266d#13",
10+
],
11+
signKey: {
12+
type: "PaymentSigningKeyShelley_ed25519",
13+
description: "Payment Signing Key",
14+
cborHex:
15+
"5820def9b27d28b25a28156e42a57e1a81d5412517ba2f73784bb230c676dd0d9d65",
16+
},
17+
};
18+
const commitResponse = await hydraService.commit(commitment, true);
19+
console.log(commitResponse);
20+
```
21+
22+
On success, you will receive a `Witnessed Tx` in CBOR format.
23+
24+
```json
25+
{
26+
"cborHex": "84a800d90102838258204051dd270c1a51da8645b6c91d23053273547f1f853929fbec5519527e18266d0d8258207a169ef2824514edc468e553a816e52dee9516a071e9ec9bdfeb547a2f30c6a4018258207a169ef2824514edc468e553a816e52dee9516a071e9ec9bdfeb547a2f30c6a4030dd90102818258207a169ef2824514edc468e553a816e52dee9516a071e9ec9bdfeb547a2f30c6a40312d90102818258200fd2468a66a0b1cb944cff9512ecfa25cdd2799cb48b07210c449a5ecace267d000182a300581d702043a9f1a685bcf491413a5f139ee42e335157c8c6bc8d9e4018669d01821a0144e7c8a1581c84673dfc0cfd3cf404251fa730fbbfef8d8229b9e2f283e59bca2236a1581ce696fc821063f9b7311bb350539e67c8fad1bd571605e75b5a353eab01028201d81858b3d8799f5820232844b0ebd1f13b62b19bc9ce0a423a84e3d2cc5efa2ac226a96255420d71379fd8799fd8799fd8799f58204051dd270c1a51da8645b6c91d23053273547f1f853929fbec5519527e18266dff0dff583cd8799fd8799fd8799f581c182aeee511419facd4bf4eab7538187288a55a633f579be0cf36897bffd87a80ffa140a1401a01312d00d87980d87a80ffffff581c84673dfc0cfd3cf404251fa730fbbfef8d8229b9e2f283e59bca2236ff82581d60e696fc821063f9b7311bb350539e67c8fad1bd571605e75b5a353eab1a055dc873021a001b55890ed9010281581ce696fc821063f9b7311bb350539e67c8fad1bd571605e75b5a353eab0b58202331de968aa627625c343ae7800e7388d3508b60fb75c545dff8077985b527a707582012e5af821e4510d6651e57185b4dae19e8bd72bf90a8c1e6cd053606cbc46514a200d9010282825820cc13514aae23bd9da11a9032cf7253a1e8f84bc5d077ae973f40f097fdd51f275840853de227e8ddb7aabb026fb1d8cbf8aaab4c2b29eccd975c870bfb64dd4a5a807b8c3fb415b1a6b8a4515815cdb156f3a8a63a404efac5b60d60c2c77787a70d825820d5888ede6fb8cc020ade5147053a9db6d1e25513ed795715decc26f2d22b77b45840a257d2df3741a7b2268e0ef4d48247c17147485ee1aebde8b82acf06655196d7c150701f3f0b82b83ea9eea2bdd7790d5ef735af5251eaf4279be6af535da30705a182000182d87a9f9fd8799fd8799f58204051dd270c1a51da8645b6c91d23053273547f1f853929fbec5519527e18266dff0dffffff821a00d59f801b00000002540be400f5d90103a100a119d90370487964726156312f436f6d6d69745478",
27+
"description": "Ledger Cddl Format",
28+
"hash": "efc69a7604a3b9ce43baad7b062b9d9133517c863cd8b2ee51dcef7b96381f84",
29+
"type": "Witnessed Tx ConwayEra"
30+
}
31+
```

docs/docs/tutorials/fanout.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
2+
# Fanout Transactions to Mainnet
3+
4+
After the contestation period, finalize and fanout:
5+
6+
```ts
7+
const response = await hydraService.fanout(true);
8+
console.log(response);
9+
```
10+
11+
**Example Response:**
12+
13+
```json
14+
{
15+
"headId": "84673dfc0cfd3cf404251fa730fbbfef8d8229b9e2f283e59bca2236",
16+
"seq": 22,
17+
"tag": "HeadIsFinalized",
18+
"timestamp": "2025-05-30T08:15:34.750763178Z",
19+
"utxo": {
20+
"4051dd270c1a51da8645b6c91d23053273547f1f853929fbec5519527e18266d#4": {
21+
"address": "addr_test1vqvf72x6j3sr2jtr5p2yu2jr9emzkxpv0gw859yde8kttqsu0vlpf",
22+
"datum": null,
23+
"datumhash": null,
24+
"inlineDatum": null,
25+
"referenceScript": null,
26+
"value": {
27+
"lovelace": 50000000
28+
}
29+
},
30+
"ff6de11af9d0998a85c3eb5333f4afd173a904164630fc0717be8ee819900d4f#0": {
31+
"address": "addr_test1vqvz4mh9z9qeltx5ha82kafcrpeg3f26vvl40xlqeumgj7cugpfje",
32+
"datum": null,
33+
"inlineDatum": {
34+
"constructor": 2,
35+
"fields": []
36+
},
37+
"inlineDatumhash": "ff5f5c41a5884f08c6e2055d2c44d4b2548b5fc30b47efaa7d337219190886c5",
38+
"referenceScript": null,
39+
"value": {
40+
"lovelace": 2000000
41+
}
42+
},
43+
"ff6de11af9d0998a85c3eb5333f4afd173a904164630fc0717be8ee819900d4f#1": {
44+
"address": "addr_test1vqvz4mh9z9qeltx5ha82kafcrpeg3f26vvl40xlqeumgj7cugpfje",
45+
"datum": null,
46+
"inlineDatum": {
47+
"constructor": 2,
48+
"fields": []
49+
},
50+
"inlineDatumhash": "ff5f5c41a5884f08c6e2055d2c44d4b2548b5fc30b47efaa7d337219190886c5",
51+
"referenceScript": null,
52+
"value": {
53+
"lovelace": 2000000
54+
}
55+
},
56+
"ff6de11af9d0998a85c3eb5333f4afd173a904164630fc0717be8ee819900d4f#2": {
57+
"address": "addr_test1vqvz4mh9z9qeltx5ha82kafcrpeg3f26vvl40xlqeumgj7cugpfje",
58+
"datum": null,
59+
"datumhash": null,
60+
"inlineDatum": null,
61+
"referenceScript": null,
62+
"value": {
63+
"lovelace": 16000000
64+
}
65+
}
66+
}
67+
}
68+
```
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Initialize Hydra Head
2+
3+
```ts
4+
const initResponse = await hydraService.initialize(true);
5+
console.log(initResponse);
6+
```
7+
8+
- The parameter `true` waits indefinitely for a WebSocket response.
9+
- If set to `false`, it waits for 15 seconds and returns a `201 Created` response if no message is received.
10+
11+
**Example Response:**
12+
13+
```json
14+
{
15+
"headId": "84673dfc0cfd3cf404251fa730fbbfef8d8229b9e2f283e59bca2236",
16+
"parties": [
17+
{
18+
"vkey": "232844b0ebd1f13b62b19bc9ce0a423a84e3d2cc5efa2ac226a96255420d7137"
19+
},
20+
{
21+
"vkey": "f6c153b29e86166552334ccbc5c2995bf5185561a270ff489e98f03a8dd74e7d"
22+
}
23+
],
24+
"seq": 2,
25+
"tag": "HeadIsInitializing",
26+
"timestamp": "2025-05-30T07:29:34.370535241Z"
27+
}
28+
```
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Query Head State
2+
3+
```ts
4+
const headState = await hydraService.queryHeadState();
5+
console.log(headState);
6+
```
7+
8+
```json
9+
{ "state": "Partial Commitments Received" }
10+
```
11+
12+
After the other participant has commited, the head state will display the following:
13+
14+
```json
15+
{ "state": "Open and Ready for Transactions" }
16+
```
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Query Protocol Parameters
2+
3+
```ts
4+
await hydraService.queryProtocolParameters();
5+
```

docs/docs/tutorials/query/utxos.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Query UTxOs
2+
3+
```ts
4+
const utxoResponse = await hydraService.queryUTxOByAddress(
5+
"addr_test1vqvz4mh9z9qeltx5ha82kafcrpeg3f26vvl40xlqeumgj7cugpfje"
6+
);
7+
console.log(utxoResponse);
8+
```
9+
This will return a UTxO type from the libcardano package.

docs/docusaurus.config.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ const config: Config = {
6969
position: "left",
7070
label: "Planning",
7171
},
72+
{
73+
type: "docSidebar",
74+
sidebarId: "tutorial",
75+
position: "left",
76+
label: "Tutorial",
77+
},
7278
{
7379
href: "https://github.com/dQuadrant/kuber/tree/feat/hydra",
7480
label: "GitHub",

0 commit comments

Comments
 (0)