@manifest-network/manifest-mcp-browser 0.1.7 → 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.
- package/README.md +5 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +0 -10
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -7
- package/dist/index.js.map +1 -1
- 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/transactions/bank.d.ts.map +1 -1
- package/dist/transactions/bank.js +7 -5
- package/dist/transactions/bank.js.map +1 -1
- package/dist/transactions/gov.d.ts.map +1 -1
- package/dist/transactions/gov.js +7 -5
- package/dist/transactions/gov.js.map +1 -1
- 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/types.d.ts +0 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +5 -2
- package/.github/workflows/ci.yml +0 -37
- package/.github/workflows/publish.yml +0 -53
- package/CLAUDE.md +0 -113
- package/dist/config.test.d.ts +0 -2
- package/dist/config.test.d.ts.map +0 -1
- package/dist/config.test.js +0 -251
- package/dist/config.test.js.map +0 -1
- package/dist/modules.test.d.ts +0 -2
- package/dist/modules.test.d.ts.map +0 -1
- package/dist/modules.test.js +0 -161
- package/dist/modules.test.js.map +0 -1
- package/dist/queries/utils.test.d.ts +0 -2
- package/dist/queries/utils.test.d.ts.map +0 -1
- package/dist/queries/utils.test.js +0 -117
- package/dist/queries/utils.test.js.map +0 -1
- package/dist/transactions/utils.test.d.ts +0 -2
- package/dist/transactions/utils.test.d.ts.map +0 -1
- package/dist/transactions/utils.test.js +0 -567
- package/dist/transactions/utils.test.js.map +0 -1
- package/src/client.ts +0 -288
- package/src/config.test.ts +0 -299
- package/src/config.ts +0 -174
- package/src/cosmos.ts +0 -106
- package/src/index.ts +0 -478
- package/src/modules.test.ts +0 -191
- package/src/modules.ts +0 -470
- package/src/queries/auth.ts +0 -97
- package/src/queries/bank.ts +0 -99
- package/src/queries/billing.ts +0 -124
- package/src/queries/distribution.ts +0 -114
- package/src/queries/gov.ts +0 -104
- package/src/queries/group.ts +0 -146
- package/src/queries/index.ts +0 -17
- package/src/queries/sku.ts +0 -85
- package/src/queries/staking.ts +0 -154
- package/src/queries/utils.test.ts +0 -156
- package/src/queries/utils.ts +0 -121
- package/src/transactions/bank.ts +0 -86
- package/src/transactions/billing.ts +0 -286
- package/src/transactions/distribution.ts +0 -76
- package/src/transactions/gov.ts +0 -164
- package/src/transactions/group.ts +0 -458
- package/src/transactions/index.ts +0 -8
- package/src/transactions/manifest.ts +0 -67
- package/src/transactions/sku.ts +0 -232
- package/src/transactions/staking.ts +0 -85
- package/src/transactions/utils.test.ts +0 -626
- package/src/transactions/utils.ts +0 -417
- package/src/types.ts +0 -548
- package/src/wallet/index.ts +0 -2
- package/src/wallet/mnemonic.ts +0 -146
- package/tsconfig.json +0 -23
package/src/client.ts
DELETED
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
import { SigningStargateClient, GasPrice, HttpEndpoint } from '@cosmjs/stargate';
|
|
2
|
-
import {
|
|
3
|
-
cosmosProtoRegistry,
|
|
4
|
-
cosmosAminoConverters,
|
|
5
|
-
liftedinitProtoRegistry,
|
|
6
|
-
liftedinitAminoConverters,
|
|
7
|
-
strangeloveVenturesProtoRegistry,
|
|
8
|
-
strangeloveVenturesAminoConverters,
|
|
9
|
-
osmosisProtoRegistry,
|
|
10
|
-
osmosisAminoConverters,
|
|
11
|
-
liftedinit,
|
|
12
|
-
} from '@manifest-network/manifestjs';
|
|
13
|
-
import { Registry } from '@cosmjs/proto-signing';
|
|
14
|
-
import { AminoTypes } from '@cosmjs/stargate';
|
|
15
|
-
import { RateLimiter } from 'limiter';
|
|
16
|
-
import { ManifestMCPConfig, WalletProvider, ManifestMCPError, ManifestMCPErrorCode } from './types.js';
|
|
17
|
-
import { DEFAULT_REQUESTS_PER_SECOND } from './config.js';
|
|
18
|
-
|
|
19
|
-
// Type for the RPC query client from manifestjs liftedinit bundle
|
|
20
|
-
// This includes cosmos modules + liftedinit-specific modules (billing, manifest, sku)
|
|
21
|
-
export type ManifestQueryClient = Awaited<ReturnType<typeof liftedinit.ClientFactory.createRPCQueryClient>>;
|
|
22
|
-
|
|
23
|
-
/** Default timeout for transaction broadcast (60 seconds) */
|
|
24
|
-
const DEFAULT_BROADCAST_TIMEOUT_MS = 60_000;
|
|
25
|
-
|
|
26
|
-
/** Default polling interval for transaction confirmation (3 seconds) */
|
|
27
|
-
const DEFAULT_BROADCAST_POLL_INTERVAL_MS = 3_000;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Get combined signing client options with all Manifest registries
|
|
31
|
-
*/
|
|
32
|
-
function getSigningManifestClientOptions() {
|
|
33
|
-
const registry = new Registry([
|
|
34
|
-
...cosmosProtoRegistry,
|
|
35
|
-
...liftedinitProtoRegistry,
|
|
36
|
-
...strangeloveVenturesProtoRegistry,
|
|
37
|
-
...osmosisProtoRegistry,
|
|
38
|
-
]);
|
|
39
|
-
|
|
40
|
-
const aminoTypes = new AminoTypes({
|
|
41
|
-
...cosmosAminoConverters,
|
|
42
|
-
...liftedinitAminoConverters,
|
|
43
|
-
...strangeloveVenturesAminoConverters,
|
|
44
|
-
...osmosisAminoConverters,
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
return { registry, aminoTypes };
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Manages CosmJS client instances with lazy initialization and singleton pattern
|
|
52
|
-
*/
|
|
53
|
-
export class CosmosClientManager {
|
|
54
|
-
private static instances: Map<string, CosmosClientManager> = new Map();
|
|
55
|
-
|
|
56
|
-
private config: ManifestMCPConfig;
|
|
57
|
-
private walletProvider: WalletProvider;
|
|
58
|
-
private queryClient: ManifestQueryClient | null = null;
|
|
59
|
-
private signingClient: SigningStargateClient | null = null;
|
|
60
|
-
private rateLimiter: RateLimiter;
|
|
61
|
-
|
|
62
|
-
// Promises to prevent concurrent client initialization (lazy init race condition)
|
|
63
|
-
private queryClientPromise: Promise<ManifestQueryClient> | null = null;
|
|
64
|
-
private signingClientPromise: Promise<SigningStargateClient> | null = null;
|
|
65
|
-
|
|
66
|
-
private constructor(config: ManifestMCPConfig, walletProvider: WalletProvider) {
|
|
67
|
-
this.config = config;
|
|
68
|
-
this.walletProvider = walletProvider;
|
|
69
|
-
|
|
70
|
-
// Initialize rate limiter with configured or default requests per second
|
|
71
|
-
const requestsPerSecond = config.rateLimit?.requestsPerSecond ?? DEFAULT_REQUESTS_PER_SECOND;
|
|
72
|
-
this.rateLimiter = new RateLimiter({
|
|
73
|
-
tokensPerInterval: requestsPerSecond,
|
|
74
|
-
interval: 'second',
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Get or create a singleton instance for the given config.
|
|
80
|
-
* Instances are keyed by chainId:rpcUrl. For existing instances:
|
|
81
|
-
* - Config and walletProvider references are always updated
|
|
82
|
-
* - Signing client is disconnected/recreated if gasPrice or walletProvider changed
|
|
83
|
-
* - Rate limiter is updated if requestsPerSecond changed (without affecting signing client)
|
|
84
|
-
*/
|
|
85
|
-
static getInstance(
|
|
86
|
-
config: ManifestMCPConfig,
|
|
87
|
-
walletProvider: WalletProvider
|
|
88
|
-
): CosmosClientManager {
|
|
89
|
-
const key = `${config.chainId}:${config.rpcUrl}`;
|
|
90
|
-
let instance = CosmosClientManager.instances.get(key);
|
|
91
|
-
|
|
92
|
-
if (!instance) {
|
|
93
|
-
instance = new CosmosClientManager(config, walletProvider);
|
|
94
|
-
CosmosClientManager.instances.set(key, instance);
|
|
95
|
-
} else {
|
|
96
|
-
// Check what changed to determine what needs updating
|
|
97
|
-
const signingClientAffected =
|
|
98
|
-
instance.config.gasPrice !== config.gasPrice ||
|
|
99
|
-
instance.walletProvider !== walletProvider;
|
|
100
|
-
|
|
101
|
-
const rateLimitChanged =
|
|
102
|
-
instance.config.rateLimit?.requestsPerSecond !== config.rateLimit?.requestsPerSecond;
|
|
103
|
-
|
|
104
|
-
// Always update config reference
|
|
105
|
-
instance.config = config;
|
|
106
|
-
instance.walletProvider = walletProvider;
|
|
107
|
-
|
|
108
|
-
// Only invalidate signing client if fields it depends on changed
|
|
109
|
-
if (signingClientAffected) {
|
|
110
|
-
if (instance.signingClient) {
|
|
111
|
-
instance.signingClient.disconnect();
|
|
112
|
-
instance.signingClient = null;
|
|
113
|
-
}
|
|
114
|
-
// Also clear the promise to allow re-initialization with new config
|
|
115
|
-
instance.signingClientPromise = null;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Update rate limiter independently (doesn't affect signing client)
|
|
119
|
-
if (rateLimitChanged) {
|
|
120
|
-
const newRps = config.rateLimit?.requestsPerSecond ?? DEFAULT_REQUESTS_PER_SECOND;
|
|
121
|
-
instance.rateLimiter = new RateLimiter({
|
|
122
|
-
tokensPerInterval: newRps,
|
|
123
|
-
interval: 'second',
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return instance;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Clear all cached instances (useful for testing or reconnection)
|
|
133
|
-
*/
|
|
134
|
-
static clearInstances(): void {
|
|
135
|
-
CosmosClientManager.instances.clear();
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Get the manifestjs RPC query client with all module extensions
|
|
140
|
-
*/
|
|
141
|
-
async getQueryClient(): Promise<ManifestQueryClient> {
|
|
142
|
-
// Return cached client if available
|
|
143
|
-
if (this.queryClient) {
|
|
144
|
-
return this.queryClient;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// If initialization is already in progress, wait for it
|
|
148
|
-
if (this.queryClientPromise) {
|
|
149
|
-
return this.queryClientPromise;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Start initialization and cache the promise to prevent concurrent init
|
|
153
|
-
this.queryClientPromise = (async () => {
|
|
154
|
-
// Capture reference to detect if superseded by disconnect/config change
|
|
155
|
-
const thisInitPromise = this.queryClientPromise;
|
|
156
|
-
try {
|
|
157
|
-
// Use liftedinit ClientFactory which includes cosmos + liftedinit modules
|
|
158
|
-
const client = await liftedinit.ClientFactory.createRPCQueryClient({
|
|
159
|
-
rpcEndpoint: this.config.rpcUrl,
|
|
160
|
-
});
|
|
161
|
-
// Only store if this is still the active promise
|
|
162
|
-
if (this.queryClientPromise === thisInitPromise) {
|
|
163
|
-
this.queryClient = client;
|
|
164
|
-
this.queryClientPromise = null;
|
|
165
|
-
}
|
|
166
|
-
return client;
|
|
167
|
-
} catch (error) {
|
|
168
|
-
// Clear promise on failure so retry is possible (only if still active)
|
|
169
|
-
if (this.queryClientPromise === thisInitPromise) {
|
|
170
|
-
this.queryClientPromise = null;
|
|
171
|
-
}
|
|
172
|
-
throw new ManifestMCPError(
|
|
173
|
-
ManifestMCPErrorCode.RPC_CONNECTION_FAILED,
|
|
174
|
-
`Failed to connect to RPC endpoint: ${error instanceof Error ? error.message : String(error)}`,
|
|
175
|
-
{ rpcUrl: this.config.rpcUrl }
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
})();
|
|
179
|
-
|
|
180
|
-
return this.queryClientPromise;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Get a signing client with all Manifest registries (for transactions)
|
|
185
|
-
*/
|
|
186
|
-
async getSigningClient(): Promise<SigningStargateClient> {
|
|
187
|
-
// Return cached client if available
|
|
188
|
-
if (this.signingClient) {
|
|
189
|
-
return this.signingClient;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// If initialization is already in progress, wait for it
|
|
193
|
-
if (this.signingClientPromise) {
|
|
194
|
-
return this.signingClientPromise;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Start initialization and cache the promise to prevent concurrent init
|
|
198
|
-
this.signingClientPromise = (async () => {
|
|
199
|
-
// Capture reference to detect if superseded by disconnect/config change
|
|
200
|
-
const thisInitPromise = this.signingClientPromise;
|
|
201
|
-
try {
|
|
202
|
-
const signer = await this.walletProvider.getSigner();
|
|
203
|
-
const gasPrice = GasPrice.fromString(this.config.gasPrice);
|
|
204
|
-
const { registry, aminoTypes } = getSigningManifestClientOptions();
|
|
205
|
-
|
|
206
|
-
// Configure endpoint with HTTP timeout
|
|
207
|
-
const endpoint: HttpEndpoint = {
|
|
208
|
-
url: this.config.rpcUrl,
|
|
209
|
-
headers: {},
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
// Note: Registry type from @cosmjs/proto-signing doesn't perfectly match
|
|
213
|
-
// SigningStargateClientOptions due to telescope-generated proto types.
|
|
214
|
-
// This is a known limitation with custom cosmos-sdk module registries.
|
|
215
|
-
const client = await SigningStargateClient.connectWithSigner(
|
|
216
|
-
endpoint,
|
|
217
|
-
signer,
|
|
218
|
-
{
|
|
219
|
-
registry: registry as Parameters<typeof SigningStargateClient.connectWithSigner>[2] extends { registry?: infer R } ? R : never,
|
|
220
|
-
aminoTypes,
|
|
221
|
-
gasPrice,
|
|
222
|
-
broadcastTimeoutMs: DEFAULT_BROADCAST_TIMEOUT_MS,
|
|
223
|
-
broadcastPollIntervalMs: DEFAULT_BROADCAST_POLL_INTERVAL_MS,
|
|
224
|
-
}
|
|
225
|
-
);
|
|
226
|
-
// Only store if this is still the active promise
|
|
227
|
-
if (this.signingClientPromise === thisInitPromise) {
|
|
228
|
-
this.signingClient = client;
|
|
229
|
-
this.signingClientPromise = null;
|
|
230
|
-
} else {
|
|
231
|
-
// Promise was superseded, clean up the client we just created
|
|
232
|
-
client.disconnect();
|
|
233
|
-
}
|
|
234
|
-
return client;
|
|
235
|
-
} catch (error) {
|
|
236
|
-
// Clear promise on failure so retry is possible (only if still active)
|
|
237
|
-
if (this.signingClientPromise === thisInitPromise) {
|
|
238
|
-
this.signingClientPromise = null;
|
|
239
|
-
}
|
|
240
|
-
if (error instanceof ManifestMCPError) {
|
|
241
|
-
throw error;
|
|
242
|
-
}
|
|
243
|
-
throw new ManifestMCPError(
|
|
244
|
-
ManifestMCPErrorCode.RPC_CONNECTION_FAILED,
|
|
245
|
-
`Failed to connect signing client: ${error instanceof Error ? error.message : String(error)}`,
|
|
246
|
-
{ rpcUrl: this.config.rpcUrl }
|
|
247
|
-
);
|
|
248
|
-
}
|
|
249
|
-
})();
|
|
250
|
-
|
|
251
|
-
return this.signingClientPromise;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Get the wallet address
|
|
256
|
-
*/
|
|
257
|
-
async getAddress(): Promise<string> {
|
|
258
|
-
return this.walletProvider.getAddress();
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Get the configuration
|
|
263
|
-
*/
|
|
264
|
-
getConfig(): ManifestMCPConfig {
|
|
265
|
-
return this.config;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Acquire a rate limit token before making an RPC request.
|
|
270
|
-
* This will wait if the rate limit has been exceeded.
|
|
271
|
-
*/
|
|
272
|
-
async acquireRateLimit(): Promise<void> {
|
|
273
|
-
await this.rateLimiter.removeTokens(1);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Disconnect all clients
|
|
278
|
-
*/
|
|
279
|
-
disconnect(): void {
|
|
280
|
-
if (this.signingClient) {
|
|
281
|
-
this.signingClient.disconnect();
|
|
282
|
-
this.signingClient = null;
|
|
283
|
-
}
|
|
284
|
-
this.signingClientPromise = null;
|
|
285
|
-
this.queryClient = null;
|
|
286
|
-
this.queryClientPromise = null;
|
|
287
|
-
}
|
|
288
|
-
}
|
package/src/config.test.ts
DELETED
|
@@ -1,299 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { createConfig, validateConfig, createValidatedConfig } from './config.js';
|
|
3
|
-
import { ManifestMCPError, ManifestMCPErrorCode } from './types.js';
|
|
4
|
-
|
|
5
|
-
describe('createConfig', () => {
|
|
6
|
-
it('should apply default values', () => {
|
|
7
|
-
const config = createConfig({
|
|
8
|
-
chainId: 'test-chain',
|
|
9
|
-
rpcUrl: 'https://example.com',
|
|
10
|
-
gasPrice: '1.0umfx',
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
expect(config.chainId).toBe('test-chain');
|
|
14
|
-
expect(config.rpcUrl).toBe('https://example.com');
|
|
15
|
-
expect(config.gasPrice).toBe('1.0umfx');
|
|
16
|
-
expect(config.gasAdjustment).toBe(1.3);
|
|
17
|
-
expect(config.addressPrefix).toBe('manifest');
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('should preserve provided optional values', () => {
|
|
21
|
-
const config = createConfig({
|
|
22
|
-
chainId: 'test-chain',
|
|
23
|
-
rpcUrl: 'https://example.com',
|
|
24
|
-
gasPrice: '1.0umfx',
|
|
25
|
-
gasAdjustment: 2.0,
|
|
26
|
-
addressPrefix: 'custom',
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
expect(config.gasAdjustment).toBe(2.0);
|
|
30
|
-
expect(config.addressPrefix).toBe('custom');
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('should apply default rateLimit', () => {
|
|
34
|
-
const config = createConfig({
|
|
35
|
-
chainId: 'test-chain',
|
|
36
|
-
rpcUrl: 'https://example.com',
|
|
37
|
-
gasPrice: '1.0umfx',
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
expect(config.rateLimit).toBeDefined();
|
|
41
|
-
expect(config.rateLimit?.requestsPerSecond).toBe(10);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should preserve provided rateLimit', () => {
|
|
45
|
-
const config = createConfig({
|
|
46
|
-
chainId: 'test-chain',
|
|
47
|
-
rpcUrl: 'https://example.com',
|
|
48
|
-
gasPrice: '1.0umfx',
|
|
49
|
-
rateLimit: { requestsPerSecond: 20 },
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
expect(config.rateLimit?.requestsPerSecond).toBe(20);
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
describe('validateConfig', () => {
|
|
57
|
-
it('should return valid for correct config', () => {
|
|
58
|
-
const result = validateConfig({
|
|
59
|
-
chainId: 'manifest-testnet',
|
|
60
|
-
rpcUrl: 'https://rpc.example.com',
|
|
61
|
-
gasPrice: '1.0umfx',
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
expect(result.valid).toBe(true);
|
|
65
|
-
expect(result.errors).toHaveLength(0);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('should detect missing required fields', () => {
|
|
69
|
-
const result = validateConfig({});
|
|
70
|
-
|
|
71
|
-
expect(result.valid).toBe(false);
|
|
72
|
-
expect(result.errors).toContain('chainId is required');
|
|
73
|
-
expect(result.errors).toContain('rpcUrl is required');
|
|
74
|
-
expect(result.errors).toContain('gasPrice is required');
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('should validate chainId format', () => {
|
|
78
|
-
const result = validateConfig({
|
|
79
|
-
chainId: '-invalid',
|
|
80
|
-
rpcUrl: 'https://example.com',
|
|
81
|
-
gasPrice: '1.0umfx',
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
expect(result.valid).toBe(false);
|
|
85
|
-
expect(result.errors.some(e => e.includes('chainId'))).toBe(true);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('should validate rpcUrl format', () => {
|
|
89
|
-
const result = validateConfig({
|
|
90
|
-
chainId: 'test',
|
|
91
|
-
rpcUrl: 'not-a-url',
|
|
92
|
-
gasPrice: '1.0umfx',
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
expect(result.valid).toBe(false);
|
|
96
|
-
expect(result.errors.some(e => e.includes('rpcUrl'))).toBe(true);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('should validate gasPrice format', () => {
|
|
100
|
-
const result = validateConfig({
|
|
101
|
-
chainId: 'test',
|
|
102
|
-
rpcUrl: 'https://example.com',
|
|
103
|
-
gasPrice: 'invalid',
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
expect(result.valid).toBe(false);
|
|
107
|
-
expect(result.errors.some(e => e.includes('gasPrice'))).toBe(true);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('should validate optional gasAdjustment', () => {
|
|
111
|
-
const result = validateConfig({
|
|
112
|
-
chainId: 'test',
|
|
113
|
-
rpcUrl: 'https://example.com',
|
|
114
|
-
gasPrice: '1.0umfx',
|
|
115
|
-
gasAdjustment: -1,
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
expect(result.valid).toBe(false);
|
|
119
|
-
expect(result.errors.some(e => e.includes('gasAdjustment'))).toBe(true);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('should validate optional addressPrefix', () => {
|
|
123
|
-
const result = validateConfig({
|
|
124
|
-
chainId: 'test',
|
|
125
|
-
rpcUrl: 'https://example.com',
|
|
126
|
-
gasPrice: '1.0umfx',
|
|
127
|
-
addressPrefix: 'INVALID',
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
expect(result.valid).toBe(false);
|
|
131
|
-
expect(result.errors.some(e => e.includes('addressPrefix'))).toBe(true);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('should accept HTTPS URLs', () => {
|
|
135
|
-
const result = validateConfig({
|
|
136
|
-
chainId: 'test',
|
|
137
|
-
rpcUrl: 'https://rpc.example.com',
|
|
138
|
-
gasPrice: '1.0umfx',
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
expect(result.valid).toBe(true);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it('should accept HTTP URLs for localhost', () => {
|
|
145
|
-
const result = validateConfig({
|
|
146
|
-
chainId: 'test',
|
|
147
|
-
rpcUrl: 'http://localhost:26657',
|
|
148
|
-
gasPrice: '1.0umfx',
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
expect(result.valid).toBe(true);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it('should accept HTTP URLs for 127.0.0.1', () => {
|
|
155
|
-
const result = validateConfig({
|
|
156
|
-
chainId: 'test',
|
|
157
|
-
rpcUrl: 'http://127.0.0.1:26657',
|
|
158
|
-
gasPrice: '1.0umfx',
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
expect(result.valid).toBe(true);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it('should accept HTTP URLs for IPv6 localhost', () => {
|
|
165
|
-
const result = validateConfig({
|
|
166
|
-
chainId: 'test',
|
|
167
|
-
rpcUrl: 'http://[::1]:26657',
|
|
168
|
-
gasPrice: '1.0umfx',
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
expect(result.valid).toBe(true);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it('should reject HTTP URLs for non-localhost', () => {
|
|
175
|
-
const result = validateConfig({
|
|
176
|
-
chainId: 'test',
|
|
177
|
-
rpcUrl: 'http://rpc.example.com',
|
|
178
|
-
gasPrice: '1.0umfx',
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
expect(result.valid).toBe(false);
|
|
182
|
-
expect(result.errors.some(e => e.includes('HTTPS'))).toBe(true);
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it('should accept valid rateLimit config', () => {
|
|
186
|
-
const result = validateConfig({
|
|
187
|
-
chainId: 'test',
|
|
188
|
-
rpcUrl: 'https://example.com',
|
|
189
|
-
gasPrice: '1.0umfx',
|
|
190
|
-
rateLimit: { requestsPerSecond: 5 },
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
expect(result.valid).toBe(true);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('should reject non-integer rateLimit.requestsPerSecond', () => {
|
|
197
|
-
const result = validateConfig({
|
|
198
|
-
chainId: 'test',
|
|
199
|
-
rpcUrl: 'https://example.com',
|
|
200
|
-
gasPrice: '1.0umfx',
|
|
201
|
-
rateLimit: { requestsPerSecond: 5.5 },
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
expect(result.valid).toBe(false);
|
|
205
|
-
expect(result.errors.some(e => e.includes('requestsPerSecond'))).toBe(true);
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
it('should reject negative rateLimit.requestsPerSecond', () => {
|
|
209
|
-
const result = validateConfig({
|
|
210
|
-
chainId: 'test',
|
|
211
|
-
rpcUrl: 'https://example.com',
|
|
212
|
-
gasPrice: '1.0umfx',
|
|
213
|
-
rateLimit: { requestsPerSecond: -1 },
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
expect(result.valid).toBe(false);
|
|
217
|
-
expect(result.errors.some(e => e.includes('requestsPerSecond'))).toBe(true);
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
it('should reject zero rateLimit.requestsPerSecond', () => {
|
|
221
|
-
const result = validateConfig({
|
|
222
|
-
chainId: 'test',
|
|
223
|
-
rpcUrl: 'https://example.com',
|
|
224
|
-
gasPrice: '1.0umfx',
|
|
225
|
-
rateLimit: { requestsPerSecond: 0 },
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
expect(result.valid).toBe(false);
|
|
229
|
-
expect(result.errors.some(e => e.includes('requestsPerSecond'))).toBe(true);
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
it('should reject null rateLimit', () => {
|
|
233
|
-
const result = validateConfig({
|
|
234
|
-
chainId: 'test',
|
|
235
|
-
rpcUrl: 'https://example.com',
|
|
236
|
-
gasPrice: '1.0umfx',
|
|
237
|
-
rateLimit: null as unknown as { requestsPerSecond?: number },
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
expect(result.valid).toBe(false);
|
|
241
|
-
expect(result.errors.some(e => e.includes('rateLimit must be a plain object'))).toBe(true);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it('should reject non-object rateLimit', () => {
|
|
245
|
-
const result = validateConfig({
|
|
246
|
-
chainId: 'test',
|
|
247
|
-
rpcUrl: 'https://example.com',
|
|
248
|
-
gasPrice: '1.0umfx',
|
|
249
|
-
rateLimit: 'invalid' as unknown as { requestsPerSecond?: number },
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
expect(result.valid).toBe(false);
|
|
253
|
-
expect(result.errors.some(e => e.includes('rateLimit must be a plain object'))).toBe(true);
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
it('should reject array rateLimit', () => {
|
|
257
|
-
const result = validateConfig({
|
|
258
|
-
chainId: 'test',
|
|
259
|
-
rpcUrl: 'https://example.com',
|
|
260
|
-
gasPrice: '1.0umfx',
|
|
261
|
-
rateLimit: [] as unknown as { requestsPerSecond?: number },
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
expect(result.valid).toBe(false);
|
|
265
|
-
expect(result.errors.some(e => e.includes('rateLimit must be a plain object'))).toBe(true);
|
|
266
|
-
});
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
describe('createValidatedConfig', () => {
|
|
270
|
-
it('should return config for valid input', () => {
|
|
271
|
-
const config = createValidatedConfig({
|
|
272
|
-
chainId: 'test-chain',
|
|
273
|
-
rpcUrl: 'https://example.com',
|
|
274
|
-
gasPrice: '1.0umfx',
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
expect(config.chainId).toBe('test-chain');
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
it('should throw ManifestMCPError for invalid input', () => {
|
|
281
|
-
expect(() => createValidatedConfig({
|
|
282
|
-
chainId: '',
|
|
283
|
-
rpcUrl: '',
|
|
284
|
-
gasPrice: '',
|
|
285
|
-
})).toThrow(ManifestMCPError);
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
it('should have INVALID_CONFIG error code', () => {
|
|
289
|
-
try {
|
|
290
|
-
createValidatedConfig({
|
|
291
|
-
chainId: '',
|
|
292
|
-
rpcUrl: '',
|
|
293
|
-
gasPrice: '',
|
|
294
|
-
});
|
|
295
|
-
} catch (error) {
|
|
296
|
-
expect((error as ManifestMCPError).code).toBe(ManifestMCPErrorCode.INVALID_CONFIG);
|
|
297
|
-
}
|
|
298
|
-
});
|
|
299
|
-
});
|