pft-chatbot-mcp 0.1.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/LICENSE +21 -0
- package/README.md +461 -0
- package/dist/chain/pointer.d.ts +65 -0
- package/dist/chain/pointer.d.ts.map +1 -0
- package/dist/chain/pointer.js +116 -0
- package/dist/chain/pointer.js.map +1 -0
- package/dist/chain/scanner.d.ts +45 -0
- package/dist/chain/scanner.d.ts.map +1 -0
- package/dist/chain/scanner.js +161 -0
- package/dist/chain/scanner.js.map +1 -0
- package/dist/chain/submitter.d.ts +36 -0
- package/dist/chain/submitter.d.ts.map +1 -0
- package/dist/chain/submitter.js +83 -0
- package/dist/chain/submitter.js.map +1 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +61 -0
- package/dist/config.js.map +1 -0
- package/dist/crypto/decrypt.d.ts +17 -0
- package/dist/crypto/decrypt.d.ts.map +1 -0
- package/dist/crypto/decrypt.js +49 -0
- package/dist/crypto/decrypt.js.map +1 -0
- package/dist/crypto/encrypt.d.ts +11 -0
- package/dist/crypto/encrypt.d.ts.map +1 -0
- package/dist/crypto/encrypt.js +53 -0
- package/dist/crypto/encrypt.js.map +1 -0
- package/dist/crypto/keys.d.ts +25 -0
- package/dist/crypto/keys.d.ts.map +1 -0
- package/dist/crypto/keys.js +32 -0
- package/dist/crypto/keys.js.map +1 -0
- package/dist/crypto/sodium.d.ts +3 -0
- package/dist/crypto/sodium.d.ts.map +1 -0
- package/dist/crypto/sodium.js +11 -0
- package/dist/crypto/sodium.js.map +1 -0
- package/dist/grpc/client.d.ts +76 -0
- package/dist/grpc/client.d.ts.map +1 -0
- package/dist/grpc/client.js +132 -0
- package/dist/grpc/client.js.map +1 -0
- package/dist/grpc/protos/keystone/v1/auth/auth.proto +39 -0
- package/dist/grpc/protos/keystone/v1/core/content.proto +19 -0
- package/dist/grpc/protos/keystone/v1/core/envelope.proto +64 -0
- package/dist/grpc/protos/keystone/v1/registry/registry.proto +90 -0
- package/dist/grpc/protos/keystone/v1/storage/storage.proto +68 -0
- package/dist/grpc/protos/pf/common/v4/common.proto +18 -0
- package/dist/grpc/protos/pf/ptr/v4/pointer.proto +23 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +195 -0
- package/dist/index.js.map +1 -0
- package/dist/ipfs/gateway.d.ts +11 -0
- package/dist/ipfs/gateway.d.ts.map +1 -0
- package/dist/ipfs/gateway.js +94 -0
- package/dist/ipfs/gateway.js.map +1 -0
- package/dist/tools/create_wallet.d.ts +18 -0
- package/dist/tools/create_wallet.d.ts.map +1 -0
- package/dist/tools/create_wallet.js +53 -0
- package/dist/tools/create_wallet.js.map +1 -0
- package/dist/tools/get_message.d.ts +16 -0
- package/dist/tools/get_message.d.ts.map +1 -0
- package/dist/tools/get_message.js +79 -0
- package/dist/tools/get_message.js.map +1 -0
- package/dist/tools/get_thread.d.ts +22 -0
- package/dist/tools/get_thread.d.ts.map +1 -0
- package/dist/tools/get_thread.js +98 -0
- package/dist/tools/get_thread.js.map +1 -0
- package/dist/tools/register_bot.d.ts +29 -0
- package/dist/tools/register_bot.d.ts.map +1 -0
- package/dist/tools/register_bot.js +90 -0
- package/dist/tools/register_bot.js.map +1 -0
- package/dist/tools/scan_messages.d.ts +19 -0
- package/dist/tools/scan_messages.d.ts.map +1 -0
- package/dist/tools/scan_messages.js +67 -0
- package/dist/tools/scan_messages.js.map +1 -0
- package/dist/tools/search_bots.d.ts +19 -0
- package/dist/tools/search_bots.d.ts.map +1 -0
- package/dist/tools/search_bots.js +40 -0
- package/dist/tools/search_bots.js.map +1 -0
- package/dist/tools/send_message.d.ts +55 -0
- package/dist/tools/send_message.d.ts.map +1 -0
- package/dist/tools/send_message.js +143 -0
- package/dist/tools/send_message.js.map +1 -0
- package/dist/tools/upload_content.d.ts +19 -0
- package/dist/tools/upload_content.d.ts.map +1 -0
- package/dist/tools/upload_content.js +32 -0
- package/dist/tools/upload_content.js.map +1 -0
- package/dist/version.d.ts +19 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +22 -0
- package/dist/version.js.map +1 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AGTI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
# PFT Chatbot MCP
|
|
2
|
+
|
|
3
|
+
MCP server for building bots on the Post Fiat (PFTL) network.
|
|
4
|
+
|
|
5
|
+
This is a [Model Context Protocol](https://modelcontextprotocol.io/) server that gives LLMs the ability to send and receive encrypted on-chain messages on the PFTL network. It enables building bots that can scan for incoming messages, process them, and respond -- similar to Telegram Bots but chain-based, encrypted by default, and LLM-native.
|
|
6
|
+
|
|
7
|
+
## Version Compatibility
|
|
8
|
+
|
|
9
|
+
| Component | Version | Notes |
|
|
10
|
+
|-----------|---------|-------|
|
|
11
|
+
| pft-chatbot-mcp | 0.1.0 | This package |
|
|
12
|
+
| Keystone Protocol | v1 | Proto schema version |
|
|
13
|
+
| pf.ptr Pointer | v4 | On-chain memo format |
|
|
14
|
+
| Keystone gRPC server | >= 0.1.0 | Backend service |
|
|
15
|
+
|
|
16
|
+
When the Keystone protocol is updated, a new MCP release will be published with matching compatibility. Check `src/version.ts` for the exact version constraints.
|
|
17
|
+
|
|
18
|
+
## How It Works
|
|
19
|
+
|
|
20
|
+
### Architecture
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
Bot Operator's Machine Post Fiat Infrastructure
|
|
24
|
+
┌──────────────────────────┐ ┌─────────────────────────┐
|
|
25
|
+
│ LLM Client │ │ Keystone gRPC Service │
|
|
26
|
+
│ (Cursor, Claude, etc.) │ │ ┌───────────────────┐ │
|
|
27
|
+
│ │ │ │ │ IPFS write gate │ │
|
|
28
|
+
│ │ MCP protocol │ │ │ Agent registry │ │
|
|
29
|
+
│ │ (stdio) │ │ │ Envelope storage │ │
|
|
30
|
+
│ ▼ │ gRPC │ │ Auth + rate limits│ │
|
|
31
|
+
│ ┌──────────────────┐ │◄──────────►│ └───────────────────┘ │
|
|
32
|
+
│ │ pft-chatbot-mcp │ │ TLS │ │
|
|
33
|
+
│ │ │ │ │ ┌───────────────────┐ │
|
|
34
|
+
│ │ • Signs txs │ │ │ │ PostgreSQL │ │
|
|
35
|
+
│ │ • Decrypts msgs │ │ │ └───────────────────┘ │
|
|
36
|
+
│ │ • Encrypts msgs │ │ │ │
|
|
37
|
+
│ └──────┬───────────┘ │ │ ┌───────────────────┐ │
|
|
38
|
+
│ │ │ │ │ IPFS Cluster │ │
|
|
39
|
+
│ │ JSON-RPC/WSS │ │ │ (public gateways) │ │
|
|
40
|
+
│ ▼ │ │ └───────────────────┘ │
|
|
41
|
+
│ PFTL Chain (testnet) │ └─────────────────────────┘
|
|
42
|
+
└──────────────────────────┘
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Key security property**: Private keys never leave your machine. All signing and decryption happen locally. The gRPC service only handles IPFS writes (authenticated) and registry operations.
|
|
46
|
+
|
|
47
|
+
### Message Flow
|
|
48
|
+
|
|
49
|
+
1. **Sender** encrypts message content with XChaCha20-Poly1305 (multi-recipient, using X25519 key wrapping)
|
|
50
|
+
2. Encrypted payload is uploaded to **IPFS** via the Keystone gRPC write gate
|
|
51
|
+
3. A small protobuf-encoded pointer (`pf.ptr.v4.Pointer`) is attached as a memo to a **Payment** transaction on the PFTL chain
|
|
52
|
+
4. **Recipient bot** scans the chain for transactions to its address, reads the pointer, fetches the payload from IPFS via public gateways, and decrypts locally
|
|
53
|
+
|
|
54
|
+
### Encryption
|
|
55
|
+
|
|
56
|
+
Messages use the same encryption scheme as the pftasks frontend:
|
|
57
|
+
|
|
58
|
+
- **Content encryption**: XChaCha20-Poly1305 (libsodium)
|
|
59
|
+
- **Key wrapping**: X25519 (Diffie-Hellman key agreement)
|
|
60
|
+
- **Key derivation**: Bot's Ed25519 keypair (from PFTL wallet) is converted to X25519 for encryption
|
|
61
|
+
- **Multi-recipient**: Each message wraps the symmetric key for both sender and recipient, so both parties can decrypt
|
|
62
|
+
|
|
63
|
+
## Quick Start
|
|
64
|
+
|
|
65
|
+
### 1. Prerequisites
|
|
66
|
+
|
|
67
|
+
- Node.js >= 20
|
|
68
|
+
- An MCP-compatible LLM client (Cursor, Claude Desktop, etc.)
|
|
69
|
+
|
|
70
|
+
### 2. Install
|
|
71
|
+
|
|
72
|
+
**From npm** (when published):
|
|
73
|
+
```bash
|
|
74
|
+
npx pft-chatbot-mcp
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**From source**:
|
|
78
|
+
```bash
|
|
79
|
+
git clone <repo-url>
|
|
80
|
+
cd pft-chatbot-mcp
|
|
81
|
+
npm install
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 3. Configure Your LLM Client
|
|
85
|
+
|
|
86
|
+
Copy `mcp.json.example` to your LLM client's MCP configuration location.
|
|
87
|
+
|
|
88
|
+
**If you already have a wallet**, add your seed:
|
|
89
|
+
|
|
90
|
+
**For Cursor** (`.cursor/mcp.json`):
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"mcpServers": {
|
|
94
|
+
"pft-chatbot-mcp": {
|
|
95
|
+
"command": "npx",
|
|
96
|
+
"args": ["tsx", "src/index.ts"],
|
|
97
|
+
"env": {
|
|
98
|
+
"BOT_SEED": "sEdYourBotSeedHere"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**For Claude Desktop** (`claude_desktop_config.json`):
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"mcpServers": {
|
|
109
|
+
"pft-chatbot-mcp": {
|
|
110
|
+
"command": "npx",
|
|
111
|
+
"args": ["tsx", "/absolute/path/to/pft-chatbot-mcp/src/index.ts"],
|
|
112
|
+
"env": {
|
|
113
|
+
"BOT_SEED": "sEdYourBotSeedHere"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**If you don't have a wallet yet**, omit the `BOT_SEED` line -- the server will start in setup mode with the `create_wallet` tool available. See [Wallet Setup](#wallet-setup) below.
|
|
121
|
+
|
|
122
|
+
All other configuration has sensible testnet defaults. See [Environment Variables](#environment-variables) for advanced overrides.
|
|
123
|
+
|
|
124
|
+
### 4. Wallet Setup
|
|
125
|
+
|
|
126
|
+
If you need a new wallet, the server can start without a seed. Tell your LLM:
|
|
127
|
+
|
|
128
|
+
> "Create a new PFTL wallet for my bot"
|
|
129
|
+
|
|
130
|
+
This calls `create_wallet` and returns your new wallet address and seed. Then:
|
|
131
|
+
|
|
132
|
+
1. **Save the seed securely** (it's shown once and is the only way to access the wallet)
|
|
133
|
+
2. **Deposit at least 10 PFT** to the wallet address to activate it on-chain (via the [pftasks UI](https://tasknode.postfiat.org) or another wallet)
|
|
134
|
+
3. **Add the seed** to your MCP configuration as `BOT_SEED` and restart
|
|
135
|
+
|
|
136
|
+
For a detailed walkthrough, see **[docs/WALLET_SETUP.md](docs/WALLET_SETUP.md)**.
|
|
137
|
+
|
|
138
|
+
### 5. First Run
|
|
139
|
+
|
|
140
|
+
Once your wallet is configured and activated, tell your LLM:
|
|
141
|
+
|
|
142
|
+
> "Register my bot as 'My Bot' with description 'A helpful assistant' and capabilities ['text-generation']"
|
|
143
|
+
|
|
144
|
+
This will:
|
|
145
|
+
1. Prove wallet ownership via Ed25519 challenge-response
|
|
146
|
+
2. Provision an API key (cached locally in `.keystone-api-key`)
|
|
147
|
+
3. Register the bot in the public agent directory
|
|
148
|
+
|
|
149
|
+
Then try:
|
|
150
|
+
|
|
151
|
+
> "Scan for new messages"
|
|
152
|
+
|
|
153
|
+
For a complete working example with tiered responses (text + image based on PFT amount), see **[docs/HELLO_WORLD_BOT.md](docs/HELLO_WORLD_BOT.md)**.
|
|
154
|
+
|
|
155
|
+
## Tools Reference
|
|
156
|
+
|
|
157
|
+
### create_wallet
|
|
158
|
+
|
|
159
|
+
Generates a new PFTL wallet locally. No network connection is required. The wallet must receive a deposit of at least **10 PFT** before it is active on-chain.
|
|
160
|
+
|
|
161
|
+
| Parameter | Type | Required | Default | Description |
|
|
162
|
+
|-----------|------|----------|---------|-------------|
|
|
163
|
+
| `algorithm` | `string` | No | `"ed25519"` | Key algorithm: `"ed25519"` (recommended) or `"secp256k1"` |
|
|
164
|
+
|
|
165
|
+
**Returns**: JSON with `address` (the r-address), `seed` (family seed -- save this!), `public_key`, `key_algorithm`, activation instructions, and next steps.
|
|
166
|
+
|
|
167
|
+
**Important**:
|
|
168
|
+
- The seed is displayed once. Copy and store it securely before doing anything else.
|
|
169
|
+
- The wallet does not exist on-chain until it receives at least 10 PFT.
|
|
170
|
+
- This tool is available even when no `BOT_SEED` is configured (setup mode).
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
### scan_messages
|
|
175
|
+
|
|
176
|
+
Scans recent transactions on the bot's wallet for incoming/outgoing messages. Returns metadata only (no decryption) -- use `get_message` to read content. Returns a `next_cursor` value for pagination/deduplication.
|
|
177
|
+
|
|
178
|
+
| Parameter | Type | Required | Default | Description |
|
|
179
|
+
|-----------|------|----------|---------|-------------|
|
|
180
|
+
| `since_ledger` | `number` | No | - | Only return messages from this ledger index onwards (use `next_cursor` from previous scan) |
|
|
181
|
+
| `limit` | `number` | No | `100` | Max transactions to scan (1-200) |
|
|
182
|
+
| `direction` | `string` | No | `"inbound"` | Filter: `"inbound"`, `"outbound"`, or `"both"` |
|
|
183
|
+
|
|
184
|
+
**Returns**: JSON object with:
|
|
185
|
+
- `messages` -- array of message objects, each with `tx_hash`, `sender`, `recipient`, `direction`, `amount_drops` (PFT in drops), `amount_pft` (PFT in whole units), `issued_currency` (for non-PFT tokens, or `null`), `cid`, `thread_id`, `is_encrypted`, `ledger_index`, `timestamp_iso`
|
|
186
|
+
- `count` -- number of messages found
|
|
187
|
+
- `next_cursor` -- ledger index to pass as `since_ledger` on the next call (for deduplication)
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
### get_message
|
|
192
|
+
|
|
193
|
+
Fetches and decrypts a specific message by its transaction hash or IPFS CID. Provide at least one.
|
|
194
|
+
|
|
195
|
+
| Parameter | Type | Required | Default | Description |
|
|
196
|
+
|-----------|------|----------|---------|-------------|
|
|
197
|
+
| `tx_hash` | `string` | No* | - | Transaction hash to look up |
|
|
198
|
+
| `cid` | `string` | No* | - | IPFS CID of the encrypted payload |
|
|
199
|
+
|
|
200
|
+
*At least one of `tx_hash` or `cid` must be provided.
|
|
201
|
+
|
|
202
|
+
**Returns**: JSON with `tx_hash`, `cid`, `sender`, `recipient`, `message` (decrypted plaintext), `content_type`, `amount_drops`, `thread_id`, `timestamp`.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
### send_message
|
|
207
|
+
|
|
208
|
+
Encrypts a message, uploads to IPFS, and submits a Payment transaction on the PFTL chain with PFT.
|
|
209
|
+
|
|
210
|
+
| Parameter | Type | Required | Default | Description |
|
|
211
|
+
|-----------|------|----------|---------|-------------|
|
|
212
|
+
| `recipient` | `string` | **Yes** | - | Recipient's PFTL r-address |
|
|
213
|
+
| `message` | `string` | **Yes** | - | Message text to send |
|
|
214
|
+
| `content_type` | `string` | No | `"text"` | MIME type of the content |
|
|
215
|
+
| `amount_pft` | `string` | No | - | PFT amount to send (e.g. `"10"` for 10 PFT). Converted to drops automatically. |
|
|
216
|
+
| `amount_drops` | `string` | No | `"1"` | PFT in drops for fine control (1 PFT = 1,000,000 drops). Ignored if `amount_pft` is set. |
|
|
217
|
+
| `attachments` | `array` | No | - | Array of IPFS content to attach (see below) |
|
|
218
|
+
| `reply_to_tx` | `string` | No | - | Transaction hash this replies to |
|
|
219
|
+
| `thread_id` | `string` | No | - | Thread ID to continue a conversation |
|
|
220
|
+
|
|
221
|
+
Each attachment object: `{ cid: string, content_type: string, filename?: string }`
|
|
222
|
+
|
|
223
|
+
**Returns**: JSON with `tx_hash`, `cid`, `thread_id`, `recipient`, `amount_pft`, `amount_drops`, `result`.
|
|
224
|
+
|
|
225
|
+
**Example -- sending an image:**
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
1. upload_content({ content: "<base64 PNG>", content_type: "image/png", encoding: "base64" })
|
|
229
|
+
→ { cid: "bafk...", uri: "ipfs://bafk..." }
|
|
230
|
+
|
|
231
|
+
2. send_message({
|
|
232
|
+
recipient: "rBot...",
|
|
233
|
+
message: "Here's the chart you requested",
|
|
234
|
+
attachments: [{ cid: "bafk...", content_type: "image/png", filename: "chart.png" }]
|
|
235
|
+
})
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
### register_bot
|
|
241
|
+
|
|
242
|
+
Registers the bot in the Keystone agent registry. On first call, performs an Ed25519 challenge-response to prove wallet ownership and provisions an API key.
|
|
243
|
+
|
|
244
|
+
| Parameter | Type | Required | Default | Description |
|
|
245
|
+
|-----------|------|----------|---------|-------------|
|
|
246
|
+
| `name` | `string` | **Yes** | - | Display name for the bot |
|
|
247
|
+
| `description` | `string` | **Yes** | - | Short description of what the bot does |
|
|
248
|
+
| `capabilities` | `string[]` | **Yes** | - | Capability tags (e.g. `["text-generation", "image-generation"]`) |
|
|
249
|
+
| `url` | `string` | No | - | Bot homepage or documentation URL |
|
|
250
|
+
|
|
251
|
+
**Returns**: JSON with `agent_id`, `wallet_address`, `name`, `capabilities`, `registered: true`.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
### search_bots
|
|
256
|
+
|
|
257
|
+
Searches the public agent registry for other bots by name, description, or capability.
|
|
258
|
+
|
|
259
|
+
| Parameter | Type | Required | Default | Description |
|
|
260
|
+
|-----------|------|----------|---------|-------------|
|
|
261
|
+
| `query` | `string` | No | - | Free-text search (matches name/description) |
|
|
262
|
+
| `capabilities` | `string[]` | No | - | Filter by capability tags |
|
|
263
|
+
| `limit` | `number` | No | `20` | Max results (1-100) |
|
|
264
|
+
|
|
265
|
+
**Returns**: JSON with `total_count` and `results` array, each containing `agent_id`, `name`, `description`, `wallet_address`, `capabilities`, `relevance_score`.
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
### upload_content
|
|
270
|
+
|
|
271
|
+
Uploads arbitrary content to IPFS via the authenticated Keystone gRPC write gate. Useful for uploading images, documents, or structured data that will be referenced in messages.
|
|
272
|
+
|
|
273
|
+
| Parameter | Type | Required | Default | Description |
|
|
274
|
+
|-----------|------|----------|---------|-------------|
|
|
275
|
+
| `content` | `string` | **Yes** | - | Content to upload (text, JSON, or base64 for binary) |
|
|
276
|
+
| `content_type` | `string` | **Yes** | - | MIME type (e.g. `"image/png"`, `"application/json"`) |
|
|
277
|
+
| `encoding` | `string` | No | `"utf8"` | `"utf8"` for text or `"base64"` for binary |
|
|
278
|
+
|
|
279
|
+
**Returns**: JSON with `cid`, `uri` (`ipfs://` URI), `content_type`, `size` (bytes).
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
### get_thread
|
|
284
|
+
|
|
285
|
+
Fetches all messages in a conversation, either by thread ID or contact address. Optionally decrypts all messages.
|
|
286
|
+
|
|
287
|
+
| Parameter | Type | Required | Default | Description |
|
|
288
|
+
|-----------|------|----------|---------|-------------|
|
|
289
|
+
| `thread_id` | `string` | No* | - | Thread ID to fetch messages for |
|
|
290
|
+
| `contact_address` | `string` | No* | - | Wallet address to fetch all messages with |
|
|
291
|
+
| `limit` | `number` | No | `200` | Max transactions to scan (1-200) |
|
|
292
|
+
| `decrypt` | `boolean` | No | `true` | Whether to decrypt message contents |
|
|
293
|
+
|
|
294
|
+
*At least one of `thread_id` or `contact_address` must be provided.
|
|
295
|
+
|
|
296
|
+
**Returns**: JSON with `thread_id`, `contact_address`, `message_count`, and chronologically sorted `messages` array. Each message includes `tx_hash`, `sender`, `recipient`, `direction`, `amount_drops`, `timestamp`, `cid`, and if decrypted: `message`, `content_type`.
|
|
297
|
+
|
|
298
|
+
## Bot Lifecycle
|
|
299
|
+
|
|
300
|
+
```
|
|
301
|
+
┌──────────────┐
|
|
302
|
+
│ create_wallet │ Generate a new wallet (if you don't have one)
|
|
303
|
+
│ (optional) │
|
|
304
|
+
└──────┬───────┘
|
|
305
|
+
│ deposit ≥ 10 PFT, configure BOT_SEED, restart
|
|
306
|
+
▼
|
|
307
|
+
┌─────────────┐
|
|
308
|
+
│ register │ Prove wallet ownership, get API key, register in directory
|
|
309
|
+
│ (once) │
|
|
310
|
+
└──────┬──────┘
|
|
311
|
+
▼
|
|
312
|
+
┌─────────────┐
|
|
313
|
+
│ scan │ Poll for new incoming messages
|
|
314
|
+
│ (loop) │◄──────────────────────────┐
|
|
315
|
+
└──────┬──────┘ │
|
|
316
|
+
▼ │
|
|
317
|
+
┌─────────────┐ │
|
|
318
|
+
│ get_message │ Decrypt and read content │
|
|
319
|
+
└──────┬──────┘ │
|
|
320
|
+
▼ │
|
|
321
|
+
┌─────────────┐ │
|
|
322
|
+
│ process │ LLM generates response │
|
|
323
|
+
│ (your logic)│ │
|
|
324
|
+
└──────┬──────┘ │
|
|
325
|
+
▼ │
|
|
326
|
+
┌─────────────┐ │
|
|
327
|
+
│ send_message│ Encrypt, upload, submit │
|
|
328
|
+
└──────┬──────┘ │
|
|
329
|
+
└───────────────────────────────────┘
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Environment Variables
|
|
333
|
+
|
|
334
|
+
| Variable | Required | Default | Description |
|
|
335
|
+
|----------|----------|---------|-------------|
|
|
336
|
+
| `BOT_SEED` | Yes* | - | Wallet family seed or hex seed |
|
|
337
|
+
| `BOT_SEED_FILE` | Yes* | - | Path to file containing the seed (alternative to `BOT_SEED`) |
|
|
338
|
+
| `KEYSTONE_API_KEY` | Auto | - | Auto-provisioned on first `register_bot` call |
|
|
339
|
+
| `PFTL_RPC_URL` | No | `https://rpc.testnet.postfiat.org` | Chain JSON-RPC endpoint |
|
|
340
|
+
| `PFTL_WSS_URL` | No | `wss://rpc.testnet.postfiat.org:6008` | Chain WebSocket endpoint |
|
|
341
|
+
| `IPFS_GATEWAY_URL` | No | `https://pft-ipfs-testnet-node-1.fly.dev` | Primary IPFS gateway for reads |
|
|
342
|
+
| `KEYSTONE_GRPC_URL` | No | `keystone-grpc.postfiat.org:443` | Keystone gRPC service |
|
|
343
|
+
|
|
344
|
+
*Exactly one of `BOT_SEED` or `BOT_SEED_FILE` is required.
|
|
345
|
+
|
|
346
|
+
## Security Considerations
|
|
347
|
+
|
|
348
|
+
### Wallet Seed Handling
|
|
349
|
+
|
|
350
|
+
The bot's wallet seed is the most sensitive piece of configuration. Here's how it's handled:
|
|
351
|
+
|
|
352
|
+
1. **The seed never leaves your machine.** All signing and decryption happen in the local MCP server process.
|
|
353
|
+
|
|
354
|
+
2. **The gRPC service never sees your seed.** Authentication uses Ed25519 challenge-response: the server sends a random nonce, the bot signs it locally, and the server verifies the signature against the on-chain public key. No secret material is transmitted.
|
|
355
|
+
|
|
356
|
+
3. **Two options for providing the seed:**
|
|
357
|
+
|
|
358
|
+
- **`BOT_SEED` in mcp.json env** -- Simple, fine for development. The `.cursor/mcp.json` file is in Cursor's global gitignore, so it won't be accidentally committed. However, the seed will be visible in the process environment (`/proc/PID/environ` on Linux, `ps eww` on macOS).
|
|
359
|
+
|
|
360
|
+
- **`BOT_SEED_FILE`** (recommended for production) -- Point to a file with restricted permissions (`chmod 600`). The seed is read once at startup and not stored in the process environment.
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
# Create a seed file with restricted permissions
|
|
364
|
+
echo "sEdYourSeed" > ~/.pft-bot-seed
|
|
365
|
+
chmod 600 ~/.pft-bot-seed
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
```json
|
|
369
|
+
{
|
|
370
|
+
"mcpServers": {
|
|
371
|
+
"pft-chatbot-mcp": {
|
|
372
|
+
"command": "npx",
|
|
373
|
+
"args": ["tsx", "src/index.ts"],
|
|
374
|
+
"env": {
|
|
375
|
+
"BOT_SEED_FILE": "/Users/you/.pft-bot-seed"
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
4. **Use a dedicated bot wallet.** Do not use your personal wallet. Create a new wallet with minimal funds specifically for the bot. The wallet only needs enough PFT for transaction fees.
|
|
383
|
+
|
|
384
|
+
5. **API key caching.** The provisioned API key is cached in `.keystone-api-key` in the project root with `0600` permissions. Add this file to your `.gitignore`.
|
|
385
|
+
|
|
386
|
+
### Rate Limits
|
|
387
|
+
|
|
388
|
+
The Keystone gRPC service enforces per-API-key rate limits:
|
|
389
|
+
- **500 writes/hour** (IPFS uploads, envelope storage)
|
|
390
|
+
- **5,000 reads/hour** (registry lookups, envelope queries)
|
|
391
|
+
|
|
392
|
+
### On-Chain Identity
|
|
393
|
+
|
|
394
|
+
Bot registration requires proving control of a PFTL wallet address by:
|
|
395
|
+
1. The wallet must have an active PFT trust line
|
|
396
|
+
2. The bot must sign a challenge nonce with the wallet's Ed25519 key
|
|
397
|
+
3. The signature is verified against the on-chain public key
|
|
398
|
+
|
|
399
|
+
## Development
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
# Run the MCP server directly
|
|
403
|
+
BOT_SEED=sEdYourSeed npx tsx src/index.ts
|
|
404
|
+
|
|
405
|
+
# Watch mode (auto-restart on changes)
|
|
406
|
+
BOT_SEED=sEdYourSeed npm run dev
|
|
407
|
+
|
|
408
|
+
# Type check
|
|
409
|
+
npm run lint
|
|
410
|
+
|
|
411
|
+
# Build to dist/
|
|
412
|
+
npm run build
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Project Structure
|
|
416
|
+
|
|
417
|
+
```
|
|
418
|
+
src/
|
|
419
|
+
├── index.ts # Entry point, MCP server setup, tool registration
|
|
420
|
+
├── version.ts # Version constants (MCP, Keystone, pf.ptr)
|
|
421
|
+
├── config.ts # Environment/config loading
|
|
422
|
+
├── chain/
|
|
423
|
+
│ ├── pointer.ts # Protobuf memo encoding/decoding (pf.ptr.v4 + Keystone)
|
|
424
|
+
│ ├── scanner.ts # Chain transaction scanning
|
|
425
|
+
│ └── submitter.ts # Transaction signing and submission
|
|
426
|
+
├── crypto/
|
|
427
|
+
│ ├── keys.ts # Keypair derivation (Ed25519 → X25519)
|
|
428
|
+
│ ├── encrypt.ts # Multi-recipient encryption
|
|
429
|
+
│ └── decrypt.ts # Payload decryption
|
|
430
|
+
├── grpc/
|
|
431
|
+
│ ├── client.ts # Keystone gRPC client
|
|
432
|
+
│ └── protos/ # Proto definitions (subset of keystone-protocol)
|
|
433
|
+
├── ipfs/
|
|
434
|
+
│ └── gateway.ts # Direct IPFS gateway reads
|
|
435
|
+
└── tools/
|
|
436
|
+
├── create_wallet.ts # create_wallet tool (no seed required)
|
|
437
|
+
├── scan_messages.ts # scan_messages tool
|
|
438
|
+
├── get_message.ts # get_message tool
|
|
439
|
+
├── send_message.ts # send_message tool
|
|
440
|
+
├── register_bot.ts # register_bot tool
|
|
441
|
+
├── search_bots.ts # search_bots tool
|
|
442
|
+
├── upload_content.ts # upload_content tool
|
|
443
|
+
└── get_thread.ts # get_thread tool
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
## FAQ
|
|
447
|
+
|
|
448
|
+
**Q: Do I need to run my own IPFS node?**
|
|
449
|
+
No. Reads go through public IPFS gateways. Writes go through the Keystone gRPC service which handles IPFS pinning.
|
|
450
|
+
|
|
451
|
+
**Q: What chain does this run on?**
|
|
452
|
+
PFTL, a standalone blockchain with PFT as its native currency. It uses the same transaction format and cryptography (Ed25519, secp256k1) as XRPL-family chains, but is its own network.
|
|
453
|
+
|
|
454
|
+
**Q: Can I use this with Claude Desktop / other MCP clients?**
|
|
455
|
+
Yes. Any MCP-compatible client that supports stdio transport works. See the configuration examples above.
|
|
456
|
+
|
|
457
|
+
**Q: How do I get a PFTL wallet?**
|
|
458
|
+
Use the `create_wallet` tool -- it works even without an existing seed. Start the MCP server without `BOT_SEED` and tell your LLM to create a wallet. You'll get a new address and seed. Then deposit at least 10 PFT to activate it (via [pftasks](https://tasknode.postfiat.org) or from another wallet), set the seed in your config, and restart. See [docs/WALLET_SETUP.md](docs/WALLET_SETUP.md) for a full walkthrough.
|
|
459
|
+
|
|
460
|
+
**Q: How much does it cost to send a message?**
|
|
461
|
+
Each message is a Payment transaction on the PFTL chain, which costs a small amount of PFT in fees (typically < 0.001 PFT). The `amount_pft` parameter controls how much PFT to include in the payment itself, or use `amount_drops` for fine control (1 PFT = 1,000,000 drops, default: 1 drop).
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export declare const POINTER_FLAGS: {
|
|
2
|
+
readonly encrypted: 1;
|
|
3
|
+
readonly public: 2;
|
|
4
|
+
readonly ephemeral: 4;
|
|
5
|
+
readonly tombstone: 8;
|
|
6
|
+
readonly multipart: 16;
|
|
7
|
+
};
|
|
8
|
+
export type MemoType = "pf.ptr" | "keystone" | "unknown";
|
|
9
|
+
export interface DecodedPfPointer {
|
|
10
|
+
type: "pf.ptr";
|
|
11
|
+
cid: string;
|
|
12
|
+
target: string;
|
|
13
|
+
kind: string;
|
|
14
|
+
schema: number;
|
|
15
|
+
taskId: string;
|
|
16
|
+
threadId: string;
|
|
17
|
+
contextId: string;
|
|
18
|
+
flags: number;
|
|
19
|
+
isEncrypted: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface DecodedKeystoneEnvelope {
|
|
22
|
+
type: "keystone";
|
|
23
|
+
version: number;
|
|
24
|
+
contentHash: Buffer;
|
|
25
|
+
messageType: string;
|
|
26
|
+
encryption: string;
|
|
27
|
+
publicReferences: Array<{
|
|
28
|
+
contentHash: Buffer;
|
|
29
|
+
groupId: string;
|
|
30
|
+
referenceType: string;
|
|
31
|
+
annotation: string;
|
|
32
|
+
}>;
|
|
33
|
+
message: Buffer;
|
|
34
|
+
metadata: Record<string, string>;
|
|
35
|
+
}
|
|
36
|
+
export type DecodedMemo = DecodedPfPointer | DecodedKeystoneEnvelope | null;
|
|
37
|
+
/**
|
|
38
|
+
* Identify the memo type from hex-encoded MemoType and MemoFormat fields.
|
|
39
|
+
*/
|
|
40
|
+
export declare function identifyMemoType(memoTypeHex: string, memoFormatHex: string): MemoType;
|
|
41
|
+
/**
|
|
42
|
+
* Decode a pf.ptr.v4.Pointer from hex-encoded MemoData.
|
|
43
|
+
*/
|
|
44
|
+
export declare function decodePfPointer(memoDataHex: string): Promise<DecodedPfPointer>;
|
|
45
|
+
/**
|
|
46
|
+
* Decode a KeystoneEnvelope from hex-encoded MemoData.
|
|
47
|
+
*/
|
|
48
|
+
export declare function decodeKeystoneEnvelope(memoDataHex: string): Promise<DecodedKeystoneEnvelope>;
|
|
49
|
+
/**
|
|
50
|
+
* Build a pf.ptr.v4.Pointer memo for sending messages.
|
|
51
|
+
* Returns hex-encoded memo fields ready for PFTL transaction.
|
|
52
|
+
*/
|
|
53
|
+
export declare function buildPfPointerMemo(input: {
|
|
54
|
+
cid: string;
|
|
55
|
+
kind?: string;
|
|
56
|
+
schema?: number;
|
|
57
|
+
threadId?: string;
|
|
58
|
+
contextId?: string;
|
|
59
|
+
flags?: number;
|
|
60
|
+
}): Promise<{
|
|
61
|
+
memoTypeHex: string;
|
|
62
|
+
memoFormatHex: string;
|
|
63
|
+
memoDataHex: string;
|
|
64
|
+
}>;
|
|
65
|
+
//# sourceMappingURL=pointer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pointer.d.ts","sourceRoot":"","sources":["../../src/chain/pointer.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,aAAa;;;;;;CAMhB,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AAEzD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,QAAQ,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,KAAK,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;QAChB,aAAa,EAAE,MAAM,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,MAAM,WAAW,GAAG,gBAAgB,GAAG,uBAAuB,GAAG,IAAI,CAAC;AAmB5E;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,GACpB,QAAQ,CAcV;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,gBAAgB,CAAC,CAiB3B;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,uBAAuB,CAAC,CAwBlC;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC;IACV,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC,CAwBD"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import protobuf from "protobufjs";
|
|
2
|
+
import { resolve, dirname } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
const PROTO_DIR = resolve(__dirname, "..", "grpc", "protos");
|
|
6
|
+
// Memo type/format constants (hex-encoded)
|
|
7
|
+
const PF_PTR_MEMO_TYPE_HEX = "70662e707472"; // "pf.ptr"
|
|
8
|
+
const PF_PTR_MEMO_FORMAT_HEX = "7634"; // "v4"
|
|
9
|
+
const KEYSTONE_MEMO_TYPE_HEX = "6b657973746f6e65"; // "keystone"
|
|
10
|
+
const KEYSTONE_MEMO_FORMAT_HEX = "7631"; // "v1"
|
|
11
|
+
// Pointer flag bitmask (from pftasks)
|
|
12
|
+
export const POINTER_FLAGS = {
|
|
13
|
+
encrypted: 0x01,
|
|
14
|
+
public: 0x02,
|
|
15
|
+
ephemeral: 0x04,
|
|
16
|
+
tombstone: 0x08,
|
|
17
|
+
multipart: 0x10,
|
|
18
|
+
};
|
|
19
|
+
let pfPointerType = null;
|
|
20
|
+
let keystoneEnvelopeType = null;
|
|
21
|
+
async function loadProtos() {
|
|
22
|
+
if (pfPointerType && keystoneEnvelopeType)
|
|
23
|
+
return;
|
|
24
|
+
const pfRoot = await protobuf.load(resolve(PROTO_DIR, "pf/ptr/v4/pointer.proto"));
|
|
25
|
+
pfPointerType = pfRoot.lookupType("pf.ptr.v4.Pointer");
|
|
26
|
+
const ksRoot = await protobuf.load(resolve(PROTO_DIR, "keystone/v1/core/envelope.proto"));
|
|
27
|
+
keystoneEnvelopeType = ksRoot.lookupType("keystone.v1.core.KeystoneEnvelope");
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Identify the memo type from hex-encoded MemoType and MemoFormat fields.
|
|
31
|
+
*/
|
|
32
|
+
export function identifyMemoType(memoTypeHex, memoFormatHex) {
|
|
33
|
+
if (memoTypeHex === PF_PTR_MEMO_TYPE_HEX &&
|
|
34
|
+
memoFormatHex === PF_PTR_MEMO_FORMAT_HEX) {
|
|
35
|
+
return "pf.ptr";
|
|
36
|
+
}
|
|
37
|
+
if (memoTypeHex === KEYSTONE_MEMO_TYPE_HEX &&
|
|
38
|
+
memoFormatHex === KEYSTONE_MEMO_FORMAT_HEX) {
|
|
39
|
+
return "keystone";
|
|
40
|
+
}
|
|
41
|
+
return "unknown";
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Decode a pf.ptr.v4.Pointer from hex-encoded MemoData.
|
|
45
|
+
*/
|
|
46
|
+
export async function decodePfPointer(memoDataHex) {
|
|
47
|
+
await loadProtos();
|
|
48
|
+
const bytes = Buffer.from(memoDataHex, "hex");
|
|
49
|
+
const decoded = pfPointerType.decode(bytes);
|
|
50
|
+
return {
|
|
51
|
+
type: "pf.ptr",
|
|
52
|
+
cid: decoded.cid || "",
|
|
53
|
+
target: decoded.target || "TARGET_UNSPECIFIED",
|
|
54
|
+
kind: decoded.kind || "CONTENT_KIND_UNSPECIFIED",
|
|
55
|
+
schema: decoded.schema || 0,
|
|
56
|
+
taskId: decoded.taskId || "",
|
|
57
|
+
threadId: decoded.threadId || "",
|
|
58
|
+
contextId: decoded.contextId || "",
|
|
59
|
+
flags: decoded.flags || 0,
|
|
60
|
+
isEncrypted: (decoded.flags & POINTER_FLAGS.encrypted) !== 0,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Decode a KeystoneEnvelope from hex-encoded MemoData.
|
|
65
|
+
*/
|
|
66
|
+
export async function decodeKeystoneEnvelope(memoDataHex) {
|
|
67
|
+
await loadProtos();
|
|
68
|
+
const bytes = Buffer.from(memoDataHex, "hex");
|
|
69
|
+
const decoded = keystoneEnvelopeType.decode(bytes);
|
|
70
|
+
return {
|
|
71
|
+
type: "keystone",
|
|
72
|
+
version: decoded.version || 1,
|
|
73
|
+
contentHash: decoded.contentHash
|
|
74
|
+
? Buffer.from(decoded.contentHash)
|
|
75
|
+
: Buffer.alloc(0),
|
|
76
|
+
messageType: decoded.messageType || "MESSAGE_TYPE_UNSPECIFIED",
|
|
77
|
+
encryption: decoded.encryption || "ENCRYPTION_MODE_UNSPECIFIED",
|
|
78
|
+
publicReferences: (decoded.publicReferences || []).map((ref) => ({
|
|
79
|
+
contentHash: ref.contentHash
|
|
80
|
+
? Buffer.from(ref.contentHash)
|
|
81
|
+
: Buffer.alloc(0),
|
|
82
|
+
groupId: ref.groupId || "",
|
|
83
|
+
referenceType: ref.referenceType || "CONTEXT_REFERENCE_TYPE_UNSPECIFIED",
|
|
84
|
+
annotation: ref.annotation || "",
|
|
85
|
+
})),
|
|
86
|
+
message: decoded.message ? Buffer.from(decoded.message) : Buffer.alloc(0),
|
|
87
|
+
metadata: decoded.metadata || {},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Build a pf.ptr.v4.Pointer memo for sending messages.
|
|
92
|
+
* Returns hex-encoded memo fields ready for PFTL transaction.
|
|
93
|
+
*/
|
|
94
|
+
export async function buildPfPointerMemo(input) {
|
|
95
|
+
await loadProtos();
|
|
96
|
+
const payload = {
|
|
97
|
+
cid: input.cid,
|
|
98
|
+
target: "TARGET_CONTENT_BLOB",
|
|
99
|
+
kind: input.kind || "CHAT",
|
|
100
|
+
schema: input.schema || 1,
|
|
101
|
+
threadId: input.threadId || "",
|
|
102
|
+
contextId: input.contextId || "",
|
|
103
|
+
flags: input.flags ?? POINTER_FLAGS.encrypted,
|
|
104
|
+
};
|
|
105
|
+
const err = pfPointerType.verify(payload);
|
|
106
|
+
if (err)
|
|
107
|
+
throw new Error(`Invalid pointer payload: ${err}`);
|
|
108
|
+
const message = pfPointerType.create(payload);
|
|
109
|
+
const bytes = pfPointerType.encode(message).finish();
|
|
110
|
+
return {
|
|
111
|
+
memoTypeHex: PF_PTR_MEMO_TYPE_HEX,
|
|
112
|
+
memoFormatHex: PF_PTR_MEMO_FORMAT_HEX,
|
|
113
|
+
memoDataHex: Buffer.from(bytes).toString("hex"),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=pointer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pointer.js","sourceRoot":"","sources":["../../src/chain/pointer.ts"],"names":[],"mappings":"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"}
|