naracli 0.1.0
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 +155 -0
- package/bin/nara-cli.ts +32 -0
- package/dist/nara-cli.mjs +2631 -0
- package/dist/quest/nara_quest.json +534 -0
- package/dist/zk/answer_proof.wasm +0 -0
- package/dist/zk/answer_proof_final.zkey +0 -0
- package/index.ts +76 -0
- package/package.json +54 -0
- package/src/cli/commands/config.ts +125 -0
- package/src/cli/commands/migrate.ts +270 -0
- package/src/cli/commands/pool.ts +364 -0
- package/src/cli/commands/quest.ts +312 -0
- package/src/cli/commands/swap.ts +349 -0
- package/src/cli/commands/wallet.ts +719 -0
- package/src/cli/index.ts +25 -0
- package/src/cli/quest/nara_quest.json +534 -0
- package/src/cli/quest/nara_quest_types.ts +540 -0
- package/src/cli/types.ts +207 -0
- package/src/cli/utils/output.ts +110 -0
- package/src/cli/utils/transaction.ts +146 -0
- package/src/cli/utils/validation.ts +120 -0
- package/src/cli/utils/wallet.ts +72 -0
- package/src/cli/zk/answer_proof.wasm +0 -0
- package/src/cli/zk/answer_proof_final.zkey +0 -0
- package/src/client.ts +96 -0
- package/src/config.ts +132 -0
- package/src/constants.ts +29 -0
- package/src/migrate.ts +222 -0
- package/src/pool.ts +259 -0
- package/src/quest.ts +379 -0
- package/src/swap.ts +608 -0
- package/src/types/snarkjs.d.ts +9 -0
|
@@ -0,0 +1,349 @@
|
|
|
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
|
+
}
|