pandora-cli-skills 1.1.53 → 1.1.54
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/SKILL.md
CHANGED
|
@@ -153,6 +153,32 @@ const POLL_STATUS_READ_CANDIDATES = [
|
|
|
153
153
|
];
|
|
154
154
|
|
|
155
155
|
const POLL_FINALIZED_READ_CANDIDATES = [
|
|
156
|
+
{
|
|
157
|
+
fn: 'getFinalizedStatus',
|
|
158
|
+
kind: 'status-answer-epoch',
|
|
159
|
+
abi: [
|
|
160
|
+
{
|
|
161
|
+
type: 'function',
|
|
162
|
+
name: 'getFinalizedStatus',
|
|
163
|
+
stateMutability: 'view',
|
|
164
|
+
inputs: [],
|
|
165
|
+
outputs: [{ type: 'uint8' }, { type: 'uint8' }, { type: 'uint32' }],
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
fn: 'getFinalizedStatus',
|
|
171
|
+
kind: 'bool-answer-epoch',
|
|
172
|
+
abi: [
|
|
173
|
+
{
|
|
174
|
+
type: 'function',
|
|
175
|
+
name: 'getFinalizedStatus',
|
|
176
|
+
stateMutability: 'view',
|
|
177
|
+
inputs: [],
|
|
178
|
+
outputs: [{ type: 'bool' }, { type: 'uint8' }, { type: 'uint32' }],
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
},
|
|
156
182
|
{ fn: 'getFinalizedStatus', abi: [{ type: 'function', name: 'getFinalizedStatus', stateMutability: 'view', inputs: [], outputs: [{ type: 'bool' }] }] },
|
|
157
183
|
{ fn: 'isFinalized', abi: [{ type: 'function', name: 'isFinalized', stateMutability: 'view', inputs: [], outputs: [{ type: 'bool' }] }] },
|
|
158
184
|
{ fn: 'finalized', abi: [{ type: 'function', name: 'finalized', stateMutability: 'view', inputs: [], outputs: [{ type: 'bool' }] }] },
|
|
@@ -179,6 +205,28 @@ const POLL_FINALIZATION_EPOCH_READ_CANDIDATES = [
|
|
|
179
205
|
},
|
|
180
206
|
];
|
|
181
207
|
|
|
208
|
+
const POLL_CURRENT_EPOCH_READ_CANDIDATES = [
|
|
209
|
+
{
|
|
210
|
+
fn: 'getCurrentEpoch',
|
|
211
|
+
abi: [{ type: 'function', name: 'getCurrentEpoch', stateMutability: 'view', inputs: [], outputs: [{ type: 'uint256' }] }],
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
fn: 'currentEpoch',
|
|
215
|
+
abi: [{ type: 'function', name: 'currentEpoch', stateMutability: 'view', inputs: [], outputs: [{ type: 'uint256' }] }],
|
|
216
|
+
},
|
|
217
|
+
];
|
|
218
|
+
|
|
219
|
+
const POLL_EPOCH_LENGTH_READ_CANDIDATES = [
|
|
220
|
+
{
|
|
221
|
+
fn: 'getEpochLength',
|
|
222
|
+
abi: [{ type: 'function', name: 'getEpochLength', stateMutability: 'view', inputs: [], outputs: [{ type: 'uint256' }] }],
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
fn: 'epochLength',
|
|
226
|
+
abi: [{ type: 'function', name: 'epochLength', stateMutability: 'view', inputs: [], outputs: [{ type: 'uint256' }] }],
|
|
227
|
+
},
|
|
228
|
+
];
|
|
229
|
+
|
|
182
230
|
const POLL_OPERATOR_READ_CANDIDATES = [
|
|
183
231
|
{ fn: 'arbiter', abi: [{ type: 'function', name: 'arbiter', stateMutability: 'view', inputs: [], outputs: [{ type: 'address' }] }] },
|
|
184
232
|
{ fn: 'operator', abi: [{ type: 'function', name: 'operator', stateMutability: 'view', inputs: [], outputs: [{ type: 'address' }] }] },
|
|
@@ -380,12 +428,12 @@ async function tryReadContractAny(publicClient, address, candidates = []) {
|
|
|
380
428
|
functionName: candidate.fn,
|
|
381
429
|
args: [],
|
|
382
430
|
});
|
|
383
|
-
return { ok: true, fn: candidate.fn, value };
|
|
431
|
+
return { ok: true, fn: candidate.fn, value, candidate };
|
|
384
432
|
} catch {
|
|
385
433
|
// continue
|
|
386
434
|
}
|
|
387
435
|
}
|
|
388
|
-
return { ok: false, fn: null, value: null };
|
|
436
|
+
return { ok: false, fn: null, value: null, candidate: null };
|
|
389
437
|
}
|
|
390
438
|
|
|
391
439
|
function normalizeOptionalBigInt(value) {
|
|
@@ -415,6 +463,14 @@ function normalizePollAnswer(value) {
|
|
|
415
463
|
return String(asBig);
|
|
416
464
|
}
|
|
417
465
|
|
|
466
|
+
function deriveCurrentEpochFromTimestamp(timestamp, epochLengthSeconds = 300n) {
|
|
467
|
+
const ts = normalizeOptionalBigInt(timestamp);
|
|
468
|
+
if (ts === null) return null;
|
|
469
|
+
const epochLength = normalizeOptionalBigInt(epochLengthSeconds);
|
|
470
|
+
if (epochLength === null || epochLength <= 0n) return null;
|
|
471
|
+
return ts / epochLength;
|
|
472
|
+
}
|
|
473
|
+
|
|
418
474
|
async function readPollResolutionState(publicClient, pollAddress) {
|
|
419
475
|
const statusRead = await tryReadContractAny(publicClient, pollAddress, POLL_STATUS_READ_CANDIDATES);
|
|
420
476
|
const finalizedRead = await tryReadContractAny(publicClient, pollAddress, POLL_FINALIZED_READ_CANDIDATES);
|
|
@@ -425,14 +481,50 @@ async function readPollResolutionState(publicClient, pollAddress) {
|
|
|
425
481
|
POLL_FINALIZATION_EPOCH_READ_CANDIDATES,
|
|
426
482
|
);
|
|
427
483
|
const operatorRead = await tryReadContractAny(publicClient, pollAddress, POLL_OPERATOR_READ_CANDIDATES);
|
|
428
|
-
const
|
|
484
|
+
const currentEpochRead = await tryReadContractAny(publicClient, pollAddress, POLL_CURRENT_EPOCH_READ_CANDIDATES);
|
|
485
|
+
const epochLengthRead = await tryReadContractAny(publicClient, pollAddress, POLL_EPOCH_LENGTH_READ_CANDIDATES);
|
|
486
|
+
|
|
487
|
+
let currentEpoch = currentEpochRead.ok ? normalizeOptionalBigInt(currentEpochRead.value) : null;
|
|
488
|
+
if (currentEpoch === null) {
|
|
489
|
+
try {
|
|
490
|
+
const block = await publicClient.getBlock({ blockTag: 'latest' });
|
|
491
|
+
const epochLength = epochLengthRead.ok ? normalizeOptionalBigInt(epochLengthRead.value) : 300n;
|
|
492
|
+
currentEpoch = deriveCurrentEpochFromTimestamp(block && block.timestamp, epochLength === null ? 300n : epochLength);
|
|
493
|
+
} catch {
|
|
494
|
+
currentEpoch = null;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
429
497
|
|
|
430
498
|
const statusNumeric = statusRead.ok ? Number(statusRead.value) : null;
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
499
|
+
let finalizedBool = null;
|
|
500
|
+
let finalizedAnswer = null;
|
|
501
|
+
let finalizedEpoch = null;
|
|
502
|
+
if (finalizedRead.ok) {
|
|
503
|
+
const kind = finalizedRead.candidate && finalizedRead.candidate.kind ? finalizedRead.candidate.kind : null;
|
|
504
|
+
if (kind === 'status-answer-epoch' || kind === 'bool-answer-epoch') {
|
|
505
|
+
const tuple = Array.isArray(finalizedRead.value) ? finalizedRead.value : null;
|
|
506
|
+
if (tuple && tuple.length >= 3) {
|
|
507
|
+
if (kind === 'status-answer-epoch') {
|
|
508
|
+
const statusRaw = normalizeOptionalBigInt(tuple[0]);
|
|
509
|
+
finalizedBool = statusRaw === null ? null : statusRaw >= 2n;
|
|
510
|
+
} else {
|
|
511
|
+
finalizedBool = Boolean(tuple[0]);
|
|
512
|
+
}
|
|
513
|
+
finalizedAnswer = normalizePollAnswer(tuple[1]);
|
|
514
|
+
finalizedEpoch = normalizeOptionalBigInt(tuple[2]);
|
|
515
|
+
}
|
|
516
|
+
} else {
|
|
517
|
+
finalizedBool = Boolean(finalizedRead.value);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
const answer = answerRead.ok ? normalizePollAnswer(answerRead.value) : finalizedAnswer;
|
|
521
|
+
const finalizationEpoch = finalizationEpochRead.ok ? normalizeOptionalBigInt(finalizationEpochRead.value) : finalizedEpoch;
|
|
434
522
|
const epochsUntilFinalization =
|
|
435
|
-
finalizationEpoch === null
|
|
523
|
+
(finalizationEpoch === null || currentEpoch === null)
|
|
524
|
+
? null
|
|
525
|
+
: finalizationEpoch > currentEpoch
|
|
526
|
+
? Number(finalizationEpoch - currentEpoch)
|
|
527
|
+
: 0;
|
|
436
528
|
const claimable =
|
|
437
529
|
answer !== null &&
|
|
438
530
|
((finalizedBool === true) || (epochsUntilFinalization !== null && epochsUntilFinalization <= 0));
|
|
@@ -443,7 +535,7 @@ async function readPollResolutionState(publicClient, pollAddress) {
|
|
|
443
535
|
pollFinalized: finalizedBool,
|
|
444
536
|
pollAnswer: answer,
|
|
445
537
|
finalizationEpoch: finalizationEpoch === null ? null : finalizationEpoch.toString(),
|
|
446
|
-
currentEpoch: currentEpoch.toString(),
|
|
538
|
+
currentEpoch: currentEpoch === null ? null : currentEpoch.toString(),
|
|
447
539
|
epochsUntilFinalization,
|
|
448
540
|
claimable,
|
|
449
541
|
operator: operatorRead.ok ? normalizeOptionalAddress(operatorRead.value) : null,
|
|
@@ -452,6 +544,8 @@ async function readPollResolutionState(publicClient, pollAddress) {
|
|
|
452
544
|
finalized: finalizedRead.fn,
|
|
453
545
|
answer: answerRead.fn,
|
|
454
546
|
finalizationEpoch: finalizationEpochRead.fn,
|
|
547
|
+
currentEpoch: currentEpochRead.fn,
|
|
548
|
+
epochLength: epochLengthRead.fn,
|
|
455
549
|
operator: operatorRead.fn,
|
|
456
550
|
},
|
|
457
551
|
};
|
package/package.json
CHANGED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const test = require('node:test');
|
|
2
|
+
const assert = require('node:assert/strict');
|
|
3
|
+
|
|
4
|
+
const { readPollResolutionState } = require('../../cli/lib/market_admin_service.cjs');
|
|
5
|
+
|
|
6
|
+
const POLL_ADDRESS = '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
|
|
7
|
+
const OPERATOR = '0x0D7B957C47Da86c2968dc52111D633D42cb7a5F7';
|
|
8
|
+
|
|
9
|
+
test('readPollResolutionState extracts answer/finalization epoch from getFinalizedStatus tuple', async () => {
|
|
10
|
+
const publicClient = {
|
|
11
|
+
async readContract({ functionName }) {
|
|
12
|
+
if (functionName === 'getStatus') return 2n;
|
|
13
|
+
if (functionName === 'getFinalizedStatus') return [2n, 1n, 5908608n];
|
|
14
|
+
if (functionName === 'arbiter') return OPERATOR;
|
|
15
|
+
if (functionName === 'getCurrentEpoch' || functionName === 'getFinalizationEpoch' || functionName === 'answer') {
|
|
16
|
+
throw new Error(`missing function: ${functionName}`);
|
|
17
|
+
}
|
|
18
|
+
if (functionName === 'getEpochLength') return 300n;
|
|
19
|
+
throw new Error(`unexpected function read: ${functionName}`);
|
|
20
|
+
},
|
|
21
|
+
async getBlock() {
|
|
22
|
+
return { timestamp: 1772550000n }; // 5908500 * 300
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const state = await readPollResolutionState(publicClient, POLL_ADDRESS);
|
|
27
|
+
|
|
28
|
+
assert.equal(state.pollAddress, POLL_ADDRESS);
|
|
29
|
+
assert.equal(state.marketState, 2);
|
|
30
|
+
assert.equal(state.pollFinalized, true);
|
|
31
|
+
assert.equal(state.pollAnswer, 'yes');
|
|
32
|
+
assert.equal(state.finalizationEpoch, '5908608');
|
|
33
|
+
assert.equal(state.currentEpoch, '5908500');
|
|
34
|
+
assert.equal(state.epochsUntilFinalization, 108);
|
|
35
|
+
assert.equal(state.claimable, true);
|
|
36
|
+
assert.equal(state.operator, OPERATOR.toLowerCase());
|
|
37
|
+
assert.equal(state.readSources.finalized, 'getFinalizedStatus');
|
|
38
|
+
assert.equal(state.readSources.answer, null);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('readPollResolutionState uses dedicated answer/currentEpoch readers when available', async () => {
|
|
42
|
+
const publicClient = {
|
|
43
|
+
async readContract({ functionName }) {
|
|
44
|
+
if (functionName === 'getStatus') return 1n;
|
|
45
|
+
if (functionName === 'getFinalizedStatus') throw new Error('tuple reader not supported');
|
|
46
|
+
if (functionName === 'isFinalized') return false;
|
|
47
|
+
if (functionName === 'answer') return 0n;
|
|
48
|
+
if (functionName === 'getFinalizationEpoch') return 5908610n;
|
|
49
|
+
if (functionName === 'getCurrentEpoch') return 5908600n;
|
|
50
|
+
if (functionName === 'arbiter') return OPERATOR;
|
|
51
|
+
if (functionName === 'getEpochLength') throw new Error('epoch length not needed');
|
|
52
|
+
throw new Error(`unexpected function read: ${functionName}`);
|
|
53
|
+
},
|
|
54
|
+
async getBlock() {
|
|
55
|
+
throw new Error('getBlock should not be called when getCurrentEpoch is available');
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const state = await readPollResolutionState(publicClient, POLL_ADDRESS);
|
|
60
|
+
|
|
61
|
+
assert.equal(state.pollFinalized, false);
|
|
62
|
+
assert.equal(state.pollAnswer, 'no');
|
|
63
|
+
assert.equal(state.finalizationEpoch, '5908610');
|
|
64
|
+
assert.equal(state.currentEpoch, '5908600');
|
|
65
|
+
assert.equal(state.epochsUntilFinalization, 10);
|
|
66
|
+
assert.equal(state.claimable, false);
|
|
67
|
+
assert.equal(state.readSources.finalized, 'isFinalized');
|
|
68
|
+
assert.equal(state.readSources.answer, 'answer');
|
|
69
|
+
assert.equal(state.readSources.currentEpoch, 'getCurrentEpoch');
|
|
70
|
+
});
|
|
71
|
+
|