@zoralabs/cli 0.3.0 → 1.0.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 +17 -16
- package/dist/index.js +1239 -502
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -52,6 +52,12 @@ function apiErrorMessage(err) {
|
|
|
52
52
|
return "Zora is temporarily unavailable. Try again later.";
|
|
53
53
|
return formatError(err);
|
|
54
54
|
}
|
|
55
|
+
function bannedCoinMessage(address) {
|
|
56
|
+
return `The coin at ${address} is unavailable because it violates the Zora terms of service.`;
|
|
57
|
+
}
|
|
58
|
+
function bannedCoinBuyMessage(address) {
|
|
59
|
+
return `Unable to buy ${address} because it violates the Zora terms of service. Already own this coin? Run zora sell ${address} --all to exit your position.`;
|
|
60
|
+
}
|
|
55
61
|
function fsErrorMessage(err, path) {
|
|
56
62
|
if (!(err instanceof Error)) return String(err);
|
|
57
63
|
const code = err.code;
|
|
@@ -279,6 +285,10 @@ var passwordOrFail = async (json, opts, nonInteractive) => {
|
|
|
279
285
|
}
|
|
280
286
|
return password(opts);
|
|
281
287
|
};
|
|
288
|
+
var passwordOrSkip = async (opts, nonInteractive) => {
|
|
289
|
+
if (nonInteractive) return "";
|
|
290
|
+
return password(opts);
|
|
291
|
+
};
|
|
282
292
|
|
|
283
293
|
// src/lib/analytics.ts
|
|
284
294
|
import { PostHog } from "posthog-node";
|
|
@@ -431,7 +441,7 @@ var getClient = () => {
|
|
|
431
441
|
return client;
|
|
432
442
|
};
|
|
433
443
|
var commonProperties = () => ({
|
|
434
|
-
cli_version: true ? "0.
|
|
444
|
+
cli_version: true ? "1.0.0" : "development",
|
|
435
445
|
os: process.platform,
|
|
436
446
|
arch: process.arch,
|
|
437
447
|
node_version: process.version
|
|
@@ -494,7 +504,9 @@ var shutdownAnalytics = async () => {
|
|
|
494
504
|
// src/commands/auth.ts
|
|
495
505
|
var authCommand = new Command("auth").description(
|
|
496
506
|
"Manage API key authentication.\nAPI key is optional \u2014 without one, requests are rate-limited.\nGet a key at https://zora.co/settings/developer"
|
|
497
|
-
)
|
|
507
|
+
).action(function() {
|
|
508
|
+
this.outputHelp();
|
|
509
|
+
});
|
|
498
510
|
authCommand.command("configure").description("Set your Zora API key").option("--yes", "Skip interactive prompt and execute directly").action(async function() {
|
|
499
511
|
const json = getJson(this);
|
|
500
512
|
const nonInteractive = getYes(this);
|
|
@@ -671,7 +683,7 @@ var Table = ({
|
|
|
671
683
|
|
|
672
684
|
// src/lib/format.ts
|
|
673
685
|
import { format, formatDistanceStrict } from "date-fns";
|
|
674
|
-
import {
|
|
686
|
+
import { formatUnits } from "viem";
|
|
675
687
|
function formatCompactUsd(value) {
|
|
676
688
|
if (!value || Number(value) === 0) return "$0";
|
|
677
689
|
return new Intl.NumberFormat("en-US", {
|
|
@@ -720,17 +732,35 @@ function formatCreatedAt(isoDate, now) {
|
|
|
720
732
|
if (isNaN(date.getTime())) return "-";
|
|
721
733
|
return `${formatRelativeTime(date, now)} (${formatAbsoluteTime(date)})`;
|
|
722
734
|
}
|
|
723
|
-
var
|
|
724
|
-
const
|
|
725
|
-
const parts =
|
|
726
|
-
if (!parts[1])
|
|
727
|
-
|
|
728
|
-
|
|
735
|
+
var formatAmountDisplay = (amount, decimals) => {
|
|
736
|
+
const formatted = formatUnits(amount, decimals);
|
|
737
|
+
const parts = formatted.split(".");
|
|
738
|
+
if (!parts[1]) {
|
|
739
|
+
return new Intl.NumberFormat("en-US", {
|
|
740
|
+
maximumFractionDigits: 2
|
|
741
|
+
}).format(Number(formatted));
|
|
742
|
+
}
|
|
743
|
+
const twoDecimal = `${parts[0]}.${parts[1].slice(0, 2)}`;
|
|
744
|
+
if (Number(twoDecimal) === 0 && amount > 0n) {
|
|
745
|
+
const sigIndex = parts[1].search(/[1-9]/);
|
|
746
|
+
const maxDecimals = sigIndex === -1 ? 6 : Math.min(sigIndex + 4, parts[1].length);
|
|
747
|
+
const truncated = `${parts[0]}.${parts[1].slice(0, maxDecimals)}`;
|
|
748
|
+
return new Intl.NumberFormat("en-US", {
|
|
749
|
+
maximumFractionDigits: maxDecimals
|
|
750
|
+
}).format(Number(truncated));
|
|
751
|
+
}
|
|
752
|
+
return new Intl.NumberFormat("en-US", {
|
|
753
|
+
maximumFractionDigits: 2
|
|
754
|
+
}).format(Number(formatted));
|
|
729
755
|
};
|
|
756
|
+
function computeMarketCapChange24h(marketCap, marketCapDelta24h) {
|
|
757
|
+
if (marketCap === null || marketCapDelta24h === null || marketCap - marketCapDelta24h === 0)
|
|
758
|
+
return null;
|
|
759
|
+
return Number(
|
|
760
|
+
(marketCapDelta24h / (marketCap - marketCapDelta24h) * 100).toFixed(4)
|
|
761
|
+
);
|
|
762
|
+
}
|
|
730
763
|
var truncateAddress = (address) => `${address.slice(0, 6)}\u2026${address.slice(-4)}`;
|
|
731
|
-
var formatCoinsDisplay = (coinsOut) => new Intl.NumberFormat("en-US", {
|
|
732
|
-
maximumFractionDigits: 2
|
|
733
|
-
}).format(Number(coinsOut));
|
|
734
764
|
|
|
735
765
|
// src/lib/balance-format.ts
|
|
736
766
|
var COIN_DECIMALS = 18;
|
|
@@ -961,8 +991,7 @@ var BalanceView = ({
|
|
|
961
991
|
const hints = [];
|
|
962
992
|
if (paginated && cursorHistory.length > 0) hints.push("\u2190 prev");
|
|
963
993
|
if (paginated && data?.pageInfo?.hasNextPage) hints.push("\u2192 next");
|
|
964
|
-
hints.push("r refresh");
|
|
965
|
-
if (autoRefresh) hints.push(`auto: ${secondsUntilRefresh}s`);
|
|
994
|
+
hints.push(autoRefresh ? `r refresh (${secondsUntilRefresh}s)` : "r refresh");
|
|
966
995
|
hints.push("q quit");
|
|
967
996
|
const footer = hints.join(" \xB7 ");
|
|
968
997
|
const showWallet = mode === "full" || mode === "wallet";
|
|
@@ -1017,7 +1046,7 @@ import { getTokenInfo } from "@zoralabs/coins-sdk";
|
|
|
1017
1046
|
import {
|
|
1018
1047
|
createPublicClient as createPublicClient2,
|
|
1019
1048
|
erc20Abi,
|
|
1020
|
-
formatUnits,
|
|
1049
|
+
formatUnits as formatUnits2,
|
|
1021
1050
|
http
|
|
1022
1051
|
} from "viem";
|
|
1023
1052
|
import { base as base2 } from "viem/chains";
|
|
@@ -1102,7 +1131,7 @@ var fetchWalletBalances = async (walletAddress) => {
|
|
|
1102
1131
|
});
|
|
1103
1132
|
const visible = resolved.filter((r) => r.balance > 0n || r.token.isNative);
|
|
1104
1133
|
const intermediate = visible.map(({ token, balance, priceUsd }) => {
|
|
1105
|
-
const human =
|
|
1134
|
+
const human = formatUnits2(balance, token.decimals);
|
|
1106
1135
|
const usdValue = priceUsd !== null ? Number(human) * priceUsd : null;
|
|
1107
1136
|
return { token, human, priceUsd, usdValue };
|
|
1108
1137
|
});
|
|
@@ -1150,11 +1179,10 @@ var formatBalanceJson = (balance, rank) => {
|
|
|
1150
1179
|
const totalVolume = balance.coin?.totalVolume ? Number(balance.coin.totalVolume) : null;
|
|
1151
1180
|
const priceUsdValue = priceUsd ? Number(priceUsd) : null;
|
|
1152
1181
|
const usdValue = priceUsdValue !== null ? Number((parseRawBalance(balance.balance) * priceUsdValue).toFixed(6)) : null;
|
|
1153
|
-
const marketCapChange24h =
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
) : null;
|
|
1182
|
+
const marketCapChange24h = computeMarketCapChange24h(
|
|
1183
|
+
marketCap,
|
|
1184
|
+
marketCapDelta24h
|
|
1185
|
+
);
|
|
1158
1186
|
return {
|
|
1159
1187
|
rank,
|
|
1160
1188
|
name: balance.coin?.name ?? null,
|
|
@@ -1177,13 +1205,9 @@ var formatBalanceJson = (balance, rank) => {
|
|
|
1177
1205
|
function resolveContext(json) {
|
|
1178
1206
|
const account = resolveAccount(json);
|
|
1179
1207
|
const apiKey = getApiKey();
|
|
1180
|
-
if (
|
|
1181
|
-
|
|
1182
|
-
json,
|
|
1183
|
-
"Not authenticated. Run 'zora auth configure' to set your API key."
|
|
1184
|
-
);
|
|
1208
|
+
if (apiKey) {
|
|
1209
|
+
setApiKey(apiKey);
|
|
1185
1210
|
}
|
|
1186
|
-
setApiKey(apiKey);
|
|
1187
1211
|
return account;
|
|
1188
1212
|
}
|
|
1189
1213
|
function renderWallet(json, walletResult) {
|
|
@@ -1527,10 +1551,10 @@ balanceCommand.command("coins").description("Show coin positions").option("--sor
|
|
|
1527
1551
|
// src/commands/buy.ts
|
|
1528
1552
|
import { Command as Command3 } from "commander";
|
|
1529
1553
|
import confirm2 from "@inquirer/confirm";
|
|
1530
|
-
import { parseUnits, formatUnits as
|
|
1554
|
+
import { parseUnits, formatUnits as formatUnits4, isAddress } from "viem";
|
|
1531
1555
|
import {
|
|
1532
1556
|
setApiKey as setApiKey2,
|
|
1533
|
-
getCoin,
|
|
1557
|
+
getCoin as getCoin2,
|
|
1534
1558
|
tradeCoin,
|
|
1535
1559
|
createTradeCall
|
|
1536
1560
|
} from "@zoralabs/coins-sdk";
|
|
@@ -1538,7 +1562,7 @@ import {
|
|
|
1538
1562
|
// src/lib/trade-helpers.ts
|
|
1539
1563
|
import {
|
|
1540
1564
|
parseEther,
|
|
1541
|
-
formatUnits as
|
|
1565
|
+
formatUnits as formatUnits3,
|
|
1542
1566
|
isAddressEqual,
|
|
1543
1567
|
parseEventLogs,
|
|
1544
1568
|
erc20Abi as erc20Abi2
|
|
@@ -1571,25 +1595,6 @@ var parsePercentageLikeValue = (value) => {
|
|
|
1571
1595
|
const parsed = Number(value);
|
|
1572
1596
|
return Number.isFinite(parsed) ? parsed : void 0;
|
|
1573
1597
|
};
|
|
1574
|
-
var formatAmountDisplay = (amount, decimals) => {
|
|
1575
|
-
const formatted = formatUnits2(amount, decimals);
|
|
1576
|
-
const parts = formatted.split(".");
|
|
1577
|
-
if (!parts[1]) {
|
|
1578
|
-
return new Intl.NumberFormat("en-US", {
|
|
1579
|
-
maximumFractionDigits: 2
|
|
1580
|
-
}).format(Number(formatted));
|
|
1581
|
-
}
|
|
1582
|
-
const twoDecimal = `${parts[0]}.${parts[1].slice(0, 2)}`;
|
|
1583
|
-
let maxDecimals = 2;
|
|
1584
|
-
if (Number(twoDecimal) === 0 && amount > 0n) {
|
|
1585
|
-
const sigIndex = parts[1].search(/[1-9]/);
|
|
1586
|
-
maxDecimals = sigIndex === -1 ? 6 : Math.min(sigIndex + 4, parts[1].length);
|
|
1587
|
-
}
|
|
1588
|
-
const truncated = `${parts[0]}.${parts[1].slice(0, maxDecimals)}`;
|
|
1589
|
-
return new Intl.NumberFormat("en-US", {
|
|
1590
|
-
maximumFractionDigits: maxDecimals
|
|
1591
|
-
}).format(Number(truncated));
|
|
1592
|
-
};
|
|
1593
1598
|
var getReceivedAmountFromReceipt = ({
|
|
1594
1599
|
receipt,
|
|
1595
1600
|
tokenAddress,
|
|
@@ -1655,12 +1660,12 @@ var printQuote = (json, info) => {
|
|
|
1655
1660
|
coin: info.coinSymbol,
|
|
1656
1661
|
address: info.address,
|
|
1657
1662
|
spend: {
|
|
1658
|
-
amount:
|
|
1663
|
+
amount: formatUnits3(info.amountIn, info.inputTokenDecimals),
|
|
1659
1664
|
raw: info.amountIn.toString(),
|
|
1660
1665
|
symbol: info.inputTokenSymbol
|
|
1661
1666
|
},
|
|
1662
1667
|
estimated: {
|
|
1663
|
-
amount:
|
|
1668
|
+
amount: formatUnits3(BigInt(info.amountOut), 18),
|
|
1664
1669
|
raw: info.amountOut,
|
|
1665
1670
|
symbol: info.coinSymbol
|
|
1666
1671
|
},
|
|
@@ -1668,29 +1673,35 @@ var printQuote = (json, info) => {
|
|
|
1668
1673
|
});
|
|
1669
1674
|
return;
|
|
1670
1675
|
}
|
|
1676
|
+
const spendFormatted = formatAmountDisplay(
|
|
1677
|
+
info.amountIn,
|
|
1678
|
+
info.inputTokenDecimals
|
|
1679
|
+
);
|
|
1680
|
+
const coinsFormatted = formatAmountDisplay(BigInt(info.amountOut), 18);
|
|
1671
1681
|
console.log(`
|
|
1672
|
-
Buy ${info.coinName}
|
|
1682
|
+
Buy \x1B[1m${info.coinName}\x1B[0m`);
|
|
1683
|
+
console.log(` ${info.coinType} \xB7 ${info.address}
|
|
1673
1684
|
`);
|
|
1674
|
-
console.log(
|
|
1675
|
-
|
|
1685
|
+
console.log(
|
|
1686
|
+
` Amount ${spendFormatted} ${info.inputTokenSymbol}${info.amountUsd ? ` (${info.amountUsd})` : ""}`
|
|
1687
|
+
);
|
|
1688
|
+
console.log(` You get ~${coinsFormatted} ${info.coinSymbol}`);
|
|
1676
1689
|
console.log(` Slippage ${info.slippagePct}%
|
|
1677
1690
|
`);
|
|
1678
1691
|
};
|
|
1679
1692
|
var printTradeResult = (json, info) => {
|
|
1680
|
-
const receivedAmount = formatUnits2(info.receivedAmountOut, 18);
|
|
1681
|
-
const receivedFormatted = formatCoinsDisplay(receivedAmount);
|
|
1682
1693
|
if (json) {
|
|
1683
1694
|
outputJson({
|
|
1684
1695
|
action: "buy",
|
|
1685
1696
|
coin: info.coinSymbol,
|
|
1686
1697
|
address: info.address,
|
|
1687
1698
|
spent: {
|
|
1688
|
-
amount:
|
|
1699
|
+
amount: formatUnits3(info.amountIn, info.inputTokenDecimals),
|
|
1689
1700
|
raw: info.amountIn.toString(),
|
|
1690
1701
|
symbol: info.inputTokenSymbol
|
|
1691
1702
|
},
|
|
1692
1703
|
received: {
|
|
1693
|
-
amount:
|
|
1704
|
+
amount: formatUnits3(info.receivedAmountOut, 18),
|
|
1694
1705
|
raw: info.receivedAmountOut.toString(),
|
|
1695
1706
|
symbol: info.coinSymbol
|
|
1696
1707
|
},
|
|
@@ -1698,21 +1709,235 @@ var printTradeResult = (json, info) => {
|
|
|
1698
1709
|
});
|
|
1699
1710
|
return;
|
|
1700
1711
|
}
|
|
1712
|
+
const spentFormatted = formatAmountDisplay(
|
|
1713
|
+
info.amountIn,
|
|
1714
|
+
info.inputTokenDecimals
|
|
1715
|
+
);
|
|
1716
|
+
const receivedFormatted = formatAmountDisplay(info.receivedAmountOut, 18);
|
|
1701
1717
|
console.log(`
|
|
1702
|
-
Bought ${info.coinName}
|
|
1718
|
+
Bought \x1B[1m${info.coinName}\x1B[0m`);
|
|
1719
|
+
console.log(` ${info.coinType} \xB7 ${info.address}
|
|
1703
1720
|
`);
|
|
1704
|
-
console.log(
|
|
1721
|
+
console.log(
|
|
1722
|
+
` Spent ${spentFormatted} ${info.inputTokenSymbol}${info.amountUsd ? ` (${info.amountUsd})` : ""}`
|
|
1723
|
+
);
|
|
1705
1724
|
console.log(` Received ${receivedFormatted} ${info.coinSymbol}`);
|
|
1706
1725
|
console.log(` Tx ${info.txHash}
|
|
1707
1726
|
`);
|
|
1708
1727
|
};
|
|
1709
1728
|
|
|
1729
|
+
// src/lib/coin-ref.ts
|
|
1730
|
+
import { getCoin, getProfile, getTrend } from "@zoralabs/coins-sdk";
|
|
1731
|
+
var TYPE_KEYWORDS = /* @__PURE__ */ new Set(["creator-coin", "trend"]);
|
|
1732
|
+
var CoinArgError = class extends Error {
|
|
1733
|
+
suggestion;
|
|
1734
|
+
constructor(message, suggestion) {
|
|
1735
|
+
super(message);
|
|
1736
|
+
this.suggestion = suggestion;
|
|
1737
|
+
}
|
|
1738
|
+
};
|
|
1739
|
+
function parsePositionalCoinArgs(firstArg, secondArg) {
|
|
1740
|
+
if (TYPE_KEYWORDS.has(firstArg)) {
|
|
1741
|
+
if (!secondArg) {
|
|
1742
|
+
throw new CoinArgError(
|
|
1743
|
+
`Missing identifier after "${firstArg}".`,
|
|
1744
|
+
`Usage: zora <command> ${firstArg} <name>`
|
|
1745
|
+
);
|
|
1746
|
+
}
|
|
1747
|
+
return { kind: "typed", type: firstArg, identifier: secondArg };
|
|
1748
|
+
}
|
|
1749
|
+
if (firstArg.startsWith("0x")) {
|
|
1750
|
+
return { kind: "address", address: firstArg };
|
|
1751
|
+
}
|
|
1752
|
+
return { kind: "ambiguous-name", name: firstArg };
|
|
1753
|
+
}
|
|
1754
|
+
function coinArgsToRef(parsed) {
|
|
1755
|
+
switch (parsed.kind) {
|
|
1756
|
+
case "typed":
|
|
1757
|
+
return { kind: "prefixed", type: parsed.type, name: parsed.identifier };
|
|
1758
|
+
case "address":
|
|
1759
|
+
return { kind: "address", address: parsed.address };
|
|
1760
|
+
case "ambiguous-name":
|
|
1761
|
+
return { kind: "ambiguous", name: parsed.name };
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
async function resolveAmbiguousName(name) {
|
|
1765
|
+
const [creatorResult, trendResult] = await Promise.all([
|
|
1766
|
+
resolveByCreatorName(name),
|
|
1767
|
+
resolveByTrendTicker(name)
|
|
1768
|
+
]);
|
|
1769
|
+
const creatorFound = creatorResult.kind === "found" ? creatorResult.coin : null;
|
|
1770
|
+
const trendFound = trendResult.kind === "found" ? trendResult.coin : null;
|
|
1771
|
+
if (creatorFound && trendFound) {
|
|
1772
|
+
return { kind: "ambiguous", creator: creatorFound, trend: trendFound };
|
|
1773
|
+
}
|
|
1774
|
+
if (creatorFound) {
|
|
1775
|
+
return { kind: "found", coin: creatorFound };
|
|
1776
|
+
}
|
|
1777
|
+
if (trendFound) {
|
|
1778
|
+
return { kind: "found", coin: trendFound };
|
|
1779
|
+
}
|
|
1780
|
+
return {
|
|
1781
|
+
kind: "not-found",
|
|
1782
|
+
message: `No coin found matching "${name}".`
|
|
1783
|
+
};
|
|
1784
|
+
}
|
|
1785
|
+
function formatAmbiguousError(name, creator, trend, command) {
|
|
1786
|
+
const creatorMcap = formatCompactUsd(creator.marketCap);
|
|
1787
|
+
const trendMcap = formatCompactUsd(trend.marketCap);
|
|
1788
|
+
return {
|
|
1789
|
+
message: [
|
|
1790
|
+
`Multiple coins match "${name}":`,
|
|
1791
|
+
` creator-coin ${creator.name} ${creatorMcap} mcap`,
|
|
1792
|
+
` trend ${trend.name} ${trendMcap} mcap`
|
|
1793
|
+
].join("\n"),
|
|
1794
|
+
suggestion: `Use: zora ${command} creator-coin ${name} or zora ${command} trend ${name}`
|
|
1795
|
+
};
|
|
1796
|
+
}
|
|
1797
|
+
var COIN_TYPE_MAP = {
|
|
1798
|
+
CONTENT: "post",
|
|
1799
|
+
CREATOR: "creator-coin",
|
|
1800
|
+
TREND: "trend"
|
|
1801
|
+
};
|
|
1802
|
+
function mapCoinType(raw) {
|
|
1803
|
+
if (!raw) return "unknown";
|
|
1804
|
+
return COIN_TYPE_MAP[raw] ?? "unknown";
|
|
1805
|
+
}
|
|
1806
|
+
function coinFromToken(token) {
|
|
1807
|
+
return {
|
|
1808
|
+
name: token.name ?? "Unknown",
|
|
1809
|
+
address: token.address ?? "",
|
|
1810
|
+
coinType: mapCoinType(token.coinType),
|
|
1811
|
+
marketCap: token.marketCap ?? "0",
|
|
1812
|
+
marketCapDelta24h: token.marketCapDelta24h ?? "0",
|
|
1813
|
+
volume24h: token.volume24h ?? "0",
|
|
1814
|
+
uniqueHolders: token.uniqueHolders ?? 0,
|
|
1815
|
+
createdAt: token.createdAt,
|
|
1816
|
+
creatorAddress: token.creatorAddress,
|
|
1817
|
+
creatorHandle: token.creatorProfile?.handle,
|
|
1818
|
+
platformBlocked: token.platformBlocked ?? false
|
|
1819
|
+
};
|
|
1820
|
+
}
|
|
1821
|
+
async function resolveByAddress(address) {
|
|
1822
|
+
const response = await getCoin({ address });
|
|
1823
|
+
if (response.error || !response.data?.zora20Token) {
|
|
1824
|
+
return {
|
|
1825
|
+
kind: "not-found",
|
|
1826
|
+
message: `No coin found at address ${address}`
|
|
1827
|
+
};
|
|
1828
|
+
}
|
|
1829
|
+
return { kind: "found", coin: coinFromToken(response.data.zora20Token) };
|
|
1830
|
+
}
|
|
1831
|
+
async function resolveByTrendTicker(ticker) {
|
|
1832
|
+
const response = await getTrend({ ticker });
|
|
1833
|
+
if (response.error || !response.data?.trendCoin) {
|
|
1834
|
+
return {
|
|
1835
|
+
kind: "not-found",
|
|
1836
|
+
message: `No trend coin found with ticker "${ticker}"`
|
|
1837
|
+
};
|
|
1838
|
+
}
|
|
1839
|
+
return { kind: "found", coin: coinFromToken(response.data.trendCoin) };
|
|
1840
|
+
}
|
|
1841
|
+
async function resolveByCreatorName(name) {
|
|
1842
|
+
const response = await getProfile({ identifier: name });
|
|
1843
|
+
if (response.error || !response.data?.profile) {
|
|
1844
|
+
return {
|
|
1845
|
+
kind: "not-found",
|
|
1846
|
+
message: `No creator found with name "${name}"`
|
|
1847
|
+
};
|
|
1848
|
+
}
|
|
1849
|
+
const profile = response.data.profile;
|
|
1850
|
+
if (!profile.creatorCoin) {
|
|
1851
|
+
return {
|
|
1852
|
+
kind: "not-found",
|
|
1853
|
+
message: `"${name}" does not have a creator coin`
|
|
1854
|
+
};
|
|
1855
|
+
}
|
|
1856
|
+
return resolveByAddress(profile.creatorCoin.address);
|
|
1857
|
+
}
|
|
1858
|
+
async function resolveCoin(ref) {
|
|
1859
|
+
switch (ref.kind) {
|
|
1860
|
+
case "address":
|
|
1861
|
+
return resolveByAddress(ref.address);
|
|
1862
|
+
case "prefixed":
|
|
1863
|
+
if (ref.type === "trend") {
|
|
1864
|
+
return resolveByTrendTicker(ref.name);
|
|
1865
|
+
}
|
|
1866
|
+
return resolveByCreatorName(ref.name);
|
|
1867
|
+
case "ambiguous":
|
|
1868
|
+
return resolveByCreatorName(ref.name);
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1710
1872
|
// src/commands/buy.ts
|
|
1711
|
-
var buyCommand = new Command3("buy").description("Buy a coin").argument(
|
|
1873
|
+
var buyCommand = new Command3("buy").description("Buy a coin").argument(
|
|
1874
|
+
"[typeOrId]",
|
|
1875
|
+
"Type prefix (creator-coin, trend) or coin address/name"
|
|
1876
|
+
).argument("[identifier]", "Coin name (when type prefix is given)").option("--eth <value>", "Buy with ETH amount").option("--usd <value>", "Buy with USD equivalent (use with --token)").option("--token <asset>", "Token to spend: eth, usdc, zora", "eth").option("--percent <value>", "Buy with percentage of ETH balance").option("--all", "Swap all ETH for coin").option("--quote", "Print quote and exit without trading").option("--yes", "Skip confirmation and execute directly").option("--slippage <pct>", "Slippage tolerance percent", "1").option("--debug", "Print full quote request/response JSON").action(async function(typeOrId, identifier, opts) {
|
|
1712
1877
|
const json = getJson(this);
|
|
1713
1878
|
const debug = opts.debug === true;
|
|
1714
|
-
|
|
1715
|
-
|
|
1879
|
+
let parsed;
|
|
1880
|
+
try {
|
|
1881
|
+
parsed = parsePositionalCoinArgs(typeOrId, identifier);
|
|
1882
|
+
} catch (err) {
|
|
1883
|
+
if (err instanceof CoinArgError) {
|
|
1884
|
+
outputErrorAndExit(json, err.message, err.suggestion);
|
|
1885
|
+
}
|
|
1886
|
+
throw err;
|
|
1887
|
+
}
|
|
1888
|
+
const apiKey = getApiKey();
|
|
1889
|
+
if (apiKey) {
|
|
1890
|
+
setApiKey2(apiKey);
|
|
1891
|
+
}
|
|
1892
|
+
let coinAddress;
|
|
1893
|
+
if (parsed.kind === "address") {
|
|
1894
|
+
if (!isAddress(parsed.address)) {
|
|
1895
|
+
outputErrorAndExit(json, `Invalid address: ${parsed.address}`);
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1898
|
+
coinAddress = parsed.address;
|
|
1899
|
+
} else if (parsed.kind === "ambiguous-name") {
|
|
1900
|
+
let ambResult;
|
|
1901
|
+
try {
|
|
1902
|
+
ambResult = await resolveAmbiguousName(parsed.name);
|
|
1903
|
+
} catch (err) {
|
|
1904
|
+
outputErrorAndExit(
|
|
1905
|
+
json,
|
|
1906
|
+
`Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1907
|
+
);
|
|
1908
|
+
return;
|
|
1909
|
+
}
|
|
1910
|
+
if (ambResult.kind === "not-found") {
|
|
1911
|
+
outputErrorAndExit(json, ambResult.message);
|
|
1912
|
+
return;
|
|
1913
|
+
}
|
|
1914
|
+
if (ambResult.kind === "ambiguous") {
|
|
1915
|
+
const { message, suggestion } = formatAmbiguousError(
|
|
1916
|
+
parsed.name,
|
|
1917
|
+
ambResult.creator,
|
|
1918
|
+
ambResult.trend,
|
|
1919
|
+
"buy"
|
|
1920
|
+
);
|
|
1921
|
+
outputErrorAndExit(json, message, suggestion);
|
|
1922
|
+
return;
|
|
1923
|
+
}
|
|
1924
|
+
coinAddress = ambResult.coin.address;
|
|
1925
|
+
} else {
|
|
1926
|
+
const ref = coinArgsToRef(parsed);
|
|
1927
|
+
try {
|
|
1928
|
+
const result = await resolveCoin(ref);
|
|
1929
|
+
if (result.kind === "not-found") {
|
|
1930
|
+
outputErrorAndExit(json, result.message, result.suggestion);
|
|
1931
|
+
return;
|
|
1932
|
+
}
|
|
1933
|
+
coinAddress = result.coin.address;
|
|
1934
|
+
} catch (err) {
|
|
1935
|
+
outputErrorAndExit(
|
|
1936
|
+
json,
|
|
1937
|
+
`Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1938
|
+
);
|
|
1939
|
+
return;
|
|
1940
|
+
}
|
|
1716
1941
|
}
|
|
1717
1942
|
const tokenKey = opts.token.toLowerCase();
|
|
1718
1943
|
if (!(tokenKey in BASE_TRADE_TOKENS)) {
|
|
@@ -1736,15 +1961,11 @@ var buyCommand = new Command3("buy").description("Buy a coin").argument("[addres
|
|
|
1736
1961
|
);
|
|
1737
1962
|
}
|
|
1738
1963
|
const slippage = slippagePct / 100;
|
|
1739
|
-
const apiKey = getApiKey();
|
|
1740
|
-
if (apiKey) {
|
|
1741
|
-
setApiKey2(apiKey);
|
|
1742
|
-
}
|
|
1743
1964
|
const account = resolveAccount(json);
|
|
1744
1965
|
const { publicClient, walletClient } = createClients(account);
|
|
1745
1966
|
let token;
|
|
1746
1967
|
try {
|
|
1747
|
-
const response = await
|
|
1968
|
+
const response = await getCoin2({ address: coinAddress });
|
|
1748
1969
|
token = response.data?.zora20Token;
|
|
1749
1970
|
} catch (err) {
|
|
1750
1971
|
outputErrorAndExit(json, `Failed to fetch coin: ${apiErrorMessage(err)}`);
|
|
@@ -1752,8 +1973,12 @@ var buyCommand = new Command3("buy").description("Buy a coin").argument("[addres
|
|
|
1752
1973
|
if (!token) {
|
|
1753
1974
|
outputErrorAndExit(json, `Coin not found: ${coinAddress}`);
|
|
1754
1975
|
}
|
|
1976
|
+
if (token.platformBlocked) {
|
|
1977
|
+
outputErrorAndExit(json, bannedCoinBuyMessage(coinAddress));
|
|
1978
|
+
}
|
|
1755
1979
|
const coinName = token.name;
|
|
1756
1980
|
const coinSymbol = token.symbol;
|
|
1981
|
+
const coinType = mapCoinType(token.coinType);
|
|
1757
1982
|
let amountIn;
|
|
1758
1983
|
if (amountMode === "usd") {
|
|
1759
1984
|
const usdVal = parsePercentageLikeValue(opts.usd);
|
|
@@ -1788,7 +2013,7 @@ var buyCommand = new Command3("buy").description("Buy a coin").argument("[addres
|
|
|
1788
2013
|
}
|
|
1789
2014
|
if (debug) {
|
|
1790
2015
|
console.error(
|
|
1791
|
-
`[debug] $${usdVal} USD = ${
|
|
2016
|
+
`[debug] $${usdVal} USD = ${formatUnits4(amountIn, inputToken.decimals)} ${inputToken.symbol} (price: $${priceUsd})`
|
|
1792
2017
|
);
|
|
1793
2018
|
}
|
|
1794
2019
|
} else if (amountMode === "eth") {
|
|
@@ -1841,7 +2066,7 @@ var buyCommand = new Command3("buy").description("Buy a coin").argument("[addres
|
|
|
1841
2066
|
if (isEth && balance <= gasReserve) {
|
|
1842
2067
|
outputErrorAndExit(
|
|
1843
2068
|
json,
|
|
1844
|
-
`Balance too low (${
|
|
2069
|
+
`Balance too low (${formatAmountDisplay(balance, 18)} ETH). Need >${formatAmountDisplay(GAS_RESERVE, 18)} ETH for gas.`
|
|
1845
2070
|
);
|
|
1846
2071
|
}
|
|
1847
2072
|
const spendableBalance = balance - gasReserve;
|
|
@@ -1871,7 +2096,7 @@ var buyCommand = new Command3("buy").description("Buy a coin").argument("[addres
|
|
|
1871
2096
|
const priceUsd = inputToken.fixedPriceUsd ?? await fetchTokenPriceUsd(inputToken.priceAddress);
|
|
1872
2097
|
if (priceUsd != null) {
|
|
1873
2098
|
swapAmountUsd = Number(
|
|
1874
|
-
(Number(
|
|
2099
|
+
(Number(formatUnits4(amountIn, inputToken.decimals)) * priceUsd).toFixed(2)
|
|
1875
2100
|
);
|
|
1876
2101
|
}
|
|
1877
2102
|
}
|
|
@@ -1926,25 +2151,20 @@ ${err instanceof Error ? err.stack || err.message : String(err)}
|
|
|
1926
2151
|
"Check the coin address is valid and try again. Use --debug for full error details."
|
|
1927
2152
|
);
|
|
1928
2153
|
}
|
|
1929
|
-
const
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
2154
|
+
const quoteInfo = {
|
|
2155
|
+
coinName,
|
|
2156
|
+
coinSymbol,
|
|
2157
|
+
coinType,
|
|
2158
|
+
address: coinAddress,
|
|
2159
|
+
amountIn,
|
|
2160
|
+
inputTokenSymbol: inputToken.symbol,
|
|
2161
|
+
inputTokenDecimals: inputToken.decimals,
|
|
2162
|
+
amountOut,
|
|
2163
|
+
slippagePct
|
|
2164
|
+
};
|
|
2165
|
+
const amountUsd = swapAmountUsd != null && inputToken.fixedPriceUsd == null ? `${amountMode === "usd" ? "" : "~"}${formatUsd(swapAmountUsd)}` : void 0;
|
|
1935
2166
|
if (opts.quote) {
|
|
1936
|
-
printQuote(json, {
|
|
1937
|
-
coinName,
|
|
1938
|
-
coinSymbol,
|
|
1939
|
-
address: coinAddress,
|
|
1940
|
-
spendAmount: `${spendFormatted} ${inputToken.symbol}`,
|
|
1941
|
-
amountIn,
|
|
1942
|
-
inputTokenSymbol: inputToken.symbol,
|
|
1943
|
-
inputTokenDecimals: inputToken.decimals,
|
|
1944
|
-
coinsFormatted,
|
|
1945
|
-
amountOut,
|
|
1946
|
-
slippagePct
|
|
1947
|
-
});
|
|
2167
|
+
printQuote(json, { ...quoteInfo, amountUsd });
|
|
1948
2168
|
track("cli_buy", {
|
|
1949
2169
|
action: "quote",
|
|
1950
2170
|
coin_address: coinAddress,
|
|
@@ -1960,18 +2180,7 @@ ${err instanceof Error ? err.stack || err.message : String(err)}
|
|
|
1960
2180
|
return;
|
|
1961
2181
|
}
|
|
1962
2182
|
if (!opts.yes) {
|
|
1963
|
-
printQuote(false, {
|
|
1964
|
-
coinName,
|
|
1965
|
-
coinSymbol,
|
|
1966
|
-
address: coinAddress,
|
|
1967
|
-
spendAmount: `${spendFormatted} ${inputToken.symbol}`,
|
|
1968
|
-
amountIn,
|
|
1969
|
-
inputTokenSymbol: inputToken.symbol,
|
|
1970
|
-
inputTokenDecimals: inputToken.decimals,
|
|
1971
|
-
coinsFormatted,
|
|
1972
|
-
amountOut,
|
|
1973
|
-
slippagePct
|
|
1974
|
-
});
|
|
2183
|
+
printQuote(false, { ...quoteInfo, amountUsd });
|
|
1975
2184
|
const ok = await confirm2({
|
|
1976
2185
|
message: "Confirm?",
|
|
1977
2186
|
default: false
|
|
@@ -2028,8 +2237,9 @@ ${err instanceof Error ? err.stack || err.message : String(err)}
|
|
|
2028
2237
|
printTradeResult(json, {
|
|
2029
2238
|
coinName,
|
|
2030
2239
|
coinSymbol,
|
|
2240
|
+
coinType,
|
|
2031
2241
|
address: coinAddress,
|
|
2032
|
-
|
|
2242
|
+
amountUsd,
|
|
2033
2243
|
amountIn,
|
|
2034
2244
|
inputTokenSymbol: inputToken.symbol,
|
|
2035
2245
|
inputTokenDecimals: inputToken.decimals,
|
|
@@ -2063,9 +2273,6 @@ import {
|
|
|
2063
2273
|
getCoinsTopVolume24h,
|
|
2064
2274
|
getCoinsMostValuable,
|
|
2065
2275
|
getCoinsNew,
|
|
2066
|
-
getCoinsTopGainers,
|
|
2067
|
-
getCoinsLastTraded,
|
|
2068
|
-
getCoinsLastTradedUnique,
|
|
2069
2276
|
getExploreTopVolumeAll24h,
|
|
2070
2277
|
getExploreTopVolumeCreators24h,
|
|
2071
2278
|
getExploreNewAll,
|
|
@@ -2088,9 +2295,6 @@ var SORT_LABELS2 = {
|
|
|
2088
2295
|
mcap: "Top by Market Cap",
|
|
2089
2296
|
volume: "Top by 24h Volume",
|
|
2090
2297
|
new: "New",
|
|
2091
|
-
gainers: "Top Gainers (24h)",
|
|
2092
|
-
"last-traded": "Last Traded",
|
|
2093
|
-
"last-traded-unique": "Last Traded (Unique)",
|
|
2094
2298
|
trending: "Trending",
|
|
2095
2299
|
featured: "Featured"
|
|
2096
2300
|
};
|
|
@@ -2340,8 +2544,7 @@ var ExploreView = ({
|
|
|
2340
2544
|
hints.push("c copy address");
|
|
2341
2545
|
if (cursorHistory.length > 0) hints.push("\u2190 prev");
|
|
2342
2546
|
if (pageInfo?.hasNextPage) hints.push("\u2192 next");
|
|
2343
|
-
hints.push("r refresh");
|
|
2344
|
-
if (autoRefresh) hints.push(`auto: ${secondsUntilRefresh}s`);
|
|
2547
|
+
hints.push(autoRefresh ? `r refresh (${secondsUntilRefresh}s)` : "r refresh");
|
|
2345
2548
|
hints.push("q quit");
|
|
2346
2549
|
const footer = hints.join(" \xB7 ") + (copyFeedback ? ` ${copyFeedback}` : "");
|
|
2347
2550
|
return /* @__PURE__ */ jsx4(
|
|
@@ -2359,6 +2562,47 @@ var ExploreView = ({
|
|
|
2359
2562
|
|
|
2360
2563
|
// src/commands/explore.tsx
|
|
2361
2564
|
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
2565
|
+
var formatExploreCoinJson = (node) => {
|
|
2566
|
+
const marketCap = node.marketCap ? Number(node.marketCap) : null;
|
|
2567
|
+
const marketCapDelta24h = node.marketCapDelta24h ? Number(node.marketCapDelta24h) : null;
|
|
2568
|
+
const marketCapChange24h = computeMarketCapChange24h(
|
|
2569
|
+
marketCap,
|
|
2570
|
+
marketCapDelta24h
|
|
2571
|
+
);
|
|
2572
|
+
const priceUsd = node.tokenPrice?.priceInUsdc ? Number(node.tokenPrice.priceInUsdc) : null;
|
|
2573
|
+
const coinType = node.coinType ? COIN_TYPE_DISPLAY[node.coinType] ?? node.coinType : null;
|
|
2574
|
+
const socials = node.creatorProfile?.socialAccounts;
|
|
2575
|
+
const socialAccounts = socials ? {
|
|
2576
|
+
instagram: socials.instagram ?? null,
|
|
2577
|
+
tiktok: socials.tiktok ?? null,
|
|
2578
|
+
twitter: socials.twitter ?? null,
|
|
2579
|
+
farcaster: socials.farcaster ?? null
|
|
2580
|
+
} : null;
|
|
2581
|
+
return {
|
|
2582
|
+
name: node.name ?? null,
|
|
2583
|
+
description: node.description ?? null,
|
|
2584
|
+
symbol: node.symbol ?? null,
|
|
2585
|
+
coinType,
|
|
2586
|
+
chainId: node.chainId ?? null,
|
|
2587
|
+
address: node.address ?? null,
|
|
2588
|
+
platformBlocked: node.platformBlocked ?? false,
|
|
2589
|
+
totalSupply: node.totalSupply ?? null,
|
|
2590
|
+
creatorAddress: node.creatorAddress ?? null,
|
|
2591
|
+
creatorHandle: node.creatorProfile?.handle ?? null,
|
|
2592
|
+
socialAccounts,
|
|
2593
|
+
mediaContentMimeType: node.mediaContent?.mimeType ?? null,
|
|
2594
|
+
mediaContentOriginalUri: node.mediaContent?.originalUri ?? null,
|
|
2595
|
+
previewImage: node.mediaContent?.previewImage?.medium ?? null,
|
|
2596
|
+
priceUsd,
|
|
2597
|
+
marketCap,
|
|
2598
|
+
marketCapDelta24h,
|
|
2599
|
+
marketCapChange24h,
|
|
2600
|
+
volume24h: node.volume24h ? Number(node.volume24h) : null,
|
|
2601
|
+
totalVolume: node.totalVolume ? Number(node.totalVolume) : null,
|
|
2602
|
+
uniqueHolders: node.uniqueHolders ?? null,
|
|
2603
|
+
createdAt: node.createdAt ?? null
|
|
2604
|
+
};
|
|
2605
|
+
};
|
|
2362
2606
|
var QUERY_MAP = {
|
|
2363
2607
|
mcap: {
|
|
2364
2608
|
all: getMostValuableAll,
|
|
@@ -2378,15 +2622,6 @@ var QUERY_MAP = {
|
|
|
2378
2622
|
"creator-coin": getCreatorCoins,
|
|
2379
2623
|
post: getCoinsNew
|
|
2380
2624
|
},
|
|
2381
|
-
gainers: {
|
|
2382
|
-
post: getCoinsTopGainers
|
|
2383
|
-
},
|
|
2384
|
-
"last-traded": {
|
|
2385
|
-
post: getCoinsLastTraded
|
|
2386
|
-
},
|
|
2387
|
-
"last-traded-unique": {
|
|
2388
|
-
post: getCoinsLastTradedUnique
|
|
2389
|
-
},
|
|
2390
2625
|
trending: {
|
|
2391
2626
|
all: getTrendingAll,
|
|
2392
2627
|
trend: getTrendingTrends,
|
|
@@ -2428,7 +2663,7 @@ var SORT_OPTIONS2 = Object.keys(SORT_LABELS2).join(", ");
|
|
|
2428
2663
|
var exploreCommand = new Command4("explore").description("Browse top, new, and highest volume coins").option("--sort <sort>", `Sort by: ${SORT_OPTIONS2}`, "mcap").option(
|
|
2429
2664
|
"--type <type>",
|
|
2430
2665
|
"Filter by type: all, trend, creator-coin, post (availability varies by sort)",
|
|
2431
|
-
"
|
|
2666
|
+
"creator-coin"
|
|
2432
2667
|
).option("--limit <n>", "Number of results (max 20)", "10").option("--after <cursor>", "Pagination cursor from a previous result").option("--live", "Interactive live-updating display (default)").option("--static", "Static snapshot").option(
|
|
2433
2668
|
"--refresh <seconds>",
|
|
2434
2669
|
"Auto-refresh interval in seconds, requires --live (min 5)",
|
|
@@ -2479,7 +2714,10 @@ var exploreCommand = new Command4("explore").description("Browse top, new, and h
|
|
|
2479
2714
|
outputErrorAndExit(json, `API error: ${msg}`);
|
|
2480
2715
|
}
|
|
2481
2716
|
const edges = response.data?.exploreList?.edges ?? [];
|
|
2482
|
-
const
|
|
2717
|
+
const rawNodes = edges.map((e) => e.node);
|
|
2718
|
+
const coins = rawNodes.map(
|
|
2719
|
+
(node, i) => formatExploreCoinJson(node)
|
|
2720
|
+
);
|
|
2483
2721
|
const pageInfo = response.data?.exploreList?.pageInfo;
|
|
2484
2722
|
outputJson({ coins, pageInfo: pageInfo ?? null });
|
|
2485
2723
|
track("cli_explore", {
|
|
@@ -2560,106 +2798,18 @@ var exploreCommand = new Command4("explore").description("Browse top, new, and h
|
|
|
2560
2798
|
import { Command as Command5 } from "commander";
|
|
2561
2799
|
import { setApiKey as setApiKey4 } from "@zoralabs/coins-sdk";
|
|
2562
2800
|
|
|
2563
|
-
// src/
|
|
2564
|
-
import {
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
}
|
|
2574
|
-
|
|
2575
|
-
return {
|
|
2576
|
-
name: token.name ?? "Unknown",
|
|
2577
|
-
address: token.address ?? "",
|
|
2578
|
-
coinType: mapCoinType(token.coinType),
|
|
2579
|
-
marketCap: token.marketCap ?? "0",
|
|
2580
|
-
marketCapDelta24h: token.marketCapDelta24h ?? "0",
|
|
2581
|
-
volume24h: token.volume24h ?? "0",
|
|
2582
|
-
uniqueHolders: token.uniqueHolders ?? 0,
|
|
2583
|
-
createdAt: token.createdAt,
|
|
2584
|
-
creatorAddress: token.creatorAddress,
|
|
2585
|
-
creatorHandle: token.creatorProfile?.handle
|
|
2586
|
-
};
|
|
2587
|
-
}
|
|
2588
|
-
function parseCoinRef(identifier, type) {
|
|
2589
|
-
if (identifier.startsWith("0x")) {
|
|
2590
|
-
return { kind: "address", address: identifier };
|
|
2591
|
-
}
|
|
2592
|
-
if (type === "creator-coin") {
|
|
2593
|
-
return { kind: "prefixed", type: "creator-coin", name: identifier };
|
|
2594
|
-
}
|
|
2595
|
-
if (type === "trend") {
|
|
2596
|
-
return { kind: "prefixed", type: "trend", name: identifier };
|
|
2597
|
-
}
|
|
2598
|
-
return { kind: "ambiguous", name: identifier };
|
|
2599
|
-
}
|
|
2600
|
-
async function resolveByAddress(address) {
|
|
2601
|
-
const response = await getCoin2({ address });
|
|
2602
|
-
if (response.error || !response.data?.zora20Token) {
|
|
2603
|
-
return {
|
|
2604
|
-
kind: "not-found",
|
|
2605
|
-
message: `No coin found at address ${address}`
|
|
2606
|
-
};
|
|
2607
|
-
}
|
|
2608
|
-
return { kind: "found", coin: coinFromToken(response.data.zora20Token) };
|
|
2609
|
-
}
|
|
2610
|
-
async function resolveByTrendTicker(ticker) {
|
|
2611
|
-
const response = await getTrend({ ticker });
|
|
2612
|
-
if (response.error || !response.data?.trendCoin) {
|
|
2613
|
-
return {
|
|
2614
|
-
kind: "not-found",
|
|
2615
|
-
message: `No trend coin found with ticker "${ticker}"`
|
|
2616
|
-
};
|
|
2617
|
-
}
|
|
2618
|
-
return { kind: "found", coin: coinFromToken(response.data.trendCoin) };
|
|
2619
|
-
}
|
|
2620
|
-
async function resolveByCreatorName(name) {
|
|
2621
|
-
const response = await getProfile({ identifier: name });
|
|
2622
|
-
if (response.error || !response.data?.profile) {
|
|
2623
|
-
return {
|
|
2624
|
-
kind: "not-found",
|
|
2625
|
-
message: `No creator found with name "${name}"`
|
|
2626
|
-
};
|
|
2627
|
-
}
|
|
2628
|
-
const profile = response.data.profile;
|
|
2629
|
-
if (!profile.creatorCoin) {
|
|
2630
|
-
return {
|
|
2631
|
-
kind: "not-found",
|
|
2632
|
-
message: `"${name}" does not have a creator coin`
|
|
2633
|
-
};
|
|
2634
|
-
}
|
|
2635
|
-
return resolveByAddress(profile.creatorCoin.address);
|
|
2636
|
-
}
|
|
2637
|
-
async function resolveCoin(ref) {
|
|
2638
|
-
switch (ref.kind) {
|
|
2639
|
-
case "address":
|
|
2640
|
-
return resolveByAddress(ref.address);
|
|
2641
|
-
case "prefixed":
|
|
2642
|
-
if (ref.type === "trend") {
|
|
2643
|
-
return resolveByTrendTicker(ref.name);
|
|
2644
|
-
}
|
|
2645
|
-
return resolveByCreatorName(ref.name);
|
|
2646
|
-
case "ambiguous":
|
|
2647
|
-
return resolveByCreatorName(ref.name);
|
|
2648
|
-
}
|
|
2649
|
-
}
|
|
2650
|
-
|
|
2651
|
-
// src/components/CoinDetail.tsx
|
|
2652
|
-
import { Box as Box5, Text as Text5 } from "ink";
|
|
2653
|
-
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2654
|
-
var LABEL_WIDTH = 18;
|
|
2655
|
-
function Row({
|
|
2656
|
-
label,
|
|
2657
|
-
children
|
|
2658
|
-
}) {
|
|
2659
|
-
return /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
2660
|
-
/* @__PURE__ */ jsx6(Box5, { width: LABEL_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: label }) }),
|
|
2661
|
-
/* @__PURE__ */ jsx6(Text5, { children })
|
|
2662
|
-
] });
|
|
2801
|
+
// src/components/CoinDetail.tsx
|
|
2802
|
+
import { Box as Box5, Text as Text5 } from "ink";
|
|
2803
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2804
|
+
var LABEL_WIDTH = 18;
|
|
2805
|
+
function Row({
|
|
2806
|
+
label,
|
|
2807
|
+
children
|
|
2808
|
+
}) {
|
|
2809
|
+
return /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
2810
|
+
/* @__PURE__ */ jsx6(Box5, { width: LABEL_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: label }) }),
|
|
2811
|
+
/* @__PURE__ */ jsx6(Text5, { children })
|
|
2812
|
+
] });
|
|
2663
2813
|
}
|
|
2664
2814
|
function CoinDetail({ coin }) {
|
|
2665
2815
|
const change = formatMcapChange(coin.marketCap, coin.marketCapDelta24h);
|
|
@@ -2702,29 +2852,87 @@ function formatCoinJson(coin) {
|
|
|
2702
2852
|
creatorHandle: coin.creatorHandle ?? null
|
|
2703
2853
|
};
|
|
2704
2854
|
}
|
|
2705
|
-
|
|
2706
|
-
|
|
2855
|
+
function outputCoin(json, coin) {
|
|
2856
|
+
outputData(json, {
|
|
2857
|
+
json: formatCoinJson(coin),
|
|
2858
|
+
render: () => {
|
|
2859
|
+
renderOnce(/* @__PURE__ */ jsx7(CoinDetail, { coin }));
|
|
2860
|
+
}
|
|
2861
|
+
});
|
|
2862
|
+
}
|
|
2863
|
+
var getCommand = new Command5("get").description("Look up a coin by address or name").argument("[typeOrId]", "Type prefix (creator-coin, trend) or identifier").argument(
|
|
2864
|
+
"[identifier]",
|
|
2865
|
+
"Coin address (0x...) or name (when type prefix is given)"
|
|
2866
|
+
).action(async function(typeOrId, identifier) {
|
|
2707
2867
|
const json = getJson(this);
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
if (type === "post" && !identifier.startsWith("0x")) {
|
|
2717
|
-
outputErrorAndExit(
|
|
2718
|
-
json,
|
|
2719
|
-
"Posts can only be looked up by address.",
|
|
2720
|
-
"Use: zora get 0x..."
|
|
2721
|
-
);
|
|
2868
|
+
let parsed;
|
|
2869
|
+
try {
|
|
2870
|
+
parsed = parsePositionalCoinArgs(typeOrId, identifier);
|
|
2871
|
+
} catch (err) {
|
|
2872
|
+
if (err instanceof CoinArgError) {
|
|
2873
|
+
outputErrorAndExit(json, err.message, err.suggestion);
|
|
2874
|
+
}
|
|
2875
|
+
throw err;
|
|
2722
2876
|
}
|
|
2723
|
-
const ref = parseCoinRef(identifier, opts.type);
|
|
2724
2877
|
const apiKey = getApiKey();
|
|
2725
2878
|
if (apiKey) {
|
|
2726
2879
|
setApiKey4(apiKey);
|
|
2727
2880
|
}
|
|
2881
|
+
if (parsed.kind === "ambiguous-name") {
|
|
2882
|
+
let ambResult;
|
|
2883
|
+
try {
|
|
2884
|
+
ambResult = await resolveAmbiguousName(parsed.name);
|
|
2885
|
+
} catch (err) {
|
|
2886
|
+
outputErrorAndExit(
|
|
2887
|
+
json,
|
|
2888
|
+
`Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2889
|
+
);
|
|
2890
|
+
return;
|
|
2891
|
+
}
|
|
2892
|
+
if (ambResult.kind === "not-found") {
|
|
2893
|
+
outputErrorAndExit(json, ambResult.message);
|
|
2894
|
+
return;
|
|
2895
|
+
}
|
|
2896
|
+
if (ambResult.kind === "ambiguous") {
|
|
2897
|
+
if (json) {
|
|
2898
|
+
outputData(json, {
|
|
2899
|
+
json: {
|
|
2900
|
+
matches: [
|
|
2901
|
+
{ type: "creator-coin", ...formatCoinJson(ambResult.creator) },
|
|
2902
|
+
{ type: "trend", ...formatCoinJson(ambResult.trend) }
|
|
2903
|
+
],
|
|
2904
|
+
hint: `Use: zora get creator-coin ${parsed.name} or zora get trend ${parsed.name}`
|
|
2905
|
+
},
|
|
2906
|
+
render: () => {
|
|
2907
|
+
}
|
|
2908
|
+
});
|
|
2909
|
+
} else {
|
|
2910
|
+
outputCoin(false, ambResult.creator);
|
|
2911
|
+
console.log("");
|
|
2912
|
+
outputCoin(false, ambResult.trend);
|
|
2913
|
+
console.log(
|
|
2914
|
+
`
|
|
2915
|
+
\x1B[2mUse \`zora get creator-coin ${parsed.name}\` or \`zora get trend ${parsed.name}\` for a specific type.\x1B[0m`
|
|
2916
|
+
);
|
|
2917
|
+
}
|
|
2918
|
+
track("cli_get", {
|
|
2919
|
+
lookup_type: "name",
|
|
2920
|
+
found: true,
|
|
2921
|
+
ambiguous: true,
|
|
2922
|
+
output_format: json ? "json" : "text"
|
|
2923
|
+
});
|
|
2924
|
+
return;
|
|
2925
|
+
}
|
|
2926
|
+
outputCoin(json, ambResult.coin);
|
|
2927
|
+
track("cli_get", {
|
|
2928
|
+
lookup_type: "name",
|
|
2929
|
+
found: true,
|
|
2930
|
+
coin_type: ambResult.coin.coinType,
|
|
2931
|
+
output_format: json ? "json" : "text"
|
|
2932
|
+
});
|
|
2933
|
+
return;
|
|
2934
|
+
}
|
|
2935
|
+
const ref = coinArgsToRef(parsed);
|
|
2728
2936
|
let result;
|
|
2729
2937
|
try {
|
|
2730
2938
|
result = await resolveCoin(ref);
|
|
@@ -2732,29 +2940,20 @@ var getCommand = new Command5("get").description("Look up a coin by address or n
|
|
|
2732
2940
|
outputErrorAndExit(json, `Request failed: ${apiErrorMessage(err)}`);
|
|
2733
2941
|
return;
|
|
2734
2942
|
}
|
|
2735
|
-
if (type && result.kind === "found" && result.coin.coinType !== type) {
|
|
2736
|
-
outputErrorAndExit(
|
|
2737
|
-
json,
|
|
2738
|
-
`Coin at ${result.coin.address} is a ${result.coin.coinType}, not a ${type}.`,
|
|
2739
|
-
`Use: zora get ${result.coin.address} --type ${result.coin.coinType}`
|
|
2740
|
-
);
|
|
2741
|
-
return;
|
|
2742
|
-
}
|
|
2743
2943
|
if (result.kind === "not-found") {
|
|
2744
2944
|
outputErrorAndExit(json, result.message);
|
|
2745
2945
|
return;
|
|
2746
2946
|
}
|
|
2747
|
-
|
|
2748
|
-
json
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
});
|
|
2947
|
+
if (result.coin.platformBlocked) {
|
|
2948
|
+
outputErrorAndExit(json, bannedCoinMessage(result.coin.address));
|
|
2949
|
+
return;
|
|
2950
|
+
}
|
|
2951
|
+
outputCoin(json, result.coin);
|
|
2753
2952
|
track("cli_get", {
|
|
2754
|
-
lookup_type:
|
|
2755
|
-
coin_type_filter: type
|
|
2756
|
-
found:
|
|
2757
|
-
coin_type: result.
|
|
2953
|
+
lookup_type: typeOrId.startsWith("0x") ? "address" : "name",
|
|
2954
|
+
coin_type_filter: parsed.kind === "typed" ? parsed.type : null,
|
|
2955
|
+
found: true,
|
|
2956
|
+
coin_type: result.coin.coinType,
|
|
2758
2957
|
output_format: json ? "json" : "text"
|
|
2759
2958
|
});
|
|
2760
2959
|
});
|
|
@@ -2827,7 +3026,6 @@ var downsample = (values, maxWidth) => {
|
|
|
2827
3026
|
|
|
2828
3027
|
// src/commands/price-history.tsx
|
|
2829
3028
|
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
2830
|
-
var VALID_TYPES2 = ["creator-coin", "post", "trend"];
|
|
2831
3029
|
var VALID_INTERVALS = ["1h", "24h", "1w", "1m", "ALL"];
|
|
2832
3030
|
var INTERVAL_TO_API_FIELD = {
|
|
2833
3031
|
"1h": "oneHour",
|
|
@@ -2868,54 +3066,7 @@ var fetchPriceHistory = async (address, interval) => {
|
|
|
2868
3066
|
price: Number(p.closePrice)
|
|
2869
3067
|
}));
|
|
2870
3068
|
};
|
|
2871
|
-
|
|
2872
|
-
"--interval <interval>",
|
|
2873
|
-
`Time range: ${VALID_INTERVALS.join(", ")}`,
|
|
2874
|
-
"1w"
|
|
2875
|
-
).action(async function(identifier, opts) {
|
|
2876
|
-
const json = getJson(this);
|
|
2877
|
-
const interval = opts.interval ?? "1w";
|
|
2878
|
-
if (!VALID_INTERVALS.includes(interval)) {
|
|
2879
|
-
outputErrorAndExit(
|
|
2880
|
-
json,
|
|
2881
|
-
`Invalid --interval value: ${interval}.`,
|
|
2882
|
-
`Supported: ${VALID_INTERVALS.join(", ")}`
|
|
2883
|
-
);
|
|
2884
|
-
}
|
|
2885
|
-
if (opts.type !== void 0 && !VALID_TYPES2.includes(opts.type)) {
|
|
2886
|
-
outputErrorAndExit(
|
|
2887
|
-
json,
|
|
2888
|
-
`Invalid --type value: ${opts.type}.`,
|
|
2889
|
-
`Supported: ${VALID_TYPES2.join(", ")}`
|
|
2890
|
-
);
|
|
2891
|
-
}
|
|
2892
|
-
if (opts.type === "post" && !identifier.startsWith("0x")) {
|
|
2893
|
-
outputErrorAndExit(
|
|
2894
|
-
json,
|
|
2895
|
-
"Posts can only be looked up by address.",
|
|
2896
|
-
"Use: zora price-history 0x..."
|
|
2897
|
-
);
|
|
2898
|
-
}
|
|
2899
|
-
const ref = parseCoinRef(identifier, opts.type);
|
|
2900
|
-
const apiKey = getApiKey();
|
|
2901
|
-
if (apiKey) {
|
|
2902
|
-
setApiKey5(apiKey);
|
|
2903
|
-
}
|
|
2904
|
-
let result;
|
|
2905
|
-
try {
|
|
2906
|
-
result = await resolveCoin(ref);
|
|
2907
|
-
} catch (err) {
|
|
2908
|
-
outputErrorAndExit(
|
|
2909
|
-
json,
|
|
2910
|
-
`Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2911
|
-
);
|
|
2912
|
-
return;
|
|
2913
|
-
}
|
|
2914
|
-
if (result.kind === "not-found") {
|
|
2915
|
-
outputErrorAndExit(json, result.message, result.suggestion);
|
|
2916
|
-
return;
|
|
2917
|
-
}
|
|
2918
|
-
const { coin } = result;
|
|
3069
|
+
async function showPriceHistory(json, coin, interval) {
|
|
2919
3070
|
let prices;
|
|
2920
3071
|
try {
|
|
2921
3072
|
prices = await fetchPriceHistory(coin.address, interval);
|
|
@@ -2941,9 +3092,7 @@ var priceHistoryCommand = new Command6("price-history").description("Display pri
|
|
|
2941
3092
|
priceValues[0],
|
|
2942
3093
|
priceValues[priceValues.length - 1]
|
|
2943
3094
|
);
|
|
2944
|
-
const sparklineText = sparkline(
|
|
2945
|
-
downsample(priceValues, MAX_SPARKLINE_WIDTH)
|
|
2946
|
-
);
|
|
3095
|
+
const sparklineText = sparkline(downsample(priceValues, MAX_SPARKLINE_WIDTH));
|
|
2947
3096
|
outputData(json, {
|
|
2948
3097
|
json: {
|
|
2949
3098
|
coin: coin.name,
|
|
@@ -2974,11 +3123,139 @@ var priceHistoryCommand = new Command6("price-history").description("Display pri
|
|
|
2974
3123
|
);
|
|
2975
3124
|
}
|
|
2976
3125
|
});
|
|
3126
|
+
return prices.length;
|
|
3127
|
+
}
|
|
3128
|
+
var priceHistoryCommand = new Command6("price-history").description("Display price history for a coin").argument("[typeOrId]", "Type prefix (creator-coin, trend) or identifier").argument(
|
|
3129
|
+
"[identifier]",
|
|
3130
|
+
"Coin address (0x...) or name (when type prefix is given)"
|
|
3131
|
+
).option(
|
|
3132
|
+
"--interval <interval>",
|
|
3133
|
+
`Time range: ${VALID_INTERVALS.join(", ")}`,
|
|
3134
|
+
"1w"
|
|
3135
|
+
).action(async function(typeOrId, identifier, opts) {
|
|
3136
|
+
const json = getJson(this);
|
|
3137
|
+
const interval = opts.interval ?? "1w";
|
|
3138
|
+
if (!VALID_INTERVALS.includes(interval)) {
|
|
3139
|
+
outputErrorAndExit(
|
|
3140
|
+
json,
|
|
3141
|
+
`Invalid --interval value: ${interval}.`,
|
|
3142
|
+
`Supported: ${VALID_INTERVALS.join(", ")}`
|
|
3143
|
+
);
|
|
3144
|
+
}
|
|
3145
|
+
let parsed;
|
|
3146
|
+
try {
|
|
3147
|
+
parsed = parsePositionalCoinArgs(typeOrId, identifier);
|
|
3148
|
+
} catch (err) {
|
|
3149
|
+
if (err instanceof CoinArgError) {
|
|
3150
|
+
outputErrorAndExit(json, err.message, err.suggestion);
|
|
3151
|
+
}
|
|
3152
|
+
throw err;
|
|
3153
|
+
}
|
|
3154
|
+
const apiKey = getApiKey();
|
|
3155
|
+
if (apiKey) {
|
|
3156
|
+
setApiKey5(apiKey);
|
|
3157
|
+
}
|
|
3158
|
+
if (parsed.kind === "ambiguous-name") {
|
|
3159
|
+
let ambResult;
|
|
3160
|
+
try {
|
|
3161
|
+
ambResult = await resolveAmbiguousName(parsed.name);
|
|
3162
|
+
} catch (err) {
|
|
3163
|
+
outputErrorAndExit(
|
|
3164
|
+
json,
|
|
3165
|
+
`Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
3166
|
+
);
|
|
3167
|
+
return;
|
|
3168
|
+
}
|
|
3169
|
+
if (ambResult.kind === "not-found") {
|
|
3170
|
+
outputErrorAndExit(json, ambResult.message);
|
|
3171
|
+
return;
|
|
3172
|
+
}
|
|
3173
|
+
if (ambResult.kind === "ambiguous") {
|
|
3174
|
+
if (json) {
|
|
3175
|
+
const [creatorPrices, trendPrices] = await Promise.all([
|
|
3176
|
+
fetchPriceHistory(ambResult.creator.address, interval),
|
|
3177
|
+
fetchPriceHistory(ambResult.trend.address, interval)
|
|
3178
|
+
]);
|
|
3179
|
+
outputData(json, {
|
|
3180
|
+
json: {
|
|
3181
|
+
matches: [
|
|
3182
|
+
{
|
|
3183
|
+
type: "creator-coin",
|
|
3184
|
+
coin: ambResult.creator.name,
|
|
3185
|
+
prices: creatorPrices
|
|
3186
|
+
},
|
|
3187
|
+
{
|
|
3188
|
+
type: "trend",
|
|
3189
|
+
coin: ambResult.trend.name,
|
|
3190
|
+
prices: trendPrices
|
|
3191
|
+
}
|
|
3192
|
+
],
|
|
3193
|
+
hint: `Use: zora price-history creator-coin ${parsed.name} or zora price-history trend ${parsed.name}`
|
|
3194
|
+
},
|
|
3195
|
+
render: () => {
|
|
3196
|
+
}
|
|
3197
|
+
});
|
|
3198
|
+
} else {
|
|
3199
|
+
await showPriceHistory(
|
|
3200
|
+
false,
|
|
3201
|
+
ambResult.creator,
|
|
3202
|
+
interval
|
|
3203
|
+
);
|
|
3204
|
+
console.log("");
|
|
3205
|
+
await showPriceHistory(false, ambResult.trend, interval);
|
|
3206
|
+
console.log(
|
|
3207
|
+
`
|
|
3208
|
+
\x1B[2mUse \`zora price-history creator-coin ${parsed.name}\` or \`zora price-history trend ${parsed.name}\` for a specific type.\x1B[0m`
|
|
3209
|
+
);
|
|
3210
|
+
}
|
|
3211
|
+
track("cli_price_history", {
|
|
3212
|
+
lookup_type: "name",
|
|
3213
|
+
ambiguous: true,
|
|
3214
|
+
interval,
|
|
3215
|
+
output_format: json ? "json" : "text"
|
|
3216
|
+
});
|
|
3217
|
+
return;
|
|
3218
|
+
}
|
|
3219
|
+
const dataPoints2 = await showPriceHistory(
|
|
3220
|
+
json,
|
|
3221
|
+
ambResult.coin,
|
|
3222
|
+
interval
|
|
3223
|
+
);
|
|
3224
|
+
track("cli_price_history", {
|
|
3225
|
+
lookup_type: "name",
|
|
3226
|
+
coin_type: ambResult.coin.coinType,
|
|
3227
|
+
interval,
|
|
3228
|
+
data_points: dataPoints2,
|
|
3229
|
+
output_format: json ? "json" : "text"
|
|
3230
|
+
});
|
|
3231
|
+
return;
|
|
3232
|
+
}
|
|
3233
|
+
const ref = coinArgsToRef(parsed);
|
|
3234
|
+
let result;
|
|
3235
|
+
try {
|
|
3236
|
+
result = await resolveCoin(ref);
|
|
3237
|
+
} catch (err) {
|
|
3238
|
+
outputErrorAndExit(
|
|
3239
|
+
json,
|
|
3240
|
+
`Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
3241
|
+
);
|
|
3242
|
+
return;
|
|
3243
|
+
}
|
|
3244
|
+
if (result.kind === "not-found") {
|
|
3245
|
+
outputErrorAndExit(json, result.message, result.suggestion);
|
|
3246
|
+
return;
|
|
3247
|
+
}
|
|
3248
|
+
if (result.coin.platformBlocked) {
|
|
3249
|
+
outputErrorAndExit(json, bannedCoinMessage(result.coin.address));
|
|
3250
|
+
return;
|
|
3251
|
+
}
|
|
3252
|
+
const { coin } = result;
|
|
3253
|
+
const dataPoints = await showPriceHistory(json, coin, interval);
|
|
2977
3254
|
track("cli_price_history", {
|
|
2978
|
-
lookup_type:
|
|
3255
|
+
lookup_type: typeOrId.startsWith("0x") ? "address" : "name",
|
|
2979
3256
|
coin_type: coin.coinType,
|
|
2980
3257
|
interval,
|
|
2981
|
-
data_points:
|
|
3258
|
+
data_points: dataPoints,
|
|
2982
3259
|
output_format: json ? "json" : "text"
|
|
2983
3260
|
});
|
|
2984
3261
|
});
|
|
@@ -2988,7 +3265,7 @@ import { Command as Command7 } from "commander";
|
|
|
2988
3265
|
import confirm3 from "@inquirer/confirm";
|
|
2989
3266
|
import {
|
|
2990
3267
|
erc20Abi as erc20Abi3,
|
|
2991
|
-
formatUnits as
|
|
3268
|
+
formatUnits as formatUnits5,
|
|
2992
3269
|
isAddress as isAddress2,
|
|
2993
3270
|
parseUnits as parseUnits2
|
|
2994
3271
|
} from "viem";
|
|
@@ -3005,12 +3282,12 @@ function printSellQuote(output, info) {
|
|
|
3005
3282
|
coin: info.coinSymbol,
|
|
3006
3283
|
address: info.address,
|
|
3007
3284
|
sell: {
|
|
3008
|
-
amount:
|
|
3285
|
+
amount: formatUnits5(info.amountIn, info.coinDecimals),
|
|
3009
3286
|
raw: info.amountIn.toString(),
|
|
3010
3287
|
symbol: info.coinSymbol
|
|
3011
3288
|
},
|
|
3012
3289
|
estimated: {
|
|
3013
|
-
amount:
|
|
3290
|
+
amount: formatUnits5(BigInt(info.quoteAmountOut), info.outputDecimals),
|
|
3014
3291
|
raw: info.quoteAmountOut,
|
|
3015
3292
|
symbol: info.outputSymbol
|
|
3016
3293
|
},
|
|
@@ -3019,17 +3296,18 @@ function printSellQuote(output, info) {
|
|
|
3019
3296
|
return;
|
|
3020
3297
|
}
|
|
3021
3298
|
console.log(`
|
|
3022
|
-
Sell ${info.coinName}
|
|
3299
|
+
Sell \x1B[1m${info.coinName}\x1B[0m`);
|
|
3300
|
+
console.log(` ${info.coinType} \xB7 ${info.address}
|
|
3023
3301
|
`);
|
|
3024
3302
|
console.log(` Amount ${info.soldFormatted} ${info.coinSymbol}`);
|
|
3025
3303
|
console.log(
|
|
3026
|
-
` You get ~${info.receivedFormatted} ${info.outputSymbol}`
|
|
3304
|
+
` You get ~${info.receivedFormatted} ${info.outputSymbol}${info.receivedUsd ? ` (${info.receivedUsd})` : ""}`
|
|
3027
3305
|
);
|
|
3028
3306
|
console.log(` Slippage ${info.slippagePct}%
|
|
3029
3307
|
`);
|
|
3030
3308
|
}
|
|
3031
3309
|
function printSellResult(output, info) {
|
|
3032
|
-
const receivedAmount =
|
|
3310
|
+
const receivedAmount = formatUnits5(
|
|
3033
3311
|
info.receivedAmountOut,
|
|
3034
3312
|
info.outputDecimals
|
|
3035
3313
|
);
|
|
@@ -3043,7 +3321,7 @@ function printSellResult(output, info) {
|
|
|
3043
3321
|
coin: info.coinSymbol,
|
|
3044
3322
|
address: info.address,
|
|
3045
3323
|
sold: {
|
|
3046
|
-
amount:
|
|
3324
|
+
amount: formatUnits5(info.amountIn, info.coinDecimals),
|
|
3047
3325
|
raw: info.amountIn.toString(),
|
|
3048
3326
|
symbol: info.coinSymbol
|
|
3049
3327
|
},
|
|
@@ -3058,11 +3336,12 @@ function printSellResult(output, info) {
|
|
|
3058
3336
|
return;
|
|
3059
3337
|
}
|
|
3060
3338
|
console.log(`
|
|
3061
|
-
Sold ${info.coinName}
|
|
3339
|
+
Sold \x1B[1m${info.coinName}\x1B[0m`);
|
|
3340
|
+
console.log(` ${info.coinType} \xB7 ${info.address}
|
|
3062
3341
|
`);
|
|
3063
3342
|
console.log(` Sold ${info.soldFormatted} ${info.coinSymbol}`);
|
|
3064
3343
|
console.log(
|
|
3065
|
-
` Received ${info.receivedSource === "quote" ? "~" : ""}${receivedFormatted} ${info.outputSymbol}`
|
|
3344
|
+
` Received ${info.receivedSource === "quote" ? "~" : ""}${receivedFormatted} ${info.outputSymbol}${info.receivedUsd ? ` (${info.receivedUsd})` : ""}`
|
|
3066
3345
|
);
|
|
3067
3346
|
if (info.receivedSource === "quote") {
|
|
3068
3347
|
console.log(" Note based on quote");
|
|
@@ -3070,11 +3349,74 @@ function printSellResult(output, info) {
|
|
|
3070
3349
|
console.log(` Tx ${info.txHash}
|
|
3071
3350
|
`);
|
|
3072
3351
|
}
|
|
3073
|
-
var sellCommand = new Command7("sell").description("Sell a coin").argument(
|
|
3352
|
+
var sellCommand = new Command7("sell").description("Sell a coin").argument(
|
|
3353
|
+
"[typeOrId]",
|
|
3354
|
+
"Type prefix (creator-coin, trend) or coin address/name"
|
|
3355
|
+
).argument("[identifier]", "Coin name (when type prefix is given)").option("--amount <value>", "Sell specific number of coins").option("--usd <value>", "Sell USD equivalent worth of coins").option("--percent <value>", "Sell percentage of coin balance").option("--all", "Sell entire coin balance").option("--to <asset>", "Receive asset: eth, usdc, zora", "eth").option("--token <asset>", "Receive asset: eth, usdc, zora (alias for --to)").option("--quote", "Print quote and exit without trading").option("--yes", "Skip confirmation and execute directly").option("--slippage <pct>", "Slippage tolerance percent", "1").option("--debug", "Print full quote request/response JSON").action(async function(typeOrId, identifier, opts) {
|
|
3074
3356
|
const json = getJson(this);
|
|
3075
3357
|
const debug = opts.debug === true;
|
|
3076
|
-
|
|
3077
|
-
|
|
3358
|
+
let parsed;
|
|
3359
|
+
try {
|
|
3360
|
+
parsed = parsePositionalCoinArgs(typeOrId, identifier);
|
|
3361
|
+
} catch (err) {
|
|
3362
|
+
if (err instanceof CoinArgError) {
|
|
3363
|
+
outputErrorAndExit(json, err.message, err.suggestion);
|
|
3364
|
+
}
|
|
3365
|
+
throw err;
|
|
3366
|
+
}
|
|
3367
|
+
const apiKey = getApiKey();
|
|
3368
|
+
if (apiKey) {
|
|
3369
|
+
setApiKey6(apiKey);
|
|
3370
|
+
}
|
|
3371
|
+
let coinAddress;
|
|
3372
|
+
if (parsed.kind === "address") {
|
|
3373
|
+
if (!isAddress2(parsed.address)) {
|
|
3374
|
+
outputErrorAndExit(json, `Invalid address: ${parsed.address}`);
|
|
3375
|
+
return;
|
|
3376
|
+
}
|
|
3377
|
+
coinAddress = parsed.address;
|
|
3378
|
+
} else if (parsed.kind === "ambiguous-name") {
|
|
3379
|
+
let ambResult;
|
|
3380
|
+
try {
|
|
3381
|
+
ambResult = await resolveAmbiguousName(parsed.name);
|
|
3382
|
+
} catch (err) {
|
|
3383
|
+
outputErrorAndExit(
|
|
3384
|
+
json,
|
|
3385
|
+
`Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
3386
|
+
);
|
|
3387
|
+
return;
|
|
3388
|
+
}
|
|
3389
|
+
if (ambResult.kind === "not-found") {
|
|
3390
|
+
outputErrorAndExit(json, ambResult.message);
|
|
3391
|
+
return;
|
|
3392
|
+
}
|
|
3393
|
+
if (ambResult.kind === "ambiguous") {
|
|
3394
|
+
const { message, suggestion } = formatAmbiguousError(
|
|
3395
|
+
parsed.name,
|
|
3396
|
+
ambResult.creator,
|
|
3397
|
+
ambResult.trend,
|
|
3398
|
+
"sell"
|
|
3399
|
+
);
|
|
3400
|
+
outputErrorAndExit(json, message, suggestion);
|
|
3401
|
+
return;
|
|
3402
|
+
}
|
|
3403
|
+
coinAddress = ambResult.coin.address;
|
|
3404
|
+
} else {
|
|
3405
|
+
const ref = coinArgsToRef(parsed);
|
|
3406
|
+
try {
|
|
3407
|
+
const result = await resolveCoin(ref);
|
|
3408
|
+
if (result.kind === "not-found") {
|
|
3409
|
+
outputErrorAndExit(json, result.message, result.suggestion);
|
|
3410
|
+
return;
|
|
3411
|
+
}
|
|
3412
|
+
coinAddress = result.coin.address;
|
|
3413
|
+
} catch (err) {
|
|
3414
|
+
outputErrorAndExit(
|
|
3415
|
+
json,
|
|
3416
|
+
`Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
3417
|
+
);
|
|
3418
|
+
return;
|
|
3419
|
+
}
|
|
3078
3420
|
}
|
|
3079
3421
|
const output = json ? "json" : "static";
|
|
3080
3422
|
const outputAsset = opts.token ? opts.token.toLowerCase() : opts.to;
|
|
@@ -3099,10 +3441,6 @@ var sellCommand = new Command7("sell").description("Sell a coin").argument("[add
|
|
|
3099
3441
|
);
|
|
3100
3442
|
}
|
|
3101
3443
|
const slippage = slippagePct / 100;
|
|
3102
|
-
const apiKey = getApiKey();
|
|
3103
|
-
if (apiKey) {
|
|
3104
|
-
setApiKey6(apiKey);
|
|
3105
|
-
}
|
|
3106
3444
|
const account = resolveAccount(json);
|
|
3107
3445
|
const { publicClient, walletClient } = createClients(account);
|
|
3108
3446
|
let token;
|
|
@@ -3117,6 +3455,7 @@ var sellCommand = new Command7("sell").description("Sell a coin").argument("[add
|
|
|
3117
3455
|
}
|
|
3118
3456
|
const coinName = token.name;
|
|
3119
3457
|
const coinSymbol = token.symbol;
|
|
3458
|
+
const coinType = mapCoinType(token.coinType);
|
|
3120
3459
|
const coinDecimals = Number(token.decimals ?? 18);
|
|
3121
3460
|
let amountIn;
|
|
3122
3461
|
if (amountMode === "usd") {
|
|
@@ -3128,22 +3467,22 @@ var sellCommand = new Command7("sell").description("Sell a coin").argument("[add
|
|
|
3128
3467
|
);
|
|
3129
3468
|
return;
|
|
3130
3469
|
}
|
|
3131
|
-
const
|
|
3132
|
-
if (
|
|
3470
|
+
const coinPriceUsd2 = await fetchTokenPriceUsd(coinAddress);
|
|
3471
|
+
if (coinPriceUsd2 === null || coinPriceUsd2 <= 0) {
|
|
3133
3472
|
outputErrorAndExit(
|
|
3134
3473
|
json,
|
|
3135
3474
|
`Failed to fetch ${coinSymbol} price for USD conversion.`
|
|
3136
3475
|
);
|
|
3137
3476
|
return;
|
|
3138
3477
|
}
|
|
3139
|
-
const coinAmount = usdVal /
|
|
3478
|
+
const coinAmount = usdVal / coinPriceUsd2;
|
|
3140
3479
|
amountIn = parseUnits2(coinAmount.toFixed(coinDecimals), coinDecimals);
|
|
3141
3480
|
if (amountIn === 0n) {
|
|
3142
3481
|
outputErrorAndExit(json, "Calculated amount is zero. USD too small.");
|
|
3143
3482
|
}
|
|
3144
3483
|
if (debug) {
|
|
3145
3484
|
console.error(
|
|
3146
|
-
`[debug] $${usdVal} USD = ${
|
|
3485
|
+
`[debug] $${usdVal} USD = ${formatUnits5(amountIn, coinDecimals)} ${coinSymbol} (coin price: $${coinPriceUsd2})`
|
|
3147
3486
|
);
|
|
3148
3487
|
}
|
|
3149
3488
|
} else if (amountMode === "amount") {
|
|
@@ -3191,18 +3530,19 @@ var sellCommand = new Command7("sell").description("Sell a coin").argument("[add
|
|
|
3191
3530
|
}
|
|
3192
3531
|
}
|
|
3193
3532
|
}
|
|
3533
|
+
const needsCoinPrice = amountMode !== "usd";
|
|
3534
|
+
const needsOutputPrice = outputToken.fixedPriceUsd == null;
|
|
3535
|
+
const [coinPriceUsd, outputPriceUsd] = await Promise.all([
|
|
3536
|
+
needsCoinPrice ? fetchTokenPriceUsd(coinAddress) : Promise.resolve(null),
|
|
3537
|
+
needsOutputPrice ? fetchTokenPriceUsd(outputToken.priceAddress) : Promise.resolve(null)
|
|
3538
|
+
]);
|
|
3194
3539
|
let swapAmountUsd;
|
|
3195
3540
|
if (amountMode === "usd") {
|
|
3196
3541
|
swapAmountUsd = parsePercentageLikeValue(opts.usd);
|
|
3197
|
-
} else {
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
(Number(formatUnits4(amountIn, coinDecimals)) * coinPriceUsd).toFixed(
|
|
3202
|
-
2
|
|
3203
|
-
)
|
|
3204
|
-
);
|
|
3205
|
-
}
|
|
3542
|
+
} else if (coinPriceUsd !== null && coinPriceUsd > 0) {
|
|
3543
|
+
swapAmountUsd = Number(
|
|
3544
|
+
(Number(formatUnits5(amountIn, coinDecimals)) * coinPriceUsd).toFixed(2)
|
|
3545
|
+
);
|
|
3206
3546
|
}
|
|
3207
3547
|
const tradeParameters = {
|
|
3208
3548
|
sell: { type: "erc20", address: coinAddress },
|
|
@@ -3260,10 +3600,18 @@ ${err instanceof Error ? err.stack || err.message : String(err)}
|
|
|
3260
3600
|
BigInt(quoteAmountOut),
|
|
3261
3601
|
outputToken.decimals
|
|
3262
3602
|
);
|
|
3603
|
+
let receivedUsd;
|
|
3604
|
+
if (outputPriceUsd != null) {
|
|
3605
|
+
const outAmount = Number(
|
|
3606
|
+
formatUnits5(BigInt(quoteAmountOut), outputToken.decimals)
|
|
3607
|
+
);
|
|
3608
|
+
receivedUsd = `~${formatUsd(outAmount * outputPriceUsd)}`;
|
|
3609
|
+
}
|
|
3263
3610
|
if (opts.quote) {
|
|
3264
3611
|
printSellQuote(output, {
|
|
3265
3612
|
coinName,
|
|
3266
3613
|
coinSymbol,
|
|
3614
|
+
coinType,
|
|
3267
3615
|
address: coinAddress,
|
|
3268
3616
|
soldFormatted,
|
|
3269
3617
|
amountIn,
|
|
@@ -3272,7 +3620,8 @@ ${err instanceof Error ? err.stack || err.message : String(err)}
|
|
|
3272
3620
|
quoteAmountOut,
|
|
3273
3621
|
outputSymbol: outputToken.symbol,
|
|
3274
3622
|
outputDecimals: outputToken.decimals,
|
|
3275
|
-
slippagePct
|
|
3623
|
+
slippagePct,
|
|
3624
|
+
receivedUsd
|
|
3276
3625
|
});
|
|
3277
3626
|
track("cli_sell", {
|
|
3278
3627
|
action: "quote",
|
|
@@ -3293,6 +3642,7 @@ ${err instanceof Error ? err.stack || err.message : String(err)}
|
|
|
3293
3642
|
printSellQuote("static", {
|
|
3294
3643
|
coinName,
|
|
3295
3644
|
coinSymbol,
|
|
3645
|
+
coinType,
|
|
3296
3646
|
address: coinAddress,
|
|
3297
3647
|
soldFormatted,
|
|
3298
3648
|
amountIn,
|
|
@@ -3301,7 +3651,8 @@ ${err instanceof Error ? err.stack || err.message : String(err)}
|
|
|
3301
3651
|
quoteAmountOut,
|
|
3302
3652
|
outputSymbol: outputToken.symbol,
|
|
3303
3653
|
outputDecimals: outputToken.decimals,
|
|
3304
|
-
slippagePct
|
|
3654
|
+
slippagePct,
|
|
3655
|
+
receivedUsd
|
|
3305
3656
|
});
|
|
3306
3657
|
const ok = await confirm3({
|
|
3307
3658
|
message: "Confirm?",
|
|
@@ -3358,9 +3709,16 @@ ${err instanceof Error ? err.stack || err.message : String(err)}
|
|
|
3358
3709
|
} catch {
|
|
3359
3710
|
}
|
|
3360
3711
|
}
|
|
3712
|
+
if (outputPriceUsd != null) {
|
|
3713
|
+
const actualAmount = Number(
|
|
3714
|
+
formatUnits5(receivedAmountOut, outputToken.decimals)
|
|
3715
|
+
);
|
|
3716
|
+
receivedUsd = `~${formatUsd(actualAmount * outputPriceUsd)}`;
|
|
3717
|
+
}
|
|
3361
3718
|
printSellResult(output, {
|
|
3362
3719
|
coinName,
|
|
3363
3720
|
coinSymbol,
|
|
3721
|
+
coinType,
|
|
3364
3722
|
address: coinAddress,
|
|
3365
3723
|
amountIn,
|
|
3366
3724
|
coinDecimals,
|
|
@@ -3369,7 +3727,8 @@ ${err instanceof Error ? err.stack || err.message : String(err)}
|
|
|
3369
3727
|
outputSymbol: outputToken.symbol,
|
|
3370
3728
|
outputDecimals: outputToken.decimals,
|
|
3371
3729
|
receivedSource,
|
|
3372
|
-
txHash
|
|
3730
|
+
txHash,
|
|
3731
|
+
receivedUsd
|
|
3373
3732
|
});
|
|
3374
3733
|
track("cli_sell", {
|
|
3375
3734
|
action: "trade",
|
|
@@ -3517,8 +3876,10 @@ var ProfileView = ({
|
|
|
3517
3876
|
] }) });
|
|
3518
3877
|
}
|
|
3519
3878
|
if (!data) return null;
|
|
3520
|
-
const hints = [
|
|
3521
|
-
|
|
3879
|
+
const hints = [
|
|
3880
|
+
"\u2190 \u2192 switch tab",
|
|
3881
|
+
autoRefresh ? `r refresh (${secondsUntilRefresh}s)` : "r refresh"
|
|
3882
|
+
];
|
|
3522
3883
|
hints.push("q quit");
|
|
3523
3884
|
const footer = hints.join(" \xB7 ");
|
|
3524
3885
|
const rankedPosts = data.posts.map((p, i) => ({ ...p, rank: i + 1 }));
|
|
@@ -3581,15 +3942,11 @@ var extractErrorMessage2 = (error) => {
|
|
|
3581
3942
|
}
|
|
3582
3943
|
return JSON.stringify(error);
|
|
3583
3944
|
};
|
|
3584
|
-
var resolveApiKey = (
|
|
3945
|
+
var resolveApiKey = () => {
|
|
3585
3946
|
const apiKey = getApiKey();
|
|
3586
|
-
if (
|
|
3587
|
-
|
|
3588
|
-
json,
|
|
3589
|
-
"Not authenticated. Run 'zora auth configure' to set your API key."
|
|
3590
|
-
);
|
|
3947
|
+
if (apiKey) {
|
|
3948
|
+
setApiKey7(apiKey);
|
|
3591
3949
|
}
|
|
3592
|
-
setApiKey7(apiKey);
|
|
3593
3950
|
};
|
|
3594
3951
|
var formatPostJson = (post, rank) => ({
|
|
3595
3952
|
rank,
|
|
@@ -3665,7 +4022,7 @@ var profileCommand = new Command8("profile").description("View profile activity
|
|
|
3665
4022
|
).action(async function(identifierArg) {
|
|
3666
4023
|
const output = getOutputMode(this, "live");
|
|
3667
4024
|
const json = output === "json";
|
|
3668
|
-
resolveApiKey(
|
|
4025
|
+
resolveApiKey();
|
|
3669
4026
|
const { live, intervalSeconds } = getLiveConfig(this, output);
|
|
3670
4027
|
let identifier = identifierArg;
|
|
3671
4028
|
if (!identifier) {
|
|
@@ -3789,7 +4146,7 @@ import { Command as Command9 } from "commander";
|
|
|
3789
4146
|
import confirm4 from "@inquirer/confirm";
|
|
3790
4147
|
import {
|
|
3791
4148
|
erc20Abi as erc20Abi4,
|
|
3792
|
-
formatUnits as
|
|
4149
|
+
formatUnits as formatUnits6,
|
|
3793
4150
|
isAddress as isAddress3,
|
|
3794
4151
|
parseUnits as parseUnits3
|
|
3795
4152
|
} from "viem";
|
|
@@ -3799,7 +4156,7 @@ var SEND_AMOUNT_CHECKS = {
|
|
|
3799
4156
|
percent: (opts) => opts.percent !== void 0,
|
|
3800
4157
|
all: (opts) => opts.all === true
|
|
3801
4158
|
};
|
|
3802
|
-
var
|
|
4159
|
+
var KNOWN_TOKEN_NAMES = /* @__PURE__ */ new Set(["eth", "usdc", "zora"]);
|
|
3803
4160
|
function printSendPreview(info) {
|
|
3804
4161
|
const usdStr = info.amountUsd != null ? ` ($${info.amountUsd.toFixed(2)})` : "";
|
|
3805
4162
|
console.log(`
|
|
@@ -3820,7 +4177,7 @@ function printSendResult(json, info) {
|
|
|
3820
4177
|
coin: info.symbol,
|
|
3821
4178
|
address: info.address,
|
|
3822
4179
|
sent: {
|
|
3823
|
-
amount:
|
|
4180
|
+
amount: formatUnits6(info.amount, info.decimals),
|
|
3824
4181
|
raw: info.amount.toString(),
|
|
3825
4182
|
symbol: info.symbol,
|
|
3826
4183
|
amountUsd: info.amountUsd
|
|
@@ -3841,7 +4198,10 @@ function printSendResult(json, info) {
|
|
|
3841
4198
|
console.log(` Tx ${info.txHash}
|
|
3842
4199
|
`);
|
|
3843
4200
|
}
|
|
3844
|
-
var sendCommand = new Command9("send").description("Send coins or ETH to an address").argument(
|
|
4201
|
+
var sendCommand = new Command9("send").description("Send coins or ETH to an address").argument(
|
|
4202
|
+
"[typeOrId]",
|
|
4203
|
+
"Token (eth, usdc, zora), type prefix (creator-coin, trend), or coin address/name"
|
|
4204
|
+
).argument("[identifier]", "Coin name (when type prefix is given)").option("--to <address>", "Recipient address (0x...)").option("--amount <value>", "Send specific amount").option("--percent <value>", "Send percentage of balance (1-100)").option("--all", "Send entire balance").option("--yes", "Skip confirmation").action(async function(firstArg, secondArg, opts) {
|
|
3845
4205
|
const json = getJson(this);
|
|
3846
4206
|
if (!opts.to) {
|
|
3847
4207
|
outputErrorAndExit(
|
|
@@ -3858,24 +4218,15 @@ var sendCommand = new Command9("send").description("Send coins or ETH to an addr
|
|
|
3858
4218
|
);
|
|
3859
4219
|
}
|
|
3860
4220
|
const recipient = opts.to;
|
|
3861
|
-
if (opts.type !== void 0 && !VALID_TYPES3.includes(opts.type)) {
|
|
3862
|
-
outputErrorAndExit(
|
|
3863
|
-
json,
|
|
3864
|
-
`Invalid --type value: ${opts.type}.`,
|
|
3865
|
-
`Supported: ${VALID_TYPES3.join(", ")}`
|
|
3866
|
-
);
|
|
3867
|
-
}
|
|
3868
4221
|
const amountMode = getAmountMode(
|
|
3869
4222
|
json,
|
|
3870
4223
|
opts,
|
|
3871
4224
|
SEND_AMOUNT_CHECKS,
|
|
3872
4225
|
"--amount, --percent, or --all"
|
|
3873
4226
|
);
|
|
3874
|
-
const
|
|
4227
|
+
const isKnownToken = KNOWN_TOKEN_NAMES.has(firstArg.toLowerCase());
|
|
4228
|
+
const isEth = firstArg.toLowerCase() === "eth";
|
|
3875
4229
|
if (isEth) {
|
|
3876
|
-
if (opts.type) {
|
|
3877
|
-
outputErrorAndExit(json, "--type is not valid when sending ETH.");
|
|
3878
|
-
}
|
|
3879
4230
|
const account = resolveAccount(json);
|
|
3880
4231
|
const { publicClient, walletClient } = createClients(account);
|
|
3881
4232
|
const balance = await publicClient.getBalance({
|
|
@@ -3913,14 +4264,14 @@ var sendCommand = new Command9("send").description("Send coins or ETH to an addr
|
|
|
3913
4264
|
if (amount + GAS_RESERVE > balance) {
|
|
3914
4265
|
outputErrorAndExit(
|
|
3915
4266
|
json,
|
|
3916
|
-
`Insufficient balance. Have ${
|
|
4267
|
+
`Insufficient balance. Have ${formatAmountDisplay(balance, 18)} ETH (need to reserve ~${formatAmountDisplay(GAS_RESERVE, 18)} ETH for gas).`
|
|
3917
4268
|
);
|
|
3918
4269
|
}
|
|
3919
4270
|
} else {
|
|
3920
4271
|
if (balance <= GAS_RESERVE) {
|
|
3921
4272
|
outputErrorAndExit(
|
|
3922
4273
|
json,
|
|
3923
|
-
`Balance too low (${
|
|
4274
|
+
`Balance too low (${formatAmountDisplay(balance, 18)} ETH). Need >${formatAmountDisplay(GAS_RESERVE, 18)} ETH for gas.`
|
|
3924
4275
|
);
|
|
3925
4276
|
}
|
|
3926
4277
|
const spendable = balance - GAS_RESERVE;
|
|
@@ -3943,12 +4294,12 @@ var sendCommand = new Command9("send").description("Send coins or ETH to an addr
|
|
|
3943
4294
|
}
|
|
3944
4295
|
}
|
|
3945
4296
|
}
|
|
3946
|
-
const amountFormatted =
|
|
4297
|
+
const amountFormatted = formatAmountDisplay(amount, 18);
|
|
3947
4298
|
let amountUsd = null;
|
|
3948
4299
|
const ethPriceUsd = await fetchTokenPriceUsd(WETH_ADDRESS);
|
|
3949
4300
|
if (ethPriceUsd != null) {
|
|
3950
4301
|
amountUsd = Number(
|
|
3951
|
-
(Number(
|
|
4302
|
+
(Number(formatUnits6(amount, 18)) * ethPriceUsd).toFixed(2)
|
|
3952
4303
|
);
|
|
3953
4304
|
}
|
|
3954
4305
|
if (!opts.yes) {
|
|
@@ -4008,14 +4359,8 @@ var sendCommand = new Command9("send").description("Send coins or ETH to an addr
|
|
|
4008
4359
|
tx_hash: txHash
|
|
4009
4360
|
});
|
|
4010
4361
|
} else {
|
|
4011
|
-
const knownTokenKey =
|
|
4012
|
-
const knownToken = knownTokenKey !== "eth" && knownTokenKey in BASE_TRADE_TOKENS ? BASE_TRADE_TOKENS[knownTokenKey] : void 0;
|
|
4013
|
-
if (knownToken && opts.type) {
|
|
4014
|
-
outputErrorAndExit(
|
|
4015
|
-
json,
|
|
4016
|
-
`--type is not valid when sending ${knownToken.symbol}.`
|
|
4017
|
-
);
|
|
4018
|
-
}
|
|
4362
|
+
const knownTokenKey = firstArg.toLowerCase();
|
|
4363
|
+
const knownToken = isKnownToken && knownTokenKey !== "eth" && knownTokenKey in BASE_TRADE_TOKENS ? BASE_TRADE_TOKENS[knownTokenKey] : void 0;
|
|
4019
4364
|
let tokenAddress;
|
|
4020
4365
|
let tokenName;
|
|
4021
4366
|
if (knownToken) {
|
|
@@ -4027,21 +4372,60 @@ var sendCommand = new Command9("send").description("Send coins or ETH to an addr
|
|
|
4027
4372
|
if (apiKey) {
|
|
4028
4373
|
setApiKey8(apiKey);
|
|
4029
4374
|
}
|
|
4030
|
-
|
|
4031
|
-
let result;
|
|
4375
|
+
let parsed;
|
|
4032
4376
|
try {
|
|
4033
|
-
|
|
4377
|
+
parsed = parsePositionalCoinArgs(firstArg, secondArg);
|
|
4034
4378
|
} catch (err) {
|
|
4035
|
-
|
|
4036
|
-
json,
|
|
4037
|
-
|
|
4038
|
-
|
|
4379
|
+
if (err instanceof CoinArgError) {
|
|
4380
|
+
outputErrorAndExit(json, err.message, err.suggestion);
|
|
4381
|
+
}
|
|
4382
|
+
throw err;
|
|
4039
4383
|
}
|
|
4040
|
-
if (
|
|
4041
|
-
|
|
4384
|
+
if (parsed.kind === "ambiguous-name") {
|
|
4385
|
+
let ambResult;
|
|
4386
|
+
try {
|
|
4387
|
+
ambResult = await resolveAmbiguousName(parsed.name);
|
|
4388
|
+
} catch (err) {
|
|
4389
|
+
outputErrorAndExit(
|
|
4390
|
+
json,
|
|
4391
|
+
`Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
4392
|
+
);
|
|
4393
|
+
return;
|
|
4394
|
+
}
|
|
4395
|
+
if (ambResult.kind === "not-found") {
|
|
4396
|
+
outputErrorAndExit(json, ambResult.message);
|
|
4397
|
+
return;
|
|
4398
|
+
}
|
|
4399
|
+
if (ambResult.kind === "ambiguous") {
|
|
4400
|
+
const { message, suggestion } = formatAmbiguousError(
|
|
4401
|
+
parsed.name,
|
|
4402
|
+
ambResult.creator,
|
|
4403
|
+
ambResult.trend,
|
|
4404
|
+
"send"
|
|
4405
|
+
);
|
|
4406
|
+
outputErrorAndExit(json, message, suggestion);
|
|
4407
|
+
return;
|
|
4408
|
+
}
|
|
4409
|
+
tokenAddress = ambResult.coin.address;
|
|
4410
|
+
tokenName = ambResult.coin.name;
|
|
4411
|
+
} else {
|
|
4412
|
+
const ref = coinArgsToRef(parsed);
|
|
4413
|
+
try {
|
|
4414
|
+
const result = await resolveCoin(ref);
|
|
4415
|
+
if (result.kind === "not-found") {
|
|
4416
|
+
outputErrorAndExit(json, result.message, result.suggestion);
|
|
4417
|
+
return;
|
|
4418
|
+
}
|
|
4419
|
+
tokenAddress = result.coin.address;
|
|
4420
|
+
tokenName = result.coin.name;
|
|
4421
|
+
} catch (err) {
|
|
4422
|
+
outputErrorAndExit(
|
|
4423
|
+
json,
|
|
4424
|
+
`Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
4425
|
+
);
|
|
4426
|
+
return;
|
|
4427
|
+
}
|
|
4042
4428
|
}
|
|
4043
|
-
tokenAddress = result.coin.address;
|
|
4044
|
-
tokenName = result.coin.name;
|
|
4045
4429
|
}
|
|
4046
4430
|
const account = resolveAccount(json);
|
|
4047
4431
|
const { publicClient, walletClient } = createClients(account);
|
|
@@ -4139,7 +4523,7 @@ var sendCommand = new Command9("send").description("Send coins or ETH to an addr
|
|
|
4139
4523
|
const priceUsd = knownToken?.fixedPriceUsd ?? await fetchTokenPriceUsd(priceAddress);
|
|
4140
4524
|
if (priceUsd != null) {
|
|
4141
4525
|
amountUsd = Number(
|
|
4142
|
-
(Number(
|
|
4526
|
+
(Number(formatUnits6(amount, decimals)) * priceUsd).toFixed(2)
|
|
4143
4527
|
);
|
|
4144
4528
|
}
|
|
4145
4529
|
if (!opts.yes) {
|
|
@@ -4207,32 +4591,33 @@ var sendCommand = new Command9("send").description("Send coins or ETH to an addr
|
|
|
4207
4591
|
}
|
|
4208
4592
|
});
|
|
4209
4593
|
|
|
4210
|
-
// src/commands/setup.
|
|
4594
|
+
// src/commands/setup.tsx
|
|
4211
4595
|
import { Command as Command10 } from "commander";
|
|
4212
|
-
import {
|
|
4596
|
+
import { Text as Text9, Box as Box9 } from "ink";
|
|
4213
4597
|
|
|
4214
4598
|
// src/lib/strings.ts
|
|
4215
|
-
var
|
|
4599
|
+
var DEPOSIT_SOURCES = "Deposit from:\n- Coinbase \u2014 withdraw directly to Base\n- Another wallet (MetaMask, Rainbow, etc.) \u2014 send on Base network\n- Bridge from other chains \u2014 use https://superbridge.app/base";
|
|
4216
4600
|
var NO_WALLET_CONFIGURED = "No wallet configured.";
|
|
4217
4601
|
var NO_WALLET_SUGGESTION = "Run 'zora setup' to create or import one.";
|
|
4218
4602
|
var SAVE_ERROR_HINT = "Check that the directory exists and is writable.";
|
|
4219
|
-
var BACKUP_WARNING = "Back up this file \u2014 it's the only copy of your key.";
|
|
4603
|
+
var BACKUP_WARNING = "Back up this file \u2014 it's the only copy of your key. Zora is not responsible for any loss of funds.";
|
|
4220
4604
|
|
|
4221
|
-
// src/
|
|
4605
|
+
// src/lib/wallet-setup.ts
|
|
4606
|
+
import { generatePrivateKey, privateKeyToAccount as privateKeyToAccount4 } from "viem/accounts";
|
|
4222
4607
|
var isValidPrivateKey = (key) => /^(0x)?[0-9a-fA-F]{64}$/.test(key);
|
|
4223
4608
|
var toAccount = (json, key, errorPrefix) => {
|
|
4224
4609
|
try {
|
|
4225
4610
|
return privateKeyToAccount4(normalizeKey(key));
|
|
4226
4611
|
} catch {
|
|
4227
|
-
outputErrorAndExit(
|
|
4612
|
+
return outputErrorAndExit(
|
|
4228
4613
|
json,
|
|
4229
4614
|
`\u2717 ${errorPrefix} isn't a valid private key.`
|
|
4230
4615
|
);
|
|
4231
4616
|
}
|
|
4232
4617
|
};
|
|
4233
|
-
var
|
|
4234
|
-
|
|
4235
|
-
const nonInteractive =
|
|
4618
|
+
var walletExistsWarning = (truncated) => `Wallet already configured: ${truncated}. Make sure your wallet is backed up \u2014 Zora is not responsible for any loss of funds.`;
|
|
4619
|
+
async function configureWallet(opts) {
|
|
4620
|
+
const { json, nonInteractive, promptOverwrite } = opts;
|
|
4236
4621
|
const envKey = process.env.ZORA_PRIVATE_KEY;
|
|
4237
4622
|
if (envKey !== void 0) {
|
|
4238
4623
|
if (!isValidPrivateKey(envKey)) {
|
|
@@ -4242,25 +4627,11 @@ var setupCommand = new Command10("setup").description("Set up your Zora wallet")
|
|
|
4242
4627
|
"Fix it and run zora setup again."
|
|
4243
4628
|
);
|
|
4244
4629
|
}
|
|
4245
|
-
const
|
|
4246
|
-
|
|
4247
|
-
json: { source: "env", address: account.address },
|
|
4248
|
-
render: () => {
|
|
4249
|
-
console.log(" Using wallet from ZORA_PRIVATE_KEY.\n");
|
|
4250
|
-
console.log(` Address: ${account.address}
|
|
4251
|
-
`);
|
|
4252
|
-
console.log(` ${DEPOSIT_INSTRUCTIONS}`);
|
|
4253
|
-
}
|
|
4254
|
-
});
|
|
4255
|
-
track("cli_setup", {
|
|
4256
|
-
action: "env_detected",
|
|
4257
|
-
source: "env",
|
|
4258
|
-
output_format: json ? "json" : "text"
|
|
4259
|
-
});
|
|
4260
|
-
return;
|
|
4630
|
+
const account2 = toAccount(json, envKey, "ZORA_PRIVATE_KEY");
|
|
4631
|
+
return { action: "env_detected", address: account2.address };
|
|
4261
4632
|
}
|
|
4262
4633
|
let existing;
|
|
4263
|
-
if (!
|
|
4634
|
+
if (!opts.force) {
|
|
4264
4635
|
try {
|
|
4265
4636
|
existing = getPrivateKey();
|
|
4266
4637
|
} catch (err) {
|
|
@@ -4272,20 +4643,33 @@ var setupCommand = new Command10("setup").description("Set up your Zora wallet")
|
|
|
4272
4643
|
}
|
|
4273
4644
|
}
|
|
4274
4645
|
if (existing) {
|
|
4275
|
-
const
|
|
4276
|
-
const truncated =
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
"
|
|
4646
|
+
const account2 = toAccount(json, existing, "Stored private key");
|
|
4647
|
+
const truncated = `${account2.address.slice(0, 6)}\u2026${account2.address.slice(-4)}`;
|
|
4648
|
+
const warning = walletExistsWarning(truncated);
|
|
4649
|
+
if (promptOverwrite) {
|
|
4650
|
+
if (nonInteractive) {
|
|
4651
|
+
return { action: "skipped", address: account2.address, warning };
|
|
4652
|
+
}
|
|
4653
|
+
const overwrite = await confirmOrDefault(
|
|
4654
|
+
{ message: "Overwrite wallet configuration?", default: false },
|
|
4655
|
+
false
|
|
4284
4656
|
);
|
|
4657
|
+
if (!overwrite) {
|
|
4658
|
+
return { action: "skipped", address: account2.address, warning };
|
|
4659
|
+
}
|
|
4660
|
+
} else {
|
|
4661
|
+
if (!opts.force) {
|
|
4662
|
+
outputErrorAndExit(
|
|
4663
|
+
json,
|
|
4664
|
+
`${warning}
|
|
4665
|
+
Wallet already exists.`,
|
|
4666
|
+
"Use --force to overwrite."
|
|
4667
|
+
);
|
|
4668
|
+
}
|
|
4285
4669
|
}
|
|
4286
4670
|
}
|
|
4287
4671
|
let choice;
|
|
4288
|
-
if (
|
|
4672
|
+
if (opts.create) {
|
|
4289
4673
|
choice = "create";
|
|
4290
4674
|
} else {
|
|
4291
4675
|
choice = await selectOrDefault(
|
|
@@ -4319,7 +4703,7 @@ var setupCommand = new Command10("setup").description("Set up your Zora wallet")
|
|
|
4319
4703
|
);
|
|
4320
4704
|
}
|
|
4321
4705
|
}
|
|
4322
|
-
const
|
|
4706
|
+
const account2 = toAccount(json, importedKey, "Imported key");
|
|
4323
4707
|
try {
|
|
4324
4708
|
savePrivateKey(importedKey);
|
|
4325
4709
|
} catch {
|
|
@@ -4329,64 +4713,201 @@ var setupCommand = new Command10("setup").description("Set up your Zora wallet")
|
|
|
4329
4713
|
SAVE_ERROR_HINT
|
|
4330
4714
|
);
|
|
4331
4715
|
}
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4716
|
+
return {
|
|
4717
|
+
action: "imported",
|
|
4718
|
+
address: account2.address,
|
|
4719
|
+
path: getWalletPath()
|
|
4720
|
+
};
|
|
4721
|
+
}
|
|
4722
|
+
const privateKey = generatePrivateKey();
|
|
4723
|
+
const account = toAccount(json, privateKey, "Generated key");
|
|
4724
|
+
try {
|
|
4725
|
+
savePrivateKey(privateKey);
|
|
4726
|
+
} catch {
|
|
4727
|
+
outputErrorAndExit(
|
|
4728
|
+
json,
|
|
4729
|
+
`\u2717 Couldn't save to ${getWalletPath()}.`,
|
|
4730
|
+
SAVE_ERROR_HINT
|
|
4731
|
+
);
|
|
4732
|
+
}
|
|
4733
|
+
return {
|
|
4734
|
+
action: "created",
|
|
4735
|
+
address: account.address,
|
|
4736
|
+
path: getWalletPath()
|
|
4737
|
+
};
|
|
4738
|
+
}
|
|
4739
|
+
|
|
4740
|
+
// src/lib/ansi.ts
|
|
4741
|
+
var DIM = "\x1B[2m";
|
|
4742
|
+
var BOLD = "\x1B[1m";
|
|
4743
|
+
var YELLOW = "\x1B[33m";
|
|
4744
|
+
var RESET = "\x1B[0m";
|
|
4745
|
+
var useAnsi = () => process.stdout.isTTY && !process.env.NO_COLOR;
|
|
4746
|
+
|
|
4747
|
+
// src/lib/warning-box.ts
|
|
4748
|
+
function warningBox(text) {
|
|
4749
|
+
if (!useAnsi()) {
|
|
4750
|
+
console.log(`\u26A0 ${text}
|
|
4342
4751
|
`);
|
|
4343
|
-
|
|
4752
|
+
return;
|
|
4753
|
+
}
|
|
4754
|
+
console.log(`${YELLOW}\u26A0 ${text}${RESET}
|
|
4755
|
+
`);
|
|
4756
|
+
}
|
|
4757
|
+
|
|
4758
|
+
// src/commands/setup.tsx
|
|
4759
|
+
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
4760
|
+
function stepLine(step, total, title) {
|
|
4761
|
+
const cols = (process.stdout.columns || 80) - 4;
|
|
4762
|
+
if (!useAnsi()) {
|
|
4763
|
+
console.log(`
|
|
4764
|
+
[${step}/${total}] ${title}`);
|
|
4765
|
+
console.log(`${"\u2500".repeat(Math.max(cols, 20))}
|
|
4344
4766
|
`);
|
|
4345
|
-
console.log(` ${DEPOSIT_INSTRUCTIONS}`);
|
|
4346
|
-
}
|
|
4347
|
-
});
|
|
4348
|
-
track("cli_setup", {
|
|
4349
|
-
action: "imported",
|
|
4350
|
-
source: "file",
|
|
4351
|
-
output_format: json ? "json" : "text"
|
|
4352
|
-
});
|
|
4353
4767
|
return;
|
|
4354
4768
|
}
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
} catch {
|
|
4361
|
-
outputErrorAndExit(
|
|
4362
|
-
json,
|
|
4363
|
-
`\u2717 Couldn't save to ${getWalletPath()}.`,
|
|
4364
|
-
SAVE_ERROR_HINT
|
|
4365
|
-
);
|
|
4366
|
-
}
|
|
4367
|
-
outputData(json, {
|
|
4368
|
-
json: {
|
|
4369
|
-
action: "created",
|
|
4370
|
-
address: account.address,
|
|
4371
|
-
path: getWalletPath()
|
|
4372
|
-
},
|
|
4373
|
-
render: () => {
|
|
4374
|
-
console.log("\n\u2713 Wallet created\n");
|
|
4375
|
-
console.log(` Address: ${account.address}`);
|
|
4376
|
-
console.log(` Private key: saved to ${getWalletPath()}
|
|
4769
|
+
console.log(
|
|
4770
|
+
`
|
|
4771
|
+
${BOLD}${DIM}[${step}/${total}]${RESET} ${BOLD}${title}${RESET}`
|
|
4772
|
+
);
|
|
4773
|
+
console.log(`${DIM}${"\u2500".repeat(Math.max(cols, 20))}${RESET}
|
|
4377
4774
|
`);
|
|
4378
|
-
|
|
4775
|
+
}
|
|
4776
|
+
var setupCommand = new Command10("setup").description("Guided first-time setup").option("--create", "Create a new wallet without prompting").option("--force", "Overwrite existing wallet without prompting").option("--yes", "Skip interactive prompt and execute directly").action(async function(options) {
|
|
4777
|
+
const json = getJson(this);
|
|
4778
|
+
const nonInteractive = getYes(this);
|
|
4779
|
+
if (!json) stepLine(1, 3, "Set up wallet");
|
|
4780
|
+
const walletResult = await configureWallet({
|
|
4781
|
+
json,
|
|
4782
|
+
nonInteractive,
|
|
4783
|
+
create: options.create,
|
|
4784
|
+
force: options.force,
|
|
4785
|
+
promptOverwrite: true
|
|
4786
|
+
});
|
|
4787
|
+
if (!json) {
|
|
4788
|
+
if (walletResult.action === "env_detected") {
|
|
4789
|
+
console.log("Using wallet from ZORA_PRIVATE_KEY.");
|
|
4790
|
+
console.log(`Address: ${walletResult.address}
|
|
4379
4791
|
`);
|
|
4380
|
-
|
|
4792
|
+
} else if (walletResult.action === "skipped") {
|
|
4793
|
+
warningBox(walletResult.warning);
|
|
4794
|
+
console.log(`${DIM}Keeping existing wallet.${RESET}
|
|
4795
|
+
`);
|
|
4796
|
+
} else {
|
|
4797
|
+
const verb = walletResult.action === "created" ? "created" : "imported";
|
|
4798
|
+
console.log(`\u2713 Wallet ${verb}`);
|
|
4799
|
+
console.log(`Address: ${walletResult.address}`);
|
|
4800
|
+
console.log(`Private key: saved to ${walletResult.path}
|
|
4801
|
+
`);
|
|
4802
|
+
warningBox(BACKUP_WARNING);
|
|
4803
|
+
}
|
|
4804
|
+
}
|
|
4805
|
+
if (!json) stepLine(2, 3, "Set up API key (optional)");
|
|
4806
|
+
let apiKeyStatus;
|
|
4807
|
+
const envApiKey = getEnvApiKey();
|
|
4808
|
+
if (envApiKey) {
|
|
4809
|
+
apiKeyStatus = "env_override";
|
|
4810
|
+
if (!json) {
|
|
4811
|
+
console.log("API key is set via ZORA_API_KEY environment variable.\n");
|
|
4812
|
+
}
|
|
4813
|
+
} else {
|
|
4814
|
+
const existingKey = getApiKey();
|
|
4815
|
+
if (existingKey && !options.force) {
|
|
4816
|
+
if (nonInteractive) {
|
|
4817
|
+
apiKeyStatus = "already_set";
|
|
4818
|
+
if (!json) {
|
|
4819
|
+
console.log(
|
|
4820
|
+
`API key already configured: ${maskKey(existingKey)}
|
|
4821
|
+
`
|
|
4822
|
+
);
|
|
4823
|
+
}
|
|
4824
|
+
} else {
|
|
4825
|
+
if (!json) console.log(`Current key: ${maskKey(existingKey)}`);
|
|
4826
|
+
const overwrite = await confirmOrDefault(
|
|
4827
|
+
{ message: "Overwrite API key?", default: false },
|
|
4828
|
+
false
|
|
4829
|
+
);
|
|
4830
|
+
if (!overwrite) {
|
|
4831
|
+
apiKeyStatus = "already_set";
|
|
4832
|
+
if (!json) console.log("");
|
|
4833
|
+
} else {
|
|
4834
|
+
apiKeyStatus = await promptAndSaveApiKey(json);
|
|
4835
|
+
}
|
|
4381
4836
|
}
|
|
4382
|
-
}
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4837
|
+
} else {
|
|
4838
|
+
apiKeyStatus = await promptAndSaveApiKey(json, nonInteractive);
|
|
4839
|
+
}
|
|
4840
|
+
}
|
|
4841
|
+
if (!json) stepLine(3, 3, "Deposit");
|
|
4842
|
+
if (!json) {
|
|
4843
|
+
renderOnce(
|
|
4844
|
+
/* @__PURE__ */ jsxs9(
|
|
4845
|
+
Box9,
|
|
4846
|
+
{
|
|
4847
|
+
flexDirection: "column",
|
|
4848
|
+
borderStyle: "single",
|
|
4849
|
+
borderDimColor: true,
|
|
4850
|
+
paddingX: 1,
|
|
4851
|
+
paddingY: 1,
|
|
4852
|
+
children: [
|
|
4853
|
+
/* @__PURE__ */ jsxs9(Text9, { children: [
|
|
4854
|
+
"Your address: ",
|
|
4855
|
+
/* @__PURE__ */ jsx12(Text9, { bold: true, children: walletResult.address })
|
|
4856
|
+
] }),
|
|
4857
|
+
/* @__PURE__ */ jsxs9(Text9, { children: [
|
|
4858
|
+
"Deposit",
|
|
4859
|
+
" ",
|
|
4860
|
+
/* @__PURE__ */ jsx12(Text9, { bold: true, color: "blue", children: "ETH or USDC on Base" }),
|
|
4861
|
+
" ",
|
|
4862
|
+
"to start trading."
|
|
4863
|
+
] })
|
|
4864
|
+
]
|
|
4865
|
+
}
|
|
4866
|
+
)
|
|
4867
|
+
);
|
|
4868
|
+
console.log(`
|
|
4869
|
+
${DEPOSIT_SOURCES}
|
|
4870
|
+
`);
|
|
4388
4871
|
}
|
|
4872
|
+
outputData(json, {
|
|
4873
|
+
json: { wallet: walletResult, apiKey: apiKeyStatus },
|
|
4874
|
+
render: () => {
|
|
4875
|
+
}
|
|
4876
|
+
});
|
|
4877
|
+
track("cli_setup", {
|
|
4878
|
+
wallet_action: walletResult.action,
|
|
4879
|
+
api_key_status: apiKeyStatus,
|
|
4880
|
+
output_format: json ? "json" : "text"
|
|
4881
|
+
});
|
|
4389
4882
|
});
|
|
4883
|
+
async function promptAndSaveApiKey(json, nonInteractive = false) {
|
|
4884
|
+
if (!json && !nonInteractive) {
|
|
4885
|
+
console.log(
|
|
4886
|
+
"Optional. An API key unlocks higher rate limits for frequent trading."
|
|
4887
|
+
);
|
|
4888
|
+
console.log("Get your API key from: https://zora.co/settings/developer\n");
|
|
4889
|
+
}
|
|
4890
|
+
const apiKey = await passwordOrSkip(
|
|
4891
|
+
{ message: "Paste your API key (Enter to skip):" },
|
|
4892
|
+
nonInteractive
|
|
4893
|
+
);
|
|
4894
|
+
const trimmed = apiKey.trim();
|
|
4895
|
+
if (!trimmed) {
|
|
4896
|
+
if (!json) console.log("Skipped API key configuration.\n");
|
|
4897
|
+
return "skipped";
|
|
4898
|
+
}
|
|
4899
|
+
try {
|
|
4900
|
+
saveApiKey(trimmed);
|
|
4901
|
+
if (!json) console.log(`API key saved to ${getConfigPath()}
|
|
4902
|
+
`);
|
|
4903
|
+
return "saved";
|
|
4904
|
+
} catch (err) {
|
|
4905
|
+
outputErrorAndExit(
|
|
4906
|
+
json,
|
|
4907
|
+
`Failed to save API key: ${fsErrorMessage(err, getConfigPath())}`
|
|
4908
|
+
);
|
|
4909
|
+
}
|
|
4910
|
+
}
|
|
4390
4911
|
|
|
4391
4912
|
// src/commands/wallet.ts
|
|
4392
4913
|
import { Command as Command11 } from "commander";
|
|
@@ -4402,9 +4923,9 @@ var resolvePrivateKey = () => {
|
|
|
4402
4923
|
}
|
|
4403
4924
|
return void 0;
|
|
4404
4925
|
};
|
|
4405
|
-
var walletCommand = new Command11("wallet").description(
|
|
4406
|
-
|
|
4407
|
-
);
|
|
4926
|
+
var walletCommand = new Command11("wallet").description("Manage your Zora wallet").action(function() {
|
|
4927
|
+
this.outputHelp();
|
|
4928
|
+
});
|
|
4408
4929
|
walletCommand.command("info").description("Show wallet address and storage location").action(function() {
|
|
4409
4930
|
const json = getJson(this);
|
|
4410
4931
|
const resolved = resolvePrivateKey();
|
|
@@ -4460,9 +4981,156 @@ walletCommand.command("export").description("Print the raw private key to stdout
|
|
|
4460
4981
|
output_format: json ? "json" : "text"
|
|
4461
4982
|
});
|
|
4462
4983
|
});
|
|
4984
|
+
walletCommand.command("configure").description("Create or import a wallet").option("--create", "Create a new wallet without prompting").option("--force", "Overwrite existing wallet without prompting").option("--yes", "Skip interactive prompt and execute directly").action(async function(options) {
|
|
4985
|
+
const json = getJson(this);
|
|
4986
|
+
const nonInteractive = getYes(this);
|
|
4987
|
+
const result = await configureWallet({
|
|
4988
|
+
json,
|
|
4989
|
+
nonInteractive,
|
|
4990
|
+
create: options.create,
|
|
4991
|
+
force: options.force,
|
|
4992
|
+
promptOverwrite: false
|
|
4993
|
+
});
|
|
4994
|
+
outputData(json, {
|
|
4995
|
+
json: result,
|
|
4996
|
+
render: () => {
|
|
4997
|
+
if (result.action === "env_detected") {
|
|
4998
|
+
console.log(" Using wallet from ZORA_PRIVATE_KEY.\n");
|
|
4999
|
+
console.log(` Address: ${result.address}
|
|
5000
|
+
`);
|
|
5001
|
+
console.log(
|
|
5002
|
+
` Deposit ETH or USDC on Base to start trading.
|
|
5003
|
+
|
|
5004
|
+
${DEPOSIT_SOURCES}`
|
|
5005
|
+
);
|
|
5006
|
+
} else if (result.action === "created" || result.action === "imported") {
|
|
5007
|
+
const verb = result.action === "created" ? "created" : "imported";
|
|
5008
|
+
console.log(`
|
|
5009
|
+
\u2713 Wallet ${verb}
|
|
5010
|
+
`);
|
|
5011
|
+
console.log(` Address: ${result.address}`);
|
|
5012
|
+
console.log(` Private key: saved to ${result.path}
|
|
5013
|
+
`);
|
|
5014
|
+
console.log(` ${BACKUP_WARNING}
|
|
5015
|
+
`);
|
|
5016
|
+
console.log(
|
|
5017
|
+
` Deposit ETH or USDC on Base to start trading.
|
|
5018
|
+
|
|
5019
|
+
${DEPOSIT_SOURCES}`
|
|
5020
|
+
);
|
|
5021
|
+
} else if (result.action === "skipped") {
|
|
5022
|
+
console.log(` Wallet already configured: ${result.address}
|
|
5023
|
+
`);
|
|
5024
|
+
}
|
|
5025
|
+
}
|
|
5026
|
+
});
|
|
5027
|
+
track("cli_wallet_config", {
|
|
5028
|
+
action: result.action,
|
|
5029
|
+
output_format: json ? "json" : "text"
|
|
5030
|
+
});
|
|
5031
|
+
});
|
|
5032
|
+
|
|
5033
|
+
// src/components/StyledHelp.tsx
|
|
5034
|
+
import { Text as Text11, Box as Box11 } from "ink";
|
|
5035
|
+
|
|
5036
|
+
// src/components/KeyValueTable.tsx
|
|
5037
|
+
import { Text as Text10, Box as Box10 } from "ink";
|
|
5038
|
+
import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
5039
|
+
function KeyValueTable({
|
|
5040
|
+
rows,
|
|
5041
|
+
labelWidth
|
|
5042
|
+
}) {
|
|
5043
|
+
const pad = labelWidth ?? Math.max(0, ...rows.map((r) => r.label.length)) + 2;
|
|
5044
|
+
return /* @__PURE__ */ jsx13(Box10, { flexDirection: "column", children: rows.map((row, i) => /* @__PURE__ */ jsxs10(Text10, { children: [
|
|
5045
|
+
/* @__PURE__ */ jsx13(Text10, { bold: true, children: row.label.padEnd(pad) }),
|
|
5046
|
+
/* @__PURE__ */ jsx13(Text10, { dimColor: true, children: row.value })
|
|
5047
|
+
] }, i)) });
|
|
5048
|
+
}
|
|
5049
|
+
|
|
5050
|
+
// src/lib/parse-help.ts
|
|
5051
|
+
var DEFAULT_DESC_COLUMN = 38;
|
|
5052
|
+
var TWO_COLUMN_REGEX = /^(.+\S)([ \t]{2,})(\S.*)/m;
|
|
5053
|
+
function getDescriptionColumnOffset(sections) {
|
|
5054
|
+
for (const section of sections) {
|
|
5055
|
+
const match = section.content.match(TWO_COLUMN_REGEX);
|
|
5056
|
+
if (match) {
|
|
5057
|
+
return match[1].length + match[2].length;
|
|
5058
|
+
}
|
|
5059
|
+
}
|
|
5060
|
+
return DEFAULT_DESC_COLUMN;
|
|
5061
|
+
}
|
|
5062
|
+
function parseHelpSections(text) {
|
|
5063
|
+
const sections = [];
|
|
5064
|
+
let currentTitle = "";
|
|
5065
|
+
let currentLines = [];
|
|
5066
|
+
for (const line of text.split("\n")) {
|
|
5067
|
+
const match = line.match(/^([A-Z]\w+):(.*)/);
|
|
5068
|
+
if (match) {
|
|
5069
|
+
if (currentTitle) {
|
|
5070
|
+
const content = currentLines.join("\n").replace(/^\n+|\n+$/g, "");
|
|
5071
|
+
sections.push({ title: currentTitle, content });
|
|
5072
|
+
}
|
|
5073
|
+
currentTitle = match[1];
|
|
5074
|
+
currentLines = match[2].trim() ? [match[2].trim()] : [];
|
|
5075
|
+
} else if (currentTitle) {
|
|
5076
|
+
currentLines.push(line.startsWith(" ") ? line.slice(2) : line);
|
|
5077
|
+
}
|
|
5078
|
+
}
|
|
5079
|
+
if (currentTitle) {
|
|
5080
|
+
const content = currentLines.join("\n").replace(/^\n+|\n+$/g, "");
|
|
5081
|
+
sections.push({ title: currentTitle, content });
|
|
5082
|
+
}
|
|
5083
|
+
return sections.filter((s) => s.content);
|
|
5084
|
+
}
|
|
5085
|
+
|
|
5086
|
+
// src/components/StyledHelp.tsx
|
|
5087
|
+
import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
5088
|
+
function StyledHelp({
|
|
5089
|
+
sections,
|
|
5090
|
+
header
|
|
5091
|
+
}) {
|
|
5092
|
+
const descriptionColumnOffset = getDescriptionColumnOffset(sections);
|
|
5093
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", gap: 1, children: [
|
|
5094
|
+
header,
|
|
5095
|
+
/* @__PURE__ */ jsxs11(Box11, { paddingX: 1, children: [
|
|
5096
|
+
/* @__PURE__ */ jsx14(Text11, { color: "yellow", children: "\u26A0 Beta:" }),
|
|
5097
|
+
/* @__PURE__ */ jsx14(Text11, { children: " This CLI is in beta and should be used with caution." })
|
|
5098
|
+
] }),
|
|
5099
|
+
sections.map((section, i) => {
|
|
5100
|
+
const hasTwoColumns = TWO_COLUMN_REGEX.test(section.content);
|
|
5101
|
+
const rows = hasTwoColumns ? section.content.split("\n").map((line) => {
|
|
5102
|
+
const m = line.match(TWO_COLUMN_REGEX);
|
|
5103
|
+
if (m)
|
|
5104
|
+
return {
|
|
5105
|
+
label: m[1],
|
|
5106
|
+
value: m[3][0].toUpperCase() + m[3].slice(1)
|
|
5107
|
+
};
|
|
5108
|
+
return { label: "", value: line.trimStart() };
|
|
5109
|
+
}) : null;
|
|
5110
|
+
return /* @__PURE__ */ jsxs11(
|
|
5111
|
+
Box11,
|
|
5112
|
+
{
|
|
5113
|
+
flexDirection: "column",
|
|
5114
|
+
borderStyle: "single",
|
|
5115
|
+
borderDimColor: true,
|
|
5116
|
+
paddingX: 1,
|
|
5117
|
+
paddingY: 1,
|
|
5118
|
+
children: [
|
|
5119
|
+
/* @__PURE__ */ jsx14(Text11, { bold: true, children: section.title }),
|
|
5120
|
+
rows ? /* @__PURE__ */ jsx14(KeyValueTable, { rows, labelWidth: descriptionColumnOffset }) : /* @__PURE__ */ jsx14(Text11, { children: section.content })
|
|
5121
|
+
]
|
|
5122
|
+
},
|
|
5123
|
+
i
|
|
5124
|
+
);
|
|
5125
|
+
})
|
|
5126
|
+
] });
|
|
5127
|
+
}
|
|
5128
|
+
|
|
5129
|
+
// src/components/StyledHelpHeader.tsx
|
|
5130
|
+
import { Text as Text13, Box as Box13 } from "ink";
|
|
4463
5131
|
|
|
4464
5132
|
// src/components/Zorb.tsx
|
|
4465
|
-
import { Text as
|
|
5133
|
+
import { Text as Text12, Box as Box12 } from "ink";
|
|
4466
5134
|
|
|
4467
5135
|
// src/lib/zorb-pixels.ts
|
|
4468
5136
|
function supportsTruecolor() {
|
|
@@ -4607,7 +5275,7 @@ function generateZorbPixels(size) {
|
|
|
4607
5275
|
}
|
|
4608
5276
|
|
|
4609
5277
|
// src/components/Zorb.tsx
|
|
4610
|
-
import { jsx as
|
|
5278
|
+
import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
4611
5279
|
var LOWER_HALF_BLOCK = "\u2584";
|
|
4612
5280
|
var UPPER_HALF_BLOCK = "\u2580";
|
|
4613
5281
|
function rgbString([r, g, b]) {
|
|
@@ -4630,19 +5298,19 @@ function Zorb({ size = 20 }) {
|
|
|
4630
5298
|
const topIsBlack = isBlack(top);
|
|
4631
5299
|
const bottomIsBlack = isBlack(bottom);
|
|
4632
5300
|
if (topIsBlack && bottomIsBlack) {
|
|
4633
|
-
cells.push(/* @__PURE__ */
|
|
5301
|
+
cells.push(/* @__PURE__ */ jsx15(Text12, { children: " " }, x));
|
|
4634
5302
|
} else if (topIsBlack) {
|
|
4635
5303
|
cells.push(
|
|
4636
|
-
/* @__PURE__ */
|
|
5304
|
+
/* @__PURE__ */ jsx15(Text12, { color: rgbString(bottom), children: LOWER_HALF_BLOCK }, x)
|
|
4637
5305
|
);
|
|
4638
5306
|
} else if (bottomIsBlack) {
|
|
4639
5307
|
cells.push(
|
|
4640
|
-
/* @__PURE__ */
|
|
5308
|
+
/* @__PURE__ */ jsx15(Text12, { color: rgbString(top), children: UPPER_HALF_BLOCK }, x)
|
|
4641
5309
|
);
|
|
4642
5310
|
} else {
|
|
4643
5311
|
cells.push(
|
|
4644
|
-
/* @__PURE__ */
|
|
4645
|
-
|
|
5312
|
+
/* @__PURE__ */ jsx15(
|
|
5313
|
+
Text12,
|
|
4646
5314
|
{
|
|
4647
5315
|
backgroundColor: rgbString(top),
|
|
4648
5316
|
color: rgbString(bottom),
|
|
@@ -4653,25 +5321,92 @@ function Zorb({ size = 20 }) {
|
|
|
4653
5321
|
);
|
|
4654
5322
|
}
|
|
4655
5323
|
}
|
|
4656
|
-
rows.push(/* @__PURE__ */
|
|
5324
|
+
rows.push(/* @__PURE__ */ jsx15(Text12, { children: cells }, y));
|
|
4657
5325
|
}
|
|
4658
|
-
return /* @__PURE__ */
|
|
4659
|
-
/* @__PURE__ */
|
|
5326
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
|
|
5327
|
+
/* @__PURE__ */ jsx15(Text12, { children: " " }),
|
|
4660
5328
|
rows,
|
|
4661
|
-
/* @__PURE__ */
|
|
5329
|
+
/* @__PURE__ */ jsx15(Text12, { children: " " })
|
|
4662
5330
|
] });
|
|
4663
5331
|
}
|
|
4664
5332
|
|
|
5333
|
+
// src/components/StyledHelpHeader.tsx
|
|
5334
|
+
import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
5335
|
+
function StyledHelpHeader({
|
|
5336
|
+
sections
|
|
5337
|
+
}) {
|
|
5338
|
+
const descriptionColumnOffset = getDescriptionColumnOffset(sections);
|
|
5339
|
+
return /* @__PURE__ */ jsxs13(
|
|
5340
|
+
Box13,
|
|
5341
|
+
{
|
|
5342
|
+
flexDirection: "row",
|
|
5343
|
+
borderStyle: "single",
|
|
5344
|
+
borderDimColor: true,
|
|
5345
|
+
paddingX: 1,
|
|
5346
|
+
paddingY: 1,
|
|
5347
|
+
children: [
|
|
5348
|
+
/* @__PURE__ */ jsx16(Box13, { flexShrink: 0, width: descriptionColumnOffset, children: /* @__PURE__ */ jsx16(Zorb, { size: 20 }) }),
|
|
5349
|
+
/* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", flexGrow: 1, justifyContent: "center", children: [
|
|
5350
|
+
/* @__PURE__ */ jsx16(Text13, { bold: true, children: /* @__PURE__ */ jsxs13(Text13, { backgroundColor: "#3fff00", color: "black", children: [
|
|
5351
|
+
" ",
|
|
5352
|
+
"Zora CLI",
|
|
5353
|
+
" "
|
|
5354
|
+
] }) }),
|
|
5355
|
+
/* @__PURE__ */ jsxs13(Text13, { children: [
|
|
5356
|
+
/* @__PURE__ */ jsx16(Text13, { dimColor: true, children: "Trade what's trending. Run" }),
|
|
5357
|
+
" ",
|
|
5358
|
+
/* @__PURE__ */ jsxs13(Text13, { backgroundColor: "#3fff00", color: "black", children: [
|
|
5359
|
+
" ",
|
|
5360
|
+
"zora setup",
|
|
5361
|
+
" "
|
|
5362
|
+
] }),
|
|
5363
|
+
" ",
|
|
5364
|
+
/* @__PURE__ */ jsx16(Text13, { dimColor: true, children: "to get started." })
|
|
5365
|
+
] })
|
|
5366
|
+
] })
|
|
5367
|
+
]
|
|
5368
|
+
}
|
|
5369
|
+
);
|
|
5370
|
+
}
|
|
5371
|
+
|
|
4665
5372
|
// src/index.tsx
|
|
4666
|
-
import { jsx as
|
|
5373
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
4667
5374
|
if (process.env.ZORA_API_TARGET) {
|
|
4668
5375
|
setApiBaseUrl(process.env.ZORA_API_TARGET);
|
|
4669
5376
|
}
|
|
4670
|
-
var version = true ? "0.
|
|
5377
|
+
var version = true ? "1.0.0" : JSON.parse(
|
|
4671
5378
|
readFileSync2(new URL("../package.json", import.meta.url), "utf-8")
|
|
4672
5379
|
).version;
|
|
5380
|
+
function styledHelpWriteOut(showHeader) {
|
|
5381
|
+
return (str) => {
|
|
5382
|
+
if (supportsTruecolor()) {
|
|
5383
|
+
const sections = parseHelpSections(str);
|
|
5384
|
+
if (sections.length > 0) {
|
|
5385
|
+
const header = showHeader ? /* @__PURE__ */ jsx17(StyledHelpHeader, { sections }) : void 0;
|
|
5386
|
+
renderOnce(/* @__PURE__ */ jsx17(StyledHelp, { sections, header }));
|
|
5387
|
+
return;
|
|
5388
|
+
}
|
|
5389
|
+
}
|
|
5390
|
+
process.stdout.write(str);
|
|
5391
|
+
process.stdout.write(
|
|
5392
|
+
"\n\x1B[33m\u26A0 Beta:\x1B[0m This CLI is in beta and should be used with caution.\n"
|
|
5393
|
+
);
|
|
5394
|
+
};
|
|
5395
|
+
}
|
|
4673
5396
|
var buildProgram = () => {
|
|
4674
|
-
const program2 = new Command12().name("zora").description("
|
|
5397
|
+
const program2 = new Command12().name("zora").description("Trade what's trending. Run `zora setup` to get started.").version(version).option("--json", "Output as JSON (for scripts and automation)", false);
|
|
5398
|
+
const helpWidth = (process.stdout.columns || 80) - 4;
|
|
5399
|
+
program2.configureHelp({
|
|
5400
|
+
helpWidth,
|
|
5401
|
+
commandDescription: (cmd) => {
|
|
5402
|
+
if (!cmd.parent && supportsTruecolor()) return "";
|
|
5403
|
+
return cmd.description();
|
|
5404
|
+
}
|
|
5405
|
+
});
|
|
5406
|
+
program2.configureOutput({ writeOut: styledHelpWriteOut(true) });
|
|
5407
|
+
program2.action(() => {
|
|
5408
|
+
program2.outputHelp();
|
|
5409
|
+
});
|
|
4675
5410
|
program2.addCommand(authCommand);
|
|
4676
5411
|
program2.addCommand(balanceCommand);
|
|
4677
5412
|
program2.addCommand(buyCommand);
|
|
@@ -4683,9 +5418,18 @@ var buildProgram = () => {
|
|
|
4683
5418
|
program2.addCommand(walletCommand);
|
|
4684
5419
|
program2.addCommand(sellCommand);
|
|
4685
5420
|
program2.addCommand(sendCommand);
|
|
5421
|
+
const applyToSubcommands = (parent) => {
|
|
5422
|
+
for (const cmd of parent.commands) {
|
|
5423
|
+
cmd.configureHelp({ helpWidth });
|
|
5424
|
+
cmd.configureOutput({ writeOut: styledHelpWriteOut(false) });
|
|
5425
|
+
applyToSubcommands(cmd);
|
|
5426
|
+
}
|
|
5427
|
+
};
|
|
5428
|
+
applyToSubcommands(program2);
|
|
5429
|
+
const argOptionalCommands = /* @__PURE__ */ new Set(["profile"]);
|
|
4686
5430
|
program2.hook("preAction", (_thisCommand, actionCommand) => {
|
|
4687
5431
|
const expected = actionCommand.registeredArguments.length;
|
|
4688
|
-
if (expected > 0 && actionCommand.args.length
|
|
5432
|
+
if (expected > 0 && actionCommand.args.length === 0 && !argOptionalCommands.has(actionCommand.name())) {
|
|
4689
5433
|
actionCommand.outputHelp();
|
|
4690
5434
|
process.exit(1);
|
|
4691
5435
|
}
|
|
@@ -4694,13 +5438,6 @@ var buildProgram = () => {
|
|
|
4694
5438
|
};
|
|
4695
5439
|
var program = buildProgram();
|
|
4696
5440
|
if (!process.env.VITEST) {
|
|
4697
|
-
const showingHelp = process.argv.length <= 2 || process.argv.includes("--help") || process.argv.includes("-h");
|
|
4698
|
-
if (showingHelp && !process.argv.includes("--json") && supportsTruecolor()) {
|
|
4699
|
-
renderOnce(/* @__PURE__ */ jsx13(Zorb, { size: 20 }));
|
|
4700
|
-
}
|
|
4701
|
-
console.warn(
|
|
4702
|
-
"\x1B[33m\u26A0 Beta:\x1B[0m This CLI is in beta and should be used with caution."
|
|
4703
|
-
);
|
|
4704
5441
|
identify();
|
|
4705
5442
|
try {
|
|
4706
5443
|
await program.parseAsync();
|