@manifest-network/manifest-mcp-core 0.1.0
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.
- package/dist/__test-utils__/callTool.d.ts +29 -0
- package/dist/__test-utils__/callTool.d.ts.map +1 -0
- package/dist/__test-utils__/callTool.js +45 -0
- package/dist/__test-utils__/callTool.js.map +1 -0
- package/dist/__test-utils__/mocks.d.ts +125 -0
- package/dist/__test-utils__/mocks.d.ts.map +1 -0
- package/dist/__test-utils__/mocks.js +146 -0
- package/dist/__test-utils__/mocks.js.map +1 -0
- package/dist/client.d.ts +67 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +209 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +30 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +127 -0
- package/dist/config.js.map +1 -0
- package/dist/cosmos.d.ts +24 -0
- package/dist/cosmos.d.ts.map +1 -0
- package/dist/cosmos.js +85 -0
- package/dist/cosmos.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +20 -0
- package/dist/lcd-adapter.d.ts +15 -0
- package/dist/lcd-adapter.d.ts.map +1 -0
- package/dist/lcd-adapter.js +98 -0
- package/dist/lcd-adapter.js.map +1 -0
- package/dist/logger.d.ts +20 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +43 -0
- package/dist/logger.js.map +1 -0
- package/dist/modules.d.ts +63 -0
- package/dist/modules.d.ts.map +1 -0
- package/dist/modules.js +759 -0
- package/dist/modules.js.map +1 -0
- package/dist/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js +78 -0
- package/dist/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js.map +1 -0
- package/dist/node_modules/@vitest/expect/dist/index.d.ts +802 -0
- package/dist/node_modules/@vitest/expect/dist/index.d.ts.map +1 -0
- package/dist/node_modules/@vitest/expect/dist/index.js +1457 -0
- package/dist/node_modules/@vitest/expect/dist/index.js.map +1 -0
- package/dist/node_modules/@vitest/pretty-format/dist/index.d.ts +95 -0
- package/dist/node_modules/@vitest/pretty-format/dist/index.d.ts.map +1 -0
- package/dist/node_modules/@vitest/pretty-format/dist/index.js +877 -0
- package/dist/node_modules/@vitest/pretty-format/dist/index.js.map +1 -0
- package/dist/node_modules/@vitest/runner/dist/chunk-tasks.js +91 -0
- package/dist/node_modules/@vitest/runner/dist/chunk-tasks.js.map +1 -0
- package/dist/node_modules/@vitest/runner/dist/index.js +1381 -0
- package/dist/node_modules/@vitest/runner/dist/index.js.map +1 -0
- package/dist/node_modules/@vitest/runner/dist/tasks.d-D2GKpdwQ.d.ts +540 -0
- package/dist/node_modules/@vitest/runner/dist/tasks.d-D2GKpdwQ.d.ts.map +1 -0
- package/dist/node_modules/@vitest/runner/dist/utils.js +1 -0
- package/dist/node_modules/@vitest/snapshot/dist/environment.d-DOJxxZV9.d.ts +16 -0
- package/dist/node_modules/@vitest/snapshot/dist/environment.d-DOJxxZV9.d.ts.map +1 -0
- package/dist/node_modules/@vitest/snapshot/dist/index.d.ts +89 -0
- package/dist/node_modules/@vitest/snapshot/dist/index.d.ts.map +1 -0
- package/dist/node_modules/@vitest/snapshot/dist/index.js +649 -0
- package/dist/node_modules/@vitest/snapshot/dist/index.js.map +1 -0
- package/dist/node_modules/@vitest/snapshot/dist/rawSnapshot.d-U2kJUxDr.d.ts +40 -0
- package/dist/node_modules/@vitest/snapshot/dist/rawSnapshot.d-U2kJUxDr.d.ts.map +1 -0
- package/dist/node_modules/@vitest/spy/dist/index.d.ts +343 -0
- package/dist/node_modules/@vitest/spy/dist/index.d.ts.map +1 -0
- package/dist/node_modules/@vitest/spy/dist/index.js +386 -0
- package/dist/node_modules/@vitest/spy/dist/index.js.map +1 -0
- package/dist/node_modules/@vitest/utils/dist/chunk-pathe.M-eThtNZ.js +82 -0
- package/dist/node_modules/@vitest/utils/dist/chunk-pathe.M-eThtNZ.js.map +1 -0
- package/dist/node_modules/@vitest/utils/dist/diff.d.ts +14 -0
- package/dist/node_modules/@vitest/utils/dist/diff.d.ts.map +1 -0
- package/dist/node_modules/@vitest/utils/dist/diff.js +1297 -0
- package/dist/node_modules/@vitest/utils/dist/diff.js.map +1 -0
- package/dist/node_modules/@vitest/utils/dist/display.d.ts +15 -0
- package/dist/node_modules/@vitest/utils/dist/display.d.ts.map +1 -0
- package/dist/node_modules/@vitest/utils/dist/display.js +558 -0
- package/dist/node_modules/@vitest/utils/dist/display.js.map +1 -0
- package/dist/node_modules/@vitest/utils/dist/error.js +30 -0
- package/dist/node_modules/@vitest/utils/dist/error.js.map +1 -0
- package/dist/node_modules/@vitest/utils/dist/helpers.js +181 -0
- package/dist/node_modules/@vitest/utils/dist/helpers.js.map +1 -0
- package/dist/node_modules/@vitest/utils/dist/offset.js +27 -0
- package/dist/node_modules/@vitest/utils/dist/offset.js.map +1 -0
- package/dist/node_modules/@vitest/utils/dist/serialize.js +77 -0
- package/dist/node_modules/@vitest/utils/dist/serialize.js.map +1 -0
- package/dist/node_modules/@vitest/utils/dist/source-map.js +367 -0
- package/dist/node_modules/@vitest/utils/dist/source-map.js.map +1 -0
- package/dist/node_modules/@vitest/utils/dist/timers.js +37 -0
- package/dist/node_modules/@vitest/utils/dist/timers.js.map +1 -0
- package/dist/node_modules/@vitest/utils/dist/types.d-BCElaP-c.d.ts +38 -0
- package/dist/node_modules/@vitest/utils/dist/types.d-BCElaP-c.d.ts.map +1 -0
- package/dist/node_modules/@vitest/utils/dist/types.d.ts +25 -0
- package/dist/node_modules/@vitest/utils/dist/types.d.ts.map +1 -0
- package/dist/node_modules/chai/index.js +2875 -0
- package/dist/node_modules/chai/index.js.map +1 -0
- package/dist/node_modules/magic-string/dist/magic-string.es.js +939 -0
- package/dist/node_modules/magic-string/dist/magic-string.es.js.map +1 -0
- package/dist/node_modules/pathe/dist/shared/pathe.M-eThtNZ.js +85 -0
- package/dist/node_modules/pathe/dist/shared/pathe.M-eThtNZ.js.map +1 -0
- package/dist/node_modules/tinybench/dist/index.d.ts +91 -0
- package/dist/node_modules/tinybench/dist/index.d.ts.map +1 -0
- package/dist/node_modules/tinyrainbow/dist/index.d.ts +9 -0
- package/dist/node_modules/tinyrainbow/dist/index.d.ts.map +1 -0
- package/dist/node_modules/tinyrainbow/dist/index.js +86 -0
- package/dist/node_modules/tinyrainbow/dist/index.js.map +1 -0
- package/dist/node_modules/vitest/dist/chunks/_commonjsHelpers.D26ty3Ew.js +6 -0
- package/dist/node_modules/vitest/dist/chunks/_commonjsHelpers.D26ty3Ew.js.map +1 -0
- package/dist/node_modules/vitest/dist/chunks/benchmark.D0SlKNbZ.js +41 -0
- package/dist/node_modules/vitest/dist/chunks/benchmark.D0SlKNbZ.js.map +1 -0
- package/dist/node_modules/vitest/dist/chunks/benchmark.d.DAaHLpsq.d.ts +12 -0
- package/dist/node_modules/vitest/dist/chunks/benchmark.d.DAaHLpsq.d.ts.map +1 -0
- package/dist/node_modules/vitest/dist/chunks/global.d.x-ILCfAE.d.ts +100 -0
- package/dist/node_modules/vitest/dist/chunks/global.d.x-ILCfAE.d.ts.map +1 -0
- package/dist/node_modules/vitest/dist/chunks/rpc.MzXet3jl.js +57 -0
- package/dist/node_modules/vitest/dist/chunks/rpc.MzXet3jl.js.map +1 -0
- package/dist/node_modules/vitest/dist/chunks/rpc.d.BFMWpdph.d.ts +13 -0
- package/dist/node_modules/vitest/dist/chunks/rpc.d.BFMWpdph.d.ts.map +1 -0
- package/dist/node_modules/vitest/dist/chunks/test.CTcmp4Su.js +2791 -0
- package/dist/node_modules/vitest/dist/chunks/test.CTcmp4Su.js.map +1 -0
- package/dist/node_modules/vitest/dist/chunks/utils.BX5Fg8C4.js +44 -0
- package/dist/node_modules/vitest/dist/chunks/utils.BX5Fg8C4.js.map +1 -0
- package/dist/node_modules/vitest/dist/index.d.ts +9 -0
- package/dist/queries/auth.d.ts +15 -0
- package/dist/queries/auth.d.ts.map +1 -0
- package/dist/queries/auth.js +58 -0
- package/dist/queries/auth.js.map +1 -0
- package/dist/queries/bank.d.ts +15 -0
- package/dist/queries/bank.d.ts.map +1 -0
- package/dist/queries/bank.js +93 -0
- package/dist/queries/bank.js.map +1 -0
- package/dist/queries/billing.d.ts +15 -0
- package/dist/queries/billing.d.ts.map +1 -0
- package/dist/queries/billing.js +114 -0
- package/dist/queries/billing.js.map +1 -0
- package/dist/queries/distribution.d.ts +15 -0
- package/dist/queries/distribution.d.ts.map +1 -0
- package/dist/queries/distribution.js +73 -0
- package/dist/queries/distribution.js.map +1 -0
- package/dist/queries/gov.d.ts +15 -0
- package/dist/queries/gov.d.ts.map +1 -0
- package/dist/queries/gov.js +98 -0
- package/dist/queries/gov.js.map +1 -0
- package/dist/queries/group.d.ts +15 -0
- package/dist/queries/group.d.ts.map +1 -0
- package/dist/queries/group.js +159 -0
- package/dist/queries/group.js.map +1 -0
- package/dist/queries/index.d.ts +10 -0
- package/dist/queries/index.js +10 -0
- package/dist/queries/sku.d.ts +16 -0
- package/dist/queries/sku.d.ts.map +1 -0
- package/dist/queries/sku.js +85 -0
- package/dist/queries/sku.js.map +1 -0
- package/dist/queries/staking.d.ts +15 -0
- package/dist/queries/staking.d.ts.map +1 -0
- package/dist/queries/staking.js +127 -0
- package/dist/queries/staking.js.map +1 -0
- package/dist/queries/utils.d.ts +54 -0
- package/dist/queries/utils.d.ts.map +1 -0
- package/dist/queries/utils.js +74 -0
- package/dist/queries/utils.js.map +1 -0
- package/dist/retry.d.ts +48 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +106 -0
- package/dist/retry.js.map +1 -0
- package/dist/server-utils.d.ts +61 -0
- package/dist/server-utils.d.ts.map +1 -0
- package/dist/server-utils.js +156 -0
- package/dist/server-utils.js.map +1 -0
- package/dist/tools/fundCredits.d.ts +8 -0
- package/dist/tools/fundCredits.d.ts.map +1 -0
- package/dist/tools/fundCredits.js +9 -0
- package/dist/tools/fundCredits.js.map +1 -0
- package/dist/tools/getBalance.d.ts +26 -0
- package/dist/tools/getBalance.d.ts.map +1 -0
- package/dist/tools/getBalance.js +59 -0
- package/dist/tools/getBalance.js.map +1 -0
- package/dist/tools/stopApp.d.ts +13 -0
- package/dist/tools/stopApp.d.ts.map +1 -0
- package/dist/tools/stopApp.js +15 -0
- package/dist/tools/stopApp.js.map +1 -0
- package/dist/transactions/bank.d.ts +11 -0
- package/dist/transactions/bank.d.ts.map +1 -0
- package/dist/transactions/bank.js +75 -0
- package/dist/transactions/bank.js.map +1 -0
- package/dist/transactions/billing.d.ts +11 -0
- package/dist/transactions/billing.d.ts.map +1 -0
- package/dist/transactions/billing.js +189 -0
- package/dist/transactions/billing.js.map +1 -0
- package/dist/transactions/distribution.d.ts +11 -0
- package/dist/transactions/distribution.d.ts.map +1 -0
- package/dist/transactions/distribution.js +60 -0
- package/dist/transactions/distribution.js.map +1 -0
- package/dist/transactions/gov.d.ts +11 -0
- package/dist/transactions/gov.d.ts.map +1 -0
- package/dist/transactions/gov.js +108 -0
- package/dist/transactions/gov.js.map +1 -0
- package/dist/transactions/group.d.ts +11 -0
- package/dist/transactions/group.d.ts.map +1 -0
- package/dist/transactions/group.js +347 -0
- package/dist/transactions/group.js.map +1 -0
- package/dist/transactions/index.d.ts +10 -0
- package/dist/transactions/index.js +10 -0
- package/dist/transactions/manifest.d.ts +11 -0
- package/dist/transactions/manifest.d.ts.map +1 -0
- package/dist/transactions/manifest.js +59 -0
- package/dist/transactions/manifest.js.map +1 -0
- package/dist/transactions/sku.d.ts +11 -0
- package/dist/transactions/sku.d.ts.map +1 -0
- package/dist/transactions/sku.js +191 -0
- package/dist/transactions/sku.js.map +1 -0
- package/dist/transactions/staking.d.ts +11 -0
- package/dist/transactions/staking.d.ts.map +1 -0
- package/dist/transactions/staking.js +79 -0
- package/dist/transactions/staking.js.map +1 -0
- package/dist/transactions/utils.d.ts +161 -0
- package/dist/transactions/utils.d.ts.map +1 -0
- package/dist/transactions/utils.js +272 -0
- package/dist/transactions/utils.js.map +1 -0
- package/dist/types.d.ts +390 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +45 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +30 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +53 -0
- package/dist/validation.js.map +1 -0
- package/dist/version.d.ts +5 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +6 -0
- package/dist/version.js.map +1 -0
- package/dist/wallet/index.d.ts +4 -0
- package/dist/wallet/index.js +3 -0
- package/dist/wallet/mnemonic.d.ts +47 -0
- package/dist/wallet/mnemonic.d.ts.map +1 -0
- package/dist/wallet/mnemonic.js +97 -0
- package/dist/wallet/mnemonic.js.map +1 -0
- package/dist/wallet/sign-arbitrary.d.ts +12 -0
- package/dist/wallet/sign-arbitrary.d.ts.map +1 -0
- package/dist/wallet/sign-arbitrary.js +36 -0
- package/dist/wallet/sign-arbitrary.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { ManifestMCPError, ManifestMCPErrorCode } from "../types.js";
|
|
2
|
+
import { DNS_LABEL_RE } from "../validation.js";
|
|
3
|
+
import { fromBech32, fromHex, toHex } from "@cosmjs/encoding";
|
|
4
|
+
//#region src/transactions/utils.ts
|
|
5
|
+
/** Maximum number of arguments allowed */
|
|
6
|
+
const MAX_ARGS = 100;
|
|
7
|
+
/** Maximum meta hash length in bytes (64 bytes for SHA-512) */
|
|
8
|
+
const MAX_META_HASH_BYTES = 64;
|
|
9
|
+
/**
|
|
10
|
+
* Extract a flag value from args array.
|
|
11
|
+
* Returns { value, consumedIndices } or { value: undefined, consumedIndices: [] } if flag not present.
|
|
12
|
+
* Throws if flag is present but value is missing or looks like another flag.
|
|
13
|
+
*
|
|
14
|
+
* @param args - The arguments array to search
|
|
15
|
+
* @param flagName - The flag to look for (e.g., '--memo')
|
|
16
|
+
* @param context - Description for error messages (e.g., 'bank send')
|
|
17
|
+
*/
|
|
18
|
+
function extractFlag(args, flagName, context, errorCode = ManifestMCPErrorCode.TX_FAILED) {
|
|
19
|
+
const flagIndex = args.indexOf(flagName);
|
|
20
|
+
if (flagIndex === -1) return {
|
|
21
|
+
value: void 0,
|
|
22
|
+
consumedIndices: []
|
|
23
|
+
};
|
|
24
|
+
if (args.indexOf(flagName, flagIndex + 1) !== -1) throw new ManifestMCPError(errorCode, `Duplicate ${flagName} flag in ${context}`);
|
|
25
|
+
const value = args[flagIndex + 1];
|
|
26
|
+
if (!value || value.startsWith("--")) throw new ManifestMCPError(errorCode, `${flagName} flag requires a value in ${context}`);
|
|
27
|
+
return {
|
|
28
|
+
value,
|
|
29
|
+
consumedIndices: [flagIndex, flagIndex + 1]
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Extract a valueless boolean flag from args array.
|
|
34
|
+
* Returns { value: true, remainingArgs } if flag is present, { value: false, remainingArgs: args } otherwise.
|
|
35
|
+
*
|
|
36
|
+
* @param args - The arguments array to search
|
|
37
|
+
* @param flagName - The flag to look for (e.g., '--active-only')
|
|
38
|
+
* @returns Object with boolean value and filtered args
|
|
39
|
+
*/
|
|
40
|
+
function extractBooleanFlag(args, flagName) {
|
|
41
|
+
const flagIndex = args.indexOf(flagName);
|
|
42
|
+
if (flagIndex === -1) return {
|
|
43
|
+
value: false,
|
|
44
|
+
remainingArgs: args
|
|
45
|
+
};
|
|
46
|
+
return {
|
|
47
|
+
value: true,
|
|
48
|
+
remainingArgs: args.filter((_, index) => index !== flagIndex)
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Filter args to remove consumed flag indices
|
|
53
|
+
*/
|
|
54
|
+
function filterConsumedArgs(args, consumedIndices) {
|
|
55
|
+
if (consumedIndices.length === 0) return args;
|
|
56
|
+
const consumedSet = new Set(consumedIndices);
|
|
57
|
+
return args.filter((_, index) => !consumedSet.has(index));
|
|
58
|
+
}
|
|
59
|
+
/** Maximum memo length (Cosmos SDK default) */
|
|
60
|
+
const MAX_MEMO_LENGTH = 256;
|
|
61
|
+
/**
|
|
62
|
+
* Parse a colon-separated pair (e.g., "address:amount", "sku:quantity").
|
|
63
|
+
* Throws with helpful error if format is invalid.
|
|
64
|
+
*
|
|
65
|
+
* @param input - The string to parse (e.g., "manifest1abc:1000umfx")
|
|
66
|
+
* @param leftName - Name of the left value for error messages (e.g., "address")
|
|
67
|
+
* @param rightName - Name of the right value for error messages (e.g., "amount")
|
|
68
|
+
* @param context - Context for error messages (e.g., "multi-send pair")
|
|
69
|
+
* @returns Tuple of [left, right] values
|
|
70
|
+
*/
|
|
71
|
+
function parseColonPair(input, leftName, rightName, context) {
|
|
72
|
+
const colonIndex = input.indexOf(":");
|
|
73
|
+
if (colonIndex === -1) throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, `Invalid ${context} format: "${input}". Missing colon separator. Expected format: ${leftName}:${rightName}`);
|
|
74
|
+
if (colonIndex === 0) throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, `Invalid ${context} format: "${input}". Missing ${leftName}. Expected format: ${leftName}:${rightName}`);
|
|
75
|
+
if (colonIndex === input.length - 1) throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, `Invalid ${context} format: "${input}". Missing ${rightName}. Expected format: ${leftName}:${rightName}`);
|
|
76
|
+
return [input.slice(0, colonIndex), input.slice(colonIndex + 1)];
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Validate args array length (max limit)
|
|
80
|
+
*/
|
|
81
|
+
function validateArgsLength(args, context) {
|
|
82
|
+
if (args.length > 100) throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, `Too many arguments for ${context}: ${args.length}. Maximum allowed: 100`);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Validate that required arguments are present.
|
|
86
|
+
* Provides helpful error messages with received vs expected args.
|
|
87
|
+
*
|
|
88
|
+
* @param args - The arguments array to validate
|
|
89
|
+
* @param minCount - Minimum number of required arguments
|
|
90
|
+
* @param expectedNames - Names of expected arguments for error messages
|
|
91
|
+
* @param context - Context for error messages (e.g., 'bank send', 'staking delegate')
|
|
92
|
+
* @param errorCode - Error code to use (defaults to TX_FAILED)
|
|
93
|
+
* @throws ManifestMCPError if args.length < minCount
|
|
94
|
+
*/
|
|
95
|
+
function requireArgs(args, minCount, expectedNames, context, errorCode = ManifestMCPErrorCode.TX_FAILED) {
|
|
96
|
+
if (args.length >= minCount) return;
|
|
97
|
+
const expectedList = expectedNames.slice(0, minCount).join(", ");
|
|
98
|
+
const receivedList = args.length === 0 ? "none" : args.map((a) => `"${a}"`).join(", ");
|
|
99
|
+
throw new ManifestMCPError(errorCode, `${context} requires ${minCount} argument(s): ${expectedList}. Received ${args.length}: ${receivedList}`, {
|
|
100
|
+
expectedArgs: expectedNames.slice(0, minCount),
|
|
101
|
+
receivedArgs: args,
|
|
102
|
+
receivedCount: args.length,
|
|
103
|
+
requiredCount: minCount
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Validate a bech32 address using @cosmjs/encoding
|
|
108
|
+
*/
|
|
109
|
+
function validateAddress(address, fieldName, expectedPrefix) {
|
|
110
|
+
if (!address || address.trim() === "") throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_ADDRESS, `${fieldName} is required`);
|
|
111
|
+
try {
|
|
112
|
+
const { prefix } = fromBech32(address);
|
|
113
|
+
if (expectedPrefix && prefix !== expectedPrefix) throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_ADDRESS, `Invalid ${fieldName}: "${address}". Expected prefix "${expectedPrefix}", got "${prefix}"`);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
if (error instanceof ManifestMCPError) throw error;
|
|
116
|
+
throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_ADDRESS, `Invalid ${fieldName}: "${address}". Not a valid bech32 address.`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Validate memo length
|
|
121
|
+
*/
|
|
122
|
+
function validateMemo(memo) {
|
|
123
|
+
if (memo.length > 256) throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, `Memo too long: ${memo.length} characters. Maximum allowed: 256`);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Parse and validate a hex string into Uint8Array.
|
|
127
|
+
* Uses @cosmjs/encoding for browser compatibility.
|
|
128
|
+
*
|
|
129
|
+
* @param hexString - The hex string to parse
|
|
130
|
+
* @param fieldName - Name of the field for error messages
|
|
131
|
+
* @param maxBytes - Maximum allowed byte length
|
|
132
|
+
* @param errorCode - Error code to use (defaults to TX_FAILED)
|
|
133
|
+
* @returns Uint8Array of the parsed bytes
|
|
134
|
+
*/
|
|
135
|
+
function parseHexBytes(hexString, fieldName, maxBytes, errorCode = ManifestMCPErrorCode.TX_FAILED) {
|
|
136
|
+
if (!hexString || hexString.trim() === "") throw new ManifestMCPError(errorCode, `Invalid ${fieldName}: empty value. Expected a hex string.`);
|
|
137
|
+
if (hexString.length % 2 !== 0) throw new ManifestMCPError(errorCode, `Invalid ${fieldName}: hex string must have even length. Got ${hexString.length} characters.`);
|
|
138
|
+
const byteLength = hexString.length / 2;
|
|
139
|
+
if (byteLength > maxBytes) throw new ManifestMCPError(errorCode, `Invalid ${fieldName}: exceeds maximum ${maxBytes} bytes. Got ${byteLength} bytes (${hexString.length} hex chars).`);
|
|
140
|
+
try {
|
|
141
|
+
return fromHex(hexString);
|
|
142
|
+
} catch (_error) {
|
|
143
|
+
throw new ManifestMCPError(errorCode, `Invalid ${fieldName}: "${hexString}". Must contain only hexadecimal characters (0-9, a-f, A-F).`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Convert Uint8Array to hex string.
|
|
148
|
+
* Uses @cosmjs/encoding for browser compatibility.
|
|
149
|
+
*/
|
|
150
|
+
function bytesToHex(bytes) {
|
|
151
|
+
return toHex(bytes);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Safely parse a string to BigInt with proper error handling and configurable error code.
|
|
155
|
+
* This is the base implementation used by both transaction and query utilities.
|
|
156
|
+
*/
|
|
157
|
+
function parseBigIntWithCode(value, fieldName, errorCode) {
|
|
158
|
+
if (!value || value.trim() === "") throw new ManifestMCPError(errorCode, `Invalid ${fieldName}: empty value. Expected a valid integer.`);
|
|
159
|
+
try {
|
|
160
|
+
return BigInt(value);
|
|
161
|
+
} catch {
|
|
162
|
+
throw new ManifestMCPError(errorCode, `Invalid ${fieldName}: "${value}". Expected a valid integer.`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Safely parse a string to BigInt with proper error handling (for transactions)
|
|
167
|
+
*/
|
|
168
|
+
function parseBigInt(value, fieldName) {
|
|
169
|
+
return parseBigIntWithCode(value, fieldName, ManifestMCPErrorCode.TX_FAILED);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Parse a vote option string to its numeric enum value.
|
|
173
|
+
* Accepts case-insensitive strings or numeric identifiers.
|
|
174
|
+
*
|
|
175
|
+
* @param optionStr - Vote option string (yes, no, abstain, no_with_veto, or 1-4)
|
|
176
|
+
* @param voteOptionEnum - The VoteOption enum object from the relevant cosmos module
|
|
177
|
+
*/
|
|
178
|
+
function parseVoteOption(optionStr, voteOptionEnum) {
|
|
179
|
+
switch (optionStr.toLowerCase()) {
|
|
180
|
+
case "yes":
|
|
181
|
+
case "1": return voteOptionEnum.VOTE_OPTION_YES;
|
|
182
|
+
case "abstain":
|
|
183
|
+
case "2": return voteOptionEnum.VOTE_OPTION_ABSTAIN;
|
|
184
|
+
case "no":
|
|
185
|
+
case "3": return voteOptionEnum.VOTE_OPTION_NO;
|
|
186
|
+
case "no_with_veto":
|
|
187
|
+
case "nowithveto":
|
|
188
|
+
case "4": return voteOptionEnum.VOTE_OPTION_NO_WITH_VETO;
|
|
189
|
+
default: throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, `Invalid vote option: ${optionStr}. Expected: yes, no, abstain, or no_with_veto`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Parse a lease item string in the format "sku-uuid:quantity" or "sku-uuid:quantity:service-name".
|
|
194
|
+
* The service-name is optional and used for stack (multi-service) deployments.
|
|
195
|
+
*
|
|
196
|
+
* @param input - The string to parse (e.g., "sku-123:1" or "sku-123:1:web")
|
|
197
|
+
* @returns Parsed lease item with skuUuid, quantity, and serviceName
|
|
198
|
+
*/
|
|
199
|
+
function parseLeaseItem(input) {
|
|
200
|
+
const parts = input.split(":");
|
|
201
|
+
if (parts.length < 2 || parts.length > 3) throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, `Invalid lease item format: "${input}". Expected sku-uuid:quantity or sku-uuid:quantity:service-name`);
|
|
202
|
+
const [skuUuid, quantityStr, serviceName] = parts;
|
|
203
|
+
if (!skuUuid) throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, `Invalid lease item format: "${input}". Missing sku-uuid.`);
|
|
204
|
+
if (!quantityStr) throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, `Invalid lease item format: "${input}". Missing quantity.`);
|
|
205
|
+
const quantity = parseBigInt(quantityStr, "quantity");
|
|
206
|
+
if (serviceName !== void 0) {
|
|
207
|
+
if (!serviceName) throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, `Invalid lease item format: "${input}". Empty service-name after trailing colon.`);
|
|
208
|
+
if (!DNS_LABEL_RE.test(serviceName)) throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, `Invalid service name: "${serviceName}". Must be a valid RFC 1123 DNS label: 1-63 lowercase alphanumeric characters or hyphens, must not start or end with a hyphen.`);
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
skuUuid,
|
|
212
|
+
quantity,
|
|
213
|
+
serviceName: serviceName ?? ""
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Parse amount string into coin (e.g., "1000umfx" -> { amount: "1000", denom: "umfx" })
|
|
218
|
+
* Supports simple denoms (umfx), IBC denoms (ibc/...), and factory denoms (factory/creator/subdenom)
|
|
219
|
+
*/
|
|
220
|
+
function parseAmount(amountStr) {
|
|
221
|
+
const match = amountStr.match(/^(\d+)([a-zA-Z][a-zA-Z0-9/_-]*)$/);
|
|
222
|
+
if (!match) {
|
|
223
|
+
let hint = "";
|
|
224
|
+
if (!amountStr || amountStr.trim() === "") hint = " Received empty string.";
|
|
225
|
+
else if (amountStr.includes(" ")) hint = " Remove the space between number and denom.";
|
|
226
|
+
else if (amountStr.includes(",")) hint = " Do not use commas in the number.";
|
|
227
|
+
else if (/^\d+$/.test(amountStr)) hint = " Missing denomination (e.g., add \"umfx\" after the number).";
|
|
228
|
+
else if (/^[a-zA-Z]/.test(amountStr)) hint = " Amount must start with a number, not the denomination.";
|
|
229
|
+
throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, `Invalid amount format: "${amountStr}".${hint} Expected format: <number><denom> (e.g., "1000000umfx" or "1000000factory/address/subdenom")`, {
|
|
230
|
+
receivedValue: amountStr,
|
|
231
|
+
expectedFormat: "<number><denom>",
|
|
232
|
+
example: "1000000umfx"
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
amount: match[1],
|
|
237
|
+
denom: match[2]
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Build transaction result from DeliverTxResponse.
|
|
242
|
+
* Throws ManifestMCPError if the transaction failed on-chain (non-zero code).
|
|
243
|
+
*/
|
|
244
|
+
function buildTxResult(module, subcommand, result, waitForConfirmation) {
|
|
245
|
+
if (result.code !== 0) throw new ManifestMCPError(ManifestMCPErrorCode.TX_FAILED, `Transaction ${module} ${subcommand} failed with code ${result.code}: ${result.rawLog || "no details"}`, {
|
|
246
|
+
module,
|
|
247
|
+
subcommand,
|
|
248
|
+
code: result.code,
|
|
249
|
+
transactionHash: result.transactionHash,
|
|
250
|
+
rawLog: result.rawLog,
|
|
251
|
+
height: String(result.height)
|
|
252
|
+
});
|
|
253
|
+
return {
|
|
254
|
+
module,
|
|
255
|
+
subcommand,
|
|
256
|
+
transactionHash: result.transactionHash,
|
|
257
|
+
code: result.code,
|
|
258
|
+
height: String(result.height),
|
|
259
|
+
rawLog: result.rawLog || void 0,
|
|
260
|
+
gasUsed: String(result.gasUsed),
|
|
261
|
+
gasWanted: String(result.gasWanted),
|
|
262
|
+
events: result.events,
|
|
263
|
+
...waitForConfirmation && {
|
|
264
|
+
confirmed: true,
|
|
265
|
+
confirmationHeight: String(result.height)
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
//#endregion
|
|
270
|
+
export { MAX_ARGS, MAX_MEMO_LENGTH, MAX_META_HASH_BYTES, buildTxResult, bytesToHex, extractBooleanFlag, extractFlag, filterConsumedArgs, parseAmount, parseBigInt, parseBigIntWithCode, parseColonPair, parseHexBytes, parseLeaseItem, parseVoteOption, requireArgs, validateAddress, validateArgsLength, validateMemo };
|
|
271
|
+
|
|
272
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","names":[],"sources":["../../src/transactions/utils.ts"],"sourcesContent":["import { fromBech32, fromHex, toHex } from '@cosmjs/encoding';\nimport type { SigningStargateClient } from '@cosmjs/stargate';\nimport {\n type CosmosTxResult,\n ManifestMCPError,\n ManifestMCPErrorCode,\n} from '../types.js';\nimport { DNS_LABEL_RE } from '../validation.js';\n\n/** Maximum number of arguments allowed */\nexport const MAX_ARGS = 100;\n\n/** Maximum meta hash length in bytes (64 bytes for SHA-512) */\nexport const MAX_META_HASH_BYTES = 64;\n\n/**\n * Result from extracting a flag from args\n */\nexport interface ExtractedFlag {\n /** The flag value, or undefined if flag not present */\n value: string | undefined;\n /** Indices consumed by the flag and its value (for filtering) */\n consumedIndices: number[];\n}\n\n/**\n * Extract a flag value from args array.\n * Returns { value, consumedIndices } or { value: undefined, consumedIndices: [] } if flag not present.\n * Throws if flag is present but value is missing or looks like another flag.\n *\n * @param args - The arguments array to search\n * @param flagName - The flag to look for (e.g., '--memo')\n * @param context - Description for error messages (e.g., 'bank send')\n */\nexport function extractFlag(\n args: string[],\n flagName: string,\n context: string,\n errorCode: ManifestMCPErrorCode = ManifestMCPErrorCode.TX_FAILED,\n): ExtractedFlag {\n const flagIndex = args.indexOf(flagName);\n if (flagIndex === -1) {\n return { value: undefined, consumedIndices: [] };\n }\n\n // Detect duplicate flags\n if (args.indexOf(flagName, flagIndex + 1) !== -1) {\n throw new ManifestMCPError(\n errorCode,\n `Duplicate ${flagName} flag in ${context}`,\n );\n }\n\n const value = args[flagIndex + 1];\n if (!value || value.startsWith('--')) {\n throw new ManifestMCPError(\n errorCode,\n `${flagName} flag requires a value in ${context}`,\n );\n }\n\n return { value, consumedIndices: [flagIndex, flagIndex + 1] };\n}\n\n/**\n * Result from extracting a boolean (valueless) flag from args\n */\nexport interface ExtractedBooleanFlag {\n /** Whether the flag was present */\n value: boolean;\n /** Args with the flag removed */\n remainingArgs: string[];\n}\n\n/**\n * Extract a valueless boolean flag from args array.\n * Returns { value: true, remainingArgs } if flag is present, { value: false, remainingArgs: args } otherwise.\n *\n * @param args - The arguments array to search\n * @param flagName - The flag to look for (e.g., '--active-only')\n * @returns Object with boolean value and filtered args\n */\nexport function extractBooleanFlag(\n args: string[],\n flagName: string,\n): ExtractedBooleanFlag {\n const flagIndex = args.indexOf(flagName);\n if (flagIndex === -1) {\n return { value: false, remainingArgs: args };\n }\n const remainingArgs = args.filter((_, index) => index !== flagIndex);\n return { value: true, remainingArgs };\n}\n\n/**\n * Filter args to remove consumed flag indices\n */\nexport function filterConsumedArgs(\n args: string[],\n consumedIndices: number[],\n): string[] {\n if (consumedIndices.length === 0) {\n return args;\n }\n const consumedSet = new Set(consumedIndices);\n return args.filter((_, index) => !consumedSet.has(index));\n}\n\n/** Maximum memo length (Cosmos SDK default) */\nexport const MAX_MEMO_LENGTH = 256;\n\n/**\n * Parse a colon-separated pair (e.g., \"address:amount\", \"sku:quantity\").\n * Throws with helpful error if format is invalid.\n *\n * @param input - The string to parse (e.g., \"manifest1abc:1000umfx\")\n * @param leftName - Name of the left value for error messages (e.g., \"address\")\n * @param rightName - Name of the right value for error messages (e.g., \"amount\")\n * @param context - Context for error messages (e.g., \"multi-send pair\")\n * @returns Tuple of [left, right] values\n */\nexport function parseColonPair(\n input: string,\n leftName: string,\n rightName: string,\n context: string,\n): [string, string] {\n const colonIndex = input.indexOf(':');\n if (colonIndex === -1) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.TX_FAILED,\n `Invalid ${context} format: \"${input}\". Missing colon separator. Expected format: ${leftName}:${rightName}`,\n );\n }\n if (colonIndex === 0) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.TX_FAILED,\n `Invalid ${context} format: \"${input}\". Missing ${leftName}. Expected format: ${leftName}:${rightName}`,\n );\n }\n if (colonIndex === input.length - 1) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.TX_FAILED,\n `Invalid ${context} format: \"${input}\". Missing ${rightName}. Expected format: ${leftName}:${rightName}`,\n );\n }\n return [input.slice(0, colonIndex), input.slice(colonIndex + 1)];\n}\n\n/**\n * Validate args array length (max limit)\n */\nexport function validateArgsLength(args: string[], context: string): void {\n if (args.length > MAX_ARGS) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.TX_FAILED,\n `Too many arguments for ${context}: ${args.length}. Maximum allowed: ${MAX_ARGS}`,\n );\n }\n}\n\n/**\n * Validate that required arguments are present.\n * Provides helpful error messages with received vs expected args.\n *\n * @param args - The arguments array to validate\n * @param minCount - Minimum number of required arguments\n * @param expectedNames - Names of expected arguments for error messages\n * @param context - Context for error messages (e.g., 'bank send', 'staking delegate')\n * @param errorCode - Error code to use (defaults to TX_FAILED)\n * @throws ManifestMCPError if args.length < minCount\n */\nexport function requireArgs(\n args: string[],\n minCount: number,\n expectedNames: string[],\n context: string,\n errorCode: ManifestMCPErrorCode = ManifestMCPErrorCode.TX_FAILED,\n): void {\n if (args.length >= minCount) {\n return;\n }\n\n const expectedList = expectedNames.slice(0, minCount).join(', ');\n const receivedList =\n args.length === 0 ? 'none' : args.map((a) => `\"${a}\"`).join(', ');\n\n throw new ManifestMCPError(\n errorCode,\n `${context} requires ${minCount} argument(s): ${expectedList}. Received ${args.length}: ${receivedList}`,\n {\n expectedArgs: expectedNames.slice(0, minCount),\n receivedArgs: args,\n receivedCount: args.length,\n requiredCount: minCount,\n },\n );\n}\n\n/**\n * Validate a bech32 address using @cosmjs/encoding\n */\nexport function validateAddress(\n address: string,\n fieldName: string,\n expectedPrefix?: string,\n): void {\n if (!address || address.trim() === '') {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_ADDRESS,\n `${fieldName} is required`,\n );\n }\n\n try {\n const { prefix } = fromBech32(address);\n if (expectedPrefix && prefix !== expectedPrefix) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_ADDRESS,\n `Invalid ${fieldName}: \"${address}\". Expected prefix \"${expectedPrefix}\", got \"${prefix}\"`,\n );\n }\n } catch (error) {\n if (error instanceof ManifestMCPError) {\n throw error;\n }\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_ADDRESS,\n `Invalid ${fieldName}: \"${address}\". Not a valid bech32 address.`,\n );\n }\n}\n\n/**\n * Validate memo length\n */\nexport function validateMemo(memo: string): void {\n if (memo.length > MAX_MEMO_LENGTH) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.TX_FAILED,\n `Memo too long: ${memo.length} characters. Maximum allowed: ${MAX_MEMO_LENGTH}`,\n );\n }\n}\n\n/**\n * Parse and validate a hex string into Uint8Array.\n * Uses @cosmjs/encoding for browser compatibility.\n *\n * @param hexString - The hex string to parse\n * @param fieldName - Name of the field for error messages\n * @param maxBytes - Maximum allowed byte length\n * @param errorCode - Error code to use (defaults to TX_FAILED)\n * @returns Uint8Array of the parsed bytes\n */\nexport function parseHexBytes(\n hexString: string,\n fieldName: string,\n maxBytes: number,\n errorCode: ManifestMCPErrorCode = ManifestMCPErrorCode.TX_FAILED,\n): Uint8Array {\n // Check for empty string\n if (!hexString || hexString.trim() === '') {\n throw new ManifestMCPError(\n errorCode,\n `Invalid ${fieldName}: empty value. Expected a hex string.`,\n );\n }\n\n // Check even length (each byte is 2 hex chars)\n if (hexString.length % 2 !== 0) {\n throw new ManifestMCPError(\n errorCode,\n `Invalid ${fieldName}: hex string must have even length. Got ${hexString.length} characters.`,\n );\n }\n\n // Check max length\n const byteLength = hexString.length / 2;\n if (byteLength > maxBytes) {\n throw new ManifestMCPError(\n errorCode,\n `Invalid ${fieldName}: exceeds maximum ${maxBytes} bytes. Got ${byteLength} bytes (${hexString.length} hex chars).`,\n );\n }\n\n // Use @cosmjs/encoding for browser-compatible hex parsing\n try {\n return fromHex(hexString);\n } catch (_error) {\n throw new ManifestMCPError(\n errorCode,\n `Invalid ${fieldName}: \"${hexString}\". Must contain only hexadecimal characters (0-9, a-f, A-F).`,\n );\n }\n}\n\n/**\n * Convert Uint8Array to hex string.\n * Uses @cosmjs/encoding for browser compatibility.\n */\nexport function bytesToHex(bytes: Uint8Array): string {\n return toHex(bytes);\n}\n\n/**\n * Safely parse a string to BigInt with proper error handling and configurable error code.\n * This is the base implementation used by both transaction and query utilities.\n */\nexport function parseBigIntWithCode(\n value: string,\n fieldName: string,\n errorCode: ManifestMCPErrorCode,\n): bigint {\n // Check for empty string explicitly (BigInt('') returns 0n, not an error)\n if (!value || value.trim() === '') {\n throw new ManifestMCPError(\n errorCode,\n `Invalid ${fieldName}: empty value. Expected a valid integer.`,\n );\n }\n\n try {\n return BigInt(value);\n } catch {\n throw new ManifestMCPError(\n errorCode,\n `Invalid ${fieldName}: \"${value}\". Expected a valid integer.`,\n );\n }\n}\n\n/**\n * Safely parse a string to BigInt with proper error handling (for transactions)\n */\nexport function parseBigInt(value: string, fieldName: string): bigint {\n return parseBigIntWithCode(value, fieldName, ManifestMCPErrorCode.TX_FAILED);\n}\n\n/**\n * Interface for VoteOption-like enums from cosmos protobuf modules.\n * Both cosmos.gov.v1.VoteOption and cosmos.group.v1.VoteOption share this shape.\n */\ninterface VoteOptionEnum {\n VOTE_OPTION_YES: number;\n VOTE_OPTION_ABSTAIN: number;\n VOTE_OPTION_NO: number;\n VOTE_OPTION_NO_WITH_VETO: number;\n}\n\n/**\n * Parse a vote option string to its numeric enum value.\n * Accepts case-insensitive strings or numeric identifiers.\n *\n * @param optionStr - Vote option string (yes, no, abstain, no_with_veto, or 1-4)\n * @param voteOptionEnum - The VoteOption enum object from the relevant cosmos module\n */\nexport function parseVoteOption(\n optionStr: string,\n voteOptionEnum: VoteOptionEnum,\n): number {\n const option = optionStr.toLowerCase();\n switch (option) {\n case 'yes':\n case '1':\n return voteOptionEnum.VOTE_OPTION_YES;\n case 'abstain':\n case '2':\n return voteOptionEnum.VOTE_OPTION_ABSTAIN;\n case 'no':\n case '3':\n return voteOptionEnum.VOTE_OPTION_NO;\n case 'no_with_veto':\n case 'nowithveto':\n case '4':\n return voteOptionEnum.VOTE_OPTION_NO_WITH_VETO;\n default:\n throw new ManifestMCPError(\n ManifestMCPErrorCode.TX_FAILED,\n `Invalid vote option: ${optionStr}. Expected: yes, no, abstain, or no_with_veto`,\n );\n }\n}\n\n/**\n * Parsed lease item with optional service name for stack deployments.\n */\nexport interface ParsedLeaseItem {\n skuUuid: string;\n quantity: bigint;\n serviceName: string;\n}\n\n/**\n * Parse a lease item string in the format \"sku-uuid:quantity\" or \"sku-uuid:quantity:service-name\".\n * The service-name is optional and used for stack (multi-service) deployments.\n *\n * @param input - The string to parse (e.g., \"sku-123:1\" or \"sku-123:1:web\")\n * @returns Parsed lease item with skuUuid, quantity, and serviceName\n */\nexport function parseLeaseItem(input: string): ParsedLeaseItem {\n const parts = input.split(':');\n if (parts.length < 2 || parts.length > 3) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.TX_FAILED,\n `Invalid lease item format: \"${input}\". Expected sku-uuid:quantity or sku-uuid:quantity:service-name`,\n );\n }\n\n const [skuUuid, quantityStr, serviceName] = parts;\n\n if (!skuUuid) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.TX_FAILED,\n `Invalid lease item format: \"${input}\". Missing sku-uuid.`,\n );\n }\n\n if (!quantityStr) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.TX_FAILED,\n `Invalid lease item format: \"${input}\". Missing quantity.`,\n );\n }\n\n const quantity = parseBigInt(quantityStr, 'quantity');\n\n if (serviceName !== undefined) {\n if (!serviceName) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.TX_FAILED,\n `Invalid lease item format: \"${input}\". Empty service-name after trailing colon.`,\n );\n }\n if (!DNS_LABEL_RE.test(serviceName)) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.TX_FAILED,\n `Invalid service name: \"${serviceName}\". Must be a valid RFC 1123 DNS label: ` +\n `1-63 lowercase alphanumeric characters or hyphens, must not start or end with a hyphen.`,\n );\n }\n }\n\n return { skuUuid, quantity, serviceName: serviceName ?? '' };\n}\n\n/**\n * Parse amount string into coin (e.g., \"1000umfx\" -> { amount: \"1000\", denom: \"umfx\" })\n * Supports simple denoms (umfx), IBC denoms (ibc/...), and factory denoms (factory/creator/subdenom)\n */\nexport function parseAmount(amountStr: string): {\n amount: string;\n denom: string;\n} {\n // Regex supports alphanumeric denoms with slashes, underscores, and hyphens for IBC/factory denoms\n const match = amountStr.match(/^(\\d+)([a-zA-Z][a-zA-Z0-9/_-]*)$/);\n if (!match) {\n // Provide specific hints based on common mistakes\n let hint = '';\n if (!amountStr || amountStr.trim() === '') {\n hint = ' Received empty string.';\n } else if (amountStr.includes(' ')) {\n hint = ' Remove the space between number and denom.';\n } else if (amountStr.includes(',')) {\n hint = ' Do not use commas in the number.';\n } else if (/^\\d+$/.test(amountStr)) {\n hint = ' Missing denomination (e.g., add \"umfx\" after the number).';\n } else if (/^[a-zA-Z]/.test(amountStr)) {\n hint = ' Amount must start with a number, not the denomination.';\n }\n\n throw new ManifestMCPError(\n ManifestMCPErrorCode.TX_FAILED,\n `Invalid amount format: \"${amountStr}\".${hint} Expected format: <number><denom> (e.g., \"1000000umfx\" or \"1000000factory/address/subdenom\")`,\n {\n receivedValue: amountStr,\n expectedFormat: '<number><denom>',\n example: '1000000umfx',\n },\n );\n }\n return { amount: match[1], denom: match[2] };\n}\n\n/**\n * Build transaction result from DeliverTxResponse.\n * Throws ManifestMCPError if the transaction failed on-chain (non-zero code).\n */\nexport function buildTxResult(\n module: string,\n subcommand: string,\n result: Awaited<ReturnType<SigningStargateClient['signAndBroadcast']>>,\n waitForConfirmation: boolean,\n): CosmosTxResult {\n if (result.code !== 0) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.TX_FAILED,\n `Transaction ${module} ${subcommand} failed with code ${result.code}: ${result.rawLog || 'no details'}`,\n {\n module,\n subcommand,\n code: result.code,\n transactionHash: result.transactionHash,\n rawLog: result.rawLog,\n height: String(result.height),\n },\n );\n }\n\n return {\n module,\n subcommand,\n transactionHash: result.transactionHash,\n code: result.code,\n height: String(result.height),\n rawLog: result.rawLog || undefined,\n gasUsed: String(result.gasUsed),\n gasWanted: String(result.gasWanted),\n events: result.events,\n ...(waitForConfirmation && {\n confirmed: true,\n confirmationHeight: String(result.height),\n }),\n };\n}\n"],"mappings":";;;;;AAUA,MAAa,WAAW;;AAGxB,MAAa,sBAAsB;;;;;;;;;;AAqBnC,SAAgB,YACd,MACA,UACA,SACA,YAAkC,qBAAqB,WACxC;CACf,MAAM,YAAY,KAAK,QAAQ,SAAS;AACxC,KAAI,cAAc,GAChB,QAAO;EAAE,OAAO,KAAA;EAAW,iBAAiB,EAAE;EAAE;AAIlD,KAAI,KAAK,QAAQ,UAAU,YAAY,EAAE,KAAK,GAC5C,OAAM,IAAI,iBACR,WACA,aAAa,SAAS,WAAW,UAClC;CAGH,MAAM,QAAQ,KAAK,YAAY;AAC/B,KAAI,CAAC,SAAS,MAAM,WAAW,KAAK,CAClC,OAAM,IAAI,iBACR,WACA,GAAG,SAAS,4BAA4B,UACzC;AAGH,QAAO;EAAE;EAAO,iBAAiB,CAAC,WAAW,YAAY,EAAE;EAAE;;;;;;;;;;AAqB/D,SAAgB,mBACd,MACA,UACsB;CACtB,MAAM,YAAY,KAAK,QAAQ,SAAS;AACxC,KAAI,cAAc,GAChB,QAAO;EAAE,OAAO;EAAO,eAAe;EAAM;AAG9C,QAAO;EAAE,OAAO;EAAM,eADA,KAAK,QAAQ,GAAG,UAAU,UAAU,UAAU;EAC/B;;;;;AAMvC,SAAgB,mBACd,MACA,iBACU;AACV,KAAI,gBAAgB,WAAW,EAC7B,QAAO;CAET,MAAM,cAAc,IAAI,IAAI,gBAAgB;AAC5C,QAAO,KAAK,QAAQ,GAAG,UAAU,CAAC,YAAY,IAAI,MAAM,CAAC;;;AAI3D,MAAa,kBAAkB;;;;;;;;;;;AAY/B,SAAgB,eACd,OACA,UACA,WACA,SACkB;CAClB,MAAM,aAAa,MAAM,QAAQ,IAAI;AACrC,KAAI,eAAe,GACjB,OAAM,IAAI,iBACR,qBAAqB,WACrB,WAAW,QAAQ,YAAY,MAAM,+CAA+C,SAAS,GAAG,YACjG;AAEH,KAAI,eAAe,EACjB,OAAM,IAAI,iBACR,qBAAqB,WACrB,WAAW,QAAQ,YAAY,MAAM,aAAa,SAAS,qBAAqB,SAAS,GAAG,YAC7F;AAEH,KAAI,eAAe,MAAM,SAAS,EAChC,OAAM,IAAI,iBACR,qBAAqB,WACrB,WAAW,QAAQ,YAAY,MAAM,aAAa,UAAU,qBAAqB,SAAS,GAAG,YAC9F;AAEH,QAAO,CAAC,MAAM,MAAM,GAAG,WAAW,EAAE,MAAM,MAAM,aAAa,EAAE,CAAC;;;;;AAMlE,SAAgB,mBAAmB,MAAgB,SAAuB;AACxE,KAAI,KAAK,SAAA,IACP,OAAM,IAAI,iBACR,qBAAqB,WACrB,0BAA0B,QAAQ,IAAI,KAAK,OAAO,wBACnD;;;;;;;;;;;;;AAeL,SAAgB,YACd,MACA,UACA,eACA,SACA,YAAkC,qBAAqB,WACjD;AACN,KAAI,KAAK,UAAU,SACjB;CAGF,MAAM,eAAe,cAAc,MAAM,GAAG,SAAS,CAAC,KAAK,KAAK;CAChE,MAAM,eACJ,KAAK,WAAW,IAAI,SAAS,KAAK,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK;AAEnE,OAAM,IAAI,iBACR,WACA,GAAG,QAAQ,YAAY,SAAS,gBAAgB,aAAa,aAAa,KAAK,OAAO,IAAI,gBAC1F;EACE,cAAc,cAAc,MAAM,GAAG,SAAS;EAC9C,cAAc;EACd,eAAe,KAAK;EACpB,eAAe;EAChB,CACF;;;;;AAMH,SAAgB,gBACd,SACA,WACA,gBACM;AACN,KAAI,CAAC,WAAW,QAAQ,MAAM,KAAK,GACjC,OAAM,IAAI,iBACR,qBAAqB,iBACrB,GAAG,UAAU,cACd;AAGH,KAAI;EACF,MAAM,EAAE,WAAW,WAAW,QAAQ;AACtC,MAAI,kBAAkB,WAAW,eAC/B,OAAM,IAAI,iBACR,qBAAqB,iBACrB,WAAW,UAAU,KAAK,QAAQ,sBAAsB,eAAe,UAAU,OAAO,GACzF;UAEI,OAAO;AACd,MAAI,iBAAiB,iBACnB,OAAM;AAER,QAAM,IAAI,iBACR,qBAAqB,iBACrB,WAAW,UAAU,KAAK,QAAQ,gCACnC;;;;;;AAOL,SAAgB,aAAa,MAAoB;AAC/C,KAAI,KAAK,SAAA,IACP,OAAM,IAAI,iBACR,qBAAqB,WACrB,kBAAkB,KAAK,OAAO,mCAC/B;;;;;;;;;;;;AAcL,SAAgB,cACd,WACA,WACA,UACA,YAAkC,qBAAqB,WAC3C;AAEZ,KAAI,CAAC,aAAa,UAAU,MAAM,KAAK,GACrC,OAAM,IAAI,iBACR,WACA,WAAW,UAAU,uCACtB;AAIH,KAAI,UAAU,SAAS,MAAM,EAC3B,OAAM,IAAI,iBACR,WACA,WAAW,UAAU,0CAA0C,UAAU,OAAO,cACjF;CAIH,MAAM,aAAa,UAAU,SAAS;AACtC,KAAI,aAAa,SACf,OAAM,IAAI,iBACR,WACA,WAAW,UAAU,oBAAoB,SAAS,cAAc,WAAW,UAAU,UAAU,OAAO,cACvG;AAIH,KAAI;AACF,SAAO,QAAQ,UAAU;UAClB,QAAQ;AACf,QAAM,IAAI,iBACR,WACA,WAAW,UAAU,KAAK,UAAU,8DACrC;;;;;;;AAQL,SAAgB,WAAW,OAA2B;AACpD,QAAO,MAAM,MAAM;;;;;;AAOrB,SAAgB,oBACd,OACA,WACA,WACQ;AAER,KAAI,CAAC,SAAS,MAAM,MAAM,KAAK,GAC7B,OAAM,IAAI,iBACR,WACA,WAAW,UAAU,0CACtB;AAGH,KAAI;AACF,SAAO,OAAO,MAAM;SACd;AACN,QAAM,IAAI,iBACR,WACA,WAAW,UAAU,KAAK,MAAM,8BACjC;;;;;;AAOL,SAAgB,YAAY,OAAe,WAA2B;AACpE,QAAO,oBAAoB,OAAO,WAAW,qBAAqB,UAAU;;;;;;;;;AAqB9E,SAAgB,gBACd,WACA,gBACQ;AAER,SADe,UAAU,aAAa,EACtC;EACE,KAAK;EACL,KAAK,IACH,QAAO,eAAe;EACxB,KAAK;EACL,KAAK,IACH,QAAO,eAAe;EACxB,KAAK;EACL,KAAK,IACH,QAAO,eAAe;EACxB,KAAK;EACL,KAAK;EACL,KAAK,IACH,QAAO,eAAe;EACxB,QACE,OAAM,IAAI,iBACR,qBAAqB,WACrB,wBAAwB,UAAU,+CACnC;;;;;;;;;;AAoBP,SAAgB,eAAe,OAAgC;CAC7D,MAAM,QAAQ,MAAM,MAAM,IAAI;AAC9B,KAAI,MAAM,SAAS,KAAK,MAAM,SAAS,EACrC,OAAM,IAAI,iBACR,qBAAqB,WACrB,+BAA+B,MAAM,iEACtC;CAGH,MAAM,CAAC,SAAS,aAAa,eAAe;AAE5C,KAAI,CAAC,QACH,OAAM,IAAI,iBACR,qBAAqB,WACrB,+BAA+B,MAAM,sBACtC;AAGH,KAAI,CAAC,YACH,OAAM,IAAI,iBACR,qBAAqB,WACrB,+BAA+B,MAAM,sBACtC;CAGH,MAAM,WAAW,YAAY,aAAa,WAAW;AAErD,KAAI,gBAAgB,KAAA,GAAW;AAC7B,MAAI,CAAC,YACH,OAAM,IAAI,iBACR,qBAAqB,WACrB,+BAA+B,MAAM,6CACtC;AAEH,MAAI,CAAC,aAAa,KAAK,YAAY,CACjC,OAAM,IAAI,iBACR,qBAAqB,WACrB,0BAA0B,YAAY,gIAEvC;;AAIL,QAAO;EAAE;EAAS;EAAU,aAAa,eAAe;EAAI;;;;;;AAO9D,SAAgB,YAAY,WAG1B;CAEA,MAAM,QAAQ,UAAU,MAAM,mCAAmC;AACjE,KAAI,CAAC,OAAO;EAEV,IAAI,OAAO;AACX,MAAI,CAAC,aAAa,UAAU,MAAM,KAAK,GACrC,QAAO;WACE,UAAU,SAAS,IAAI,CAChC,QAAO;WACE,UAAU,SAAS,IAAI,CAChC,QAAO;WACE,QAAQ,KAAK,UAAU,CAChC,QAAO;WACE,YAAY,KAAK,UAAU,CACpC,QAAO;AAGT,QAAM,IAAI,iBACR,qBAAqB,WACrB,2BAA2B,UAAU,IAAI,KAAK,+FAC9C;GACE,eAAe;GACf,gBAAgB;GAChB,SAAS;GACV,CACF;;AAEH,QAAO;EAAE,QAAQ,MAAM;EAAI,OAAO,MAAM;EAAI;;;;;;AAO9C,SAAgB,cACd,QACA,YACA,QACA,qBACgB;AAChB,KAAI,OAAO,SAAS,EAClB,OAAM,IAAI,iBACR,qBAAqB,WACrB,eAAe,OAAO,GAAG,WAAW,oBAAoB,OAAO,KAAK,IAAI,OAAO,UAAU,gBACzF;EACE;EACA;EACA,MAAM,OAAO;EACb,iBAAiB,OAAO;EACxB,QAAQ,OAAO;EACf,QAAQ,OAAO,OAAO,OAAO;EAC9B,CACF;AAGH,QAAO;EACL;EACA;EACA,iBAAiB,OAAO;EACxB,MAAM,OAAO;EACb,QAAQ,OAAO,OAAO,OAAO;EAC7B,QAAQ,OAAO,UAAU,KAAA;EACzB,SAAS,OAAO,OAAO,QAAQ;EAC/B,WAAW,OAAO,OAAO,UAAU;EACnC,QAAQ,OAAO;EACf,GAAI,uBAAuB;GACzB,WAAW;GACX,oBAAoB,OAAO,OAAO,OAAO;GAC1C;EACF"}
|