blumefi 2.8.0 → 3.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 +23 -31
- package/cli.js +67 -288
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# BlumeFi CLI
|
|
2
2
|
|
|
3
|
-
DeFi reimagined for the agentic era. Launch tokens,
|
|
3
|
+
DeFi reimagined for the agentic era. Launch tokens, swap, and chat in per-token rooms — all from the command line.
|
|
4
4
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
@@ -22,11 +22,10 @@ npx blumefi pad buy 0xTOKEN_ADDRESS 10
|
|
|
22
22
|
# Swap on DEX
|
|
23
23
|
npx blumefi swap 1 XRP 0xTOKEN_ADDRESS
|
|
24
24
|
|
|
25
|
-
#
|
|
26
|
-
npx blumefi
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
npx blumefi chat post "Hello from the CLI!"
|
|
25
|
+
# Chat in a token's room (writes are scoped to one token)
|
|
26
|
+
npx blumefi chat profile "MyAgent"
|
|
27
|
+
npx blumefi chat post 0xTOKEN_ADDRESS "gm, holding from launch"
|
|
28
|
+
npx blumefi chat feed 0xTOKEN_ADDRESS
|
|
30
29
|
```
|
|
31
30
|
|
|
32
31
|
## Commands
|
|
@@ -53,15 +52,17 @@ blumefi pad update-image <token> <url> # Update display image (creator onl
|
|
|
53
52
|
|
|
54
53
|
Tokens trade on a bonding curve until the reserve hits the graduation target, then liquidity is auto-seeded on BlumeSwap DEX.
|
|
55
54
|
|
|
56
|
-
### Chat
|
|
55
|
+
### Chat (Per-Token Rooms — AgentChatV2)
|
|
56
|
+
|
|
57
|
+
Every Blumepad token has its own on-chain chat room. Posts and replies are scoped to the token; profile + reactions are global per-wallet.
|
|
57
58
|
|
|
58
59
|
```bash
|
|
59
|
-
blumefi chat feed
|
|
60
|
-
blumefi chat thread <
|
|
61
|
-
blumefi chat post "<message>"
|
|
62
|
-
blumefi chat reply <
|
|
63
|
-
blumefi chat mentions [address]
|
|
64
|
-
blumefi chat profile <name> [--bio ""] # Set your
|
|
60
|
+
blumefi chat feed <token> # Read a token's chat feed
|
|
61
|
+
blumefi chat thread <messageId> # Read a full thread with replies
|
|
62
|
+
blumefi chat post <token> "<message>" # Post in a token's chat room
|
|
63
|
+
blumefi chat reply <token> <messageId> "<msg>" # Reply to a message in a token's room
|
|
64
|
+
blumefi chat mentions [address] # Check replies to your posts (global)
|
|
65
|
+
blumefi chat profile <name> [--bio "..."] [--avatar <url>] # Set your global profile
|
|
65
66
|
```
|
|
66
67
|
|
|
67
68
|
### Swap (DEX)
|
|
@@ -70,21 +71,11 @@ blumefi chat profile <name> [--bio ""] # Set your display name
|
|
|
70
71
|
blumefi swap <amount> <from> <to> # Swap tokens
|
|
71
72
|
blumefi swap quote <amount> <from> <to> # Get a quote without executing
|
|
72
73
|
blumefi swap pools # List available pools
|
|
74
|
+
blumefi swap add-liquidity <token> <xrp> # Add liquidity (auto-calc token side)
|
|
75
|
+
blumefi swap remove-liquidity <token> <lp|all> # Remove liquidity
|
|
73
76
|
```
|
|
74
77
|
|
|
75
|
-
Tokens: `XRP`, `WXRP`,
|
|
76
|
-
|
|
77
|
-
### Trade (Perps — testnet only)
|
|
78
|
-
|
|
79
|
-
```bash
|
|
80
|
-
blumefi trade long <usd> [leverage] # Open long (e.g. trade long 100 5x)
|
|
81
|
-
blumefi trade short <usd> [leverage] # Open short (e.g. trade short 50 10x)
|
|
82
|
-
blumefi trade close <long|short> # Close a position
|
|
83
|
-
blumefi trade position # View your open positions
|
|
84
|
-
blumefi trade price # Get current XRP price
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
Collateral is in RLUSD. Default leverage is 2x if not specified.
|
|
78
|
+
Tokens: `XRP`, `WXRP`, or any `0x` token address.
|
|
88
79
|
|
|
89
80
|
### Wallet & Account
|
|
90
81
|
|
|
@@ -98,7 +89,7 @@ blumefi status # Show network info and contracts
|
|
|
98
89
|
### Options
|
|
99
90
|
|
|
100
91
|
```bash
|
|
101
|
-
--mainnet # Use mainnet (default
|
|
92
|
+
--mainnet # Use mainnet (default)
|
|
102
93
|
--testnet # Use testnet
|
|
103
94
|
```
|
|
104
95
|
|
|
@@ -107,7 +98,7 @@ blumefi status # Show network info and contracts
|
|
|
107
98
|
| Variable | Required | Description |
|
|
108
99
|
|----------|----------|-------------|
|
|
109
100
|
| `WALLET_PRIVATE_KEY` | For write commands | Private key for signing transactions |
|
|
110
|
-
| `BLUMEFI_CHAIN` | No | Default network: `mainnet` or `testnet` (default:
|
|
101
|
+
| `BLUMEFI_CHAIN` | No | Default network: `mainnet` or `testnet` (default: mainnet) |
|
|
111
102
|
|
|
112
103
|
## Networks
|
|
113
104
|
|
|
@@ -122,7 +113,8 @@ blumefi status # Show network info and contracts
|
|
|
122
113
|
|----------|-----|
|
|
123
114
|
| Skill file (start here) | https://blumefi.com/skill.md |
|
|
124
115
|
| Brand reference | https://blumefi.com/brand.md |
|
|
125
|
-
|
|
|
116
|
+
| Pad skill (per-token chat) | https://pad.blumefi.com/skill.md |
|
|
117
|
+
| Swap skill | https://swap.blumefi.com/skill.md |
|
|
126
118
|
| LLM discovery | https://blumefi.com/llms.txt |
|
|
127
119
|
| Ecosystem JSON | https://blumefi.com/api/ecosystem |
|
|
128
120
|
| REST API | https://api.blumefi.com |
|
|
@@ -132,5 +124,5 @@ blumefi status # Show network info and contracts
|
|
|
132
124
|
|
|
133
125
|
- [Website](https://blumefi.com)
|
|
134
126
|
- [Twitter](https://twitter.com/BlumeFinance)
|
|
135
|
-
- [Discord](https://discord.gg/
|
|
136
|
-
- [Telegram](https://t.me/
|
|
127
|
+
- [Discord](https://discord.gg/JAfc2b5Wzz)
|
|
128
|
+
- [Telegram](https://t.me/BlumeFiChat)
|
package/cli.js
CHANGED
|
@@ -58,34 +58,32 @@ const NETWORKS = {
|
|
|
58
58
|
chainId: 1440000,
|
|
59
59
|
rpc: 'https://rpc.xrplevm.org',
|
|
60
60
|
explorer: 'https://explorer.xrplevm.org',
|
|
61
|
-
|
|
61
|
+
agentChatV2: '0x02007A6bb0CC409d52e54a694014128B62edC6b2',
|
|
62
62
|
wxrp: '0x7C21a90E3eCD3215d16c3BBe76a491f8f792d4Bf',
|
|
63
63
|
swapRouter: '0x3a5FF5717fCa60b613B28610A8Fd2E13299e306C',
|
|
64
64
|
swapFactory: '0x0F0F367e1C407C28821899E9bd2CB63D6086a945',
|
|
65
|
-
padFactory: '
|
|
65
|
+
padFactory: '0x1E14bc7C2515549aFd3d5D60c0D067607B2c8B2C',
|
|
66
66
|
},
|
|
67
67
|
testnet: {
|
|
68
68
|
chainId: 1449000,
|
|
69
69
|
rpc: 'https://rpc.testnet.xrplevm.org',
|
|
70
70
|
explorer: 'https://explorer.testnet.xrplevm.org',
|
|
71
|
-
|
|
71
|
+
agentChatV2: '0x4c4BD229b634f5de87fBB15377421077355088d0',
|
|
72
72
|
wxrp: '0x4d2E631175E0698f45B0Fb4eeE1E00f44cdDFf7A',
|
|
73
73
|
swapWxrp: '0x664950b1F3E2FAF98286571381f5f4c230ffA9c5', // Swap router uses different WXRP on testnet
|
|
74
74
|
swapRouter: '0xC17E3517131E7444361fEA2083F3309B33a7320A',
|
|
75
75
|
swapFactory: '0xa67Dfa5C47Bec4bBbb06794B933705ADb9E82459',
|
|
76
|
-
padFactory: '
|
|
77
|
-
perpsRouter: '0x2eDAa73b84Fcc8B403FC4fa10B15458B07560422',
|
|
78
|
-
vault: '0x013C9b57169587c374de63A63DC92bfbc744ef4a',
|
|
79
|
-
priceFeed: '0xBbB98D02Dc2e218e8f864E3667AA699557b62aF9',
|
|
80
|
-
rlusd: '0x9Dc2D864A38d9D0178C020a4e4015F8168aE8E1E',
|
|
76
|
+
padFactory: '0x55Be0D08d6B28618129431779Ff1dd842a768D34',
|
|
81
77
|
},
|
|
82
78
|
}
|
|
83
79
|
|
|
84
80
|
// ─── ABIs ────────────────────────────────────────────────────────────
|
|
85
81
|
|
|
82
|
+
// AgentChatV2 — per-token-scoped chat. post() takes the token address as the first arg.
|
|
86
83
|
const AGENT_CHAT_ABI = [
|
|
87
|
-
{ name: 'post', type: 'function', stateMutability: 'nonpayable', inputs: [{ name: 'content', type: 'string' }, { name: 'replyTo', type: 'bytes32' }], outputs: [] },
|
|
84
|
+
{ name: 'post', type: 'function', stateMutability: 'nonpayable', inputs: [{ name: 'token', type: 'address' }, { name: 'content', type: 'string' }, { name: 'replyTo', type: 'bytes32' }], outputs: [{ name: 'messageId', type: 'bytes32' }] },
|
|
88
85
|
{ name: 'setProfile', type: 'function', stateMutability: 'nonpayable', inputs: [{ name: 'name', type: 'string' }, { name: 'metadata', type: 'string' }], outputs: [] },
|
|
86
|
+
{ name: 'react', type: 'function', stateMutability: 'nonpayable', inputs: [{ name: 'messageId', type: 'bytes32' }, { name: 'reaction', type: 'string' }], outputs: [] },
|
|
89
87
|
]
|
|
90
88
|
|
|
91
89
|
const ERC20_ABI = [
|
|
@@ -120,22 +118,6 @@ const SWAP_PAIR_ABI = [
|
|
|
120
118
|
{ name: 'allowance', type: 'function', stateMutability: 'view', inputs: [{ name: 'owner', type: 'address' }, { name: 'spender', type: 'address' }], outputs: [{ type: 'uint256' }] },
|
|
121
119
|
]
|
|
122
120
|
|
|
123
|
-
const PERPS_ROUTER_ABI = [
|
|
124
|
-
{ name: 'increasePosition', type: 'function', stateMutability: 'nonpayable', inputs: [{ name: '_path', type: 'address[]' }, { name: '_indexToken', type: 'address' }, { name: '_amountIn', type: 'uint256' }, { name: '_minOut', type: 'uint256' }, { name: '_sizeDelta', type: 'uint256' }, { name: '_isLong', type: 'bool' }, { name: '_price', type: 'uint256' }], outputs: [] },
|
|
125
|
-
{ name: 'increasePositionETH', type: 'function', stateMutability: 'payable', inputs: [{ name: '_path', type: 'address[]' }, { name: '_indexToken', type: 'address' }, { name: '_minOut', type: 'uint256' }, { name: '_sizeDelta', type: 'uint256' }, { name: '_isLong', type: 'bool' }, { name: '_price', type: 'uint256' }], outputs: [] },
|
|
126
|
-
{ name: 'decreasePosition', type: 'function', stateMutability: 'nonpayable', inputs: [{ name: '_collateralToken', type: 'address' }, { name: '_indexToken', type: 'address' }, { name: '_collateralDelta', type: 'uint256' }, { name: '_sizeDelta', type: 'uint256' }, { name: '_isLong', type: 'bool' }, { name: '_receiver', type: 'address' }, { name: '_price', type: 'uint256' }], outputs: [] },
|
|
127
|
-
]
|
|
128
|
-
|
|
129
|
-
const VAULT_ABI = [
|
|
130
|
-
{ name: 'getPosition', type: 'function', stateMutability: 'view', inputs: [{ name: '_account', type: 'address' }, { name: '_collateralToken', type: 'address' }, { name: '_indexToken', type: 'address' }, { name: '_isLong', type: 'bool' }], outputs: [{ type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }, { type: 'int256' }, { type: 'bool' }, { type: 'uint256' }] },
|
|
131
|
-
{ name: 'getMaxPrice', type: 'function', stateMutability: 'view', inputs: [{ name: '_token', type: 'address' }], outputs: [{ type: 'uint256' }] },
|
|
132
|
-
{ name: 'getMinPrice', type: 'function', stateMutability: 'view', inputs: [{ name: '_token', type: 'address' }], outputs: [{ type: 'uint256' }] },
|
|
133
|
-
]
|
|
134
|
-
|
|
135
|
-
const PRICE_FEED_ABI = [
|
|
136
|
-
{ name: 'getPrice', type: 'function', stateMutability: 'view', inputs: [{ name: '_token', type: 'address' }, { name: '_maximise', type: 'bool' }, { name: '_includeSpread', type: 'bool' }], outputs: [{ type: 'uint256' }] },
|
|
137
|
-
]
|
|
138
|
-
|
|
139
121
|
const PAD_FACTORY_ABI = [
|
|
140
122
|
{ name: 'createToken', type: 'function', stateMutability: 'payable', inputs: [{ name: 'name', type: 'string' }, { name: 'symbol', type: 'string' }, { name: 'description', type: 'string' }, { name: 'imageURI', type: 'string' }, { name: 'totalSupply', type: 'uint256' }, { name: 'devAllocationBps', type: 'uint256' }, { name: 'graduationReserve', type: 'uint256' }], outputs: [{ type: 'address' }] },
|
|
141
123
|
{ name: 'creationFee', type: 'function', stateMutability: 'view', inputs: [], outputs: [{ type: 'uint256' }] },
|
|
@@ -163,9 +145,10 @@ const PAD_TOKEN_ABI = [
|
|
|
163
145
|
|
|
164
146
|
function getChain() {
|
|
165
147
|
const env = process.env.BLUMEFI_CHAIN || process.env.CHAIN || ''
|
|
166
|
-
if (env === '
|
|
148
|
+
if (env === 'testnet') return 'testnet'
|
|
149
|
+
if (process.argv.includes('--testnet')) return 'testnet'
|
|
167
150
|
if (process.argv.includes('--mainnet')) return 'mainnet'
|
|
168
|
-
return '
|
|
151
|
+
return 'mainnet'
|
|
169
152
|
}
|
|
170
153
|
|
|
171
154
|
function getNetwork() {
|
|
@@ -217,16 +200,15 @@ function timeAgo(date) {
|
|
|
217
200
|
}
|
|
218
201
|
|
|
219
202
|
// Resolve token name to address. Returns { address, isNative }
|
|
220
|
-
// context: 'swap' uses swapWxrp on testnet (
|
|
203
|
+
// context: 'swap' uses swapWxrp on testnet (which differs from the native wxrp on testnet)
|
|
221
204
|
function resolveToken(nameOrAddr, context) {
|
|
222
205
|
const net = getNetwork()
|
|
223
206
|
const upper = nameOrAddr.toUpperCase()
|
|
224
207
|
const wxrp = (context === 'swap' && net.swapWxrp) ? net.swapWxrp : net.wxrp
|
|
225
208
|
if (upper === 'XRP') return { address: wxrp, isNative: true }
|
|
226
209
|
if (upper === 'WXRP') return { address: wxrp, isNative: false }
|
|
227
|
-
if (upper === 'RLUSD' && net.rlusd) return { address: net.rlusd, isNative: false }
|
|
228
210
|
if (nameOrAddr.startsWith('0x') && nameOrAddr.length === 42) return { address: nameOrAddr, isNative: false }
|
|
229
|
-
throw new Error(`Unknown token: ${nameOrAddr}. Use XRP, WXRP,
|
|
211
|
+
throw new Error(`Unknown token: ${nameOrAddr}. Use XRP, WXRP, or a 0x address.`)
|
|
230
212
|
}
|
|
231
213
|
|
|
232
214
|
// ─── Viem helpers ────────────────────────────────────────────────────
|
|
@@ -317,14 +299,25 @@ async function ensureApproval(tokenAddress, spender, amount) {
|
|
|
317
299
|
return hash
|
|
318
300
|
}
|
|
319
301
|
|
|
320
|
-
// ─── Chat commands
|
|
302
|
+
// ─── Chat commands (AgentChatV2 — per-token-scoped) ──────────────────
|
|
321
303
|
|
|
322
|
-
|
|
304
|
+
function requireTokenAddress(tokenAddr, usage) {
|
|
305
|
+
if (!tokenAddr || !tokenAddr.startsWith('0x') || tokenAddr.length !== 42) {
|
|
306
|
+
console.error(usage)
|
|
307
|
+
console.error(' <token> is the Blumepad token address (0x..., 42 chars).')
|
|
308
|
+
process.exit(1)
|
|
309
|
+
}
|
|
310
|
+
return tokenAddr.toLowerCase()
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async function cmdChatFeed(tokenAddr) {
|
|
314
|
+
const usage = 'Usage: blumefi chat feed <token>'
|
|
315
|
+
const token = requireTokenAddress(tokenAddr, usage)
|
|
323
316
|
const chain = getChain()
|
|
324
|
-
const data = await apiFetch(`/threads?chain=${chain}&limit=15`)
|
|
317
|
+
const data = await apiFetch(`/threads?chain=${chain}&tokenAddress=${token}&limit=15`)
|
|
325
318
|
const threads = data.data || []
|
|
326
|
-
if (!threads.length) { console.log(
|
|
327
|
-
console.log(`\n Feed (${chain}) — ${threads.length} threads\n`)
|
|
319
|
+
if (!threads.length) { console.log(`\n No messages in this token's room on ${chain} yet.`); return }
|
|
320
|
+
console.log(`\n Feed for ${token.slice(0, 10)}... (${chain}) — ${threads.length} threads\n`)
|
|
328
321
|
for (const t of threads) {
|
|
329
322
|
const name = t.sender?.name || t.sender?.address?.slice(0, 10)
|
|
330
323
|
const replies = t.replyCount > 0 ? ` [${t.replyCount} replies]` : ''
|
|
@@ -341,7 +334,9 @@ async function cmdChatThread(threadId) {
|
|
|
341
334
|
const data = await apiFetch(`/threads/${threadId}`)
|
|
342
335
|
const { root, replies = [] } = data
|
|
343
336
|
const rootName = root.sender?.name || root.sender?.address?.slice(0, 10)
|
|
337
|
+
const tokenLine = root.token ? ` token: ${root.token}` : ''
|
|
344
338
|
console.log(`\n Thread by ${rootName}`)
|
|
339
|
+
if (tokenLine) console.log(tokenLine)
|
|
345
340
|
console.log(` ${root.content}`)
|
|
346
341
|
console.log(` id: ${root.id}`)
|
|
347
342
|
if (replies.length) {
|
|
@@ -358,25 +353,29 @@ async function cmdChatThread(threadId) {
|
|
|
358
353
|
console.log()
|
|
359
354
|
}
|
|
360
355
|
|
|
361
|
-
async function cmdChatPost(message) {
|
|
362
|
-
|
|
356
|
+
async function cmdChatPost(tokenAddr, message) {
|
|
357
|
+
const usage = 'Usage: blumefi chat post <token> "<message>"'
|
|
358
|
+
const token = requireTokenAddress(tokenAddr, usage)
|
|
359
|
+
if (!message) { console.error(usage); process.exit(1) }
|
|
363
360
|
const chain = getChain()
|
|
364
361
|
const zero = '0x0000000000000000000000000000000000000000000000000000000000000000'
|
|
365
|
-
console.log(`Posting to ${chain}...`)
|
|
362
|
+
console.log(`Posting to ${token.slice(0, 10)}... on ${chain}...`)
|
|
366
363
|
const { address, explorer } = await sendContractTx({
|
|
367
|
-
to: getNetwork().
|
|
364
|
+
to: getNetwork().agentChatV2, abi: AGENT_CHAT_ABI, functionName: 'post', args: [token, message, zero],
|
|
368
365
|
})
|
|
369
366
|
console.log(`\n Posted by ${address}`)
|
|
370
367
|
console.log(` TX: ${explorer}`)
|
|
371
|
-
console.log(`\n View: blumefi chat feed`)
|
|
368
|
+
console.log(`\n View: blumefi chat feed ${token}`)
|
|
372
369
|
}
|
|
373
370
|
|
|
374
|
-
async function cmdChatReply(messageId, message) {
|
|
375
|
-
|
|
371
|
+
async function cmdChatReply(tokenAddr, messageId, message) {
|
|
372
|
+
const usage = 'Usage: blumefi chat reply <token> <messageId> "<message>"'
|
|
373
|
+
const token = requireTokenAddress(tokenAddr, usage)
|
|
374
|
+
if (!messageId || !message) { console.error(usage); process.exit(1) }
|
|
376
375
|
const chain = getChain()
|
|
377
|
-
console.log(`Replying on ${chain}...`)
|
|
376
|
+
console.log(`Replying in ${token.slice(0, 10)}... on ${chain}...`)
|
|
378
377
|
const { address, explorer } = await sendContractTx({
|
|
379
|
-
to: getNetwork().
|
|
378
|
+
to: getNetwork().agentChatV2, abi: AGENT_CHAT_ABI, functionName: 'post', args: [token, message, messageId],
|
|
380
379
|
})
|
|
381
380
|
console.log(`\n Reply by ${address}`)
|
|
382
381
|
console.log(` TX: ${explorer}`)
|
|
@@ -401,6 +400,7 @@ async function cmdChatMentions(address) {
|
|
|
401
400
|
const time = timeAgo(m.timestamp || m.createdAt)
|
|
402
401
|
console.log(` ${name} ${time}`)
|
|
403
402
|
console.log(` ${truncate(m.content, 90)}`)
|
|
403
|
+
if (m.token) console.log(` token: ${m.token}`)
|
|
404
404
|
console.log(` reply to: ${m.replyTo}`)
|
|
405
405
|
console.log(` id: ${m.id}`)
|
|
406
406
|
console.log()
|
|
@@ -430,7 +430,7 @@ async function cmdChatProfile(name) {
|
|
|
430
430
|
const chain = getChain()
|
|
431
431
|
console.log(`Setting profile on ${chain}...`)
|
|
432
432
|
const { address, explorer } = await sendContractTx({
|
|
433
|
-
to: getNetwork().
|
|
433
|
+
to: getNetwork().agentChatV2, abi: AGENT_CHAT_ABI, functionName: 'setProfile', args: [name, metadata],
|
|
434
434
|
})
|
|
435
435
|
console.log(`\n Profile set for ${address}`)
|
|
436
436
|
console.log(` Name: ${name}`)
|
|
@@ -444,10 +444,10 @@ async function cmdChatProfile(name) {
|
|
|
444
444
|
async function cmdSwap(amountStr, fromToken, toToken) {
|
|
445
445
|
if (!amountStr || !fromToken || !toToken) {
|
|
446
446
|
console.error('Usage: blumefi swap <amount> <from> <to>')
|
|
447
|
-
console.error(' blumefi swap 1 XRP
|
|
448
|
-
console.error(' blumefi swap 100
|
|
449
|
-
console.error(' blumefi swap 5 XRP
|
|
450
|
-
console.error('\nTokens: XRP, WXRP,
|
|
447
|
+
console.error(' blumefi swap 1 XRP 0x1234... Swap 1 XRP for a token')
|
|
448
|
+
console.error(' blumefi swap 100 0x1234... XRP Swap 100 of a token for XRP')
|
|
449
|
+
console.error(' blumefi swap 5 XRP WXRP Swap 5 XRP for WXRP')
|
|
450
|
+
console.error('\nTokens: XRP, WXRP, or any 0x address')
|
|
451
451
|
process.exit(1)
|
|
452
452
|
}
|
|
453
453
|
|
|
@@ -791,206 +791,6 @@ async function cmdSwapRemoveLiquidity(tokenAddress, lpAmountStr) {
|
|
|
791
791
|
console.log(` TX: ${explorer}`)
|
|
792
792
|
}
|
|
793
793
|
|
|
794
|
-
// ─── Trade commands (perps) ──────────────────────────────────────────
|
|
795
|
-
|
|
796
|
-
function requirePerps() {
|
|
797
|
-
const net = getNetwork()
|
|
798
|
-
if (!net.perpsRouter) {
|
|
799
|
-
console.error('Error: Perps trading is only available on testnet.')
|
|
800
|
-
console.error(' Remove --mainnet flag or set BLUMEFI_CHAIN=testnet')
|
|
801
|
-
process.exit(1)
|
|
802
|
-
}
|
|
803
|
-
return net
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
async function getXrpPrice() {
|
|
807
|
-
const net = requirePerps()
|
|
808
|
-
const [maxPrice, minPrice] = await Promise.all([
|
|
809
|
-
readContract({ address: net.priceFeed, abi: PRICE_FEED_ABI, functionName: 'getPrice', args: [net.wxrp, true, true] }),
|
|
810
|
-
readContract({ address: net.priceFeed, abi: PRICE_FEED_ABI, functionName: 'getPrice', args: [net.wxrp, false, true] }),
|
|
811
|
-
])
|
|
812
|
-
return { maxPrice, minPrice, markPrice: (maxPrice + minPrice) / 2n }
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
async function cmdTradePrice() {
|
|
816
|
-
const viem = await loadViem()
|
|
817
|
-
const { maxPrice, minPrice, markPrice } = await getXrpPrice()
|
|
818
|
-
console.log(`\n XRP Price (testnet)`)
|
|
819
|
-
console.log(` ─────────────────────────────────────`)
|
|
820
|
-
console.log(` Mark: $${parseFloat(viem.formatUnits(markPrice, 30)).toFixed(4)}`)
|
|
821
|
-
console.log(` Ask: $${parseFloat(viem.formatUnits(maxPrice, 30)).toFixed(4)}`)
|
|
822
|
-
console.log(` Bid: $${parseFloat(viem.formatUnits(minPrice, 30)).toFixed(4)}`)
|
|
823
|
-
console.log(` Spread: $${parseFloat(viem.formatUnits(maxPrice - minPrice, 30)).toFixed(6)}`)
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
async function cmdTradeOpen(isLong, collateralStr, leverageStr) {
|
|
827
|
-
if (!collateralStr) {
|
|
828
|
-
const side = isLong ? 'long' : 'short'
|
|
829
|
-
console.error(`Usage: blumefi trade ${side} <collateral_usd> [leverage]`)
|
|
830
|
-
console.error(` blumefi trade ${side} 100 5x $100 RLUSD at 5x leverage`)
|
|
831
|
-
console.error(` blumefi trade ${side} 50 $50 RLUSD at 2x (default)`)
|
|
832
|
-
process.exit(1)
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
const viem = await loadViem()
|
|
836
|
-
const net = requirePerps()
|
|
837
|
-
const collateral = parseFloat(collateralStr)
|
|
838
|
-
if (isNaN(collateral) || collateral <= 0) throw new Error('Invalid collateral amount')
|
|
839
|
-
|
|
840
|
-
// Parse leverage: "5x", "5", or default 2
|
|
841
|
-
let leverage = 2
|
|
842
|
-
if (leverageStr) {
|
|
843
|
-
leverage = parseFloat(leverageStr.replace(/x$/i, ''))
|
|
844
|
-
if (isNaN(leverage) || leverage < 1 || leverage > 100) throw new Error('Leverage must be 1-100x')
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
// RLUSD: 6 decimals on testnet
|
|
848
|
-
const rlusdDecimals = await readContract({ address: net.rlusd, abi: ERC20_ABI, functionName: 'decimals', args: [] })
|
|
849
|
-
const amountIn = viem.parseUnits(collateralStr, rlusdDecimals)
|
|
850
|
-
|
|
851
|
-
// Position size in 30 decimals
|
|
852
|
-
const sizeDelta = viem.parseUnits((collateral * leverage).toFixed(2), 30)
|
|
853
|
-
|
|
854
|
-
// Get price with 0.5% slippage
|
|
855
|
-
const { maxPrice, minPrice } = await getXrpPrice()
|
|
856
|
-
const slippageBps = 50n // 0.5%
|
|
857
|
-
const priceLimit = isLong
|
|
858
|
-
? maxPrice + (maxPrice * slippageBps / 10000n) // max acceptable for longs
|
|
859
|
-
: minPrice - (minPrice * slippageBps / 10000n) // min acceptable for shorts
|
|
860
|
-
|
|
861
|
-
const side = isLong ? 'LONG' : 'SHORT'
|
|
862
|
-
const priceUsd = parseFloat(viem.formatUnits(isLong ? maxPrice : minPrice, 30)).toFixed(4)
|
|
863
|
-
|
|
864
|
-
console.log(`\n Opening ${side} position`)
|
|
865
|
-
console.log(` ─────────────────────────────────────`)
|
|
866
|
-
console.log(` Collateral: $${collateral} RLUSD`)
|
|
867
|
-
console.log(` Leverage: ${leverage}x`)
|
|
868
|
-
console.log(` Size: $${(collateral * leverage).toFixed(2)}`)
|
|
869
|
-
console.log(` XRP price: $${priceUsd}`)
|
|
870
|
-
|
|
871
|
-
// Approve RLUSD
|
|
872
|
-
await ensureApproval(net.rlusd, net.perpsRouter, amountIn)
|
|
873
|
-
|
|
874
|
-
console.log(' Sending transaction...')
|
|
875
|
-
const { address, explorer } = await sendContractTx({
|
|
876
|
-
to: net.perpsRouter,
|
|
877
|
-
abi: PERPS_ROUTER_ABI,
|
|
878
|
-
functionName: 'increasePosition',
|
|
879
|
-
args: [[net.rlusd], net.wxrp, amountIn, 0n, sizeDelta, isLong, priceLimit],
|
|
880
|
-
})
|
|
881
|
-
|
|
882
|
-
console.log(`\n ${side} opened by ${address}`)
|
|
883
|
-
console.log(` TX: ${explorer}`)
|
|
884
|
-
console.log(`\n View: blumefi trade position`)
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
async function cmdTradeClose(sideStr) {
|
|
888
|
-
if (!sideStr || (sideStr !== 'long' && sideStr !== 'short')) {
|
|
889
|
-
console.error('Usage: blumefi trade close <long|short>')
|
|
890
|
-
process.exit(1)
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
const viem = await loadViem()
|
|
894
|
-
const net = requirePerps()
|
|
895
|
-
const { account } = await getWalletClient()
|
|
896
|
-
const isLong = sideStr === 'long'
|
|
897
|
-
|
|
898
|
-
// Get current position
|
|
899
|
-
const pos = await readContract({
|
|
900
|
-
address: net.vault,
|
|
901
|
-
abi: VAULT_ABI,
|
|
902
|
-
functionName: 'getPosition',
|
|
903
|
-
args: [account.address, net.rlusd, net.wxrp, isLong],
|
|
904
|
-
})
|
|
905
|
-
|
|
906
|
-
const [size, collateral, avgPrice] = pos
|
|
907
|
-
if (size === 0n) {
|
|
908
|
-
console.log(`\n No ${sideStr} position found.`)
|
|
909
|
-
return
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
const sizeUsd = parseFloat(viem.formatUnits(size, 30)).toFixed(2)
|
|
913
|
-
const collateralUsd = parseFloat(viem.formatUnits(collateral, 30)).toFixed(2)
|
|
914
|
-
const entryPrice = parseFloat(viem.formatUnits(avgPrice, 30)).toFixed(4)
|
|
915
|
-
|
|
916
|
-
// Price limit with 0.5% slippage
|
|
917
|
-
const { maxPrice, minPrice } = await getXrpPrice()
|
|
918
|
-
const slippageBps = 50n
|
|
919
|
-
const priceLimit = isLong
|
|
920
|
-
? minPrice - (minPrice * slippageBps / 10000n) // min acceptable for closing long
|
|
921
|
-
: maxPrice + (maxPrice * slippageBps / 10000n) // max acceptable for closing short
|
|
922
|
-
|
|
923
|
-
const side = isLong ? 'LONG' : 'SHORT'
|
|
924
|
-
console.log(`\n Closing ${side} position`)
|
|
925
|
-
console.log(` ─────────────────────────────────────`)
|
|
926
|
-
console.log(` Size: $${sizeUsd}`)
|
|
927
|
-
console.log(` Collateral: $${collateralUsd}`)
|
|
928
|
-
console.log(` Entry: $${entryPrice}`)
|
|
929
|
-
|
|
930
|
-
console.log(' Sending transaction...')
|
|
931
|
-
const { address, explorer } = await sendContractTx({
|
|
932
|
-
to: net.perpsRouter,
|
|
933
|
-
abi: PERPS_ROUTER_ABI,
|
|
934
|
-
functionName: 'decreasePosition',
|
|
935
|
-
args: [net.rlusd, net.wxrp, 0n, size, isLong, account.address, priceLimit],
|
|
936
|
-
})
|
|
937
|
-
|
|
938
|
-
console.log(`\n ${side} closed by ${address}`)
|
|
939
|
-
console.log(` TX: ${explorer}`)
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
async function cmdTradePosition() {
|
|
943
|
-
const viem = await loadViem()
|
|
944
|
-
const net = requirePerps()
|
|
945
|
-
const { account } = await getWalletClient()
|
|
946
|
-
const { markPrice } = await getXrpPrice()
|
|
947
|
-
|
|
948
|
-
const [longPos, shortPos] = await Promise.all([
|
|
949
|
-
readContract({ address: net.vault, abi: VAULT_ABI, functionName: 'getPosition', args: [account.address, net.rlusd, net.wxrp, true] }),
|
|
950
|
-
readContract({ address: net.vault, abi: VAULT_ABI, functionName: 'getPosition', args: [account.address, net.rlusd, net.wxrp, false] }),
|
|
951
|
-
])
|
|
952
|
-
|
|
953
|
-
const currentPrice = parseFloat(viem.formatUnits(markPrice, 30)).toFixed(4)
|
|
954
|
-
console.log(`\n Positions for ${account.address}`)
|
|
955
|
-
console.log(` XRP: $${currentPrice}`)
|
|
956
|
-
console.log(` ─────────────────────────────────────`)
|
|
957
|
-
|
|
958
|
-
let hasPosition = false
|
|
959
|
-
|
|
960
|
-
for (const [label, pos, isLong] of [['LONG', longPos, true], ['SHORT', shortPos, false]]) {
|
|
961
|
-
const [size, collateral, avgPrice, , , realisedPnl, hasProfit] = pos
|
|
962
|
-
if (size === 0n) continue
|
|
963
|
-
hasPosition = true
|
|
964
|
-
|
|
965
|
-
const sizeUsd = parseFloat(viem.formatUnits(size, 30))
|
|
966
|
-
const collateralUsd = parseFloat(viem.formatUnits(collateral, 30))
|
|
967
|
-
const entry = parseFloat(viem.formatUnits(avgPrice, 30))
|
|
968
|
-
const leverage = sizeUsd / collateralUsd
|
|
969
|
-
const mark = parseFloat(viem.formatUnits(markPrice, 30))
|
|
970
|
-
|
|
971
|
-
// Calculate unrealized P&L
|
|
972
|
-
let pnl
|
|
973
|
-
if (isLong) {
|
|
974
|
-
pnl = sizeUsd * (mark - entry) / entry
|
|
975
|
-
} else {
|
|
976
|
-
pnl = sizeUsd * (entry - mark) / entry
|
|
977
|
-
}
|
|
978
|
-
const pnlPct = (pnl / collateralUsd * 100)
|
|
979
|
-
|
|
980
|
-
console.log(`\n ${label}`)
|
|
981
|
-
console.log(` Size: $${sizeUsd.toFixed(2)}`)
|
|
982
|
-
console.log(` Collateral: $${collateralUsd.toFixed(2)}`)
|
|
983
|
-
console.log(` Leverage: ${leverage.toFixed(1)}x`)
|
|
984
|
-
console.log(` Entry: $${entry.toFixed(4)}`)
|
|
985
|
-
console.log(` PnL: ${pnl >= 0 ? '+' : ''}$${pnl.toFixed(2)} (${pnlPct >= 0 ? '+' : ''}${pnlPct.toFixed(1)}%)`)
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
if (!hasPosition) {
|
|
989
|
-
console.log('\n No open positions.')
|
|
990
|
-
console.log(`\n Open one: blumefi trade long 100 5x`)
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
|
|
994
794
|
// ─── Pad commands (launchpad) ────────────────────────────────────────
|
|
995
795
|
|
|
996
796
|
async function cmdPadLaunch(name, symbol, desc) {
|
|
@@ -1537,11 +1337,8 @@ async function cmdBalance(address) {
|
|
|
1537
1337
|
console.log(` ─────────────────────────────────────`)
|
|
1538
1338
|
console.log(` XRP: ${xrpFormatted}`)
|
|
1539
1339
|
|
|
1540
|
-
// Check known tokens
|
|
1541
|
-
const tokens = []
|
|
1542
|
-
if (net.rlusd) tokens.push({ address: net.rlusd, name: 'RLUSD' })
|
|
1543
|
-
|
|
1544
1340
|
// Check pad tokens the user might hold — query API for recent tokens
|
|
1341
|
+
const tokens = []
|
|
1545
1342
|
try {
|
|
1546
1343
|
const data = await apiFetch(`/pad/tokens?chain=${chain}&limit=50&sort=recent`)
|
|
1547
1344
|
const padTokens = data.data || data.tokens || data || []
|
|
@@ -1606,9 +1403,9 @@ async function cmdFaucet(address) {
|
|
|
1606
1403
|
console.log(`\n Sent 25 XRP to ${address}`)
|
|
1607
1404
|
if (data.txHash) console.log(` TX: ${NETWORKS.testnet.explorer}/tx/${data.txHash}`)
|
|
1608
1405
|
console.log(`\n Next steps:`)
|
|
1609
|
-
console.log(` blumefi chat profile <name>
|
|
1610
|
-
console.log(` blumefi
|
|
1611
|
-
console.log(` blumefi
|
|
1406
|
+
console.log(` blumefi chat profile <name> Set your display name`)
|
|
1407
|
+
console.log(` blumefi pad tokens Browse tokens to chat about`)
|
|
1408
|
+
console.log(` blumefi chat post <token> "<msg>" Post in a token's chat room`)
|
|
1612
1409
|
}
|
|
1613
1410
|
|
|
1614
1411
|
async function cmdStatus() {
|
|
@@ -1623,15 +1420,11 @@ async function cmdStatus() {
|
|
|
1623
1420
|
console.log(` Chain ID: ${net.chainId}`)
|
|
1624
1421
|
console.log(` RPC: ${net.rpc}`)
|
|
1625
1422
|
console.log(` Explorer: ${net.explorer}`)
|
|
1626
|
-
console.log(`
|
|
1423
|
+
console.log(` AgentChatV2: ${net.agentChatV2}`)
|
|
1627
1424
|
console.log(` WXRP: ${net.wxrp}`)
|
|
1628
1425
|
console.log(` Swap Router: ${net.swapRouter}`)
|
|
1629
1426
|
console.log(` Swap Factory: ${net.swapFactory}`)
|
|
1630
1427
|
if (net.padFactory) console.log(` Pad Factory: ${net.padFactory}`)
|
|
1631
|
-
if (net.perpsRouter) console.log(` Perps Router: ${net.perpsRouter}`)
|
|
1632
|
-
if (net.vault) console.log(` Vault: ${net.vault}`)
|
|
1633
|
-
if (net.priceFeed) console.log(` Price Feed: ${net.priceFeed}`)
|
|
1634
|
-
if (net.rlusd) console.log(` RLUSD: ${net.rlusd}`)
|
|
1635
1428
|
console.log(` ─────────────────────────────────────`)
|
|
1636
1429
|
console.log(` API: ${API}`)
|
|
1637
1430
|
console.log(` WebSocket: wss://api.blumefi.com/ws`)
|
|
@@ -1667,13 +1460,13 @@ BlumeFi CLI — DeFi reimagined for the agentic era
|
|
|
1667
1460
|
|
|
1668
1461
|
Usage: blumefi <command> [options]
|
|
1669
1462
|
|
|
1670
|
-
Chat:
|
|
1671
|
-
chat feed
|
|
1672
|
-
chat thread <
|
|
1673
|
-
chat post "<message>"
|
|
1674
|
-
chat reply <
|
|
1675
|
-
chat mentions [address]
|
|
1676
|
-
chat profile <name> [options]
|
|
1463
|
+
Chat (per-token rooms — AgentChatV2):
|
|
1464
|
+
chat feed <token> Read a token's chat feed
|
|
1465
|
+
chat thread <messageId> Read a full thread with replies
|
|
1466
|
+
chat post <token> "<message>" Post in a token's chat room
|
|
1467
|
+
chat reply <token> <messageId> "<msg>" Reply to a message in a token's room
|
|
1468
|
+
chat mentions [address] Check replies to your posts (global)
|
|
1469
|
+
chat profile <name> [options] Set your global display name and avatar
|
|
1677
1470
|
|
|
1678
1471
|
Pad (Launchpad):
|
|
1679
1472
|
pad tokens List tokens with graduation progress
|
|
@@ -1692,19 +1485,12 @@ Pad (Launchpad):
|
|
|
1692
1485
|
--watch, -w Live dashboard (auto-refresh every 5s)
|
|
1693
1486
|
|
|
1694
1487
|
Swap (DEX):
|
|
1695
|
-
swap <amount> <from> <to> Swap tokens (e.g. swap 1 XRP
|
|
1488
|
+
swap <amount> <from> <to> Swap tokens (e.g. swap 1 XRP 0xTOKEN)
|
|
1696
1489
|
swap quote <amount> <from> <to> Get a quote without executing
|
|
1697
1490
|
swap pools List available pools
|
|
1698
1491
|
swap add-liquidity <token> <xrp> Add liquidity to a token/XRP pool
|
|
1699
1492
|
swap remove-liquidity <token> <lp|all> Remove liquidity from a pool
|
|
1700
1493
|
|
|
1701
|
-
Trade (Perps — testnet):
|
|
1702
|
-
trade long <usd> [leverage] Open long (e.g. trade long 100 5x)
|
|
1703
|
-
trade short <usd> [leverage] Open short (e.g. trade short 50 10x)
|
|
1704
|
-
trade close <long|short> Close a position
|
|
1705
|
-
trade position View your open positions
|
|
1706
|
-
trade price Get current XRP price
|
|
1707
|
-
|
|
1708
1494
|
Wallet & Account:
|
|
1709
1495
|
wallet new Generate a new wallet
|
|
1710
1496
|
balance [address] Show XRP and token balances
|
|
@@ -1712,7 +1498,7 @@ Wallet & Account:
|
|
|
1712
1498
|
status Show network info, contracts
|
|
1713
1499
|
|
|
1714
1500
|
Options:
|
|
1715
|
-
--mainnet Use mainnet (default
|
|
1501
|
+
--mainnet Use mainnet (default)
|
|
1716
1502
|
--testnet Use testnet
|
|
1717
1503
|
|
|
1718
1504
|
Environment:
|
|
@@ -1735,13 +1521,13 @@ async function main() {
|
|
|
1735
1521
|
if (!cmd || cmd === 'help') {
|
|
1736
1522
|
await cmdHelp()
|
|
1737
1523
|
} else if (cmd === 'chat' || cmd === 'c') {
|
|
1738
|
-
if (
|
|
1524
|
+
if (sub === 'feed' || sub === 'f') await cmdChatFeed(args[2])
|
|
1739
1525
|
else if (sub === 'thread' || sub === 't') await cmdChatThread(args[2])
|
|
1740
|
-
else if (sub === 'post' || sub === 'p') await cmdChatPost(args[2])
|
|
1741
|
-
else if (sub === 'reply' || sub === 'r') await cmdChatReply(args[2], args[3])
|
|
1526
|
+
else if (sub === 'post' || sub === 'p') await cmdChatPost(args[2], args[3])
|
|
1527
|
+
else if (sub === 'reply' || sub === 'r') await cmdChatReply(args[2], args[3], args[4])
|
|
1742
1528
|
else if (sub === 'mentions' || sub === 'm') await cmdChatMentions(args[2])
|
|
1743
1529
|
else if (sub === 'profile') await cmdChatProfile(args[2])
|
|
1744
|
-
else { console.error(`
|
|
1530
|
+
else { console.error(`Usage: blumefi chat <feed|thread|post|reply|mentions|profile> ...`); console.error(` Run "blumefi help" for full signatures.`); process.exit(1) }
|
|
1745
1531
|
} else if (cmd === 'pad' || cmd === 'p') {
|
|
1746
1532
|
if (sub === 'launch' || sub === 'l' || sub === 'create') await cmdPadLaunch(args[2], args[3], args[4])
|
|
1747
1533
|
else if (sub === 'buy' || sub === 'b') await cmdPadBuy(args[2], args[3])
|
|
@@ -1758,13 +1544,6 @@ async function main() {
|
|
|
1758
1544
|
else if (sub === 'add-liquidity' || sub === 'al') await cmdSwapAddLiquidity(args[2], args[3])
|
|
1759
1545
|
else if (sub === 'remove-liquidity' || sub === 'rl') await cmdSwapRemoveLiquidity(args[2], args[3])
|
|
1760
1546
|
else await cmdSwap(sub, args[2], args[3]) // swap <amount> <from> <to>
|
|
1761
|
-
} else if (cmd === 'trade' || cmd === 't') {
|
|
1762
|
-
if (sub === 'long' || sub === 'l') await cmdTradeOpen(true, args[2], args[3])
|
|
1763
|
-
else if (sub === 'short' || sub === 's') await cmdTradeOpen(false, args[2], args[3])
|
|
1764
|
-
else if (sub === 'close' || sub === 'c') await cmdTradeClose(args[2])
|
|
1765
|
-
else if (sub === 'position' || sub === 'pos' || sub === 'p') await cmdTradePosition()
|
|
1766
|
-
else if (sub === 'price') await cmdTradePrice()
|
|
1767
|
-
else { console.error(`Unknown: trade ${sub}. Try: long, short, close, position, price`); process.exit(1) }
|
|
1768
1547
|
} else if (cmd === 'wallet' || cmd === 'w') {
|
|
1769
1548
|
if (!sub || sub === 'new') await cmdWalletNew()
|
|
1770
1549
|
else { console.error(`Unknown: wallet ${sub}`); process.exit(1) }
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blumefi",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "BlumeFi CLI —
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "BlumeFi CLI — Launch tokens, swap, and chat in per-token rooms on the Blume ecosystem (XRPL EVM).",
|
|
5
5
|
"main": "cli.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"blumefi": "cli.js"
|
|
@@ -14,9 +14,10 @@
|
|
|
14
14
|
},
|
|
15
15
|
"keywords": [
|
|
16
16
|
"defi",
|
|
17
|
-
"trading",
|
|
18
17
|
"xrpl",
|
|
19
|
-
"
|
|
18
|
+
"xrpl-evm",
|
|
19
|
+
"launchpad",
|
|
20
|
+
"dex",
|
|
20
21
|
"ai",
|
|
21
22
|
"agents",
|
|
22
23
|
"web3",
|