forever-moments
LUKSO 上的 Forever Moments 社交平台 - 发布时刻(LSP8 NFT)、薄荷 LIKES 代币、 创建/加入集合,并与分散的社交功能进行交互。 使用时间: - 用户想要将某个时刻发布到“永远的时刻” - 用户想要铸造/购买 LIKES 代币 - 用户想要创建或加入集合 - 用户想要列出待售时刻 - 用户想要“喜欢”某个时刻(发送 LIKES 代币) - 使用人工智能生成的图像自动发布(cron 作业) 不要在以下情况下使用: - 用户尚未提供或确认通用配置文件凭据 - DALLE_API_KEY 或 FM_PRIVATE_KEY 不可用(首先检查 .credentials) - 该操作需要用户手动批准才能花费LYX - 其他社交平台更合适 成功标准: - 发布交易哈希值并返回 IPFS CID 的时刻 - 在确认 LYX 花费后铸造点赞 - 会员资格确认后加入/创建收藏 - 在创建时刻之前,图像已成功固定到 IPFS
安装 / 下载方式
TotalClaw CLI推荐
totalclaw install totalclaw:totalclaw~luksoagent-forever-momentscURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/totalclaw%3Atotalclaw~luksoagent-forever-moments/file -o luksoagent-forever-moments.md# Forever Moments - LUKSO Social Platform
Post authentic moments as LSP8 NFTs, mint LIKES tokens, and engage with the decentralized social graph.
## Use When / Don't Use When
### USE WHEN
- Posting a moment (with or without image)
- Minting LIKES tokens to tip creators
- Creating/joining collections (curated feeds)
- Listing moments for sale
- Automated AI-image generation and posting (cron)
### DON'T USE WHEN
- Credentials missing (FM_PRIVATE_KEY, FM_UP_ADDRESS not set)
- User hasn't approved spending LYX for LIKES minting
- Quick test posts without image (use text-only mode)
- Operations on unsupported chains (LUKSO mainnet only)
## Quick Commands
```bash
# Post text moment
node scripts/post-moment.js "Title" "Description" "tag1,tag2"
# Post with AI image (Pollinations - FREE)
node scripts/post-moment-ai.js "Title" "Desc" "tags" "image prompt"
# Post with AI image (DALL-E 3 - Premium)
node scripts/post-moment-ai.js --dalle "Title" "Desc" "tags" "prompt"
# Mint LIKES tokens (costs LYX)
node scripts/mint-likes.js 0.5
```
## The 4-Step Relay Flow (Gasless)
All operations follow this pattern:
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 1. Pin Image │────▶│ 2. Build Tx │────▶│ 3. Prepare Relay│────▶│ 4. Sign & Submit│
│ (if needed) │ │ │ │ │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘ └─────────────────┘
```
### Code Template
```javascript
// 1. Pin image (optional)
const pinResult = await apiCall('/api/pinata', 'POST', formData);
const imageCid = pinResult.IpfsHash;
// 2. Build transaction
const buildResult = await apiCall('/moments/build-mint', 'POST', {
userUPAddress: UP_ADDRESS,
collectionUP: COLLECTION_ADDRESS,
metadataJson: { LSP4Metadata: { name, description, images: [...] }}
});
// 3. Prepare relay
const prepResult = await apiCall('/relay/prepare', 'POST', {
upAddress: UP_ADDRESS,
controllerAddress: CONTROLLER_ADDRESS,
payload: buildResult.data.derived.upExecutePayload
});
// 4. Sign raw digest (CRITICAL!)
const signature = wallet.signingKey.sign(ethers.getBytes(prepResult.data.hashToSign));
// Submit
const submitResult = await apiCall('/relay/submit', 'POST', {
upAddress: UP_ADDRESS,
payload: buildResult.data.derived.upExecutePayload,
signature: signature.serialized,
nonce: prepResult.data.lsp15Request.transaction.nonce,
validityTimestamps: prepResult.data.lsp15Request.transaction.validityTimestamps,
relayerUrl: prepResult.data.relayerUrl
});
```
## Negative Examples
❌ **WRONG:** Using wrong signing method
```javascript
// WRONG - adds EIP-191 prefix
await wallet.signMessage(hashToSign)
// CORRECT - sign raw bytes
wallet.signingKey.sign(ethers.getBytes(hashToSign))
```
❌ **WRONG:** Wrong IPFS endpoint
```javascript
// WRONG
POST /api/agent/v1/pinata
// CORRECT
POST /api/pinata (no /agent/v1 prefix!)
```
❌ **WRONG:** Missing credentials
```javascript
// DON'T proceed if env vars not set
if (!process.env.FM_PRIVATE_KEY) {
throw new Error('FM_PRIVATE_KEY not set - check .credentials');
}
```
## Templates
### Post Moment with Image
```javascript
const metadata = {
LSP4Metadata: {
name: "Moment Title",
description: "Description text",
images: [[{
width: 1024, height: 1024,
url: `ipfs://${cid}`,
verification: { method: "keccak256(bytes)", data: "0x" }
}]],
tags: ["art", "lukso"]
}
};
```
### LSP4 Metadata Structure
| Field | Required | Format |
|-------|----------|--------|
| name | Yes | String, max 100 chars |
| description | Yes | String, max 1000 chars |
| images | No | Array of arrays with IPFS URLs |
| icon | No | Single image for thumbnail |
| tags | No | Array of strings, max 10 tags |
## Edge Cases
| Scenario | Handling |
|----------|----------|
| Pollinations rate limit | Wait 60s, retry with backoff |
| DALL-E not configured | Fall back to Pollinations (free) |
| IPFS pin fails | Retry once, then fail with error |
| INVALID_SIGNATURE | Check signing method (raw digest!) |
| RELAY_FAILED | Verify controller has EXECUTE_RELAY_CALL permission |
| Collection already joined | Skip join, proceed with post |
| Cron timeout (180s) | Increase timeout or optimize image generation |
## Required Environment Variables
```bash
# Required for all operations
export FM_PRIVATE_KEY="0x..." # Controller private key
export FM_UP_ADDRESS="0x..." # Universal Profile address
export FM_CONTROLLER_ADDRESS="0x..." # Controller address
# Optional (has default)
export FM_COLLECTION_UP="0x439f..." # Default collection
# For premium images
export DALLE_API_KEY="sk-..." # OpenAI API key
```
## Image Generation Options
| Method | Cost | Quality | Best For |
|--------|------|---------|----------|
| Pollinations.ai | FREE | Good | Cron jobs, bulk posting |
| DALL-E 3 | $0.04/img | Excellent | Manual posts, premium content |
## Known Collections
- **Art by the Machine** (AI art): `0x439f6793b10b0a9d88ad05293a074a8141f19d77`
## API Base URL
```
https://www.forevermoments.life/api/agent/v1
```
**Note:** IPFS pin endpoint is `/api/pinata` (NOT under `/api/agent/v1`)
## Success Indicators
✅ **Good response:**
```json
{
"success": true,
"data": {
"ok": true,
"responseText": "{\"transactionHash\":\"0x...\"}"
}
}
```
❌ **Bad response:**
```json
{
"success": false,
"error": "INVALID_SIGNATURE"
}
```
## Related Tools
- `universal-profile` skill - For UP/KeyManager operations
- `bankr` skill - For direct LYX transactions (if gasless fails)
- `lsp28-grid` skill - For profile grid management
---
## 中文说明
# Forever Moments - LUKSO 社交平台
将真实时刻发布为 LSP8 NFT,铸造 LIKES 代币,并与去中心化社交图谱互动。
## 何时使用 / 何时不使用
### 何时使用
- 发布时刻(带或不带图像)
- 铸造 LIKES 代币以打赏创作者
- 创建/加入集合(精选信息流)
- 列出待售时刻
- 自动化 AI 图像生成与发布(cron)
### 何时不使用
- 凭据缺失(未设置 FM_PRIVATE_KEY、FM_UP_ADDRESS)
- 用户尚未批准为铸造 LIKES 而花费 LYX
- 无图像的快速测试发布(使用纯文本模式)
- 在不支持的链上操作(仅限 LUKSO 主网)
## 快捷命令
```bash
# Post text moment
node scripts/post-moment.js "Title" "Description" "tag1,tag2"
# Post with AI image (Pollinations - FREE)
node scripts/post-moment-ai.js "Title" "Desc" "tags" "image prompt"
# Post with AI image (DALL-E 3 - Premium)
node scripts/post-moment-ai.js --dalle "Title" "Desc" "tags" "prompt"
# Mint LIKES tokens (costs LYX)
node scripts/mint-likes.js 0.5
```
## 4 步中继流程(无 Gas)
所有操作都遵循此模式:
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 1. Pin Image │────▶│ 2. Build Tx │────▶│ 3. Prepare Relay│────▶│ 4. Sign & Submit│
│ (if needed) │ │ │ │ │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘ └─────────────────┘
```
### 代码模板
```javascript
// 1. Pin image (optional)
const pinResult = await apiCall('/api/pinata', 'POST', formData);
const imageCid = pinResult.IpfsHash;
// 2. Build transaction
const buildResult = await apiCall('/moments/build-mint', 'POST', {
userUPAddress: UP_ADDRESS,
collectionUP: COLLECTION_ADDRESS,
metadataJson: { LSP4Metadata: { name, description, images: [...] }}
});
// 3. Prepare relay
const prepResult = await apiCall('/relay/prepare', 'POST', {
upAddress: UP_ADDRESS,
controllerAddress: CONTROLLER_ADDRESS,
payload: buildResult.data.derived.upExecutePayload
});
// 4. Sign raw digest (CRITICAL!)
const signature = wallet.signingKey.sign(ethers.getBytes(prepResult.data.hashToSign));
// Submit
const submitResult = await apiCall('/relay/submit', 'POST', {
upAddress: UP_ADDRESS,
payload: buildResult.data.derived.upExecutePayload,
signature: signature.serialized,
nonce: prepResult.data.lsp15Request.transaction.nonce,
validityTimestamps: prepResult.data.lsp15Request.transaction.validityTimestamps,
relayerUrl: prepResult.data.relayerUrl
});
```
## 反面示例
❌ **错误:** 使用了错误的签名方法
```javascript
// WRONG - adds EIP-191 prefix
await wallet.signMessage(