@msgboard/sdk 0.0.28 → 0.0.30
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 +55 -7
- package/openrpc.json +254 -39
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ npm i --save @msgboard/sdk
|
|
|
10
10
|
|
|
11
11
|
## Quickstart
|
|
12
12
|
|
|
13
|
-
You need an RPC endpoint whose node runs the `msgboard_` module. Public PulseChain/Ethereum RPCs do **not** run it yet; valve.city does (for example `https://one.valve.city/rpc/vk_demo/evm/369`). You can also run your own node with the module.
|
|
13
|
+
You need an RPC endpoint whose node runs the `msgboard_` module. Public PulseChain/Ethereum RPCs do **not** run it yet; [valve.city](https://valve.city) does. Its RPC endpoints are **keyed** — the API key sits in the path (`https://one.valve.city/rpc/<key>/evm/<chainId>`), and `vk_demo` is a public demo key for trying it out (for example `https://one.valve.city/rpc/vk_demo/evm/369`). You can also run your own node with the module.
|
|
14
14
|
|
|
15
15
|
### with viem
|
|
16
16
|
|
|
@@ -72,13 +72,15 @@ board.getDifficulty('0x...') // bigint
|
|
|
72
72
|
|
|
73
73
|
`workMultiplier` and `workDivisor` come from `status()` and are applied automatically by `doPoW`.
|
|
74
74
|
|
|
75
|
+
Because each node validates incoming messages against **its own** current factors, these two numbers are also an implicit board-level setting, not just a per-message cost. Raising the required work — a higher `workMultiplier`, or a lower `workDivisor` — makes the board reject any message whose proof of work falls below the new threshold, so it accumulates **fewer** messages; loosening them admits **more**. Operators tune the same two knobs to trade spam-resistance against message volume. A client must therefore grind against the board's **live** factors (which `doPoW` reads from `status()`): work that was valid under looser settings can be rejected once a board tightens them.
|
|
76
|
+
|
|
75
77
|
## Categories
|
|
76
78
|
|
|
77
79
|
A category is a 32-byte hash. Pass a string and the client hashes it for you (`categoryHash`); pass hex and it is used as-is. The demo board uses the `gasmoneyplease` category.
|
|
78
80
|
|
|
79
81
|
## Ephemerality
|
|
80
82
|
|
|
81
|
-
Messages are short-lived: the board retains roughly the last 120 blocks of messages, so the board is a live signal, not durable storage.
|
|
83
|
+
Messages are short-lived: the board retains roughly the last 120 blocks of messages, so the board is a live signal, not durable storage. The board also has a maximum size cap — if a burst of large messages fills the cap before the 120-block window expires, new submissions may be rejected until older messages age out. Design for loss: treat the board as a delivery channel, not a store.
|
|
82
84
|
|
|
83
85
|
## Keeping work off the UI thread
|
|
84
86
|
|
|
@@ -299,15 +301,61 @@ Object whose values are `RPCMessage[]`.
|
|
|
299
301
|
|
|
300
302
|
## Client methods (not JSON-RPC)
|
|
301
303
|
|
|
302
|
-
These run in the client, not on the node, so they are not
|
|
304
|
+
These run in the client process, not on the node, so they are not part of the OpenRPC spec.
|
|
305
|
+
|
|
306
|
+
### `doPoW(category, data, limit?)`
|
|
307
|
+
|
|
308
|
+
Grinds a valid proof-of-work message. Reads current difficulty from `status()` before starting, so the work is always valid for the live board settings. Returns `{ message, stats }` where `stats` includes `nonce`, `duration`, and the number of iterations. The `limit` parameter sets a maximum number of iterations — useful for streaming progress or cancellation in long-running environments.
|
|
309
|
+
|
|
310
|
+
### `getDifficulty(data)`
|
|
311
|
+
|
|
312
|
+
Returns the difficulty threshold for a given payload hex string as a `bigint`. Helpful for estimating how long `doPoW` will take before committing to it.
|
|
313
|
+
|
|
314
|
+
### `categoryHash(name)`
|
|
315
|
+
|
|
316
|
+
Encodes a plain-text category name to the 32-byte hex hash the board stores. Pass the result directly to `doPoW` or `content()` filters.
|
|
317
|
+
|
|
318
|
+
### `wrapLegacySend(provider)`
|
|
319
|
+
|
|
320
|
+
Wraps an ethers v5 `JsonRpcProvider` (or any provider with a `send` method) into the `Provider` interface the client expects. Use this when you cannot upgrade to viem.
|
|
321
|
+
|
|
322
|
+
### Other utilities
|
|
323
|
+
|
|
324
|
+
`checkWork`, `difficulty`, `encodeData`, `toRLP`, `fromRLP`, `fromRPCMessage`, `toRPCMessage` — lower-level building blocks for custom proof-of-work loops, message encoding, and RPC message conversion. Their signatures are in the TypeScript types shipped with the package.
|
|
303
325
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
-
|
|
326
|
+
## Building automations
|
|
327
|
+
|
|
328
|
+
For server-side work — polling continuously, reacting to specific categories, archiving messages, triggering cross-chain actions — install the companion relayer package:
|
|
329
|
+
|
|
330
|
+
```sh
|
|
331
|
+
npm i @msgboard/relayer
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
```ts
|
|
335
|
+
import { http } from 'viem'
|
|
336
|
+
import { Relayer, msgboardContentSource, noopAction } from '@msgboard/relayer'
|
|
337
|
+
import type { RPCMessage } from '@msgboard/sdk'
|
|
338
|
+
|
|
339
|
+
const relayer = new Relayer<RPCMessage>({
|
|
340
|
+
node: { transport: http('https://one.valve.city/rpc/vk_demo/evm/369') },
|
|
341
|
+
// chain is auto-detected via eth_chainId — pass node.chain to override
|
|
342
|
+
source: msgboardContentSource({ category: 'myapp' }),
|
|
343
|
+
key: (msg) => msg.hash,
|
|
344
|
+
action: noopAction(),
|
|
345
|
+
// mode defaults to 'observe' — swap in webhookAction or submitMessageAction and set
|
|
346
|
+
// mode: 'live' to execute real effects
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
relayer.start()
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
`Relayer` polls on a configurable heartbeat, deduplicates via a pluggable store (in-memory, Postgres), records everything to an optional archive sink regardless of mode, and gates `action.execute` on `mode: 'live'`. See the [`@msgboard/relayer`](https://www.npmjs.com/package/@msgboard/relayer) package for the full API, built-in sources/actions, and runnable examples.
|
|
307
353
|
|
|
308
354
|
## Machine-readable spec
|
|
309
355
|
|
|
310
|
-
The JSON-RPC surface is published as an OpenRPC document
|
|
356
|
+
The JSON-RPC surface is published as an OpenRPC document — [`openrpc.json`](https://github.com/valve-tech/msgboard/blob/master/packages/sdk/openrpc.json) — in this package, and hosted at [`msgboard.xyz/openrpc.json`](https://msgboard.xyz/openrpc.json). Open it in the [OpenRPC Playground](https://playground.open-rpc.org/?schemaUrl=https%3A%2F%2Fmsgboard.xyz%2Fopenrpc.json) (it loads the schema and pre-selects the valve.city PulseChain mainnet endpoint so you can call live methods directly), or point a code generator at the hosted spec.
|
|
357
|
+
|
|
358
|
+
All published packages are under the [`@msgboard`](https://www.npmjs.com/search?q=%40msgboard) scope on npm.
|
|
311
359
|
|
|
312
360
|
## License
|
|
313
361
|
|
package/openrpc.json
CHANGED
|
@@ -4,94 +4,309 @@
|
|
|
4
4
|
"title": "MsgBoard JSON-RPC API",
|
|
5
5
|
"version": "0.0.28",
|
|
6
6
|
"description": "JSON-RPC methods exposed by the msgboard_ module. Any node running the module serves these methods. Proof of work, not a fee, gates message submission.",
|
|
7
|
-
"license": {
|
|
7
|
+
"license": {
|
|
8
|
+
"name": "MIT"
|
|
9
|
+
}
|
|
8
10
|
},
|
|
11
|
+
"servers": [
|
|
12
|
+
{
|
|
13
|
+
"name": "PulseChain mainnet (valve.city demo)",
|
|
14
|
+
"url": "https://one.valve.city/rpc/vk_demo/evm/369"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
9
17
|
"methods": [
|
|
10
18
|
{
|
|
11
19
|
"name": "msgboard_status",
|
|
12
20
|
"summary": "Board status and the difficulty factors required for valid messages.",
|
|
13
21
|
"params": [],
|
|
14
|
-
"result": {
|
|
22
|
+
"result": {
|
|
23
|
+
"name": "status",
|
|
24
|
+
"schema": {
|
|
25
|
+
"$ref": "#/components/schemas/Status"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
15
28
|
"examples": [
|
|
16
|
-
{
|
|
29
|
+
{
|
|
30
|
+
"name": "default",
|
|
31
|
+
"params": [],
|
|
32
|
+
"result": {
|
|
33
|
+
"name": "status",
|
|
34
|
+
"value": {
|
|
35
|
+
"enabled": true,
|
|
36
|
+
"count": "0x0",
|
|
37
|
+
"size": "0x0",
|
|
38
|
+
"workMultiplier": "0x2710",
|
|
39
|
+
"workDivisor": "0xf4240"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
17
43
|
]
|
|
18
44
|
},
|
|
19
45
|
{
|
|
20
46
|
"name": "msgboard_categories",
|
|
21
47
|
"summary": "The list of 32-byte category hashes currently present on the board.",
|
|
22
48
|
"params": [],
|
|
23
|
-
"result": {
|
|
49
|
+
"result": {
|
|
50
|
+
"name": "categories",
|
|
51
|
+
"schema": {
|
|
52
|
+
"$ref": "#/components/schemas/Categories"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
24
55
|
"examples": [
|
|
25
|
-
{
|
|
56
|
+
{
|
|
57
|
+
"name": "gasmoneyplease",
|
|
58
|
+
"params": [],
|
|
59
|
+
"result": {
|
|
60
|
+
"name": "categories",
|
|
61
|
+
"value": [
|
|
62
|
+
"0x6761736d6f6e6579706c65617365000000000000000000000000000000000000"
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
}
|
|
26
66
|
]
|
|
27
67
|
},
|
|
28
68
|
{
|
|
29
69
|
"name": "msgboard_content",
|
|
30
70
|
"summary": "All messages on the board, grouped by category hash. Optionally filtered.",
|
|
31
|
-
"params": [
|
|
32
|
-
|
|
71
|
+
"params": [
|
|
72
|
+
{
|
|
73
|
+
"name": "filter",
|
|
74
|
+
"required": false,
|
|
75
|
+
"schema": {
|
|
76
|
+
"$ref": "#/components/schemas/ContentFilter"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
"result": {
|
|
81
|
+
"name": "content",
|
|
82
|
+
"schema": {
|
|
83
|
+
"$ref": "#/components/schemas/Content"
|
|
84
|
+
}
|
|
85
|
+
},
|
|
33
86
|
"examples": [
|
|
34
|
-
{
|
|
87
|
+
{
|
|
88
|
+
"name": "empty",
|
|
89
|
+
"params": [
|
|
90
|
+
{
|
|
91
|
+
"name": "filter",
|
|
92
|
+
"value": {}
|
|
93
|
+
}
|
|
94
|
+
],
|
|
95
|
+
"result": {
|
|
96
|
+
"name": "content",
|
|
97
|
+
"value": {}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
35
100
|
]
|
|
36
101
|
},
|
|
37
102
|
{
|
|
38
103
|
"name": "msgboard_addMessage",
|
|
39
104
|
"summary": "Submit a proof-of-work message (RLP-encoded) to the board.",
|
|
40
|
-
"params": [
|
|
41
|
-
|
|
105
|
+
"params": [
|
|
106
|
+
{
|
|
107
|
+
"name": "rlp",
|
|
108
|
+
"required": true,
|
|
109
|
+
"schema": {
|
|
110
|
+
"$ref": "#/components/schemas/Hex"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
],
|
|
114
|
+
"result": {
|
|
115
|
+
"name": "hash",
|
|
116
|
+
"schema": {
|
|
117
|
+
"$ref": "#/components/schemas/Hex"
|
|
118
|
+
}
|
|
119
|
+
},
|
|
42
120
|
"examples": [
|
|
43
|
-
{
|
|
121
|
+
{
|
|
122
|
+
"name": "submit",
|
|
123
|
+
"params": [
|
|
124
|
+
{
|
|
125
|
+
"name": "rlp",
|
|
126
|
+
"value": "0xf800"
|
|
127
|
+
}
|
|
128
|
+
],
|
|
129
|
+
"result": {
|
|
130
|
+
"name": "hash",
|
|
131
|
+
"value": "0x0d1e2f00000000000000000000000000000000000000000000000000c46845f9"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
44
134
|
]
|
|
45
135
|
},
|
|
46
136
|
{
|
|
47
137
|
"name": "msgboard_getMessage",
|
|
48
138
|
"summary": "Fetch a single message by its hash.",
|
|
49
|
-
"params": [
|
|
50
|
-
|
|
139
|
+
"params": [
|
|
140
|
+
{
|
|
141
|
+
"name": "hash",
|
|
142
|
+
"required": true,
|
|
143
|
+
"schema": {
|
|
144
|
+
"$ref": "#/components/schemas/Hex"
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
],
|
|
148
|
+
"result": {
|
|
149
|
+
"name": "message",
|
|
150
|
+
"schema": {
|
|
151
|
+
"$ref": "#/components/schemas/RPCMessage"
|
|
152
|
+
}
|
|
153
|
+
},
|
|
51
154
|
"examples": [
|
|
52
|
-
{
|
|
155
|
+
{
|
|
156
|
+
"name": "zero",
|
|
157
|
+
"params": [
|
|
158
|
+
{
|
|
159
|
+
"name": "hash",
|
|
160
|
+
"value": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
|
161
|
+
}
|
|
162
|
+
],
|
|
163
|
+
"result": {
|
|
164
|
+
"name": "message",
|
|
165
|
+
"value": {
|
|
166
|
+
"version": "0x1",
|
|
167
|
+
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
168
|
+
"blockNumber": "0x0",
|
|
169
|
+
"category": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
170
|
+
"data": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
171
|
+
"nonce": "0x0",
|
|
172
|
+
"hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
173
|
+
"workMultiplier": "0x0",
|
|
174
|
+
"workDivisor": "0x0"
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
53
178
|
]
|
|
54
179
|
}
|
|
55
180
|
],
|
|
56
181
|
"components": {
|
|
57
182
|
"schemas": {
|
|
58
|
-
"Hex": {
|
|
183
|
+
"Hex": {
|
|
184
|
+
"title": "Hex",
|
|
185
|
+
"type": "string",
|
|
186
|
+
"pattern": "^0x[0-9a-fA-F]*$"
|
|
187
|
+
},
|
|
59
188
|
"Status": {
|
|
60
|
-
"title": "Status",
|
|
61
|
-
"
|
|
189
|
+
"title": "Status",
|
|
190
|
+
"type": "object",
|
|
191
|
+
"required": [
|
|
192
|
+
"enabled",
|
|
193
|
+
"count",
|
|
194
|
+
"size",
|
|
195
|
+
"workMultiplier",
|
|
196
|
+
"workDivisor"
|
|
197
|
+
],
|
|
62
198
|
"properties": {
|
|
63
|
-
"enabled": {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
"
|
|
199
|
+
"enabled": {
|
|
200
|
+
"type": "boolean",
|
|
201
|
+
"description": "Whether the module is enabled on this node."
|
|
202
|
+
},
|
|
203
|
+
"count": {
|
|
204
|
+
"$ref": "#/components/schemas/Hex",
|
|
205
|
+
"description": "Overall count of messages stored on the board."
|
|
206
|
+
},
|
|
207
|
+
"size": {
|
|
208
|
+
"$ref": "#/components/schemas/Hex",
|
|
209
|
+
"description": "Overall size of messages stored on the board."
|
|
210
|
+
},
|
|
211
|
+
"workMultiplier": {
|
|
212
|
+
"$ref": "#/components/schemas/Hex",
|
|
213
|
+
"description": "Factor that increases required work."
|
|
214
|
+
},
|
|
215
|
+
"workDivisor": {
|
|
216
|
+
"$ref": "#/components/schemas/Hex",
|
|
217
|
+
"description": "Factor that decreases required work."
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
"Categories": {
|
|
222
|
+
"title": "Categories",
|
|
223
|
+
"type": "array",
|
|
224
|
+
"items": {
|
|
225
|
+
"$ref": "#/components/schemas/Hex"
|
|
68
226
|
}
|
|
69
227
|
},
|
|
70
|
-
"Categories": { "title": "Categories", "type": "array", "items": { "$ref": "#/components/schemas/Hex" } },
|
|
71
228
|
"ContentFilter": {
|
|
72
|
-
"title": "ContentFilter",
|
|
229
|
+
"title": "ContentFilter",
|
|
230
|
+
"type": "object",
|
|
73
231
|
"properties": {
|
|
74
|
-
"category": {
|
|
75
|
-
|
|
76
|
-
|
|
232
|
+
"category": {
|
|
233
|
+
"$ref": "#/components/schemas/Hex",
|
|
234
|
+
"description": "Restrict to one category hash."
|
|
235
|
+
},
|
|
236
|
+
"fromBlock": {
|
|
237
|
+
"$ref": "#/components/schemas/Hex",
|
|
238
|
+
"description": "Lower block bound (hex quantity)."
|
|
239
|
+
},
|
|
240
|
+
"toBlock": {
|
|
241
|
+
"$ref": "#/components/schemas/Hex",
|
|
242
|
+
"description": "Upper block bound (hex quantity)."
|
|
243
|
+
}
|
|
77
244
|
}
|
|
78
245
|
},
|
|
79
246
|
"RPCMessage": {
|
|
80
|
-
"title": "RPCMessage",
|
|
81
|
-
"
|
|
247
|
+
"title": "RPCMessage",
|
|
248
|
+
"type": "object",
|
|
249
|
+
"required": [
|
|
250
|
+
"version",
|
|
251
|
+
"blockHash",
|
|
252
|
+
"blockNumber",
|
|
253
|
+
"category",
|
|
254
|
+
"data",
|
|
255
|
+
"nonce",
|
|
256
|
+
"hash",
|
|
257
|
+
"workMultiplier",
|
|
258
|
+
"workDivisor"
|
|
259
|
+
],
|
|
82
260
|
"properties": {
|
|
83
|
-
"version": {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
"
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
"
|
|
261
|
+
"version": {
|
|
262
|
+
"$ref": "#/components/schemas/Hex",
|
|
263
|
+
"description": "Message/encoding version."
|
|
264
|
+
},
|
|
265
|
+
"blockHash": {
|
|
266
|
+
"$ref": "#/components/schemas/Hex",
|
|
267
|
+
"description": "Hash of the block the message is rooted to."
|
|
268
|
+
},
|
|
269
|
+
"blockNumber": {
|
|
270
|
+
"$ref": "#/components/schemas/Hex",
|
|
271
|
+
"description": "Number of the block the message is rooted to."
|
|
272
|
+
},
|
|
273
|
+
"category": {
|
|
274
|
+
"$ref": "#/components/schemas/Hex",
|
|
275
|
+
"description": "32-byte category hash."
|
|
276
|
+
},
|
|
277
|
+
"data": {
|
|
278
|
+
"$ref": "#/components/schemas/Hex",
|
|
279
|
+
"description": "Arbitrary message data."
|
|
280
|
+
},
|
|
281
|
+
"nonce": {
|
|
282
|
+
"$ref": "#/components/schemas/Hex",
|
|
283
|
+
"description": "Nonce discovered through proof of work."
|
|
284
|
+
},
|
|
285
|
+
"hash": {
|
|
286
|
+
"$ref": "#/components/schemas/Hex",
|
|
287
|
+
"description": "The message hash."
|
|
288
|
+
},
|
|
289
|
+
"workMultiplier": {
|
|
290
|
+
"$ref": "#/components/schemas/Hex",
|
|
291
|
+
"description": "Work multiplier in force when posted."
|
|
292
|
+
},
|
|
293
|
+
"workDivisor": {
|
|
294
|
+
"$ref": "#/components/schemas/Hex",
|
|
295
|
+
"description": "Work divisor in force when posted."
|
|
296
|
+
}
|
|
92
297
|
}
|
|
93
298
|
},
|
|
94
|
-
"Content": {
|
|
299
|
+
"Content": {
|
|
300
|
+
"title": "Content",
|
|
301
|
+
"type": "object",
|
|
302
|
+
"description": "Messages grouped by category hash.",
|
|
303
|
+
"additionalProperties": {
|
|
304
|
+
"type": "array",
|
|
305
|
+
"items": {
|
|
306
|
+
"$ref": "#/components/schemas/RPCMessage"
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
95
310
|
}
|
|
96
311
|
}
|
|
97
312
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@msgboard/sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.30",
|
|
4
4
|
"description": "MsgBoard client SDK for the msgboard_ JSON-RPC module",
|
|
5
5
|
"repository": "github:valve-tech/msgboard",
|
|
6
6
|
"author": "MsgBoard",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"vitest": "^3.1.1"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@msgboard/core": "^0.0.
|
|
62
|
+
"@msgboard/core": "^0.0.30",
|
|
63
63
|
"bn.js": "^5.2.1",
|
|
64
64
|
"debug": "^4.4.0",
|
|
65
65
|
"elliptic": "^6.6.1",
|