@kadi.build/deploy-ability 0.0.1

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 (198) hide show
  1. package/README.md +523 -0
  2. package/dist/constants.d.ts +82 -0
  3. package/dist/constants.d.ts.map +1 -0
  4. package/dist/constants.js +82 -0
  5. package/dist/constants.js.map +1 -0
  6. package/dist/errors/certificate-error.d.ts +95 -0
  7. package/dist/errors/certificate-error.d.ts.map +1 -0
  8. package/dist/errors/certificate-error.js +111 -0
  9. package/dist/errors/certificate-error.js.map +1 -0
  10. package/dist/errors/deployment-error.d.ts +122 -0
  11. package/dist/errors/deployment-error.d.ts.map +1 -0
  12. package/dist/errors/deployment-error.js +185 -0
  13. package/dist/errors/deployment-error.js.map +1 -0
  14. package/dist/errors/index.d.ts +13 -0
  15. package/dist/errors/index.d.ts.map +1 -0
  16. package/dist/errors/index.js +18 -0
  17. package/dist/errors/index.js.map +1 -0
  18. package/dist/errors/profile-error.d.ts +106 -0
  19. package/dist/errors/profile-error.d.ts.map +1 -0
  20. package/dist/errors/profile-error.js +127 -0
  21. package/dist/errors/profile-error.js.map +1 -0
  22. package/dist/errors/provider-error.d.ts +104 -0
  23. package/dist/errors/provider-error.d.ts.map +1 -0
  24. package/dist/errors/provider-error.js +120 -0
  25. package/dist/errors/provider-error.js.map +1 -0
  26. package/dist/errors/wallet-error.d.ts +131 -0
  27. package/dist/errors/wallet-error.d.ts.map +1 -0
  28. package/dist/errors/wallet-error.js +154 -0
  29. package/dist/errors/wallet-error.js.map +1 -0
  30. package/dist/index.d.ts +49 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +53 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/targets/akash/bid-selectors.d.ts +251 -0
  35. package/dist/targets/akash/bid-selectors.d.ts.map +1 -0
  36. package/dist/targets/akash/bid-selectors.js +322 -0
  37. package/dist/targets/akash/bid-selectors.js.map +1 -0
  38. package/dist/targets/akash/bid-types.d.ts +297 -0
  39. package/dist/targets/akash/bid-types.d.ts.map +1 -0
  40. package/dist/targets/akash/bid-types.js +89 -0
  41. package/dist/targets/akash/bid-types.js.map +1 -0
  42. package/dist/targets/akash/blockchain-client.d.ts +577 -0
  43. package/dist/targets/akash/blockchain-client.d.ts.map +1 -0
  44. package/dist/targets/akash/blockchain-client.js +803 -0
  45. package/dist/targets/akash/blockchain-client.js.map +1 -0
  46. package/dist/targets/akash/certificate-manager.d.ts +228 -0
  47. package/dist/targets/akash/certificate-manager.d.ts.map +1 -0
  48. package/dist/targets/akash/certificate-manager.js +395 -0
  49. package/dist/targets/akash/certificate-manager.js.map +1 -0
  50. package/dist/targets/akash/constants.d.ts +231 -0
  51. package/dist/targets/akash/constants.d.ts.map +1 -0
  52. package/dist/targets/akash/constants.js +225 -0
  53. package/dist/targets/akash/constants.js.map +1 -0
  54. package/dist/targets/akash/deployer.d.ts +136 -0
  55. package/dist/targets/akash/deployer.d.ts.map +1 -0
  56. package/dist/targets/akash/deployer.js +599 -0
  57. package/dist/targets/akash/deployer.js.map +1 -0
  58. package/dist/targets/akash/environment.d.ts +241 -0
  59. package/dist/targets/akash/environment.d.ts.map +1 -0
  60. package/dist/targets/akash/environment.js +245 -0
  61. package/dist/targets/akash/environment.js.map +1 -0
  62. package/dist/targets/akash/index.d.ts +1113 -0
  63. package/dist/targets/akash/index.d.ts.map +1 -0
  64. package/dist/targets/akash/index.js +909 -0
  65. package/dist/targets/akash/index.js.map +1 -0
  66. package/dist/targets/akash/lease-monitor.d.ts +51 -0
  67. package/dist/targets/akash/lease-monitor.d.ts.map +1 -0
  68. package/dist/targets/akash/lease-monitor.js +110 -0
  69. package/dist/targets/akash/lease-monitor.js.map +1 -0
  70. package/dist/targets/akash/logs.d.ts +71 -0
  71. package/dist/targets/akash/logs.d.ts.map +1 -0
  72. package/dist/targets/akash/logs.js +311 -0
  73. package/dist/targets/akash/logs.js.map +1 -0
  74. package/dist/targets/akash/logs.types.d.ts +102 -0
  75. package/dist/targets/akash/logs.types.d.ts.map +1 -0
  76. package/dist/targets/akash/logs.types.js +9 -0
  77. package/dist/targets/akash/logs.types.js.map +1 -0
  78. package/dist/targets/akash/pricing.d.ts +247 -0
  79. package/dist/targets/akash/pricing.d.ts.map +1 -0
  80. package/dist/targets/akash/pricing.js +246 -0
  81. package/dist/targets/akash/pricing.js.map +1 -0
  82. package/dist/targets/akash/provider-client.d.ts +114 -0
  83. package/dist/targets/akash/provider-client.d.ts.map +1 -0
  84. package/dist/targets/akash/provider-client.js +318 -0
  85. package/dist/targets/akash/provider-client.js.map +1 -0
  86. package/dist/targets/akash/provider-metadata.d.ts +228 -0
  87. package/dist/targets/akash/provider-metadata.d.ts.map +1 -0
  88. package/dist/targets/akash/provider-metadata.js +14 -0
  89. package/dist/targets/akash/provider-metadata.js.map +1 -0
  90. package/dist/targets/akash/provider-service.d.ts +133 -0
  91. package/dist/targets/akash/provider-service.d.ts.map +1 -0
  92. package/dist/targets/akash/provider-service.js +391 -0
  93. package/dist/targets/akash/provider-service.js.map +1 -0
  94. package/dist/targets/akash/query-client.d.ts +125 -0
  95. package/dist/targets/akash/query-client.d.ts.map +1 -0
  96. package/dist/targets/akash/query-client.js +332 -0
  97. package/dist/targets/akash/query-client.js.map +1 -0
  98. package/dist/targets/akash/sdl-generator.d.ts +31 -0
  99. package/dist/targets/akash/sdl-generator.d.ts.map +1 -0
  100. package/dist/targets/akash/sdl-generator.js +279 -0
  101. package/dist/targets/akash/sdl-generator.js.map +1 -0
  102. package/dist/targets/akash/types.d.ts +285 -0
  103. package/dist/targets/akash/types.d.ts.map +1 -0
  104. package/dist/targets/akash/types.js +54 -0
  105. package/dist/targets/akash/types.js.map +1 -0
  106. package/dist/targets/akash/wallet-manager.d.ts +526 -0
  107. package/dist/targets/akash/wallet-manager.d.ts.map +1 -0
  108. package/dist/targets/akash/wallet-manager.js +953 -0
  109. package/dist/targets/akash/wallet-manager.js.map +1 -0
  110. package/dist/targets/local/compose-generator.d.ts +244 -0
  111. package/dist/targets/local/compose-generator.d.ts.map +1 -0
  112. package/dist/targets/local/compose-generator.js +324 -0
  113. package/dist/targets/local/compose-generator.js.map +1 -0
  114. package/dist/targets/local/deployer.d.ts +82 -0
  115. package/dist/targets/local/deployer.d.ts.map +1 -0
  116. package/dist/targets/local/deployer.js +367 -0
  117. package/dist/targets/local/deployer.js.map +1 -0
  118. package/dist/targets/local/engine-manager.d.ts +155 -0
  119. package/dist/targets/local/engine-manager.d.ts.map +1 -0
  120. package/dist/targets/local/engine-manager.js +250 -0
  121. package/dist/targets/local/engine-manager.js.map +1 -0
  122. package/dist/targets/local/index.d.ts +40 -0
  123. package/dist/targets/local/index.d.ts.map +1 -0
  124. package/dist/targets/local/index.js +43 -0
  125. package/dist/targets/local/index.js.map +1 -0
  126. package/dist/targets/local/network-manager.d.ts +160 -0
  127. package/dist/targets/local/network-manager.d.ts.map +1 -0
  128. package/dist/targets/local/network-manager.js +337 -0
  129. package/dist/targets/local/network-manager.js.map +1 -0
  130. package/dist/targets/local/types.d.ts +327 -0
  131. package/dist/targets/local/types.d.ts.map +1 -0
  132. package/dist/targets/local/types.js +9 -0
  133. package/dist/targets/local/types.js.map +1 -0
  134. package/dist/types/common.d.ts +585 -0
  135. package/dist/types/common.d.ts.map +1 -0
  136. package/dist/types/common.js +13 -0
  137. package/dist/types/common.js.map +1 -0
  138. package/dist/types/index.d.ts +15 -0
  139. package/dist/types/index.d.ts.map +1 -0
  140. package/dist/types/index.js +12 -0
  141. package/dist/types/index.js.map +1 -0
  142. package/dist/types/options.d.ts +329 -0
  143. package/dist/types/options.d.ts.map +1 -0
  144. package/dist/types/options.js +10 -0
  145. package/dist/types/options.js.map +1 -0
  146. package/dist/types/profiles.d.ts +329 -0
  147. package/dist/types/profiles.d.ts.map +1 -0
  148. package/dist/types/profiles.js +27 -0
  149. package/dist/types/profiles.js.map +1 -0
  150. package/dist/types/results.d.ts +443 -0
  151. package/dist/types/results.d.ts.map +1 -0
  152. package/dist/types/results.js +64 -0
  153. package/dist/types/results.js.map +1 -0
  154. package/dist/types/validators.d.ts +118 -0
  155. package/dist/types/validators.d.ts.map +1 -0
  156. package/dist/types/validators.js +198 -0
  157. package/dist/types/validators.js.map +1 -0
  158. package/dist/utils/command-runner.d.ts +128 -0
  159. package/dist/utils/command-runner.d.ts.map +1 -0
  160. package/dist/utils/command-runner.js +210 -0
  161. package/dist/utils/command-runner.js.map +1 -0
  162. package/dist/utils/index.d.ts +10 -0
  163. package/dist/utils/index.d.ts.map +1 -0
  164. package/dist/utils/index.js +10 -0
  165. package/dist/utils/index.js.map +1 -0
  166. package/dist/utils/logger.d.ts +68 -0
  167. package/dist/utils/logger.d.ts.map +1 -0
  168. package/dist/utils/logger.js +93 -0
  169. package/dist/utils/logger.js.map +1 -0
  170. package/dist/utils/profile-loader.d.ts +76 -0
  171. package/dist/utils/profile-loader.d.ts.map +1 -0
  172. package/dist/utils/profile-loader.js +194 -0
  173. package/dist/utils/profile-loader.js.map +1 -0
  174. package/dist/utils/registry/index.d.ts +27 -0
  175. package/dist/utils/registry/index.d.ts.map +1 -0
  176. package/dist/utils/registry/index.js +29 -0
  177. package/dist/utils/registry/index.js.map +1 -0
  178. package/dist/utils/registry/manager.d.ts +319 -0
  179. package/dist/utils/registry/manager.d.ts.map +1 -0
  180. package/dist/utils/registry/manager.js +671 -0
  181. package/dist/utils/registry/manager.js.map +1 -0
  182. package/dist/utils/registry/setup.d.ts +135 -0
  183. package/dist/utils/registry/setup.d.ts.map +1 -0
  184. package/dist/utils/registry/setup.js +207 -0
  185. package/dist/utils/registry/setup.js.map +1 -0
  186. package/dist/utils/registry/transformer.d.ts +92 -0
  187. package/dist/utils/registry/transformer.d.ts.map +1 -0
  188. package/dist/utils/registry/transformer.js +131 -0
  189. package/dist/utils/registry/transformer.js.map +1 -0
  190. package/dist/utils/registry/types.d.ts +241 -0
  191. package/dist/utils/registry/types.d.ts.map +1 -0
  192. package/dist/utils/registry/types.js +10 -0
  193. package/dist/utils/registry/types.js.map +1 -0
  194. package/docs/EXAMPLES.md +293 -0
  195. package/docs/PLACEMENT.md +433 -0
  196. package/docs/STORAGE.md +318 -0
  197. package/docs/building-provider-reliability-tracker.md +2581 -0
  198. package/package.json +109 -0
