blumefi 2.5.0 → 2.6.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 +3 -2
- package/cli.js +231 -36
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,8 +13,8 @@ npx blumefi faucet 0xYOUR_ADDRESS
|
|
|
13
13
|
|
|
14
14
|
export WALLET_PRIVATE_KEY=0x...
|
|
15
15
|
|
|
16
|
-
# Launch a token
|
|
17
|
-
npx blumefi pad launch "Moon Cat" MCAT "The first cat on the moon"
|
|
16
|
+
# Launch a token (always include --image!)
|
|
17
|
+
npx blumefi pad launch "Moon Cat" MCAT "The first cat on the moon" --image https://example.com/cat.png
|
|
18
18
|
|
|
19
19
|
# Buy tokens on bonding curve
|
|
20
20
|
npx blumefi pad buy 0xTOKEN_ADDRESS 10
|
|
@@ -40,6 +40,7 @@ blumefi pad sell <token> <amount|all> # Sell tokens for XRP
|
|
|
40
40
|
blumefi pad info <token> # View token info and progress
|
|
41
41
|
blumefi pad search <query> # Search tokens by name or symbol
|
|
42
42
|
blumefi pad tokens # List recent tokens
|
|
43
|
+
blumefi pad update-image <token> <url> # Update display image (creator only)
|
|
43
44
|
```
|
|
44
45
|
|
|
45
46
|
**Launch options:**
|
package/cli.js
CHANGED
|
@@ -7,6 +7,50 @@
|
|
|
7
7
|
|
|
8
8
|
const API = 'https://api.blumefi.com'
|
|
9
9
|
|
|
10
|
+
// ─── ANSI colors (zero deps) ───────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
const c = {
|
|
13
|
+
bold: s => `\x1b[1m${s}\x1b[22m`,
|
|
14
|
+
dim: s => `\x1b[2m${s}\x1b[22m`,
|
|
15
|
+
green: s => `\x1b[32m${s}\x1b[39m`,
|
|
16
|
+
yellow: s => `\x1b[33m${s}\x1b[39m`,
|
|
17
|
+
red: s => `\x1b[31m${s}\x1b[39m`,
|
|
18
|
+
white: s => `\x1b[97m${s}\x1b[39m`,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function progressBar(pct, width = 16) {
|
|
22
|
+
const clamped = Math.max(0, Math.min(pct || 0, 100))
|
|
23
|
+
const filled = Math.round((clamped / 100) * width)
|
|
24
|
+
const empty = width - filled
|
|
25
|
+
const colorFn = clamped < 25 ? c.dim : clamped < 75 ? c.yellow : c.green
|
|
26
|
+
return colorFn('█'.repeat(filled)) + c.dim('░'.repeat(empty))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function fmtPrice(price) {
|
|
30
|
+
const n = parseFloat(price)
|
|
31
|
+
if (isNaN(n) || n === 0) return '0'
|
|
32
|
+
if (n < 0.000001) return n.toExponential(2)
|
|
33
|
+
if (n < 0.01) return n.toFixed(6).replace(/0+$/, '').replace(/\.$/, '.0')
|
|
34
|
+
if (n < 1) return n.toFixed(4)
|
|
35
|
+
return n.toFixed(2)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function fmtNum(val) {
|
|
39
|
+
const n = parseFloat(val)
|
|
40
|
+
if (isNaN(n)) return '—'
|
|
41
|
+
return n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function padRight(str, len) {
|
|
45
|
+
const visible = str.replace(/\x1b\[[0-9;]*m/g, '')
|
|
46
|
+
return str + ' '.repeat(Math.max(0, len - visible.length))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function padLeft(str, len) {
|
|
50
|
+
const visible = str.replace(/\x1b\[[0-9;]*m/g, '')
|
|
51
|
+
return ' '.repeat(Math.max(0, len - visible.length)) + str
|
|
52
|
+
}
|
|
53
|
+
|
|
10
54
|
const NETWORKS = {
|
|
11
55
|
mainnet: {
|
|
12
56
|
chainId: 1440000,
|
|
@@ -791,7 +835,11 @@ async function cmdPadLaunch(name, symbol, desc) {
|
|
|
791
835
|
console.log(` Name: ${name}`)
|
|
792
836
|
console.log(` Symbol: ${symbol}`)
|
|
793
837
|
if (description) console.log(` Description: ${description}`)
|
|
794
|
-
if (imageURI)
|
|
838
|
+
if (imageURI) {
|
|
839
|
+
console.log(` Image: ${imageURI}`)
|
|
840
|
+
} else {
|
|
841
|
+
console.log(` Image: (none — consider adding --image <url>)`)
|
|
842
|
+
}
|
|
795
843
|
console.log(` Supply: ${viem.formatEther(totalSupply)}`)
|
|
796
844
|
console.log(` Dev alloc: ${devPct}%`)
|
|
797
845
|
console.log(` Grad target: ${viem.formatEther(gradReserve)} XRP`)
|
|
@@ -829,6 +877,9 @@ async function cmdPadLaunch(name, symbol, desc) {
|
|
|
829
877
|
console.log(` Creator: ${account.address}`)
|
|
830
878
|
if (tokenAddress) {
|
|
831
879
|
console.log(`\n Next steps:`)
|
|
880
|
+
if (!imageURI) {
|
|
881
|
+
console.log(` blumefi pad update-image ${tokenAddress} <url> Add an image`)
|
|
882
|
+
}
|
|
832
883
|
console.log(` blumefi pad buy ${tokenAddress} 10 Buy with 10 XRP`)
|
|
833
884
|
console.log(` blumefi pad info ${tokenAddress} View token info`)
|
|
834
885
|
}
|
|
@@ -964,23 +1015,34 @@ async function cmdPadInfo(tokenAddress) {
|
|
|
964
1015
|
readContract({ address: tokenAddress, abi: PAD_TOKEN_ABI, functionName: 'totalSupply', args: [] }),
|
|
965
1016
|
])
|
|
966
1017
|
|
|
967
|
-
const
|
|
968
|
-
const
|
|
969
|
-
const
|
|
970
|
-
const progressPct =
|
|
1018
|
+
const priceVal = parseFloat(viem.formatEther(currentPrice))
|
|
1019
|
+
const reserveVal = parseFloat(viem.formatEther(reserveBalance))
|
|
1020
|
+
const gradVal = parseFloat(viem.formatEther(gradReserve))
|
|
1021
|
+
const progressPct = gradVal > 0 ? Math.min((reserveVal / gradVal) * 100, 100) : 0
|
|
971
1022
|
const supplyStr = parseFloat(viem.formatEther(totalSupply)).toLocaleString()
|
|
972
1023
|
const curveStr = parseFloat(viem.formatEther(curveSupply)).toLocaleString()
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
console.log(
|
|
978
|
-
console.log(`
|
|
979
|
-
console.log(`
|
|
980
|
-
console.log(
|
|
1024
|
+
const statusText = graduated
|
|
1025
|
+
? c.green('Graduated (DEX)')
|
|
1026
|
+
: c.yellow('Active (bonding curve)')
|
|
1027
|
+
|
|
1028
|
+
console.log('')
|
|
1029
|
+
console.log(c.bold(` ${symbol}`) + c.dim(` — ${name}`))
|
|
1030
|
+
console.log(c.dim(` ${tokenAddress} · ${chain}`))
|
|
1031
|
+
console.log('')
|
|
1032
|
+
console.log(` Price: ${c.white(fmtPrice(priceVal))} XRP`)
|
|
1033
|
+
console.log(` Reserve: ${c.white(fmtNum(reserveVal))} / ${fmtNum(gradVal)} XRP`)
|
|
1034
|
+
console.log(` Progress: ${progressBar(progressPct, 20)} ${progressPct >= 100 ? c.green(Math.round(progressPct) + '%') : c.yellow(Math.round(progressPct) + '%')}`)
|
|
1035
|
+
console.log(` Status: ${statusText}`)
|
|
1036
|
+
console.log('')
|
|
981
1037
|
console.log(` Total supply: ${supplyStr}`)
|
|
982
|
-
console.log(`
|
|
983
|
-
console.log(`
|
|
1038
|
+
console.log(` Curve supply: ${curveStr}`)
|
|
1039
|
+
console.log(` Contract: ${c.dim(tokenAddress)}`)
|
|
1040
|
+
console.log(` Explorer: ${c.dim(net.explorer + '/address/' + tokenAddress)}`)
|
|
1041
|
+
|
|
1042
|
+
if (desc) {
|
|
1043
|
+
console.log('')
|
|
1044
|
+
console.log(c.dim(` ${truncate(desc, 100)}`))
|
|
1045
|
+
}
|
|
984
1046
|
|
|
985
1047
|
// Show user balance if wallet is set
|
|
986
1048
|
const key = process.env.WALLET_PRIVATE_KEY || process.env.PRIVATE_KEY
|
|
@@ -991,9 +1053,11 @@ async function cmdPadInfo(tokenAddress) {
|
|
|
991
1053
|
address: tokenAddress, abi: PAD_TOKEN_ABI, functionName: 'balanceOf', args: [account.address],
|
|
992
1054
|
})
|
|
993
1055
|
if (userBalance > 0n) {
|
|
994
|
-
console.log(
|
|
1056
|
+
console.log('')
|
|
1057
|
+
console.log(` Your balance: ${c.white(parseFloat(viem.formatEther(userBalance)).toLocaleString())} ${symbol}`)
|
|
995
1058
|
}
|
|
996
1059
|
}
|
|
1060
|
+
console.log('')
|
|
997
1061
|
}
|
|
998
1062
|
|
|
999
1063
|
async function cmdPadSearch(query) {
|
|
@@ -1034,30 +1098,153 @@ async function cmdPadSearch(query) {
|
|
|
1034
1098
|
}
|
|
1035
1099
|
}
|
|
1036
1100
|
|
|
1101
|
+
function renderTokenTable(tokens, total, chain, filter) {
|
|
1102
|
+
const lines = []
|
|
1103
|
+
if (!tokens.length) {
|
|
1104
|
+
lines.push(c.dim(` No tokens found (${chain}, filter: ${filter})`))
|
|
1105
|
+
return lines.join('\n')
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
const active = tokens.filter(t => !t.graduated).length
|
|
1109
|
+
const graduated = tokens.filter(t => t.graduated).length
|
|
1110
|
+
const totalVol = tokens.reduce((sum, t) => sum + (parseFloat(t.volume24h) || 0), 0)
|
|
1111
|
+
|
|
1112
|
+
// Header banner
|
|
1113
|
+
const title = 'BLUMEPAD'
|
|
1114
|
+
const netLabel = chain
|
|
1115
|
+
const summary = `${tokens.length} tokens · ${graduated} graduated · ${fmtNum(totalVol)} XRP 24h vol`
|
|
1116
|
+
const innerWidth = 53
|
|
1117
|
+
lines.push(' ' + c.dim('┌' + '─'.repeat(innerWidth) + '┐'))
|
|
1118
|
+
lines.push(' ' + c.dim('│') + ' ' + c.bold(title) + ' '.repeat(innerWidth - title.length - netLabel.length - 4) + c.dim(netLabel) + ' ' + c.dim('│'))
|
|
1119
|
+
lines.push(' ' + c.dim('│') + ' ' + c.dim(summary) + ' '.repeat(Math.max(0, innerWidth - summary.length - 4)) + ' ' + c.dim('│'))
|
|
1120
|
+
lines.push(' ' + c.dim('└' + '─'.repeat(innerWidth) + '┘'))
|
|
1121
|
+
lines.push('')
|
|
1122
|
+
|
|
1123
|
+
const cols = { symbol: 10, name: 20, price: 14, mcap: 12, vol: 12, progress: 26, status: 10 }
|
|
1124
|
+
|
|
1125
|
+
const header =
|
|
1126
|
+
padRight(c.dim('Symbol'), cols.symbol) +
|
|
1127
|
+
padRight(c.dim('Name'), cols.name) +
|
|
1128
|
+
padLeft(c.dim('Price (XRP)'), cols.price) + ' ' +
|
|
1129
|
+
padLeft(c.dim('MCap'), cols.mcap) + ' ' +
|
|
1130
|
+
padLeft(c.dim('24h Vol'), cols.vol) + ' ' +
|
|
1131
|
+
padRight(c.dim('Progress'), cols.progress) +
|
|
1132
|
+
padRight(c.dim('Status'), cols.status)
|
|
1133
|
+
|
|
1134
|
+
lines.push(' ' + header)
|
|
1135
|
+
lines.push(' ' + c.dim('─'.repeat(106)))
|
|
1136
|
+
|
|
1137
|
+
for (const t of tokens) {
|
|
1138
|
+
const pct = Math.min(t.progress || 0, 100)
|
|
1139
|
+
const pctStr = `${Math.round(pct)}%`
|
|
1140
|
+
const bar = progressBar(pct) + ' ' + (pct >= 100 ? c.green(pctStr) : c.yellow(pctStr))
|
|
1141
|
+
const status = t.graduated ? c.green('Graduated') : c.yellow('Active')
|
|
1142
|
+
|
|
1143
|
+
const row =
|
|
1144
|
+
padRight(c.bold(t.symbol || '???'), cols.symbol) +
|
|
1145
|
+
padRight((t.name || '—').slice(0, 18), cols.name) +
|
|
1146
|
+
padLeft(fmtPrice(t.price), cols.price) + ' ' +
|
|
1147
|
+
padLeft(fmtNum(t.marketCap), cols.mcap) + ' ' +
|
|
1148
|
+
padLeft(fmtNum(t.volume24h), cols.vol) + ' ' +
|
|
1149
|
+
padRight(bar, cols.progress) +
|
|
1150
|
+
padRight(status, cols.status)
|
|
1151
|
+
|
|
1152
|
+
lines.push(' ' + row)
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
lines.push('')
|
|
1156
|
+
if (total > tokens.length) {
|
|
1157
|
+
lines.push(c.dim(` Showing ${tokens.length} of ${total}. Use --limit to see more.`))
|
|
1158
|
+
lines.push('')
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
return lines.join('\n')
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1037
1164
|
async function cmdPadTokens() {
|
|
1038
1165
|
const chain = getChain()
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
const
|
|
1042
|
-
if (
|
|
1166
|
+
const argv = process.argv
|
|
1167
|
+
const flagVal = (name, fallback) => {
|
|
1168
|
+
const idx = argv.indexOf(name)
|
|
1169
|
+
if (idx === -1) return fallback
|
|
1170
|
+
const val = argv[idx + 1]
|
|
1171
|
+
return (val && !val.startsWith('--')) ? val : fallback
|
|
1172
|
+
}
|
|
1173
|
+
const filter = flagVal('--filter', 'all')
|
|
1174
|
+
const sort = flagVal('--sort', 'newest')
|
|
1175
|
+
const limit = flagVal('--limit', '20')
|
|
1176
|
+
const watchMode = argv.includes('--watch') || argv.includes('-w')
|
|
1177
|
+
|
|
1178
|
+
if (!watchMode) {
|
|
1179
|
+
// One-shot mode (existing behavior)
|
|
1180
|
+
const data = await apiFetch(`/pad/tokens?chain=${chain}&filter=${filter}&sort=${sort}&limit=${limit}&offset=0`)
|
|
1181
|
+
const tokens = data.tokens || data.data || []
|
|
1182
|
+
const total = data.total || tokens.length
|
|
1183
|
+
console.log('')
|
|
1184
|
+
console.log(renderTokenTable(tokens, total, chain, filter))
|
|
1185
|
+
return
|
|
1186
|
+
}
|
|
1043
1187
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1188
|
+
// Live dashboard mode
|
|
1189
|
+
const REFRESH = 5
|
|
1190
|
+
const hideCursor = () => process.stdout.write('\x1b[?25l')
|
|
1191
|
+
const showCursor = () => process.stdout.write('\x1b[?25h')
|
|
1192
|
+
const clearScreen = () => process.stdout.write('\x1b[2J\x1b[H')
|
|
1193
|
+
|
|
1194
|
+
let running = true
|
|
1195
|
+
const cleanup = () => {
|
|
1196
|
+
running = false
|
|
1197
|
+
showCursor()
|
|
1198
|
+
console.log(c.dim('\n Goodbye.\n'))
|
|
1199
|
+
process.exit(0)
|
|
1200
|
+
}
|
|
1201
|
+
process.on('SIGINT', cleanup)
|
|
1202
|
+
process.on('SIGTERM', cleanup)
|
|
1203
|
+
|
|
1204
|
+
hideCursor()
|
|
1205
|
+
|
|
1206
|
+
while (running) {
|
|
1207
|
+
let output
|
|
1208
|
+
try {
|
|
1209
|
+
const data = await apiFetch(`/pad/tokens?chain=${chain}&filter=${filter}&sort=${sort}&limit=${limit}&offset=0`)
|
|
1210
|
+
const tokens = data.tokens || data.data || []
|
|
1211
|
+
const total = data.total || tokens.length
|
|
1212
|
+
output = renderTokenTable(tokens, total, chain, filter)
|
|
1213
|
+
} catch (err) {
|
|
1214
|
+
output = c.red(` Error fetching data: ${err.message}`) + '\n' + c.dim(' Will retry...')
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
clearScreen()
|
|
1218
|
+
console.log('')
|
|
1219
|
+
console.log(output)
|
|
1220
|
+
|
|
1221
|
+
const now = new Date()
|
|
1222
|
+
const ts = now.toLocaleTimeString('en-US', { hour12: false })
|
|
1223
|
+
console.log('')
|
|
1224
|
+
console.log(c.dim(` Updated ${ts} · Refreshing in ${REFRESH}s · Ctrl+C to exit`))
|
|
1225
|
+
|
|
1226
|
+
// Countdown sleep — break early if stopped
|
|
1227
|
+
for (let i = 0; i < REFRESH * 10 && running; i++) {
|
|
1228
|
+
await new Promise(r => setTimeout(r, 100))
|
|
1053
1229
|
}
|
|
1054
|
-
} catch {
|
|
1055
|
-
// Fallback: if API doesn't have /pad/tokens, just point to the website
|
|
1056
|
-
console.log(`\n Browse tokens: https://${chain === 'mainnet' ? '' : 'testnet.'}pad.blumefi.com`)
|
|
1057
|
-
console.log(` API: ${API}/pad/tokens?chain=${chain}`)
|
|
1058
1230
|
}
|
|
1059
1231
|
}
|
|
1060
1232
|
|
|
1233
|
+
async function cmdPadStats() {
|
|
1234
|
+
const chain = getChain()
|
|
1235
|
+
const data = await apiFetch(`/pad/stats?chain=${chain}`)
|
|
1236
|
+
|
|
1237
|
+
console.log('')
|
|
1238
|
+
console.log(c.bold(' Blumepad Stats') + c.dim(` (${chain})`))
|
|
1239
|
+
console.log('')
|
|
1240
|
+
console.log(` Total tokens: ${c.white(String(data.totalTokens ?? 0))}`)
|
|
1241
|
+
console.log(` Active: ${c.yellow(String(data.activeTokens ?? 0))}`)
|
|
1242
|
+
console.log(` Graduated: ${c.green(String(data.graduatedTokens ?? 0))}`)
|
|
1243
|
+
console.log(` 24h Volume: ${c.white(fmtNum(data.totalVolume24h ?? 0))} XRP`)
|
|
1244
|
+
console.log(` 24h Trades: ${c.white(String(data.totalTrades24h ?? 0))}`)
|
|
1245
|
+
console.log('')
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1061
1248
|
async function cmdPadUpdateImage(tokenAddress, imageUrl) {
|
|
1062
1249
|
if (!tokenAddress || !imageUrl) {
|
|
1063
1250
|
console.error('Usage: blumefi pad update-image <token_address> <image_url>')
|
|
@@ -1255,14 +1442,21 @@ Chat:
|
|
|
1255
1442
|
chat profile <name> [options] Set your display name and avatar
|
|
1256
1443
|
|
|
1257
1444
|
Pad (Launchpad):
|
|
1445
|
+
pad tokens List tokens with graduation progress
|
|
1446
|
+
pad info <token> Detailed token view with progress bar
|
|
1447
|
+
pad stats Aggregate launchpad stats
|
|
1258
1448
|
pad launch <name> <symbol> [desc] Launch a token on bonding curve
|
|
1259
1449
|
pad buy <token> <xrp_amount> Buy tokens with XRP
|
|
1260
1450
|
pad sell <token> <amount|all> Sell tokens for XRP
|
|
1261
|
-
pad info <token> View token info and progress
|
|
1262
1451
|
pad search <query> Search tokens by name or symbol
|
|
1263
|
-
pad tokens List recent tokens
|
|
1264
1452
|
pad update-image <token> <url> Update token image (creator only)
|
|
1265
1453
|
|
|
1454
|
+
Pad Options:
|
|
1455
|
+
--filter <active|graduated|all> Filter tokens (default: all)
|
|
1456
|
+
--sort <newest|marketcap|progress|price> Sort order (default: newest)
|
|
1457
|
+
--limit <N> Number of tokens (default: 20)
|
|
1458
|
+
--watch, -w Live dashboard (auto-refresh every 5s)
|
|
1459
|
+
|
|
1266
1460
|
Swap (DEX):
|
|
1267
1461
|
swap <amount> <from> <to> Swap tokens (e.g. swap 1 XRP RLUSD)
|
|
1268
1462
|
swap quote <amount> <from> <to> Get a quote without executing
|
|
@@ -1319,8 +1513,9 @@ async function main() {
|
|
|
1319
1513
|
else if (sub === 'info' || sub === 'i') await cmdPadInfo(args[2])
|
|
1320
1514
|
else if (sub === 'search' || sub === 'find') await cmdPadSearch(args[2])
|
|
1321
1515
|
else if (sub === 'tokens' || sub === 'list') await cmdPadTokens()
|
|
1516
|
+
else if (sub === 'stats') await cmdPadStats()
|
|
1322
1517
|
else if (sub === 'update-image') await cmdPadUpdateImage(args[2], args[3])
|
|
1323
|
-
else { console.error(`Unknown: pad ${sub}. Try: launch, buy, sell, info, tokens, update-image`); process.exit(1) }
|
|
1518
|
+
else { console.error(`Unknown: pad ${sub}. Try: launch, buy, sell, info, tokens, stats, update-image`); process.exit(1) }
|
|
1324
1519
|
} else if (cmd === 'swap') {
|
|
1325
1520
|
if (sub === 'quote' || sub === 'q') await cmdSwapQuote(args[2], args[3], args[4])
|
|
1326
1521
|
else if (sub === 'pools' || sub === 'p') await cmdSwapPools()
|
package/package.json
CHANGED