@lerna-labs/hydra-sdk 1.0.0-beta.9 → 2.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +159 -0
  2. package/dist/cache/disk-cache.d.ts +37 -0
  3. package/dist/cache/disk-cache.js +84 -0
  4. package/dist/config.d.ts +4 -0
  5. package/dist/config.js +12 -0
  6. package/dist/hydra/hydra-http-client.d.ts +36 -0
  7. package/dist/hydra/hydra-http-client.js +66 -0
  8. package/dist/hydra/hydra-monitor.d.ts +88 -0
  9. package/dist/hydra/hydra-monitor.js +270 -0
  10. package/dist/hydra/hydra-websocket.d.ts +46 -0
  11. package/dist/hydra/hydra-websocket.js +181 -0
  12. package/dist/hydra/messages.d.ts +14 -0
  13. package/dist/hydra/messages.js +1 -0
  14. package/dist/hydra/types.d.ts +486 -0
  15. package/dist/hydra/types.js +2 -0
  16. package/dist/hydra/utxo-conversion.d.ts +10 -0
  17. package/dist/hydra/utxo-conversion.js +111 -0
  18. package/dist/hydra/utxo.d.ts +25 -5
  19. package/dist/hydra/utxo.js +37 -31
  20. package/dist/index.d.ts +15 -7
  21. package/dist/index.js +11 -7
  22. package/dist/ipfs/ipfs.d.ts +22 -0
  23. package/dist/ipfs/ipfs.js +90 -0
  24. package/dist/mesh/get-admin.d.ts +13 -1
  25. package/dist/mesh/get-admin.js +37 -7
  26. package/dist/mesh/native-script.d.ts +30 -5
  27. package/dist/mesh/native-script.js +38 -10
  28. package/dist/test.js +3 -3
  29. package/dist/tx3/submit-tx.d.ts +8 -0
  30. package/dist/tx3/submit-tx.js +8 -0
  31. package/dist/utils/chunk-string.d.ts +7 -0
  32. package/dist/utils/chunk-string.js +7 -0
  33. package/dist/utils/verify-signature.d.ts +28 -5
  34. package/dist/utils/verify-signature.js +39 -18
  35. package/dist/wrangler.d.ts +179 -0
  36. package/dist/wrangler.js +452 -0
  37. package/package.json +25 -6
  38. package/dist/mesh/wrangler.d.ts +0 -29
  39. package/dist/mesh/wrangler.js +0 -277
