@manifest-network/manifest-mcp-browser 0.1.6 → 0.1.8

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 (95) hide show
  1. package/README.md +5 -2
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +0 -10
  4. package/dist/config.js.map +1 -1
  5. package/dist/index.d.ts +0 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +3 -7
  8. package/dist/index.js.map +1 -1
  9. package/dist/modules.d.ts.map +1 -1
  10. package/dist/modules.js +42 -0
  11. package/dist/modules.js.map +1 -1
  12. package/dist/queries/group.d.ts +12 -0
  13. package/dist/queries/group.d.ts.map +1 -0
  14. package/dist/queries/group.js +107 -0
  15. package/dist/queries/group.js.map +1 -0
  16. package/dist/queries/index.d.ts +2 -0
  17. package/dist/queries/index.d.ts.map +1 -1
  18. package/dist/queries/index.js +2 -0
  19. package/dist/queries/index.js.map +1 -1
  20. package/dist/queries/utils.d.ts +3 -18
  21. package/dist/queries/utils.d.ts.map +1 -1
  22. package/dist/queries/utils.js +2 -17
  23. package/dist/queries/utils.js.map +1 -1
  24. package/dist/transactions/bank.d.ts.map +1 -1
  25. package/dist/transactions/bank.js +7 -5
  26. package/dist/transactions/bank.js.map +1 -1
  27. package/dist/transactions/gov.d.ts.map +1 -1
  28. package/dist/transactions/gov.js +9 -30
  29. package/dist/transactions/gov.js.map +1 -1
  30. package/dist/transactions/group.d.ts +7 -0
  31. package/dist/transactions/group.d.ts.map +1 -0
  32. package/dist/transactions/group.js +339 -0
  33. package/dist/transactions/group.js.map +1 -0
  34. package/dist/transactions/index.d.ts +2 -0
  35. package/dist/transactions/index.d.ts.map +1 -1
  36. package/dist/transactions/index.js +2 -0
  37. package/dist/transactions/index.js.map +1 -1
  38. package/dist/transactions/utils.d.ts +37 -0
  39. package/dist/transactions/utils.d.ts.map +1 -1
  40. package/dist/transactions/utils.js +43 -0
  41. package/dist/transactions/utils.js.map +1 -1
  42. package/dist/types.d.ts +31 -3
  43. package/dist/types.d.ts.map +1 -1
  44. package/dist/types.js.map +1 -1
  45. package/package.json +5 -2
  46. package/.github/workflows/ci.yml +0 -37
  47. package/.github/workflows/publish.yml +0 -53
  48. package/CLAUDE.md +0 -111
  49. package/dist/config.test.d.ts +0 -2
  50. package/dist/config.test.d.ts.map +0 -1
  51. package/dist/config.test.js +0 -251
  52. package/dist/config.test.js.map +0 -1
  53. package/dist/modules.test.d.ts +0 -2
  54. package/dist/modules.test.d.ts.map +0 -1
  55. package/dist/modules.test.js +0 -159
  56. package/dist/modules.test.js.map +0 -1
  57. package/dist/queries/utils.test.d.ts +0 -2
  58. package/dist/queries/utils.test.d.ts.map +0 -1
  59. package/dist/queries/utils.test.js +0 -117
  60. package/dist/queries/utils.test.js.map +0 -1
  61. package/dist/transactions/utils.test.d.ts +0 -2
  62. package/dist/transactions/utils.test.d.ts.map +0 -1
  63. package/dist/transactions/utils.test.js +0 -471
  64. package/dist/transactions/utils.test.js.map +0 -1
  65. package/src/client.ts +0 -288
  66. package/src/config.test.ts +0 -299
  67. package/src/config.ts +0 -174
  68. package/src/cosmos.ts +0 -106
  69. package/src/index.ts +0 -478
  70. package/src/modules.test.ts +0 -189
  71. package/src/modules.ts +0 -428
  72. package/src/queries/auth.ts +0 -97
  73. package/src/queries/bank.ts +0 -99
  74. package/src/queries/billing.ts +0 -124
  75. package/src/queries/distribution.ts +0 -114
  76. package/src/queries/gov.ts +0 -104
  77. package/src/queries/index.ts +0 -16
  78. package/src/queries/sku.ts +0 -85
  79. package/src/queries/staking.ts +0 -154
  80. package/src/queries/utils.test.ts +0 -156
  81. package/src/queries/utils.ts +0 -145
  82. package/src/transactions/bank.ts +0 -86
  83. package/src/transactions/billing.ts +0 -286
  84. package/src/transactions/distribution.ts +0 -76
  85. package/src/transactions/gov.ts +0 -191
  86. package/src/transactions/index.ts +0 -7
  87. package/src/transactions/manifest.ts +0 -67
  88. package/src/transactions/sku.ts +0 -232
  89. package/src/transactions/staking.ts +0 -85
  90. package/src/transactions/utils.test.ts +0 -518
  91. package/src/transactions/utils.ts +0 -348
  92. package/src/types.ts +0 -497
  93. package/src/wallet/index.ts +0 -2
  94. package/src/wallet/mnemonic.ts +0 -146
  95. package/tsconfig.json +0 -23
