minara 0.2.0 → 0.2.2
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 +33 -41
- package/dist/api/perps.d.ts +2 -2
- package/dist/api/perps.js +4 -4
- package/dist/commands/assets.js +77 -54
- package/dist/commands/balance.d.ts +2 -0
- package/dist/commands/balance.js +43 -0
- package/dist/commands/deposit.js +129 -62
- package/dist/commands/limit-order.js +1 -1
- package/dist/commands/premium.js +25 -6
- package/dist/commands/swap.js +59 -35
- package/dist/commands/transfer.js +4 -20
- package/dist/commands/withdraw.js +4 -24
- package/dist/formatters.d.ts +2 -4
- package/dist/formatters.js +31 -23
- package/dist/index.js +3 -3
- package/dist/types.d.ts +0 -35
- package/dist/utils.d.ts +12 -1
- package/dist/utils.js +129 -9
- package/package.json +1 -1
package/dist/utils.js
CHANGED
|
@@ -115,6 +115,83 @@ export function wrapAction(fn) {
|
|
|
115
115
|
}
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
|
+
const NATIVE_TOKEN_ADDRESS = {
|
|
119
|
+
sol: 'So11111111111111111111111111111111111111112',
|
|
120
|
+
solana: 'So11111111111111111111111111111111111111112',
|
|
121
|
+
};
|
|
122
|
+
const EVM_NATIVE = '0x' + '0'.repeat(40);
|
|
123
|
+
function resolveNativeAddress(chain) {
|
|
124
|
+
if (!chain)
|
|
125
|
+
return EVM_NATIVE;
|
|
126
|
+
return NATIVE_TOKEN_ADDRESS[chain.toLowerCase()] ?? EVM_NATIVE;
|
|
127
|
+
}
|
|
128
|
+
const CHAIN_ALIAS = {
|
|
129
|
+
sol: 'solana',
|
|
130
|
+
eth: 'ethereum',
|
|
131
|
+
arb: 'arbitrum',
|
|
132
|
+
op: 'optimism',
|
|
133
|
+
matic: 'polygon',
|
|
134
|
+
poly: 'polygon',
|
|
135
|
+
avax: 'avalanche',
|
|
136
|
+
bnb: 'bsc',
|
|
137
|
+
bera: 'berachain',
|
|
138
|
+
// Numeric chain IDs returned by the token search API
|
|
139
|
+
'101': 'solana',
|
|
140
|
+
'1': 'ethereum',
|
|
141
|
+
'8453': 'base',
|
|
142
|
+
'42161': 'arbitrum',
|
|
143
|
+
'10': 'optimism',
|
|
144
|
+
'56': 'bsc',
|
|
145
|
+
'137': 'polygon',
|
|
146
|
+
'43114': 'avalanche',
|
|
147
|
+
'81457': 'blast',
|
|
148
|
+
'169': 'manta',
|
|
149
|
+
'34443': 'mode',
|
|
150
|
+
'146': 'sonic',
|
|
151
|
+
'80094': 'berachain',
|
|
152
|
+
'196': 'xlayer',
|
|
153
|
+
'4200': 'merlin',
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Normalize a chain identifier from the token search API to a supported
|
|
157
|
+
* `Chain` value used by the swap / transfer APIs.
|
|
158
|
+
*/
|
|
159
|
+
export function normalizeChain(raw) {
|
|
160
|
+
if (!raw)
|
|
161
|
+
return undefined;
|
|
162
|
+
const lower = raw.toLowerCase();
|
|
163
|
+
if (SUPPORTED_CHAINS.includes(lower))
|
|
164
|
+
return lower;
|
|
165
|
+
return CHAIN_ALIAS[lower];
|
|
166
|
+
}
|
|
167
|
+
/** Capitalize chain name for display (e.g. "solana" → "Solana", "bsc" → "BSC"). */
|
|
168
|
+
function displayChain(raw) {
|
|
169
|
+
const name = normalizeChain(raw) ?? raw ?? 'unknown';
|
|
170
|
+
if (name === 'bsc')
|
|
171
|
+
return 'BSC';
|
|
172
|
+
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
173
|
+
}
|
|
174
|
+
/** Lower = cheaper gas. Used to sort chain choices so the cheapest is first. */
|
|
175
|
+
const CHAIN_GAS_RANK = {
|
|
176
|
+
sol: 1, solana: 1, '101': 1,
|
|
177
|
+
base: 2, '8453': 2,
|
|
178
|
+
arbitrum: 3, arb: 3, '42161': 3,
|
|
179
|
+
optimism: 4, op: 4, '10': 4,
|
|
180
|
+
bsc: 5, bnb: 5, '56': 5,
|
|
181
|
+
polygon: 6, matic: 6, poly: 6, '137': 6,
|
|
182
|
+
sonic: 7, '146': 7,
|
|
183
|
+
avalanche: 8, avax: 8, '43114': 8,
|
|
184
|
+
berachain: 9, bera: 9, '80094': 9,
|
|
185
|
+
blast: 10, '81457': 10,
|
|
186
|
+
manta: 11, '169': 11,
|
|
187
|
+
mode: 12, '34443': 12,
|
|
188
|
+
ethereum: 50, eth: 50, '1': 50,
|
|
189
|
+
};
|
|
190
|
+
function chainGasRank(chain) {
|
|
191
|
+
if (!chain)
|
|
192
|
+
return 99;
|
|
193
|
+
return CHAIN_GAS_RANK[chain.toLowerCase()] ?? 30;
|
|
194
|
+
}
|
|
118
195
|
/**
|
|
119
196
|
* Look up token metadata by address, ticker, or name.
|
|
120
197
|
*
|
|
@@ -149,25 +226,54 @@ export async function lookupToken(tokenInput) {
|
|
|
149
226
|
if (!isTicker) {
|
|
150
227
|
const exact = tokens.find((t) => t.address?.toLowerCase() === keyword.toLowerCase());
|
|
151
228
|
if (exact) {
|
|
152
|
-
return { symbol: exact.symbol, name: exact.name, address: exact.address ??
|
|
229
|
+
return { symbol: exact.symbol, name: exact.name ?? displayChain(exact.chain), address: exact.address ?? resolveNativeAddress(exact.chain), chain: exact.chain };
|
|
153
230
|
}
|
|
154
231
|
}
|
|
155
232
|
if (tokens.length === 1) {
|
|
156
233
|
const t = tokens[0];
|
|
157
|
-
return { symbol: t.symbol, name: t.name, address: t.address ??
|
|
234
|
+
return { symbol: t.symbol, name: t.name ?? displayChain(t.chain), address: t.address ?? resolveNativeAddress(t.chain), chain: t.chain };
|
|
235
|
+
}
|
|
236
|
+
// Check if all results share the same symbol → multi-chain scenario
|
|
237
|
+
const uniqueSymbols = new Set(tokens.map((t) => t.symbol?.toLowerCase()));
|
|
238
|
+
if (uniqueSymbols.size === 1) {
|
|
239
|
+
const sorted = [...tokens].sort((a, b) => chainGasRank(a.chain) - chainGasRank(b.chain));
|
|
240
|
+
info(`$${sorted[0].symbol} is available on ${sorted.length} chains`);
|
|
241
|
+
const selected = await select({
|
|
242
|
+
message: 'Select chain:',
|
|
243
|
+
choices: sorted.map((t, i) => {
|
|
244
|
+
const chainName = displayChain(t.chain);
|
|
245
|
+
const addr = t.address
|
|
246
|
+
? chalk.dim(` · ${t.address.slice(0, 10)}…${t.address.slice(-6)}`)
|
|
247
|
+
: chalk.dim(' · native token');
|
|
248
|
+
const tag = i === 0 ? chalk.green(' (lowest gas)') : '';
|
|
249
|
+
return { name: `${chalk.cyan(chainName)}${tag}${addr}`, value: t };
|
|
250
|
+
}),
|
|
251
|
+
});
|
|
252
|
+
return {
|
|
253
|
+
symbol: selected.symbol,
|
|
254
|
+
name: selected.name ?? displayChain(selected.chain),
|
|
255
|
+
address: selected.address ?? resolveNativeAddress(selected.chain),
|
|
256
|
+
chain: selected.chain,
|
|
257
|
+
};
|
|
158
258
|
}
|
|
159
259
|
info(`Found ${tokens.length} tokens matching "${tokenInput}"`);
|
|
160
260
|
const selected = await select({
|
|
161
261
|
message: 'Select the correct token:',
|
|
162
|
-
choices: tokens.map((t) =>
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
262
|
+
choices: tokens.map((t) => {
|
|
263
|
+
const sym = t.symbol ? chalk.bold('$' + t.symbol) : '?';
|
|
264
|
+
const chainName = displayChain(t.chain);
|
|
265
|
+
const label = t.name || chainName;
|
|
266
|
+
const desc = label ? ` — ${label}` : '';
|
|
267
|
+
const chainTag = chainName && chainName !== label ? chalk.dim(` [${chainName}]`) : '';
|
|
268
|
+
const addr = t.address ? `\n ${chalk.yellow(t.address)}` : chalk.dim('\n (native token)');
|
|
269
|
+
return { name: `${sym}${desc}${chainTag}${addr}`, value: t };
|
|
270
|
+
}),
|
|
166
271
|
});
|
|
167
272
|
return {
|
|
168
273
|
symbol: selected.symbol,
|
|
169
|
-
name: selected.name,
|
|
170
|
-
address: selected.address ??
|
|
274
|
+
name: selected.name ?? displayChain(selected.chain),
|
|
275
|
+
address: selected.address ?? resolveNativeAddress(selected.chain),
|
|
276
|
+
chain: selected.chain,
|
|
171
277
|
};
|
|
172
278
|
}
|
|
173
279
|
catch {
|
|
@@ -197,18 +303,32 @@ export function formatTokenLabel(token) {
|
|
|
197
303
|
*
|
|
198
304
|
* Exits the process if the user declines.
|
|
199
305
|
*/
|
|
200
|
-
export async function requireTransactionConfirmation(description, token) {
|
|
306
|
+
export async function requireTransactionConfirmation(description, token, details) {
|
|
201
307
|
const config = loadConfig();
|
|
202
308
|
if (config.confirmBeforeTransaction === false)
|
|
203
309
|
return;
|
|
204
310
|
console.log('');
|
|
205
311
|
console.log(chalk.yellow('⚠'), chalk.bold('Transaction confirmation'));
|
|
312
|
+
if (details?.chain) {
|
|
313
|
+
console.log(chalk.dim(' Chain : ') + chalk.cyan(details.chain));
|
|
314
|
+
}
|
|
206
315
|
if (token) {
|
|
207
316
|
const ticker = token.symbol ? '$' + token.symbol : undefined;
|
|
208
317
|
const label = [ticker, token.name].filter(Boolean).join(' — ');
|
|
209
318
|
console.log(chalk.dim(' Token : ') + (label ? chalk.bold(label) : chalk.dim('Unknown token')));
|
|
210
319
|
console.log(chalk.dim(' Address : ') + chalk.yellow(token.address));
|
|
211
320
|
}
|
|
321
|
+
if (details?.side) {
|
|
322
|
+
const s = details.side.toLowerCase();
|
|
323
|
+
const colored = s === 'buy' ? chalk.green.bold(details.side.toUpperCase()) : chalk.red.bold(details.side.toUpperCase());
|
|
324
|
+
console.log(chalk.dim(' Side : ') + colored);
|
|
325
|
+
}
|
|
326
|
+
if (details?.amount) {
|
|
327
|
+
console.log(chalk.dim(' Amount : ') + chalk.bold(details.amount));
|
|
328
|
+
}
|
|
329
|
+
if (details?.destination) {
|
|
330
|
+
console.log(chalk.dim(' To : ') + chalk.yellow(details.destination));
|
|
331
|
+
}
|
|
212
332
|
console.log(chalk.dim(` Action : ${description}`));
|
|
213
333
|
console.log('');
|
|
214
334
|
const ok = await confirm({
|