@@ -1,52 +1,58 @@
1
- import axios from 'axios';
2
- export async function getUtxoSet() {
3
- const baseUrl = process.env.HYDRA_API_URL;
4
- if (!baseUrl) {
5
- throw new Error("HYDRA_API_URL is not defined in the environment variables!");
6
- }
1
+ import { requireEnv } from '../config.js';
2
+ /**
3
+ * Fetch the full UTxO set from the Hydra head snapshot.
4
+ *
5
+ * Reads `HYDRA_API_URL` from the environment.
6
+ *
7
+ * @param options - Query options. Set `includeDatums: true` to include datum/script fields.
8
+ * @returns All UTxOs currently held in the Hydra head.
9
+ */
10
+ export async function getUtxoSet(options) {
11
+ const baseUrl = requireEnv('HYDRA_API_URL');
7
12
  const url = `${baseUrl}/snapshot/utxo`;
8
13
  try {
9
- const response = await axios.get(url);
10
- const data = response.data;
14
+ const response = await fetch(url);
15
+ if (!response.ok) {
16
+ throw new Error(`HTTP ${response.status} fetching ${url}`);
17
+ }
18
+ const data = await response.json();
11
19
  const UtxoSet = [];
12
20
  for (const [txKey, utxo] of Object.entries(data)) {
13
- const [tx_hash, index_str] = txKey.split("#");
21
+ const [tx_hash, index_str] = txKey.split('#');
14
22
  const output_index = parseInt(index_str, 10);
15
23
  const amount = Object.entries(utxo.value).map(([unit, quantity]) => ({
16
24
  unit,
17
25
  quantity,
18
26
  }));
19
- UtxoSet.push({
20
- tx_hash,
21
- output_index,
22
- address: utxo.address,
23
- amount,
24
- });
27
+ const parsed = { tx_hash, output_index, address: utxo.address, amount };
28
+ if (options?.includeDatums) {
29
+ parsed.datum = utxo.datum ?? null;
30
+ parsed.datumHash = utxo.datumHash ?? null;
31
+ parsed.inlineDatum = utxo.inlineDatum ?? null;
32
+ parsed.referenceScript = utxo.referenceScript ?? null;
33
+ }
34
+ UtxoSet.push(parsed);
25
35
  }
26
36
  return UtxoSet;
27
37
  }
28
38
  catch (error) {
29
- console.error("Error fetching the Hydra Ledger?", error);
39
+ console.error('Error fetching the Hydra Ledger?', error);
30
40
  throw error;
31
41
  }
32
42
  }
33
43
  /**
34
- * Fetches the full UTxO snapshot and filters by address.
44
+ * Fetch UTxOs belonging to a specific address from the Hydra head snapshot.
45
+ *
46
+ * @param address - Bech32 address to filter by.
47
+ * @returns UTxOs matching the given address.
35
48
  */
36
- export async function queryUtxoByAddress(address) {
37
- console.log(`Querying UTxO by Address: ${address}`);
38
- try {
39
- const result = [];
40
- const data = await getUtxoSet();
41
- for (const utxo of data) {
42
- if (utxo.address === address) {
43
- result.push(utxo);
44
- }
49
+ export async function queryUtxoByAddress(address, options) {
50
+ const result = [];
51
+ const data = await getUtxoSet(options);
52
+ for (const utxo of data) {
53
+ if (utxo.address === address) {
54
+ result.push(utxo);
45
55
  }
46
- return result;
47
- }
48
- catch (error) {
49
- console.error('Failed to fetch or parse UTxO snapshot:', error.message);
50
- throw error;
51
56
  }
57
+ return result;
52
58
  }
package/dist/index.d.ts CHANGED
@@ -1,7 +1,15 @@
1
- export * from './mesh/get-admin';
2
- export * from './mesh/native-script';
3
- export * from './mesh/wrangler';
4
- export * from './hydra/utxo';
5
- export * from './tx3/submit-tx';
6
- export * from './utils/verify-signature';
7
- export * from './utils/chunk-string';
1
+ export type { DiskCache, DiskCacheConfig } from './cache/disk-cache.js';
2
+ export { createDiskCache } from './cache/disk-cache.js';
3
+ export { optionalEnv, requireEnv } from './config.js';
4
+ export { HydraMonitor } from './hydra/hydra-monitor.js';
5
+ export type { ConfirmedSnapshot, HeadStatus, HydraHeadInfo, HydraMessage, HydraMonitorOptions, HydraSnapshot, HydraStatus, HydraTransaction, HydraWsMessage, hydraStatus, hydraTransaction, ServerOutput, TimestampedEvent, } from './hydra/messages.js';
6
+ export type { ParsedUtxo, UtxoQueryOptions } from './hydra/utxo.js';
7
+ export { getUtxoSet, queryUtxoByAddress } from './hydra/utxo.js';
8
+ export type { IpfsClient, IpfsConfig, PinResult } from './ipfs/ipfs.js';
9
+ export { createIpfsClient } from './ipfs/ipfs.js';
10
+ export { getAdmin } from './mesh/get-admin.js';
11
+ export { createMultisigAddress, createNativeScript } from './mesh/native-script.js';
12
+ export { submitTx } from './tx3/submit-tx.js';
13
+ export { chunkString } from './utils/chunk-string.js';
14
+ export { BECH32_DECODE_LIMIT, bufferToAscii, bufferToHex, decodeBech32Address, verifySignature, } from './utils/verify-signature.js';
15
+ export { CommitArgs, UTxORef, Wrangler } from './wrangler.js';
package/dist/index.js CHANGED
@@ -1,7 +1,11 @@
1
- export * from './mesh/get-admin';
2
- export * from './mesh/native-script';
3
- export * from './mesh/wrangler';
4
- export * from './hydra/utxo';
5
- export * from './tx3/submit-tx';
6
- export * from './utils/verify-signature';
7
- export * from './utils/chunk-string';
1
+ export { createDiskCache } from './cache/disk-cache.js';
2
+ export { optionalEnv, requireEnv } from './config.js';
3
+ export { HydraMonitor } from './hydra/hydra-monitor.js';
4
+ export { getUtxoSet, queryUtxoByAddress } from './hydra/utxo.js';
5
+ export { createIpfsClient } from './ipfs/ipfs.js';
6
+ export { getAdmin } from './mesh/get-admin.js';
7
+ export { createMultisigAddress, createNativeScript } from './mesh/native-script.js';
8
+ export { submitTx } from './tx3/submit-tx.js';
9
+ export { chunkString } from './utils/chunk-string.js';
10
+ export { BECH32_DECODE_LIMIT, bufferToAscii, bufferToHex, decodeBech32Address, verifySignature, } from './utils/verify-signature.js';
11
+ export { Wrangler } from './wrangler.js';
@@ -0,0 +1,22 @@
1
+ export interface IpfsConfig {
2
+ /** Base URL for the Kubo HTTP API (e.g. "http://localhost:5001"). */
3
+ apiUrl: string;
4
+ }
5
+ export interface PinResult {
6
+ /** The content-identifier returned by the IPFS node. */
7
+ cid: string;
8
+ /** Size in bytes as reported by the IPFS node. */
9
+ size: number;
10
+ }
11
+ /**
12
+ * Create an IPFS client bound to the given Kubo API endpoint.
13
+ *
14
+ * All operations go through the Kubo HTTP RPC, so the only requirement
15
+ * is a reachable Kubo node — no additional IPFS libraries are needed.
16
+ */
17
+ export declare function createIpfsClient(config: IpfsConfig): {
18
+ pinJson: (filename: string, payload: unknown) => Promise<PinResult>;
19
+ pinDirectory: (dirPath: string) => Promise<PinResult>;
20
+ fetchJson: <T = unknown>(cid: string) => Promise<T>;
21
+ };
22
+ export type IpfsClient = ReturnType<typeof createIpfsClient>;
@@ -0,0 +1,90 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ /**
4
+ * Create an IPFS client bound to the given Kubo API endpoint.
5
+ *
6
+ * All operations go through the Kubo HTTP RPC, so the only requirement
7
+ * is a reachable Kubo node — no additional IPFS libraries are needed.
8
+ */
9
+ export function createIpfsClient(config) {
10
+ const { apiUrl } = config;
11
+ /**
12
+ * Pin a single JSON-serialisable payload and return its CID.
13
+ *
14
+ * @param filename - Filename used inside the IPFS object.
15
+ * @param payload - Any JSON-serialisable value.
16
+ */
17
+ async function pinJson(filename, payload) {
18
+ const body = JSON.stringify(payload, null, 2);
19
+ const form = new FormData();
20
+ form.append('file', new Blob([body], { type: 'application/json' }), filename);
21
+ const res = await fetch(`${apiUrl}/api/v0/add?pin=true`, {
22
+ method: 'POST',
23
+ body: form,
24
+ });
25
+ if (!res.ok) {
26
+ throw new Error(`IPFS pin failed: ${res.status} ${await res.text()}`);
27
+ }
28
+ const json = (await res.json());
29
+ return { cid: json.Hash, size: Number.parseInt(json.Size, 10) };
30
+ }
31
+ /**
32
+ * Pin every file in a directory and wrap them in an IPFS directory object.
33
+ *
34
+ * @param dirPath - Absolute path to a local directory.
35
+ * @returns The CID of the wrapping IPFS directory.
36
+ */
37
+ async function pinDirectory(dirPath) {
38
+ const form = new FormData();
39
+ const collectFiles = async (rel) => {
40
+ const out = [];
41
+ const dirents = await fs.readdir(path.join(dirPath, rel), { withFileTypes: true });
42
+ for (const dirent of dirents) {
43
+ const childRel = path.join(rel, dirent.name);
44
+ if (dirent.isDirectory()) {
45
+ out.push(...(await collectFiles(childRel)));
46
+ }
47
+ else if (dirent.isFile()) {
48
+ out.push(childRel);
49
+ }
50
+ }
51
+ return out;
52
+ };
53
+ for (const relPath of await collectFiles('')) {
54
+ const buf = await fs.readFile(path.join(dirPath, relPath));
55
+ const content = new Uint8Array(buf).slice().buffer;
56
+ // Kubo reconstructs the directory tree from entry names — always
57
+ // use POSIX separators so Windows callers get the same CID.
58
+ const entryName = relPath.split(path.sep).join('/');
59
+ form.append('file', new Blob([content]), entryName);
60
+ }
61
+ const res = await fetch(`${apiUrl}/api/v0/add?pin=true&wrap-with-directory=true&recursive=true`, {
62
+ method: 'POST',
63
+ body: form,
64
+ });
65
+ if (!res.ok) {
66
+ throw new Error(`IPFS directory pin failed: ${res.status} ${await res.text()}`);
67
+ }
68
+ // Kubo returns one JSON object per line (ndjson). The last line is the
69
+ // wrapping directory entry.
70
+ const text = await res.text();
71
+ const lines = text.trim().split('\n');
72
+ const last = JSON.parse(lines[lines.length - 1]);
73
+ return { cid: last.Hash, size: Number.parseInt(last.Size, 10) };
74
+ }
75
+ /**
76
+ * Fetch a JSON payload from IPFS by CID.
77
+ *
78
+ * @param cid - The content identifier to retrieve.
79
+ */
80
+ async function fetchJson(cid) {
81
+ const res = await fetch(`${apiUrl}/api/v0/cat?arg=${cid}`, {
82
+ method: 'POST',
83
+ });
84
+ if (!res.ok) {
85
+ throw new Error(`IPFS fetch failed: ${res.status} ${await res.text()}`);
86
+ }
87
+ return (await res.json());
88
+ }
89
+ return { pinJson, pinDirectory, fetchJson };
90
+ }
@@ -1,2 +1,14 @@
1
1
  import { MeshWallet } from '@meshsdk/core';
2
- export declare function getAdmin(): Promise<MeshWallet>;
2
+ /**
3
+ * Create and initialize a MeshWallet for the Hydra head admin.
4
+ *
5
+ * Reads the Cardano signing key from `HYDRA_ADMIN_KEY_FILE` (preferred)
6
+ * or falls back to `HYDRA_ADMIN_CARDANO_PK`. Network is selected via `HYDRA_NETWORK`.
7
+ *
8
+ * @param blockfrostProjectId - Optional Blockfrost project ID. When provided, the wallet
9
+ * is configured with a Blockfrost fetcher and submitter for L1 operations (e.g. preparing
10
+ * the Hydra head, querying UTxOs, submitting commit transactions).
11
+ * @returns An initialized MeshWallet ready for signing transactions.
12
+ * @throws If no signing key is available or the wallet fails to initialize.
13
+ */
14
+ export declare function getAdmin(blockfrostProjectId?: string): Promise<MeshWallet>;
@@ -1,23 +1,53 @@
1
- import { MeshWallet, } from '@meshsdk/core';
2
- export async function getAdmin() {
3
- const keyCborHex = process.env.HYDRA_ADMIN_CARDANO_PK || null;
1
+ import { readFileSync } from 'node:fs';
2
+ import { BlockfrostProvider, MeshWallet } from '@meshsdk/core';
3
+ import { optionalEnv } from '../config.js';
4
+ /**
5
+ * Create and initialize a MeshWallet for the Hydra head admin.
6
+ *
7
+ * Reads the Cardano signing key from `HYDRA_ADMIN_KEY_FILE` (preferred)
8
+ * or falls back to `HYDRA_ADMIN_CARDANO_PK`. Network is selected via `HYDRA_NETWORK`.
9
+ *
10
+ * @param blockfrostProjectId - Optional Blockfrost project ID. When provided, the wallet
11
+ * is configured with a Blockfrost fetcher and submitter for L1 operations (e.g. preparing
12
+ * the Hydra head, querying UTxOs, submitting commit transactions).
13
+ * @returns An initialized MeshWallet ready for signing transactions.
14
+ * @throws If no signing key is available or the wallet fails to initialize.
15
+ */
16
+ export async function getAdmin(blockfrostProjectId) {
17
+ let keyCborHex = null;
18
+ // Preferred: read the instance's cardano.sk file directly (no secrets in .env)
19
+ const keyFile = process.env.HYDRA_ADMIN_KEY_FILE;
20
+ if (keyFile) {
21
+ const content = JSON.parse(readFileSync(keyFile, 'utf-8'));
22
+ keyCborHex = content.cborHex ?? content.key?.cborHex ?? null;
23
+ }
24
+ // Fallback: cborHex passed via env var (backward compatible)
25
+ if (!keyCborHex) {
26
+ keyCborHex = process.env.HYDRA_ADMIN_CARDANO_PK || null;
27
+ }
4
28
  if (!keyCborHex) {
5
- throw new Error('Admin signing key is not defined!');
29
+ throw new Error('Cardano signing key not found. Set HYDRA_ADMIN_KEY_FILE to the instance cardano.sk path, or HYDRA_ADMIN_CARDANO_PK to its cborHex.');
6
30
  }
7
- let networkId = parseInt(process.env.HYDRA_NETWORK || '0', 10);
31
+ let networkId = parseInt(optionalEnv('HYDRA_NETWORK', '0'), 10);
8
32
  if (networkId < 0) {
9
33
  networkId = 0;
10
34
  }
11
35
  else if (networkId > 1) {
12
36
  networkId = 1;
13
37
  }
14
- const wallet = new MeshWallet({
38
+ const walletOptions = {
15
39
  networkId: networkId,
16
40
  key: {
17
41
  type: 'cli',
18
42
  payment: keyCborHex,
19
43
  },
20
- });
44
+ };
45
+ if (blockfrostProjectId) {
46
+ const blockfrost = new BlockfrostProvider(blockfrostProjectId);
47
+ walletOptions.fetcher = blockfrost;
48
+ walletOptions.submitter = blockfrost;
49
+ }
50
+ const wallet = new MeshWallet(walletOptions);
21
51
  await wallet.init();
22
52
  if (!wallet.addresses.enterpriseAddressBech32) {
23
53
  throw new Error('Wallet failed to initialize!');
@@ -1,12 +1,37 @@
1
1
  /**
2
- * Create a multisig address using 'any' policy (AND logic)
3
- * @param address1 The first address to include in the script (staking or payment)
4
- * @param address2 The second address to include in the script (staking or payment)
5
- * @param networkId 0 = testnet, 1 = mainnet
6
- * @param scriptType
2
+ * Create a multisig script address from two Cardano addresses.
3
+ *
4
+ * @param address1 - First address (bech32) to include in the native script.
5
+ * @param address2 - Second address (bech32) to include in the native script.
6
+ * @param networkId - `0` for testnet, `1` for mainnet.
7
+ * @param scriptType - `"any"` requires one signature, `"all"` requires both.
8
+ * @returns The script address, serialized CBOR, and script hash.
7
9
  */
8
10
  export declare function createMultisigAddress(address1: string, address2: string, networkId?: number, scriptType?: 'any' | 'all'): {
9
11
  address: string;
10
12
  scriptCbor?: string;
11
13
  scriptHash?: string;
12
14
  };
15
+ /**
16
+ * Create a native script policy for minting tokens.
17
+ *
18
+ * Without options, produces a bare `sig(keyHash)` script suitable for
19
+ * in-head voter tokens that must remain burnable by the admin.
20
+ *
21
+ * When `invalidHereafter` is provided, produces a compound time-bound
22
+ * script: `all: [sig(keyHash), before(slot)]` — each ballot gets its
23
+ * own time-bound policy.
24
+ *
25
+ * @param address - Address (bech32) whose key hash is the required signer.
26
+ * @param opts.invalidHereafter - Slot after which minting is no longer possible.
27
+ * @param opts.networkId - `0` for testnet (default), `1` for mainnet.
28
+ * @returns The script address, serialized CBOR, and script hash.
29
+ */
30
+ export declare function createNativeScript(address: string, opts?: {
31
+ invalidHereafter?: number;
32
+ networkId?: number;
33
+ }): {
34
+ address: string;
35
+ scriptCbor?: string;
36
+ scriptHash?: string;
37
+ };
@@ -1,10 +1,12 @@
1
- import { serializeNativeScript, resolveScriptHash, deserializeAddress, } from '@meshsdk/core';
1
+ import { deserializeAddress, resolveScriptHash, serializeNativeScript } from '@meshsdk/core';
2
2
  /**
3
- * Create a multisig address using 'any' policy (AND logic)
4
- * @param address1 The first address to include in the script (staking or payment)
5
- * @param address2 The second address to include in the script (staking or payment)
6
- * @param networkId 0 = testnet, 1 = mainnet
7
- * @param scriptType
3
+ * Create a multisig script address from two Cardano addresses.
4
+ *
5
+ * @param address1 - First address (bech32) to include in the native script.
6
+ * @param address2 - Second address (bech32) to include in the native script.
7
+ * @param networkId - `0` for testnet, `1` for mainnet.
8
+ * @param scriptType - `"any"` requires one signature, `"all"` requires both.
9
+ * @returns The script address, serialized CBOR, and script hash.
8
10
  */
9
11
  export function createMultisigAddress(address1, address2, networkId = 0, scriptType = 'any') {
10
12
  const keyHash1 = deserializeAddress(address1).pubKeyHash;
@@ -23,9 +25,35 @@ export function createMultisigAddress(address1, address2, networkId = 0, scriptT
23
25
  ],
24
26
  };
25
27
  const { address, scriptCbor } = serializeNativeScript(script, undefined, networkId);
26
- let scriptHash;
27
- if (scriptCbor != null) {
28
- scriptHash = resolveScriptHash(scriptCbor);
29
- }
28
+ const scriptHash = scriptCbor != null ? resolveScriptHash(scriptCbor) : undefined;
30
29
  return { address, scriptCbor, scriptHash };
31
30
  }
31
+ /**
32
+ * Create a native script policy for minting tokens.
33
+ *
34
+ * Without options, produces a bare `sig(keyHash)` script suitable for
35
+ * in-head voter tokens that must remain burnable by the admin.
36
+ *
37
+ * When `invalidHereafter` is provided, produces a compound time-bound
38
+ * script: `all: [sig(keyHash), before(slot)]` — each ballot gets its
39
+ * own time-bound policy.
40
+ *
41
+ * @param address - Address (bech32) whose key hash is the required signer.
42
+ * @param opts.invalidHereafter - Slot after which minting is no longer possible.
43
+ * @param opts.networkId - `0` for testnet (default), `1` for mainnet.
44
+ * @returns The script address, serialized CBOR, and script hash.
45
+ */
46
+ export function createNativeScript(address, opts) {
47
+ const networkId = opts?.networkId ?? 0;
48
+ const keyHash = deserializeAddress(address).pubKeyHash;
49
+ const sigScript = { type: 'sig', keyHash };
50
+ const script = opts?.invalidHereafter != null
51
+ ? {
52
+ type: 'all',
53
+ scripts: [sigScript, { type: 'before', slot: opts.invalidHereafter.toString() }],
54
+ }
55
+ : sigScript;
56
+ const { address: scriptAddress, scriptCbor } = serializeNativeScript(script, undefined, networkId);
57
+ const scriptHash = scriptCbor != null ? resolveScriptHash(scriptCbor) : undefined;
58
+ return { address: scriptAddress, scriptCbor, scriptHash };
59
+ }
package/dist/test.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as dotenv from 'dotenv';
2
2
  dotenv.config({ path: '.local.env' });
3
3
  import { BlockfrostProvider, MeshTxBuilder, MeshWallet } from '@meshsdk/core';
4
- import { Wrangler } from './mesh/wrangler';
4
+ import { Wrangler } from './wrangler.js';
5
5
  (async () => {
6
6
  const admin_wallet = new MeshWallet({
7
7
  networkId: parseInt(process.env.HYDRA_NETWORK_ID || '0', 10),
@@ -14,8 +14,8 @@ import { Wrangler } from './mesh/wrangler';
14
14
  const admin_address = admin_wallet.addresses.enterpriseAddressBech32;
15
15
  console.log(`Admin address: ${admin_address}`);
16
16
  const blockfrostProvider = new BlockfrostProvider(process.env.BLOCKFROST_API_KEY);
17
- const txHash = 'a000003f633d9b2efcc18dedefaf60623e3132c8b05a5751ac08d3bf6f505d54';
18
- const txIndex = 3;
17
+ const _txHash = 'a000003f633d9b2efcc18dedefaf60623e3132c8b05a5751ac08d3bf6f505d54';
18
+ const _txIndex = 3;
19
19
  const utxo = await blockfrostProvider.fetchAddressUTxOs(admin_address);
20
20
  if (utxo.length < 3) {
21
21
  console.log(utxo);
@@ -1 +1,9 @@
1
+ /**
2
+ * Submit a transaction to a TRP endpoint via JSON-RPC.
3
+ *
4
+ * @param submit_endpoint - URL of the TRP submit endpoint.
5
+ * @param payload - Hex-encoded transaction payload.
6
+ * @param id - JSON-RPC request identifier.
7
+ * @returns The fetch Response from the TRP endpoint.
8
+ */
1
9
  export declare function submitTx(submit_endpoint: string, payload: string, id: string): Promise<Response>;
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Submit a transaction to a TRP endpoint via JSON-RPC.
3
+ *
4
+ * @param submit_endpoint - URL of the TRP submit endpoint.
5
+ * @param payload - Hex-encoded transaction payload.
6
+ * @param id - JSON-RPC request identifier.
7
+ * @returns The fetch Response from the TRP endpoint.
8
+ */
1
9
  export async function submitTx(submit_endpoint, payload, id) {
2
10
  return await fetch(submit_endpoint, {
3
11
  method: 'POST',
@@ -1 +1,8 @@
1
+ /**
2
+ * Split a string into fixed-size chunks.
3
+ *
4
+ * @param str - The string to split.
5
+ * @param size - Maximum character count per chunk.
6
+ * @returns Array of string chunks.
7
+ */
1
8
  export declare function chunkString(str: string, size: number): string[];
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Split a string into fixed-size chunks.
3
+ *
4
+ * @param str - The string to split.
5
+ * @param size - Maximum character count per chunk.
6
+ * @returns Array of string chunks.
7
+ */
1
8
  export function chunkString(str, size) {
2
9
  const chunks = [];
3
10
  for (let i = 0; i < str.length; i += size) {
@@ -1,10 +1,33 @@
1
- export declare const bufferToHex: (buffer: any) => string;
2
- export declare const bufferToAscii: (buffer: any) => string;
1
+ import { Buffer } from 'node:buffer';
2
+ /** Convert a buffer-like value to a hex string. */
3
+ export declare const bufferToHex: (buffer: Uint8Array | string) => string;
4
+ /** Convert a buffer-like value to an ASCII string. */
5
+ export declare const bufferToAscii: (buffer: Uint8Array | string) => string;
6
+ /**
7
+ * Character limit used for bech32 decoding. The library defaults to 90, which truncates
8
+ * Cardano base addresses (payment + staking) that are commonly 103+ characters. We raise
9
+ * the ceiling well above any realistic Cardano encoding (addr, stake, drep, pool, cc_*).
10
+ */
11
+ export declare const BECH32_DECODE_LIMIT = 1023;
12
+ /**
13
+ * Decode a Cardano bech32 string (addr, stake, pool, drep, cc_*) with a character
14
+ * limit high enough to accommodate base addresses that include a staking component.
15
+ */
16
+ export declare function decodeBech32Address(address: string): {
17
+ prefix: string;
18
+ addressBytes: Buffer;
19
+ };
20
+ /**
21
+ * Verify a CIP-30 COSE_Sign1 signature against an expected message and address.
22
+ *
23
+ * @param signature - Hex-encoded COSE_Sign1 signature bytes.
24
+ * @param message - The original plaintext message that was signed.
25
+ * @param signingAddress - Bech32 address of the expected signer.
26
+ * @param signatureKey - Hex-encoded COSE key containing the public key.
27
+ * @returns Validation result with `isValid`, chunked signature metadata, and public key hex.
28
+ */
3
29
  export declare function verifySignature(signature: string, message: string, signingAddress: string, signatureKey: string): {
4
30
  isValid: boolean;
5
31
  sigMeta: string[];
6
32
  pubKeyHex: string;
7
33
  };
8
- /**
9
- * End Signature Validation Stuff
10
- */
@@ -1,43 +1,64 @@
1
+ import { Buffer } from 'node:buffer';
2
+ import { COSESign1 } from '@emurgo/cardano-message-signing-nodejs';
3
+ import * as CSL from '@emurgo/cardano-serialization-lib-nodejs';
4
+ import { bech32 } from 'bech32';
5
+ import { default as cbor } from 'cbor';
6
+ import { chunkString } from './chunk-string.js';
7
+ /** Convert a buffer-like value to a hex string. */
8
+ export const bufferToHex = (buffer) => Buffer.from(buffer).toString('hex');
9
+ /** Convert a buffer-like value to an ASCII string. */
10
+ export const bufferToAscii = (buffer) => Buffer.from(buffer).toString('ascii');
1
11
  /**
2
- * Signature Validation Stuff
12
+ * Character limit used for bech32 decoding. The library defaults to 90, which truncates
13
+ * Cardano base addresses (payment + staking) that are commonly 103+ characters. We raise
14
+ * the ceiling well above any realistic Cardano encoding (addr, stake, drep, pool, cc_*).
15
+ */
16
+ export const BECH32_DECODE_LIMIT = 1023;
17
+ /**
18
+ * Decode a Cardano bech32 string (addr, stake, pool, drep, cc_*) with a character
19
+ * limit high enough to accommodate base addresses that include a staking component.
20
+ */
21
+ export function decodeBech32Address(address) {
22
+ const { words, prefix } = bech32.decode(address, BECH32_DECODE_LIMIT);
23
+ return { prefix, addressBytes: Buffer.from(bech32.fromWords(words)) };
24
+ }
25
+ /**
26
+ * Verify a CIP-30 COSE_Sign1 signature against an expected message and address.
27
+ *
28
+ * @param signature - Hex-encoded COSE_Sign1 signature bytes.
29
+ * @param message - The original plaintext message that was signed.
30
+ * @param signingAddress - Bech32 address of the expected signer.
31
+ * @param signatureKey - Hex-encoded COSE key containing the public key.
32
+ * @returns Validation result with `isValid`, chunked signature metadata, and public key hex.
3
33
  */
4
- import * as CSL from "@emurgo/cardano-serialization-lib-nodejs";
5
- import { COSESign1 } from "@emurgo/cardano-message-signing-nodejs";
6
- import { Buffer } from "buffer";
7
- import { default as cbor } from "cbor";
8
- import { chunkString } from "./chunk-string";
9
- import { bech32 } from "bech32";
10
- export const bufferToHex = (buffer) => Buffer.from(buffer).toString("hex");
11
- export const bufferToAscii = (buffer) => Buffer.from(buffer).toString("ascii");
12
34
  export function verifySignature(signature, message, signingAddress, signatureKey) {
13
35
  try {
14
- const coseSign1 = COSESign1.from_bytes(Buffer.from(signature, "hex"));
36
+ const coseSign1 = COSESign1.from_bytes(Buffer.from(signature, 'hex'));
15
37
  const signatureBytes = coseSign1.signature();
16
38
  const [, , , payload1] = cbor.decode(bufferToHex(coseSign1.signed_data().to_bytes()));
17
39
  const signaturePayloadAscii = bufferToAscii(payload1);
18
- const { words } = bech32.decode(signingAddress);
19
- const addressBytes = Buffer.from(bech32.fromWords(words));
40
+ const { prefix, addressBytes } = decodeBech32Address(signingAddress);
20
41
  const coseSigKey = cbor.decode(signatureKey);
21
42
  const cosePublicKey = coseSigKey.get(-2);
22
43
  const sigKey = CSL.PublicKey.from_bytes(cosePublicKey);
23
44
  const publicKeyHash = sigKey.hash();
24
- const address_matches = addressBytes.toString("hex").slice(2) === publicKeyHash.to_hex();
45
+ // pool1... bech32 encodes the raw 28-byte key hash with no header byte.
46
+ // All other address types (addr, drep, stake) have a 1-byte header prefix.
47
+ const addressHex = addressBytes.toString('hex');
48
+ const address_matches = prefix === 'pool' ? addressHex === publicKeyHash.to_hex() : addressHex.slice(2) === publicKeyHash.to_hex();
25
49
  const sig = CSL.Ed25519Signature.from_bytes(signatureBytes);
26
50
  const validates = sigKey.verify(coseSign1.signed_data().to_bytes(), sig);
27
51
  const message_matches = signaturePayloadAscii === message;
28
52
  const isValid = validates && message_matches && address_matches;
29
53
  const sigMeta = chunkString(sig.to_hex(), 64);
30
54
  if (!isValid) {
31
- console.log("Failed to validate signature!");
55
+ console.log('Failed to validate signature!');
32
56
  console.log(isValid, validates, message_matches, address_matches);
33
57
  }
34
58
  return { isValid, sigMeta, pubKeyHex: sigKey.to_hex() };
35
59
  }
36
60
  catch (error) {
37
- console.error(`Error during signature validation:`, error);
61
+ console.error(`Error during signature validation:`, String(error));
38
62
  return { isValid: false, sigMeta: [], pubKeyHex: '' };
39
63
  }
40
64
  }
41
- /**
42
- * End Signature Validation Stuff
43
- */