@@ -1,156 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import {
3
- parseBigInt,
4
- parseInteger,
5
- createPagination,
6
- extractPaginationArgs,
7
- DEFAULT_PAGE_LIMIT,
8
- MAX_PAGE_LIMIT,
9
- } from './utils.js';
10
- import { ManifestMCPError, ManifestMCPErrorCode } from '../types.js';
11
-
12
- describe('parseBigInt', () => {
13
- it('should parse valid integer strings', () => {
14
- expect(parseBigInt('0', 'height')).toBe(BigInt(0));
15
- expect(parseBigInt('123', 'height')).toBe(BigInt(123));
16
- expect(parseBigInt('9999999999999999999', 'height')).toBe(BigInt('9999999999999999999'));
17
- });
18
-
19
- it('should throw ManifestMCPError for invalid integers', () => {
20
- expect(() => parseBigInt('abc', 'height')).toThrow(ManifestMCPError);
21
- expect(() => parseBigInt('12.34', 'height')).toThrow(ManifestMCPError);
22
- });
23
-
24
- it('should throw ManifestMCPError for empty string', () => {
25
- // Empty string should be rejected for security (prevents accidental 0 values)
26
- expect(() => parseBigInt('', 'height')).toThrow(ManifestMCPError);
27
- expect(() => parseBigInt(' ', 'height')).toThrow(ManifestMCPError);
28
- });
29
-
30
- it('should have correct error code', () => {
31
- try {
32
- parseBigInt('invalid', 'height');
33
- } catch (error) {
34
- expect(error).toBeInstanceOf(ManifestMCPError);
35
- expect((error as ManifestMCPError).code).toBe(ManifestMCPErrorCode.QUERY_FAILED);
36
- }
37
- });
38
-
39
- it('should include field name in error message', () => {
40
- try {
41
- parseBigInt('invalid', 'block-height');
42
- } catch (error) {
43
- expect((error as ManifestMCPError).message).toContain('block-height');
44
- }
45
- });
46
- });
47
-
48
- describe('parseInteger', () => {
49
- it('should parse valid integer strings', () => {
50
- expect(parseInteger('0', 'status')).toBe(0);
51
- expect(parseInteger('123', 'status')).toBe(123);
52
- expect(parseInteger('-5', 'status')).toBe(-5);
53
- });
54
-
55
- it('should throw ManifestMCPError for invalid integers', () => {
56
- expect(() => parseInteger('', 'status')).toThrow(ManifestMCPError);
57
- expect(() => parseInteger('abc', 'status')).toThrow(ManifestMCPError);
58
- });
59
-
60
- it('should have correct error code', () => {
61
- try {
62
- parseInteger('invalid', 'status');
63
- } catch (error) {
64
- expect(error).toBeInstanceOf(ManifestMCPError);
65
- expect((error as ManifestMCPError).code).toBe(ManifestMCPErrorCode.QUERY_FAILED);
66
- }
67
- });
68
- });
69
-
70
- describe('createPagination', () => {
71
- it('should use default limit when none provided', () => {
72
- const pagination = createPagination();
73
- expect(pagination.limit).toBe(DEFAULT_PAGE_LIMIT);
74
- expect(pagination.offset).toBe(BigInt(0));
75
- expect(pagination.countTotal).toBe(false);
76
- expect(pagination.reverse).toBe(false);
77
- expect(pagination.key).toEqual(new Uint8Array());
78
- });
79
-
80
- it('should use provided limit', () => {
81
- const pagination = createPagination(BigInt(50));
82
- expect(pagination.limit).toBe(BigInt(50));
83
- });
84
-
85
- it('should clamp limit to minimum of 1', () => {
86
- const pagination = createPagination(BigInt(0));
87
- expect(pagination.limit).toBe(BigInt(1));
88
-
89
- const paginationNegative = createPagination(BigInt(-10));
90
- expect(paginationNegative.limit).toBe(BigInt(1));
91
- });
92
-
93
- it('should clamp limit to maximum', () => {
94
- const pagination = createPagination(BigInt(9999));
95
- expect(pagination.limit).toBe(MAX_PAGE_LIMIT);
96
- });
97
- });
98
-
99
- describe('extractPaginationArgs', () => {
100
- it('should return default pagination when no --limit flag', () => {
101
- const { pagination, remainingArgs } = extractPaginationArgs(['arg1', 'arg2'], 'test');
102
- expect(pagination.limit).toBe(DEFAULT_PAGE_LIMIT);
103
- expect(remainingArgs).toEqual(['arg1', 'arg2']);
104
- });
105
-
106
- it('should extract --limit flag and value', () => {
107
- const { pagination, remainingArgs } = extractPaginationArgs(
108
- ['arg1', '--limit', '50', 'arg2'],
109
- 'test'
110
- );
111
- expect(pagination.limit).toBe(BigInt(50));
112
- expect(remainingArgs).toEqual(['arg1', 'arg2']);
113
- });
114
-
115
- it('should handle --limit at end of args', () => {
116
- const { pagination, remainingArgs } = extractPaginationArgs(
117
- ['arg1', '--limit', '25'],
118
- 'test'
119
- );
120
- expect(pagination.limit).toBe(BigInt(25));
121
- expect(remainingArgs).toEqual(['arg1']);
122
- });
123
-
124
- it('should handle --limit at start of args', () => {
125
- const { pagination, remainingArgs } = extractPaginationArgs(
126
- ['--limit', '75', 'arg1'],
127
- 'test'
128
- );
129
- expect(pagination.limit).toBe(BigInt(75));
130
- expect(remainingArgs).toEqual(['arg1']);
131
- });
132
-
133
- it('should throw for invalid limit value', () => {
134
- expect(() =>
135
- extractPaginationArgs(['--limit', 'abc'], 'test')
136
- ).toThrow(ManifestMCPError);
137
- });
138
-
139
- it('should throw for limit below minimum', () => {
140
- expect(() =>
141
- extractPaginationArgs(['--limit', '0'], 'test')
142
- ).toThrow(ManifestMCPError);
143
- });
144
-
145
- it('should throw for limit above maximum', () => {
146
- expect(() =>
147
- extractPaginationArgs(['--limit', '9999'], 'test')
148
- ).toThrow(ManifestMCPError);
149
- });
150
-
151
- it('should handle empty args array', () => {
152
- const { pagination, remainingArgs } = extractPaginationArgs([], 'test');
153
- expect(pagination.limit).toBe(DEFAULT_PAGE_LIMIT);
154
- expect(remainingArgs).toEqual([]);
155
- });
156
- });
@@ -1,145 +0,0 @@
1
- import { ManifestMCPError, ManifestMCPErrorCode } from '../types.js';
2
- import { parseBigIntWithCode, requireArgs as requireArgsBase, extractFlag, filterConsumedArgs } from '../transactions/utils.js';
3
-
4
- /**
5
- * Result from extracting a boolean (valueless) flag from args
6
- */
7
- export interface ExtractedBooleanFlag {
8
- /** Whether the flag was present */
9
- value: boolean;
10
- /** Args with the flag removed */
11
- remainingArgs: string[];
12
- }
13
-
14
- /**
15
- * Extract a valueless boolean flag from args array.
16
- * Returns { value: true, remainingArgs } if flag is present, { value: false, remainingArgs: args } otherwise.
17
- *
18
- * @param args - The arguments array to search
19
- * @param flagName - The flag to look for (e.g., '--active-only')
20
- * @returns Object with boolean value and filtered args
21
- */
22
- export function extractBooleanFlag(args: string[], flagName: string): ExtractedBooleanFlag {
23
- const flagIndex = args.indexOf(flagName);
24
- if (flagIndex === -1) {
25
- return { value: false, remainingArgs: args };
26
- }
27
- const remainingArgs = args.filter((_, index) => index !== flagIndex);
28
- return { value: true, remainingArgs };
29
- }
30
-
31
- /** Default page size limit for paginated queries to prevent resource exhaustion */
32
- export const DEFAULT_PAGE_LIMIT = BigInt(100);
33
-
34
- /** Maximum page size limit to prevent DoS */
35
- export const MAX_PAGE_LIMIT = BigInt(1000);
36
-
37
- /**
38
- * Cosmos SDK pagination configuration
39
- */
40
- export interface PaginationConfig {
41
- readonly key: Uint8Array;
42
- readonly offset: bigint;
43
- readonly limit: bigint;
44
- readonly countTotal: boolean;
45
- readonly reverse: boolean;
46
- }
47
-
48
- /**
49
- * Create pagination configuration with optional custom limit.
50
- * Validates that limit is within acceptable bounds.
51
- *
52
- * @param limit - Optional custom limit (defaults to DEFAULT_PAGE_LIMIT)
53
- * @returns Cosmos SDK pagination object
54
- */
55
- export function createPagination(limit?: bigint): PaginationConfig {
56
- let effectiveLimit = limit ?? DEFAULT_PAGE_LIMIT;
57
-
58
- // Clamp to valid range
59
- if (effectiveLimit < BigInt(1)) {
60
- effectiveLimit = BigInt(1);
61
- } else if (effectiveLimit > MAX_PAGE_LIMIT) {
62
- effectiveLimit = MAX_PAGE_LIMIT;
63
- }
64
-
65
- return {
66
- key: new Uint8Array(),
67
- offset: BigInt(0),
68
- limit: effectiveLimit,
69
- countTotal: false,
70
- reverse: false,
71
- };
72
- }
73
-
74
- /**
75
- * Default pagination configuration for queries (for backwards compatibility)
76
- * @deprecated Use createPagination() instead for configurable limits
77
- */
78
- export const defaultPagination = createPagination();
79
-
80
- /**
81
- * Extract --limit flag from args and return pagination config with remaining args.
82
- * Use this helper for paginated queries.
83
- *
84
- * @param args - The arguments array
85
- * @param context - Context for error messages
86
- * @returns Object with pagination config and filtered args
87
- */
88
- export function extractPaginationArgs(
89
- args: string[],
90
- context: string
91
- ): { pagination: PaginationConfig; remainingArgs: string[] } {
92
- const { value: limitStr, consumedIndices } = extractFlag(args, '--limit', context);
93
- const remainingArgs = filterConsumedArgs(args, consumedIndices);
94
-
95
- let pagination: PaginationConfig;
96
- if (limitStr) {
97
- const limit = parseBigInt(limitStr, 'limit');
98
- if (limit < BigInt(1) || limit > MAX_PAGE_LIMIT) {
99
- throw new ManifestMCPError(
100
- ManifestMCPErrorCode.QUERY_FAILED,
101
- `Invalid limit: ${limit}. Must be between 1 and ${MAX_PAGE_LIMIT}.`
102
- );
103
- }
104
- pagination = createPagination(limit);
105
- } else {
106
- pagination = createPagination();
107
- }
108
-
109
- return { pagination, remainingArgs };
110
- }
111
-
112
- /**
113
- * Safely parse a string to BigInt with proper error handling (for queries)
114
- */
115
- export function parseBigInt(value: string, fieldName: string): bigint {
116
- return parseBigIntWithCode(value, fieldName, ManifestMCPErrorCode.QUERY_FAILED);
117
- }
118
-
119
- /**
120
- * Safely parse a string to integer with proper error handling.
121
- * Named parseInteger to avoid shadowing global parseInt.
122
- */
123
- export function parseInteger(value: string, fieldName: string): number {
124
- const parsed = Number.parseInt(value, 10);
125
- if (Number.isNaN(parsed)) {
126
- throw new ManifestMCPError(
127
- ManifestMCPErrorCode.QUERY_FAILED,
128
- `Invalid ${fieldName}: "${value}". Expected a valid integer.`
129
- );
130
- }
131
- return parsed;
132
- }
133
-
134
- /**
135
- * Validate that required arguments are present (for queries).
136
- * Uses QUERY_FAILED error code by default.
137
- */
138
- export function requireArgs(
139
- args: string[],
140
- minCount: number,
141
- expectedNames: string[],
142
- context: string
143
- ): void {
144
- requireArgsBase(args, minCount, expectedNames, context, ManifestMCPErrorCode.QUERY_FAILED);
145
- }
@@ -1,86 +0,0 @@
1
- import { SigningStargateClient } from '@cosmjs/stargate';
2
- import { cosmos } from '@manifest-network/manifestjs';
3
- import { CosmosTxResult } from '../types.js';
4
- import { throwUnsupportedSubcommand } from '../modules.js';
5
- import { parseAmount, buildTxResult, validateAddress, validateMemo, validateArgsLength, extractFlag, parseColonPair, requireArgs } from './utils.js';
6
-
7
- const { MsgSend, MsgMultiSend } = cosmos.bank.v1beta1;
8
-
9
- /**
10
- * Route bank transaction to appropriate handler
11
- */
12
- export async function routeBankTransaction(
13
- client: SigningStargateClient,
14
- senderAddress: string,
15
- subcommand: string,
16
- args: string[],
17
- waitForConfirmation: boolean
18
- ): Promise<CosmosTxResult> {
19
- validateArgsLength(args, 'bank transaction');
20
-
21
- switch (subcommand) {
22
- case 'send': {
23
- requireArgs(args, 2, ['recipient-address', 'amount'], 'bank send');
24
- const [recipientAddress, amountStr] = args;
25
- validateAddress(recipientAddress, 'recipient address');
26
- const { amount, denom } = parseAmount(amountStr);
27
-
28
- // Extract optional memo from args
29
- const { value: memo = '' } = extractFlag(args, '--memo', 'bank send');
30
- if (memo) {
31
- validateMemo(memo);
32
- }
33
-
34
- const msg = {
35
- typeUrl: '/cosmos.bank.v1beta1.MsgSend',
36
- value: MsgSend.fromPartial({
37
- fromAddress: senderAddress,
38
- toAddress: recipientAddress,
39
- amount: [{ denom, amount }],
40
- }),
41
- };
42
-
43
- const result = await client.signAndBroadcast(senderAddress, [msg], 'auto', memo);
44
- return buildTxResult('bank', 'send', result, waitForConfirmation);
45
- }
46
-
47
- case 'multi-send': {
48
- requireArgs(args, 1, ['recipient:amount'], 'bank multi-send');
49
- // Parse format: multi-send recipient1:amount1 recipient2:amount2 ...
50
- const outputs = args.map((arg) => {
51
- const [address, amountStr] = parseColonPair(arg, 'address', 'amount', 'multi-send');
52
- validateAddress(address, 'recipient address');
53
- const { amount, denom } = parseAmount(amountStr);
54
- return { address, coins: [{ denom, amount }] };
55
- });
56
-
57
- // Calculate total input
58
- const totalByDenom = new Map<string, bigint>();
59
- for (const output of outputs) {
60
- for (const coin of output.coins) {
61
- const current = totalByDenom.get(coin.denom) || BigInt(0);
62
- totalByDenom.set(coin.denom, current + BigInt(coin.amount));
63
- }
64
- }
65
-
66
- const inputCoins = Array.from(totalByDenom.entries()).map(([denom, amount]) => ({
67
- denom,
68
- amount: amount.toString(),
69
- }));
70
-
71
- const msg = {
72
- typeUrl: '/cosmos.bank.v1beta1.MsgMultiSend',
73
- value: MsgMultiSend.fromPartial({
74
- inputs: [{ address: senderAddress, coins: inputCoins }],
75
- outputs,
76
- }),
77
- };
78
-
79
- const result = await client.signAndBroadcast(senderAddress, [msg], 'auto');
80
- return buildTxResult('bank', 'multi-send', result, waitForConfirmation);
81
- }
82
-
83
- default:
84
- throwUnsupportedSubcommand('tx', 'bank', subcommand);
85
- }
86
- }
@@ -1,286 +0,0 @@
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, parseBigInt, validateAddress, validateArgsLength, extractFlag, filterConsumedArgs, parseColonPair, requireArgs, parseHexBytes, MAX_META_HASH_BYTES } from './utils.js';
5
- import { getSubcommandUsage, throwUnsupportedSubcommand } from '../modules.js';
6
-
7
- const {
8
- MsgFundCredit, MsgCreateLease, MsgCloseLease, MsgWithdraw,
9
- MsgCreateLeaseForTenant, MsgAcknowledgeLease, MsgRejectLease, MsgCancelLease,
10
- MsgUpdateParams,
11
- } = liftedinit.billing.v1;
12
-
13
- /**
14
- * Route billing transaction to appropriate handler
15
- */
16
- export async function routeBillingTransaction(
17
- client: SigningStargateClient,
18
- senderAddress: string,
19
- subcommand: string,
20
- args: string[],
21
- waitForConfirmation: boolean
22
- ): Promise<CosmosTxResult> {
23
- validateArgsLength(args, 'billing transaction');
24
-
25
- switch (subcommand) {
26
- case 'fund-credit': {
27
- requireArgs(args, 2, ['tenant-address', 'amount'], 'billing fund-credit');
28
- const [tenant, amountStr] = args;
29
- validateAddress(tenant, 'tenant address');
30
- const { amount, denom } = parseAmount(amountStr);
31
-
32
- const msg = {
33
- typeUrl: '/liftedinit.billing.v1.MsgFundCredit',
34
- value: MsgFundCredit.fromPartial({
35
- sender: senderAddress,
36
- tenant,
37
- amount: { denom, amount },
38
- }),
39
- };
40
-
41
- const result = await client.signAndBroadcast(senderAddress, [msg], 'auto');
42
- return buildTxResult('billing', 'fund-credit', result, waitForConfirmation);
43
- }
44
-
45
- case 'create-lease': {
46
- // Parse optional --meta-hash flag (can appear anywhere in args)
47
- const { value: metaHashHex, consumedIndices } = extractFlag(args, '--meta-hash', 'billing create-lease');
48
- const metaHash = metaHashHex ? parseHexBytes(metaHashHex, 'meta-hash', MAX_META_HASH_BYTES) : undefined;
49
-
50
- // Filter out --meta-hash and its value to get item args
51
- const itemArgs = filterConsumedArgs(args, consumedIndices);
52
- requireArgs(itemArgs, 1, ['sku-uuid:quantity'], 'billing create-lease');
53
-
54
- // Parse items (format: sku-uuid:quantity ...)
55
- const items = itemArgs.map((arg) => {
56
- const [skuUuid, quantityStr] = parseColonPair(arg, 'sku-uuid', 'quantity', 'lease item');
57
- return { skuUuid, quantity: parseBigInt(quantityStr, 'quantity') };
58
- });
59
-
60
- const msg = {
61
- typeUrl: '/liftedinit.billing.v1.MsgCreateLease',
62
- value: MsgCreateLease.fromPartial({
63
- tenant: senderAddress,
64
- items,
65
- metaHash: metaHash ?? new Uint8Array(),
66
- }),
67
- };
68
-
69
- const result = await client.signAndBroadcast(senderAddress, [msg], 'auto');
70
- return buildTxResult('billing', 'create-lease', result, waitForConfirmation);
71
- }
72
-
73
- case 'close-lease': {
74
- // Parse optional --reason flag
75
- const { value: reason, consumedIndices } = extractFlag(args, '--reason', 'billing close-lease');
76
- const leaseArgs = filterConsumedArgs(args, consumedIndices);
77
- requireArgs(leaseArgs, 1, ['lease-uuid'], 'billing close-lease');
78
-
79
- // MsgCloseLease can close multiple leases at once
80
- const leaseUuids = leaseArgs;
81
-
82
- const msg = {
83
- typeUrl: '/liftedinit.billing.v1.MsgCloseLease',
84
- value: MsgCloseLease.fromPartial({
85
- sender: senderAddress,
86
- leaseUuids,
87
- reason: reason ?? '',
88
- }),
89
- };
90
-
91
- const result = await client.signAndBroadcast(senderAddress, [msg], 'auto');
92
- return buildTxResult('billing', 'close-lease', result, waitForConfirmation);
93
- }
94
-
95
- case 'withdraw': {
96
- requireArgs(args, 1, ['lease-uuid or --provider'], 'billing withdraw');
97
-
98
- // Extract flags
99
- const providerFlag = extractFlag(args, '--provider', 'billing withdraw');
100
- const limitFlag = extractFlag(args, '--limit', 'billing withdraw');
101
-
102
- let leaseUuids: string[] = [];
103
- let providerUuid = '';
104
- let limit = BigInt(0); // 0 means use default (50)
105
-
106
- if (providerFlag.value) {
107
- // Provider-wide withdrawal mode
108
- providerUuid = providerFlag.value;
109
-
110
- // Parse optional --limit flag (only valid with --provider)
111
- if (limitFlag.value) {
112
- limit = parseBigInt(limitFlag.value, 'limit');
113
- if (limit < BigInt(1) || limit > BigInt(100)) {
114
- throw new ManifestMCPError(
115
- ManifestMCPErrorCode.TX_FAILED,
116
- `Invalid limit: ${limit}. Must be between 1 and 100.`
117
- );
118
- }
119
- }
120
-
121
- // Check for any extra arguments that weren't consumed
122
- const allConsumed = [...providerFlag.consumedIndices, ...limitFlag.consumedIndices];
123
- const extraArgs = filterConsumedArgs(args, allConsumed);
124
- if (extraArgs.length > 0) {
125
- const usage = getSubcommandUsage('tx', 'billing', 'withdraw');
126
- throw new ManifestMCPError(
127
- ManifestMCPErrorCode.TX_FAILED,
128
- `Provider-wide withdrawal does not accept additional arguments. ` +
129
- `Got unexpected: ${extraArgs.map(a => `"${a}"`).join(', ')}. ` +
130
- `For lease-specific withdrawal, omit --provider flag. Usage: withdraw ${usage ?? '<args>'}`
131
- );
132
- }
133
- } else {
134
- // Lease-specific withdrawal mode
135
- // Check for unexpected flags (--limit without --provider is invalid)
136
- const unexpectedFlags = args.filter(arg => arg.startsWith('--'));
137
- if (unexpectedFlags.length > 0) {
138
- const usage = getSubcommandUsage('tx', 'billing', 'withdraw');
139
- throw new ManifestMCPError(
140
- ManifestMCPErrorCode.TX_FAILED,
141
- `Unexpected flag(s) in lease-specific withdrawal mode: ${unexpectedFlags.join(', ')}. ` +
142
- `Use --provider for provider-wide withdrawal. Usage: withdraw ${usage ?? '<args>'}`
143
- );
144
- }
145
-
146
- leaseUuids = args;
147
- }
148
-
149
- const msg = {
150
- typeUrl: '/liftedinit.billing.v1.MsgWithdraw',
151
- value: MsgWithdraw.fromPartial({
152
- sender: senderAddress,
153
- leaseUuids,
154
- providerUuid,
155
- limit,
156
- }),
157
- };
158
-
159
- const result = await client.signAndBroadcast(senderAddress, [msg], 'auto');
160
- return buildTxResult('billing', 'withdraw', result, waitForConfirmation);
161
- }
162
-
163
- case 'create-lease-for-tenant': {
164
- // Parse optional --meta-hash flag
165
- const { value: metaHashHex, consumedIndices } = extractFlag(args, '--meta-hash', 'billing create-lease-for-tenant');
166
- const metaHash = metaHashHex ? parseHexBytes(metaHashHex, 'meta-hash', MAX_META_HASH_BYTES) : undefined;
167
-
168
- // Filter out --meta-hash and its value to get remaining args
169
- const remainingArgs = filterConsumedArgs(args, consumedIndices);
170
- requireArgs(remainingArgs, 2, ['tenant-address', 'sku-uuid:quantity'], 'billing create-lease-for-tenant');
171
-
172
- const [tenant, ...itemArgs] = remainingArgs;
173
- validateAddress(tenant, 'tenant address');
174
-
175
- // Parse items (format: sku-uuid:quantity ...)
176
- const items = itemArgs.map((arg) => {
177
- const [skuUuid, quantityStr] = parseColonPair(arg, 'sku-uuid', 'quantity', 'lease item');
178
- return { skuUuid, quantity: parseBigInt(quantityStr, 'quantity') };
179
- });
180
-
181
- const msg = {
182
- typeUrl: '/liftedinit.billing.v1.MsgCreateLeaseForTenant',
183
- value: MsgCreateLeaseForTenant.fromPartial({
184
- authority: senderAddress,
185
- tenant,
186
- items,
187
- metaHash: metaHash ?? new Uint8Array(),
188
- }),
189
- };
190
-
191
- const result = await client.signAndBroadcast(senderAddress, [msg], 'auto');
192
- return buildTxResult('billing', 'create-lease-for-tenant', result, waitForConfirmation);
193
- }
194
-
195
- case 'acknowledge-lease': {
196
- requireArgs(args, 1, ['lease-uuid'], 'billing acknowledge-lease');
197
- const leaseUuids = args;
198
-
199
- const msg = {
200
- typeUrl: '/liftedinit.billing.v1.MsgAcknowledgeLease',
201
- value: MsgAcknowledgeLease.fromPartial({
202
- sender: senderAddress,
203
- leaseUuids,
204
- }),
205
- };
206
-
207
- const result = await client.signAndBroadcast(senderAddress, [msg], 'auto');
208
- return buildTxResult('billing', 'acknowledge-lease', result, waitForConfirmation);
209
- }
210
-
211
- case 'reject-lease': {
212
- // Parse optional --reason flag
213
- const { value: reason, consumedIndices } = extractFlag(args, '--reason', 'billing reject-lease');
214
- const leaseArgs = filterConsumedArgs(args, consumedIndices);
215
- requireArgs(leaseArgs, 1, ['lease-uuid'], 'billing reject-lease');
216
-
217
- const leaseUuids = leaseArgs;
218
-
219
- const msg = {
220
- typeUrl: '/liftedinit.billing.v1.MsgRejectLease',
221
- value: MsgRejectLease.fromPartial({
222
- sender: senderAddress,
223
- leaseUuids,
224
- reason: reason ?? '',
225
- }),
226
- };
227
-
228
- const result = await client.signAndBroadcast(senderAddress, [msg], 'auto');
229
- return buildTxResult('billing', 'reject-lease', result, waitForConfirmation);
230
- }
231
-
232
- case 'cancel-lease': {
233
- requireArgs(args, 1, ['lease-uuid'], 'billing cancel-lease');
234
- const leaseUuids = args;
235
-
236
- const msg = {
237
- typeUrl: '/liftedinit.billing.v1.MsgCancelLease',
238
- value: MsgCancelLease.fromPartial({
239
- tenant: senderAddress,
240
- leaseUuids,
241
- }),
242
- };
243
-
244
- const result = await client.signAndBroadcast(senderAddress, [msg], 'auto');
245
- return buildTxResult('billing', 'cancel-lease', result, waitForConfirmation);
246
- }
247
-
248
- case 'update-params': {
249
- requireArgs(args, 5, [
250
- 'max-leases-per-tenant', 'max-items-per-lease', 'min-lease-duration',
251
- 'max-pending-leases-per-tenant', 'pending-timeout',
252
- ], 'billing update-params');
253
-
254
- const [
255
- maxLeasesPerTenantStr, maxItemsPerLeaseStr, minLeaseDurationStr,
256
- maxPendingLeasesPerTenantStr, pendingTimeoutStr,
257
- ...allowedAddresses
258
- ] = args;
259
-
260
- for (const addr of allowedAddresses) {
261
- validateAddress(addr, 'allowed address');
262
- }
263
-
264
- const msg = {
265
- typeUrl: '/liftedinit.billing.v1.MsgUpdateParams',
266
- value: MsgUpdateParams.fromPartial({
267
- authority: senderAddress,
268
- params: {
269
- maxLeasesPerTenant: parseBigInt(maxLeasesPerTenantStr, 'max-leases-per-tenant'),
270
- maxItemsPerLease: parseBigInt(maxItemsPerLeaseStr, 'max-items-per-lease'),
271
- minLeaseDuration: parseBigInt(minLeaseDurationStr, 'min-lease-duration'),
272
- maxPendingLeasesPerTenant: parseBigInt(maxPendingLeasesPerTenantStr, 'max-pending-leases-per-tenant'),
273
- pendingTimeout: parseBigInt(pendingTimeoutStr, 'pending-timeout'),
274
- allowedList: allowedAddresses,
275
- },
276
- }),
277
- };
278
-
279
- const result = await client.signAndBroadcast(senderAddress, [msg], 'auto');
280
- return buildTxResult('billing', 'update-params', result, waitForConfirmation);
281
- }
282
-
283
- default:
284
- throwUnsupportedSubcommand('tx', 'billing', subcommand);
285
- }
286
- }