naracli 1.0.17 → 1.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +101 -114
  2. package/bin/nara-cli.ts +0 -20
  3. package/dist/nara-cli.mjs +49930 -2222
  4. package/index.ts +10 -58
  5. package/package.json +7 -6
  6. package/src/cli/commands/quest.ts +8 -7
  7. package/src/cli/commands/skills.ts +491 -0
  8. package/src/cli/commands/skillsInstall.ts +793 -0
  9. package/src/cli/commands/wallet.ts +13 -114
  10. package/src/cli/commands/zkid.ts +410 -0
  11. package/src/cli/index.ts +215 -9
  12. package/src/cli/prompts/searchMultiselect.ts +297 -0
  13. package/src/cli/types.ts +0 -138
  14. package/src/cli/utils/transaction.ts +1 -1
  15. package/src/cli/utils/validation.ts +0 -40
  16. package/src/cli/utils/wallet.ts +3 -1
  17. package/src/tests/helpers.ts +78 -0
  18. package/src/tests/skills.e2e.test.ts +126 -0
  19. package/src/tests/skills.test.ts +192 -0
  20. package/src/tests/test_skill.md +18 -0
  21. package/src/tests/zkid.e2e.test.ts +128 -0
  22. package/src/tests/zkid.test.ts +153 -0
  23. package/src/types/snarkjs.d.ts +4 -1
  24. package/dist/quest/nara_quest.json +0 -534
  25. package/dist/zk/answer_proof.wasm +0 -0
  26. package/dist/zk/answer_proof_final.zkey +0 -0
  27. package/src/cli/commands/config.ts +0 -125
  28. package/src/cli/commands/migrate.ts +0 -270
  29. package/src/cli/commands/pool.ts +0 -364
  30. package/src/cli/commands/swap.ts +0 -349
  31. package/src/cli/quest/nara_quest.json +0 -534
  32. package/src/cli/quest/nara_quest_types.ts +0 -540
  33. package/src/cli/zk/answer_proof.wasm +0 -0
  34. package/src/cli/zk/answer_proof_final.zkey +0 -0
  35. package/src/client.ts +0 -96
  36. package/src/config.ts +0 -132
  37. package/src/constants.ts +0 -35
  38. package/src/migrate.ts +0 -222
  39. package/src/pool.ts +0 -259
  40. package/src/quest.ts +0 -387
  41. package/src/swap.ts +0 -608