@@ -0,0 +1,803 @@
1
+ /**
2
+ * Akash Network Blockchain Client Module
3
+ *
4
+ * Handles all blockchain operations for Akash Network deployments including:
5
+ * - Signing client creation and management
6
+ * - Deployment creation on blockchain
7
+ * - Provider bid collection and filtering
8
+ * - Lease creation with selected providers
9
+ *
10
+ * Design Philosophy:
11
+ * This is a LIBRARY, not a CLI. Operations return Result types with detailed
12
+ * data. The caller (deployer.ts) handles progress tracking and user feedback.
13
+ *
14
+ * We use the Akash SDK types and helpers directly rather than reinventing them.
15
+ * The SDK is well-designed and thoroughly tested - we enhance the DX with Result
16
+ * types and comprehensive comments.
17
+ *
18
+ * @module targets/akash/blockchain-client
19
+ */
20
+ import { SigningStargateClient } from '@cosmjs/stargate';
21
+ import { Registry } from '@cosmjs/proto-signing';
22
+ import { getAkashTypeRegistry, getTypeUrl } from '@akashnetwork/akashjs/build/stargate/index.js';
23
+ import { getRpc } from '@akashnetwork/akashjs/build/rpc/index.js';
24
+ import { QueryBidsRequest, QueryClientImpl as QueryMarketClient, MsgCreateLease, LeaseID, } from '@akashnetwork/akash-api/akash/market/v1beta4';
25
+ import { MsgCreateDeployment, MsgCloseDeployment, } from '@akashnetwork/akash-api/akash/deployment/v1beta3';
26
+ import { success, failure } from '../../types/index.js';
27
+ import { DeploymentError } from '../../errors/index.js';
28
+ import { getNetworkConfig, isProviderBlacklisted } from './environment.js';
29
+ import { createBidPricing } from './bid-types.js';
30
+ import { fetchAllProviders } from './provider-service.js';
31
+ /**
32
+ * Create a signing client for Akash blockchain operations
33
+ *
34
+ * Creates a SigningStargateClient configured with Akash-specific message
35
+ * types. This client is used for all blockchain transactions including
36
+ * deployment creation, lease creation, and certificate broadcasting.
37
+ *
38
+ * **Why you need this:**
39
+ * The Akash blockchain uses custom message types (MsgCreateDeployment,
40
+ * MsgCreateLease, etc.) that aren't in the standard Cosmos SDK. We need
41
+ * to register these types so the client can encode/decode them correctly.
42
+ *
43
+ * **What you get:**
44
+ * - A signing client connected to Akash RPC
45
+ * - Account information for transaction signing
46
+ * - Ability to broadcast any Akash transaction
47
+ *
48
+ * @param wallet - Wallet context with offline signer
49
+ * @param network - Akash network to connect to
50
+ * @returns Result with signing client and account info
51
+ *
52
+ * @example Basic usage
53
+ * ```typescript
54
+ * const clientResult = await createSigningClient(wallet, 'mainnet');
55
+ * if (!clientResult.success) {
56
+ * console.error('Failed to create client:', clientResult.error.message);
57
+ * return;
58
+ * }
59
+ *
60
+ * const { client, account } = clientResult.data;
61
+ * console.log('Connected with address:', account.address);
62
+ * ```
63
+ *
64
+ * @example Error handling
65
+ * ```typescript
66
+ * const result = await createSigningClient(wallet, 'testnet');
67
+ * if (!result.success) {
68
+ * if (result.error.code === 'WALLET_ERROR') {
69
+ * console.error('Wallet issue - check connection');
70
+ * } else if (result.error.code === 'RPC_ERROR') {
71
+ * console.error('Network issue - try different RPC endpoint');
72
+ * }
73
+ * return;
74
+ * }
75
+ * ```
76
+ */
77
+ export async function createSigningClient(wallet, network) {
78
+ try {
79
+ // Step 1: Create Cosmos SDK type registry with Akash-specific message types
80
+ // The Akash type registry includes all necessary Cosmos SDK types plus
81
+ // Akash deployment/market/provider message types. We don't need to merge
82
+ // with defaultRegistryTypes as the Akash registry is already complete.
83
+ const registry = new Registry(getAkashTypeRegistry());
84
+ // Step 2: Get RPC endpoint for the selected network
85
+ const networkConfig = getNetworkConfig(network);
86
+ const rpc = networkConfig.rpc;
87
+ // Step 3: Get accounts from wallet's offline signer
88
+ // The offline signer provides accounts without needing network access
89
+ const accounts = await wallet.offlineSigner.getAccounts();
90
+ if (accounts.length === 0) {
91
+ return {
92
+ success: false,
93
+ error: new DeploymentError('No accounts found in wallet', 'WALLET_ERROR', {}, false, 'Please check your wallet connection and ensure it has at least one account'),
94
+ };
95
+ }
96
+ // Step 4: Create SigningStargateClient connected to Akash RPC
97
+ // This client can sign and broadcast transactions using the wallet's signer
98
+ const client = await SigningStargateClient.connectWithSigner(rpc, wallet.offlineSigner, { registry });
99
+ // Step 5: Return client and account info for transaction operations
100
+ const account = accounts[0];
101
+ return {
102
+ success: true,
103
+ data: {
104
+ client,
105
+ account: {
106
+ address: account.address,
107
+ pubkey: account.pubkey,
108
+ accountNumber: 0, // Will be fetched from chain when needed
109
+ sequence: 0, // Will be fetched from chain when needed
110
+ },
111
+ },
112
+ };
113
+ }
114
+ catch (error) {
115
+ return {
116
+ success: false,
117
+ error: new DeploymentError(`Failed to create signing client: ${error}`, 'RPC_ERROR', { error: String(error) }, false, 'Check network connection and RPC endpoint availability'),
118
+ };
119
+ }
120
+ }
121
+ /**
122
+ * Create a new Akash deployment on the blockchain
123
+ *
124
+ * Broadcasts a MsgCreateDeployment transaction that registers your application
125
+ * with the Akash marketplace. Providers will see this deployment and submit bids.
126
+ *
127
+ * **The deployment process:**
128
+ * 1. Your deployment gets a unique identifier (dseq = deployment sequence)
129
+ * 2. Deployment is posted to the blockchain with resource requirements
130
+ * 3. An escrow deposit is held (default 5 AKT, returned when you close)
131
+ * 4. Providers see your deployment and submit competitive bids
132
+ * 5. You choose a bid and create a lease
133
+ *
134
+ * **Escrow Deposit:**
135
+ * The deposit ensures you can pay for resources. It's returned when you
136
+ * close the deployment. Default is 5 AKT but you can customize it.
137
+ *
138
+ * **About dseq:**
139
+ * The deployment sequence number (dseq) is taken from the current block
140
+ * height, ensuring it's unique and monotonically increasing.
141
+ *
142
+ * @param clientContext - Signing client and account info
143
+ * @param wallet - Wallet context with address
144
+ * @param sdl - SDL object with deployment configuration
145
+ * @param depositAkt - Optional deposit amount in AKT (default: 5)
146
+ * @returns Result with deployment sequence number and transaction info
147
+ *
148
+ * @example Basic deployment
149
+ * ```typescript
150
+ * import { SDL } from '@akashnetwork/akashjs/build/sdl';
151
+ *
152
+ * // Parse SDL from YAML
153
+ * const sdl = SDL.fromString(yamlContent, 'beta3');
154
+ *
155
+ * // Create signing client
156
+ * const clientResult = await createSigningClient(wallet, 'mainnet');
157
+ * if (!clientResult.success) return clientResult;
158
+ *
159
+ * // Deploy with default 5 AKT deposit
160
+ * const deployResult = await createDeployment(
161
+ * clientResult.data,
162
+ * wallet,
163
+ * sdl
164
+ * );
165
+ *
166
+ * if (deployResult.success) {
167
+ * console.log('Deployment created!');
168
+ * console.log('DSEQ:', deployResult.data.dseq);
169
+ * console.log('TX:', deployResult.data.transactionHash);
170
+ * }
171
+ * ```
172
+ *
173
+ * @example Custom deposit
174
+ * ```typescript
175
+ * // Deploy with 10 AKT deposit for larger deployments
176
+ * const result = await createDeployment(
177
+ * clientContext,
178
+ * wallet,
179
+ * sdl,
180
+ * 10 // 10 AKT deposit
181
+ * );
182
+ * ```
183
+ */
184
+ export async function createDeployment(clientContext, wallet, sdl, depositAkt = 5) {
185
+ try {
186
+ const { client, account } = clientContext;
187
+ const owner = wallet.address;
188
+ // Step 1: Get current block height for dseq
189
+ // The deployment sequence number (dseq) uniquely identifies this deployment.
190
+ // Using block height ensures uniqueness and monotonic increase.
191
+ const dseq = await client.getHeight();
192
+ // Step 2: Extract deployment groups and manifest version from SDL
193
+ // The SDL library from @akashnetwork/akashjs handles all validation
194
+ // and returns properly structured deployment groups ready for blockchain.
195
+ // We trust the SDK - no manual normalization needed!
196
+ const groups = sdl.groups();
197
+ const manifestVersion = await sdl.manifestVersion();
198
+ // Step 3: Calculate escrow deposit in uakt (micro-AKT)
199
+ // 1 AKT = 1,000,000 uakt
200
+ // The deposit is held in escrow and returned when deployment closes
201
+ const aktAmount = Number.isFinite(depositAkt) && depositAkt > 0 ? depositAkt : 5;
202
+ const depositUakt = Math.round(aktAmount * 1_000_000).toString();
203
+ // Step 4: Create deployment message with all parameters
204
+ const msg = MsgCreateDeployment.fromPartial({
205
+ id: { owner, dseq },
206
+ groups: groups, // Trust SDL - it returns correct structure
207
+ version: manifestVersion,
208
+ deposit: { amount: depositUakt, denom: 'uakt' },
209
+ depositor: owner,
210
+ });
211
+ // Step 5: Wrap message with type-safe URL
212
+ // getTypeUrl() ensures we use the correct protobuf type identifier
213
+ // for this message type, preventing runtime errors from typos
214
+ const anyMsg = {
215
+ typeUrl: getTypeUrl(MsgCreateDeployment),
216
+ value: msg,
217
+ };
218
+ // Step 6: Set transaction fee
219
+ // Gas estimation: 300,000 gas units
220
+ // Fee: 8,000 uakt (approximately 0.008 AKT)
221
+ const fee = {
222
+ amount: [{ denom: 'uakt', amount: '8000' }],
223
+ gas: '300000',
224
+ };
225
+ // Step 7: Sign and broadcast deployment transaction
226
+ const res = await client.signAndBroadcast(account.address, [anyMsg], fee, 'Create deployment via deploy-ability');
227
+ // Step 8: Check transaction result
228
+ // code === 0 means success, any other code is an error
229
+ if (res.code !== 0) {
230
+ return {
231
+ success: false,
232
+ error: new DeploymentError(`Deployment transaction failed: ${res.rawLog}`, 'TRANSACTION_FAILED', {
233
+ code: res.code,
234
+ rawLog: res.rawLog,
235
+ transactionHash: res.transactionHash,
236
+ }, false, 'Check wallet balance (need funds for deposit + fees) and SDL validity'),
237
+ };
238
+ }
239
+ // Step 9: Return deployment result with dseq
240
+ return {
241
+ success: true,
242
+ data: {
243
+ dseq,
244
+ transactionHash: res.transactionHash,
245
+ height: res.height,
246
+ },
247
+ };
248
+ }
249
+ catch (error) {
250
+ return {
251
+ success: false,
252
+ error: new DeploymentError(`Failed to create deployment: ${error}`, 'DEPLOYMENT_FAILED', { error: String(error) }, false, 'Check wallet connection, network status, and SDL validity'),
253
+ };
254
+ }
255
+ }
256
+ /**
257
+ * Extract valid bids from marketplace response
258
+ *
259
+ * The marketplace returns bid responses in a wrapped format. We filter
260
+ * for bids that exist and have valid provider addresses, then filter out
261
+ * blacklisted providers.
262
+ *
263
+ * **Why this is needed:**
264
+ * QueryBidsResponse.bids is an array of { bid?: Bid } where bid might be
265
+ * undefined. We extract the actual Bid objects and validate them.
266
+ *
267
+ * @param response - Raw bids response from marketplace query
268
+ * @returns Array of valid provider bids
269
+ */
270
+ function extractValidBids(response) {
271
+ if (!response.bids)
272
+ return [];
273
+ return response.bids
274
+ .filter((b) => b.bid !== undefined && b.bid !== null)
275
+ .map((b) => b.bid)
276
+ .filter((bid) => {
277
+ const provider = bid.bidId?.provider ?? '';
278
+ return provider !== '';
279
+ });
280
+ }
281
+ /**
282
+ * Query provider bids for a deployment with enhanced information
283
+ *
284
+ * Queries the Akash marketplace for provider bids and enriches each bid with:
285
+ * - Complete provider information (name, location, audit status)
286
+ * - Reliability metrics (uptime percentages)
287
+ * - Pre-calculated pricing for all time periods (hour, day, week, month)
288
+ *
289
+ * Returns ALL valid bids so the caller can implement custom selection logic.
290
+ *
291
+ * **The Akash Bidding Process:**
292
+ * After you create a deployment, here's what happens:
293
+ * 1. Providers monitor the blockchain for new deployments
294
+ * 2. They check if they can fulfill your resource requirements
295
+ * 3. They submit competitive bids with their pricing
296
+ * 4. You query to see available bids (this function)
297
+ * 5. You choose the best bid based on your criteria
298
+ * 6. You create a lease with the selected provider
299
+ *
300
+ * **Bid Filtering:**
301
+ * We automatically exclude:
302
+ * - Blacklisted providers (known to be unreliable)
303
+ * - Custom blacklist from your deployment configuration
304
+ * - Bids without valid provider addresses
305
+ *
306
+ * **Note:** This returns immediately with current state. Use `waitForBids()`
307
+ * if you want to poll until bids arrive.
308
+ *
309
+ * @param wallet - Wallet context with owner address
310
+ * @param network - Akash network to query
311
+ * @param dseq - Deployment sequence number
312
+ * @param options - Optional query options
313
+ * @param options.customBlacklist - Additional provider addresses to exclude
314
+ * @param options.includeOffline - Include offline providers (default: false)
315
+ * @returns Result with array of enhanced bids (empty array if none found)
316
+ *
317
+ * @example Query and display bids
318
+ * ```typescript
319
+ * const result = await queryBids(wallet, 'mainnet', dseq);
320
+ *
321
+ * if (result.success) {
322
+ * console.log(`Received ${result.data.length} bids`);
323
+ * for (const bid of result.data) {
324
+ * const provider = bid.provider;
325
+ * const uptime = provider.reliability?.uptime7d;
326
+ * console.log(`
327
+ * Provider: ${provider.name || provider.owner}
328
+ * Price: ${bid.pricing.akt.perMonth.toFixed(2)} AKT/month
329
+ * Uptime (7d): ${uptime ? (uptime * 100).toFixed(1) + '%' : 'Unknown'}
330
+ * Location: ${provider.location?.country || 'Unknown'}
331
+ * `);
332
+ * }
333
+ * }
334
+ * ```
335
+ *
336
+ * @example With filtering options
337
+ * ```typescript
338
+ * const blacklist = ['akash1badprovider...', 'akash1another...'];
339
+ * const result = await queryBids(wallet, 'mainnet', dseq, {
340
+ * customBlacklist: blacklist,
341
+ * includeOffline: false
342
+ * });
343
+ * ```
344
+ */
345
+ export async function queryBids(wallet, network, dseq, options) {
346
+ try {
347
+ // Step 1: Get RPC endpoint for network
348
+ const networkConfig = getNetworkConfig(network);
349
+ const rpc = await getRpc(networkConfig.rpc);
350
+ // Step 2: Create marketplace query client
351
+ const queryClient = new QueryMarketClient(rpc);
352
+ // Step 3: Build bid query request
353
+ // Filter for open bids for this specific deployment
354
+ const request = QueryBidsRequest.fromPartial({
355
+ filters: {
356
+ owner: wallet.address,
357
+ dseq: dseq.toString(),
358
+ state: 'open',
359
+ },
360
+ });
361
+ // Step 4: Query marketplace for bids
362
+ const response = await queryClient.Bids(request);
363
+ // Step 5: Build effective blacklist (built-in + custom)
364
+ // Convert custom blacklist to Set for O(1) lookup
365
+ const customBlacklistSet = new Set((options?.customBlacklist || [])
366
+ .map((addr) => (typeof addr === 'string' ? addr.trim() : ''))
367
+ .filter((addr) => addr.length > 0));
368
+ // Step 6: Extract and filter valid bids
369
+ // Remove bids from blacklisted providers (unreliable/problematic)
370
+ const validBids = extractValidBids(response).filter((bid) => {
371
+ const provider = bid.bidId?.provider ?? '';
372
+ return (provider !== '' &&
373
+ !isProviderBlacklisted(provider) &&
374
+ !customBlacklistSet.has(provider));
375
+ });
376
+ // Step 7: If no bids found, return empty array
377
+ if (validBids.length === 0) {
378
+ return success([]);
379
+ }
380
+ // Step 8: Fetch ALL provider metadata from Console API
381
+ // This is much more efficient than fetching individual providers
382
+ // and matches how the web console works
383
+ const providerInfoResult = await fetchAllProviders(network);
384
+ // If provider info fetch failed, we can still continue with basic data
385
+ const providerInfoMap = providerInfoResult.success
386
+ ? providerInfoResult.data
387
+ : new Map();
388
+ // Step 9: Transform raw bids into EnhancedBids
389
+ const enhancedBids = [];
390
+ for (const bid of validBids) {
391
+ const provider = bid.bidId?.provider;
392
+ if (!provider)
393
+ continue;
394
+ // Get provider info (or create minimal fallback)
395
+ let providerInfo = providerInfoMap.get(provider);
396
+ if (!providerInfo) {
397
+ providerInfo = {
398
+ owner: provider,
399
+ hostUri: '', // Will be populated when creating lease
400
+ isAudited: false,
401
+ };
402
+ }
403
+ // Filter out offline providers if requested
404
+ if (!options?.includeOffline &&
405
+ providerInfo.reliability &&
406
+ !providerInfo.reliability.isOnline) {
407
+ continue;
408
+ }
409
+ // Create pricing information
410
+ const pricing = createBidPricing({
411
+ denom: bid.price?.denom || 'uakt',
412
+ amount: bid.price?.amount || '0',
413
+ });
414
+ // Construct enhanced bid with unique ID
415
+ const bidId = `${provider}-${dseq}-${bid.bidId?.gseq}-${bid.bidId?.oseq}`;
416
+ const enhancedBid = {
417
+ id: bidId,
418
+ bid,
419
+ provider: providerInfo,
420
+ pricing,
421
+ createdAt: new Date(), // Bid creation time
422
+ };
423
+ enhancedBids.push(enhancedBid);
424
+ }
425
+ return success(enhancedBids);
426
+ }
427
+ catch (error) {
428
+ return failure(new DeploymentError(`Failed to query bids: ${error}`, 'QUERY_FAILED', { error: String(error), dseq }, true, 'Check network connection and try again'));
429
+ }
430
+ }
431
+ /**
432
+ * Wait for provider bids with polling
433
+ *
434
+ * Polls the marketplace for provider bids until at least one valid bid is received
435
+ * or timeout is reached. This is a convenience wrapper around `queryBids()` that
436
+ * handles the polling logic for you.
437
+ *
438
+ * Returns ALL enhanced bids received, allowing the caller to implement custom
439
+ * selection logic based on price, reliability, location, or other criteria.
440
+ *
441
+ * **Why this is useful:**
442
+ * After creating a deployment, providers need time to:
443
+ * 1. See your deployment on the blockchain
444
+ * 2. Check if they can fulfill requirements
445
+ * 3. Calculate pricing
446
+ * 4. Submit their bid
447
+ *
448
+ * This usually takes 30-60 seconds. This function handles the wait for you.
449
+ *
450
+ * **Polling Strategy:**
451
+ * - Poll interval: 5 seconds (reasonable for network latency)
452
+ * - Default timeout: 5 minutes (enough for providers to respond)
453
+ * - Returns immediately when at least one bid is found
454
+ * - Returns all errors from query operation
455
+ *
456
+ * @param wallet - Wallet context with owner address
457
+ * @param network - Akash network to query
458
+ * @param dseq - Deployment sequence number
459
+ * @param options - Optional query and polling options
460
+ * @param options.customBlacklist - Provider addresses to exclude
461
+ * @param options.includeOffline - Include offline providers (default: false)
462
+ * @param options.timeoutMs - Timeout in milliseconds (default: 300000 = 5 min)
463
+ * @returns Result with array of enhanced bids or timeout error
464
+ *
465
+ * @example Wait for bids and display
466
+ * ```typescript
467
+ * console.log('Waiting for provider bids...');
468
+ *
469
+ * const result = await waitForBids(wallet, 'mainnet', dseq);
470
+ *
471
+ * if (result.success) {
472
+ * console.log(`Received ${result.data.length} bids!`);
473
+ * for (const bid of result.data) {
474
+ * console.log(`Provider: ${bid.provider.name || bid.provider.owner}`);
475
+ * console.log(`Price: ${bid.pricing.akt.perMonth} AKT/month`);
476
+ * }
477
+ * } else {
478
+ * if (result.error.code === 'TIMEOUT') {
479
+ * console.error('No bids received within timeout');
480
+ * console.log('Try increasing deployment resources or deposit');
481
+ * } else {
482
+ * console.error('Query failed:', result.error.message);
483
+ * }
484
+ * }
485
+ * ```
486
+ *
487
+ * @example Custom timeout and options
488
+ * ```typescript
489
+ * // Wait up to 10 minutes, excluding specific providers
490
+ * const result = await waitForBids(wallet, 'mainnet', dseq, {
491
+ * customBlacklist: ['akash1bad...', 'akash1another...'],
492
+ * includeOffline: false,
493
+ * timeoutMs: 600000 // 10 minutes
494
+ * });
495
+ * ```
496
+ */
497
+ export async function waitForBids(wallet, network, dseq, options) {
498
+ const pollInterval = 5000; // 5 seconds
499
+ const timeoutMs = options?.timeoutMs ?? 300000; // Default: 5 minutes
500
+ const startTime = Date.now();
501
+ while (Date.now() - startTime < timeoutMs) {
502
+ // Query for bids with provided options
503
+ const bidResult = await queryBids(wallet, network, dseq, {
504
+ customBlacklist: options?.customBlacklist,
505
+ includeOffline: options?.includeOffline,
506
+ });
507
+ // Check for query errors
508
+ if (!bidResult.success) {
509
+ return bidResult;
510
+ }
511
+ // Check if we got at least one bid
512
+ if (bidResult.data.length > 0) {
513
+ return success(bidResult.data);
514
+ }
515
+ // Wait before next poll
516
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
517
+ }
518
+ // Timeout reached without receiving bids
519
+ return failure(new DeploymentError('Timeout waiting for provider bids', 'TIMEOUT', { dseq, timeoutMs }, true, 'No providers bid within timeout. Try: 1) Increase resources to attract more providers, 2) Increase deposit amount, 3) Check if your requirements are too specific'));
520
+ }
521
+ /**
522
+ * Create a lease by accepting a provider bid
523
+ *
524
+ * Broadcasts a MsgCreateLease transaction to accept a provider's bid and
525
+ * establish a lease. This commits the deployment to the selected provider
526
+ * and allows them to start running your containers.
527
+ *
528
+ * **What happens when you create a lease:**
529
+ * 1. You accept a specific provider's bid
530
+ * 2. A lease is created on the blockchain
531
+ * 3. The provider is notified of the accepted bid
532
+ * 4. The provider prepares to receive your manifest
533
+ * 5. You send the manifest (deployment details) to the provider
534
+ * 6. The provider starts your containers
535
+ *
536
+ * **Important:** After creating the lease, you MUST send the manifest
537
+ * to the provider within a reasonable time, or they may close the lease.
538
+ *
539
+ * @param clientContext - Signing client and account info
540
+ * @param bid - Provider bid to accept
541
+ * @returns Result with lease identifier and transaction info
542
+ *
543
+ * @example Accept bid and create lease
544
+ * ```typescript
545
+ * // Wait for bids
546
+ * const bidResult = await waitForBids(wallet, 'mainnet', dseq);
547
+ * if (!bidResult.success) return bidResult;
548
+ *
549
+ * // Accept the bid by creating a lease
550
+ * const leaseResult = await createLease(clientContext, bidResult.data);
551
+ *
552
+ * if (leaseResult.success) {
553
+ * const lease = leaseResult.data.leaseId;
554
+ * console.log('Lease created!');
555
+ * console.log('DSEQ:', lease.dseq);
556
+ * console.log('Provider:', lease.provider);
557
+ * console.log('TX:', leaseResult.data.transactionHash);
558
+ *
559
+ * // Next step: send manifest to provider
560
+ * await sendManifest(lease, manifest, certificate);
561
+ * }
562
+ * ```
563
+ *
564
+ * @example Error handling
565
+ * ```typescript
566
+ * const result = await createLease(clientContext, bid);
567
+ * if (!result.success) {
568
+ * if (result.error.code === 'TRANSACTION_FAILED') {
569
+ * console.error('Transaction failed:', result.error.context.rawLog);
570
+ * console.log('Common causes: insufficient funds, bid expired');
571
+ * }
572
+ * }
573
+ * ```
574
+ */
575
+ export async function createLease(clientContext, bid) {
576
+ try {
577
+ const { client, account } = clientContext;
578
+ const { bidId } = bid;
579
+ // Step 1: Create lease message from accepted bid
580
+ // The bid ID contains all coordinates needed to create the lease
581
+ const msg = {
582
+ typeUrl: getTypeUrl(MsgCreateLease),
583
+ value: MsgCreateLease.fromPartial({ bidId }),
584
+ };
585
+ // Step 2: Estimate gas dynamically
586
+ // Simulate the transaction to get accurate gas estimation
587
+ const gasEstimation = await client.simulate(account.address, [msg], 'Create lease via deploy-ability');
588
+ // Add 30% safety margin (standard for Cosmos SDK transactions)
589
+ // This matches cosmos-kit's default gas adjustment
590
+ const gasLimit = Math.ceil(gasEstimation * 1.3);
591
+ // Set transaction fee with dynamic gas limit
592
+ // Fee: 5,000 uakt (approximately 0.005 AKT)
593
+ const fee = {
594
+ amount: [{ denom: 'uakt', amount: '5000' }],
595
+ gas: gasLimit.toString(),
596
+ };
597
+ // Step 3: Sign and broadcast lease transaction
598
+ const res = await client.signAndBroadcast(account.address, [msg], fee, 'Create lease via deploy-ability');
599
+ // Step 4: Check transaction result
600
+ if (res.code !== 0) {
601
+ return {
602
+ success: false,
603
+ error: new DeploymentError(`Lease transaction failed: ${res.rawLog}`, 'TRANSACTION_FAILED', {
604
+ code: res.code,
605
+ rawLog: res.rawLog,
606
+ transactionHash: res.transactionHash,
607
+ }, false, 'Check wallet balance and bid validity. The bid may have expired or been withdrawn by the provider.'),
608
+ };
609
+ }
610
+ // Step 5: Return lease result with full coordinates
611
+ // The lease ID contains all information needed to interact with the provider
612
+ // BidID and LeaseID are structurally identical, so we extract the fields
613
+ // rather than using type casting (TypeScript sees them as different nominal types)
614
+ if (!bidId) {
615
+ return {
616
+ success: false,
617
+ error: new DeploymentError('Bid ID is missing from bid', 'INVALID_BID', { bid }, false, 'This should not happen - verify bid data structure'),
618
+ };
619
+ }
620
+ return {
621
+ success: true,
622
+ data: {
623
+ // Use LeaseID.fromPartial() to create proper protobuf type with $type field
624
+ leaseId: LeaseID.fromPartial({
625
+ owner: bidId.owner,
626
+ provider: bidId.provider,
627
+ dseq: bidId.dseq,
628
+ gseq: bidId.gseq,
629
+ oseq: bidId.oseq,
630
+ }),
631
+ transactionHash: res.transactionHash,
632
+ height: res.height,
633
+ },
634
+ };
635
+ }
636
+ catch (error) {
637
+ return {
638
+ success: false,
639
+ error: new DeploymentError(`Failed to create lease: ${error}`, 'LEASE_FAILED', { error: String(error) }, false, 'Check wallet connection and bid parameters'),
640
+ };
641
+ }
642
+ }
643
+ /**
644
+ * Close an active Akash deployment
645
+ *
646
+ * Broadcasts a MsgCloseDeployment transaction that terminates all leases
647
+ * associated with the deployment and returns the escrow deposit to the owner.
648
+ *
649
+ * **What happens when you close a deployment:**
650
+ * 1. All active leases for this deployment are terminated
651
+ * 2. Providers stop running your containers
652
+ * 3. The escrow deposit (typically 5 AKT) is returned to your wallet
653
+ * 4. The deployment is marked as closed on the blockchain
654
+ *
655
+ * **When to close a deployment:**
656
+ * - When you're done with the deployment and want to stop paying
657
+ * - When migrating to a different provider or configuration
658
+ * - When cleaning up test/development deployments
659
+ * - Before wallet maintenance (to recover locked funds)
660
+ *
661
+ * **Important Notes:**
662
+ * - You can only close deployments you own
663
+ * - Closed deployments cannot be reopened (create a new deployment instead)
664
+ * - Always close deployments when done to avoid ongoing charges
665
+ * - The escrow deposit is returned immediately upon closure
666
+ *
667
+ * @param clientContext - Signing client and account info
668
+ * @param wallet - Wallet context (must be deployment owner)
669
+ * @param dseq - Deployment sequence number to close
670
+ * @returns Result with closure confirmation or error
671
+ *
672
+ * @example Close a deployment
673
+ * ```typescript
674
+ * import { closeDeployment, createSigningClient } from 'deploy-ability/akash';
675
+ *
676
+ * // Create signing client
677
+ * const clientResult = await createSigningClient(wallet, 'mainnet');
678
+ * if (!clientResult.success) {
679
+ * console.error('Failed to create client:', clientResult.error.message);
680
+ * return;
681
+ * }
682
+ *
683
+ * // Close the deployment
684
+ * const result = await closeDeployment(
685
+ * clientResult.data,
686
+ * wallet,
687
+ * 1234567 // DSEQ from deployment
688
+ * );
689
+ *
690
+ * if (result.success) {
691
+ * console.log('Deployment closed successfully!');
692
+ * console.log('DSEQ:', result.data.dseq);
693
+ * console.log('TX:', result.data.transactionHash);
694
+ * console.log('Deposit returned to:', result.data.owner);
695
+ * } else {
696
+ * console.error('Failed to close:', result.error.message);
697
+ * }
698
+ * ```
699
+ *
700
+ * @example Close with error handling
701
+ * ```typescript
702
+ * const result = await closeDeployment(clientContext, wallet, dseq);
703
+ *
704
+ * if (!result.success) {
705
+ * if (result.error.code === 'TRANSACTION_FAILED') {
706
+ * console.error('Transaction failed:', result.error.context.rawLog);
707
+ * // Common causes:
708
+ * // - Not the deployment owner
709
+ * // - Deployment already closed
710
+ * // - Insufficient funds for transaction fee
711
+ * } else if (result.error.code === 'WALLET_ERROR') {
712
+ * console.error('Wallet issue - check connection');
713
+ * }
714
+ * }
715
+ * ```
716
+ *
717
+ * @example Complete deployment lifecycle
718
+ * ```typescript
719
+ * // Deploy
720
+ * const deployment = await deployToAkash({
721
+ * wallet,
722
+ * certificate,
723
+ * projectRoot: './',
724
+ * profile: 'dev',
725
+ * bidSelector: selectCheapestBid
726
+ * });
727
+ *
728
+ * if (deployment.success) {
729
+ * console.log('Deployed with DSEQ:', deployment.data.dseq);
730
+ *
731
+ * // ... use the deployment ...
732
+ *
733
+ * // Close when done
734
+ * const close = await closeDeployment(
735
+ * clientContext,
736
+ * wallet,
737
+ * deployment.data.dseq
738
+ * );
739
+ *
740
+ * if (close.success) {
741
+ * console.log('Cleaned up successfully');
742
+ * }
743
+ * }
744
+ * ```
745
+ */
746
+ export async function closeDeployment(clientContext, wallet, dseq) {
747
+ try {
748
+ const { client, account } = clientContext;
749
+ // Convert dseq to string for the message
750
+ const dseqString = typeof dseq === 'string' ? dseq : dseq.toString();
751
+ // Step 1: Create close deployment message
752
+ // This message tells the blockchain to terminate the deployment
753
+ // and return the escrow deposit to the owner
754
+ const msg = MsgCloseDeployment.fromPartial({
755
+ id: {
756
+ dseq: dseqString,
757
+ owner: wallet.address,
758
+ },
759
+ });
760
+ // Step 2: Wrap message with type-safe URL
761
+ const anyMsg = {
762
+ typeUrl: getTypeUrl(MsgCloseDeployment),
763
+ value: msg,
764
+ };
765
+ // Step 3: Estimate gas dynamically
766
+ // Simulate the transaction to get accurate gas estimation
767
+ // This prevents out-of-gas errors for deployments with many resources
768
+ const gasEstimation = await client.simulate(account.address, [anyMsg], 'Close deployment via deploy-ability');
769
+ // Add 30% safety margin (standard for Cosmos SDK transactions)
770
+ // This matches cosmos-kit's default gas adjustment
771
+ const gasLimit = Math.ceil(gasEstimation * 1.3);
772
+ // Set transaction fee with dynamic gas limit
773
+ // Fee: 5,000 uakt (approximately 0.005 AKT)
774
+ const fee = {
775
+ amount: [{ denom: 'uakt', amount: '5000' }],
776
+ gas: gasLimit.toString(),
777
+ };
778
+ // Step 4: Sign and broadcast close deployment transaction
779
+ const res = await client.signAndBroadcast(account.address, [anyMsg], fee, 'Close deployment via deploy-ability');
780
+ // Step 5: Check transaction result
781
+ // code === 0 means success, any other code is an error
782
+ if (res.code !== 0) {
783
+ return failure(new DeploymentError(`Close deployment transaction failed: ${res.rawLog}`, 'TRANSACTION_FAILED', {
784
+ code: res.code,
785
+ rawLog: res.rawLog,
786
+ transactionHash: res.transactionHash,
787
+ dseq: dseqString,
788
+ }, false, 'Common causes: Not deployment owner, deployment already closed, or insufficient funds for fees'));
789
+ }
790
+ // Step 6: Return closure confirmation
791
+ return success({
792
+ dseq: dseqString,
793
+ owner: wallet.address,
794
+ transactionHash: res.transactionHash,
795
+ height: res.height,
796
+ closedAt: new Date(),
797
+ });
798
+ }
799
+ catch (error) {
800
+ return failure(new DeploymentError(`Failed to close deployment: ${error}`, 'DEPLOYMENT_CLOSE_FAILED', { error: String(error), dseq: String(dseq) }, false, 'Check wallet connection, network status, and deployment ownership'));
801
+ }
802
+ }
803
+ //# sourceMappingURL=blockchain-client.js.map