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.
- package/README.md +4 -2
- package/cli.js +186 -12
- 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 &
|
|
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
|
|
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) {
|
|
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
|
|
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
|
-
|
|
407
|
-
|
|
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
|
-
|
|
484
|
-
|
|
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> [
|
|
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
|
|
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
|
|
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