minara 0.4.4 → 0.4.6
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/commands/deposit.js +16 -64
- package/dist/commands/discover.js +38 -11
- package/package.json +1 -1
package/dist/commands/deposit.js
CHANGED
|
@@ -5,7 +5,7 @@ import { getAccount } from '../api/crosschain.js';
|
|
|
5
5
|
import { getCurrentUser } from '../api/auth.js';
|
|
6
6
|
import * as perpsApi from '../api/perps.js';
|
|
7
7
|
import { requireAuth } from '../config.js';
|
|
8
|
-
import { info, success,
|
|
8
|
+
import { info, success, spinner, assertApiOk, wrapAction, requireTransactionConfirmation } from '../utils.js';
|
|
9
9
|
import { requireTouchId } from '../touchid.js';
|
|
10
10
|
import { printTxResult } from '../formatters.js';
|
|
11
11
|
const EVM_CHAINS = 'Ethereum, Base, Arbitrum, Optimism, Polygon, Avalanche, BSC, Berachain, Blast';
|
|
@@ -52,65 +52,27 @@ async function showSpotDeposit(token) {
|
|
|
52
52
|
console.log(chalk.red(' • Sending tokens on the wrong network may result in permanent loss.'));
|
|
53
53
|
console.log('');
|
|
54
54
|
}
|
|
55
|
-
// ─── moonpay (credit card on-ramp) ───────────────────────────────────────
|
|
56
|
-
const MOONPAY_PK = process.env.MOONPAY_PK ?? 'pk_live_yIf64w79W6ufwip4j51PWbymdwGtI';
|
|
57
|
-
const MOONPAY_CURRENCIES = [
|
|
58
|
-
{ name: 'USDC (Base)', code: 'usdc_base', network: 'base' },
|
|
59
|
-
{ name: 'USDC (Ethereum)', code: 'usdc', network: 'ethereum' },
|
|
60
|
-
{ name: 'USDC (Arbitrum)', code: 'usdc_arbitrum', network: 'arbitrum' },
|
|
61
|
-
{ name: 'USDC (Polygon)', code: 'usdc_polygon', network: 'polygon' },
|
|
62
|
-
{ name: 'ETH', code: 'eth', network: 'ethereum' },
|
|
63
|
-
{ name: 'ETH (Base)', code: 'eth_base', network: 'base' },
|
|
64
|
-
{ name: 'SOL', code: 'sol', network: 'solana' },
|
|
65
|
-
];
|
|
66
|
-
async function moonPayOnRamp(token) {
|
|
67
|
-
const addrSpin = spinner('Fetching wallet address…');
|
|
68
|
-
const accountRes = await getAccount(token);
|
|
69
|
-
addrSpin.stop();
|
|
70
|
-
const account = accountRes.data;
|
|
71
|
-
const evmAddr = account?.evmAddress;
|
|
72
|
-
const solAddr = account?.solanaAddress;
|
|
73
|
-
if (!evmAddr && !solAddr) {
|
|
74
|
-
warn('No wallet address found. Your account may not be fully initialized.');
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
const currency = await select({
|
|
78
|
-
message: 'Currency to buy:',
|
|
79
|
-
choices: MOONPAY_CURRENCIES
|
|
80
|
-
.filter((c) => c.network === 'solana' ? !!solAddr : !!evmAddr)
|
|
81
|
-
.map((c) => ({ name: c.name, value: c })),
|
|
82
|
-
});
|
|
83
|
-
const walletAddress = currency.network === 'solana' ? solAddr : evmAddr;
|
|
84
|
-
// Build MoonPay buy URL (no walletAddress in URL — requires server-side signing
|
|
85
|
-
// which depends on backend MoonPay secret key config).
|
|
86
|
-
// User pastes their address in MoonPay's form instead.
|
|
87
|
-
const params = new URLSearchParams();
|
|
88
|
-
params.set('apiKey', MOONPAY_PK);
|
|
89
|
-
params.set('currencyCode', currency.code);
|
|
90
|
-
params.set('defaultCurrencyCode', currency.code);
|
|
91
|
-
const buyUrl = `https://buy.moonpay.com?${params.toString()}`;
|
|
92
|
-
console.log('');
|
|
93
|
-
console.log(chalk.bold('Buy Crypto with Credit Card (MoonPay)'));
|
|
94
|
-
console.log('');
|
|
95
|
-
console.log(` Currency : ${chalk.cyan(currency.name)}`);
|
|
96
|
-
console.log(` Wallet : ${chalk.yellow(walletAddress)}`);
|
|
97
|
-
console.log(chalk.dim(' ↑ Copy this address and paste it in MoonPay when prompted.'));
|
|
98
|
-
console.log('');
|
|
99
|
-
info('Opening MoonPay in your browser…');
|
|
100
|
-
openBrowser(buyUrl);
|
|
101
|
-
console.log(chalk.dim(' Complete the purchase in your browser. Funds will arrive in your Minara wallet.'));
|
|
102
|
-
console.log('');
|
|
103
|
-
}
|
|
104
55
|
// ─── perps ───────────────────────────────────────────────────────────────
|
|
105
56
|
const perpsCmd = new Command('perps')
|
|
106
57
|
.description('Deposit USDC to perps account')
|
|
107
|
-
.option('-a, --amount <amount>', 'USDC amount (for transfer)')
|
|
58
|
+
.option('-a, --amount <amount>', 'USDC amount (for transfer from Spot)')
|
|
59
|
+
.option('--address', 'Show perps deposit address (non-interactive)')
|
|
108
60
|
.option('-y, --yes', 'Skip confirmation')
|
|
109
61
|
.action(wrapAction(async (opts) => {
|
|
110
62
|
const creds = requireAuth();
|
|
111
63
|
await perpsDepositFlow(creds.accessToken, opts);
|
|
112
64
|
}));
|
|
113
65
|
async function perpsDepositFlow(token, opts) {
|
|
66
|
+
// Non-interactive mode: --address or --amount specified
|
|
67
|
+
if (opts?.address) {
|
|
68
|
+
await showPerpsDepositAddresses(token);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (opts?.amount) {
|
|
72
|
+
await transferSpotToPerps(token, opts);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
// Interactive mode
|
|
114
76
|
const method = await select({
|
|
115
77
|
message: 'How would you like to deposit to perps?',
|
|
116
78
|
choices: [
|
|
@@ -177,35 +139,25 @@ async function transferSpotToPerps(token, opts) {
|
|
|
177
139
|
printTxResult(res.data);
|
|
178
140
|
}
|
|
179
141
|
// ─── parent ──────────────────────────────────────────────────────────────
|
|
180
|
-
const buyCmd = new Command('buy')
|
|
181
|
-
.description('Buy crypto with credit card via MoonPay')
|
|
182
|
-
.action(wrapAction(async () => {
|
|
183
|
-
const creds = requireAuth();
|
|
184
|
-
await moonPayOnRamp(creds.accessToken);
|
|
185
|
-
}));
|
|
186
142
|
export const depositCommand = new Command('deposit')
|
|
187
143
|
.alias('receive')
|
|
188
|
-
.description('Deposit / receive to spot wallet or perps account
|
|
144
|
+
.description('Deposit / receive to spot wallet or perps account')
|
|
189
145
|
.addCommand(spotCmd)
|
|
190
146
|
.addCommand(perpsCmd)
|
|
191
|
-
.
|
|
147
|
+
.allowExcessArguments(false)
|
|
192
148
|
.action(wrapAction(async () => {
|
|
193
149
|
const action = await select({
|
|
194
150
|
message: 'Deposit to:',
|
|
195
151
|
choices: [
|
|
196
152
|
{ name: 'Spot wallet — view deposit addresses', value: 'spot' },
|
|
197
153
|
{ name: 'Perps wallet — view deposit address or transfer from Spot', value: 'perps' },
|
|
198
|
-
{ name: `Buy crypto with credit card ${chalk.dim('(MoonPay)')}`, value: 'buy' },
|
|
199
154
|
],
|
|
200
155
|
});
|
|
201
156
|
const creds = requireAuth();
|
|
202
157
|
if (action === 'spot') {
|
|
203
158
|
await showSpotDeposit(creds.accessToken);
|
|
204
159
|
}
|
|
205
|
-
else if (action === 'perps') {
|
|
206
|
-
await perpsDepositFlow(creds.accessToken);
|
|
207
|
-
}
|
|
208
160
|
else {
|
|
209
|
-
await
|
|
161
|
+
await perpsDepositFlow(creds.accessToken);
|
|
210
162
|
}
|
|
211
163
|
}));
|
|
@@ -21,9 +21,21 @@ function flattenStock(item) {
|
|
|
21
21
|
const trendingCmd = new Command('trending')
|
|
22
22
|
.description('View trending tokens or stocks')
|
|
23
23
|
.argument('[category]', 'tokens or stocks (default: interactive)')
|
|
24
|
-
.
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
.option('-t, --type <category>', 'Trending type: tokens or stocks (skips interactive prompt)')
|
|
25
|
+
.action(wrapAction(async (categoryArg, options) => {
|
|
26
|
+
let category;
|
|
27
|
+
const typeOpt = options?.type?.toLowerCase() || categoryArg?.toLowerCase();
|
|
28
|
+
if (typeOpt === 'tokens' || typeOpt === 'token') {
|
|
29
|
+
category = 'tokens';
|
|
30
|
+
}
|
|
31
|
+
else if (typeOpt === 'stocks' || typeOpt === 'stock') {
|
|
32
|
+
category = 'stocks';
|
|
33
|
+
}
|
|
34
|
+
else if (!process.stdin.isTTY) {
|
|
35
|
+
// Non-interactive mode: default to tokens
|
|
36
|
+
category = 'tokens';
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
27
39
|
category = await select({
|
|
28
40
|
message: 'Trending:',
|
|
29
41
|
choices: [
|
|
@@ -68,15 +80,30 @@ const trendingCmd = new Command('trending')
|
|
|
68
80
|
const searchCmd = new Command('search')
|
|
69
81
|
.description('Search for tokens or stocks')
|
|
70
82
|
.argument('[keyword]', 'Search keyword')
|
|
71
|
-
.
|
|
83
|
+
.option('-t, --type <category>', 'Search type: tokens or stocks (skips interactive prompt)')
|
|
84
|
+
.action(wrapAction(async (keywordArg, options) => {
|
|
72
85
|
const keyword = keywordArg ?? await input({ message: 'Search keyword:' });
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
86
|
+
let category;
|
|
87
|
+
const typeOpt = options?.type?.toLowerCase();
|
|
88
|
+
if (typeOpt === 'tokens' || typeOpt === 'token') {
|
|
89
|
+
category = 'tokens';
|
|
90
|
+
}
|
|
91
|
+
else if (typeOpt === 'stocks' || typeOpt === 'stock') {
|
|
92
|
+
category = 'stocks';
|
|
93
|
+
}
|
|
94
|
+
else if (!process.stdin.isTTY) {
|
|
95
|
+
// Non-interactive mode: default to tokens
|
|
96
|
+
category = 'tokens';
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
category = await select({
|
|
100
|
+
message: 'Search in:',
|
|
101
|
+
choices: [
|
|
102
|
+
{ name: 'Tokens (crypto)', value: 'tokens' },
|
|
103
|
+
{ name: 'Stocks', value: 'stocks' },
|
|
104
|
+
],
|
|
105
|
+
});
|
|
106
|
+
}
|
|
80
107
|
const spin = spinner(`Searching ${category}…`);
|
|
81
108
|
const res = category === 'tokens'
|
|
82
109
|
? await searchTokens(keyword)
|