@manifest-network/manifest-mcp-browser 0.1.5 → 0.1.7
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/CLAUDE.md +7 -4
- package/dist/modules.d.ts.map +1 -1
- package/dist/modules.js +75 -0
- package/dist/modules.js.map +1 -1
- package/dist/modules.test.js +4 -0
- package/dist/modules.test.js.map +1 -1
- package/dist/queries/group.d.ts +12 -0
- package/dist/queries/group.d.ts.map +1 -0
- package/dist/queries/group.js +107 -0
- package/dist/queries/group.js.map +1 -0
- package/dist/queries/index.d.ts +1 -0
- package/dist/queries/index.d.ts.map +1 -1
- package/dist/queries/index.js +1 -0
- package/dist/queries/index.js.map +1 -1
- package/dist/queries/sku.d.ts +13 -0
- package/dist/queries/sku.d.ts.map +1 -0
- package/dist/queries/sku.js +60 -0
- package/dist/queries/sku.js.map +1 -0
- package/dist/queries/utils.d.ts +3 -0
- package/dist/queries/utils.d.ts.map +1 -1
- package/dist/queries/utils.js +2 -1
- package/dist/queries/utils.js.map +1 -1
- package/dist/transactions/billing.d.ts.map +1 -1
- package/dist/transactions/billing.js +97 -4
- package/dist/transactions/billing.js.map +1 -1
- package/dist/transactions/gov.d.ts.map +1 -1
- package/dist/transactions/gov.js +3 -26
- package/dist/transactions/gov.js.map +1 -1
- package/dist/transactions/group.d.ts +7 -0
- package/dist/transactions/group.d.ts.map +1 -0
- package/dist/transactions/group.js +339 -0
- package/dist/transactions/group.js.map +1 -0
- package/dist/transactions/index.d.ts +1 -0
- package/dist/transactions/index.d.ts.map +1 -1
- package/dist/transactions/index.js +1 -0
- package/dist/transactions/index.js.map +1 -1
- package/dist/transactions/sku.d.ts +7 -0
- package/dist/transactions/sku.d.ts.map +1 -0
- package/dist/transactions/sku.js +184 -0
- package/dist/transactions/sku.js.map +1 -0
- package/dist/transactions/utils.d.ts +39 -0
- package/dist/transactions/utils.d.ts.map +1 -1
- package/dist/transactions/utils.js +45 -0
- package/dist/transactions/utils.js.map +1 -1
- package/dist/transactions/utils.test.js +97 -1
- package/dist/transactions/utils.test.js.map +1 -1
- package/dist/types.d.ts +46 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/modules.test.ts +4 -0
- package/src/modules.ts +75 -0
- package/src/queries/group.ts +146 -0
- package/src/queries/index.ts +1 -0
- package/src/queries/sku.ts +85 -0
- package/src/queries/utils.ts +4 -1
- package/src/transactions/billing.ts +126 -5
- package/src/transactions/gov.ts +3 -30
- package/src/transactions/group.ts +458 -0
- package/src/transactions/index.ts +1 -0
- package/src/transactions/sku.ts +232 -0
- package/src/transactions/utils.test.ts +109 -1
- package/src/transactions/utils.ts +72 -0
- package/src/types.ts +78 -1
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { SigningStargateClient } from '@cosmjs/stargate';
|
|
2
|
+
import { liftedinit } from '@manifest-network/manifestjs';
|
|
3
|
+
import { CosmosTxResult, ManifestMCPError, ManifestMCPErrorCode } from '../types.js';
|
|
4
|
+
import { parseAmount, buildTxResult, validateAddress, validateArgsLength, extractFlag, filterConsumedArgs, requireArgs, parseHexBytes, MAX_META_HASH_BYTES } from './utils.js';
|
|
5
|
+
import { throwUnsupportedSubcommand } from '../modules.js';
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
MsgCreateProvider, MsgUpdateProvider, MsgDeactivateProvider,
|
|
9
|
+
MsgCreateSKU, MsgUpdateSKU, MsgDeactivateSKU,
|
|
10
|
+
MsgUpdateParams,
|
|
11
|
+
Unit,
|
|
12
|
+
} = liftedinit.sku.v1;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Parse a unit string to the Unit enum value.
|
|
16
|
+
* Accepts 'per-hour' or 'per-day'.
|
|
17
|
+
*/
|
|
18
|
+
function parseUnit(value: string): number {
|
|
19
|
+
switch (value.toLowerCase()) {
|
|
20
|
+
case 'per-hour':
|
|
21
|
+
return Unit.UNIT_PER_HOUR;
|
|
22
|
+
case 'per-day':
|
|
23
|
+
return Unit.UNIT_PER_DAY;
|
|
24
|
+
default:
|
|
25
|
+
throw new ManifestMCPError(
|
|
26
|
+
ManifestMCPErrorCode.TX_FAILED,
|
|
27
|
+
`Invalid unit: "${value}". Expected "per-hour" or "per-day".`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Parse a boolean string ('true' or 'false').
|
|
34
|
+
*/
|
|
35
|
+
function parseBooleanString(value: string, fieldName: string): boolean {
|
|
36
|
+
const lower = value.toLowerCase();
|
|
37
|
+
if (lower === 'true') return true;
|
|
38
|
+
if (lower === 'false') return false;
|
|
39
|
+
throw new ManifestMCPError(
|
|
40
|
+
ManifestMCPErrorCode.TX_FAILED,
|
|
41
|
+
`Invalid ${fieldName}: "${value}". Expected "true" or "false".`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Route SKU transaction to appropriate handler
|
|
47
|
+
*/
|
|
48
|
+
export async function routeSkuTransaction(
|
|
49
|
+
client: SigningStargateClient,
|
|
50
|
+
senderAddress: string,
|
|
51
|
+
subcommand: string,
|
|
52
|
+
args: string[],
|
|
53
|
+
waitForConfirmation: boolean
|
|
54
|
+
): Promise<CosmosTxResult> {
|
|
55
|
+
validateArgsLength(args, 'sku transaction');
|
|
56
|
+
|
|
57
|
+
switch (subcommand) {
|
|
58
|
+
case 'create-provider': {
|
|
59
|
+
// Parse optional --meta-hash flag
|
|
60
|
+
const { value: metaHashHex, consumedIndices } = extractFlag(args, '--meta-hash', 'sku create-provider');
|
|
61
|
+
const metaHash = metaHashHex ? parseHexBytes(metaHashHex, 'meta-hash', MAX_META_HASH_BYTES) : new Uint8Array();
|
|
62
|
+
const positionalArgs = filterConsumedArgs(args, consumedIndices);
|
|
63
|
+
|
|
64
|
+
requireArgs(positionalArgs, 3, ['address', 'payout-address', 'api-url'], 'sku create-provider');
|
|
65
|
+
const [address, payoutAddress, apiUrl] = positionalArgs;
|
|
66
|
+
validateAddress(address, 'address');
|
|
67
|
+
validateAddress(payoutAddress, 'payout address');
|
|
68
|
+
|
|
69
|
+
const msg = {
|
|
70
|
+
typeUrl: '/liftedinit.sku.v1.MsgCreateProvider',
|
|
71
|
+
value: MsgCreateProvider.fromPartial({
|
|
72
|
+
authority: senderAddress,
|
|
73
|
+
address,
|
|
74
|
+
payoutAddress,
|
|
75
|
+
metaHash,
|
|
76
|
+
apiUrl,
|
|
77
|
+
}),
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const result = await client.signAndBroadcast(senderAddress, [msg], 'auto');
|
|
81
|
+
return buildTxResult('sku', 'create-provider', result, waitForConfirmation);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
case 'update-provider': {
|
|
85
|
+
// Parse optional flags
|
|
86
|
+
const metaHashFlag = extractFlag(args, '--meta-hash', 'sku update-provider');
|
|
87
|
+
const activeFlag = extractFlag(args, '--active', 'sku update-provider');
|
|
88
|
+
const allConsumed = [...metaHashFlag.consumedIndices, ...activeFlag.consumedIndices];
|
|
89
|
+
const positionalArgs = filterConsumedArgs(args, allConsumed);
|
|
90
|
+
|
|
91
|
+
requireArgs(positionalArgs, 4, ['provider-uuid', 'address', 'payout-address', 'api-url'], 'sku update-provider');
|
|
92
|
+
const [uuid, address, payoutAddress, apiUrl] = positionalArgs;
|
|
93
|
+
validateAddress(address, 'address');
|
|
94
|
+
validateAddress(payoutAddress, 'payout address');
|
|
95
|
+
|
|
96
|
+
const metaHash = metaHashFlag.value ? parseHexBytes(metaHashFlag.value, 'meta-hash', MAX_META_HASH_BYTES) : new Uint8Array();
|
|
97
|
+
const active = activeFlag.value ? parseBooleanString(activeFlag.value, 'active') : true;
|
|
98
|
+
|
|
99
|
+
const msg = {
|
|
100
|
+
typeUrl: '/liftedinit.sku.v1.MsgUpdateProvider',
|
|
101
|
+
value: MsgUpdateProvider.fromPartial({
|
|
102
|
+
authority: senderAddress,
|
|
103
|
+
uuid,
|
|
104
|
+
address,
|
|
105
|
+
payoutAddress,
|
|
106
|
+
metaHash,
|
|
107
|
+
active,
|
|
108
|
+
apiUrl,
|
|
109
|
+
}),
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const result = await client.signAndBroadcast(senderAddress, [msg], 'auto');
|
|
113
|
+
return buildTxResult('sku', 'update-provider', result, waitForConfirmation);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
case 'deactivate-provider': {
|
|
117
|
+
requireArgs(args, 1, ['provider-uuid'], 'sku deactivate-provider');
|
|
118
|
+
const [uuid] = args;
|
|
119
|
+
|
|
120
|
+
const msg = {
|
|
121
|
+
typeUrl: '/liftedinit.sku.v1.MsgDeactivateProvider',
|
|
122
|
+
value: MsgDeactivateProvider.fromPartial({
|
|
123
|
+
authority: senderAddress,
|
|
124
|
+
uuid,
|
|
125
|
+
}),
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const result = await client.signAndBroadcast(senderAddress, [msg], 'auto');
|
|
129
|
+
return buildTxResult('sku', 'deactivate-provider', result, waitForConfirmation);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
case 'create-sku': {
|
|
133
|
+
// Parse optional --meta-hash flag
|
|
134
|
+
const { value: metaHashHex, consumedIndices } = extractFlag(args, '--meta-hash', 'sku create-sku');
|
|
135
|
+
const metaHash = metaHashHex ? parseHexBytes(metaHashHex, 'meta-hash', MAX_META_HASH_BYTES) : new Uint8Array();
|
|
136
|
+
const positionalArgs = filterConsumedArgs(args, consumedIndices);
|
|
137
|
+
|
|
138
|
+
requireArgs(positionalArgs, 4, ['provider-uuid', 'name', 'unit', 'base-price'], 'sku create-sku');
|
|
139
|
+
const [providerUuid, name, unitStr, basePriceStr] = positionalArgs;
|
|
140
|
+
|
|
141
|
+
const unit = parseUnit(unitStr);
|
|
142
|
+
const { amount, denom } = parseAmount(basePriceStr);
|
|
143
|
+
|
|
144
|
+
const msg = {
|
|
145
|
+
typeUrl: '/liftedinit.sku.v1.MsgCreateSKU',
|
|
146
|
+
value: MsgCreateSKU.fromPartial({
|
|
147
|
+
authority: senderAddress,
|
|
148
|
+
providerUuid,
|
|
149
|
+
name,
|
|
150
|
+
unit,
|
|
151
|
+
basePrice: { denom, amount },
|
|
152
|
+
metaHash,
|
|
153
|
+
}),
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const result = await client.signAndBroadcast(senderAddress, [msg], 'auto');
|
|
157
|
+
return buildTxResult('sku', 'create-sku', result, waitForConfirmation);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
case 'update-sku': {
|
|
161
|
+
// Parse optional flags
|
|
162
|
+
const metaHashFlag = extractFlag(args, '--meta-hash', 'sku update-sku');
|
|
163
|
+
const activeFlag = extractFlag(args, '--active', 'sku update-sku');
|
|
164
|
+
const allConsumed = [...metaHashFlag.consumedIndices, ...activeFlag.consumedIndices];
|
|
165
|
+
const positionalArgs = filterConsumedArgs(args, allConsumed);
|
|
166
|
+
|
|
167
|
+
requireArgs(positionalArgs, 5, ['sku-uuid', 'provider-uuid', 'name', 'unit', 'base-price'], 'sku update-sku');
|
|
168
|
+
const [uuid, providerUuid, name, unitStr, basePriceStr] = positionalArgs;
|
|
169
|
+
|
|
170
|
+
const unit = parseUnit(unitStr);
|
|
171
|
+
const { amount, denom } = parseAmount(basePriceStr);
|
|
172
|
+
const metaHash = metaHashFlag.value ? parseHexBytes(metaHashFlag.value, 'meta-hash', MAX_META_HASH_BYTES) : new Uint8Array();
|
|
173
|
+
const active = activeFlag.value ? parseBooleanString(activeFlag.value, 'active') : true;
|
|
174
|
+
|
|
175
|
+
const msg = {
|
|
176
|
+
typeUrl: '/liftedinit.sku.v1.MsgUpdateSKU',
|
|
177
|
+
value: MsgUpdateSKU.fromPartial({
|
|
178
|
+
authority: senderAddress,
|
|
179
|
+
uuid,
|
|
180
|
+
providerUuid,
|
|
181
|
+
name,
|
|
182
|
+
unit,
|
|
183
|
+
basePrice: { denom, amount },
|
|
184
|
+
metaHash,
|
|
185
|
+
active,
|
|
186
|
+
}),
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const result = await client.signAndBroadcast(senderAddress, [msg], 'auto');
|
|
190
|
+
return buildTxResult('sku', 'update-sku', result, waitForConfirmation);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
case 'deactivate-sku': {
|
|
194
|
+
requireArgs(args, 1, ['sku-uuid'], 'sku deactivate-sku');
|
|
195
|
+
const [uuid] = args;
|
|
196
|
+
|
|
197
|
+
const msg = {
|
|
198
|
+
typeUrl: '/liftedinit.sku.v1.MsgDeactivateSKU',
|
|
199
|
+
value: MsgDeactivateSKU.fromPartial({
|
|
200
|
+
authority: senderAddress,
|
|
201
|
+
uuid,
|
|
202
|
+
}),
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const result = await client.signAndBroadcast(senderAddress, [msg], 'auto');
|
|
206
|
+
return buildTxResult('sku', 'deactivate-sku', result, waitForConfirmation);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
case 'update-params': {
|
|
210
|
+
requireArgs(args, 1, ['allowed-address'], 'sku update-params');
|
|
211
|
+
for (const addr of args) {
|
|
212
|
+
validateAddress(addr, 'allowed address');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const msg = {
|
|
216
|
+
typeUrl: '/liftedinit.sku.v1.MsgUpdateParams',
|
|
217
|
+
value: MsgUpdateParams.fromPartial({
|
|
218
|
+
authority: senderAddress,
|
|
219
|
+
params: {
|
|
220
|
+
allowedList: args,
|
|
221
|
+
},
|
|
222
|
+
}),
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const result = await client.signAndBroadcast(senderAddress, [msg], 'auto');
|
|
226
|
+
return buildTxResult('sku', 'update-params', result, waitForConfirmation);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
default:
|
|
230
|
+
throwUnsupportedSubcommand('tx', 'sku', subcommand);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
2
|
import { toBech32 } from '@cosmjs/encoding';
|
|
3
|
-
import { parseAmount, parseBigInt, extractFlag, filterConsumedArgs, parseColonPair, validateAddress, validateMemo, validateArgsLength, requireArgs, parseHexBytes, bytesToHex } from './utils.js';
|
|
3
|
+
import { parseAmount, parseBigInt, extractFlag, extractBooleanFlag, filterConsumedArgs, parseColonPair, validateAddress, validateMemo, validateArgsLength, requireArgs, parseHexBytes, bytesToHex, parseVoteOption } from './utils.js';
|
|
4
4
|
import { ManifestMCPError, ManifestMCPErrorCode } from '../types.js';
|
|
5
5
|
|
|
6
6
|
describe('parseAmount', () => {
|
|
@@ -516,3 +516,111 @@ describe('bytesToHex', () => {
|
|
|
516
516
|
expect(bytesToHex(bytes)).toBe(original);
|
|
517
517
|
});
|
|
518
518
|
});
|
|
519
|
+
|
|
520
|
+
describe('extractBooleanFlag', () => {
|
|
521
|
+
it('should return true and filtered args when flag is present', () => {
|
|
522
|
+
const result = extractBooleanFlag(['arg1', '--active-only', 'arg2'], '--active-only');
|
|
523
|
+
expect(result.value).toBe(true);
|
|
524
|
+
expect(result.remainingArgs).toEqual(['arg1', 'arg2']);
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
it('should return false and original args when flag is absent', () => {
|
|
528
|
+
const args = ['arg1', 'arg2'];
|
|
529
|
+
const result = extractBooleanFlag(args, '--active-only');
|
|
530
|
+
expect(result.value).toBe(false);
|
|
531
|
+
expect(result.remainingArgs).toEqual(['arg1', 'arg2']);
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
it('should handle flag at the beginning', () => {
|
|
535
|
+
const result = extractBooleanFlag(['--flag', 'a', 'b'], '--flag');
|
|
536
|
+
expect(result.value).toBe(true);
|
|
537
|
+
expect(result.remainingArgs).toEqual(['a', 'b']);
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
it('should handle flag at the end', () => {
|
|
541
|
+
const result = extractBooleanFlag(['a', 'b', '--flag'], '--flag');
|
|
542
|
+
expect(result.value).toBe(true);
|
|
543
|
+
expect(result.remainingArgs).toEqual(['a', 'b']);
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
it('should handle flag as only argument', () => {
|
|
547
|
+
const result = extractBooleanFlag(['--flag'], '--flag');
|
|
548
|
+
expect(result.value).toBe(true);
|
|
549
|
+
expect(result.remainingArgs).toEqual([]);
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
it('should handle empty args', () => {
|
|
553
|
+
const result = extractBooleanFlag([], '--flag');
|
|
554
|
+
expect(result.value).toBe(false);
|
|
555
|
+
expect(result.remainingArgs).toEqual([]);
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
it('should only remove the first occurrence', () => {
|
|
559
|
+
const result = extractBooleanFlag(['--flag', 'a', '--flag'], '--flag');
|
|
560
|
+
expect(result.value).toBe(true);
|
|
561
|
+
// indexOf finds the first occurrence at index 0, only that is removed
|
|
562
|
+
expect(result.remainingArgs).toEqual(['a', '--flag']);
|
|
563
|
+
});
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
describe('parseVoteOption', () => {
|
|
567
|
+
// Mock VoteOption enum matching the cosmos.gov.v1 / cosmos.group.v1 shape
|
|
568
|
+
const mockVoteOption = {
|
|
569
|
+
VOTE_OPTION_YES: 1,
|
|
570
|
+
VOTE_OPTION_ABSTAIN: 2,
|
|
571
|
+
VOTE_OPTION_NO: 3,
|
|
572
|
+
VOTE_OPTION_NO_WITH_VETO: 4,
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
it('should parse string vote options (case-insensitive)', () => {
|
|
576
|
+
expect(parseVoteOption('yes', mockVoteOption)).toBe(1);
|
|
577
|
+
expect(parseVoteOption('YES', mockVoteOption)).toBe(1);
|
|
578
|
+
expect(parseVoteOption('Yes', mockVoteOption)).toBe(1);
|
|
579
|
+
expect(parseVoteOption('no', mockVoteOption)).toBe(3);
|
|
580
|
+
expect(parseVoteOption('abstain', mockVoteOption)).toBe(2);
|
|
581
|
+
expect(parseVoteOption('no_with_veto', mockVoteOption)).toBe(4);
|
|
582
|
+
expect(parseVoteOption('nowithveto', mockVoteOption)).toBe(4);
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
it('should parse numeric vote options', () => {
|
|
586
|
+
expect(parseVoteOption('1', mockVoteOption)).toBe(1);
|
|
587
|
+
expect(parseVoteOption('2', mockVoteOption)).toBe(2);
|
|
588
|
+
expect(parseVoteOption('3', mockVoteOption)).toBe(3);
|
|
589
|
+
expect(parseVoteOption('4', mockVoteOption)).toBe(4);
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
it('should use enum values from the provided object', () => {
|
|
593
|
+
// Verify it actually uses the enum values, not hardcoded numbers
|
|
594
|
+
const customEnum = {
|
|
595
|
+
VOTE_OPTION_YES: 10,
|
|
596
|
+
VOTE_OPTION_ABSTAIN: 20,
|
|
597
|
+
VOTE_OPTION_NO: 30,
|
|
598
|
+
VOTE_OPTION_NO_WITH_VETO: 40,
|
|
599
|
+
};
|
|
600
|
+
expect(parseVoteOption('yes', customEnum)).toBe(10);
|
|
601
|
+
expect(parseVoteOption('no', customEnum)).toBe(30);
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
it('should throw ManifestMCPError for invalid option', () => {
|
|
605
|
+
expect(() => parseVoteOption('invalid', mockVoteOption)).toThrow(ManifestMCPError);
|
|
606
|
+
expect(() => parseVoteOption('maybe', mockVoteOption)).toThrow(ManifestMCPError);
|
|
607
|
+
expect(() => parseVoteOption('0', mockVoteOption)).toThrow(ManifestMCPError);
|
|
608
|
+
expect(() => parseVoteOption('5', mockVoteOption)).toThrow(ManifestMCPError);
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
it('should use TX_FAILED error code', () => {
|
|
612
|
+
try {
|
|
613
|
+
parseVoteOption('invalid', mockVoteOption);
|
|
614
|
+
} catch (error) {
|
|
615
|
+
expect((error as ManifestMCPError).code).toBe(ManifestMCPErrorCode.TX_FAILED);
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
it('should include the invalid option in error message', () => {
|
|
620
|
+
try {
|
|
621
|
+
parseVoteOption('badvalue', mockVoteOption);
|
|
622
|
+
} catch (error) {
|
|
623
|
+
expect((error as ManifestMCPError).message).toContain('badvalue');
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
});
|
|
@@ -5,6 +5,9 @@ import { ManifestMCPError, ManifestMCPErrorCode, CosmosTxResult } from '../types
|
|
|
5
5
|
/** Maximum number of arguments allowed */
|
|
6
6
|
export const MAX_ARGS = 100;
|
|
7
7
|
|
|
8
|
+
/** Maximum meta hash length in bytes (64 bytes for SHA-512) */
|
|
9
|
+
export const MAX_META_HASH_BYTES = 64;
|
|
10
|
+
|
|
8
11
|
/**
|
|
9
12
|
* Result from extracting a flag from args
|
|
10
13
|
*/
|
|
@@ -45,6 +48,33 @@ export function extractFlag(
|
|
|
45
48
|
return { value, consumedIndices: [flagIndex, flagIndex + 1] };
|
|
46
49
|
}
|
|
47
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Result from extracting a boolean (valueless) flag from args
|
|
53
|
+
*/
|
|
54
|
+
export interface ExtractedBooleanFlag {
|
|
55
|
+
/** Whether the flag was present */
|
|
56
|
+
value: boolean;
|
|
57
|
+
/** Args with the flag removed */
|
|
58
|
+
remainingArgs: string[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Extract a valueless boolean flag from args array.
|
|
63
|
+
* Returns { value: true, remainingArgs } if flag is present, { value: false, remainingArgs: args } otherwise.
|
|
64
|
+
*
|
|
65
|
+
* @param args - The arguments array to search
|
|
66
|
+
* @param flagName - The flag to look for (e.g., '--active-only')
|
|
67
|
+
* @returns Object with boolean value and filtered args
|
|
68
|
+
*/
|
|
69
|
+
export function extractBooleanFlag(args: string[], flagName: string): ExtractedBooleanFlag {
|
|
70
|
+
const flagIndex = args.indexOf(flagName);
|
|
71
|
+
if (flagIndex === -1) {
|
|
72
|
+
return { value: false, remainingArgs: args };
|
|
73
|
+
}
|
|
74
|
+
const remainingArgs = args.filter((_, index) => index !== flagIndex);
|
|
75
|
+
return { value: true, remainingArgs };
|
|
76
|
+
}
|
|
77
|
+
|
|
48
78
|
/**
|
|
49
79
|
* Filter args to remove consumed flag indices
|
|
50
80
|
*/
|
|
@@ -284,6 +314,48 @@ export function parseBigInt(value: string, fieldName: string): bigint {
|
|
|
284
314
|
return parseBigIntWithCode(value, fieldName, ManifestMCPErrorCode.TX_FAILED);
|
|
285
315
|
}
|
|
286
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Interface for VoteOption-like enums from cosmos protobuf modules.
|
|
319
|
+
* Both cosmos.gov.v1.VoteOption and cosmos.group.v1.VoteOption share this shape.
|
|
320
|
+
*/
|
|
321
|
+
interface VoteOptionEnum {
|
|
322
|
+
VOTE_OPTION_YES: number;
|
|
323
|
+
VOTE_OPTION_ABSTAIN: number;
|
|
324
|
+
VOTE_OPTION_NO: number;
|
|
325
|
+
VOTE_OPTION_NO_WITH_VETO: number;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Parse a vote option string to its numeric enum value.
|
|
330
|
+
* Accepts case-insensitive strings or numeric identifiers.
|
|
331
|
+
*
|
|
332
|
+
* @param optionStr - Vote option string (yes, no, abstain, no_with_veto, or 1-4)
|
|
333
|
+
* @param voteOptionEnum - The VoteOption enum object from the relevant cosmos module
|
|
334
|
+
*/
|
|
335
|
+
export function parseVoteOption(optionStr: string, voteOptionEnum: VoteOptionEnum): number {
|
|
336
|
+
const option = optionStr.toLowerCase();
|
|
337
|
+
switch (option) {
|
|
338
|
+
case 'yes':
|
|
339
|
+
case '1':
|
|
340
|
+
return voteOptionEnum.VOTE_OPTION_YES;
|
|
341
|
+
case 'abstain':
|
|
342
|
+
case '2':
|
|
343
|
+
return voteOptionEnum.VOTE_OPTION_ABSTAIN;
|
|
344
|
+
case 'no':
|
|
345
|
+
case '3':
|
|
346
|
+
return voteOptionEnum.VOTE_OPTION_NO;
|
|
347
|
+
case 'no_with_veto':
|
|
348
|
+
case 'nowithveto':
|
|
349
|
+
case '4':
|
|
350
|
+
return voteOptionEnum.VOTE_OPTION_NO_WITH_VETO;
|
|
351
|
+
default:
|
|
352
|
+
throw new ManifestMCPError(
|
|
353
|
+
ManifestMCPErrorCode.TX_FAILED,
|
|
354
|
+
`Invalid vote option: ${optionStr}. Expected: yes, no, abstain, or no_with_veto`
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
287
359
|
/**
|
|
288
360
|
* Parse amount string into coin (e.g., "1000umfx" -> { amount: "1000", denom: "umfx" })
|
|
289
361
|
* Supports simple denoms (umfx), IBC denoms (ibc/...), and factory denoms (factory/creator/subdenom)
|
package/src/types.ts
CHANGED
|
@@ -405,6 +405,68 @@ export interface CreditEstimateResult {
|
|
|
405
405
|
readonly estimate: unknown;
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
+
// Group query results
|
|
409
|
+
export interface GroupInfoResult {
|
|
410
|
+
readonly info?: unknown;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
export interface GroupPolicyInfoResult {
|
|
414
|
+
readonly info?: unknown;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
export interface GroupMembersResult extends PaginatedResult {
|
|
418
|
+
readonly members: readonly unknown[];
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export interface GroupsResult extends PaginatedResult {
|
|
422
|
+
readonly groups: readonly unknown[];
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
export interface GroupPoliciesResult extends PaginatedResult {
|
|
426
|
+
readonly groupPolicies: readonly unknown[];
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
export interface GroupProposalResult {
|
|
430
|
+
readonly proposal?: unknown;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export interface GroupProposalsResult extends PaginatedResult {
|
|
434
|
+
readonly proposals: readonly unknown[];
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export interface GroupVoteResult {
|
|
438
|
+
readonly vote?: unknown;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
export interface GroupVotesResult extends PaginatedResult {
|
|
442
|
+
readonly votes: readonly unknown[];
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
export interface GroupTallyResult {
|
|
446
|
+
readonly tally?: unknown;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// SKU query results
|
|
450
|
+
export interface SkuParamsResult {
|
|
451
|
+
readonly params?: unknown;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export interface ProviderResult {
|
|
455
|
+
readonly provider?: unknown;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export interface ProvidersResult extends PaginatedResult {
|
|
459
|
+
readonly providers: readonly unknown[];
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
export interface SkuResult {
|
|
463
|
+
readonly sku?: unknown;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
export interface SkusResult extends PaginatedResult {
|
|
467
|
+
readonly skus: readonly unknown[];
|
|
468
|
+
}
|
|
469
|
+
|
|
408
470
|
/**
|
|
409
471
|
* Union type of all query results for type-safe handling
|
|
410
472
|
*/
|
|
@@ -459,7 +521,22 @@ export type QueryResult =
|
|
|
459
521
|
| CreditAddressResult
|
|
460
522
|
| WithdrawableAmountResult
|
|
461
523
|
| ProviderWithdrawableResult
|
|
462
|
-
| CreditEstimateResult
|
|
524
|
+
| CreditEstimateResult
|
|
525
|
+
| SkuParamsResult
|
|
526
|
+
| ProviderResult
|
|
527
|
+
| ProvidersResult
|
|
528
|
+
| SkuResult
|
|
529
|
+
| SkusResult
|
|
530
|
+
| GroupInfoResult
|
|
531
|
+
| GroupPolicyInfoResult
|
|
532
|
+
| GroupMembersResult
|
|
533
|
+
| GroupsResult
|
|
534
|
+
| GroupPoliciesResult
|
|
535
|
+
| GroupProposalResult
|
|
536
|
+
| GroupProposalsResult
|
|
537
|
+
| GroupVoteResult
|
|
538
|
+
| GroupVotesResult
|
|
539
|
+
| GroupTallyResult;
|
|
463
540
|
|
|
464
541
|
/**
|
|
465
542
|
* Result from a Cosmos query
|