naracli 1.0.11 ā 1.0.13
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/README.md +8 -8
- package/bin/nara-cli.ts +21 -1
- package/dist/nara-cli.mjs +45 -23
- package/index.ts +2 -2
- package/package.json +4 -4
- package/src/cli/commands/wallet.ts +13 -12
- package/src/cli/utils/wallet.ts +1 -2
- package/src/constants.ts +6 -0
- package/src/quest.ts +19 -11
package/README.md
CHANGED
|
@@ -106,11 +106,11 @@ See [examples/](examples/) for complete SDK usage examples.
|
|
|
106
106
|
|
|
107
107
|
```bash
|
|
108
108
|
# Create a new wallet
|
|
109
|
-
|
|
109
|
+
npx naracli wallet create
|
|
110
110
|
|
|
111
111
|
# Or import from mnemonic / private key
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
npx naracli wallet import -m "your twelve word mnemonic phrase ..."
|
|
113
|
+
npx naracli wallet import -k "your-private-key"
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
Wallet is saved to `~/.config/nara/id.json` by default.
|
|
@@ -126,7 +126,7 @@ migrate Check migration eligibility and launch to DAMM V2
|
|
|
126
126
|
quest On-chain quiz with ZK proof verification
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
-
Run `
|
|
129
|
+
Run `npx naracli <command> --help` for details on each command.
|
|
130
130
|
|
|
131
131
|
### Global Options
|
|
132
132
|
|
|
@@ -140,14 +140,14 @@ Run `nara-cli <command> --help` for details on each command.
|
|
|
140
140
|
|
|
141
141
|
```bash
|
|
142
142
|
# Check balance
|
|
143
|
-
|
|
143
|
+
npx naracli wallet balance
|
|
144
144
|
|
|
145
145
|
# Buy tokens
|
|
146
|
-
|
|
146
|
+
npx naracli swap buy <TOKEN_ADDRESS> 0.1
|
|
147
147
|
|
|
148
148
|
# Answer a quest
|
|
149
|
-
|
|
150
|
-
|
|
149
|
+
npx naracli quest get
|
|
150
|
+
npx naracli quest answer "your answer"
|
|
151
151
|
```
|
|
152
152
|
|
|
153
153
|
## License
|
package/bin/nara-cli.ts
CHANGED
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
import { registerCommands } from "../src/cli/index";
|
|
7
|
+
import { loadWallet } from "../src/cli/utils/wallet";
|
|
7
8
|
|
|
8
9
|
// Create program
|
|
9
10
|
const program = new Command();
|
|
10
11
|
|
|
11
12
|
// Set program metadata
|
|
12
13
|
program
|
|
13
|
-
.name("
|
|
14
|
+
.name("naracli")
|
|
14
15
|
.description("CLI for the Nara chain (Solana-compatible)")
|
|
15
16
|
.version("0.1.0");
|
|
16
17
|
|
|
@@ -20,6 +21,25 @@ program
|
|
|
20
21
|
.option("-w, --wallet <path>", "Path to wallet keypair JSON file")
|
|
21
22
|
.option("-j, --json", "Output in JSON format");
|
|
22
23
|
|
|
24
|
+
// Top-level address shortcut
|
|
25
|
+
program
|
|
26
|
+
.command("address")
|
|
27
|
+
.description("Show wallet address")
|
|
28
|
+
.action(async () => {
|
|
29
|
+
const opts = program.opts();
|
|
30
|
+
try {
|
|
31
|
+
const wallet = await loadWallet(opts.wallet);
|
|
32
|
+
if (opts.json) {
|
|
33
|
+
console.log(JSON.stringify({ address: wallet.publicKey.toBase58() }, null, 2));
|
|
34
|
+
} else {
|
|
35
|
+
console.log(wallet.publicKey.toBase58());
|
|
36
|
+
}
|
|
37
|
+
} catch (error: any) {
|
|
38
|
+
console.error(`Error: ${error.message}`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
23
43
|
// Register all command modules
|
|
24
44
|
registerCommands(program);
|
|
25
45
|
|
package/dist/nara-cli.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
try{process.loadEnvFile()}catch{}
|
|
2
3
|
|
|
3
4
|
// bin/nara-cli.ts
|
|
4
5
|
import { Command as Command8 } from "commander";
|
|
@@ -208,6 +209,7 @@ var DEFAULT_RPC_URL = process.env.RPC_URL || "https://mainnet-api.nara.build/";
|
|
|
208
209
|
var DEFAULT_DBC_CONFIG_ADDRESS = process.env.DBC_CONFIG_ADDRESS || "";
|
|
209
210
|
var DEFAULT_WALLET_PATH = process.env.WALLET_PATH || "~/.config/nara/id.json";
|
|
210
211
|
var DEFAULT_QUEST_RELAY_URL = process.env.QUEST_RELAY_URL || "https://quest-api.nara.build/";
|
|
212
|
+
var DEFAULT_QUEST_PROGRAM_ID = process.env.QUEST_PROGRAM_ID || "Quest11111111111111111111111111111111111111";
|
|
211
213
|
|
|
212
214
|
// src/cli/utils/wallet.ts
|
|
213
215
|
var DEFAULT_WALLET_PATH2 = DEFAULT_WALLET_PATH.startsWith("~") ? join(homedir(), DEFAULT_WALLET_PATH.slice(1)) : DEFAULT_WALLET_PATH;
|
|
@@ -229,7 +231,10 @@ async function loadWallet(walletPath) {
|
|
|
229
231
|
} catch (error) {
|
|
230
232
|
if (!walletPath) {
|
|
231
233
|
throw new Error(
|
|
232
|
-
`
|
|
234
|
+
`No wallet found. Create one first:
|
|
235
|
+
|
|
236
|
+
npx naracli wallet create
|
|
237
|
+
`
|
|
233
238
|
);
|
|
234
239
|
} else {
|
|
235
240
|
throw new Error(`Failed to load wallet from ${path}: ${error.message}`);
|
|
@@ -1711,7 +1716,7 @@ import * as bip39 from "bip39";
|
|
|
1711
1716
|
import { derivePath } from "ed25519-hd-key";
|
|
1712
1717
|
import { join as join2 } from "node:path";
|
|
1713
1718
|
import { homedir as homedir2 } from "node:os";
|
|
1714
|
-
import { mkdir } from "node:fs/promises";
|
|
1719
|
+
import { mkdir, writeFile, access } from "node:fs/promises";
|
|
1715
1720
|
import bs58 from "bs58";
|
|
1716
1721
|
var DEFAULT_WALLET_PATH3 = DEFAULT_WALLET_PATH.startsWith("~") ? join2(homedir2(), DEFAULT_WALLET_PATH.slice(1)) : DEFAULT_WALLET_PATH;
|
|
1717
1722
|
function registerWalletCommands(program2) {
|
|
@@ -2061,10 +2066,13 @@ Token Transfer Details:`);
|
|
|
2061
2066
|
}
|
|
2062
2067
|
async function handleWalletCreate(options) {
|
|
2063
2068
|
const outputPath = options.output || DEFAULT_WALLET_PATH3;
|
|
2064
|
-
|
|
2069
|
+
try {
|
|
2070
|
+
await access(outputPath);
|
|
2065
2071
|
throw new Error(
|
|
2066
2072
|
`Wallet file already exists at ${outputPath}. Please use a different path or remove the existing file first.`
|
|
2067
2073
|
);
|
|
2074
|
+
} catch (e) {
|
|
2075
|
+
if (e.code !== "ENOENT") throw e;
|
|
2068
2076
|
}
|
|
2069
2077
|
const mnemonic = bip39.generateMnemonic(128);
|
|
2070
2078
|
const seed = await bip39.mnemonicToSeed(mnemonic);
|
|
@@ -2073,7 +2081,7 @@ async function handleWalletCreate(options) {
|
|
|
2073
2081
|
const dir = outputPath.substring(0, outputPath.lastIndexOf("/"));
|
|
2074
2082
|
await mkdir(dir, { recursive: true });
|
|
2075
2083
|
const walletData = Array.from(keypair.secretKey);
|
|
2076
|
-
await
|
|
2084
|
+
await writeFile(outputPath, JSON.stringify(walletData, null, 2));
|
|
2077
2085
|
console.log("\n\u2705 Wallet created successfully!");
|
|
2078
2086
|
console.log(`
|
|
2079
2087
|
\u{1F4C1} Wallet saved to: ${outputPath}`);
|
|
@@ -2089,10 +2097,13 @@ ${mnemonic}
|
|
|
2089
2097
|
}
|
|
2090
2098
|
async function handleWalletImport(options) {
|
|
2091
2099
|
const outputPath = options.output || DEFAULT_WALLET_PATH3;
|
|
2092
|
-
|
|
2100
|
+
try {
|
|
2101
|
+
await access(outputPath);
|
|
2093
2102
|
throw new Error(
|
|
2094
2103
|
`Wallet file already exists at ${outputPath}. Please use a different path or remove the existing file first.`
|
|
2095
2104
|
);
|
|
2105
|
+
} catch (e) {
|
|
2106
|
+
if (e.code !== "ENOENT") throw e;
|
|
2096
2107
|
}
|
|
2097
2108
|
let keypair;
|
|
2098
2109
|
if (options.mnemonic) {
|
|
@@ -2123,7 +2134,7 @@ async function handleWalletImport(options) {
|
|
|
2123
2134
|
const dir = outputPath.substring(0, outputPath.lastIndexOf("/"));
|
|
2124
2135
|
await mkdir(dir, { recursive: true });
|
|
2125
2136
|
const walletData = Array.from(keypair.secretKey);
|
|
2126
|
-
await
|
|
2137
|
+
await writeFile(outputPath, JSON.stringify(walletData, null, 2));
|
|
2127
2138
|
console.log("\n\u2705 Wallet imported successfully!");
|
|
2128
2139
|
console.log(`
|
|
2129
2140
|
\u{1F4C1} Wallet saved to: ${outputPath}`);
|
|
@@ -2133,14 +2144,9 @@ async function handleWalletImport(options) {
|
|
|
2133
2144
|
async function handleWalletAddress(options) {
|
|
2134
2145
|
const wallet = await loadWallet(options.wallet);
|
|
2135
2146
|
if (options.json) {
|
|
2136
|
-
|
|
2137
|
-
address: wallet.publicKey.toBase58()
|
|
2138
|
-
};
|
|
2139
|
-
console.log(JSON.stringify(output, null, 2));
|
|
2147
|
+
console.log(JSON.stringify({ address: wallet.publicKey.toBase58() }, null, 2));
|
|
2140
2148
|
} else {
|
|
2141
|
-
console.log(
|
|
2142
|
-
\u{1F511} Wallet Address: ${wallet.publicKey.toBase58()}
|
|
2143
|
-
`);
|
|
2149
|
+
console.log(wallet.publicKey.toBase58());
|
|
2144
2150
|
}
|
|
2145
2151
|
}
|
|
2146
2152
|
|
|
@@ -2236,16 +2242,18 @@ async function silentProve(snarkjs, input, wasmPath, zkeyPath) {
|
|
|
2236
2242
|
console.error = savedError;
|
|
2237
2243
|
}
|
|
2238
2244
|
}
|
|
2239
|
-
function createProgram(connection, wallet) {
|
|
2245
|
+
function createProgram(connection, wallet, programId) {
|
|
2240
2246
|
const idlPath = existsSync(join3(__dirname, "cli/quest/nara_quest.json")) ? "./cli/quest/nara_quest.json" : "./quest/nara_quest.json";
|
|
2241
2247
|
const idl = _require(idlPath);
|
|
2248
|
+
const pid = programId ?? DEFAULT_QUEST_PROGRAM_ID;
|
|
2249
|
+
const idlWithPid = { ...idl, address: pid };
|
|
2242
2250
|
const provider = new AnchorProvider(
|
|
2243
2251
|
connection,
|
|
2244
2252
|
new Wallet(wallet),
|
|
2245
2253
|
{ commitment: "confirmed" }
|
|
2246
2254
|
);
|
|
2247
2255
|
anchor.setProvider(provider);
|
|
2248
|
-
return new Program(
|
|
2256
|
+
return new Program(idlWithPid, provider);
|
|
2249
2257
|
}
|
|
2250
2258
|
function getPoolPda(programId) {
|
|
2251
2259
|
const [pda] = PublicKey8.findProgramAddressSync(
|
|
@@ -2261,9 +2269,9 @@ function getWinnerRecordPda(programId, user) {
|
|
|
2261
2269
|
);
|
|
2262
2270
|
return pda;
|
|
2263
2271
|
}
|
|
2264
|
-
async function getQuestInfo(connection, wallet) {
|
|
2272
|
+
async function getQuestInfo(connection, wallet, options) {
|
|
2265
2273
|
const kp = wallet ?? Keypair7.generate();
|
|
2266
|
-
const program2 = createProgram(connection, kp);
|
|
2274
|
+
const program2 = createProgram(connection, kp, options?.programId);
|
|
2267
2275
|
const poolPda = getPoolPda(program2.programId);
|
|
2268
2276
|
const pool = await program2.account.pool.fetch(poolPda);
|
|
2269
2277
|
const now = Math.floor(Date.now() / 1e3);
|
|
@@ -2285,9 +2293,9 @@ async function getQuestInfo(connection, wallet) {
|
|
|
2285
2293
|
expired: secsLeft <= 0
|
|
2286
2294
|
};
|
|
2287
2295
|
}
|
|
2288
|
-
async function hasAnswered(connection, wallet) {
|
|
2289
|
-
const program2 = createProgram(connection, wallet);
|
|
2290
|
-
const quest = await getQuestInfo(connection, wallet);
|
|
2296
|
+
async function hasAnswered(connection, wallet, options) {
|
|
2297
|
+
const program2 = createProgram(connection, wallet, options?.programId);
|
|
2298
|
+
const quest = await getQuestInfo(connection, wallet, options);
|
|
2291
2299
|
const winnerPda = getWinnerRecordPda(program2.programId, wallet.publicKey);
|
|
2292
2300
|
try {
|
|
2293
2301
|
const wr = await program2.account.winnerRecord.fetch(winnerPda);
|
|
@@ -2318,8 +2326,8 @@ async function generateProof(answer, answerHash, userPubkey, options) {
|
|
|
2318
2326
|
hex: proofToHex(result.proof)
|
|
2319
2327
|
};
|
|
2320
2328
|
}
|
|
2321
|
-
async function submitAnswer(connection, wallet, proof) {
|
|
2322
|
-
const program2 = createProgram(connection, wallet);
|
|
2329
|
+
async function submitAnswer(connection, wallet, proof, options) {
|
|
2330
|
+
const program2 = createProgram(connection, wallet, options?.programId);
|
|
2323
2331
|
const signature = await program2.methods.submitAnswer(proof.proofA, proof.proofB, proof.proofC).accounts({ user: wallet.publicKey, payer: wallet.publicKey }).signers([wallet]).rpc({ skipPreflight: true });
|
|
2324
2332
|
return { signature };
|
|
2325
2333
|
}
|
|
@@ -2622,8 +2630,22 @@ function registerCommands(program2) {
|
|
|
2622
2630
|
|
|
2623
2631
|
// bin/nara-cli.ts
|
|
2624
2632
|
var program = new Command8();
|
|
2625
|
-
program.name("
|
|
2633
|
+
program.name("naracli").description("CLI for the Nara chain (Solana-compatible)").version("0.1.0");
|
|
2626
2634
|
program.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");
|
|
2635
|
+
program.command("address").description("Show wallet address").action(async () => {
|
|
2636
|
+
const opts = program.opts();
|
|
2637
|
+
try {
|
|
2638
|
+
const wallet = await loadWallet(opts.wallet);
|
|
2639
|
+
if (opts.json) {
|
|
2640
|
+
console.log(JSON.stringify({ address: wallet.publicKey.toBase58() }, null, 2));
|
|
2641
|
+
} else {
|
|
2642
|
+
console.log(wallet.publicKey.toBase58());
|
|
2643
|
+
}
|
|
2644
|
+
} catch (error) {
|
|
2645
|
+
console.error(`Error: ${error.message}`);
|
|
2646
|
+
process.exit(1);
|
|
2647
|
+
}
|
|
2648
|
+
});
|
|
2627
2649
|
registerCommands(program);
|
|
2628
2650
|
program.parse(process.argv);
|
|
2629
2651
|
if (!process.argv.slice(2).length) {
|
package/index.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
export { NaraSDK, type NaraSDKConfig } from "./src/client";
|
|
11
11
|
|
|
12
12
|
// Export constants
|
|
13
|
-
export { DEFAULT_RPC_URL } from "./src/constants";
|
|
13
|
+
export { DEFAULT_RPC_URL, DEFAULT_QUEST_PROGRAM_ID } from "./src/constants";
|
|
14
14
|
|
|
15
15
|
// Export config functions and types
|
|
16
16
|
export {
|
|
@@ -68,7 +68,7 @@ export {
|
|
|
68
68
|
type ZkProofHex,
|
|
69
69
|
type SubmitAnswerResult,
|
|
70
70
|
type SubmitRelayResult,
|
|
71
|
-
type
|
|
71
|
+
type QuestOptions,
|
|
72
72
|
} from "./src/quest";
|
|
73
73
|
|
|
74
74
|
// Re-export commonly used types from dependencies
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "naracli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
4
4
|
"description": "CLI for the Nara chain (Solana-compatible)",
|
|
5
5
|
"module": "index.ts",
|
|
6
6
|
"main": "index.ts",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"private": false,
|
|
9
9
|
"bin": {
|
|
10
|
-
"
|
|
10
|
+
"naracli": "./dist/nara-cli.mjs"
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
|
-
"cli": "
|
|
14
|
-
"build": "npx esbuild bin/nara-cli.ts --bundle --platform=node --format=esm --outfile=dist/nara-cli.mjs --packages=external --banner:js='#!/usr/bin/env node' && mkdir -p dist/zk dist/quest && cp src/cli/zk/answer_proof.wasm src/cli/zk/answer_proof_final.zkey dist/zk/ && cp src/cli/quest/nara_quest.json dist/quest/",
|
|
13
|
+
"cli": "node --env-file=.env --import tsx bin/nara-cli.ts",
|
|
14
|
+
"build": "npx esbuild bin/nara-cli.ts --bundle --platform=node --format=esm --outfile=dist/nara-cli.mjs --packages=external --banner:js='#!/usr/bin/env node\ntry{process.loadEnvFile()}catch{}' && mkdir -p dist/zk dist/quest && cp src/cli/zk/answer_proof.wasm src/cli/zk/answer_proof_final.zkey dist/zk/ && cp src/cli/quest/nara_quest.json dist/quest/",
|
|
15
15
|
"prepublishOnly": "npm run build"
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
@@ -19,7 +19,7 @@ import * as bip39 from "bip39";
|
|
|
19
19
|
import { derivePath } from "ed25519-hd-key";
|
|
20
20
|
import { join } from "node:path";
|
|
21
21
|
import { homedir } from "node:os";
|
|
22
|
-
import { mkdir } from "node:fs/promises";
|
|
22
|
+
import { mkdir, writeFile, access } from "node:fs/promises";
|
|
23
23
|
import bs58 from "bs58";
|
|
24
24
|
import { NaraSDK } from "../../client";
|
|
25
25
|
import { DEFAULT_WALLET_PATH as _DEFAULT_WALLET_PATH } from "../../constants";
|
|
@@ -593,10 +593,13 @@ async function handleWalletCreate(options: { output?: string }): Promise<void> {
|
|
|
593
593
|
const outputPath = options.output || DEFAULT_WALLET_PATH;
|
|
594
594
|
|
|
595
595
|
// Check if wallet file already exists
|
|
596
|
-
|
|
596
|
+
try {
|
|
597
|
+
await access(outputPath);
|
|
597
598
|
throw new Error(
|
|
598
599
|
`Wallet file already exists at ${outputPath}. Please use a different path or remove the existing file first.`
|
|
599
600
|
);
|
|
601
|
+
} catch (e: any) {
|
|
602
|
+
if (e.code !== "ENOENT") throw e;
|
|
600
603
|
}
|
|
601
604
|
|
|
602
605
|
// Generate mnemonic (12 words by default, can be changed to 24)
|
|
@@ -613,7 +616,7 @@ async function handleWalletCreate(options: { output?: string }): Promise<void> {
|
|
|
613
616
|
|
|
614
617
|
// Save wallet to file
|
|
615
618
|
const walletData = Array.from(keypair.secretKey);
|
|
616
|
-
await
|
|
619
|
+
await writeFile(outputPath, JSON.stringify(walletData, null, 2));
|
|
617
620
|
|
|
618
621
|
// Display results
|
|
619
622
|
console.log("\nā
Wallet created successfully!");
|
|
@@ -641,10 +644,13 @@ async function handleWalletImport(options: {
|
|
|
641
644
|
const outputPath = options.output || DEFAULT_WALLET_PATH;
|
|
642
645
|
|
|
643
646
|
// Check if wallet file already exists
|
|
644
|
-
|
|
647
|
+
try {
|
|
648
|
+
await access(outputPath);
|
|
645
649
|
throw new Error(
|
|
646
650
|
`Wallet file already exists at ${outputPath}. Please use a different path or remove the existing file first.`
|
|
647
651
|
);
|
|
652
|
+
} catch (e: any) {
|
|
653
|
+
if (e.code !== "ENOENT") throw e;
|
|
648
654
|
}
|
|
649
655
|
|
|
650
656
|
let keypair: Keypair;
|
|
@@ -691,7 +697,7 @@ async function handleWalletImport(options: {
|
|
|
691
697
|
|
|
692
698
|
// Save wallet to file
|
|
693
699
|
const walletData = Array.from(keypair.secretKey);
|
|
694
|
-
await
|
|
700
|
+
await writeFile(outputPath, JSON.stringify(walletData, null, 2));
|
|
695
701
|
|
|
696
702
|
// Display results
|
|
697
703
|
console.log("\nā
Wallet imported successfully!");
|
|
@@ -704,16 +710,11 @@ async function handleWalletImport(options: {
|
|
|
704
710
|
* @param options Command options
|
|
705
711
|
*/
|
|
706
712
|
async function handleWalletAddress(options: GlobalOptions): Promise<void> {
|
|
707
|
-
// Load wallet
|
|
708
713
|
const wallet = await loadWallet(options.wallet);
|
|
709
714
|
|
|
710
|
-
// Output result
|
|
711
715
|
if (options.json) {
|
|
712
|
-
|
|
713
|
-
address: wallet.publicKey.toBase58(),
|
|
714
|
-
};
|
|
715
|
-
console.log(JSON.stringify(output, null, 2));
|
|
716
|
+
console.log(JSON.stringify({ address: wallet.publicKey.toBase58() }, null, 2));
|
|
716
717
|
} else {
|
|
717
|
-
console.log(
|
|
718
|
+
console.log(wallet.publicKey.toBase58());
|
|
718
719
|
}
|
|
719
720
|
}
|
package/src/cli/utils/wallet.ts
CHANGED
|
@@ -47,9 +47,8 @@ export async function loadWallet(walletPath?: string): Promise<Keypair> {
|
|
|
47
47
|
}
|
|
48
48
|
} catch (error: any) {
|
|
49
49
|
if (!walletPath) {
|
|
50
|
-
// If using default path and it doesn't exist, provide helpful error message
|
|
51
50
|
throw new Error(
|
|
52
|
-
`
|
|
51
|
+
`No wallet found. Create one first:\n\n npx naracli wallet create\n`
|
|
53
52
|
);
|
|
54
53
|
} else {
|
|
55
54
|
throw new Error(`Failed to load wallet from ${path}: ${error.message}`);
|
package/src/constants.ts
CHANGED
|
@@ -27,3 +27,9 @@ export const DEFAULT_WALLET_PATH =
|
|
|
27
27
|
*/
|
|
28
28
|
export const DEFAULT_QUEST_RELAY_URL =
|
|
29
29
|
process.env.QUEST_RELAY_URL || "https://quest-api.nara.build/";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Quest program ID
|
|
33
|
+
*/
|
|
34
|
+
export const DEFAULT_QUEST_PROGRAM_ID =
|
|
35
|
+
process.env.QUEST_PROGRAM_ID || "Quest11111111111111111111111111111111111111";
|
package/src/quest.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import * as anchor from "@coral-xyz/anchor";
|
|
12
12
|
import { Program, AnchorProvider, Wallet } from "@coral-xyz/anchor";
|
|
13
13
|
import type { NaraQuest } from "./cli/quest/nara_quest_types";
|
|
14
|
+
import { DEFAULT_QUEST_PROGRAM_ID } from "./constants";
|
|
14
15
|
|
|
15
16
|
import { createRequire } from "module";
|
|
16
17
|
const _require = createRequire(import.meta.url);
|
|
@@ -76,7 +77,8 @@ export interface SubmitRelayResult {
|
|
|
76
77
|
txHash: string;
|
|
77
78
|
}
|
|
78
79
|
|
|
79
|
-
export interface
|
|
80
|
+
export interface QuestOptions {
|
|
81
|
+
programId?: string;
|
|
80
82
|
circuitWasmPath?: string;
|
|
81
83
|
zkeyPath?: string;
|
|
82
84
|
}
|
|
@@ -162,19 +164,22 @@ async function silentProve(snarkjs: any, input: Record<string, string>, wasmPath
|
|
|
162
164
|
|
|
163
165
|
function createProgram(
|
|
164
166
|
connection: Connection,
|
|
165
|
-
wallet: Keypair
|
|
167
|
+
wallet: Keypair,
|
|
168
|
+
programId?: string
|
|
166
169
|
): Program<NaraQuest> {
|
|
167
170
|
const idlPath = existsSync(join(__dirname, "cli/quest/nara_quest.json"))
|
|
168
171
|
? "./cli/quest/nara_quest.json"
|
|
169
172
|
: "./quest/nara_quest.json";
|
|
170
173
|
const idl = _require(idlPath);
|
|
174
|
+
const pid = programId ?? DEFAULT_QUEST_PROGRAM_ID;
|
|
175
|
+
const idlWithPid = { ...idl, address: pid };
|
|
171
176
|
const provider = new AnchorProvider(
|
|
172
177
|
connection,
|
|
173
178
|
new Wallet(wallet),
|
|
174
179
|
{ commitment: "confirmed" }
|
|
175
180
|
);
|
|
176
181
|
anchor.setProvider(provider);
|
|
177
|
-
return new Program<NaraQuest>(
|
|
182
|
+
return new Program<NaraQuest>(idlWithPid as any, provider);
|
|
178
183
|
}
|
|
179
184
|
|
|
180
185
|
function getPoolPda(programId: PublicKey): PublicKey {
|
|
@@ -203,10 +208,11 @@ function getWinnerRecordPda(
|
|
|
203
208
|
*/
|
|
204
209
|
export async function getQuestInfo(
|
|
205
210
|
connection: Connection,
|
|
206
|
-
wallet?: Keypair
|
|
211
|
+
wallet?: Keypair,
|
|
212
|
+
options?: QuestOptions
|
|
207
213
|
): Promise<QuestInfo> {
|
|
208
214
|
const kp = wallet ?? Keypair.generate();
|
|
209
|
-
const program = createProgram(connection, kp);
|
|
215
|
+
const program = createProgram(connection, kp, options?.programId);
|
|
210
216
|
const poolPda = getPoolPda(program.programId);
|
|
211
217
|
const pool = await program.account.pool.fetch(poolPda);
|
|
212
218
|
|
|
@@ -236,10 +242,11 @@ export async function getQuestInfo(
|
|
|
236
242
|
*/
|
|
237
243
|
export async function hasAnswered(
|
|
238
244
|
connection: Connection,
|
|
239
|
-
wallet: Keypair
|
|
245
|
+
wallet: Keypair,
|
|
246
|
+
options?: QuestOptions
|
|
240
247
|
): Promise<boolean> {
|
|
241
|
-
const program = createProgram(connection, wallet);
|
|
242
|
-
const quest = await getQuestInfo(connection, wallet);
|
|
248
|
+
const program = createProgram(connection, wallet, options?.programId);
|
|
249
|
+
const quest = await getQuestInfo(connection, wallet, options);
|
|
243
250
|
const winnerPda = getWinnerRecordPda(program.programId, wallet.publicKey);
|
|
244
251
|
try {
|
|
245
252
|
const wr = await program.account.winnerRecord.fetch(winnerPda);
|
|
@@ -257,7 +264,7 @@ export async function generateProof(
|
|
|
257
264
|
answer: string,
|
|
258
265
|
answerHash: number[],
|
|
259
266
|
userPubkey: PublicKey,
|
|
260
|
-
options?:
|
|
267
|
+
options?: QuestOptions
|
|
261
268
|
): Promise<{ solana: ZkProof; hex: ZkProofHex }> {
|
|
262
269
|
const wasmPath = options?.circuitWasmPath ?? process.env.QUEST_CIRCUIT_WASM ?? DEFAULT_CIRCUIT_WASM;
|
|
263
270
|
const zkeyPath = options?.zkeyPath ?? process.env.QUEST_ZKEY ?? DEFAULT_ZKEY;
|
|
@@ -290,9 +297,10 @@ export async function generateProof(
|
|
|
290
297
|
export async function submitAnswer(
|
|
291
298
|
connection: Connection,
|
|
292
299
|
wallet: Keypair,
|
|
293
|
-
proof: ZkProof
|
|
300
|
+
proof: ZkProof,
|
|
301
|
+
options?: QuestOptions
|
|
294
302
|
): Promise<SubmitAnswerResult> {
|
|
295
|
-
const program = createProgram(connection, wallet);
|
|
303
|
+
const program = createProgram(connection, wallet, options?.programId);
|
|
296
304
|
const signature = await program.methods
|
|
297
305
|
.submitAnswer(proof.proofA as any, proof.proofB as any, proof.proofC as any)
|
|
298
306
|
.accounts({ user: wallet.publicKey, payer: wallet.publicKey })
|