blumefi 2.2.0 → 2.4.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.
Files changed (3) hide show
  1. package/README.md +4 -2
  2. package/cli.js +186 -12
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -38,6 +38,7 @@ blumefi pad launch <name> <symbol> [desc] # Launch a token on bonding curve
38
38
  blumefi pad buy <token> <xrp_amount> # Buy tokens with XRP
39
39
  blumefi pad sell <token> <amount|all> # Sell tokens for XRP
40
40
  blumefi pad info <token> # View token info and progress
41
+ blumefi pad search <query> # Search tokens by name or symbol
41
42
  blumefi pad tokens # List recent tokens
42
43
  ```
43
44
 
@@ -84,12 +85,13 @@ blumefi trade price # Get current XRP price
84
85
 
85
86
  Collateral is in RLUSD. Default leverage is 2x if not specified.
86
87
 
87
- ### Wallet & Network
88
+ ### Wallet & Account
88
89
 
89
90
  ```bash
90
91
  blumefi wallet new # Generate a new wallet
92
+ blumefi balance [address] # Show XRP and token balances
91
93
  blumefi faucet [address] # Get 25 testnet XRP
92
- blumefi status # Show network info, contracts, and balance
94
+ blumefi status # Show network info and contracts
93
95
  ```
94
96
 
95
97
  ### Options
package/cli.js CHANGED
@@ -346,10 +346,25 @@ async function cmdChatMentions(address) {
346
346
  }
347
347
 
348
348
  async function cmdChatProfile(name) {
349
- if (!name) { console.error('Usage: blumefi chat profile <name> [--bio "your bio"]'); process.exit(1) }
349
+ if (!name) {
350
+ console.error('Usage: blumefi chat profile <name> [--bio "your bio"] [--avatar <url>]')
351
+ console.error(' blumefi chat profile "MyAgent" --bio "I trade on XRPL EVM"')
352
+ console.error(' blumefi chat profile "MyAgent" --avatar https://example.com/pic.png')
353
+ console.error(' blumefi chat profile "MyAgent" --bio "Bio" --avatar https://arweave.net/TXID')
354
+ process.exit(1)
355
+ }
350
356
  const bioIdx = process.argv.indexOf('--bio')
351
357
  const bio = bioIdx !== -1 ? process.argv[bioIdx + 1] || '' : ''
352
- const metadata = JSON.stringify(bio ? { bio, platform: 'blumefi-cli' } : { platform: 'blumefi-cli' })
358
+ const avatarIdx = process.argv.indexOf('--avatar')
359
+ const avatarUrl = avatarIdx !== -1 ? process.argv[avatarIdx + 1] || '' : ''
360
+ if (avatarUrl && !avatarUrl.startsWith('https://')) {
361
+ console.error('Error: --avatar must be an HTTPS URL')
362
+ process.exit(1)
363
+ }
364
+ const meta = { platform: 'blumefi-cli' }
365
+ if (bio) meta.bio = bio
366
+ if (avatarUrl) meta.avatarUrl = avatarUrl
367
+ const metadata = JSON.stringify(meta)
353
368
  const chain = getChain()
354
369
  console.log(`Setting profile on ${chain}...`)
355
370
  const { address, explorer } = await sendContractTx({
@@ -358,6 +373,7 @@ async function cmdChatProfile(name) {
358
373
  console.log(`\n Profile set for ${address}`)
359
374
  console.log(` Name: ${name}`)
360
375
  if (bio) console.log(` Bio: ${bio}`)
376
+ if (avatarUrl) console.log(` Avatar: ${avatarUrl}`)
361
377
  console.log(` TX: ${explorer}`)
362
378
  }
363
379
 
@@ -403,8 +419,16 @@ async function cmdSwap(amountStr, fromToken, toToken) {
403
419
  const amountOutMin = amountOut * 99n / 100n
404
420
 
405
421
  const outFormatted = viem.formatUnits(amountOut, toDecimals)
406
- const fromLabel = fromToken.toUpperCase()
407
- const toLabel = toToken.toUpperCase()
422
+
423
+ // Resolve display labels — show symbol instead of raw address
424
+ let fromLabel = fromToken.toUpperCase()
425
+ let toLabel = toToken.toUpperCase()
426
+ if (fromToken.startsWith('0x')) {
427
+ try { fromLabel = await readContract({ address: from.address, abi: ERC20_ABI, functionName: 'symbol', args: [] }) } catch {}
428
+ }
429
+ if (toToken.startsWith('0x')) {
430
+ try { toLabel = await readContract({ address: to.address, abi: ERC20_ABI, functionName: 'symbol', args: [] }) } catch {}
431
+ }
408
432
 
409
433
  console.log(`\n Swap: ${amount} ${fromLabel} -> ~${parseFloat(outFormatted).toFixed(6)} ${toLabel}`)
410
434
  console.log(` Network: ${chain}`)
@@ -480,8 +504,17 @@ async function cmdSwapQuote(amountStr, fromToken, toToken) {
480
504
  const outFormatted = viem.formatUnits(amountOut, toDecimals)
481
505
  const rate = parseFloat(outFormatted) / parseFloat(amountStr)
482
506
 
483
- console.log(`\n Quote: ${amountStr} ${fromToken.toUpperCase()} -> ${parseFloat(outFormatted).toFixed(6)} ${toToken.toUpperCase()}`)
484
- console.log(` Rate: 1 ${fromToken.toUpperCase()} = ${rate.toFixed(6)} ${toToken.toUpperCase()}`)
507
+ let fromLabel = fromToken.toUpperCase()
508
+ let toLabel = toToken.toUpperCase()
509
+ if (fromToken.startsWith('0x')) {
510
+ try { fromLabel = await readContract({ address: from.address, abi: ERC20_ABI, functionName: 'symbol', args: [] }) } catch {}
511
+ }
512
+ if (toToken.startsWith('0x')) {
513
+ try { toLabel = await readContract({ address: to.address, abi: ERC20_ABI, functionName: 'symbol', args: [] }) } catch {}
514
+ }
515
+
516
+ console.log(`\n Quote: ${amountStr} ${fromLabel} -> ${parseFloat(outFormatted).toFixed(6)} ${toLabel}`)
517
+ console.log(` Rate: 1 ${fromLabel} = ${rate.toFixed(6)} ${toLabel}`)
485
518
  console.log(` Network: ${getChain()}`)
486
519
  console.log(`\n Execute: blumefi swap ${amountStr} ${fromToken} ${toToken}`)
487
520
  }
@@ -963,6 +996,44 @@ async function cmdPadInfo(tokenAddress) {
963
996
  }
964
997
  }
965
998
 
999
+ async function cmdPadSearch(query) {
1000
+ if (!query) {
1001
+ console.error('Usage: blumefi pad search <name or symbol>')
1002
+ console.error(' blumefi pad search tulip')
1003
+ process.exit(1)
1004
+ }
1005
+
1006
+ const chain = getChain()
1007
+ const q = query.toLowerCase()
1008
+
1009
+ try {
1010
+ const data = await apiFetch(`/pad/tokens?chain=${chain}&limit=50&sort=recent`)
1011
+ const tokens = data.data || data.tokens || data || []
1012
+ if (!Array.isArray(tokens)) { console.log('No tokens found.'); return }
1013
+
1014
+ const matches = tokens.filter(t => {
1015
+ const name = (t.name || '').toLowerCase()
1016
+ const symbol = (t.symbol || '').toLowerCase()
1017
+ return name.includes(q) || symbol.includes(q)
1018
+ })
1019
+
1020
+ if (!matches.length) { console.log(`\n No tokens matching "${query}" on ${chain}.`); return }
1021
+
1022
+ console.log(`\n Search: "${query}" on ${chain} — ${matches.length} match${matches.length === 1 ? '' : 'es'}\n`)
1023
+ for (const t of matches) {
1024
+ const name = t.name || '???'
1025
+ const symbol = t.symbol || '???'
1026
+ const graduated = t.graduated ? ' [GRADUATED]' : ''
1027
+ const reserve = t.reserveBalance ? ` ${parseFloat(t.reserveBalance).toFixed(1)} XRP` : ''
1028
+ console.log(` ${name} (${symbol})${graduated}${reserve}`)
1029
+ console.log(` ${t.address}`)
1030
+ console.log()
1031
+ }
1032
+ } catch {
1033
+ console.log(`\n Search not available. Browse: https://${chain === 'mainnet' ? '' : 'testnet.'}pad.blumefi.com`)
1034
+ }
1035
+ }
1036
+
966
1037
  async function cmdPadTokens() {
967
1038
  const chain = getChain()
968
1039
  try {
@@ -987,6 +1058,104 @@ async function cmdPadTokens() {
987
1058
  }
988
1059
  }
989
1060
 
1061
+ async function cmdPadUpdateImage(tokenAddress, imageUrl) {
1062
+ if (!tokenAddress || !imageUrl) {
1063
+ console.error('Usage: blumefi pad update-image <token_address> <image_url>')
1064
+ console.error(' blumefi pad update-image 0x1234... https://arweave.net/TXID')
1065
+ console.error(' blumefi pad update-image 0x1234... https://example.com/img.png')
1066
+ console.error('\nOnly the token creator can update the image.')
1067
+ process.exit(1)
1068
+ }
1069
+ if (!imageUrl.startsWith('https://')) {
1070
+ console.error('Error: Image URL must start with https://')
1071
+ process.exit(1)
1072
+ }
1073
+ const viem = await loadViem()
1074
+ const key = getPrivateKey()
1075
+ const account = viem.privateKeyToAccount(key)
1076
+ const message = `Update socials for ${tokenAddress} at ${Date.now()}`
1077
+ const signature = await account.signMessage({ message })
1078
+ console.log(`Updating token image...`)
1079
+ const res = await fetch(`${API}/pad/tokens/${tokenAddress}/socials`, {
1080
+ method: 'PUT',
1081
+ headers: { 'Content-Type': 'application/json' },
1082
+ body: JSON.stringify({ imageURI: imageUrl, signature, message, address: account.address }),
1083
+ })
1084
+ const data = await res.json()
1085
+ if (!res.ok) {
1086
+ console.error(`\n Error: ${data.error || `API error ${res.status}`}`)
1087
+ process.exit(1)
1088
+ }
1089
+ console.log(`\n Token image updated!`)
1090
+ console.log(` Token: ${tokenAddress}`)
1091
+ console.log(` Image: ${imageUrl}`)
1092
+ console.log(` Creator: ${account.address}`)
1093
+ }
1094
+
1095
+ // ─── Balance command ─────────────────────────────────────────────────
1096
+
1097
+ async function cmdBalance(address) {
1098
+ const viem = await loadViem()
1099
+ const chain = getChain()
1100
+ const net = getNetwork()
1101
+ const pub = await getPublicClient()
1102
+
1103
+ if (!address) {
1104
+ const key = process.env.WALLET_PRIVATE_KEY || process.env.PRIVATE_KEY
1105
+ if (key) {
1106
+ const k = key.startsWith('0x') ? key : `0x${key}`
1107
+ address = viem.privateKeyToAccount(k).address
1108
+ } else { console.error('Usage: blumefi balance [address]'); process.exit(1) }
1109
+ }
1110
+
1111
+ const xrpBalance = await pub.getBalance({ address })
1112
+ const xrpFormatted = parseFloat(viem.formatEther(xrpBalance)).toFixed(4)
1113
+
1114
+ console.log(`\n Balances for ${address}`)
1115
+ console.log(` Network: ${chain}`)
1116
+ console.log(` ─────────────────────────────────────`)
1117
+ console.log(` XRP: ${xrpFormatted}`)
1118
+
1119
+ // Check known tokens
1120
+ const tokens = []
1121
+ if (net.rlusd) tokens.push({ address: net.rlusd, name: 'RLUSD' })
1122
+
1123
+ // Check pad tokens the user might hold — query API for recent tokens
1124
+ try {
1125
+ const data = await apiFetch(`/pad/tokens?chain=${chain}&limit=50&sort=recent`)
1126
+ const padTokens = data.data || data.tokens || data || []
1127
+ if (Array.isArray(padTokens)) {
1128
+ for (const t of padTokens) {
1129
+ if (t.address) tokens.push({ address: t.address, name: t.symbol || t.name || '???' })
1130
+ }
1131
+ }
1132
+ } catch {}
1133
+
1134
+ // Batch check balances
1135
+ const results = []
1136
+ for (const t of tokens) {
1137
+ try {
1138
+ const [bal, decimals] = await Promise.all([
1139
+ readContract({ address: t.address, abi: ERC20_ABI, functionName: 'balanceOf', args: [address] }),
1140
+ readContract({ address: t.address, abi: ERC20_ABI, functionName: 'decimals', args: [] }),
1141
+ ])
1142
+ if (bal > 0n) {
1143
+ const formatted = parseFloat(viem.formatUnits(bal, decimals))
1144
+ results.push({ name: t.name, balance: formatted, address: t.address })
1145
+ }
1146
+ } catch {}
1147
+ }
1148
+
1149
+ if (results.length > 0) {
1150
+ for (const r of results) {
1151
+ const balStr = r.balance > 1000 ? r.balance.toLocaleString() : r.balance.toFixed(4)
1152
+ console.log(` ${r.name.padEnd(9)}${balStr}`)
1153
+ }
1154
+ }
1155
+
1156
+ console.log()
1157
+ }
1158
+
990
1159
  // ─── Wallet & network commands ───────────────────────────────────────
991
1160
 
992
1161
  async function cmdWalletNew() {
@@ -1082,14 +1251,16 @@ Chat:
1082
1251
  chat post "<message>" Post a new message
1083
1252
  chat reply <id> "<message>" Reply to a message
1084
1253
  chat mentions [address] Check replies to your posts
1085
- chat profile <name> [--bio ""] Set your display name
1254
+ chat profile <name> [options] Set your display name and avatar
1086
1255
 
1087
1256
  Pad (Launchpad):
1088
1257
  pad launch <name> <symbol> [desc] Launch a token on bonding curve
1089
1258
  pad buy <token> <xrp_amount> Buy tokens with XRP
1090
1259
  pad sell <token> <amount|all> Sell tokens for XRP
1091
1260
  pad info <token> View token info and progress
1261
+ pad search <query> Search tokens by name or symbol
1092
1262
  pad tokens List recent tokens
1263
+ pad update-image <token> <url> Update token image (creator only)
1093
1264
 
1094
1265
  Swap (DEX):
1095
1266
  swap <amount> <from> <to> Swap tokens (e.g. swap 1 XRP RLUSD)
@@ -1103,12 +1274,11 @@ Trade (Perps — testnet):
1103
1274
  trade position View your open positions
1104
1275
  trade price Get current XRP price
1105
1276
 
1106
- Wallet:
1277
+ Wallet & Account:
1107
1278
  wallet new Generate a new wallet
1108
-
1109
- Network:
1279
+ balance [address] Show XRP and token balances
1110
1280
  faucet [address] Get 25 testnet XRP
1111
- status Show network info, contracts, balance
1281
+ status Show network info, contracts
1112
1282
 
1113
1283
  Options:
1114
1284
  --mainnet Use mainnet (default: testnet)
@@ -1145,8 +1315,10 @@ async function main() {
1145
1315
  else if (sub === 'buy' || sub === 'b') await cmdPadBuy(args[2], args[3])
1146
1316
  else if (sub === 'sell' || sub === 's') await cmdPadSell(args[2], args[3])
1147
1317
  else if (sub === 'info' || sub === 'i') await cmdPadInfo(args[2])
1318
+ else if (sub === 'search' || sub === 'find') await cmdPadSearch(args[2])
1148
1319
  else if (sub === 'tokens' || sub === 'list') await cmdPadTokens()
1149
- else { console.error(`Unknown: pad ${sub}. Try: launch, buy, sell, info, tokens`); process.exit(1) }
1320
+ else if (sub === 'update-image') await cmdPadUpdateImage(args[2], args[3])
1321
+ else { console.error(`Unknown: pad ${sub}. Try: launch, buy, sell, info, tokens, update-image`); process.exit(1) }
1150
1322
  } else if (cmd === 'swap') {
1151
1323
  if (sub === 'quote' || sub === 'q') await cmdSwapQuote(args[2], args[3], args[4])
1152
1324
  else if (sub === 'pools' || sub === 'p') await cmdSwapPools()
@@ -1161,6 +1333,8 @@ async function main() {
1161
1333
  } else if (cmd === 'wallet' || cmd === 'w') {
1162
1334
  if (!sub || sub === 'new') await cmdWalletNew()
1163
1335
  else { console.error(`Unknown: wallet ${sub}`); process.exit(1) }
1336
+ } else if (cmd === 'balance' || cmd === 'bal' || cmd === 'b') {
1337
+ await cmdBalance(args[1])
1164
1338
  } else if (cmd === 'faucet') {
1165
1339
  await cmdFaucet(args[1])
1166
1340
  } else if (cmd === 'status' || cmd === 's') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blumefi",
3
- "version": "2.2.0",
3
+ "version": "2.4.0",
4
4
  "description": "BlumeFi CLI — DeFi reimagined for the agentic era. Trade, chat, and interact with the Blume ecosystem from the command line.",
5
5
  "main": "cli.js",
6
6
  "bin": {