@kadi.build/deploy-ability 0.0.3 → 0.0.5
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/targets/akash/bids.d.ts +183 -0
- package/dist/targets/akash/bids.d.ts.map +1 -0
- package/dist/targets/akash/bids.js +247 -0
- package/dist/targets/akash/bids.js.map +1 -0
- package/dist/targets/akash/certificate-manager.d.ts +89 -167
- package/dist/targets/akash/certificate-manager.d.ts.map +1 -1
- package/dist/targets/akash/certificate-manager.js +193 -301
- package/dist/targets/akash/certificate-manager.js.map +1 -1
- package/dist/targets/akash/client.d.ts +644 -0
- package/dist/targets/akash/client.d.ts.map +1 -0
- package/dist/targets/akash/client.js +972 -0
- package/dist/targets/akash/client.js.map +1 -0
- package/dist/targets/akash/constants.d.ts +12 -149
- package/dist/targets/akash/constants.d.ts.map +1 -1
- package/dist/targets/akash/constants.js +14 -136
- package/dist/targets/akash/constants.js.map +1 -1
- package/dist/targets/akash/deployer.d.ts +3 -82
- package/dist/targets/akash/deployer.d.ts.map +1 -1
- package/dist/targets/akash/deployer.js +122 -160
- package/dist/targets/akash/deployer.js.map +1 -1
- package/dist/targets/akash/environment.d.ts +16 -214
- package/dist/targets/akash/environment.d.ts.map +1 -1
- package/dist/targets/akash/environment.js +20 -210
- package/dist/targets/akash/environment.js.map +1 -1
- package/dist/targets/akash/index.d.ts +95 -189
- package/dist/targets/akash/index.d.ts.map +1 -1
- package/dist/targets/akash/index.js +69 -197
- package/dist/targets/akash/index.js.map +1 -1
- package/dist/targets/akash/lease-monitor.d.ts +3 -21
- package/dist/targets/akash/lease-monitor.d.ts.map +1 -1
- package/dist/targets/akash/lease-monitor.js +39 -56
- package/dist/targets/akash/lease-monitor.js.map +1 -1
- package/dist/targets/akash/logs.d.ts +103 -4
- package/dist/targets/akash/logs.d.ts.map +1 -1
- package/dist/targets/akash/logs.js +12 -3
- package/dist/targets/akash/logs.js.map +1 -1
- package/dist/targets/akash/pricing.d.ts +12 -191
- package/dist/targets/akash/pricing.d.ts.map +1 -1
- package/dist/targets/akash/pricing.js +12 -188
- package/dist/targets/akash/pricing.js.map +1 -1
- package/dist/targets/akash/provider-manager.d.ts +120 -0
- package/dist/targets/akash/provider-manager.d.ts.map +1 -0
- package/dist/targets/akash/provider-manager.js +574 -0
- package/dist/targets/akash/provider-manager.js.map +1 -0
- package/dist/targets/akash/sdl-generator.d.ts +2 -2
- package/dist/targets/akash/sdl-generator.d.ts.map +1 -1
- package/dist/targets/akash/sdl-generator.js +6 -39
- package/dist/targets/akash/sdl-generator.js.map +1 -1
- package/dist/targets/akash/types.d.ts +66 -243
- package/dist/targets/akash/types.d.ts.map +1 -1
- package/dist/targets/akash/types.js +4 -41
- package/dist/targets/akash/types.js.map +1 -1
- package/dist/targets/akash/wallet-manager.d.ts +35 -352
- package/dist/targets/akash/wallet-manager.d.ts.map +1 -1
- package/dist/targets/akash/wallet-manager.js +37 -439
- package/dist/targets/akash/wallet-manager.js.map +1 -1
- package/dist/targets/local/compose-generator.d.ts.map +1 -1
- package/dist/targets/local/compose-generator.js +1 -0
- package/dist/targets/local/compose-generator.js.map +1 -1
- package/dist/targets/local/deployer.js +4 -4
- package/dist/targets/local/deployer.js.map +1 -1
- package/dist/targets/local/types.d.ts +4 -0
- package/dist/targets/local/types.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/options.d.ts +45 -4
- package/dist/types/options.d.ts.map +1 -1
- package/dist/utils/registry/manager.js +6 -6
- package/dist/utils/registry/manager.js.map +1 -1
- package/dist/utils/registry/setup.js +4 -4
- package/dist/utils/registry/setup.js.map +1 -1
- package/docs/KADI_ABILITY_CONVERSION.md +1365 -0
- package/docs/PIPELINE_BUILDER_DESIGN.md +1149 -0
- package/package.json +8 -11
- package/dist/targets/akash/bid-selectors.d.ts +0 -251
- package/dist/targets/akash/bid-selectors.d.ts.map +0 -1
- package/dist/targets/akash/bid-selectors.js +0 -322
- package/dist/targets/akash/bid-selectors.js.map +0 -1
- package/dist/targets/akash/bid-types.d.ts +0 -297
- package/dist/targets/akash/bid-types.d.ts.map +0 -1
- package/dist/targets/akash/bid-types.js +0 -89
- package/dist/targets/akash/bid-types.js.map +0 -1
- package/dist/targets/akash/blockchain-client.d.ts +0 -577
- package/dist/targets/akash/blockchain-client.d.ts.map +0 -1
- package/dist/targets/akash/blockchain-client.js +0 -803
- package/dist/targets/akash/blockchain-client.js.map +0 -1
- package/dist/targets/akash/logs.types.d.ts +0 -102
- package/dist/targets/akash/logs.types.d.ts.map +0 -1
- package/dist/targets/akash/logs.types.js +0 -9
- package/dist/targets/akash/logs.types.js.map +0 -1
- package/dist/targets/akash/provider-client.d.ts +0 -114
- package/dist/targets/akash/provider-client.d.ts.map +0 -1
- package/dist/targets/akash/provider-client.js +0 -318
- package/dist/targets/akash/provider-client.js.map +0 -1
- package/dist/targets/akash/provider-metadata.d.ts +0 -228
- package/dist/targets/akash/provider-metadata.d.ts.map +0 -1
- package/dist/targets/akash/provider-metadata.js +0 -14
- package/dist/targets/akash/provider-metadata.js.map +0 -1
- package/dist/targets/akash/provider-service.d.ts +0 -133
- package/dist/targets/akash/provider-service.d.ts.map +0 -1
- package/dist/targets/akash/provider-service.js +0 -391
- package/dist/targets/akash/provider-service.js.map +0 -1
- package/dist/targets/akash/query-client.d.ts +0 -125
- package/dist/targets/akash/query-client.d.ts.map +0 -1
- package/dist/targets/akash/query-client.js +0 -332
- package/dist/targets/akash/query-client.js.map +0 -1
- package/docs/EXAMPLES.md +0 -293
- package/docs/PLACEMENT.md +0 -433
- package/docs/STORAGE.md +0 -318
|
@@ -0,0 +1,972 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Akash Blockchain Client
|
|
3
|
+
*
|
|
4
|
+
* Single SDK instance manager for all Akash blockchain operations.
|
|
5
|
+
* Eliminates SDK duplication by creating one instance reused across all methods.
|
|
6
|
+
*
|
|
7
|
+
* **Usage Pattern:**
|
|
8
|
+
* ```typescript
|
|
9
|
+
* // Create client (read-only or with signer)
|
|
10
|
+
* const client = new AkashClient({ network: 'mainnet', signer });
|
|
11
|
+
*
|
|
12
|
+
* // Perform operations
|
|
13
|
+
* const bids = await client.getBids(wallet, dseq);
|
|
14
|
+
* const lease = await client.acceptBid(bids.data[0].bid);
|
|
15
|
+
*
|
|
16
|
+
* // Cleanup
|
|
17
|
+
* await client.disconnect();
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @module targets/akash/client
|
|
21
|
+
*/
|
|
22
|
+
import Long from 'long';
|
|
23
|
+
import { createChainNodeWebSDK } from '@akashnetwork/chain-sdk/web';
|
|
24
|
+
import { createStargateClient } from '@akashnetwork/chain-sdk';
|
|
25
|
+
import { success, failure } from '../../types/index.js';
|
|
26
|
+
import { DeploymentError } from '../../errors/index.js';
|
|
27
|
+
import { getNetworkConfig } from './environment.js';
|
|
28
|
+
import { createBidPricing } from './bids.js';
|
|
29
|
+
import { fetchAllProviders } from './provider-manager.js';
|
|
30
|
+
import { CertificateManager } from './certificate-manager.js';
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// AkashClient Class
|
|
33
|
+
// ============================================================================
|
|
34
|
+
/**
|
|
35
|
+
* Akash blockchain client with managed SDK lifecycle
|
|
36
|
+
*
|
|
37
|
+
* @example Query Operations (No Signer)
|
|
38
|
+
* ```typescript
|
|
39
|
+
* const client = new AkashClient({ network: 'mainnet' });
|
|
40
|
+
*
|
|
41
|
+
* const deployment = await client.getDeployment('akash1...', 12345);
|
|
42
|
+
* const bids = await client.getBids({ address: 'akash1...' }, 12345);
|
|
43
|
+
*
|
|
44
|
+
* await client.disconnect();
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @example Write Operations (Signer Required)
|
|
48
|
+
* ```typescript
|
|
49
|
+
* const client = new AkashClient({
|
|
50
|
+
* network: 'mainnet',
|
|
51
|
+
* signer: keplrSigner
|
|
52
|
+
* });
|
|
53
|
+
*
|
|
54
|
+
* const deployment = await client.createDeployment(sdl, 5);
|
|
55
|
+
* const bids = await client.awaitBids({ address }, deployment.data.dseq);
|
|
56
|
+
* const lease = await client.acceptBid(bids.data[0].bid);
|
|
57
|
+
*
|
|
58
|
+
* await client.disconnect();
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export class AkashClient {
|
|
62
|
+
/**
|
|
63
|
+
* Chain SDK instance (created once, reused for all operations)
|
|
64
|
+
* @private
|
|
65
|
+
*/
|
|
66
|
+
sdk;
|
|
67
|
+
/**
|
|
68
|
+
* Network configuration
|
|
69
|
+
* @private
|
|
70
|
+
*/
|
|
71
|
+
network;
|
|
72
|
+
config;
|
|
73
|
+
/**
|
|
74
|
+
* Optional signer for write operations
|
|
75
|
+
* @private
|
|
76
|
+
*/
|
|
77
|
+
signer;
|
|
78
|
+
/**
|
|
79
|
+
* Gas multiplier for transaction safety
|
|
80
|
+
* @private
|
|
81
|
+
*/
|
|
82
|
+
gasMultiplier;
|
|
83
|
+
/**
|
|
84
|
+
* Lazy-initialized certificate manager
|
|
85
|
+
* @private
|
|
86
|
+
*/
|
|
87
|
+
certManager;
|
|
88
|
+
// ==========================================================================
|
|
89
|
+
// Constructor
|
|
90
|
+
// ==========================================================================
|
|
91
|
+
/**
|
|
92
|
+
* Create new Akash blockchain client
|
|
93
|
+
*
|
|
94
|
+
* Initializes a single SDK instance that will be reused for all operations.
|
|
95
|
+
* This is the key improvement over the old implementation which created
|
|
96
|
+
* 7+ SDK instances per deployment.
|
|
97
|
+
*
|
|
98
|
+
* @param options - Client configuration
|
|
99
|
+
*
|
|
100
|
+
* @example Read-Only Client
|
|
101
|
+
* ```typescript
|
|
102
|
+
* const client = new AkashClient({ network: 'mainnet' });
|
|
103
|
+
* // Can only perform queries (getBids, getDeployment, etc.)
|
|
104
|
+
* ```
|
|
105
|
+
*
|
|
106
|
+
* @example Full Client (Read + Write)
|
|
107
|
+
* ```typescript
|
|
108
|
+
* const client = new AkashClient({
|
|
109
|
+
* network: 'mainnet',
|
|
110
|
+
* signer: keplrSigner,
|
|
111
|
+
* gasMultiplier: 1.6
|
|
112
|
+
* });
|
|
113
|
+
* // Can perform queries AND transactions
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
constructor(options) {
|
|
117
|
+
this.network = options.network;
|
|
118
|
+
this.config = getNetworkConfig(options.network);
|
|
119
|
+
this.signer = options.signer;
|
|
120
|
+
this.gasMultiplier = options.gasMultiplier ?? 1.6;
|
|
121
|
+
// Create SDK once - this is the key optimization!
|
|
122
|
+
// Old code created this 7+ times per deployment
|
|
123
|
+
//
|
|
124
|
+
// SDK SELECTION: Why We Use createChainNodeWebSDK
|
|
125
|
+
// ================================================
|
|
126
|
+
// chain-sdk provides TWO SDK creation functions:
|
|
127
|
+
//
|
|
128
|
+
// 1. createChainNodeSDK (Node.js) - Uses gRPC protocol (binary)
|
|
129
|
+
// - Requires: gRPC endpoint (e.g., http://grpc.akashnet.net:9090)
|
|
130
|
+
// - Problem: Public gRPC endpoints are NOT accessible (firewall/proxy issues)
|
|
131
|
+
// - Use case: Internal/private networks with gRPC access
|
|
132
|
+
//
|
|
133
|
+
// 2. createChainNodeWebSDK (Web) - Uses gRPC Gateway protocol (HTTP/JSON)
|
|
134
|
+
// - Requires: REST endpoint (e.g., https://api.akashnet.net:443)
|
|
135
|
+
// - Benefit: REST endpoints ARE publicly accessible
|
|
136
|
+
// - Use case: Public networks, browsers, CLI tools
|
|
137
|
+
//
|
|
138
|
+
// Both return the SAME API - only the transport layer differs.
|
|
139
|
+
// We use the Web SDK because Akash's public infrastructure provides REST/LCD endpoints,
|
|
140
|
+
// not raw gRPC endpoints.
|
|
141
|
+
this.sdk = createChainNodeWebSDK({
|
|
142
|
+
query: {
|
|
143
|
+
baseUrl: this.config.rest // REST API (gRPC Gateway): https://api.akashnet.net:443
|
|
144
|
+
},
|
|
145
|
+
tx: options.signer ? {
|
|
146
|
+
signer: createStargateClient({
|
|
147
|
+
baseUrl: this.config.rpc, // RPC for transactions: https://rpc.akashnet.net:443
|
|
148
|
+
signer: options.signer,
|
|
149
|
+
gasMultiplier: this.gasMultiplier
|
|
150
|
+
})
|
|
151
|
+
} : undefined
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
// ==========================================================================
|
|
155
|
+
// Query Methods (Read-Only - No Signer Required)
|
|
156
|
+
// ==========================================================================
|
|
157
|
+
/**
|
|
158
|
+
* Fetch provider bids for a deployment
|
|
159
|
+
*
|
|
160
|
+
* Queries the Akash marketplace for open bids on a specific deployment.
|
|
161
|
+
* Automatically filters blacklisted providers and optionally enriches
|
|
162
|
+
* with provider metadata (uptime, location, audit status).
|
|
163
|
+
*
|
|
164
|
+
* **Performance**: ~200ms (first call), ~50ms (subsequent calls with warm SDK)
|
|
165
|
+
*
|
|
166
|
+
* @param wallet - Wallet context (for filtering bids by owner)
|
|
167
|
+
* @param dseq - Deployment sequence number
|
|
168
|
+
* @param options - Optional filtering and enhancement options
|
|
169
|
+
* @returns Array of enhanced bids with pricing and provider info
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```typescript
|
|
173
|
+
* const bids = await client.getBids(
|
|
174
|
+
* { address: 'akash1...' },
|
|
175
|
+
* 12345,
|
|
176
|
+
* { filterOffline: true, blacklist: ['akash1bad...'] }
|
|
177
|
+
* );
|
|
178
|
+
*
|
|
179
|
+
* if (bids.success) {
|
|
180
|
+
* console.log(`Found ${bids.data.length} bids`);
|
|
181
|
+
* console.log(`Cheapest: ${bids.data[0].pricing.akt.perMonth} AKT/month`);
|
|
182
|
+
* }
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
async getBids(wallet, dseq, options) {
|
|
186
|
+
try {
|
|
187
|
+
const { blacklist = [], filterOffline = true, includeProviderMetadata = true } = options ?? {};
|
|
188
|
+
// Query marketplace for bids (reuses this.sdk)
|
|
189
|
+
const response = await this.sdk.akash.market.v1beta5.getBids({
|
|
190
|
+
filters: {
|
|
191
|
+
owner: wallet.address,
|
|
192
|
+
dseq: Long.fromNumber(dseq),
|
|
193
|
+
state: 'open'
|
|
194
|
+
},
|
|
195
|
+
pagination: {
|
|
196
|
+
limit: 100n
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
// Extract valid bids
|
|
200
|
+
const validBids = this.extractValidBids(response);
|
|
201
|
+
if (validBids.length === 0) {
|
|
202
|
+
return success([]);
|
|
203
|
+
}
|
|
204
|
+
// Filter blacklisted providers (user-provided only)
|
|
205
|
+
const filteredBids = validBids.filter(bid => {
|
|
206
|
+
const provider = bid.id?.provider ?? bid.bidId?.provider ?? '';
|
|
207
|
+
return !blacklist.includes(provider);
|
|
208
|
+
});
|
|
209
|
+
// Optionally enrich with provider metadata
|
|
210
|
+
let enhancedBids;
|
|
211
|
+
if (includeProviderMetadata) {
|
|
212
|
+
// Fetch provider metadata from Console API
|
|
213
|
+
const providersResult = await fetchAllProviders(this.network);
|
|
214
|
+
const providerMap = providersResult.success
|
|
215
|
+
? providersResult.data
|
|
216
|
+
: new Map();
|
|
217
|
+
// Enhance bids with pricing and provider info
|
|
218
|
+
enhancedBids = filteredBids.map(bid => {
|
|
219
|
+
const bidId = bid.id ?? bid.bidId;
|
|
220
|
+
const providerId = bidId?.provider ?? '';
|
|
221
|
+
const providerInfo = providerMap.get(providerId);
|
|
222
|
+
// Create unique ID from bid coordinates
|
|
223
|
+
const id = bidId
|
|
224
|
+
? `${bidId.owner}/${bidId.dseq}/${bidId.gseq}/${bidId.oseq}/${bidId.provider}`
|
|
225
|
+
: '';
|
|
226
|
+
// Convert createdAt (Long) to Date
|
|
227
|
+
const createdAt = bid.createdAt ? new Date(Number(bid.createdAt) * 1000) : new Date();
|
|
228
|
+
// Default provider info for bids without metadata
|
|
229
|
+
const defaultProviderInfo = {
|
|
230
|
+
owner: providerId,
|
|
231
|
+
hostUri: '',
|
|
232
|
+
isAudited: false
|
|
233
|
+
};
|
|
234
|
+
return {
|
|
235
|
+
id,
|
|
236
|
+
bid,
|
|
237
|
+
pricing: createBidPricing(bid.price ?? { denom: 'uakt', amount: '0' }),
|
|
238
|
+
provider: providerInfo ?? defaultProviderInfo,
|
|
239
|
+
createdAt
|
|
240
|
+
};
|
|
241
|
+
});
|
|
242
|
+
// Filter offline providers if requested
|
|
243
|
+
if (filterOffline) {
|
|
244
|
+
enhancedBids = enhancedBids.filter(bid => bid.provider?.reliability?.isOnline !== false);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
// Just pricing, no provider metadata
|
|
249
|
+
enhancedBids = filteredBids.map(bid => {
|
|
250
|
+
const bidId = bid.id ?? bid.bidId;
|
|
251
|
+
const providerId = bidId?.provider ?? '';
|
|
252
|
+
// Create unique ID from bid coordinates
|
|
253
|
+
const id = bidId
|
|
254
|
+
? `${bidId.owner}/${bidId.dseq}/${bidId.gseq}/${bidId.oseq}/${bidId.provider}`
|
|
255
|
+
: '';
|
|
256
|
+
// Convert createdAt (Long) to Date
|
|
257
|
+
const createdAt = bid.createdAt ? new Date(Number(bid.createdAt) * 1000) : new Date();
|
|
258
|
+
// Default provider info
|
|
259
|
+
const defaultProviderInfo = {
|
|
260
|
+
owner: providerId,
|
|
261
|
+
hostUri: '',
|
|
262
|
+
isAudited: false
|
|
263
|
+
};
|
|
264
|
+
return {
|
|
265
|
+
id,
|
|
266
|
+
bid,
|
|
267
|
+
pricing: createBidPricing(bid.price ?? { denom: 'uakt', amount: '0' }),
|
|
268
|
+
provider: defaultProviderInfo,
|
|
269
|
+
createdAt
|
|
270
|
+
};
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
return success(enhancedBids);
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
return failure(new DeploymentError('Failed to fetch bids from marketplace', 'BID_QUERY_FAILED', { network: this.network, dseq, error: error instanceof Error ? error.message : String(error) }, true, 'Verify network connectivity and that deployment exists'));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Extract valid bids from marketplace response
|
|
281
|
+
*
|
|
282
|
+
* Filters out invalid/malformed bids from the raw response.
|
|
283
|
+
*
|
|
284
|
+
* @param response - Raw marketplace response
|
|
285
|
+
* @returns Array of valid bids
|
|
286
|
+
* @private
|
|
287
|
+
*/
|
|
288
|
+
extractValidBids(response) {
|
|
289
|
+
if (!response.bids || !Array.isArray(response.bids)) {
|
|
290
|
+
return [];
|
|
291
|
+
}
|
|
292
|
+
return response.bids
|
|
293
|
+
.filter((b) => b.bid !== undefined && b.bid !== null)
|
|
294
|
+
.map((b) => b.bid)
|
|
295
|
+
.filter((bid) => {
|
|
296
|
+
const providerId = bid.id?.provider ?? bid.bidId?.provider ?? '';
|
|
297
|
+
return providerId !== '';
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Poll marketplace for bids with timeout
|
|
302
|
+
*
|
|
303
|
+
* Waits for at least `minBidCount` bids to appear, polling the marketplace
|
|
304
|
+
* at regular intervals. Returns when bids are found or timeout is reached.
|
|
305
|
+
*
|
|
306
|
+
* **Use case**: After creating a deployment, wait for providers to bid
|
|
307
|
+
*
|
|
308
|
+
* @param wallet - Wallet context
|
|
309
|
+
* @param dseq - Deployment sequence number
|
|
310
|
+
* @param options - Polling configuration
|
|
311
|
+
* @returns Array of bids when found, or error if timeout
|
|
312
|
+
*
|
|
313
|
+
* @example
|
|
314
|
+
* ```typescript
|
|
315
|
+
* // Wait up to 2 minutes for at least 3 bids
|
|
316
|
+
* const bids = await client.awaitBids(
|
|
317
|
+
* { address: 'akash1...' },
|
|
318
|
+
* 12345,
|
|
319
|
+
* { timeout: 120000, minBidCount: 3 }
|
|
320
|
+
* );
|
|
321
|
+
*
|
|
322
|
+
* if (bids.success) {
|
|
323
|
+
* console.log(`Received ${bids.data.length} bids`);
|
|
324
|
+
* }
|
|
325
|
+
* ```
|
|
326
|
+
*/
|
|
327
|
+
async awaitBids(wallet, dseq, options) {
|
|
328
|
+
const { timeout = 60000, pollInterval = 5000, minBidCount = 1, ...bidOptions } = options ?? {};
|
|
329
|
+
const startTime = Date.now();
|
|
330
|
+
while (true) {
|
|
331
|
+
// Query for bids
|
|
332
|
+
const bidsResult = await this.getBids(wallet, dseq, bidOptions);
|
|
333
|
+
if (!bidsResult.success) {
|
|
334
|
+
return bidsResult;
|
|
335
|
+
}
|
|
336
|
+
// Check if we have enough bids
|
|
337
|
+
if (bidsResult.data.length >= minBidCount) {
|
|
338
|
+
return bidsResult;
|
|
339
|
+
}
|
|
340
|
+
// Check timeout
|
|
341
|
+
const elapsed = Date.now() - startTime;
|
|
342
|
+
if (elapsed >= timeout) {
|
|
343
|
+
return failure(new DeploymentError(`Timeout waiting for bids (waited ${elapsed}ms, found ${bidsResult.data.length}/${minBidCount} bids)`, 'BID_TIMEOUT', { dseq, timeout, minBidCount, found: bidsResult.data.length }, true, 'Try increasing timeout or reducing minBidCount. Check deployment is visible on blockchain.'));
|
|
344
|
+
}
|
|
345
|
+
// Wait before next poll
|
|
346
|
+
await new Promise(resolve => setTimeout(resolve, pollInterval));
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Fetch lease details by ID
|
|
351
|
+
*
|
|
352
|
+
* @param lease - Lease identifier
|
|
353
|
+
* @returns Lease details from blockchain
|
|
354
|
+
*
|
|
355
|
+
* @example
|
|
356
|
+
* ```typescript
|
|
357
|
+
* const lease = await client.getLeaseById({
|
|
358
|
+
* owner: 'akash1...',
|
|
359
|
+
* dseq: 12345,
|
|
360
|
+
* gseq: 1,
|
|
361
|
+
* oseq: 1,
|
|
362
|
+
* provider: 'akash1provider...'
|
|
363
|
+
* });
|
|
364
|
+
*
|
|
365
|
+
* console.log(`Lease state: ${lease.data.state}`);
|
|
366
|
+
* console.log(`Price: ${lease.data.price.amount} ${lease.data.price.denom}`);
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
async getLeaseById(lease) {
|
|
370
|
+
try {
|
|
371
|
+
// Query blockchain (reuses this.sdk)
|
|
372
|
+
const response = await this.sdk.akash.market.v1beta5.getLease({
|
|
373
|
+
id: {
|
|
374
|
+
owner: lease.owner,
|
|
375
|
+
dseq: Long.fromNumber(lease.dseq),
|
|
376
|
+
gseq: lease.gseq,
|
|
377
|
+
oseq: lease.oseq,
|
|
378
|
+
provider: lease.provider
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
const details = this.mapLease(response);
|
|
382
|
+
if (!details) {
|
|
383
|
+
return failure(new DeploymentError('Lease not found', 'LEASE_NOT_FOUND', { network: this.network, lease }));
|
|
384
|
+
}
|
|
385
|
+
return success(details);
|
|
386
|
+
}
|
|
387
|
+
catch (error) {
|
|
388
|
+
return failure(new DeploymentError('Failed to query lease', 'RPC_ERROR', { network: this.network, error: error instanceof Error ? error.message : String(error) }, true));
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Fetch multiple leases with optional filtering
|
|
393
|
+
*
|
|
394
|
+
* @param wallet - Wallet context
|
|
395
|
+
* @param filters - Optional filters (dseq, state, provider)
|
|
396
|
+
* @returns Array of matching leases
|
|
397
|
+
*
|
|
398
|
+
* @example
|
|
399
|
+
* ```typescript
|
|
400
|
+
* // Get all active leases
|
|
401
|
+
* const leases = await client.getLeases(
|
|
402
|
+
* { address: 'akash1...' },
|
|
403
|
+
* { state: 'active' }
|
|
404
|
+
* );
|
|
405
|
+
*
|
|
406
|
+
* console.log(`Active leases: ${leases.data.length}`);
|
|
407
|
+
* ```
|
|
408
|
+
*/
|
|
409
|
+
async getLeases(wallet, filters) {
|
|
410
|
+
try {
|
|
411
|
+
const queryFilters = {
|
|
412
|
+
owner: wallet.address
|
|
413
|
+
};
|
|
414
|
+
if (filters?.dseq !== undefined) {
|
|
415
|
+
queryFilters.dseq = Long.fromString(String(filters.dseq));
|
|
416
|
+
}
|
|
417
|
+
if (filters?.provider)
|
|
418
|
+
queryFilters.provider = filters.provider;
|
|
419
|
+
if (typeof filters?.gseq === 'number')
|
|
420
|
+
queryFilters.gseq = filters.gseq;
|
|
421
|
+
if (typeof filters?.oseq === 'number')
|
|
422
|
+
queryFilters.oseq = filters.oseq;
|
|
423
|
+
if (filters?.state)
|
|
424
|
+
queryFilters.state = filters.state;
|
|
425
|
+
// Query blockchain (reuses this.sdk)
|
|
426
|
+
const response = await this.sdk.akash.market.v1beta5.getLeases({
|
|
427
|
+
filters: queryFilters
|
|
428
|
+
});
|
|
429
|
+
const leases = [];
|
|
430
|
+
for (const entry of response.leases ?? []) {
|
|
431
|
+
const details = this.mapLease(entry);
|
|
432
|
+
if (details) {
|
|
433
|
+
leases.push(details);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return success(Object.freeze(leases));
|
|
437
|
+
}
|
|
438
|
+
catch (error) {
|
|
439
|
+
return failure(new DeploymentError('Failed to query leases', 'RPC_ERROR', { network: this.network, error: error instanceof Error ? error.message : String(error) }, true));
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Map lease response to LeaseDetails
|
|
444
|
+
* @private
|
|
445
|
+
*/
|
|
446
|
+
mapLease(response) {
|
|
447
|
+
if (!response?.lease) {
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
const lease = response.lease;
|
|
451
|
+
const id = lease.id; // FIX: Was lease.leaseId, but actual field is lease.id
|
|
452
|
+
if (!id) {
|
|
453
|
+
return null;
|
|
454
|
+
}
|
|
455
|
+
return {
|
|
456
|
+
owner: id.owner,
|
|
457
|
+
provider: id.provider,
|
|
458
|
+
dseq: this.longToString(id.dseq),
|
|
459
|
+
gseq: id.gseq,
|
|
460
|
+
oseq: id.oseq,
|
|
461
|
+
state: this.mapLeaseState(lease.state),
|
|
462
|
+
price: {
|
|
463
|
+
denom: lease.price?.denom ?? 'uakt',
|
|
464
|
+
amount: lease.price?.amount ?? '0'
|
|
465
|
+
},
|
|
466
|
+
createdAt: this.longToString(lease.createdAt ?? Long.ZERO)
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Map lease state enum to string
|
|
471
|
+
* @private
|
|
472
|
+
*/
|
|
473
|
+
mapLeaseState(state) {
|
|
474
|
+
if (typeof state === 'string') {
|
|
475
|
+
// Validate and return known states
|
|
476
|
+
if (state === 'active' || state === 'closed' || state === 'insufficient_funds') {
|
|
477
|
+
return state;
|
|
478
|
+
}
|
|
479
|
+
// Default to closed for unknown string states
|
|
480
|
+
return 'closed';
|
|
481
|
+
}
|
|
482
|
+
switch (state) {
|
|
483
|
+
case 1: return 'active';
|
|
484
|
+
case 2: return 'insufficient_funds';
|
|
485
|
+
case 3: return 'closed';
|
|
486
|
+
default: return 'closed'; // Default to closed for unknown states
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Convert Long to string
|
|
491
|
+
* @private
|
|
492
|
+
*/
|
|
493
|
+
longToString(value) {
|
|
494
|
+
if (Long.isLong(value)) {
|
|
495
|
+
return value.toString();
|
|
496
|
+
}
|
|
497
|
+
return String(value);
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Fetch deployment details from blockchain
|
|
501
|
+
*
|
|
502
|
+
* @param owner - Wallet address that created deployment
|
|
503
|
+
* @param dseq - Deployment sequence number
|
|
504
|
+
* @returns Deployment details with groups and resources
|
|
505
|
+
*
|
|
506
|
+
* @example
|
|
507
|
+
* ```typescript
|
|
508
|
+
* const deployment = await client.getDeployment('akash1...', 12345);
|
|
509
|
+
*
|
|
510
|
+
* console.log(`State: ${deployment.data.state}`);
|
|
511
|
+
* console.log(`Groups: ${deployment.data.groups.length}`);
|
|
512
|
+
* ```
|
|
513
|
+
*/
|
|
514
|
+
async getDeployment(owner, dseq) {
|
|
515
|
+
try {
|
|
516
|
+
// Query blockchain (reuses this.sdk)
|
|
517
|
+
const response = await this.sdk.akash.deployment.v1beta4.getDeployment({
|
|
518
|
+
id: {
|
|
519
|
+
owner,
|
|
520
|
+
dseq: Long.fromNumber(dseq)
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
if (!response.deployment) {
|
|
524
|
+
return failure(new DeploymentError('Deployment not found', 'DEPLOYMENT_NOT_FOUND', { network: this.network, owner, dseq }));
|
|
525
|
+
}
|
|
526
|
+
const details = this.mapDeployment(response);
|
|
527
|
+
return success(details);
|
|
528
|
+
}
|
|
529
|
+
catch (error) {
|
|
530
|
+
return failure(new DeploymentError('Failed to query deployment', 'RPC_ERROR', { network: this.network, error: error instanceof Error ? error.message : String(error) }, true));
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Map deployment response to DeploymentDetails
|
|
535
|
+
* @private
|
|
536
|
+
*/
|
|
537
|
+
mapDeployment(response) {
|
|
538
|
+
const deployment = response.deployment;
|
|
539
|
+
const owner = deployment.deploymentId?.owner ?? '';
|
|
540
|
+
const dseq = deployment.deploymentId?.dseq ?? '';
|
|
541
|
+
const state = this.mapDeploymentState(deployment.state);
|
|
542
|
+
const version = this.toHexString(deployment.version);
|
|
543
|
+
const createdAt = this.longToString(deployment.createdAt ?? Long.ZERO);
|
|
544
|
+
const groups = (response.groups ?? []).map((group) => {
|
|
545
|
+
const groupSpec = group.groupSpec;
|
|
546
|
+
const resources = (groupSpec?.resources ?? []).map((item) => {
|
|
547
|
+
const resource = item.resource;
|
|
548
|
+
return {
|
|
549
|
+
resourceId: resource?.id ?? 0,
|
|
550
|
+
count: item.count ?? 0,
|
|
551
|
+
cpuUnits: this.decodeResourceValue(resource?.cpu?.units?.val),
|
|
552
|
+
memoryQuantity: this.decodeResourceValue(resource?.memory?.quantity?.val),
|
|
553
|
+
storageQuantities: (resource?.storage ?? []).map((storage) => this.decodeResourceValue(storage.quantity?.val))
|
|
554
|
+
};
|
|
555
|
+
});
|
|
556
|
+
return {
|
|
557
|
+
name: groupSpec?.name ?? 'group',
|
|
558
|
+
resources: Object.freeze(resources)
|
|
559
|
+
};
|
|
560
|
+
});
|
|
561
|
+
return {
|
|
562
|
+
owner,
|
|
563
|
+
dseq: String(dseq),
|
|
564
|
+
state,
|
|
565
|
+
version,
|
|
566
|
+
createdAt,
|
|
567
|
+
groups: Object.freeze(groups)
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Map deployment state enum to string
|
|
572
|
+
* @private
|
|
573
|
+
*/
|
|
574
|
+
mapDeploymentState(state) {
|
|
575
|
+
if (typeof state === 'string') {
|
|
576
|
+
return state;
|
|
577
|
+
}
|
|
578
|
+
switch (state) {
|
|
579
|
+
case 1: return 'active';
|
|
580
|
+
case 2: return 'closed';
|
|
581
|
+
default: return 'unknown';
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Decode resource value from protobuf bytes
|
|
586
|
+
* @private
|
|
587
|
+
*/
|
|
588
|
+
decodeResourceValue(value) {
|
|
589
|
+
if (!value || value.length === 0) {
|
|
590
|
+
return '0';
|
|
591
|
+
}
|
|
592
|
+
return new TextDecoder().decode(value);
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Convert binary version to hex string
|
|
596
|
+
* @private
|
|
597
|
+
*/
|
|
598
|
+
toHexString(value) {
|
|
599
|
+
if (!value || value.length === 0) {
|
|
600
|
+
return '';
|
|
601
|
+
}
|
|
602
|
+
return Buffer.from(value).toString('hex');
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Fetch provider metadata from blockchain
|
|
606
|
+
*
|
|
607
|
+
* @param providerAddress - Provider wallet address
|
|
608
|
+
* @returns Provider metadata (hostUri, attributes)
|
|
609
|
+
*
|
|
610
|
+
* @example
|
|
611
|
+
* ```typescript
|
|
612
|
+
* const provider = await client.getProvider('akash1provider...');
|
|
613
|
+
*
|
|
614
|
+
* console.log(`Host: ${provider.data.hostUri}`);
|
|
615
|
+
* console.log(`Audited: ${provider.data.attributes.some(a => a.key === 'audited-by')}`);
|
|
616
|
+
* ```
|
|
617
|
+
*/
|
|
618
|
+
async getProvider(providerAddress) {
|
|
619
|
+
try {
|
|
620
|
+
// Query blockchain (reuses this.sdk)
|
|
621
|
+
const response = await this.sdk.akash.provider.v1beta4.getProvider({
|
|
622
|
+
owner: providerAddress
|
|
623
|
+
});
|
|
624
|
+
if (!response.provider) {
|
|
625
|
+
return success(undefined);
|
|
626
|
+
}
|
|
627
|
+
return success(this.mapProvider(response.provider));
|
|
628
|
+
}
|
|
629
|
+
catch (error) {
|
|
630
|
+
return failure(new DeploymentError('Failed to query provider', 'RPC_ERROR', { network: this.network, error: error instanceof Error ? error.message : String(error) }, true));
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Map provider response to ProviderMetadata
|
|
635
|
+
* @private
|
|
636
|
+
*/
|
|
637
|
+
mapProvider(provider) {
|
|
638
|
+
return {
|
|
639
|
+
owner: provider.owner,
|
|
640
|
+
hostUri: this.normalizeProviderUri(provider.hostUri),
|
|
641
|
+
attributes: Object.freeze((provider.attributes ?? []).map((attr) => ({
|
|
642
|
+
key: attr.key ?? '',
|
|
643
|
+
value: attr.value ?? ''
|
|
644
|
+
})))
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Normalize provider URI
|
|
649
|
+
* @private
|
|
650
|
+
*/
|
|
651
|
+
normalizeProviderUri(uri) {
|
|
652
|
+
if (!uri) {
|
|
653
|
+
return undefined;
|
|
654
|
+
}
|
|
655
|
+
try {
|
|
656
|
+
const parsed = new URL(uri);
|
|
657
|
+
if (parsed.protocol !== 'https:') {
|
|
658
|
+
return undefined;
|
|
659
|
+
}
|
|
660
|
+
parsed.pathname = parsed.pathname.replace(/\/$/, '');
|
|
661
|
+
return parsed.toString();
|
|
662
|
+
}
|
|
663
|
+
catch {
|
|
664
|
+
return undefined;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
// ==========================================================================
|
|
668
|
+
// Transaction Methods (Write - Signer Required)
|
|
669
|
+
// ==========================================================================
|
|
670
|
+
/**
|
|
671
|
+
* Create deployment on Akash blockchain
|
|
672
|
+
*
|
|
673
|
+
* Submits an SDL to the blockchain, creating a new deployment that
|
|
674
|
+
* providers can bid on. Requires signing capability.
|
|
675
|
+
*
|
|
676
|
+
* **Gas cost**: ~0.05 AKT + deposit
|
|
677
|
+
*
|
|
678
|
+
* @param sdl - Service Definition Language document
|
|
679
|
+
* @param depositAkt - Initial deposit in AKT (refunded on close)
|
|
680
|
+
* @returns Deployment sequence number and transaction hash
|
|
681
|
+
*
|
|
682
|
+
* @example
|
|
683
|
+
* ```typescript
|
|
684
|
+
* const result = await client.createDeployment(sdl, 5);
|
|
685
|
+
*
|
|
686
|
+
* if (result.success) {
|
|
687
|
+
* console.log(`Deployment created: DSEQ ${result.data.dseq}`);
|
|
688
|
+
* console.log(`Transaction: ${result.data.transactionHash}`);
|
|
689
|
+
* }
|
|
690
|
+
* ```
|
|
691
|
+
*/
|
|
692
|
+
async createDeployment(sdl, depositAkt = 5) {
|
|
693
|
+
// Validate signer exists
|
|
694
|
+
if (!this.signer) {
|
|
695
|
+
return failure(new DeploymentError('Signer required for creating deployments', 'SIGNER_REQUIRED', {}, false, 'Create AkashClient with a signer to perform write operations', 'error'));
|
|
696
|
+
}
|
|
697
|
+
try {
|
|
698
|
+
const owner = await this.getSignerAddress();
|
|
699
|
+
// Get current block height for dseq
|
|
700
|
+
const blockResult = await this.sdk.cosmos.base.tendermint.v1beta1.getLatestBlock({});
|
|
701
|
+
const dseq = Number(blockResult.block?.header?.height ?? 0);
|
|
702
|
+
// Extract SDL groups and manifest hash
|
|
703
|
+
const groups = sdl.groups();
|
|
704
|
+
const hash = await sdl.manifestVersion();
|
|
705
|
+
// Calculate deposit in uakt (1 AKT = 1,000,000 uakt)
|
|
706
|
+
const aktAmount = Number.isFinite(depositAkt) && depositAkt > 0 ? depositAkt : 5;
|
|
707
|
+
const depositUakt = String(Math.round(aktAmount * 1_000_000));
|
|
708
|
+
// Capture transaction metadata
|
|
709
|
+
let txHash = '';
|
|
710
|
+
let txHeight = 0;
|
|
711
|
+
// Create deployment message (reuses this.sdk)
|
|
712
|
+
const txResponse = await this.sdk.akash.deployment.v1beta4.createDeployment({
|
|
713
|
+
id: { owner, dseq: Long.fromNumber(dseq) },
|
|
714
|
+
groups,
|
|
715
|
+
hash,
|
|
716
|
+
deposit: {
|
|
717
|
+
amount: { denom: 'uakt', amount: depositUakt },
|
|
718
|
+
sources: [1] // Source.balance
|
|
719
|
+
}
|
|
720
|
+
}, {
|
|
721
|
+
afterBroadcast: (response) => {
|
|
722
|
+
txHash = response.transactionHash;
|
|
723
|
+
txHeight = response.height;
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
// If afterBroadcast wasn't called, extract from response
|
|
727
|
+
if (!txHash && txResponse) {
|
|
728
|
+
// The SDK might return the response directly
|
|
729
|
+
const response = txResponse;
|
|
730
|
+
txHash = response.transactionHash || response.txHash || '';
|
|
731
|
+
txHeight = response.height || 0;
|
|
732
|
+
}
|
|
733
|
+
return success({
|
|
734
|
+
dseq,
|
|
735
|
+
transactionHash: txHash,
|
|
736
|
+
height: txHeight
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
catch (error) {
|
|
740
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
741
|
+
console.error('Deployment creation error:', errorMessage, error);
|
|
742
|
+
return failure(new DeploymentError(`Failed to create deployment on blockchain: ${errorMessage}`, 'TRANSACTION_FAILED', { error: errorMessage, details: error }, true, 'Check wallet balance and network connectivity'));
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Get signer address
|
|
747
|
+
* @private
|
|
748
|
+
*/
|
|
749
|
+
async getSignerAddress() {
|
|
750
|
+
if (!this.signer) {
|
|
751
|
+
throw new Error('Signer not available');
|
|
752
|
+
}
|
|
753
|
+
const accounts = await this.signer.getAccounts();
|
|
754
|
+
if (accounts.length === 0) {
|
|
755
|
+
throw new Error('No accounts found in signer');
|
|
756
|
+
}
|
|
757
|
+
const account = accounts[0];
|
|
758
|
+
if (!account) {
|
|
759
|
+
throw new Error('First account is undefined');
|
|
760
|
+
}
|
|
761
|
+
return account.address;
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* Accept a provider bid and create lease
|
|
765
|
+
*
|
|
766
|
+
* Creates a lease by accepting a provider's bid. The provider will then
|
|
767
|
+
* start deploying containers according to the SDL.
|
|
768
|
+
*
|
|
769
|
+
* **Gas cost**: ~0.02 AKT
|
|
770
|
+
*
|
|
771
|
+
* @param bid - Provider bid to accept (from getBids)
|
|
772
|
+
* @returns Lease identifier and transaction hash
|
|
773
|
+
*
|
|
774
|
+
* @example
|
|
775
|
+
* ```typescript
|
|
776
|
+
* const bids = await client.getBids(wallet, dseq);
|
|
777
|
+
* const cheapestBid = bids.data[0];
|
|
778
|
+
*
|
|
779
|
+
* const lease = await client.acceptBid(cheapestBid.bid);
|
|
780
|
+
*
|
|
781
|
+
* if (lease.success) {
|
|
782
|
+
* console.log(`Lease created with ${lease.data.lease.provider}`);
|
|
783
|
+
* }
|
|
784
|
+
* ```
|
|
785
|
+
*/
|
|
786
|
+
async acceptBid(bid) {
|
|
787
|
+
// Validate signer exists
|
|
788
|
+
if (!this.signer) {
|
|
789
|
+
return failure(new DeploymentError('Signer required for accepting bids', 'SIGNER_REQUIRED', {}, false, 'Create AkashClient with a signer to perform write operations'));
|
|
790
|
+
}
|
|
791
|
+
try {
|
|
792
|
+
const bidId = bid.id ?? bid.bidId;
|
|
793
|
+
if (!bidId) {
|
|
794
|
+
return failure(new DeploymentError('Invalid bid: missing bid ID', 'INVALID_BID', { bid }));
|
|
795
|
+
}
|
|
796
|
+
// Capture transaction metadata
|
|
797
|
+
let txHash = '';
|
|
798
|
+
let txHeight = 0;
|
|
799
|
+
// Create lease (reuses this.sdk)
|
|
800
|
+
await this.sdk.akash.market.v1beta5.createLease({
|
|
801
|
+
bidId: {
|
|
802
|
+
owner: bidId.owner,
|
|
803
|
+
dseq: bidId.dseq,
|
|
804
|
+
gseq: bidId.gseq,
|
|
805
|
+
oseq: bidId.oseq,
|
|
806
|
+
provider: bidId.provider,
|
|
807
|
+
bseq: bidId.bseq ?? 0
|
|
808
|
+
}
|
|
809
|
+
}, {
|
|
810
|
+
afterBroadcast: (txResponse) => {
|
|
811
|
+
txHash = txResponse.transactionHash;
|
|
812
|
+
txHeight = txResponse.height;
|
|
813
|
+
}
|
|
814
|
+
});
|
|
815
|
+
const lease = {
|
|
816
|
+
owner: bidId.owner,
|
|
817
|
+
dseq: Number(bidId.dseq),
|
|
818
|
+
gseq: bidId.gseq,
|
|
819
|
+
oseq: bidId.oseq,
|
|
820
|
+
provider: bidId.provider
|
|
821
|
+
};
|
|
822
|
+
return success({
|
|
823
|
+
lease,
|
|
824
|
+
transactionHash: txHash,
|
|
825
|
+
height: txHeight
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
catch (error) {
|
|
829
|
+
return failure(new DeploymentError('Failed to create lease', 'TRANSACTION_FAILED', { error: error instanceof Error ? error.message : String(error) }, true, 'Check wallet balance and that bid is still valid'));
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Close deployment on blockchain
|
|
834
|
+
*
|
|
835
|
+
* Closes the deployment and all associated leases. Remaining deposit
|
|
836
|
+
* is refunded to the wallet.
|
|
837
|
+
*
|
|
838
|
+
* **Gas cost**: ~0.02 AKT
|
|
839
|
+
*
|
|
840
|
+
* @param dseq - Deployment sequence number to close
|
|
841
|
+
* @returns Closure confirmation with refund amount
|
|
842
|
+
*
|
|
843
|
+
* @example
|
|
844
|
+
* ```typescript
|
|
845
|
+
* const result = await client.closeDeployment(12345);
|
|
846
|
+
*
|
|
847
|
+
* if (result.success) {
|
|
848
|
+
* console.log(`Deployment ${result.data.dseq} closed`);
|
|
849
|
+
* console.log(`Refunded: ${result.data.refundAmount}`);
|
|
850
|
+
* }
|
|
851
|
+
* ```
|
|
852
|
+
*/
|
|
853
|
+
async closeDeployment(dseq) {
|
|
854
|
+
// Validate signer exists
|
|
855
|
+
if (!this.signer) {
|
|
856
|
+
return failure(new DeploymentError('Signer required for closing deployments', 'SIGNER_REQUIRED', {}, false, 'Create AkashClient with a signer to perform write operations'));
|
|
857
|
+
}
|
|
858
|
+
try {
|
|
859
|
+
const owner = await this.getSignerAddress();
|
|
860
|
+
// Capture transaction metadata
|
|
861
|
+
let txHash = '';
|
|
862
|
+
let txHeight = 0;
|
|
863
|
+
// Close deployment (reuses this.sdk)
|
|
864
|
+
await this.sdk.akash.deployment.v1beta4.closeDeployment({
|
|
865
|
+
id: {
|
|
866
|
+
owner,
|
|
867
|
+
dseq: Long.fromNumber(dseq)
|
|
868
|
+
}
|
|
869
|
+
}, {
|
|
870
|
+
afterBroadcast: (txResponse) => {
|
|
871
|
+
txHash = txResponse.transactionHash;
|
|
872
|
+
txHeight = txResponse.height;
|
|
873
|
+
}
|
|
874
|
+
});
|
|
875
|
+
return success({
|
|
876
|
+
dseq: String(dseq),
|
|
877
|
+
owner,
|
|
878
|
+
transactionHash: txHash,
|
|
879
|
+
height: txHeight,
|
|
880
|
+
closedAt: new Date(),
|
|
881
|
+
confirmed: true
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
catch (error) {
|
|
885
|
+
return failure(new DeploymentError('Failed to close deployment', 'TRANSACTION_FAILED', { dseq, error: error instanceof Error ? error.message : String(error) }, true, 'Verify deployment exists and is owned by this wallet'));
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
// ==========================================================================
|
|
889
|
+
// Certificate Methods
|
|
890
|
+
// ==========================================================================
|
|
891
|
+
/**
|
|
892
|
+
* Get certificate manager instance
|
|
893
|
+
*
|
|
894
|
+
* Returns a lazy-initialized CertificateManager that uses this client's SDK.
|
|
895
|
+
* The manager handles certificate generation, querying, and broadcasting.
|
|
896
|
+
*
|
|
897
|
+
* @returns Certificate manager instance
|
|
898
|
+
*
|
|
899
|
+
* @example
|
|
900
|
+
* ```typescript
|
|
901
|
+
* const certManager = client.getCertificateManager();
|
|
902
|
+
* const cert = await certManager.getOrCreate('akash1...');
|
|
903
|
+
* ```
|
|
904
|
+
*/
|
|
905
|
+
getCertificateManager() {
|
|
906
|
+
// Lazy initialization - create manager on first access
|
|
907
|
+
if (!this.certManager) {
|
|
908
|
+
this.certManager = new CertificateManager(this);
|
|
909
|
+
}
|
|
910
|
+
return this.certManager;
|
|
911
|
+
}
|
|
912
|
+
// ==========================================================================
|
|
913
|
+
// Utility Methods
|
|
914
|
+
// ==========================================================================
|
|
915
|
+
/**
|
|
916
|
+
* Disconnect and cleanup resources
|
|
917
|
+
*
|
|
918
|
+
* Closes SDK connections and cleans up resources. Call this when
|
|
919
|
+
* done with the client to prevent memory leaks.
|
|
920
|
+
*
|
|
921
|
+
* **Important**: Always call this in a `finally` block
|
|
922
|
+
*
|
|
923
|
+
* @example
|
|
924
|
+
* ```typescript
|
|
925
|
+
* const client = new AkashClient({ network: 'mainnet', signer });
|
|
926
|
+
*
|
|
927
|
+
* try {
|
|
928
|
+
* await client.createDeployment(sdl);
|
|
929
|
+
* } finally {
|
|
930
|
+
* await client.disconnect(); // Always cleanup!
|
|
931
|
+
* }
|
|
932
|
+
* ```
|
|
933
|
+
*/
|
|
934
|
+
async disconnect() {
|
|
935
|
+
// SDK cleanup if available
|
|
936
|
+
// The chain-sdk handles cleanup internally
|
|
937
|
+
// This method is a placeholder for future cleanup needs
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* Get current network
|
|
941
|
+
*
|
|
942
|
+
* @returns Network this client is connected to
|
|
943
|
+
*
|
|
944
|
+
* @example
|
|
945
|
+
* ```typescript
|
|
946
|
+
* console.log(`Connected to: ${client.getNetwork()}`);
|
|
947
|
+
* ```
|
|
948
|
+
*/
|
|
949
|
+
getNetwork() {
|
|
950
|
+
return this.network;
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Check if client can sign transactions
|
|
954
|
+
*
|
|
955
|
+
* Returns true if signer was provided in constructor.
|
|
956
|
+
* Use this to check if write operations are available.
|
|
957
|
+
*
|
|
958
|
+
* @returns True if client can sign transactions
|
|
959
|
+
*
|
|
960
|
+
* @example
|
|
961
|
+
* ```typescript
|
|
962
|
+
* if (!client.canSign()) {
|
|
963
|
+
* console.error('Cannot create deployment - client is read-only');
|
|
964
|
+
* console.log('Create client with signer for write operations');
|
|
965
|
+
* }
|
|
966
|
+
* ```
|
|
967
|
+
*/
|
|
968
|
+
canSign() {
|
|
969
|
+
return this.signer !== undefined;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
//# sourceMappingURL=client.js.map
|