@coinmasters/e2e-staking-suite 1.11.4
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/.turbo/turbo-build.log +5 -0
- package/CHANGELOG.md +1315 -0
- package/LICENSE +674 -0
- package/README.md +164 -0
- package/dist/index.js +740 -0
- package/jest.config.js +9 -0
- package/package.json +36 -0
- package/src/index.ts +909 -0
- package/tsconfig.json +14 -0
- package/vite.config.ts +19 -0
package/src/index.ts
ADDED
@@ -0,0 +1,909 @@
|
|
1
|
+
/*
|
2
|
+
E2E Staking Test Suite
|
3
|
+
|
4
|
+
This test suite focuses on Cosmos staking operations:
|
5
|
+
- Delegate to validators
|
6
|
+
- Undelegate from validators
|
7
|
+
- Claim staking rewards
|
8
|
+
- Validator API testing
|
9
|
+
|
10
|
+
============================================================================
|
11
|
+
๐ง CONFIGURATION - Edit TEST_CONFIG object to enable/disable specific tests
|
12
|
+
============================================================================
|
13
|
+
|
14
|
+
TEST_VALIDATOR_API: Test validator fetching APIs
|
15
|
+
TEST_STAKING_POSITIONS_API: Test staking positions detection
|
16
|
+
TEST_DIRECT_STAKING_API: Test direct staking API calls
|
17
|
+
TEST_CLAIM_REWARDS: Test reward claiming (๐ฏ RECOMMENDED TO START)
|
18
|
+
TEST_DELEGATE: Test delegation transactions
|
19
|
+
TEST_UNDELEGATE: Test undelegation transactions
|
20
|
+
TEST_STAKING_INTEGRATION: Test staking data integration
|
21
|
+
ACTUALLY_EXECUTE_TRANSACTIONS: Set to true to actually execute transactions
|
22
|
+
NETWORKS: Array of networks to test
|
23
|
+
|
24
|
+
Example for reward claiming only:
|
25
|
+
{
|
26
|
+
TEST_CLAIM_REWARDS: true,
|
27
|
+
TEST_DELEGATE: false,
|
28
|
+
TEST_UNDELEGATE: false,
|
29
|
+
ACTUALLY_EXECUTE_TRANSACTIONS: true, // To actually claim rewards
|
30
|
+
NETWORKS: ['GAIA'] // Cosmos Hub only
|
31
|
+
}
|
32
|
+
*/
|
33
|
+
|
34
|
+
require("dotenv").config()
|
35
|
+
require('dotenv').config({path:"../../.env"});
|
36
|
+
require('dotenv').config({path:"./../../.env"});
|
37
|
+
require("dotenv").config({path:'../../../.env'})
|
38
|
+
require("dotenv").config({path:'../../../../.env'})
|
39
|
+
|
40
|
+
const TAG = " | staking-e2e-test | "
|
41
|
+
// @ts-ignore
|
42
|
+
import { shortListSymbolToCaip, caipToNetworkId, networkIdToCaip } from '@pioneer-platform/pioneer-caip';
|
43
|
+
import { getChainEnumValue } from "@coinmasters/types";
|
44
|
+
const log = require("@pioneer-platform/loggerdog")()
|
45
|
+
let assert = require('assert')
|
46
|
+
let SDK = require('@coinmasters/pioneer-sdk')
|
47
|
+
let wait = require('wait-promise');
|
48
|
+
let {ChainToNetworkId} = require('@pioneer-platform/pioneer-caip');
|
49
|
+
let sleep = wait.sleep;
|
50
|
+
|
51
|
+
import {
|
52
|
+
getPaths,
|
53
|
+
// @ts-ignore
|
54
|
+
} from '@pioneer-platform/pioneer-coins';
|
55
|
+
|
56
|
+
interface StakingPosition {
|
57
|
+
type: 'delegation' | 'reward' | 'unbonding';
|
58
|
+
balance: string;
|
59
|
+
ticker: string;
|
60
|
+
valueUsd: number;
|
61
|
+
validator: string;
|
62
|
+
validatorAddress: string;
|
63
|
+
networkId: string;
|
64
|
+
caip: string;
|
65
|
+
}
|
66
|
+
|
67
|
+
interface Validator {
|
68
|
+
address: string;
|
69
|
+
moniker: string;
|
70
|
+
commission: string;
|
71
|
+
status: string;
|
72
|
+
tokens: string;
|
73
|
+
description?: {
|
74
|
+
moniker: string;
|
75
|
+
identity: string;
|
76
|
+
website: string;
|
77
|
+
details: string;
|
78
|
+
};
|
79
|
+
}
|
80
|
+
|
81
|
+
const test_staking_service = async function () {
|
82
|
+
let tag = TAG + " | test_staking_service | "
|
83
|
+
try {
|
84
|
+
console.time('staking-test-start');
|
85
|
+
|
86
|
+
// ๐ฏ Simple Configuration - Focus on reward claiming
|
87
|
+
const TEST_CONFIG = {
|
88
|
+
TEST_VALIDATOR_API: false,
|
89
|
+
TEST_STAKING_POSITIONS_API: true, // Need to find existing delegations
|
90
|
+
TEST_DIRECT_STAKING_API: true, // ๐ Enable to check direct API
|
91
|
+
TEST_CLAIM_REWARDS: true, // ๐ฏ Main focus - claim rewards
|
92
|
+
TEST_DELEGATE: false,
|
93
|
+
TEST_UNDELEGATE: false,
|
94
|
+
TEST_STAKING_INTEGRATION: false,
|
95
|
+
ACTUALLY_EXECUTE_TRANSACTIONS: true, // ๐ Actually claim rewards
|
96
|
+
NETWORKS: ['GAIA'] // Cosmos Hub only
|
97
|
+
};
|
98
|
+
|
99
|
+
log.info(tag, "๐ง Test Configuration:", TEST_CONFIG);
|
100
|
+
|
101
|
+
// Test configuration
|
102
|
+
const queryKey = "sdk:staking-test:"+Math.random();
|
103
|
+
log.info(tag, "queryKey: ", queryKey)
|
104
|
+
|
105
|
+
const username = "staking-user:"+Math.random()
|
106
|
+
assert(username)
|
107
|
+
|
108
|
+
// Use local or remote Pioneer API
|
109
|
+
let spec = process.env.PIONEER_SPEC || 'https://pioneers.dev/spec/swagger.json'
|
110
|
+
// let spec = 'http://127.0.0.1:9001/spec/swagger.json' // For local testing
|
111
|
+
|
112
|
+
// Focus on Cosmos chains that support staking
|
113
|
+
let stakingChains = TEST_CONFIG.NETWORKS
|
114
|
+
|
115
|
+
const allByCaip = stakingChains.map(chainStr => {
|
116
|
+
const chain = getChainEnumValue(chainStr);
|
117
|
+
if (chain) {
|
118
|
+
return ChainToNetworkId[chain];
|
119
|
+
}
|
120
|
+
return;
|
121
|
+
}).filter(Boolean);
|
122
|
+
|
123
|
+
let blockchains = allByCaip
|
124
|
+
log.info(tag, "Staking blockchains: ", blockchains)
|
125
|
+
|
126
|
+
// Get paths for staking chains
|
127
|
+
let paths = getPaths(blockchains)
|
128
|
+
log.info(tag, "Staking paths: ", paths.length)
|
129
|
+
|
130
|
+
let config: any = {
|
131
|
+
username,
|
132
|
+
queryKey,
|
133
|
+
spec,
|
134
|
+
wss: process.env.VITE_PIONEER_URL_WSS || 'wss://pioneers.dev',
|
135
|
+
keepkeyApiKey: process.env.KEEPKEY_API_KEY || 'e4ea6479-5ea4-4c7d-b824-e075101bf9fd',
|
136
|
+
keepkeyEndpoint: 'http://127.0.0.1:1647',
|
137
|
+
paths,
|
138
|
+
blockchains,
|
139
|
+
nodes: [],
|
140
|
+
pubkeys: [],
|
141
|
+
balances: [],
|
142
|
+
};
|
143
|
+
|
144
|
+
log.info(tag, "Initializing Pioneer SDK for staking tests...")
|
145
|
+
log.info(tag, "๐ WebSocket URL:", config.wss)
|
146
|
+
log.info(tag, "๐ KeepKey API Key:", config.keepkeyApiKey ? 'configured' : 'missing')
|
147
|
+
log.info(tag, "๐ KeepKey Endpoint:", config.keepkeyEndpoint)
|
148
|
+
|
149
|
+
let app = new SDK.SDK(spec, config)
|
150
|
+
let resultInit = await app.init({}, {})
|
151
|
+
|
152
|
+
log.info(tag, "โ
Pioneer SDK initialized")
|
153
|
+
|
154
|
+
// Manual step-by-step initialization like working test
|
155
|
+
log.info(tag, "๐ Starting getGasAssets()...")
|
156
|
+
await app.getGasAssets()
|
157
|
+
log.info(tag, "โ
getGasAssets() complete")
|
158
|
+
|
159
|
+
log.info(tag, "๐ Starting getPubkeys()...")
|
160
|
+
await app.getPubkeys()
|
161
|
+
log.info(tag, "โ
getPubkeys() complete - pubkeys count:", app.pubkeys.length)
|
162
|
+
|
163
|
+
log.info(tag, "๐ Starting getBalances()...")
|
164
|
+
await app.getBalances()
|
165
|
+
log.info(tag, "โ
getBalances() complete - balances count:", app.balances.length)
|
166
|
+
|
167
|
+
// Verify basic requirements
|
168
|
+
assert(app.blockchains, "Blockchains not initialized")
|
169
|
+
assert(app.pubkeys, "Pubkeys not initialized")
|
170
|
+
assert(app.balances, "Balances not initialized")
|
171
|
+
assert(app.pioneer, "Pioneer API not initialized")
|
172
|
+
|
173
|
+
// Debug: Check what we have after initialization
|
174
|
+
log.info(tag, "๐ After initialization:")
|
175
|
+
log.info(tag, ` - Pubkeys: ${app.pubkeys.length}`)
|
176
|
+
log.info(tag, ` - Balances: ${app.balances.length}`)
|
177
|
+
log.info(tag, ` - KeepKey SDK: ${app.keepKeySdk ? 'initialized' : 'not initialized'}`)
|
178
|
+
|
179
|
+
if (app.pubkeys.length > 0) {
|
180
|
+
log.info(tag, "๐ Available pubkeys:")
|
181
|
+
app.pubkeys.forEach((pubkey: any, index: number) => {
|
182
|
+
log.info(tag, ` ${index + 1}. ${pubkey.address} (${pubkey.networks.join(', ')})`)
|
183
|
+
})
|
184
|
+
}
|
185
|
+
|
186
|
+
if (app.balances.length > 0) {
|
187
|
+
log.info(tag, "๐ฐ Available balances:")
|
188
|
+
app.balances.slice(0, 10).forEach((balance: any, index: number) => {
|
189
|
+
log.info(tag, ` ${index + 1}. ${balance.balance} ${balance.ticker} (${balance.type || 'normal'})`)
|
190
|
+
})
|
191
|
+
}
|
192
|
+
|
193
|
+
// **TEST 1: Validator API Testing**
|
194
|
+
if (TEST_CONFIG.TEST_VALIDATOR_API) {
|
195
|
+
log.info(tag, "")
|
196
|
+
log.info(tag, "=".repeat(60))
|
197
|
+
log.info(tag, "๐งช TEST 1: Validator API Testing")
|
198
|
+
log.info(tag, "=".repeat(60))
|
199
|
+
} else {
|
200
|
+
log.info(tag, "โญ๏ธ Skipping TEST 1: Validator API Testing")
|
201
|
+
}
|
202
|
+
|
203
|
+
const validatorTests = new Map<string, Validator[]>()
|
204
|
+
|
205
|
+
if (TEST_CONFIG.TEST_VALIDATOR_API) {
|
206
|
+
for (const networkId of blockchains) {
|
207
|
+
log.info(tag, `Testing validator API for ${networkId}...`)
|
208
|
+
|
209
|
+
try {
|
210
|
+
let network: string;
|
211
|
+
if (networkId === 'cosmos:cosmoshub-4') {
|
212
|
+
network = 'cosmos';
|
213
|
+
} else if (networkId === 'cosmos:osmosis-1') {
|
214
|
+
network = 'osmosis';
|
215
|
+
} else {
|
216
|
+
log.warn(tag, `Unsupported networkId for validator testing: ${networkId}`)
|
217
|
+
continue;
|
218
|
+
}
|
219
|
+
|
220
|
+
// Test GetValidators API
|
221
|
+
log.info(tag, `Fetching validators for ${network}...`)
|
222
|
+
const validatorsResponse = await app.pioneer.GetValidators({ network })
|
223
|
+
|
224
|
+
if (validatorsResponse && validatorsResponse.data && validatorsResponse.data.length > 0) {
|
225
|
+
const validators = validatorsResponse.data
|
226
|
+
log.info(tag, `โ
Found ${validators.length} validators for ${network}`)
|
227
|
+
|
228
|
+
// Validate validator structure
|
229
|
+
const firstValidator = validators[0]
|
230
|
+
assert(firstValidator.address, `Validator missing address`)
|
231
|
+
assert(firstValidator.moniker, `Validator missing moniker`)
|
232
|
+
assert(firstValidator.commission !== undefined, `Validator missing commission`)
|
233
|
+
assert(firstValidator.status, `Validator missing status`)
|
234
|
+
|
235
|
+
// Store for later use in delegation tests
|
236
|
+
validatorTests.set(networkId, validators.slice(0, 3)) // Keep top 3 validators
|
237
|
+
|
238
|
+
// Log sample validators
|
239
|
+
log.info(tag, `Sample validators for ${network}:`)
|
240
|
+
validators.slice(0, 3).forEach((validator: Validator, index: number) => {
|
241
|
+
log.info(tag, ` ${index + 1}. ${validator.moniker} - ${(parseFloat(validator.commission) * 100).toFixed(2)}% commission`)
|
242
|
+
})
|
243
|
+
} else {
|
244
|
+
log.warn(tag, `โ ๏ธ No validators found for ${network}`)
|
245
|
+
}
|
246
|
+
|
247
|
+
} catch (error) {
|
248
|
+
log.error(tag, `โ Error fetching validators for ${networkId}:`, error)
|
249
|
+
// Continue with other networks
|
250
|
+
}
|
251
|
+
}
|
252
|
+
}
|
253
|
+
|
254
|
+
// **TEST 2: Existing Staking Positions Detection**
|
255
|
+
if (TEST_CONFIG.TEST_STAKING_POSITIONS_API) {
|
256
|
+
log.info(tag, "")
|
257
|
+
log.info(tag, "=".repeat(60))
|
258
|
+
log.info(tag, "๐งช TEST 2: Existing Staking Positions Detection")
|
259
|
+
log.info(tag, "=".repeat(60))
|
260
|
+
} else {
|
261
|
+
log.info(tag, "โญ๏ธ Skipping TEST 2: Existing Staking Positions Detection")
|
262
|
+
}
|
263
|
+
|
264
|
+
// Find existing staking positions
|
265
|
+
let existingStakingPositions: any[] = []
|
266
|
+
|
267
|
+
if (TEST_CONFIG.TEST_STAKING_POSITIONS_API) {
|
268
|
+
existingStakingPositions = app.balances.filter((balance: any) =>
|
269
|
+
balance.chart === 'staking' || balance.type === 'delegation' || balance.type === 'reward' || balance.type === 'unbonding'
|
270
|
+
)
|
271
|
+
|
272
|
+
log.info(tag, `Found ${existingStakingPositions.length} existing staking positions`)
|
273
|
+
|
274
|
+
if (existingStakingPositions.length > 0) {
|
275
|
+
log.info(tag, "๐ Existing staking positions:")
|
276
|
+
existingStakingPositions.forEach((position: any, index: number) => {
|
277
|
+
log.info(tag, ` ${index + 1}. ${position.type} - ${position.balance} ${position.ticker} (${position.validator || 'N/A'})`)
|
278
|
+
})
|
279
|
+
} else {
|
280
|
+
log.info(tag, "โน๏ธ No existing staking positions found")
|
281
|
+
}
|
282
|
+
}
|
283
|
+
|
284
|
+
// **TEST 3: Direct Staking Position API Testing**
|
285
|
+
if (TEST_CONFIG.TEST_DIRECT_STAKING_API) {
|
286
|
+
log.info(tag, "")
|
287
|
+
log.info(tag, "=".repeat(60))
|
288
|
+
log.info(tag, "๐งช TEST 3: Direct Staking Position API Testing")
|
289
|
+
log.info(tag, "=".repeat(60))
|
290
|
+
} else {
|
291
|
+
log.info(tag, "โญ๏ธ Skipping TEST 3: Direct Staking Position API Testing")
|
292
|
+
}
|
293
|
+
|
294
|
+
const stakingPositionsByNetwork = new Map<string, StakingPosition[]>()
|
295
|
+
|
296
|
+
if (TEST_CONFIG.TEST_DIRECT_STAKING_API) {
|
297
|
+
for (const networkId of blockchains) {
|
298
|
+
log.info(tag, `Testing staking positions API for ${networkId}...`)
|
299
|
+
|
300
|
+
// Find pubkeys for this network
|
301
|
+
const networkPubkeys = app.pubkeys.filter((pubkey: any) =>
|
302
|
+
pubkey.networks.includes(networkId)
|
303
|
+
)
|
304
|
+
|
305
|
+
if (networkPubkeys.length === 0) {
|
306
|
+
log.warn(tag, `No pubkeys found for ${networkId}`)
|
307
|
+
continue
|
308
|
+
}
|
309
|
+
|
310
|
+
try {
|
311
|
+
let network: string;
|
312
|
+
if (networkId === 'cosmos:cosmoshub-4') {
|
313
|
+
network = 'cosmos';
|
314
|
+
} else if (networkId === 'cosmos:osmosis-1') {
|
315
|
+
network = 'osmosis';
|
316
|
+
} else {
|
317
|
+
log.warn(tag, `Unsupported networkId for staking positions: ${networkId}`)
|
318
|
+
continue;
|
319
|
+
}
|
320
|
+
|
321
|
+
for (const pubkey of networkPubkeys) {
|
322
|
+
log.info(tag, `Checking staking positions for ${pubkey.address} on ${network}...`)
|
323
|
+
|
324
|
+
const stakingResponse = await app.pioneer.GetStakingPositions({
|
325
|
+
network: network,
|
326
|
+
address: pubkey.address
|
327
|
+
})
|
328
|
+
|
329
|
+
if (stakingResponse && stakingResponse.data && stakingResponse.data.length > 0) {
|
330
|
+
const positions = stakingResponse.data
|
331
|
+
log.info(tag, `โ
Found ${positions.length} staking positions for ${pubkey.address}`)
|
332
|
+
|
333
|
+
// Store positions for later use
|
334
|
+
stakingPositionsByNetwork.set(networkId, positions)
|
335
|
+
|
336
|
+
// Also add to existingStakingPositions for reward claiming
|
337
|
+
positions.forEach((position: StakingPosition) => {
|
338
|
+
// Add missing fields for compatibility
|
339
|
+
if (!position.networkId) position.networkId = networkId
|
340
|
+
if (!position.caip) position.caip = networkIdToCaip(networkId) || networkId
|
341
|
+
existingStakingPositions.push(position)
|
342
|
+
})
|
343
|
+
|
344
|
+
// Log position details
|
345
|
+
positions.forEach((position: StakingPosition, index: number) => {
|
346
|
+
log.info(tag, ` ${index + 1}. ${position.type}: ${position.balance} ${position.ticker} (${position.validator})`)
|
347
|
+
})
|
348
|
+
|
349
|
+
// Debug: Log the actual API response structure
|
350
|
+
log.info(tag, `Raw API response for ${network}:`, JSON.stringify(positions, null, 2))
|
351
|
+
|
352
|
+
// Validate position structure
|
353
|
+
positions.forEach((position: StakingPosition, index: number) => {
|
354
|
+
log.info(tag, `Position ${index + 1} raw data:`, position)
|
355
|
+
assert(position.type, 'Position must have type')
|
356
|
+
assert(position.balance, 'Position must have balance')
|
357
|
+
assert(position.ticker, 'Position must have ticker')
|
358
|
+
|
359
|
+
if (position.type === 'delegation') {
|
360
|
+
if (!position.validatorAddress) {
|
361
|
+
log.error(tag, `โ CRITICAL: Delegation position missing validatorAddress`)
|
362
|
+
log.error(tag, `Position data:`, position)
|
363
|
+
throw new Error(`API response missing validatorAddress for delegation position. This is required for transactions.`)
|
364
|
+
}
|
365
|
+
}
|
366
|
+
|
367
|
+
if (position.type === 'reward') {
|
368
|
+
if (!position.validatorAddress) {
|
369
|
+
log.error(tag, `โ CRITICAL: Reward position missing validatorAddress`)
|
370
|
+
log.error(tag, `Position data:`, position)
|
371
|
+
throw new Error(`API response missing validatorAddress for reward position. This is required for claiming rewards.`)
|
372
|
+
}
|
373
|
+
}
|
374
|
+
})
|
375
|
+
|
376
|
+
break // Found positions for this network, no need to check other pubkeys
|
377
|
+
} else {
|
378
|
+
log.info(tag, `โน๏ธ No staking positions found for ${pubkey.address} on ${network}`)
|
379
|
+
}
|
380
|
+
}
|
381
|
+
|
382
|
+
} catch (error) {
|
383
|
+
log.error(tag, `โ Error fetching staking positions for ${networkId}:`, error)
|
384
|
+
// Continue with other networks
|
385
|
+
}
|
386
|
+
}
|
387
|
+
}
|
388
|
+
|
389
|
+
// **TEST 4: Undelegation Flow Testing**
|
390
|
+
if (TEST_CONFIG.TEST_UNDELEGATE) {
|
391
|
+
log.info(tag, "")
|
392
|
+
log.info(tag, "=".repeat(60))
|
393
|
+
log.info(tag, "๐งช TEST 4: Undelegation Flow Testing")
|
394
|
+
log.info(tag, "=".repeat(60))
|
395
|
+
} else {
|
396
|
+
log.info(tag, "โญ๏ธ Skipping TEST 4: Undelegation Flow Testing")
|
397
|
+
}
|
398
|
+
|
399
|
+
if (TEST_CONFIG.TEST_UNDELEGATE) {
|
400
|
+
// Find delegation positions to undelegate from
|
401
|
+
const delegationPositions = existingStakingPositions.filter((position: any) =>
|
402
|
+
position.type === 'delegation' && parseFloat(position.balance) > 0
|
403
|
+
)
|
404
|
+
|
405
|
+
if (delegationPositions.length > 0) {
|
406
|
+
log.info(tag, `Found ${delegationPositions.length} delegation positions available for undelegation`)
|
407
|
+
|
408
|
+
// Test undelegation with the first delegation position
|
409
|
+
const targetDelegation = delegationPositions[0]
|
410
|
+
log.info(tag, `๐ฏ Testing undelegation from: ${targetDelegation.validator}`)
|
411
|
+
log.info(tag, `๐ Current delegation: ${targetDelegation.balance} ${targetDelegation.ticker}`)
|
412
|
+
|
413
|
+
const networkId = targetDelegation.networkId || caipToNetworkId(targetDelegation.caip)
|
414
|
+
const caip = targetDelegation.caip
|
415
|
+
|
416
|
+
// Set asset context for undelegation
|
417
|
+
await app.setAssetContext({ caip })
|
418
|
+
|
419
|
+
// Calculate small undelegation amount (10% of current delegation)
|
420
|
+
const currentBalance = parseFloat(targetDelegation.balance)
|
421
|
+
const undelegateAmount = Math.max(0.01, currentBalance * 0.1) // At least 0.01 or 10%
|
422
|
+
|
423
|
+
log.info(tag, `๐ Attempting to undelegate ${undelegateAmount} ${targetDelegation.ticker}...`)
|
424
|
+
|
425
|
+
try {
|
426
|
+
// Build undelegation transaction
|
427
|
+
const undelegatePayload = {
|
428
|
+
validatorAddress: targetDelegation.validatorAddress || targetDelegation.validator,
|
429
|
+
amount: undelegateAmount,
|
430
|
+
memo: 'E2E Undelegation Test'
|
431
|
+
}
|
432
|
+
|
433
|
+
log.info(tag, `๐จ Building undelegation transaction...`)
|
434
|
+
log.info(tag, `๐ค Payload:`, undelegatePayload)
|
435
|
+
|
436
|
+
// Test actual undelegation transaction building
|
437
|
+
try {
|
438
|
+
const unsignedTx = await app.buildUndelegateTx(caip, undelegatePayload)
|
439
|
+
log.info(tag, `โ
Undelegation transaction built successfully`)
|
440
|
+
log.info(tag, `๐ Transaction structure:`, JSON.stringify(unsignedTx, null, 2))
|
441
|
+
|
442
|
+
// Validate transaction structure
|
443
|
+
assert(unsignedTx.signDoc, 'Transaction must have signDoc')
|
444
|
+
assert(unsignedTx.signDoc.msgs, 'Transaction must have messages')
|
445
|
+
assert(unsignedTx.signDoc.msgs[0].type === 'cosmos-sdk/MsgUndelegate', 'Must be MsgUndelegate')
|
446
|
+
assert(unsignedTx.signDoc.msgs[0].value.delegator_address, 'Must have delegator_address')
|
447
|
+
assert(unsignedTx.signDoc.msgs[0].value.validator_address, 'Must have validator_address')
|
448
|
+
assert(unsignedTx.signDoc.msgs[0].value.amount, 'Must have amount')
|
449
|
+
|
450
|
+
log.info(tag, `โ
Undelegation transaction structure validated`)
|
451
|
+
|
452
|
+
if (TEST_CONFIG.ACTUALLY_EXECUTE_TRANSACTIONS) {
|
453
|
+
log.info(tag, `๐ EXECUTING UNDELEGATION TRANSACTION...`)
|
454
|
+
log.info(tag, `โ ๏ธ This will start unbonding period (21 days for Cosmos)`)
|
455
|
+
|
456
|
+
// Sign the transaction
|
457
|
+
log.info(tag, `โ๏ธ Signing transaction...`)
|
458
|
+
const signedTx = await app.signTx({ caip, unsignedTx })
|
459
|
+
log.info(tag, `โ
Transaction signed successfully`)
|
460
|
+
|
461
|
+
// Broadcast the transaction
|
462
|
+
log.info(tag, `๐ก Broadcasting transaction...`)
|
463
|
+
const broadcast = await app.broadcastTx(caip, signedTx)
|
464
|
+
log.info(tag, `โ
Transaction broadcasted:`, broadcast)
|
465
|
+
|
466
|
+
// Follow the transaction
|
467
|
+
log.info(tag, `๐ Following transaction...`)
|
468
|
+
const followResult = await app.followTransaction(caip, broadcast)
|
469
|
+
log.info(tag, `โ
Transaction completed:`, followResult)
|
470
|
+
|
471
|
+
} else {
|
472
|
+
log.info(tag, `โน๏ธ Skipping execution - set ACTUALLY_EXECUTE_TRANSACTIONS to true to execute`)
|
473
|
+
}
|
474
|
+
|
475
|
+
} catch (buildError) {
|
476
|
+
log.error(tag, `โ Error building undelegation transaction:`, buildError)
|
477
|
+
// Continue with test - this is expected if no actual delegation exists
|
478
|
+
}
|
479
|
+
|
480
|
+
} catch (error) {
|
481
|
+
log.error(tag, `โ Error in undelegation flow:`, error)
|
482
|
+
}
|
483
|
+
|
484
|
+
} else {
|
485
|
+
log.info(tag, `โน๏ธ No delegation positions found for undelegation testing`)
|
486
|
+
}
|
487
|
+
}
|
488
|
+
|
489
|
+
// **TEST 5: Delegation Flow Testing**
|
490
|
+
if (TEST_CONFIG.TEST_DELEGATE) {
|
491
|
+
log.info(tag, "")
|
492
|
+
log.info(tag, "=".repeat(60))
|
493
|
+
log.info(tag, "๐งช TEST 5: Delegation Flow Testing")
|
494
|
+
log.info(tag, "=".repeat(60))
|
495
|
+
} else {
|
496
|
+
log.info(tag, "โญ๏ธ Skipping TEST 5: Delegation Flow Testing")
|
497
|
+
}
|
498
|
+
|
499
|
+
if (TEST_CONFIG.TEST_DELEGATE) {
|
500
|
+
// Test delegation for each network with available balance
|
501
|
+
for (const networkId of blockchains) {
|
502
|
+
log.info(tag, `Testing delegation flow for ${networkId}...`)
|
503
|
+
|
504
|
+
const caip = networkIdToCaip(networkId)
|
505
|
+
if (!caip) {
|
506
|
+
log.warn(tag, `Could not convert networkId to CAIP: ${networkId}`)
|
507
|
+
continue
|
508
|
+
}
|
509
|
+
|
510
|
+
// Find available balance for delegation
|
511
|
+
const availableBalance = app.balances.find((balance: any) =>
|
512
|
+
balance.caip === caip && balance.chart !== 'staking'
|
513
|
+
)
|
514
|
+
|
515
|
+
if (!availableBalance || parseFloat(availableBalance.balance) <= 0) {
|
516
|
+
log.info(tag, `โน๏ธ No available balance for delegation on ${networkId}`)
|
517
|
+
continue
|
518
|
+
}
|
519
|
+
|
520
|
+
// Get validators for this network
|
521
|
+
const validators = validatorTests.get(networkId)
|
522
|
+
if (!validators || validators.length === 0) {
|
523
|
+
log.info(tag, `โน๏ธ No validators available for delegation on ${networkId}`)
|
524
|
+
continue
|
525
|
+
}
|
526
|
+
|
527
|
+
// Set asset context
|
528
|
+
await app.setAssetContext({ caip })
|
529
|
+
|
530
|
+
// Calculate small delegation amount
|
531
|
+
const currentBalance = parseFloat(availableBalance.balance)
|
532
|
+
const delegateAmount = Math.min(0.1, currentBalance * 0.1) // Max 0.1 or 10% of balance
|
533
|
+
|
534
|
+
if (delegateAmount < 0.01) {
|
535
|
+
log.info(tag, `โน๏ธ Balance too low for delegation test on ${networkId}`)
|
536
|
+
continue
|
537
|
+
}
|
538
|
+
|
539
|
+
// Select first validator for delegation
|
540
|
+
const targetValidator = validators[0]
|
541
|
+
|
542
|
+
log.info(tag, `๐ฏ Testing delegation to: ${targetValidator.moniker}`)
|
543
|
+
log.info(tag, `๐ฐ Available balance: ${currentBalance} ${availableBalance.ticker}`)
|
544
|
+
log.info(tag, `๐ Delegation amount: ${delegateAmount} ${availableBalance.ticker}`)
|
545
|
+
|
546
|
+
try {
|
547
|
+
// Build delegation transaction payload
|
548
|
+
const delegatePayload = {
|
549
|
+
validatorAddress: targetValidator.address,
|
550
|
+
amount: delegateAmount,
|
551
|
+
memo: 'E2E Delegation Test'
|
552
|
+
}
|
553
|
+
|
554
|
+
log.info(tag, `๐จ Building delegation transaction...`)
|
555
|
+
log.info(tag, `๐ค Payload:`, delegatePayload)
|
556
|
+
|
557
|
+
// Test actual delegation transaction building
|
558
|
+
try {
|
559
|
+
const unsignedTx = await app.buildDelegateTx(caip, delegatePayload)
|
560
|
+
log.info(tag, `โ
Delegation transaction built successfully`)
|
561
|
+
log.info(tag, `๐ Transaction structure:`, JSON.stringify(unsignedTx, null, 2))
|
562
|
+
|
563
|
+
// Validate transaction structure
|
564
|
+
assert(unsignedTx.signDoc, 'Transaction must have signDoc')
|
565
|
+
assert(unsignedTx.signDoc.msgs, 'Transaction must have messages')
|
566
|
+
assert(unsignedTx.signDoc.msgs[0].type === 'cosmos-sdk/MsgDelegate', 'Must be MsgDelegate')
|
567
|
+
assert(unsignedTx.signDoc.msgs[0].value.delegator_address, 'Must have delegator_address')
|
568
|
+
assert(unsignedTx.signDoc.msgs[0].value.validator_address, 'Must have validator_address')
|
569
|
+
assert(unsignedTx.signDoc.msgs[0].value.amount, 'Must have amount')
|
570
|
+
|
571
|
+
log.info(tag, `โ
Delegation transaction structure validated`)
|
572
|
+
|
573
|
+
if (TEST_CONFIG.ACTUALLY_EXECUTE_TRANSACTIONS) {
|
574
|
+
log.info(tag, `๐ EXECUTING DELEGATION TRANSACTION...`)
|
575
|
+
|
576
|
+
// Sign the transaction
|
577
|
+
log.info(tag, `โ๏ธ Signing transaction...`)
|
578
|
+
const signedTx = await app.signTx({ caip, unsignedTx })
|
579
|
+
log.info(tag, `โ
Transaction signed successfully`)
|
580
|
+
|
581
|
+
// Broadcast the transaction
|
582
|
+
log.info(tag, `๐ก Broadcasting transaction...`)
|
583
|
+
const broadcast = await app.broadcastTx(caip, signedTx)
|
584
|
+
log.info(tag, `โ
Transaction broadcasted:`, broadcast)
|
585
|
+
|
586
|
+
// Follow the transaction
|
587
|
+
log.info(tag, `๐ Following transaction...`)
|
588
|
+
const followResult = await app.followTransaction(caip, broadcast)
|
589
|
+
log.info(tag, `โ
Transaction completed:`, followResult)
|
590
|
+
|
591
|
+
} else {
|
592
|
+
log.info(tag, `โน๏ธ Skipping execution - set ACTUALLY_EXECUTE_TRANSACTIONS to true to execute`)
|
593
|
+
}
|
594
|
+
|
595
|
+
} catch (buildError) {
|
596
|
+
log.error(tag, `โ Error building delegation transaction:`, buildError)
|
597
|
+
// Continue with test - this is expected if no balance available
|
598
|
+
}
|
599
|
+
|
600
|
+
} catch (error) {
|
601
|
+
log.error(tag, `โ Error in delegation flow:`, error)
|
602
|
+
}
|
603
|
+
}
|
604
|
+
}
|
605
|
+
|
606
|
+
// **TEST 6: Reward Claiming Testing**
|
607
|
+
if (TEST_CONFIG.TEST_CLAIM_REWARDS) {
|
608
|
+
log.info(tag, "")
|
609
|
+
log.info(tag, "=".repeat(60))
|
610
|
+
log.info(tag, "๐งช TEST 6: Reward Claiming Testing")
|
611
|
+
log.info(tag, "=".repeat(60))
|
612
|
+
} else {
|
613
|
+
log.info(tag, "โญ๏ธ Skipping TEST 6: Reward Claiming Testing")
|
614
|
+
}
|
615
|
+
|
616
|
+
if (TEST_CONFIG.TEST_CLAIM_REWARDS) {
|
617
|
+
// Find reward positions
|
618
|
+
const rewardPositions = existingStakingPositions.filter((position: any) =>
|
619
|
+
position.type === 'reward' && parseFloat(position.balance) > 0
|
620
|
+
)
|
621
|
+
|
622
|
+
if (rewardPositions.length > 0) {
|
623
|
+
log.info(tag, `Found ${rewardPositions.length} reward positions available for claiming`)
|
624
|
+
|
625
|
+
rewardPositions.forEach((reward: any, index: number) => {
|
626
|
+
log.info(tag, ` ${index + 1}. ${reward.balance} ${reward.ticker} from ${reward.validatorAddress}`)
|
627
|
+
})
|
628
|
+
|
629
|
+
// Test reward claiming with the first reward position
|
630
|
+
const targetReward = rewardPositions[0]
|
631
|
+
const networkId = targetReward.networkId || caipToNetworkId(targetReward.caip)
|
632
|
+
const caip = targetReward.caip
|
633
|
+
|
634
|
+
log.info(tag, `๐ฏ Testing reward claiming for: ${targetReward.validatorAddress}`)
|
635
|
+
log.info(tag, `๐ฐ Available rewards: ${targetReward.balance} ${targetReward.ticker}`)
|
636
|
+
|
637
|
+
try {
|
638
|
+
// Build reward claiming transaction payload
|
639
|
+
const claimPayload = {
|
640
|
+
validatorAddress: targetReward.validatorAddress,
|
641
|
+
memo: 'E2E Reward Claim Test'
|
642
|
+
}
|
643
|
+
|
644
|
+
log.info(tag, `๐จ Building reward claim transaction...`)
|
645
|
+
log.info(tag, `๐ค Payload:`, claimPayload)
|
646
|
+
|
647
|
+
// Test actual reward claiming transaction building
|
648
|
+
try {
|
649
|
+
const unsignedTx = await app.buildClaimRewardsTx(caip, claimPayload)
|
650
|
+
log.info(tag, `โ
Reward claim transaction built successfully`)
|
651
|
+
log.info(tag, `๐ Transaction structure:`, JSON.stringify(unsignedTx, null, 2))
|
652
|
+
|
653
|
+
// Validate transaction structure
|
654
|
+
assert(unsignedTx.signDoc, 'Transaction must have signDoc')
|
655
|
+
assert(unsignedTx.signDoc.msgs, 'Transaction must have messages')
|
656
|
+
assert(unsignedTx.signDoc.msgs[0].type === 'cosmos-sdk/MsgWithdrawDelegationReward', 'Must be MsgWithdrawDelegationReward')
|
657
|
+
assert(unsignedTx.signDoc.msgs[0].value.delegator_address, 'Must have delegator_address')
|
658
|
+
assert(unsignedTx.signDoc.msgs[0].value.validator_address, 'Must have validator_address')
|
659
|
+
|
660
|
+
log.info(tag, `โ
Reward claim transaction structure validated`)
|
661
|
+
|
662
|
+
if (TEST_CONFIG.ACTUALLY_EXECUTE_TRANSACTIONS) {
|
663
|
+
log.info(tag, `๐ EXECUTING REWARD CLAIM TRANSACTION...`)
|
664
|
+
|
665
|
+
// Sign the transaction
|
666
|
+
log.info(tag, `โ๏ธ Signing transaction...`)
|
667
|
+
log.info(tag, `๐ Signing with CAIP: ${caip}`)
|
668
|
+
log.info(tag, `๐ Signing with unsignedTx keys: ${Object.keys(unsignedTx)}`)
|
669
|
+
const signedTx = await app.signTx({ caip, unsignedTx })
|
670
|
+
log.info(tag, `โ
Transaction signed successfully`)
|
671
|
+
|
672
|
+
// Broadcast the transaction
|
673
|
+
log.info(tag, `๐ก Broadcasting transaction...`)
|
674
|
+
const broadcast = await app.broadcastTx(caip, signedTx)
|
675
|
+
log.info(tag, `โ
Transaction broadcasted:`, broadcast)
|
676
|
+
|
677
|
+
// Follow the transaction
|
678
|
+
log.info(tag, `๐ Following transaction...`)
|
679
|
+
const followResult = await app.followTransaction(caip, broadcast)
|
680
|
+
log.info(tag, `โ
Transaction completed:`, followResult)
|
681
|
+
|
682
|
+
} else {
|
683
|
+
log.info(tag, `โน๏ธ Skipping execution - set ACTUALLY_EXECUTE_TRANSACTIONS to true to execute`)
|
684
|
+
}
|
685
|
+
|
686
|
+
} catch (buildError) {
|
687
|
+
log.error(tag, `โ Error building reward claim transaction:`, buildError)
|
688
|
+
// Continue with test - this is expected if no rewards available
|
689
|
+
}
|
690
|
+
|
691
|
+
} catch (error) {
|
692
|
+
log.error(tag, `โ Error in reward claiming flow:`, error)
|
693
|
+
}
|
694
|
+
|
695
|
+
} else {
|
696
|
+
log.info(tag, `โน๏ธ No reward positions found for claiming testing`)
|
697
|
+
|
698
|
+
// Try to find delegation positions that might have rewards
|
699
|
+
const delegationPositions = existingStakingPositions.filter((position: any) =>
|
700
|
+
position.type === 'delegation' && parseFloat(position.balance) > 0
|
701
|
+
)
|
702
|
+
|
703
|
+
if (delegationPositions.length > 0) {
|
704
|
+
log.info(tag, `๐ก Found ${delegationPositions.length} delegation positions that might have rewards:`)
|
705
|
+
delegationPositions.forEach((delegation: any, index: number) => {
|
706
|
+
log.info(tag, ` ${index + 1}. ${delegation.balance} ${delegation.ticker} delegated to ${delegation.validatorAddress}`)
|
707
|
+
})
|
708
|
+
|
709
|
+
// Test claiming ALL rewards from all delegations
|
710
|
+
const validatorAddresses = delegationPositions.map((delegation: any) =>
|
711
|
+
delegation.validatorAddress
|
712
|
+
)
|
713
|
+
|
714
|
+
const caip = delegationPositions[0].caip
|
715
|
+
|
716
|
+
log.info(tag, `๐ฏ Testing CLAIM ALL REWARDS from ${validatorAddresses.length} validators`)
|
717
|
+
|
718
|
+
try {
|
719
|
+
const claimAllPayload = {
|
720
|
+
validatorAddresses: validatorAddresses,
|
721
|
+
memo: 'E2E Claim All Rewards Test'
|
722
|
+
}
|
723
|
+
|
724
|
+
log.info(tag, `๐จ Building claim all rewards transaction...`)
|
725
|
+
log.info(tag, `๐ค Payload:`, claimAllPayload)
|
726
|
+
|
727
|
+
const unsignedTx = await app.buildClaimAllRewardsTx(caip, claimAllPayload)
|
728
|
+
log.info(tag, `โ
Claim all rewards transaction built successfully`)
|
729
|
+
log.info(tag, `๐ Transaction structure:`, JSON.stringify(unsignedTx, null, 2))
|
730
|
+
|
731
|
+
// Validate transaction structure
|
732
|
+
assert(unsignedTx.signDoc, 'Transaction must have signDoc')
|
733
|
+
assert(unsignedTx.signDoc.msgs, 'Transaction must have messages')
|
734
|
+
assert(unsignedTx.signDoc.msgs.length === validatorAddresses.length, `Must have ${validatorAddresses.length} messages`)
|
735
|
+
|
736
|
+
// Validate each message
|
737
|
+
unsignedTx.signDoc.msgs.forEach((msg: any, index: number) => {
|
738
|
+
assert(msg.type === 'cosmos-sdk/MsgWithdrawDelegationReward', `Message ${index} must be MsgWithdrawDelegationReward`)
|
739
|
+
assert(msg.value.delegator_address, `Message ${index} must have delegator_address`)
|
740
|
+
assert(msg.value.validator_address, `Message ${index} must have validator_address`)
|
741
|
+
})
|
742
|
+
|
743
|
+
log.info(tag, `โ
Claim all rewards transaction structure validated`)
|
744
|
+
|
745
|
+
if (TEST_CONFIG.ACTUALLY_EXECUTE_TRANSACTIONS) {
|
746
|
+
log.info(tag, `๐ EXECUTING CLAIM ALL REWARDS TRANSACTION...`)
|
747
|
+
log.info(tag, `๐ฐ Claiming rewards from ${validatorAddresses.length} validators`)
|
748
|
+
|
749
|
+
// Sign the transaction
|
750
|
+
log.info(tag, `โ๏ธ Signing transaction...`)
|
751
|
+
const signedTx = await app.signTx({ caip, unsignedTx })
|
752
|
+
log.info(tag, `โ
Transaction signed successfully`)
|
753
|
+
|
754
|
+
// Broadcast the transaction
|
755
|
+
log.info(tag, `๐ก Broadcasting transaction...`)
|
756
|
+
const broadcast = await app.broadcastTx(caip, signedTx)
|
757
|
+
log.info(tag, `โ
Transaction broadcasted:`, broadcast)
|
758
|
+
|
759
|
+
// Follow the transaction
|
760
|
+
log.info(tag, `๐ Following transaction...`)
|
761
|
+
const followResult = await app.followTransaction(caip, broadcast)
|
762
|
+
log.info(tag, `โ
Transaction completed:`, followResult)
|
763
|
+
|
764
|
+
} else {
|
765
|
+
log.info(tag, `โน๏ธ Skipping execution - set ACTUALLY_EXECUTE_TRANSACTIONS to true to execute`)
|
766
|
+
}
|
767
|
+
|
768
|
+
} catch (buildError) {
|
769
|
+
log.error(tag, `โ Error building claim all rewards transaction:`, buildError)
|
770
|
+
}
|
771
|
+
}
|
772
|
+
}
|
773
|
+
}
|
774
|
+
|
775
|
+
// **TEST 7: Staking Integration Validation**
|
776
|
+
if (TEST_CONFIG.TEST_STAKING_INTEGRATION) {
|
777
|
+
log.info(tag, "")
|
778
|
+
log.info(tag, "=".repeat(60))
|
779
|
+
log.info(tag, "๐งช TEST 7: Staking Integration Validation")
|
780
|
+
log.info(tag, "=".repeat(60))
|
781
|
+
} else {
|
782
|
+
log.info(tag, "โญ๏ธ Skipping TEST 7: Staking Integration Validation")
|
783
|
+
}
|
784
|
+
|
785
|
+
if (TEST_CONFIG.TEST_STAKING_INTEGRATION) {
|
786
|
+
// Validate that staking positions are properly integrated into the app
|
787
|
+
const allStakingInBalances = app.balances.filter((balance: any) =>
|
788
|
+
balance.chart === 'staking' || ['delegation', 'reward', 'unbonding'].includes(balance.type)
|
789
|
+
)
|
790
|
+
|
791
|
+
log.info(tag, `Total staking positions in app.balances: ${allStakingInBalances.length}`)
|
792
|
+
|
793
|
+
if (allStakingInBalances.length > 0) {
|
794
|
+
const delegations = allStakingInBalances.filter((p: any) => p.type === 'delegation')
|
795
|
+
const rewards = allStakingInBalances.filter((p: any) => p.type === 'reward')
|
796
|
+
const unbonding = allStakingInBalances.filter((p: any) => p.type === 'unbonding')
|
797
|
+
|
798
|
+
log.info(tag, `๐ Breakdown:`)
|
799
|
+
log.info(tag, ` - Delegations: ${delegations.length}`)
|
800
|
+
log.info(tag, ` - Rewards: ${rewards.length}`)
|
801
|
+
log.info(tag, ` - Unbonding: ${unbonding.length}`)
|
802
|
+
|
803
|
+
// Validate pricing integration
|
804
|
+
const stakingWithPricing = allStakingInBalances.filter((p: any) =>
|
805
|
+
p.priceUsd && p.priceUsd > 0 && p.valueUsd && p.valueUsd > 0
|
806
|
+
)
|
807
|
+
|
808
|
+
log.info(tag, `๐ฐ Positions with pricing: ${stakingWithPricing.length}/${allStakingInBalances.length}`)
|
809
|
+
|
810
|
+
// Calculate total staking value
|
811
|
+
const totalStakingValue = allStakingInBalances.reduce((sum: number, position: any) =>
|
812
|
+
sum + (parseFloat(position.valueUsd) || 0), 0
|
813
|
+
)
|
814
|
+
|
815
|
+
log.info(tag, `๐ต Total staking value: $${totalStakingValue.toFixed(2)}`)
|
816
|
+
}
|
817
|
+
}
|
818
|
+
|
819
|
+
// **TEST RESULTS VALIDATION**
|
820
|
+
log.info(tag, "")
|
821
|
+
log.info(tag, "=".repeat(60))
|
822
|
+
log.info(tag, "๐ STAKING TEST SUITE RESULTS")
|
823
|
+
log.info(tag, "=".repeat(60))
|
824
|
+
|
825
|
+
console.timeEnd('staking-test-start')
|
826
|
+
|
827
|
+
// Check critical requirements for test success
|
828
|
+
let testsPassed = 0
|
829
|
+
let testsTotal = 0
|
830
|
+
let criticalFailures = []
|
831
|
+
|
832
|
+
// CRITICAL: KeepKey device connection
|
833
|
+
testsTotal++
|
834
|
+
if (app.pubkeys.length > 0) {
|
835
|
+
log.info(tag, `โ
KeepKey device connected: ${app.pubkeys.length} pubkeys loaded`)
|
836
|
+
testsPassed++
|
837
|
+
} else {
|
838
|
+
log.error(tag, `โ CRITICAL FAILURE: KeepKey device not connected (0 pubkeys)`)
|
839
|
+
criticalFailures.push("KeepKey device not connected - no pubkeys loaded")
|
840
|
+
}
|
841
|
+
|
842
|
+
// CRITICAL: Staking positions found
|
843
|
+
testsTotal++
|
844
|
+
if (existingStakingPositions.length > 0) {
|
845
|
+
log.info(tag, `โ
Staking positions found: ${existingStakingPositions.length}`)
|
846
|
+
testsPassed++
|
847
|
+
} else {
|
848
|
+
log.error(tag, `โ CRITICAL FAILURE: No staking positions found`)
|
849
|
+
criticalFailures.push("No staking positions found - nothing to claim rewards from")
|
850
|
+
}
|
851
|
+
|
852
|
+
// CRITICAL: Transaction execution (if enabled)
|
853
|
+
if (TEST_CONFIG.ACTUALLY_EXECUTE_TRANSACTIONS) {
|
854
|
+
testsTotal++
|
855
|
+
// This will be updated by the transaction execution logic
|
856
|
+
log.info(tag, `โณ Transaction execution: Checking if any transactions were executed...`)
|
857
|
+
|
858
|
+
// For now, mark as failed since we know no transactions were executed
|
859
|
+
log.error(tag, `โ CRITICAL FAILURE: No transactions were executed`)
|
860
|
+
criticalFailures.push("No transactions were executed - test set to execute but nothing happened")
|
861
|
+
}
|
862
|
+
|
863
|
+
// Final test result
|
864
|
+
log.info(tag, "")
|
865
|
+
log.info(tag, "=".repeat(60))
|
866
|
+
if (criticalFailures.length > 0) {
|
867
|
+
log.error(tag, "โ STAKING TEST SUITE FAILED!")
|
868
|
+
log.error(tag, "")
|
869
|
+
log.error(tag, "๐ฅ CRITICAL FAILURES:")
|
870
|
+
criticalFailures.forEach((failure, index) => {
|
871
|
+
log.error(tag, ` ${index + 1}. ${failure}`)
|
872
|
+
})
|
873
|
+
log.error(tag, "")
|
874
|
+
log.error(tag, "๐ง TO FIX:")
|
875
|
+
log.error(tag, " 1. Connect and unlock your KeepKey device")
|
876
|
+
log.error(tag, " 2. Ensure you have existing staking positions with rewards")
|
877
|
+
log.error(tag, " 3. Make sure your device is properly paired")
|
878
|
+
log.error(tag, "")
|
879
|
+
|
880
|
+
// Exit with error code
|
881
|
+
process.exit(1)
|
882
|
+
} else {
|
883
|
+
log.info(tag, "๐ STAKING TEST SUITE COMPLETED SUCCESSFULLY!")
|
884
|
+
log.info(tag, `๐ Tests passed: ${testsPassed}/${testsTotal}`)
|
885
|
+
}
|
886
|
+
log.info(tag, "")
|
887
|
+
|
888
|
+
// All staking functionality is now implemented and ready to use:
|
889
|
+
log.info(tag, "โ
Implementation Status:")
|
890
|
+
log.info(tag, " โ
buildDelegateTx() method - IMPLEMENTED")
|
891
|
+
log.info(tag, " โ
buildUndelegateTx() method - IMPLEMENTED")
|
892
|
+
log.info(tag, " โ
buildClaimRewardsTx() method - IMPLEMENTED")
|
893
|
+
log.info(tag, " โ
buildClaimAllRewardsTx() method - IMPLEMENTED")
|
894
|
+
log.info(tag, " โ
Staking transaction templates - IMPLEMENTED")
|
895
|
+
log.info(tag, " โ
KeepKey SDK staking signing methods - IMPLEMENTED")
|
896
|
+
log.info(tag, "")
|
897
|
+
log.info(tag, "๐ก To test staking functionality:")
|
898
|
+
log.info(tag, " 1. Make sure your KeepKey device is connected and unlocked")
|
899
|
+
log.info(tag, " 2. Ensure you have existing staking positions with rewards")
|
900
|
+
log.info(tag, " 3. Run this test - it will ask you to sign transactions if positions are found")
|
901
|
+
|
902
|
+
} catch (e) {
|
903
|
+
log.error(tag, "โ STAKING TEST FAILED:", e)
|
904
|
+
process.exit(1)
|
905
|
+
}
|
906
|
+
}
|
907
|
+
|
908
|
+
// Run the staking test suite
|
909
|
+
test_staking_service()
|