@msgboard/hardhat 0.0.1

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 ADDED
@@ -0,0 +1,77 @@
1
+ # @pulsechain/hardhat-msgboard
2
+
3
+ Test your msgboard interfaces
4
+
5
+ [Hardhat](https://hardhat.org) plugin example.
6
+
7
+ ## MsgBoard
8
+
9
+ MsgBoard allows for decentralized, public communication. Each msgboard instance may behave slightly differently quirks, but so long as the basic interface holds true, it can reliably push the ability to run software closer to the individual. One needs to tightly control which process is utilizing msgboard because it will block as it works through and eventually finds a valid hash.
10
+
11
+ ## Installation
12
+
13
+ After installing nodejs (+23.6.1 with asdf recommended)
14
+
15
+ ```bash
16
+ npm install @pulsechain/hardhat-msgboard
17
+ ```
18
+
19
+ Import the plugin in your `hardhat.config.js`:
20
+
21
+ ```js
22
+ require("@pulsechain/hardhat-msgboard");
23
+ ```
24
+
25
+ Or if you are using TypeScript, in your `hardhat.config.ts`:
26
+
27
+ ```ts
28
+ import "@pulsechain/hardhat-msgboard";
29
+ ```
30
+
31
+ ## Tasks
32
+
33
+ `msgboard:status`
34
+ `msgboard:work`
35
+ `msgboard:send`
36
+ `msgboard:work:send`
37
+
38
+ ```sh
39
+ > hardhat msgboard:status
40
+ ```
41
+
42
+ ## Environment extensions
43
+
44
+ <_A description of each extension to the Hardhat Runtime Environment_>
45
+
46
+ This plugin extends the Hardhat Runtime Environment by adding an `example` field
47
+ whose type is `ExampleHardhatRuntimeEnvironmentField`.
48
+
49
+ ## Configuration
50
+
51
+ <_A description of each extension to the HardhatConfig or to its fields_>
52
+
53
+ This plugin extends the `HardhatUserConfig`'s `MsgBoardUserConfig` object with an optional `msgboard` field that is only applicable to the hardhat network.
54
+
55
+ This is an example of how to set it:
56
+
57
+ ```ts
58
+ module.exports = {
59
+ msgboard: {
60
+ // only applicable to hardhat network
61
+ enabled: true,
62
+ workMultiplier: 1_000_000n,
63
+ workDivisor: 10_000n,
64
+ messageSizeLimit: 8n * 1024n,
65
+ boardCountLimit: 10_000n,
66
+ blockRangeLimit: 120n,
67
+ },
68
+ };
69
+ ```
70
+
71
+ ## Usage
72
+
73
+ send messages to the msgboard and read from it
74
+
75
+ ```ts
76
+ const msg = await hre.msgboard.doWork('0xcategory...', '0xdata...')
77
+ ```
@@ -0,0 +1,2 @@
1
+ import './type-extensions';
2
+ import './tasks';
package/dist/index.js ADDED
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const config_1 = require("hardhat/config");
37
+ const plugins_1 = require("hardhat/plugins");
38
+ const msgboard = __importStar(require("@msgboard/sdk"));
39
+ const provider_1 = require("./provider");
40
+ // This import is needed to let the TypeScript compiler know that it should include your type
41
+ // extensions in your npm package's types file.
42
+ require("./type-extensions");
43
+ require("./tasks");
44
+ (0, config_1.extendConfig)((config, userConfig) => {
45
+ const userConf = userConfig;
46
+ const c = config;
47
+ c.msgboard = {
48
+ ...provider_1.globalDefaultSettings,
49
+ ...(userConf.msgboard ?? {}),
50
+ };
51
+ });
52
+ (0, config_1.extendEnvironment)((hre) => {
53
+ // We add a field to the Hardhat Runtime Environment here.
54
+ // We use lazyObject to avoid initializing things until they are actually
55
+ // needed.
56
+ hre.msgboard = (0, plugins_1.lazyObject)(() => {
57
+ return new msgboard.MsgBoardClient(msgboard.wrapLegacySend(hre.network.provider));
58
+ });
59
+ });
60
+ (0, config_1.extendProvider)((provider, config, network) => {
61
+ const isHardhatNetwork = network === 'hardhat';
62
+ const c = config;
63
+ const prov = new provider_1.MsgBoardProvider(provider, isHardhatNetwork);
64
+ prov.setNodeConstraints(c);
65
+ provider_1.providers.add(prov);
66
+ return prov;
67
+ });
@@ -0,0 +1,113 @@
1
+ import { ProviderWrapper } from 'hardhat/plugins';
2
+ import type { EIP1193Provider, RequestArguments } from 'hardhat/types';
3
+ import type { MsgBoardSettings } from './types';
4
+ export declare const globalDefaultSettings: MsgBoardSettings;
5
+ export declare const defaultSettings: () => {
6
+ enabled: boolean;
7
+ workMultiplier: bigint;
8
+ workDivisor: bigint;
9
+ messageSizeLimit: bigint;
10
+ boardCountLimit: bigint;
11
+ blockRangeLimit: bigint;
12
+ };
13
+ export declare class MsgBoardProvider extends ProviderWrapper {
14
+ protected readonly _wrappedProvider: EIP1193Provider;
15
+ protected readonly isHardhatNetwork: boolean;
16
+ private messages;
17
+ id: number;
18
+ private settings;
19
+ constructor(_wrappedProvider: EIP1193Provider, isHardhatNetwork: boolean);
20
+ /**
21
+ * Returns the default settings for a msgboard instance
22
+ * @returns The default settings for a msgboard instance
23
+ */
24
+ static defaultSettings(): {
25
+ enabled: boolean;
26
+ workMultiplier: bigint;
27
+ workDivisor: bigint;
28
+ messageSizeLimit: bigint;
29
+ boardCountLimit: bigint;
30
+ blockRangeLimit: bigint;
31
+ };
32
+ /**
33
+ * Returns the status of the msgboard
34
+ * @returns The status object of this message board instance
35
+ */
36
+ private status;
37
+ /**
38
+ * Sets the constraints for this message board instance
39
+ * @param constraints The constraints to set for this message board instance
40
+ */
41
+ setNodeConstraints(constraints?: Partial<MsgBoardSettings>): Promise<void>;
42
+ /**
43
+ * Validates a message
44
+ * @param m The message to validate
45
+ * @returns The validated message and the produced hash
46
+ */
47
+ private validateMessage;
48
+ /**
49
+ * Adds a message to the msgboard
50
+ * @param msg The rlp encoded message to add
51
+ * @returns The hash of the message
52
+ */
53
+ private addMessage;
54
+ /**
55
+ * Adds a message to the msgboard in order
56
+ * @param latestBlockNumber The latest block number
57
+ * - to be passed to the removeStaleMessages method to prune old messages
58
+ * @param m The message to add
59
+ */
60
+ private addOrderedMessage;
61
+ /**
62
+ * Removes stale messages from the msgboard
63
+ * @param latestBlockNumber The latest block number - used to prune old messages
64
+ */
65
+ private removeStaleMessages;
66
+ /**
67
+ * Returns a message from the msgboard by hash
68
+ * @param hash The hash of the message to return
69
+ * @returns The message or null if it is not found
70
+ */
71
+ private getMessage;
72
+ /**
73
+ * Returns the content of the msgboard
74
+ * @param filter The filter to apply to the content
75
+ * @returns The content of the msgboard
76
+ */
77
+ private content;
78
+ /**
79
+ * Returns the categories of the msgboard
80
+ * @returns The categories of the msgboard
81
+ */
82
+ private categories;
83
+ /**
84
+ * Handles a msgboard request
85
+ * @param args The request arguments
86
+ * @returns The result of the request
87
+ */
88
+ private handleMsgboardRequest;
89
+ /**
90
+ * Emulates network latency by adding a XXms delay before and after the function call
91
+ * @param fn The function to execute
92
+ * @returns The result of the function
93
+ */
94
+ private emulateNetworkLatency;
95
+ /**
96
+ * Handles a request to the msgboard, handling it locally if the network is hardhat
97
+ * @param args The request arguments
98
+ * @returns The result of the request
99
+ */
100
+ request(args: RequestArguments): Promise<unknown>;
101
+ /**
102
+ * Resets the msgboard
103
+ */
104
+ reset(): void;
105
+ destroy(): void;
106
+ }
107
+ export declare const providers: Set<MsgBoardProvider>;
108
+ /**
109
+ * Sets the constraints for all created msgboard providers
110
+ * @param constraints The settings to apply to all msgboard providers
111
+ */
112
+ export declare const setNodeConstraints: (constraints: Partial<MsgBoardSettings>) => void;
113
+ export declare const reset: () => void;
@@ -0,0 +1,337 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.reset = exports.setNodeConstraints = exports.providers = exports.MsgBoardProvider = exports.defaultSettings = exports.globalDefaultSettings = void 0;
37
+ const plugins_1 = require("hardhat/plugins");
38
+ const viem_1 = require("viem");
39
+ const msgboard = __importStar(require("@msgboard/sdk"));
40
+ exports.globalDefaultSettings = {
41
+ enabled: true,
42
+ workMultiplier: 10000n,
43
+ workDivisor: 1000000n,
44
+ messageSizeLimit: 1024n * 8n,
45
+ boardCountLimit: 10000n,
46
+ blockRangeLimit: 120n,
47
+ };
48
+ const defaultSettings = () => ({
49
+ ...exports.globalDefaultSettings,
50
+ });
51
+ exports.defaultSettings = defaultSettings;
52
+ /**
53
+ * Waits for a given number of milliseconds
54
+ * @param ms The number of milliseconds to wait
55
+ */
56
+ const wait = async (ms) => {
57
+ await new Promise((resolve) => setTimeout(resolve, ms));
58
+ };
59
+ const isNil = (value) => value === null || value === undefined;
60
+ let id = 0;
61
+ class MsgBoardProvider extends plugins_1.ProviderWrapper {
62
+ constructor(_wrappedProvider, isHardhatNetwork) {
63
+ super(_wrappedProvider);
64
+ this._wrappedProvider = _wrappedProvider;
65
+ this.isHardhatNetwork = isHardhatNetwork;
66
+ this.messages = [];
67
+ this.settings = {
68
+ ...exports.globalDefaultSettings,
69
+ };
70
+ this.id = ++id;
71
+ }
72
+ /**
73
+ * Returns the default settings for a msgboard instance
74
+ * @returns The default settings for a msgboard instance
75
+ */
76
+ static defaultSettings() {
77
+ return {
78
+ ...exports.globalDefaultSettings,
79
+ };
80
+ }
81
+ /**
82
+ * Returns the status of the msgboard
83
+ * @returns The status object of this message board instance
84
+ */
85
+ status() {
86
+ return {
87
+ enabled: this.settings.enabled,
88
+ workMultiplier: (0, viem_1.numberToHex)(this.settings.workMultiplier),
89
+ workDivisor: (0, viem_1.numberToHex)(this.settings.workDivisor),
90
+ count: (0, viem_1.numberToHex)(this.messages.length),
91
+ size: (0, viem_1.numberToHex)(this.messages.reduce((acc, msg) => acc + msg.data.length, 0)),
92
+ };
93
+ }
94
+ /**
95
+ * Sets the constraints for this message board instance
96
+ * @param constraints The constraints to set for this message board instance
97
+ */
98
+ async setNodeConstraints(constraints = {}) {
99
+ this.settings = { ...this.settings, ...constraints };
100
+ }
101
+ /**
102
+ * Validates a message
103
+ * @param m The message to validate
104
+ * @returns The validated message and the produced hash
105
+ */
106
+ async validateMessage(m) {
107
+ // these errors may be out of order compared to the current node implementation
108
+ const bytes = (0, viem_1.hexToBytes)(m.data);
109
+ if (bytes.length > this.settings.messageSizeLimit) {
110
+ throw new Error('msgboard: message too large');
111
+ }
112
+ if (m.version !== 1) {
113
+ throw new Error('powmsg: invalid version');
114
+ }
115
+ if ((0, viem_1.hexToBytes)(m.category).length !== 32) {
116
+ throw new Error('powmsg: invalid category');
117
+ }
118
+ if (m.blockHash === viem_1.zeroHash) {
119
+ throw new Error('powmsg: invalid block hash');
120
+ }
121
+ if (m.nonce === 0n) {
122
+ throw new Error('powmsg: invalid nonce');
123
+ }
124
+ if (m.workDivisor === 0n || m.workMultiplier === 0n) {
125
+ throw new Error('powmsg: invalid difficulty');
126
+ }
127
+ const difficultyFactors = {
128
+ workMultiplier: this.settings.workMultiplier,
129
+ workDivisor: this.settings.workDivisor,
130
+ };
131
+ if (m.category === viem_1.zeroHash && m.data.length !== 0) {
132
+ throw new Error('powmsg: invalid data');
133
+ }
134
+ const difficulty = msgboard.difficulty(difficultyFactors, bytes.length);
135
+ const hash = msgboard.checkWork(m, difficulty);
136
+ if (!hash) {
137
+ if (difficultyFactors.workMultiplier !== this.settings.workMultiplier ||
138
+ difficultyFactors.workDivisor !== this.settings.workDivisor) {
139
+ console.log(this.id, difficultyFactors, m);
140
+ }
141
+ throw new Error('powmsg: invalid work');
142
+ }
143
+ const block = (await this._wrappedProvider.request({
144
+ method: 'eth_getBlockByHash',
145
+ params: [m.blockHash, false],
146
+ }));
147
+ if (!block) {
148
+ throw new Error('powmsg: invalid block hash');
149
+ }
150
+ return { hash, block };
151
+ }
152
+ /**
153
+ * Adds a message to the msgboard
154
+ * @param msg The rlp encoded message to add
155
+ * @returns The hash of the message
156
+ */
157
+ async addMessage(msg) {
158
+ const m = msgboard.fromRLP(msg);
159
+ const { hash, block: workBlock } = await this.validateMessage(m);
160
+ const latestBlock = (await this._wrappedProvider.request({
161
+ method: 'eth_getBlockByNumber',
162
+ params: ['latest', false],
163
+ }));
164
+ const latestBlockNumber = BigInt(latestBlock.number);
165
+ const workBlockNumber = BigInt(workBlock.number);
166
+ if (
167
+ // could swap out for bignumber.js to avoid precision issues
168
+ Number(m.workMultiplier) / Number(m.workDivisor) >
169
+ Number(this.settings.workMultiplier) / Number(this.settings.workDivisor)) {
170
+ throw new Error('msgboard: message work too easy');
171
+ }
172
+ if (workBlockNumber < latestBlockNumber - this.settings.blockRangeLimit) {
173
+ throw new Error('msgboard: message block too old');
174
+ }
175
+ this.addOrderedMessage(latestBlockNumber, {
176
+ ...m,
177
+ blockNumber: workBlockNumber,
178
+ hash,
179
+ });
180
+ return hash;
181
+ }
182
+ /**
183
+ * Adds a message to the msgboard in order
184
+ * @param latestBlockNumber The latest block number
185
+ * - to be passed to the removeStaleMessages method to prune old messages
186
+ * @param m The message to add
187
+ */
188
+ addOrderedMessage(latestBlockNumber, m) {
189
+ const index = this.messages.findIndex((msg) => {
190
+ return msg.blockNumber > m.blockNumber;
191
+ });
192
+ if (index === -1) {
193
+ this.messages.push(m);
194
+ }
195
+ else {
196
+ this.messages.splice(index, 0, m);
197
+ }
198
+ this.removeStaleMessages(latestBlockNumber);
199
+ }
200
+ /**
201
+ * Removes stale messages from the msgboard
202
+ * @param latestBlockNumber The latest block number - used to prune old messages
203
+ */
204
+ removeStaleMessages(latestBlockNumber) {
205
+ // remove messages that are too old
206
+ const finalValidBlockNumber = latestBlockNumber - this.settings.blockRangeLimit;
207
+ const index = this.messages.findIndex((msg) => {
208
+ return msg.blockNumber >= finalValidBlockNumber;
209
+ });
210
+ if (index !== -1) {
211
+ this.messages.splice(0, index);
212
+ }
213
+ // remove messages when the board count limit is exceeded
214
+ if (BigInt(this.messages.length) > this.settings.boardCountLimit) {
215
+ this.messages.splice(0, this.messages.length - Number(this.settings.boardCountLimit));
216
+ }
217
+ }
218
+ /**
219
+ * Returns a message from the msgboard by hash
220
+ * @param hash The hash of the message to return
221
+ * @returns The message or null if it is not found
222
+ */
223
+ getMessage(hash) {
224
+ const msg = this.messages.find((msg) => msg.hash === hash) ?? null;
225
+ return !msg ? null : msgboard.toRPCMessage(msg);
226
+ }
227
+ /**
228
+ * Returns the content of the msgboard
229
+ * @param filter The filter to apply to the content
230
+ * @returns The content of the msgboard
231
+ */
232
+ content(filter = {}) {
233
+ return this.messages.reduce((content, msg) => {
234
+ if (filter.category && msg.category !== filter.category) {
235
+ // message category does not match filter category
236
+ return content;
237
+ }
238
+ if (!isNil(filter.fromBlock) && msg.blockNumber < filter.fromBlock) {
239
+ // message block number is below lower bound block number
240
+ return content;
241
+ }
242
+ if (!isNil(filter.toBlock) && msg.blockNumber > filter.toBlock) {
243
+ // message block number is above upper bound block number
244
+ return content;
245
+ }
246
+ if (!content[msg.category]) {
247
+ content[msg.category] = [];
248
+ }
249
+ content[msg.category].push(msgboard.toRPCMessage(msg));
250
+ return content;
251
+ }, {});
252
+ }
253
+ /**
254
+ * Returns the categories of the msgboard
255
+ * @returns The categories of the msgboard
256
+ */
257
+ categories() {
258
+ return Array.from(new Set(this.messages.map((msg) => msg.category)));
259
+ }
260
+ /**
261
+ * Handles a msgboard request
262
+ * @param args The request arguments
263
+ * @returns The result of the request
264
+ */
265
+ handleMsgboardRequest(args) {
266
+ if (args.method === 'msgboard_status') {
267
+ return this.status(...args.params);
268
+ }
269
+ if (args.method === 'msgboard_addMessage') {
270
+ return this.addMessage(...args.params);
271
+ }
272
+ if (args.method === 'msgboard_getMessage') {
273
+ return this.getMessage(...args.params);
274
+ }
275
+ if (args.method === 'msgboard_content') {
276
+ return this.content(...args.params);
277
+ }
278
+ if (args.method === 'msgboard_categories') {
279
+ return this.categories(...args.params);
280
+ }
281
+ // only for local hardhat network - does not exist on production networks
282
+ if (args.method === 'msgboard_reset') {
283
+ return this.reset(...args.params);
284
+ }
285
+ throw new Error('msgboard: unknown method');
286
+ }
287
+ /**
288
+ * Emulates network latency by adding a XXms delay before and after the function call
289
+ * @param fn The function to execute
290
+ * @returns The result of the function
291
+ */
292
+ async emulateNetworkLatency(fn) {
293
+ await wait(2);
294
+ const result = await fn();
295
+ await wait(2);
296
+ return result;
297
+ }
298
+ /**
299
+ * Handles a request to the msgboard, handling it locally if the network is hardhat
300
+ * @param args The request arguments
301
+ * @returns The result of the request
302
+ */
303
+ async request(args) {
304
+ if (this.isHardhatNetwork && args.method.startsWith('msgboard_')) {
305
+ return this.emulateNetworkLatency(() => this.handleMsgboardRequest(args));
306
+ }
307
+ return this._wrappedProvider.request(args);
308
+ }
309
+ /**
310
+ * Resets the msgboard
311
+ */
312
+ reset() {
313
+ this.messages = [];
314
+ }
315
+ destroy() {
316
+ this.reset();
317
+ exports.providers.delete(this);
318
+ }
319
+ }
320
+ exports.MsgBoardProvider = MsgBoardProvider;
321
+ exports.providers = new Set();
322
+ /**
323
+ * Sets the constraints for all created msgboard providers
324
+ * @param constraints The settings to apply to all msgboard providers
325
+ */
326
+ const setNodeConstraints = (constraints) => {
327
+ Array.from(exports.providers).forEach((p) => {
328
+ p.setNodeConstraints(constraints);
329
+ });
330
+ };
331
+ exports.setNodeConstraints = setNodeConstraints;
332
+ const reset = () => {
333
+ Array.from(exports.providers).forEach((p) => {
334
+ p.reset();
335
+ });
336
+ };
337
+ exports.reset = reset;
@@ -0,0 +1 @@
1
+ export {};
package/dist/tasks.js ADDED
@@ -0,0 +1,298 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const config_1 = require("hardhat/config");
37
+ const viem_1 = require("viem");
38
+ const msgboard = __importStar(require("@msgboard/sdk"));
39
+ const argumentTypes = __importStar(require("hardhat/internal/core/params/argumentTypes"));
40
+ const options = {
41
+ // params
42
+ category: ['category', 'the category of the work', '', argumentTypes.string],
43
+ data: ['data', 'the data of the work', '', argumentTypes.string],
44
+ rlp: ['rlp', 'the rlp of the message', '', argumentTypes.string],
45
+ messageJson: ['message', 'the decoded message to send', 'null', argumentTypes.string],
46
+ hash: ['hash', 'the hash of the message', '', argumentTypes.string],
47
+ // optional params
48
+ board: ['board', 'the board to use (programmatic only - no CLI)', null, argumentTypes.any],
49
+ message: ['message', 'the decoded message to send', null, argumentTypes.any],
50
+ fromBlock: ['fromBlock', 'the block number to start from', null, argumentTypes.bigint],
51
+ toBlock: ['toBlock', 'the block number to end at', null, argumentTypes.bigint],
52
+ size: ['size', 'the number of bytes to calculate the difficulty for', 0, argumentTypes.int],
53
+ workMultiplier: ['workMultiplier', 'the work multiplier', 0n, argumentTypes.bigint],
54
+ workDivisor: ['workDivisor', 'the work divisor', 0n, argumentTypes.bigint],
55
+ // flags
56
+ log: ['log', 'log the status'],
57
+ };
58
+ const descriptions = {
59
+ status: 'Gets the status of the msgboard',
60
+ work: 'Produce a proof of work for a given category and data',
61
+ send: 'Send an RLP encoded message to the msgboard',
62
+ sendDecoded: 'Send a decoded message to the msgboard',
63
+ workSend: 'Produce and send a proof of work to the msgboard',
64
+ getMessage: 'Get a message from the msgboard',
65
+ reset: 'Reset the msgboard',
66
+ categories: 'Get the categories of the msgboard',
67
+ content: 'Get the content of the msgboard',
68
+ getDifficulty: 'Calculate the difficulty for a given number of bytes',
69
+ };
70
+ const taskNames = {
71
+ status: 'status',
72
+ work: 'work',
73
+ send: 'send',
74
+ sendDecoded: 'send-decoded',
75
+ workSend: 'work:send',
76
+ getMessage: 'get-message',
77
+ reset: 'reset',
78
+ categories: 'categories',
79
+ content: 'content',
80
+ getDifficulty: 'get-difficulty',
81
+ };
82
+ const globalTaskName = (taskName) => `${scopeKey}:${taskName}`;
83
+ const scopeKey = 'msgboard';
84
+ const scoped = (0, config_1.scope)(scopeKey, 'A scope for msgboard tasks');
85
+ const omitNull = (obj) => Object.fromEntries(Object.entries(obj).filter(([_, v]) => v !== null && v !== undefined));
86
+ scoped.subtask(globalTaskName(taskNames.status))
87
+ .setDescription(descriptions.status)
88
+ .addOptionalParam(...options.board)
89
+ .addFlag(...options.log)
90
+ .setAction(async (taskArgs, hre) => {
91
+ const board = (taskArgs.board ?? hre.msgboard);
92
+ const status = await board.status();
93
+ if (taskArgs.log) {
94
+ board.log('status: %o', {
95
+ enabled: status.enabled,
96
+ workMultiplier: BigInt(status.workMultiplier),
97
+ workDivisor: BigInt(status.workDivisor),
98
+ count: BigInt(status.count),
99
+ size: BigInt(status.size),
100
+ });
101
+ }
102
+ return status;
103
+ });
104
+ scoped.task(taskNames.status)
105
+ .setDescription(descriptions.status)
106
+ .addFlag(...options.log)
107
+ .setAction(async (taskArgs, hre) => {
108
+ return hre.run({
109
+ scope: scopeKey,
110
+ task: globalTaskName(taskNames.status),
111
+ }, omitNull(taskArgs));
112
+ });
113
+ scoped.subtask(globalTaskName(taskNames.work))
114
+ .setDescription(descriptions.work)
115
+ .addParam(...options.category)
116
+ .addParam(...options.data)
117
+ .addFlag(...options.log)
118
+ .addOptionalParam(...options.board)
119
+ .setAction(async (taskArgs, hre) => {
120
+ const { category, data, board: b, log } = taskArgs;
121
+ const d = msgboard.encodeData(data);
122
+ const c = (0, viem_1.isHex)(category) ? (0, viem_1.toHex)(category, { size: 32 }) : (0, viem_1.keccak256)((0, viem_1.stringToBytes)(category));
123
+ const board = (b ?? hre.msgboard);
124
+ const work = await board.doPoW(c, d);
125
+ if (log) {
126
+ board.log('work: %o', work);
127
+ }
128
+ return work;
129
+ });
130
+ scoped.task(taskNames.work)
131
+ .setDescription(descriptions.work)
132
+ .setAction(async (taskArgs, hre) => {
133
+ return hre.run({
134
+ scope: scopeKey,
135
+ task: globalTaskName(taskNames.work),
136
+ }, omitNull(taskArgs));
137
+ });
138
+ scoped.subtask(globalTaskName(taskNames.send))
139
+ .setDescription(descriptions.send)
140
+ .addParam(...options.rlp)
141
+ .addOptionalParam(...options.board)
142
+ .setAction(async (taskArgs, hre) => {
143
+ const { rlp, board: b } = taskArgs;
144
+ const board = (b ?? hre.msgboard);
145
+ return await board.addMessage(rlp);
146
+ });
147
+ scoped.task(taskNames.send)
148
+ .setDescription(descriptions.send)
149
+ .setAction(async (taskArgs, hre) => {
150
+ return hre.run({
151
+ scope: scopeKey,
152
+ task: globalTaskName(taskNames.send),
153
+ }, omitNull(taskArgs));
154
+ });
155
+ scoped.subtask(globalTaskName(taskNames.sendDecoded))
156
+ .setDescription(descriptions.sendDecoded)
157
+ .addParam(...options.message)
158
+ .addOptionalParam(...options.board)
159
+ .setAction(async (taskArgs, hre) => {
160
+ const { message, board: b } = taskArgs;
161
+ return await hre.run({
162
+ scope: scopeKey,
163
+ task: globalTaskName(taskNames.send),
164
+ }, {
165
+ rlp: msgboard.toRLP(message),
166
+ board: b,
167
+ });
168
+ });
169
+ scoped.task(taskNames.sendDecoded)
170
+ .setDescription(descriptions.sendDecoded)
171
+ .setAction(async (taskArgs, hre) => {
172
+ return hre.run({
173
+ scope: scopeKey,
174
+ task: globalTaskName(taskNames.sendDecoded),
175
+ }, {
176
+ ...taskArgs,
177
+ message: JSON.parse(taskArgs.message),
178
+ });
179
+ });
180
+ scoped.subtask(globalTaskName(taskNames.workSend))
181
+ .setDescription(descriptions.workSend)
182
+ .addParam(...options.category)
183
+ .addParam(...options.data)
184
+ .addOptionalParam(...options.board)
185
+ .addFlag(...options.log)
186
+ .setAction(async (taskArgs, hre) => {
187
+ const board = (taskArgs.board ?? hre.msgboard);
188
+ const work = (await hre.run({
189
+ scope: scopeKey,
190
+ task: globalTaskName(taskNames.work),
191
+ }, {
192
+ ...taskArgs,
193
+ board,
194
+ }));
195
+ return await hre.run({
196
+ scope: scopeKey,
197
+ task: globalTaskName(taskNames.sendDecoded),
198
+ }, {
199
+ message: work.message,
200
+ board,
201
+ });
202
+ });
203
+ scoped.task(taskNames.workSend)
204
+ .setDescription(descriptions.workSend)
205
+ .addFlag(...options.log)
206
+ .addParam(...options.category)
207
+ .addParam(...options.data)
208
+ .setAction(async (taskArgs, hre) => {
209
+ return hre.run({
210
+ scope: scopeKey,
211
+ task: globalTaskName(taskNames.workSend),
212
+ }, omitNull(taskArgs));
213
+ });
214
+ scoped.subtask(globalTaskName(taskNames.getMessage))
215
+ .setDescription(descriptions.getMessage)
216
+ .addParam(...options.hash)
217
+ .addOptionalParam(...options.board)
218
+ .setAction(async (taskArgs, hre) => {
219
+ const { hash, board: b } = taskArgs;
220
+ const board = (b ?? hre.msgboard);
221
+ return await board.getMessage(hash);
222
+ });
223
+ scoped.task(taskNames.getMessage)
224
+ .setDescription(descriptions.getMessage)
225
+ .addParam(...options.hash)
226
+ .setAction(async (taskArgs, hre) => {
227
+ return hre.run({
228
+ scope: scopeKey,
229
+ task: globalTaskName(taskNames.getMessage),
230
+ }, omitNull(taskArgs));
231
+ });
232
+ scoped.task(taskNames.reset)
233
+ .setDescription(descriptions.reset)
234
+ .setAction(async (_taskArgs, hre) => {
235
+ if (hre.network.name !== 'hardhat')
236
+ return;
237
+ await hre.network.provider.send('msgboard_reset', []);
238
+ });
239
+ scoped.task('categories')
240
+ .setDescription('Get the categories of the msgboard')
241
+ .setAction(async (_taskArgs, hre) => {
242
+ return await hre.network.provider.send('msgboard_categories', []);
243
+ });
244
+ scoped.subtask(globalTaskName(taskNames.content))
245
+ .setDescription(descriptions.content)
246
+ .addOptionalParam(...options.board)
247
+ .addOptionalParam(...options.category)
248
+ .addOptionalParam(...options.fromBlock)
249
+ .addOptionalParam(...options.toBlock)
250
+ .setAction(async (taskArgs, hre) => {
251
+ const { board: b, category, fromBlock, toBlock } = taskArgs;
252
+ const board = (b ?? hre.msgboard);
253
+ return await board.content({ category, fromBlock, toBlock });
254
+ });
255
+ scoped.task(taskNames.content)
256
+ .setDescription(descriptions.content)
257
+ .addOptionalParam(...options.category)
258
+ .addOptionalParam(...options.fromBlock)
259
+ .addOptionalParam(...options.toBlock)
260
+ .setAction(async (taskArgs, hre) => {
261
+ return hre.run({
262
+ scope: scopeKey,
263
+ task: globalTaskName(taskNames.content),
264
+ }, omitNull(taskArgs));
265
+ });
266
+ scoped.subtask(globalTaskName(taskNames.getDifficulty))
267
+ .setDescription(descriptions.getDifficulty)
268
+ .addOptionalParam(...options.data)
269
+ .addOptionalParam(...options.size)
270
+ .addOptionalParam(...options.workMultiplier)
271
+ .addOptionalParam(...options.workDivisor)
272
+ .addOptionalParam(...options.board)
273
+ .setAction(async (taskArgs, hre) => {
274
+ const { size, data, board: b, workMultiplier, workDivisor } = taskArgs;
275
+ const board = (b ?? hre.msgboard);
276
+ if (workMultiplier && workDivisor) {
277
+ board.setDifficultyFactors(workMultiplier, workDivisor);
278
+ }
279
+ else {
280
+ const status = await board.status().catch(() => null);
281
+ if (status)
282
+ board.setDifficultyFactors(BigInt(status.workMultiplier), BigInt(status.workDivisor));
283
+ }
284
+ const d = data ?? `0x${new Array(size * 2).fill(0).join('')}`;
285
+ return board.getDifficulty(d);
286
+ });
287
+ scoped.task(taskNames.getDifficulty)
288
+ .setDescription(descriptions.getDifficulty)
289
+ .addOptionalParam(...options.size)
290
+ .addOptionalParam(...options.data)
291
+ .addOptionalParam(...options.workMultiplier)
292
+ .addOptionalParam(...options.workDivisor)
293
+ .setAction(async (taskArgs, hre) => {
294
+ return hre.run({
295
+ scope: scopeKey,
296
+ task: globalTaskName(taskNames.getDifficulty),
297
+ }, omitNull(taskArgs));
298
+ });
@@ -0,0 +1,17 @@
1
+ import 'hardhat/types/config';
2
+ import 'hardhat/types/runtime';
3
+ import type { MsgBoardClient } from '@msgboard/sdk';
4
+ import type { MsgBoardSettings } from './types';
5
+ declare module 'hardhat/types/config' {
6
+ interface MsgBoardUserConfig {
7
+ msgboard?: Partial<MsgBoardSettings>;
8
+ }
9
+ interface MsgBoardConfig {
10
+ msgboard: MsgBoardSettings;
11
+ }
12
+ }
13
+ declare module 'hardhat/types/runtime' {
14
+ interface HardhatRuntimeEnvironment {
15
+ msgboard: MsgBoardClient;
16
+ }
17
+ }
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ require("hardhat/types/config");
4
+ require("hardhat/types/runtime");
@@ -0,0 +1,8 @@
1
+ export type MsgBoardSettings = {
2
+ enabled: boolean;
3
+ workMultiplier: bigint;
4
+ workDivisor: bigint;
5
+ messageSizeLimit: bigint;
6
+ boardCountLimit: bigint;
7
+ blockRangeLimit: bigint;
8
+ };
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@msgboard/hardhat",
3
+ "version": "0.0.1",
4
+ "description": "Hardhat plugin for MsgBoard",
5
+ "repository": "github:valve-tech/msgboard",
6
+ "license": "MIT",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "main": "dist/index.js",
11
+ "types": "dist/index.d.ts",
12
+ "commonjs": "dist/index.js",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "require": "./dist/index.js",
17
+ "import": "./dist/index.js",
18
+ "default": "./dist/index.js"
19
+ },
20
+ "./provider": {
21
+ "types": "./dist/provider.d.ts",
22
+ "require": "./dist/provider.js",
23
+ "import": "./dist/provider.js",
24
+ "default": "./dist/provider.js"
25
+ },
26
+ "./types": {
27
+ "types": "./dist/types.d.ts",
28
+ "require": "./dist/types.js",
29
+ "import": "./dist/types.js",
30
+ "default": "./dist/types.js"
31
+ },
32
+ "./tasks": {
33
+ "types": "./dist/tasks.d.ts",
34
+ "require": "./dist/tasks.js",
35
+ "import": "./dist/tasks.js",
36
+ "default": "./dist/tasks.js"
37
+ }
38
+ },
39
+ "files": [
40
+ "dist"
41
+ ],
42
+ "devDependencies": {
43
+ "@nomicfoundation/hardhat-network-helpers": "^1.0.8",
44
+ "hardhat": "^2.16.1",
45
+ "nyc": "^17.1.0",
46
+ "solidity-coverage": "^0.8.15",
47
+ "tsx": "^4.19.3",
48
+ "viem": "^2.28.0",
49
+ "vitest": "^3.1.2"
50
+ },
51
+ "scripts": {
52
+ "prebuild": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
53
+ "build": "npm run compile",
54
+ "print": "hardhat help",
55
+ "watch": "npm run compile:watch",
56
+ "compile": "tsc && hardhat compile",
57
+ "compile:watch": "as-soon -w contracts npm run compile",
58
+ "test": "vitest",
59
+ "coverage": "hardhat compile-for-coverage && vitest run --coverage && hardhat compile",
60
+ "coverage:compile:watch": "as-soon -w contracts hardhat compile-for-coverage",
61
+ "coverage:watch": "hardhat compile-for-coverage && vitest --coverage"
62
+ },
63
+ "dependencies": {
64
+ "@msgboard/sdk": "^0.0.28"
65
+ }
66
+ }