naracli 1.0.46 → 1.0.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/nara-cli-bundle.cjs
CHANGED
|
@@ -32114,7 +32114,7 @@ Message: ${transactionMessage}.
|
|
|
32114
32114
|
* @internal
|
|
32115
32115
|
*/
|
|
32116
32116
|
static checkProgramId(programId) {
|
|
32117
|
-
if (!programId.equals(
|
|
32117
|
+
if (!programId.equals(ComputeBudgetProgram2.programId)) {
|
|
32118
32118
|
throw new Error("invalid instruction; programId is not ComputeBudgetProgram");
|
|
32119
32119
|
}
|
|
32120
32120
|
}
|
|
@@ -32137,7 +32137,7 @@ Message: ${transactionMessage}.
|
|
|
32137
32137
|
layout: BufferLayout__namespace.struct([BufferLayout__namespace.u8("instruction"), u642("microLamports")])
|
|
32138
32138
|
}
|
|
32139
32139
|
});
|
|
32140
|
-
var
|
|
32140
|
+
var ComputeBudgetProgram2 = class {
|
|
32141
32141
|
/**
|
|
32142
32142
|
* @internal
|
|
32143
32143
|
*/
|
|
@@ -32188,7 +32188,7 @@ Message: ${transactionMessage}.
|
|
|
32188
32188
|
});
|
|
32189
32189
|
}
|
|
32190
32190
|
};
|
|
32191
|
-
|
|
32191
|
+
ComputeBudgetProgram2.programId = new PublicKey30("ComputeBudget111111111111111111111111111111");
|
|
32192
32192
|
var PRIVATE_KEY_BYTES$1 = 64;
|
|
32193
32193
|
var PUBLIC_KEY_BYTES$1 = 32;
|
|
32194
32194
|
var SIGNATURE_BYTES = 64;
|
|
@@ -33688,7 +33688,7 @@ Message: ${transactionMessage}.
|
|
|
33688
33688
|
exports2.BpfLoader = BpfLoader;
|
|
33689
33689
|
exports2.COMPUTE_BUDGET_INSTRUCTION_LAYOUTS = COMPUTE_BUDGET_INSTRUCTION_LAYOUTS;
|
|
33690
33690
|
exports2.ComputeBudgetInstruction = ComputeBudgetInstruction;
|
|
33691
|
-
exports2.ComputeBudgetProgram =
|
|
33691
|
+
exports2.ComputeBudgetProgram = ComputeBudgetProgram2;
|
|
33692
33692
|
exports2.Connection = Connection13;
|
|
33693
33693
|
exports2.Ed25519Program = Ed25519Program;
|
|
33694
33694
|
exports2.Enum = Enum;
|
|
@@ -60397,7 +60397,35 @@ async function loadAlt(connection) {
|
|
|
60397
60397
|
_cachedAltAddress = addr;
|
|
60398
60398
|
return _cachedAlt;
|
|
60399
60399
|
}
|
|
60400
|
+
async function getRecentPriorityFee(connection) {
|
|
60401
|
+
const fees = await connection.getRecentPrioritizationFees();
|
|
60402
|
+
if (!fees.length) return 0;
|
|
60403
|
+
const nonZero = fees.filter((f) => f.prioritizationFee > 0);
|
|
60404
|
+
if (!nonZero.length) return 0;
|
|
60405
|
+
const avg = nonZero.reduce((s, f) => s + f.prioritizationFee, 0) / nonZero.length;
|
|
60406
|
+
return Math.ceil(avg);
|
|
60407
|
+
}
|
|
60400
60408
|
async function sendTx(connection, payer, instructions, signers, opts) {
|
|
60409
|
+
const budgetIxs = [];
|
|
60410
|
+
if (opts?.computeUnitLimit) {
|
|
60411
|
+
budgetIxs.push(
|
|
60412
|
+
import_web388.ComputeBudgetProgram.setComputeUnitLimit({ units: opts.computeUnitLimit })
|
|
60413
|
+
);
|
|
60414
|
+
}
|
|
60415
|
+
if (opts?.computeUnitPrice !== void 0) {
|
|
60416
|
+
let price;
|
|
60417
|
+
if (opts.computeUnitPrice === "auto") {
|
|
60418
|
+
price = await getRecentPriorityFee(connection);
|
|
60419
|
+
} else {
|
|
60420
|
+
price = opts.computeUnitPrice;
|
|
60421
|
+
}
|
|
60422
|
+
if (price > 0) {
|
|
60423
|
+
budgetIxs.push(
|
|
60424
|
+
import_web388.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: price })
|
|
60425
|
+
);
|
|
60426
|
+
}
|
|
60427
|
+
}
|
|
60428
|
+
const allInstructions = [...budgetIxs, ...instructions];
|
|
60401
60429
|
const alt = await loadAlt(connection);
|
|
60402
60430
|
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash("confirmed");
|
|
60403
60431
|
let signature;
|
|
@@ -60405,7 +60433,7 @@ async function sendTx(connection, payer, instructions, signers, opts) {
|
|
|
60405
60433
|
const message = new import_web388.TransactionMessage({
|
|
60406
60434
|
payerKey: payer.publicKey,
|
|
60407
60435
|
recentBlockhash: blockhash,
|
|
60408
|
-
instructions
|
|
60436
|
+
instructions: allInstructions
|
|
60409
60437
|
}).compileToV0Message([alt]);
|
|
60410
60438
|
const tx = new import_web388.VersionedTransaction(message);
|
|
60411
60439
|
const allSigners = [payer, ...signers ?? []];
|
|
@@ -60425,7 +60453,7 @@ async function sendTx(connection, payer, instructions, signers, opts) {
|
|
|
60425
60453
|
tx.recentBlockhash = blockhash;
|
|
60426
60454
|
tx.lastValidBlockHeight = lastValidBlockHeight;
|
|
60427
60455
|
tx.feePayer = payer.publicKey;
|
|
60428
|
-
for (const ix of
|
|
60456
|
+
for (const ix of allInstructions) tx.add(ix);
|
|
60429
60457
|
const allSigners = [payer, ...signers ?? []];
|
|
60430
60458
|
const seen = /* @__PURE__ */ new Set();
|
|
60431
60459
|
const uniqueSigners = allSigners.filter((s) => {
|
|
@@ -60439,16 +60467,31 @@ async function sendTx(connection, payer, instructions, signers, opts) {
|
|
|
60439
60467
|
skipPreflight: opts?.skipPreflight ?? false
|
|
60440
60468
|
});
|
|
60441
60469
|
}
|
|
60442
|
-
const
|
|
60443
|
-
|
|
60444
|
-
|
|
60445
|
-
)
|
|
60446
|
-
|
|
60447
|
-
|
|
60448
|
-
|
|
60449
|
-
|
|
60470
|
+
const startTime = Date.now();
|
|
60471
|
+
const TIMEOUT_MS = 2e4;
|
|
60472
|
+
const POLL_INTERVAL_MS = 1e3;
|
|
60473
|
+
while (Date.now() - startTime < TIMEOUT_MS) {
|
|
60474
|
+
const currentBlockHeight = await connection.getBlockHeight("confirmed");
|
|
60475
|
+
if (currentBlockHeight > lastValidBlockHeight) {
|
|
60476
|
+
throw new Error(
|
|
60477
|
+
`Transaction ${signature} expired: block height exceeded`
|
|
60478
|
+
);
|
|
60479
|
+
}
|
|
60480
|
+
const statusResult = await connection.getSignatureStatuses([signature]);
|
|
60481
|
+
const status = statusResult?.value?.[0];
|
|
60482
|
+
if (status) {
|
|
60483
|
+
if (status.err) {
|
|
60484
|
+
throw new Error(
|
|
60485
|
+
`Transaction ${signature} failed: ${JSON.stringify(status.err)}`
|
|
60486
|
+
);
|
|
60487
|
+
}
|
|
60488
|
+
if (status.confirmationStatus === "confirmed" || status.confirmationStatus === "finalized") {
|
|
60489
|
+
return signature;
|
|
60490
|
+
}
|
|
60491
|
+
}
|
|
60492
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
60450
60493
|
}
|
|
60451
|
-
|
|
60494
|
+
throw new Error(`Transaction ${signature} confirmation timeout`);
|
|
60452
60495
|
}
|
|
60453
60496
|
var import_web388, _cachedAlt, _cachedAltAddress, _overrideAltAddress;
|
|
60454
60497
|
var init_tx = __esm({
|
|
@@ -124721,16 +124764,16 @@ var nara_quest_default = {
|
|
|
124721
124764
|
args: []
|
|
124722
124765
|
},
|
|
124723
124766
|
{
|
|
124724
|
-
name: "
|
|
124767
|
+
name: "set_reward_config",
|
|
124725
124768
|
discriminator: [
|
|
124726
|
-
|
|
124727
|
-
|
|
124728
|
-
|
|
124729
|
-
|
|
124730
|
-
|
|
124731
|
-
|
|
124732
|
-
|
|
124733
|
-
|
|
124769
|
+
163,
|
|
124770
|
+
34,
|
|
124771
|
+
211,
|
|
124772
|
+
14,
|
|
124773
|
+
25,
|
|
124774
|
+
118,
|
|
124775
|
+
181,
|
|
124776
|
+
233
|
|
124734
124777
|
],
|
|
124735
124778
|
accounts: [
|
|
124736
124779
|
{
|
|
@@ -124764,6 +124807,10 @@ var nara_quest_default = {
|
|
|
124764
124807
|
}
|
|
124765
124808
|
],
|
|
124766
124809
|
args: [
|
|
124810
|
+
{
|
|
124811
|
+
name: "min_reward_count",
|
|
124812
|
+
type: "u32"
|
|
124813
|
+
},
|
|
124767
124814
|
{
|
|
124768
124815
|
name: "max_reward_count",
|
|
124769
124816
|
type: "u32"
|
|
@@ -124771,16 +124818,16 @@ var nara_quest_default = {
|
|
|
124771
124818
|
]
|
|
124772
124819
|
},
|
|
124773
124820
|
{
|
|
124774
|
-
name: "
|
|
124821
|
+
name: "set_stake_config",
|
|
124775
124822
|
discriminator: [
|
|
124776
|
-
|
|
124777
|
-
|
|
124778
|
-
|
|
124779
|
-
|
|
124780
|
-
|
|
124781
|
-
|
|
124782
|
-
|
|
124783
|
-
|
|
124823
|
+
84,
|
|
124824
|
+
37,
|
|
124825
|
+
76,
|
|
124826
|
+
39,
|
|
124827
|
+
236,
|
|
124828
|
+
111,
|
|
124829
|
+
214,
|
|
124830
|
+
191
|
|
124784
124831
|
],
|
|
124785
124832
|
accounts: [
|
|
124786
124833
|
{
|
|
@@ -124815,8 +124862,16 @@ var nara_quest_default = {
|
|
|
124815
124862
|
],
|
|
124816
124863
|
args: [
|
|
124817
124864
|
{
|
|
124818
|
-
name: "
|
|
124819
|
-
type: "
|
|
124865
|
+
name: "bps_high",
|
|
124866
|
+
type: "u64"
|
|
124867
|
+
},
|
|
124868
|
+
{
|
|
124869
|
+
name: "bps_low",
|
|
124870
|
+
type: "u64"
|
|
124871
|
+
},
|
|
124872
|
+
{
|
|
124873
|
+
name: "decay_ms",
|
|
124874
|
+
type: "i64"
|
|
124820
124875
|
}
|
|
124821
124876
|
]
|
|
124822
124877
|
},
|
|
@@ -125015,6 +125070,30 @@ var nara_quest_default = {
|
|
|
125015
125070
|
48
|
|
125016
125071
|
],
|
|
125017
125072
|
accounts: [
|
|
125073
|
+
{
|
|
125074
|
+
name: "game_config",
|
|
125075
|
+
pda: {
|
|
125076
|
+
seeds: [
|
|
125077
|
+
{
|
|
125078
|
+
kind: "const",
|
|
125079
|
+
value: [
|
|
125080
|
+
113,
|
|
125081
|
+
117,
|
|
125082
|
+
101,
|
|
125083
|
+
115,
|
|
125084
|
+
116,
|
|
125085
|
+
95,
|
|
125086
|
+
99,
|
|
125087
|
+
111,
|
|
125088
|
+
110,
|
|
125089
|
+
102,
|
|
125090
|
+
105,
|
|
125091
|
+
103
|
|
125092
|
+
]
|
|
125093
|
+
}
|
|
125094
|
+
]
|
|
125095
|
+
}
|
|
125096
|
+
},
|
|
125018
125097
|
{
|
|
125019
125098
|
name: "pool",
|
|
125020
125099
|
writable: true,
|
|
@@ -125709,12 +125788,12 @@ var nara_quest_default = {
|
|
|
125709
125788
|
{
|
|
125710
125789
|
code: 6008,
|
|
125711
125790
|
name: "InvalidMinRewardCount",
|
|
125712
|
-
msg: "
|
|
125791
|
+
msg: "Invalid reward config: need 0 < min <= max"
|
|
125713
125792
|
},
|
|
125714
125793
|
{
|
|
125715
125794
|
code: 6009,
|
|
125716
|
-
name: "
|
|
125717
|
-
msg: "
|
|
125795
|
+
name: "InvalidStakeConfig",
|
|
125796
|
+
msg: "Stake config values must be > 0"
|
|
125718
125797
|
},
|
|
125719
125798
|
{
|
|
125720
125799
|
code: 6010,
|
|
@@ -125725,6 +125804,11 @@ var nara_quest_default = {
|
|
|
125725
125804
|
code: 6011,
|
|
125726
125805
|
name: "InsufficientStakeBalance",
|
|
125727
125806
|
msg: "Unstake amount exceeds staked balance"
|
|
125807
|
+
},
|
|
125808
|
+
{
|
|
125809
|
+
code: 6012,
|
|
125810
|
+
name: "InsufficientStake",
|
|
125811
|
+
msg: "Stake does not meet dynamic requirement"
|
|
125728
125812
|
}
|
|
125729
125813
|
],
|
|
125730
125814
|
types: [
|
|
@@ -125777,6 +125861,18 @@ var nara_quest_default = {
|
|
|
125777
125861
|
name: "max_reward_count",
|
|
125778
125862
|
type: "u32"
|
|
125779
125863
|
},
|
|
125864
|
+
{
|
|
125865
|
+
name: "stake_bps_high",
|
|
125866
|
+
type: "u64"
|
|
125867
|
+
},
|
|
125868
|
+
{
|
|
125869
|
+
name: "stake_bps_low",
|
|
125870
|
+
type: "u64"
|
|
125871
|
+
},
|
|
125872
|
+
{
|
|
125873
|
+
name: "decay_ms",
|
|
125874
|
+
type: "i64"
|
|
125875
|
+
},
|
|
125780
125876
|
{
|
|
125781
125877
|
name: "_padding",
|
|
125782
125878
|
type: {
|
|
@@ -125836,11 +125932,19 @@ var nara_quest_default = {
|
|
|
125836
125932
|
type: "u32"
|
|
125837
125933
|
},
|
|
125838
125934
|
{
|
|
125839
|
-
name: "
|
|
125935
|
+
name: "created_at",
|
|
125936
|
+
type: "i64"
|
|
125937
|
+
},
|
|
125938
|
+
{
|
|
125939
|
+
name: "stake_high",
|
|
125840
125940
|
type: "u64"
|
|
125841
125941
|
},
|
|
125842
125942
|
{
|
|
125843
|
-
name: "
|
|
125943
|
+
name: "stake_low",
|
|
125944
|
+
type: "u64"
|
|
125945
|
+
},
|
|
125946
|
+
{
|
|
125947
|
+
name: "avg_participant_stake",
|
|
125844
125948
|
type: "u64"
|
|
125845
125949
|
},
|
|
125846
125950
|
{
|
|
@@ -125906,14 +126010,7 @@ var BN254_FIELD = 21888242871839275222246405745257275088696311157297823662689037
|
|
|
125906
126010
|
async function resolveDefaultZkPaths() {
|
|
125907
126011
|
const { fileURLToPath } = await import("url");
|
|
125908
126012
|
const { dirname: dirname2, join: join6 } = await import("path");
|
|
125909
|
-
|
|
125910
|
-
if (import_meta.url) {
|
|
125911
|
-
dir = dirname2(fileURLToPath(import_meta.url));
|
|
125912
|
-
} else {
|
|
125913
|
-
const { createRequire } = await import("module");
|
|
125914
|
-
const req = createRequire(process.cwd() + "/");
|
|
125915
|
-
dir = dirname2(req.resolve("nara-sdk/package.json")) + "/src";
|
|
125916
|
-
}
|
|
126013
|
+
const dir = dirname2(fileURLToPath(import_meta.url));
|
|
125917
126014
|
return {
|
|
125918
126015
|
wasm: join6(dir, "zk", "answer_proof.wasm"),
|
|
125919
126016
|
zkey: join6(dir, "zk", "answer_proof_final.zkey")
|
|
@@ -126031,6 +126128,14 @@ var WSOL_MINT = new import_web390.PublicKey("So111111111111111111111111111111111
|
|
|
126031
126128
|
function getStakeTokenAccount(stakeRecordPda) {
|
|
126032
126129
|
return getAssociatedTokenAddressSync(WSOL_MINT, stakeRecordPda, true);
|
|
126033
126130
|
}
|
|
126131
|
+
function computeEffectiveStake(stakeHigh, stakeLow, createdAt, decayMs, nowMs) {
|
|
126132
|
+
if (decayMs <= 0) return stakeLow;
|
|
126133
|
+
const elapsedMs = nowMs - createdAt;
|
|
126134
|
+
if (elapsedMs >= decayMs) return stakeLow;
|
|
126135
|
+
const range = stakeHigh - stakeLow;
|
|
126136
|
+
const ratio = elapsedMs / decayMs;
|
|
126137
|
+
return stakeHigh - range * ratio * ratio;
|
|
126138
|
+
}
|
|
126034
126139
|
async function getQuestInfo(connection, wallet, options) {
|
|
126035
126140
|
const kp = wallet ?? import_web390.Keypair.generate();
|
|
126036
126141
|
const program3 = createProgram2(connection, kp, options?.programId);
|
|
@@ -126040,6 +126145,25 @@ async function getQuestInfo(connection, wallet, options) {
|
|
|
126040
126145
|
const deadline = pool.deadline.toNumber();
|
|
126041
126146
|
const secsLeft = deadline - now;
|
|
126042
126147
|
const active = pool.question.length > 0 && secsLeft > 0;
|
|
126148
|
+
const stakeHigh = Number(pool.stakeHigh.toString()) / import_web390.LAMPORTS_PER_SOL;
|
|
126149
|
+
const stakeLow = Number(pool.stakeLow.toString()) / import_web390.LAMPORTS_PER_SOL;
|
|
126150
|
+
const createdAt = pool.createdAt.toNumber();
|
|
126151
|
+
const programId = new import_web390.PublicKey(options?.programId ?? DEFAULT_QUEST_PROGRAM_ID);
|
|
126152
|
+
const [configPda] = import_web390.PublicKey.findProgramAddressSync(
|
|
126153
|
+
[new TextEncoder().encode("quest_config")],
|
|
126154
|
+
programId
|
|
126155
|
+
);
|
|
126156
|
+
const config = await program3.account.gameConfig.fetch(configPda);
|
|
126157
|
+
const decayMs = Number(config.decayMs.toString());
|
|
126158
|
+
const nowMs = Date.now();
|
|
126159
|
+
const createdAtMs = createdAt * 1e3;
|
|
126160
|
+
const effectiveStakeRequirement = computeEffectiveStake(
|
|
126161
|
+
stakeHigh,
|
|
126162
|
+
stakeLow,
|
|
126163
|
+
createdAtMs,
|
|
126164
|
+
decayMs,
|
|
126165
|
+
nowMs
|
|
126166
|
+
);
|
|
126043
126167
|
return {
|
|
126044
126168
|
active,
|
|
126045
126169
|
round: pool.round.toString(),
|
|
@@ -126054,8 +126178,11 @@ async function getQuestInfo(connection, wallet, options) {
|
|
|
126054
126178
|
deadline,
|
|
126055
126179
|
timeRemaining: secsLeft,
|
|
126056
126180
|
expired: secsLeft <= 0,
|
|
126057
|
-
|
|
126058
|
-
|
|
126181
|
+
stakeHigh,
|
|
126182
|
+
stakeLow,
|
|
126183
|
+
avgParticipantStake: Number(pool.avgParticipantStake.toString()) / import_web390.LAMPORTS_PER_SOL,
|
|
126184
|
+
createdAt,
|
|
126185
|
+
effectiveStakeRequirement
|
|
126059
126186
|
};
|
|
126060
126187
|
}
|
|
126061
126188
|
async function hasAnswered(connection, wallet, options) {
|
|
@@ -126105,7 +126232,7 @@ async function submitAnswer(connection, wallet, proof, agent = "", model = "", o
|
|
|
126105
126232
|
if (options.stake === "auto") {
|
|
126106
126233
|
const quest = await getQuestInfo(connection, wallet, options);
|
|
126107
126234
|
const stakeInfo = await getStakeInfo(connection, wallet.publicKey, options);
|
|
126108
|
-
const required = quest.
|
|
126235
|
+
const required = quest.effectiveStakeRequirement;
|
|
126109
126236
|
const current = stakeInfo?.amount ?? 0;
|
|
126110
126237
|
const deficit = required - current;
|
|
126111
126238
|
if (deficit > 0) {
|
|
@@ -130103,8 +130230,10 @@ async function handleQuestGet(options) {
|
|
|
130103
130230
|
deadline: new Date(quest.deadline * 1e3).toLocaleString(),
|
|
130104
130231
|
timeRemaining: formatTimeRemaining(quest.timeRemaining),
|
|
130105
130232
|
expired: quest.expired,
|
|
130106
|
-
stakeRequirement: `${quest.
|
|
130107
|
-
|
|
130233
|
+
stakeRequirement: `${quest.effectiveStakeRequirement.toFixed(4)} NARA`,
|
|
130234
|
+
stakeHigh: `${quest.stakeHigh} NARA`,
|
|
130235
|
+
stakeLow: `${quest.stakeLow} NARA`,
|
|
130236
|
+
avgParticipantStake: `${quest.avgParticipantStake} NARA`
|
|
130108
130237
|
};
|
|
130109
130238
|
if (options.json) {
|
|
130110
130239
|
formatOutput(data, true);
|
|
@@ -130118,9 +130247,8 @@ async function handleQuestGet(options) {
|
|
|
130118
130247
|
console.log(
|
|
130119
130248
|
` Reward slots: ${quest.winnerCount}/${quest.rewardCount} (${quest.remainingSlots} remaining)`
|
|
130120
130249
|
);
|
|
130121
|
-
if (quest.
|
|
130122
|
-
console.log(` Stake requirement: ${quest.
|
|
130123
|
-
console.log(` Min winner stake: ${quest.minWinnerStake} NARA`);
|
|
130250
|
+
if (quest.effectiveStakeRequirement > 0) {
|
|
130251
|
+
console.log(` Stake requirement: ${quest.effectiveStakeRequirement.toFixed(4)} NARA (decays ${quest.stakeHigh} \u2192 ${quest.stakeLow})`);
|
|
130124
130252
|
}
|
|
130125
130253
|
console.log(` Deadline: ${new Date(quest.deadline * 1e3).toLocaleString()}`);
|
|
130126
130254
|
if (quest.timeRemaining > 0) {
|
|
@@ -133245,7 +133373,7 @@ function registerCommands(program3) {
|
|
|
133245
133373
|
}
|
|
133246
133374
|
|
|
133247
133375
|
// bin/nara-cli.ts
|
|
133248
|
-
var version2 = true ? "1.0.
|
|
133376
|
+
var version2 = true ? "1.0.47" : "dev";
|
|
133249
133377
|
var program2 = new Command();
|
|
133250
133378
|
program2.name("naracli").description("CLI for the Nara chain (Solana-compatible)").version(version2);
|
|
133251
133379
|
program2.option("-r, --rpc-url <url>", "RPC endpoint URL").option("-w, --wallet <path>", "Path to wallet keypair JSON file").option("-j, --json", "Output in JSON format");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "naracli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.47",
|
|
4
4
|
"description": "CLI for the Nara chain (Solana-compatible)",
|
|
5
5
|
"module": "index.ts",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"test:skills": "node --require ./bin/env-loader.cjs --import tsx --test --experimental-test-isolation=none --test-reporter spec src/tests/skills.test.ts",
|
|
21
21
|
"test:zkid": "node --require ./bin/env-loader.cjs --import tsx --test --experimental-test-isolation=none --test-reporter spec src/tests/zkid.test.ts",
|
|
22
22
|
"test:agent": "node --require ./bin/env-loader.cjs --import tsx --test --experimental-test-isolation=none --test-reporter spec src/tests/agent-registry.test.ts",
|
|
23
|
+
"test:quest-relay": "node --require ./bin/env-loader.cjs --import tsx --test --experimental-test-isolation=none --test-reporter spec src/tests/quest-relay.test.ts",
|
|
23
24
|
"test:quest-referral": "node --require ./bin/env-loader.cjs --import tsx --test --experimental-test-isolation=none --test-reporter spec src/tests/quest-referral.test.ts",
|
|
24
25
|
"prepublishOnly": "npm run build"
|
|
25
26
|
},
|
|
@@ -56,7 +57,7 @@
|
|
|
56
57
|
"bs58": "^6.0.0",
|
|
57
58
|
"commander": "^12.1.0",
|
|
58
59
|
"ed25519-hd-key": "^1.3.0",
|
|
59
|
-
"nara-sdk": "^1.0.
|
|
60
|
+
"nara-sdk": "^1.0.48",
|
|
60
61
|
"picocolors": "^1.1.1"
|
|
61
62
|
},
|
|
62
63
|
"packageManager": "pnpm@10.27.0+sha512.72d699da16b1179c14ba9e64dc71c9a40988cbdc65c264cb0e489db7de917f20dcf4d64d8723625f2969ba52d4b7e2a1170682d9ac2a5dcaeaab732b7e16f04a"
|
|
@@ -117,8 +117,10 @@ async function handleQuestGet(options: GlobalOptions) {
|
|
|
117
117
|
deadline: new Date(quest.deadline * 1000).toLocaleString(),
|
|
118
118
|
timeRemaining: formatTimeRemaining(quest.timeRemaining),
|
|
119
119
|
expired: quest.expired,
|
|
120
|
-
stakeRequirement: `${quest.
|
|
121
|
-
|
|
120
|
+
stakeRequirement: `${quest.effectiveStakeRequirement.toFixed(4)} NARA`,
|
|
121
|
+
stakeHigh: `${quest.stakeHigh} NARA`,
|
|
122
|
+
stakeLow: `${quest.stakeLow} NARA`,
|
|
123
|
+
avgParticipantStake: `${quest.avgParticipantStake} NARA`,
|
|
122
124
|
};
|
|
123
125
|
|
|
124
126
|
if (options.json) {
|
|
@@ -133,9 +135,8 @@ async function handleQuestGet(options: GlobalOptions) {
|
|
|
133
135
|
console.log(
|
|
134
136
|
` Reward slots: ${quest.winnerCount}/${quest.rewardCount} (${quest.remainingSlots} remaining)`
|
|
135
137
|
);
|
|
136
|
-
if (quest.
|
|
137
|
-
console.log(` Stake requirement: ${quest.
|
|
138
|
-
console.log(` Min winner stake: ${quest.minWinnerStake} NARA`);
|
|
138
|
+
if (quest.effectiveStakeRequirement > 0) {
|
|
139
|
+
console.log(` Stake requirement: ${quest.effectiveStakeRequirement.toFixed(4)} NARA (decays ${quest.stakeHigh} → ${quest.stakeLow})`);
|
|
139
140
|
}
|
|
140
141
|
console.log(` Deadline: ${new Date(quest.deadline * 1000).toLocaleString()}`);
|
|
141
142
|
if (quest.timeRemaining > 0) {
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for quest relay (gasless) submission
|
|
3
|
+
*
|
|
4
|
+
* Requires PRIVATE_KEY in .env and an active quest.
|
|
5
|
+
*
|
|
6
|
+
* Run: npm run test:quest-relay
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, it } from "node:test";
|
|
10
|
+
import assert from "node:assert/strict";
|
|
11
|
+
import { readFileSync } from "node:fs";
|
|
12
|
+
import { join, dirname } from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { Connection } from "@solana/web3.js";
|
|
15
|
+
import { runCli, hasWallet } from "./helpers.js";
|
|
16
|
+
import { getQuestInfo } from "nara-sdk";
|
|
17
|
+
|
|
18
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
|
|
20
|
+
interface TestQuestion {
|
|
21
|
+
text: string;
|
|
22
|
+
answer: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function loadTestQuestions(): TestQuestion[] {
|
|
26
|
+
const filePath = join(__dirname, "../../.assets/test-questions.json");
|
|
27
|
+
return JSON.parse(readFileSync(filePath, "utf-8"));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ─── Relay quest answer ──────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
describe("quest answer (relay)", { skip: !hasWallet ? "no wallet" : undefined }, () => {
|
|
33
|
+
it("submits answer via relay and outputs tx", async () => {
|
|
34
|
+
const rpcUrl = process.env.RPC_URL || "https://mainnet-api.nara.build/";
|
|
35
|
+
const relayUrl = process.env.QUEST_RELAY_URL || "https://quest-api.nara.build/";
|
|
36
|
+
console.log(` Relay: ${relayUrl}`);
|
|
37
|
+
const connection = new Connection(rpcUrl, "confirmed");
|
|
38
|
+
|
|
39
|
+
const quest = await getQuestInfo(connection);
|
|
40
|
+
if (!quest.active || quest.expired) {
|
|
41
|
+
console.log(" (skipped: no active quest)");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const questions = loadTestQuestions();
|
|
46
|
+
const match = questions.find((q) => q.text === quest.question);
|
|
47
|
+
if (!match) {
|
|
48
|
+
console.log(` (skipped: question not found in test-questions.json)`);
|
|
49
|
+
console.log(` Question: ${quest.question}`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log(` Question: ${quest.question}`);
|
|
54
|
+
console.log(` Answer: ${match.answer}`);
|
|
55
|
+
|
|
56
|
+
const { stdout, stderr, exitCode } = await runCli([
|
|
57
|
+
"quest", "answer", match.answer,
|
|
58
|
+
"--relay",
|
|
59
|
+
"--agent", "test",
|
|
60
|
+
"--model", "test",
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
const output = stdout + stderr;
|
|
64
|
+
|
|
65
|
+
if (output.includes("already answered") || output.includes("Already answered")) {
|
|
66
|
+
console.log(" Already answered this round");
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (output.includes("expired")) {
|
|
70
|
+
console.log(" Quest expired during test");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
assert.equal(exitCode, 0, `CLI failed: ${stderr}`);
|
|
75
|
+
assert.ok(output.includes("Transaction:") || output.includes("submitted"), "should confirm submission");
|
|
76
|
+
|
|
77
|
+
// Extract and print tx signature
|
|
78
|
+
const txMatch = output.match(/Transaction:\s+(\S+)/);
|
|
79
|
+
if (txMatch) {
|
|
80
|
+
console.log(` TX: ${txMatch[1]}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Print reward info
|
|
84
|
+
if (output.includes("Reward received")) {
|
|
85
|
+
const rewardMatch = output.match(/Reward received:\s+(.+)/);
|
|
86
|
+
console.log(` Reward: ${rewardMatch ? rewardMatch[1] : "yes"}`);
|
|
87
|
+
} else if (output.includes("no reward") || output.includes("all reward slots")) {
|
|
88
|
+
console.log(" Reward: none (all slots claimed)");
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
});
|
package/src/tests/quest.test.ts
CHANGED