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.
- package/README.md +101 -114
- package/bin/nara-cli.ts +0 -20
- package/dist/nara-cli.mjs +49930 -2222
- package/index.ts +10 -58
- package/package.json +7 -6
- package/src/cli/commands/quest.ts +8 -7
- package/src/cli/commands/skills.ts +491 -0
- package/src/cli/commands/skillsInstall.ts +793 -0
- package/src/cli/commands/wallet.ts +13 -114
- package/src/cli/commands/zkid.ts +410 -0
- package/src/cli/index.ts +215 -9
- package/src/cli/prompts/searchMultiselect.ts +297 -0
- package/src/cli/types.ts +0 -138
- package/src/cli/utils/transaction.ts +1 -1
- package/src/cli/utils/validation.ts +0 -40
- package/src/cli/utils/wallet.ts +3 -1
- package/src/tests/helpers.ts +78 -0
- package/src/tests/skills.e2e.test.ts +126 -0
- package/src/tests/skills.test.ts +192 -0
- package/src/tests/test_skill.md +18 -0
- package/src/tests/zkid.e2e.test.ts +128 -0
- package/src/tests/zkid.test.ts +153 -0
- package/src/types/snarkjs.d.ts +4 -1
- package/dist/quest/nara_quest.json +0 -534
- package/dist/zk/answer_proof.wasm +0 -0
- package/dist/zk/answer_proof_final.zkey +0 -0
- package/src/cli/commands/config.ts +0 -125
- package/src/cli/commands/migrate.ts +0 -270
- package/src/cli/commands/pool.ts +0 -364
- package/src/cli/commands/swap.ts +0 -349
- package/src/cli/quest/nara_quest.json +0 -534
- package/src/cli/quest/nara_quest_types.ts +0 -540
- package/src/cli/zk/answer_proof.wasm +0 -0
- package/src/cli/zk/answer_proof_final.zkey +0 -0
- package/src/client.ts +0 -96
- package/src/config.ts +0 -132
- package/src/constants.ts +0 -35
- package/src/migrate.ts +0 -222
- package/src/pool.ts +0 -259
- package/src/quest.ts +0 -387
- package/src/swap.ts +0 -608
package/src/cli/commands/pool.ts
DELETED
|
@@ -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
|
-
}
|
package/src/cli/commands/swap.ts
DELETED
|
@@ -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
|
-
}
|