@@ -1,364 +0,0 @@
1
- /**
2
- * Pool commands
3
- */
4
-
5
- import { Command } from "commander";
6
- import { NaraSDK } from "../../client";
7
- import { DEFAULT_DBC_CONFIG_ADDRESS } from "../../constants";
8
- import {
9
- createPool,
10
- createPoolWithFirstBuy,
11
- getPoolInfo,
12
- getPoolProgress,
13
- } from "../../pool";
14
- import { loadWallet, getRpcUrl } from "../utils/wallet";
15
- import {
16
- validatePublicKey,
17
- validateRequired,
18
- validatePositiveNumber,
19
- validateNonNegativeNumber,
20
- } from "../utils/validation";
21
- import {
22
- handleTransaction,
23
- printTransactionResult,
24
- } from "../utils/transaction";
25
- import { formatOutput, printError, printInfo } from "../utils/output";
26
- import type {
27
- PoolCreateOptions,
28
- PoolCreateWithBuyOptions,
29
- PoolInfoOptions,
30
- } from "../types";
31
-
32
- /**
33
- * Register pool commands
34
- * @param program Commander program
35
- */
36
- export function registerPoolCommands(program: Command): void {
37
- const pool = program.command("pool").description("Pool management commands");
38
-
39
- // pool create
40
- pool
41
- .command("create")
42
- .description("Create a new token pool")
43
- .requiredOption("-n, --name <string>", "Token name")
44
- .requiredOption("-s, --symbol <string>", "Token symbol")
45
- .requiredOption("-u, --uri <string>", "Metadata URI")
46
- .requiredOption(
47
- "--dbc-config <address>",
48
- "DBC config address (or set DBC_CONFIG_ADDRESS env)"
49
- )
50
- .option("--creator <address>", "Pool creator address")
51
- .option("-e, --export-tx", "Export unsigned transaction", false)
52
- .action(async (options: PoolCreateOptions) => {
53
- try {
54
- await handlePoolCreate(options);
55
- } catch (error: any) {
56
- printError(error.message);
57
- process.exit(1);
58
- }
59
- });
60
-
61
- // pool create-with-buy
62
- pool
63
- .command("create-with-buy")
64
- .description("Create a new token pool with initial buy")
65
- .requiredOption("-n, --name <string>", "Token name")
66
- .requiredOption("-s, --symbol <string>", "Token symbol")
67
- .requiredOption("-u, --uri <string>", "Metadata URI")
68
- .requiredOption(
69
- "--dbc-config <address>",
70
- "DBC config address (or set DBC_CONFIG_ADDRESS env)"
71
- )
72
- .requiredOption("--amount <number>", "Initial buy amount in NSO")
73
- .option("--creator <address>", "Pool creator address")
74
- .option("--buyer <address>", "Buyer address")
75
- .option("--receiver <address>", "Token receiver address")
76
- .option("--slippage <number>", "Slippage in basis points", "100")
77
- .option("-e, --export-tx", "Export unsigned transaction", false)
78
- .action(async (options: PoolCreateWithBuyOptions) => {
79
- try {
80
- await handlePoolCreateWithBuy(options);
81
- } catch (error: any) {
82
- printError(error.message);
83
- process.exit(1);
84
- }
85
- });
86
-
87
- // pool info
88
- pool
89
- .command("info <token-address>")
90
- .description("Get pool information")
91
- .action(async (tokenAddress: string, options: PoolInfoOptions) => {
92
- try {
93
- await handlePoolInfo(tokenAddress, options);
94
- } catch (error: any) {
95
- printError(error.message);
96
- process.exit(1);
97
- }
98
- });
99
-
100
- // pool progress
101
- pool
102
- .command("progress <token-address>")
103
- .description("Get bonding curve progress")
104
- .action(async (tokenAddress: string, options: PoolInfoOptions) => {
105
- try {
106
- await handlePoolProgress(tokenAddress, options);
107
- } catch (error: any) {
108
- printError(error.message);
109
- process.exit(1);
110
- }
111
- });
112
- }
113
-
114
- /**
115
- * Handle pool create command
116
- * @param options Command options
117
- */
118
- async function handlePoolCreate(options: PoolCreateOptions): Promise<void> {
119
- // Load wallet
120
- const wallet = await loadWallet(options.wallet);
121
- const rpcUrl = getRpcUrl(options.rpcUrl);
122
-
123
- printInfo(`Using RPC: ${rpcUrl}`);
124
- printInfo(`Wallet: ${wallet.publicKey.toBase58()}`);
125
-
126
- // Initialize SDK
127
- const sdk = new NaraSDK({
128
- rpcUrl,
129
- commitment: "confirmed",
130
- });
131
-
132
- // Get config address from option or env
133
- const configAddress =
134
- options.dbcConfig || DEFAULT_DBC_CONFIG_ADDRESS;
135
- if (!configAddress) {
136
- throw new Error(
137
- "DBC config address is required. Use --dbc-config flag or set DBC_CONFIG_ADDRESS environment variable."
138
- );
139
- }
140
-
141
- // Parse addresses
142
- const configPubkey = validatePublicKey(configAddress);
143
- const creator = options.creator
144
- ? validatePublicKey(options.creator)
145
- : wallet.publicKey;
146
-
147
- printInfo("Creating token pool...");
148
-
149
- // Create pool
150
- const result = await createPool(sdk, {
151
- name: options.name,
152
- symbol: options.symbol,
153
- uri: options.uri,
154
- configAddress,
155
- payer: wallet.publicKey,
156
- poolCreator: creator,
157
- });
158
-
159
- printInfo(`Pool address: ${result.poolAddress}`);
160
- printInfo(`Token address: ${result.baseMint}`);
161
-
162
- // Handle transaction
163
- const txResult = await handleTransaction(
164
- sdk,
165
- result.transaction,
166
- [wallet, result.baseMintKeypair], // Both wallet and baseMint keypair need to sign
167
- options.exportTx || false
168
- );
169
-
170
- // Output result
171
- if (options.json) {
172
- const output = {
173
- poolAddress: result.poolAddress,
174
- tokenAddress: result.baseMint,
175
- ...(txResult.signature && { signature: txResult.signature }),
176
- ...(txResult.base64 && { transaction: txResult.base64 }),
177
- };
178
- console.log(JSON.stringify(output, null, 2));
179
- } else {
180
- console.log(`\nPool Address: ${result.poolAddress}`);
181
- console.log(`Token Address: ${result.baseMint}`);
182
- printTransactionResult(txResult, false);
183
-
184
- if (txResult.signature) {
185
- printInfo("\nSave this token address for buying/selling:");
186
- console.log(`export TOKEN_ADDRESS="${result.baseMint}"`);
187
- }
188
- }
189
- }
190
-
191
- /**
192
- * Handle pool create-with-buy command
193
- * @param options Command options
194
- */
195
- async function handlePoolCreateWithBuy(
196
- options: PoolCreateWithBuyOptions
197
- ): Promise<void> {
198
- // Load wallet
199
- const wallet = await loadWallet(options.wallet);
200
- const rpcUrl = getRpcUrl(options.rpcUrl);
201
-
202
- printInfo(`Using RPC: ${rpcUrl}`);
203
- printInfo(`Wallet: ${wallet.publicKey.toBase58()}`);
204
-
205
- // Initialize SDK
206
- const sdk = new NaraSDK({
207
- rpcUrl,
208
- commitment: "confirmed",
209
- });
210
-
211
- // Get config address from option or env
212
- const configAddress =
213
- options.dbcConfig || DEFAULT_DBC_CONFIG_ADDRESS;
214
- if (!configAddress) {
215
- throw new Error(
216
- "DBC config address is required. Use --dbc-config flag or set DBC_CONFIG_ADDRESS environment variable."
217
- );
218
- }
219
-
220
- // Parse addresses
221
- const configPubkey = validatePublicKey(configAddress);
222
- const creator = options.creator
223
- ? validatePublicKey(options.creator)
224
- : wallet.publicKey;
225
- const buyer = options.buyer
226
- ? validatePublicKey(options.buyer)
227
- : wallet.publicKey;
228
- const receiver = options.receiver
229
- ? validatePublicKey(options.receiver)
230
- : buyer;
231
-
232
- // Parse numbers
233
- const amount = validatePositiveNumber(options.amount, "amount");
234
- const slippage = validateNonNegativeNumber(
235
- options.slippage || "100",
236
- "slippage"
237
- );
238
-
239
- printInfo("Creating token pool with initial buy...");
240
- printInfo(`Initial buy amount: ${amount} NSO`);
241
-
242
- // Create pool with first buy
243
- const result = await createPoolWithFirstBuy(sdk, {
244
- name: options.name,
245
- symbol: options.symbol,
246
- uri: options.uri,
247
- configAddress,
248
- payer: wallet.publicKey,
249
- poolCreator: creator,
250
- initialBuyAmountSOL: amount,
251
- buyer,
252
- receiver,
253
- slippageBps: slippage,
254
- });
255
-
256
- printInfo(`Pool address: ${result.poolAddress}`);
257
- printInfo(`Token address: ${result.baseMint}`);
258
-
259
- // Handle transaction
260
- const txResult = await handleTransaction(
261
- sdk,
262
- result.createPoolTx,
263
- [wallet, result.baseMintKeypair], // Both wallet and baseMint keypair need to sign
264
- options.exportTx || false
265
- );
266
-
267
- // Output result
268
- if (options.json) {
269
- const output = {
270
- poolAddress: result.poolAddress,
271
- tokenAddress: result.baseMint,
272
- buyInfo: result.buyInfo,
273
- ...(txResult.signature && { signature: txResult.signature }),
274
- ...(txResult.base64 && { transaction: txResult.base64 }),
275
- };
276
- console.log(JSON.stringify(output, null, 2));
277
- } else {
278
- console.log(`\nPool Address: ${result.poolAddress}`);
279
- console.log(`Token Address: ${result.baseMint}`);
280
- console.log(`\nBuy Info:`);
281
- console.log(` Amount In: ${(parseInt(result.buyInfo.amountIn) / 1e9).toFixed(4)} NSO`);
282
- console.log(` Minimum Out: ${result.buyInfo.minimumAmountOut} tokens (smallest unit)`);
283
- printTransactionResult(txResult, false);
284
-
285
- if (txResult.signature) {
286
- printInfo("\nSave this token address for buying/selling:");
287
- console.log(`export TOKEN_ADDRESS="${result.baseMint}"`);
288
- }
289
- }
290
- }
291
-
292
- /**
293
- * Handle pool info command
294
- * @param tokenAddress Token address
295
- * @param options Command options
296
- */
297
- async function handlePoolInfo(
298
- tokenAddress: string,
299
- options: PoolInfoOptions
300
- ): Promise<void> {
301
- const rpcUrl = getRpcUrl(options.rpcUrl);
302
-
303
- printInfo(`Using RPC: ${rpcUrl}`);
304
-
305
- // Validate address
306
- validatePublicKey(tokenAddress);
307
-
308
- // Initialize SDK
309
- const sdk = new NaraSDK({
310
- rpcUrl,
311
- commitment: "confirmed",
312
- });
313
-
314
- printInfo("Fetching pool information...");
315
-
316
- // Get pool info
317
- const poolInfo = await getPoolInfo(sdk, tokenAddress);
318
-
319
- // Output result
320
- formatOutput(poolInfo, options.json || false);
321
- }
322
-
323
- /**
324
- * Handle pool progress command
325
- * @param tokenAddress Token address
326
- * @param options Command options
327
- */
328
- async function handlePoolProgress(
329
- tokenAddress: string,
330
- options: PoolInfoOptions
331
- ): Promise<void> {
332
- const rpcUrl = getRpcUrl(options.rpcUrl);
333
-
334
- printInfo(`Using RPC: ${rpcUrl}`);
335
-
336
- // Validate address
337
- validatePublicKey(tokenAddress);
338
-
339
- // Initialize SDK
340
- const sdk = new NaraSDK({
341
- rpcUrl,
342
- commitment: "confirmed",
343
- });
344
-
345
- printInfo("Fetching bonding curve progress...");
346
-
347
- // Get pool progress
348
- const progress = await getPoolProgress(sdk, tokenAddress);
349
-
350
- // Format progress as percentage for human-readable output
351
- const output = {
352
- progress: `${(progress.progress * 100).toFixed(2)}%`,
353
- progressRaw: progress.progress,
354
- quoteReserve: progress.quoteReserve,
355
- isMigrated: progress.isMigrated,
356
- };
357
-
358
- // Output result
359
- if (options.json) {
360
- console.log(JSON.stringify(progress, null, 2));
361
- } else {
362
- formatOutput(output, false);
363
- }
364
- }
@@ -1,349 +0,0 @@
1
- /**
2
- * Swap commands
3
- */
4
-
5
- import { Command } from "commander";
6
- import BN from "bn.js";
7
- import { NaraSDK } from "../../client";
8
- import { buyToken, sellToken, getSwapQuote, SwapMode } from "../../swap";
9
- import { loadWallet, getRpcUrl } from "../utils/wallet";
10
- import {
11
- validatePublicKey,
12
- validatePositiveNumber,
13
- validateNonNegativeNumber,
14
- validateSwapMode,
15
- validateDirection,
16
- } from "../utils/validation";
17
- import {
18
- handleTransaction,
19
- printTransactionResult,
20
- } from "../utils/transaction";
21
- import { formatOutput, printError, printInfo } from "../utils/output";
22
- import type {
23
- SwapBuyOptions,
24
- SwapSellOptions,
25
- SwapQuoteOptions,
26
- } from "../types";
27
-
28
- /**
29
- * Register swap commands
30
- * @param program Commander program
31
- */
32
- export function registerSwapCommands(program: Command): void {
33
- const swap = program.command("swap").description("Token swap commands");
34
-
35
- // swap buy
36
- swap
37
- .command("buy <token-address> <amount>")
38
- .description("Buy tokens with NSO")
39
- .option("--slippage <number>", "Slippage in basis points", "100")
40
- .option(
41
- "--mode <mode>",
42
- "Swap mode: exact-in|partial-fill|exact-out",
43
- "partial-fill"
44
- )
45
- .option("-e, --export-tx", "Export unsigned transaction", false)
46
- .action(
47
- async (
48
- tokenAddress: string,
49
- amount: string,
50
- options: Omit<SwapBuyOptions, "tokenAddress" | "amount">
51
- ) => {
52
- try {
53
- await handleSwapBuy(tokenAddress, amount, options);
54
- } catch (error: any) {
55
- printError(error.message);
56
- process.exit(1);
57
- }
58
- }
59
- );
60
-
61
- // swap sell
62
- swap
63
- .command("sell <token-address> <amount>")
64
- .description("Sell tokens for NSO")
65
- .option("--decimals <number>", "Token decimals", "6")
66
- .option("--slippage <number>", "Slippage in basis points", "100")
67
- .option(
68
- "--mode <mode>",
69
- "Swap mode: exact-in|partial-fill|exact-out",
70
- "partial-fill"
71
- )
72
- .option("-e, --export-tx", "Export unsigned transaction", false)
73
- .action(
74
- async (
75
- tokenAddress: string,
76
- amount: string,
77
- options: Omit<SwapSellOptions, "tokenAddress" | "amount">
78
- ) => {
79
- try {
80
- await handleSwapSell(tokenAddress, amount, options);
81
- } catch (error: any) {
82
- printError(error.message);
83
- process.exit(1);
84
- }
85
- }
86
- );
87
-
88
- // swap quote
89
- swap
90
- .command("quote <token-address> <amount> <direction>")
91
- .description("Get swap quote (direction: buy|sell)")
92
- .option("--decimals <number>", "Token decimals (for sell only)", "6")
93
- .option("--slippage <number>", "Slippage in basis points", "100")
94
- .action(
95
- async (
96
- tokenAddress: string,
97
- amount: string,
98
- direction: string,
99
- options: Omit<SwapQuoteOptions, "tokenAddress" | "amount" | "direction">
100
- ) => {
101
- try {
102
- await handleSwapQuote(tokenAddress, amount, direction, options);
103
- } catch (error: any) {
104
- printError(error.message);
105
- process.exit(1);
106
- }
107
- }
108
- );
109
- }
110
-
111
- /**
112
- * Convert swap mode string to enum
113
- * @param mode Mode string
114
- * @returns SwapMode enum value
115
- */
116
- function parseSwapMode(mode: string): SwapMode {
117
- const normalized = validateSwapMode(mode);
118
- switch (normalized) {
119
- case "exact-in":
120
- return SwapMode.ExactIn;
121
- case "partial-fill":
122
- return SwapMode.PartialFill;
123
- case "exact-out":
124
- return SwapMode.ExactOut;
125
- default:
126
- return SwapMode.PartialFill;
127
- }
128
- }
129
-
130
- /**
131
- * Handle swap buy command
132
- * @param tokenAddress Token address
133
- * @param amount Amount in SOL
134
- * @param options Command options
135
- */
136
- async function handleSwapBuy(
137
- tokenAddress: string,
138
- amount: string,
139
- options: Omit<SwapBuyOptions, "tokenAddress" | "amount">
140
- ): Promise<void> {
141
- // Load wallet
142
- const wallet = await loadWallet(options.wallet);
143
- const rpcUrl = getRpcUrl(options.rpcUrl);
144
-
145
- printInfo(`Using RPC: ${rpcUrl}`);
146
- printInfo(`Wallet: ${wallet.publicKey.toBase58()}`);
147
-
148
- // Validate inputs
149
- validatePublicKey(tokenAddress);
150
- const amountInSOL = validatePositiveNumber(amount, "amount");
151
- const slippage = validateNonNegativeNumber(
152
- options.slippage || "100",
153
- "slippage"
154
- );
155
- const swapMode = parseSwapMode(options.mode || "partial-fill");
156
-
157
- // Initialize SDK
158
- const sdk = new NaraSDK({
159
- rpcUrl,
160
- commitment: "confirmed",
161
- });
162
-
163
- printInfo(`Buying tokens with ${amountInSOL} NSO...`);
164
-
165
- // Buy tokens
166
- const result = await buyToken(sdk, {
167
- tokenAddress,
168
- amountInSOL,
169
- owner: wallet.publicKey,
170
- slippageBps: slippage,
171
- swapMode,
172
- });
173
-
174
- printInfo(`Expected output: ${result.expectedAmountOut} tokens (smallest unit)`);
175
- printInfo(`Minimum output: ${result.minimumAmountOut} tokens (smallest unit)`);
176
-
177
- // Handle transaction
178
- const txResult = await handleTransaction(
179
- sdk,
180
- result.transaction,
181
- [wallet],
182
- options.exportTx || false
183
- );
184
-
185
- // Output result
186
- if (options.json) {
187
- const output = {
188
- amountIn: result.amountIn,
189
- expectedAmountOut: result.expectedAmountOut,
190
- minimumAmountOut: result.minimumAmountOut,
191
- ...(txResult.signature && { signature: txResult.signature }),
192
- ...(txResult.base64 && { transaction: txResult.base64 }),
193
- };
194
- console.log(JSON.stringify(output, null, 2));
195
- } else {
196
- console.log(`\nSwap Details:`);
197
- console.log(` Input: ${(parseInt(result.amountIn) / 1e9).toFixed(4)} NSO`);
198
- console.log(` Expected Output: ${result.expectedAmountOut} tokens`);
199
- console.log(` Minimum Output: ${result.minimumAmountOut} tokens`);
200
- printTransactionResult(txResult, false);
201
- }
202
- }
203
-
204
- /**
205
- * Handle swap sell command
206
- * @param tokenAddress Token address
207
- * @param amount Amount in tokens
208
- * @param options Command options
209
- */
210
- async function handleSwapSell(
211
- tokenAddress: string,
212
- amount: string,
213
- options: Omit<SwapSellOptions, "tokenAddress" | "amount">
214
- ): Promise<void> {
215
- // Load wallet
216
- const wallet = await loadWallet(options.wallet);
217
- const rpcUrl = getRpcUrl(options.rpcUrl);
218
-
219
- printInfo(`Using RPC: ${rpcUrl}`);
220
- printInfo(`Wallet: ${wallet.publicKey.toBase58()}`);
221
-
222
- // Validate inputs
223
- validatePublicKey(tokenAddress);
224
- const amountInToken = validatePositiveNumber(amount, "amount");
225
- const decimals = parseInt(String(options.decimals || "6"));
226
- const slippage = validateNonNegativeNumber(
227
- options.slippage || "100",
228
- "slippage"
229
- );
230
- const swapMode = parseSwapMode(options.mode || "partial-fill");
231
-
232
- // Initialize SDK
233
- const sdk = new NaraSDK({
234
- rpcUrl,
235
- commitment: "confirmed",
236
- });
237
-
238
- printInfo(`Selling ${amountInToken} tokens...`);
239
-
240
- // Sell tokens
241
- const result = await sellToken(sdk, {
242
- tokenAddress,
243
- amountInToken,
244
- owner: wallet.publicKey,
245
- tokenDecimals: decimals,
246
- slippageBps: slippage,
247
- swapMode,
248
- });
249
-
250
- printInfo(`Expected output: ${(parseInt(result.expectedAmountOut) / 1e9).toFixed(4)} NSO`);
251
- printInfo(`Minimum output: ${(parseInt(result.minimumAmountOut) / 1e9).toFixed(4)} NSO`);
252
-
253
- // Handle transaction
254
- const txResult = await handleTransaction(
255
- sdk,
256
- result.transaction,
257
- [wallet],
258
- options.exportTx || false
259
- );
260
-
261
- // Output result
262
- if (options.json) {
263
- const output = {
264
- amountIn: result.amountIn,
265
- expectedAmountOut: result.expectedAmountOut,
266
- minimumAmountOut: result.minimumAmountOut,
267
- ...(txResult.signature && { signature: txResult.signature }),
268
- ...(txResult.base64 && { transaction: txResult.base64 }),
269
- };
270
- console.log(JSON.stringify(output, null, 2));
271
- } else {
272
- console.log(`\nSwap Details:`);
273
- console.log(` Input: ${result.amountIn} tokens`);
274
- console.log(` Expected Output: ${(parseInt(result.expectedAmountOut) / 1e9).toFixed(4)} NSO`);
275
- console.log(` Minimum Output: ${(parseInt(result.minimumAmountOut) / 1e9).toFixed(4)} NSO`);
276
- printTransactionResult(txResult, false);
277
- }
278
- }
279
-
280
- /**
281
- * Handle swap quote command
282
- * @param tokenAddress Token address
283
- * @param amount Amount
284
- * @param direction Direction (buy or sell)
285
- * @param options Command options
286
- */
287
- async function handleSwapQuote(
288
- tokenAddress: string,
289
- amount: string,
290
- direction: string,
291
- options: Omit<SwapQuoteOptions, "tokenAddress" | "amount" | "direction">
292
- ): Promise<void> {
293
- const rpcUrl = getRpcUrl(options.rpcUrl);
294
-
295
- printInfo(`Using RPC: ${rpcUrl}`);
296
-
297
- // Validate inputs
298
- validatePublicKey(tokenAddress);
299
- const amountNum = validatePositiveNumber(amount, "amount");
300
- const dir = validateDirection(direction);
301
- const decimals = parseInt(String(options.decimals || "6"));
302
- const slippage = validateNonNegativeNumber(
303
- options.slippage || "100",
304
- "slippage"
305
- );
306
-
307
- // Initialize SDK
308
- const sdk = new NaraSDK({
309
- rpcUrl,
310
- commitment: "confirmed",
311
- });
312
-
313
- // Prepare parameters based on direction
314
- const swapBaseForQuote = dir === "sell";
315
- const amountIn = swapBaseForQuote
316
- ? new BN(amountNum * 10 ** decimals) // Tokens to smallest unit
317
- : new BN(amountNum * 1e9); // SOL to lamports
318
-
319
- printInfo(`Getting ${dir} quote for ${amountNum} ${swapBaseForQuote ? "tokens" : "NSO"}...`);
320
-
321
- // Get quote
322
- const quote = await getSwapQuote(
323
- sdk,
324
- tokenAddress,
325
- amountIn,
326
- swapBaseForQuote,
327
- slippage
328
- );
329
-
330
- // Output result
331
- if (options.json) {
332
- console.log(JSON.stringify(quote, null, 2));
333
- } else {
334
- console.log(`\nQuote:`);
335
- if (swapBaseForQuote) {
336
- // Selling tokens for SOL
337
- console.log(` Input: ${(parseInt(quote.amountIn) / 10 ** decimals).toFixed(4)} tokens`);
338
- console.log(` Expected Output: ${(parseInt(quote.outputAmount) / 1e9).toFixed(4)} NSO`);
339
- console.log(` Minimum Output: ${(parseInt(quote.minimumAmountOut) / 1e9).toFixed(4)} NSO`);
340
- } else {
341
- // Buying tokens with NSO
342
- console.log(` Input: ${(parseInt(quote.amountIn) / 1e9).toFixed(4)} NSO`);
343
- console.log(` Expected Output: ${quote.outputAmount} tokens (smallest unit)`);
344
- console.log(` Minimum Output: ${quote.minimumAmountOut} tokens (smallest unit)`);
345
- }
346
- console.log(` Trading Fee: ${quote.tradingFee}`);
347
- console.log(` Protocol Fee: ${quote.protocolFee}`);
348
- }
349
- }