@msgboard/relayer 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 +71 -0
- package/dist/actions/noop.d.ts +4 -0
- package/dist/actions/noop.d.ts.map +1 -0
- package/dist/actions/noop.js +6 -0
- package/dist/actions/noop.js.map +1 -0
- package/dist/actions/send-value.d.ts +17 -0
- package/dist/actions/send-value.d.ts.map +1 -0
- package/dist/actions/send-value.js +27 -0
- package/dist/actions/send-value.js.map +1 -0
- package/dist/actions/submit-message.d.ts +10 -0
- package/dist/actions/submit-message.d.ts.map +1 -0
- package/dist/actions/submit-message.js +13 -0
- package/dist/actions/submit-message.js.map +1 -0
- package/dist/actions/webhook.d.ts +9 -0
- package/dist/actions/webhook.d.ts.map +1 -0
- package/dist/actions/webhook.js +16 -0
- package/dist/actions/webhook.js.map +1 -0
- package/dist/chains.d.ts +7 -0
- package/dist/chains.d.ts.map +1 -0
- package/dist/chains.js +20 -0
- package/dist/chains.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +5 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +7 -0
- package/dist/logger.js.map +1 -0
- package/dist/relayer.d.ts +31 -0
- package/dist/relayer.d.ts.map +1 -0
- package/dist/relayer.js +169 -0
- package/dist/relayer.js.map +1 -0
- package/dist/sinks/postgres-archive.d.ts +45 -0
- package/dist/sinks/postgres-archive.d.ts.map +1 -0
- package/dist/sinks/postgres-archive.js +85 -0
- package/dist/sinks/postgres-archive.js.map +1 -0
- package/dist/sinks/postgres.d.ts +16 -0
- package/dist/sinks/postgres.d.ts.map +1 -0
- package/dist/sinks/postgres.js +19 -0
- package/dist/sinks/postgres.js.map +1 -0
- package/dist/sources/bridge-affirmation.d.ts +15 -0
- package/dist/sources/bridge-affirmation.d.ts.map +1 -0
- package/dist/sources/bridge-affirmation.js +38 -0
- package/dist/sources/bridge-affirmation.js.map +1 -0
- package/dist/sources/generated.d.ts +4 -0
- package/dist/sources/generated.d.ts.map +1 -0
- package/dist/sources/generated.js +5 -0
- package/dist/sources/generated.js.map +1 -0
- package/dist/sources/msgboard-content.d.ts +9 -0
- package/dist/sources/msgboard-content.d.ts.map +1 -0
- package/dist/sources/msgboard-content.js +19 -0
- package/dist/sources/msgboard-content.js.map +1 -0
- package/dist/stores/memory-ttl.d.ts +11 -0
- package/dist/stores/memory-ttl.d.ts.map +1 -0
- package/dist/stores/memory-ttl.js +29 -0
- package/dist/stores/memory-ttl.js.map +1 -0
- package/dist/stores/noop.d.ts +4 -0
- package/dist/stores/noop.d.ts.map +1 -0
- package/dist/stores/noop.js +6 -0
- package/dist/stores/noop.js.map +1 -0
- package/dist/stores/postgres.d.ts +19 -0
- package/dist/stores/postgres.d.ts.map +1 -0
- package/dist/stores/postgres.js +26 -0
- package/dist/stores/postgres.js.map +1 -0
- package/dist/types.d.ts +93 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# @msgboard/relayer
|
|
2
|
+
|
|
3
|
+
A controllable, safe-by-default pool-watcher engine for the msgboard board. Runs a heartbeat: poll a **Source**, record every item to an always-on **Sink**, filter by a **Condition**, dedup via a **Store**, then gate the **Action** based on mode.
|
|
4
|
+
|
|
5
|
+
## Safe by default
|
|
6
|
+
|
|
7
|
+
The engine has two modes:
|
|
8
|
+
|
|
9
|
+
- **`observe`** (default): polls, records to the sink, and logs what it *would* do. No outbound side effect. A relayer constructed without an explicit `mode` never writes on chain.
|
|
10
|
+
- **`live`**: executes the action when all conditions are met. Use `BRIDGE_LIVE=1`, `SPAM_OBSERVE=1` etc. to flip the mode from environment variables — never hardcode `live`.
|
|
11
|
+
|
|
12
|
+
## The four contracts
|
|
13
|
+
|
|
14
|
+
| Contract | Role | Retention |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| `RelayerSource<T>` | Reads the current batch of candidates from the watched pool | — |
|
|
17
|
+
| `RelayerAction<T>` | Describes (observe) or executes (live) the outbound effect | — |
|
|
18
|
+
| `RelayerStore<T>` | Action-level dedup: "have I already acted on this?" | Short (minutes–hours) |
|
|
19
|
+
| `RelayerSink<T>` | Unconditional history/observability recording | Long (months–years) |
|
|
20
|
+
|
|
21
|
+
Sink and Store are intentionally separate: the sink runs in **both** modes; the store only advances in live mode after a successful execute.
|
|
22
|
+
|
|
23
|
+
## Minimal usage
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { Relayer, msgboardContentSource, noopAction } from '@msgboard/relayer'
|
|
27
|
+
|
|
28
|
+
const relayer = new Relayer({
|
|
29
|
+
node: { rpcUrl: 'https://rpc.v4.testnet.pulsechain.com', chainId: 943 },
|
|
30
|
+
// mode defaults to 'observe' — no on-chain writes
|
|
31
|
+
source: msgboardContentSource({ category: 'myapp' }),
|
|
32
|
+
key: (msg) => msg.hash,
|
|
33
|
+
action: noopAction(),
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
relayer.start()
|
|
37
|
+
// later: await relayer.stop()
|
|
38
|
+
// or: const report = await relayer.runOnce()
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Historical archive
|
|
42
|
+
|
|
43
|
+
`postgresArchiveSink` records every message the relayer sees to a `message_archive` table with a default 1-year retention window. Run a dedicated **archivist** relayer (see `examples/archivist.ts`) to populate the archive from all board traffic, then query it:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
const archive = postgresArchiveSink({ pool, retention: { days: 365 } })
|
|
47
|
+
await archive.migrate()
|
|
48
|
+
|
|
49
|
+
const recent = await archive.query({ chainId: 943, category: 'lorem', limit: 20 })
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`query()` filters by `chainId`, `category` (hex or decoded text), `since`/`until`, `contains` (substring match on decoded content), `limit`, and `offset`.
|
|
53
|
+
|
|
54
|
+
## Example relayers
|
|
55
|
+
|
|
56
|
+
| Relayer | source | store | sink | action (live only) | demonstrates |
|
|
57
|
+
|---|---|---|---|---|---|
|
|
58
|
+
| Gas sponsor (`index.ts`) | content `gasmoneyplease` + address check | postgres | archive | `sendValueAction` 10 coins | durable dedup, value transfer |
|
|
59
|
+
| Bridge watcher (`bridge.ts`) | `bridgeAffirmationSource` (per chain) | memory-ttl | — | `submitMessageAction` | finalized event source, multi-node |
|
|
60
|
+
| Spam writer (`spam.ts`) | `generatedSource(sentence)` | noop | — | `submitMessageAction` | producer source, noop dedup |
|
|
61
|
+
| Archivist | all content | — | `postgresArchiveSink` 1yr | `noopAction` | sink-only; records in observe mode |
|
|
62
|
+
| Cross-chain mirror | content on 369 | memory-ttl | — | `submitMessageAction` on 943 | source node ≠ action node |
|
|
63
|
+
| Moderation flagger | all content + blocklist predicate | — | `postgresSink('flagged')` | `noopAction` | condition + sink, no action |
|
|
64
|
+
|
|
65
|
+
See `examples/` for runnable code for the archivist, mirror, and flagger.
|
|
66
|
+
|
|
67
|
+
## Known limitations
|
|
68
|
+
|
|
69
|
+
- **Difficulty sync**: `submitMessageAction` uses the `MsgBoardClient` default difficulty factors (10 000 / 1 000 000). If a board deployment uses non-standard difficulty, pass `difficultyFactors` when constructing your own `MsgBoardClient` and supply it via a custom action rather than `submitMessageAction`.
|
|
70
|
+
- **Reorg handling**: sources read at the current head by default. The `bridgeAffirmationSource` reads at the finalized block, so it is reorg-safe. Other sources are best-effort; for strict reorg safety, build a checkpointed source using the `RelayerSource` contract.
|
|
71
|
+
- **Multi-node fan-out**: construct one `Relayer` per node and call `start()` on each — no special machinery needed.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noop.d.ts","sourceRoot":"","sources":["../../src/actions/noop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD,gFAAgF;AAChF,eAAO,MAAM,UAAU,GAAI,CAAC,OAAK,aAAa,CAAC,CAAC,CAG9C,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noop.js","sourceRoot":"","sources":["../../src/actions/noop.ts"],"names":[],"mappings":"AAEA,gFAAgF;AAChF,MAAM,CAAC,MAAM,UAAU,GAAG,GAAwB,EAAE,CAAC,CAAC;IACpD,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM;IACtB,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;CACpC,CAAC,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Account, type Address, type WalletClient } from 'viem';
|
|
2
|
+
import type { RelayerAction, RelayerContext } from '../types.js';
|
|
3
|
+
export type SendValueActionOptions<T> = {
|
|
4
|
+
/** The funding account (e.g. from `mnemonicToAccount`). */
|
|
5
|
+
account: Account;
|
|
6
|
+
/** Derives the recipient address for an item. */
|
|
7
|
+
recipient: (item: T, context: RelayerContext) => Address;
|
|
8
|
+
/** Amount to send, in wei. */
|
|
9
|
+
amount: bigint;
|
|
10
|
+
/** Gas limit for the transfer. */
|
|
11
|
+
gas: bigint;
|
|
12
|
+
/** Overridable wallet-client factory (injected in tests). */
|
|
13
|
+
walletFactory?: (context: RelayerContext) => WalletClient;
|
|
14
|
+
};
|
|
15
|
+
/** Sends native coin to an address derived from each item; waits for the receipt. */
|
|
16
|
+
export declare const sendValueAction: <T>(options: SendValueActionOptions<T>) => RelayerAction<T>;
|
|
17
|
+
//# sourceMappingURL=send-value.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send-value.d.ts","sourceRoot":"","sources":["../../src/actions/send-value.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,OAAO,EACZ,KAAK,YAAY,EAIlB,MAAM,MAAM,CAAA;AACb,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEhE,MAAM,MAAM,sBAAsB,CAAC,CAAC,IAAI;IACtC,2DAA2D;IAC3D,OAAO,EAAE,OAAO,CAAA;IAChB,iDAAiD;IACjD,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,cAAc,KAAK,OAAO,CAAA;IACxD,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAA;IACX,6DAA6D;IAC7D,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,YAAY,CAAA;CAC1D,CAAA;AAED,qFAAqF;AACrF,eAAO,MAAM,eAAe,GAAI,CAAC,EAAE,SAAS,sBAAsB,CAAC,CAAC,CAAC,KAAG,aAAa,CAAC,CAAC,CAyBtF,CAAA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createWalletClient, formatEther, http, } from 'viem';
|
|
2
|
+
/** Sends native coin to an address derived from each item; waits for the receipt. */
|
|
3
|
+
export const sendValueAction = (options) => {
|
|
4
|
+
const makeWallet = (context) => options.walletFactory?.(context) ??
|
|
5
|
+
createWalletClient({
|
|
6
|
+
account: options.account,
|
|
7
|
+
chain: context.chain,
|
|
8
|
+
transport: http(context.node.rpcUrl, { timeout: 30_000 }),
|
|
9
|
+
});
|
|
10
|
+
return {
|
|
11
|
+
describe: (item, context) => `send ${formatEther(options.amount)} to ${options.recipient(item, context)}`,
|
|
12
|
+
execute: async (item, context) => {
|
|
13
|
+
const wallet = makeWallet(context);
|
|
14
|
+
const to = options.recipient(item, context);
|
|
15
|
+
const hash = await wallet.sendTransaction({
|
|
16
|
+
account: options.account,
|
|
17
|
+
chain: context.chain,
|
|
18
|
+
to,
|
|
19
|
+
value: options.amount,
|
|
20
|
+
gas: options.gas,
|
|
21
|
+
});
|
|
22
|
+
await context.publicClient.waitForTransactionReceipt({ hash });
|
|
23
|
+
return { ok: true, ref: hash };
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=send-value.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send-value.js","sourceRoot":"","sources":["../../src/actions/send-value.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,kBAAkB,EAClB,WAAW,EACX,IAAI,GACL,MAAM,MAAM,CAAA;AAgBb,qFAAqF;AACrF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAI,OAAkC,EAAoB,EAAE;IACzF,MAAM,UAAU,GAAG,CAAC,OAAuB,EAAgB,EAAE,CAC3D,OAAO,CAAC,aAAa,EAAE,CAAC,OAAO,CAAC;QAChC,kBAAkB,CAAC;YACjB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;SAC1D,CAAC,CAAA;IACJ,OAAO;QACL,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,CAC1B,QAAQ,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE;QAC9E,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;YAC/B,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,CAAA;YAClC,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;YAC3C,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC;gBACxC,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,EAAE;gBACF,KAAK,EAAE,OAAO,CAAC,MAAM;gBACrB,GAAG,EAAE,OAAO,CAAC,GAAG;aACjB,CAAC,CAAA;YACF,MAAM,OAAO,CAAC,YAAY,CAAC,yBAAyB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAA;YAC9D,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAA;QAChC,CAAC;KACF,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { RelayerAction, RelayerContext } from '../types.js';
|
|
2
|
+
export type SubmitMessageActionOptions<T> = {
|
|
3
|
+
/** Derives the category (name or bytes32 hex) for an item. */
|
|
4
|
+
category: (item: T, context: RelayerContext) => string;
|
|
5
|
+
/** Derives the message data (text or hex) for an item. */
|
|
6
|
+
data: (item: T, context: RelayerContext) => string;
|
|
7
|
+
};
|
|
8
|
+
/** Posts a proof-of-work message to the board. No wallet or gas required. */
|
|
9
|
+
export declare const submitMessageAction: <T>(options: SubmitMessageActionOptions<T>) => RelayerAction<T>;
|
|
10
|
+
//# sourceMappingURL=submit-message.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"submit-message.d.ts","sourceRoot":"","sources":["../../src/actions/submit-message.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEhE,MAAM,MAAM,0BAA0B,CAAC,CAAC,IAAI;IAC1C,8DAA8D;IAC9D,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,cAAc,KAAK,MAAM,CAAA;IACtD,0DAA0D;IAC1D,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,cAAc,KAAK,MAAM,CAAA;CACnD,CAAA;AAED,6EAA6E;AAC7E,eAAO,MAAM,mBAAmB,GAAI,CAAC,EACnC,SAAS,0BAA0B,CAAC,CAAC,CAAC,KACrC,aAAa,CAAC,CAAC,CAUhB,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { encodeData } from '@msgboard/sdk';
|
|
2
|
+
/** Posts a proof-of-work message to the board. No wallet or gas required. */
|
|
3
|
+
export const submitMessageAction = (options) => ({
|
|
4
|
+
describe: (item, context) => `post message category=${options.category(item, context)} data=${options.data(item, context)}`,
|
|
5
|
+
execute: async (item, context) => {
|
|
6
|
+
const category = options.category(item, context);
|
|
7
|
+
const data = encodeData(options.data(item, context));
|
|
8
|
+
const work = await context.client.doPoW(category, data);
|
|
9
|
+
const hash = await context.client.addMessage(work.message);
|
|
10
|
+
return { ok: true, ref: hash, meta: { stats: work.stats } };
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
//# sourceMappingURL=submit-message.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"submit-message.js","sourceRoot":"","sources":["../../src/actions/submit-message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAU1C,6EAA6E;AAC7E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,OAAsC,EACpB,EAAE,CAAC,CAAC;IACtB,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,CAC1B,yBAAyB,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,SAAS,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE;IAChG,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;QAC/B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QAChD,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAA;QACpD,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;QACvD,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC1D,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,CAAA;IAC7D,CAAC;CACF,CAAC,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RelayerAction } from '../types.js';
|
|
2
|
+
export type WebhookActionOptions = {
|
|
3
|
+
url: string;
|
|
4
|
+
/** Overridable fetch implementation (injected in tests). Defaults to global fetch. */
|
|
5
|
+
fetchImpl?: typeof fetch;
|
|
6
|
+
};
|
|
7
|
+
/** Posts each item as JSON to a webhook. Demonstrates a non-on-chain gated action. */
|
|
8
|
+
export declare const webhookAction: <T>(options: WebhookActionOptions) => RelayerAction<T>;
|
|
9
|
+
//# sourceMappingURL=webhook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/actions/webhook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD,MAAM,MAAM,oBAAoB,GAAG;IACjC,GAAG,EAAE,MAAM,CAAA;IACX,sFAAsF;IACtF,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CACzB,CAAA;AAED,sFAAsF;AACtF,eAAO,MAAM,aAAa,GAAI,CAAC,EAAE,SAAS,oBAAoB,KAAG,aAAa,CAAC,CAAC,CAa/E,CAAA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Posts each item as JSON to a webhook. Demonstrates a non-on-chain gated action. */
|
|
2
|
+
export const webhookAction = (options) => {
|
|
3
|
+
const doFetch = options.fetchImpl ?? fetch;
|
|
4
|
+
return {
|
|
5
|
+
describe: (_item) => `POST to ${options.url}`,
|
|
6
|
+
execute: async (item) => {
|
|
7
|
+
const response = await doFetch(options.url, {
|
|
8
|
+
method: 'POST',
|
|
9
|
+
headers: { 'content-type': 'application/json' },
|
|
10
|
+
body: JSON.stringify(item),
|
|
11
|
+
});
|
|
12
|
+
return { ok: response.ok, meta: { status: response.status } };
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=webhook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.js","sourceRoot":"","sources":["../../src/actions/webhook.ts"],"names":[],"mappings":"AAQA,sFAAsF;AACtF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAI,OAA6B,EAAoB,EAAE;IAClF,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAA;IAC1C,OAAO;QACL,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,OAAO,CAAC,GAAG,EAAE;QAC7C,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YACtB,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE;gBAC1C,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;aAC3B,CAAC,CAAA;YACF,OAAO,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAA;QAC/D,CAAC;KACF,CAAA;AACH,CAAC,CAAA"}
|
package/dist/chains.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chains.d.ts","sourceRoot":"","sources":["../src/chains.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,MAAM,CAAA;AAUjC;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,SAAS,MAAM,KAAG,KAO9C,CAAA"}
|
package/dist/chains.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { mainnet, pulsechain, pulsechainV4 } from 'viem/chains';
|
|
2
|
+
/** The networks a relayer can target, keyed by chain id. */
|
|
3
|
+
const chainsById = {
|
|
4
|
+
[mainnet.id]: mainnet,
|
|
5
|
+
[pulsechain.id]: pulsechain,
|
|
6
|
+
[pulsechainV4.id]: pulsechainV4,
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Resolves a viem chain from its numeric id.
|
|
10
|
+
* @throws if the chain id is not one of the supported networks
|
|
11
|
+
*/
|
|
12
|
+
export const resolveChain = (chainId) => {
|
|
13
|
+
const chain = chainsById[chainId];
|
|
14
|
+
if (!chain) {
|
|
15
|
+
const supported = Object.keys(chainsById).join(', ');
|
|
16
|
+
throw new Error(`unsupported chainId ${chainId} (expected one of ${supported})`);
|
|
17
|
+
}
|
|
18
|
+
return chain;
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=chains.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chains.js","sourceRoot":"","sources":["../src/chains.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/D,4DAA4D;AAC5D,MAAM,UAAU,GAA0B;IACxC,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,OAAO;IACrB,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,UAAU;IAC3B,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,YAAY;CAChC,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,OAAe,EAAS,EAAE;IACrD,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,CAAA;IACjC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACpD,MAAM,IAAI,KAAK,CAAC,uBAAuB,OAAO,qBAAqB,SAAS,GAAG,CAAC,CAAA;IAClF,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC,CAAA"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { Relayer } from './relayer.js';
|
|
2
|
+
export { defaultLogger } from './logger.js';
|
|
3
|
+
export type { Logger } from './logger.js';
|
|
4
|
+
export { resolveChain } from './chains.js';
|
|
5
|
+
export type { ActionResult, RelayerAction, RelayerCondition, RelayerConfig, RelayerContext, RelayerKey, RelayerMode, RelayerNode, RelayerSink, RelayerSource, RelayerStore, TickReport, } from './types.js';
|
|
6
|
+
export { memoryTtlStore } from './stores/memory-ttl.js';
|
|
7
|
+
export { noopStore } from './stores/noop.js';
|
|
8
|
+
export { postgresStore } from './stores/postgres.js';
|
|
9
|
+
export type { Queryable } from './stores/postgres.js';
|
|
10
|
+
export { postgresArchiveSink } from './sinks/postgres-archive.js';
|
|
11
|
+
export type { ArchiveQuery, ArchivedMessage, ArchiveRetention } from './sinks/postgres-archive.js';
|
|
12
|
+
export { postgresSink } from './sinks/postgres.js';
|
|
13
|
+
export { msgboardContentSource } from './sources/msgboard-content.js';
|
|
14
|
+
export { bridgeAffirmationSource } from './sources/bridge-affirmation.js';
|
|
15
|
+
export { generatedSource } from './sources/generated.js';
|
|
16
|
+
export { submitMessageAction } from './actions/submit-message.js';
|
|
17
|
+
export { sendValueAction } from './actions/send-value.js';
|
|
18
|
+
export { webhookAction } from './actions/webhook.js';
|
|
19
|
+
export { noopAction } from './actions/noop.js';
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,YAAY,EACV,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,UAAU,EACV,WAAW,EACX,WAAW,EACX,WAAW,EACX,aAAa,EACb,YAAY,EACZ,UAAU,GACX,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAErD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACjE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AAClG,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAElD,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAA;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAA;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAExD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { Relayer } from './relayer.js';
|
|
2
|
+
export { defaultLogger } from './logger.js';
|
|
3
|
+
export { resolveChain } from './chains.js';
|
|
4
|
+
export { memoryTtlStore } from './stores/memory-ttl.js';
|
|
5
|
+
export { noopStore } from './stores/noop.js';
|
|
6
|
+
export { postgresStore } from './stores/postgres.js';
|
|
7
|
+
export { postgresArchiveSink } from './sinks/postgres-archive.js';
|
|
8
|
+
export { postgresSink } from './sinks/postgres.js';
|
|
9
|
+
export { msgboardContentSource } from './sources/msgboard-content.js';
|
|
10
|
+
export { bridgeAffirmationSource } from './sources/bridge-affirmation.js';
|
|
11
|
+
export { generatedSource } from './sources/generated.js';
|
|
12
|
+
export { submitMessageAction } from './actions/submit-message.js';
|
|
13
|
+
export { sendValueAction } from './actions/send-value.js';
|
|
14
|
+
export { webhookAction } from './actions/webhook.js';
|
|
15
|
+
export { noopAction } from './actions/noop.js';
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE3C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAgB1C,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAGpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AAEjE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAElD,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAA;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAA;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAExD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** A minimal structured logger; printf-style, matching the SDK's logger shape. */
|
|
2
|
+
export type Logger = (formatter: string, ...args: unknown[]) => void;
|
|
3
|
+
/** The default logger writes to the console with a fixed prefix. */
|
|
4
|
+
export declare const defaultLogger: (prefix: string) => Logger;
|
|
5
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,MAAM,MAAM,MAAM,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;AAEpE,oEAAoE;AACpE,eAAO,MAAM,aAAa,GAAI,QAAQ,MAAM,KAAG,MAI9C,CAAA"}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAGA,oEAAoE;AACpE,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,MAAc,EAAU,EAAE;IACtD,OAAO,CAAC,SAAiB,EAAE,GAAG,IAAe,EAAE,EAAE;QAC/C,OAAO,CAAC,GAAG,CAAC,IAAI,MAAM,KAAK,SAAS,EAAE,EAAE,GAAG,IAAI,CAAC,CAAA;IAClD,CAAC,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { RelayerConfig, RelayerMode, TickReport } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* A controllable pool-watcher. Safe by default: in `observe` mode it records and
|
|
4
|
+
* logs what it would do, but performs no outbound side effect.
|
|
5
|
+
*/
|
|
6
|
+
export declare class Relayer<T> {
|
|
7
|
+
private readonly config;
|
|
8
|
+
private readonly logger;
|
|
9
|
+
private readonly context;
|
|
10
|
+
private readonly intervalMs;
|
|
11
|
+
private readonly pruneEveryTicks;
|
|
12
|
+
private tickCount;
|
|
13
|
+
private running;
|
|
14
|
+
private loopPromise;
|
|
15
|
+
private abort;
|
|
16
|
+
constructor(config: RelayerConfig<T>);
|
|
17
|
+
get mode(): RelayerMode;
|
|
18
|
+
/** Runs a single tick and returns a report. Used by tests and one-shot runs. */
|
|
19
|
+
runOnce(): Promise<TickReport>;
|
|
20
|
+
/** Begins the poll loop. Idempotent — a second call is a no-op while running. */
|
|
21
|
+
start(): void;
|
|
22
|
+
/** Stops the loop and awaits the in-flight tick. */
|
|
23
|
+
stop(): Promise<void>;
|
|
24
|
+
private loop;
|
|
25
|
+
private handleItem;
|
|
26
|
+
private recordItem;
|
|
27
|
+
private isEligible;
|
|
28
|
+
private actOnItem;
|
|
29
|
+
private maybePrune;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=relayer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relayer.d.ts","sourceRoot":"","sources":["../src/relayer.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAkB,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAuCxF;;;GAGG;AACH,qBAAa,OAAO,CAAC,CAAC;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAQ;IACxC,OAAO,CAAC,SAAS,CAAI;IACrB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,KAAK,CAA+B;gBAEhC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;IAQpC,IAAI,IAAI,IAAI,WAAW,CAEtB;IAED,gFAAgF;IAC1E,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC;IAmBpC,iFAAiF;IACjF,KAAK,IAAI,IAAI;IAQb,oDAAoD;IAC9C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YASb,IAAI;YAYJ,UAAU;YAWV,UAAU;YAMV,UAAU;YAcV,SAAS;YAiBT,UAAU;CAKzB"}
|
package/dist/relayer.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { createPublicClient, http } from 'viem';
|
|
2
|
+
import { MsgBoardClient } from '@msgboard/sdk';
|
|
3
|
+
import { resolveChain } from './chains.js';
|
|
4
|
+
import { defaultLogger } from './logger.js';
|
|
5
|
+
const DEFAULT_INTERVAL_MS = 30_000;
|
|
6
|
+
const DEFAULT_PRUNE_EVERY_TICKS = 30;
|
|
7
|
+
/** Builds the runtime context (viem + SDK clients) for a relayer config. */
|
|
8
|
+
const buildContext = (config, logger) => {
|
|
9
|
+
const chain = resolveChain(config.node.chainId);
|
|
10
|
+
const publicClient = createPublicClient({
|
|
11
|
+
chain,
|
|
12
|
+
transport: http(config.node.rpcUrl, { timeout: 30_000 }),
|
|
13
|
+
});
|
|
14
|
+
const client = new MsgBoardClient(publicClient);
|
|
15
|
+
return {
|
|
16
|
+
node: config.node,
|
|
17
|
+
mode: config.mode ?? 'observe',
|
|
18
|
+
chain,
|
|
19
|
+
publicClient,
|
|
20
|
+
client,
|
|
21
|
+
logger,
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
/** Resolves after `ms`, or immediately if the signal aborts. */
|
|
25
|
+
const sleep = (ms, signal) => {
|
|
26
|
+
return new Promise((resolve) => {
|
|
27
|
+
if (signal.aborted)
|
|
28
|
+
return resolve();
|
|
29
|
+
const timer = setTimeout(() => {
|
|
30
|
+
signal.removeEventListener('abort', onAbort);
|
|
31
|
+
resolve();
|
|
32
|
+
}, ms);
|
|
33
|
+
const onAbort = () => {
|
|
34
|
+
clearTimeout(timer);
|
|
35
|
+
resolve();
|
|
36
|
+
};
|
|
37
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* A controllable pool-watcher. Safe by default: in `observe` mode it records and
|
|
42
|
+
* logs what it would do, but performs no outbound side effect.
|
|
43
|
+
*/
|
|
44
|
+
export class Relayer {
|
|
45
|
+
config;
|
|
46
|
+
logger;
|
|
47
|
+
context;
|
|
48
|
+
intervalMs;
|
|
49
|
+
pruneEveryTicks;
|
|
50
|
+
tickCount = 0;
|
|
51
|
+
running = false;
|
|
52
|
+
loopPromise = null;
|
|
53
|
+
abort = null;
|
|
54
|
+
constructor(config) {
|
|
55
|
+
this.config = config;
|
|
56
|
+
this.logger = config.logger ?? defaultLogger('relayer');
|
|
57
|
+
this.context = buildContext(config, this.logger);
|
|
58
|
+
this.intervalMs = config.intervalMs ?? DEFAULT_INTERVAL_MS;
|
|
59
|
+
this.pruneEveryTicks = config.pruneEveryTicks ?? DEFAULT_PRUNE_EVERY_TICKS;
|
|
60
|
+
}
|
|
61
|
+
get mode() {
|
|
62
|
+
return this.context.mode;
|
|
63
|
+
}
|
|
64
|
+
/** Runs a single tick and returns a report. Used by tests and one-shot runs. */
|
|
65
|
+
async runOnce() {
|
|
66
|
+
const report = {
|
|
67
|
+
polled: 0,
|
|
68
|
+
recorded: 0,
|
|
69
|
+
eligible: 0,
|
|
70
|
+
executed: 0,
|
|
71
|
+
described: 0,
|
|
72
|
+
deduped: 0,
|
|
73
|
+
};
|
|
74
|
+
const items = await this.config.source.poll(this.context);
|
|
75
|
+
report.polled = items.length;
|
|
76
|
+
for (const item of items) {
|
|
77
|
+
await this.handleItem(item, report);
|
|
78
|
+
}
|
|
79
|
+
this.tickCount += 1;
|
|
80
|
+
await this.maybePrune();
|
|
81
|
+
return report;
|
|
82
|
+
}
|
|
83
|
+
/** Begins the poll loop. Idempotent — a second call is a no-op while running. */
|
|
84
|
+
start() {
|
|
85
|
+
if (this.running)
|
|
86
|
+
return;
|
|
87
|
+
this.running = true;
|
|
88
|
+
this.abort = new AbortController();
|
|
89
|
+
const signal = this.abort.signal;
|
|
90
|
+
this.loopPromise = this.loop(signal);
|
|
91
|
+
}
|
|
92
|
+
/** Stops the loop and awaits the in-flight tick. */
|
|
93
|
+
async stop() {
|
|
94
|
+
if (!this.running)
|
|
95
|
+
return;
|
|
96
|
+
this.running = false;
|
|
97
|
+
this.abort?.abort();
|
|
98
|
+
await this.loopPromise;
|
|
99
|
+
this.loopPromise = null;
|
|
100
|
+
this.abort = null;
|
|
101
|
+
}
|
|
102
|
+
async loop(signal) {
|
|
103
|
+
while (!signal.aborted) {
|
|
104
|
+
try {
|
|
105
|
+
await this.runOnce();
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
this.logger('tick failed: %o', error instanceof Error ? error.message : error);
|
|
109
|
+
}
|
|
110
|
+
if (signal.aborted)
|
|
111
|
+
return;
|
|
112
|
+
await sleep(this.intervalMs, signal);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async handleItem(item, report) {
|
|
116
|
+
await this.recordItem(item, report);
|
|
117
|
+
const eligible = await this.isEligible(item);
|
|
118
|
+
if (!eligible.proceed) {
|
|
119
|
+
if (eligible.reason === 'deduped')
|
|
120
|
+
report.deduped += 1;
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
report.eligible += 1;
|
|
124
|
+
await this.actOnItem(item, report);
|
|
125
|
+
}
|
|
126
|
+
async recordItem(item, report) {
|
|
127
|
+
if (!this.config.sink)
|
|
128
|
+
return;
|
|
129
|
+
await this.config.sink.record(item, this.context);
|
|
130
|
+
report.recorded += 1;
|
|
131
|
+
}
|
|
132
|
+
async isEligible(item) {
|
|
133
|
+
if (this.config.condition) {
|
|
134
|
+
const ok = await this.config.condition(item, this.context);
|
|
135
|
+
if (!ok)
|
|
136
|
+
return { proceed: false, reason: 'condition' };
|
|
137
|
+
}
|
|
138
|
+
if (this.config.store) {
|
|
139
|
+
const seen = await this.config.store.has(this.config.key(item));
|
|
140
|
+
if (seen)
|
|
141
|
+
return { proceed: false, reason: 'deduped' };
|
|
142
|
+
}
|
|
143
|
+
return { proceed: true };
|
|
144
|
+
}
|
|
145
|
+
async actOnItem(item, report) {
|
|
146
|
+
if (this.context.mode === 'observe') {
|
|
147
|
+
this.logger('observe: %s', this.config.action.describe(item, this.context));
|
|
148
|
+
report.described += 1;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
const result = await this.config.action.execute(item, this.context);
|
|
153
|
+
report.executed += 1;
|
|
154
|
+
if (this.config.store) {
|
|
155
|
+
await this.config.store.remember(this.config.key(item), result);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
this.logger('action failed: %o', error instanceof Error ? error.message : error);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async maybePrune() {
|
|
163
|
+
if (this.tickCount % this.pruneEveryTicks !== 0)
|
|
164
|
+
return;
|
|
165
|
+
await this.config.store?.prune?.();
|
|
166
|
+
await this.config.sink?.prune?.();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=relayer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relayer.js","sourceRoot":"","sources":["../src/relayer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { RPCMessage } from '@msgboard/sdk';
|
|
2
|
+
import type { RelayerSink } from '../types.js';
|
|
3
|
+
import type { Queryable } from '../stores/postgres.js';
|
|
4
|
+
export type ArchiveRetention = {
|
|
5
|
+
/** Rows older than this many days are pruned. */
|
|
6
|
+
days: number;
|
|
7
|
+
};
|
|
8
|
+
export type PostgresArchiveOptions = {
|
|
9
|
+
pool: Queryable;
|
|
10
|
+
retention: ArchiveRetention;
|
|
11
|
+
};
|
|
12
|
+
/** Filters for querying the historical archive. */
|
|
13
|
+
export type ArchiveQuery = {
|
|
14
|
+
chainId?: number;
|
|
15
|
+
/** A bytes32 hex category or its decoded text. */
|
|
16
|
+
category?: string;
|
|
17
|
+
since?: Date;
|
|
18
|
+
until?: Date;
|
|
19
|
+
/** Substring match on decoded content. */
|
|
20
|
+
contains?: string;
|
|
21
|
+
limit?: number;
|
|
22
|
+
offset?: number;
|
|
23
|
+
};
|
|
24
|
+
/** A row of the historical archive. */
|
|
25
|
+
export type ArchivedMessage = {
|
|
26
|
+
hash: string;
|
|
27
|
+
chain_id: number;
|
|
28
|
+
category: string | null;
|
|
29
|
+
category_text: string | null;
|
|
30
|
+
data: string | null;
|
|
31
|
+
content: string | null;
|
|
32
|
+
block_number: string | null;
|
|
33
|
+
block_hash: string | null;
|
|
34
|
+
first_seen_at: string;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* The historical index of every message seen flowing through the board. An
|
|
38
|
+
* ever-growing table, pruned to a retention window (default one year). `record`
|
|
39
|
+
* is idempotent on `(hash, chain_id)`. Call `migrate()` once at startup.
|
|
40
|
+
*/
|
|
41
|
+
export declare const postgresArchiveSink: (options: PostgresArchiveOptions) => RelayerSink<RPCMessage> & {
|
|
42
|
+
migrate(): Promise<void>;
|
|
43
|
+
query(filter: ArchiveQuery): Promise<ArchivedMessage[]>;
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=postgres-archive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres-archive.d.ts","sourceRoot":"","sources":["../../src/sinks/postgres-archive.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,KAAK,EAAkB,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAEtD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,SAAS,CAAA;IACf,SAAS,EAAE,gBAAgB,CAAA;CAC5B,CAAA;AAED,mDAAmD;AACnD,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,IAAI,CAAA;IACZ,KAAK,CAAC,EAAE,IAAI,CAAA;IACZ,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,uCAAuC;AACvC,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA;AAeD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAC9B,SAAS,sBAAsB,KAC9B,WAAW,CAAC,UAAU,CAAC,GAAG;IAC3B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACxB,KAAK,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAAA;CA+ExD,CAAA"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { hexToString } from 'viem';
|
|
2
|
+
/** Decodes a hex blob to text, stripping null padding and returning null if not printable. */
|
|
3
|
+
const tryDecodeText = (hex) => {
|
|
4
|
+
try {
|
|
5
|
+
const text = hexToString(hex).replace(/\0+$/g, '').trim();
|
|
6
|
+
if (text.length === 0)
|
|
7
|
+
return null;
|
|
8
|
+
// Reject blobs with non-printable control characters
|
|
9
|
+
if (/[\x00-\x1f\x7f]/u.test(text))
|
|
10
|
+
return null;
|
|
11
|
+
return text;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* The historical index of every message seen flowing through the board. An
|
|
19
|
+
* ever-growing table, pruned to a retention window (default one year). `record`
|
|
20
|
+
* is idempotent on `(hash, chain_id)`. Call `migrate()` once at startup.
|
|
21
|
+
*/
|
|
22
|
+
export const postgresArchiveSink = (options) => {
|
|
23
|
+
const { pool } = options;
|
|
24
|
+
const retentionDays = options.retention.days;
|
|
25
|
+
const migrate = async () => {
|
|
26
|
+
await pool.query(`CREATE TABLE IF NOT EXISTS message_archive (
|
|
27
|
+
hash TEXT NOT NULL,
|
|
28
|
+
chain_id INTEGER NOT NULL,
|
|
29
|
+
category TEXT,
|
|
30
|
+
category_text TEXT,
|
|
31
|
+
data TEXT,
|
|
32
|
+
content TEXT,
|
|
33
|
+
block_number BIGINT,
|
|
34
|
+
block_hash TEXT,
|
|
35
|
+
first_seen_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
36
|
+
PRIMARY KEY (hash, chain_id)
|
|
37
|
+
)`);
|
|
38
|
+
await pool.query(`CREATE INDEX IF NOT EXISTS message_archive_seen_idx ON message_archive (first_seen_at)`);
|
|
39
|
+
await pool.query(`CREATE INDEX IF NOT EXISTS message_archive_chain_seen ON message_archive (chain_id, first_seen_at)`);
|
|
40
|
+
await pool.query(`CREATE INDEX IF NOT EXISTS message_archive_category_idx ON message_archive (category)`);
|
|
41
|
+
};
|
|
42
|
+
const record = async (message, context) => {
|
|
43
|
+
await pool.query(`INSERT INTO message_archive
|
|
44
|
+
(hash, chain_id, category, category_text, data, content, block_number, block_hash)
|
|
45
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
46
|
+
ON CONFLICT (hash, chain_id) DO NOTHING`, [
|
|
47
|
+
message.hash,
|
|
48
|
+
context.node.chainId,
|
|
49
|
+
message.category,
|
|
50
|
+
tryDecodeText(message.category),
|
|
51
|
+
message.data,
|
|
52
|
+
tryDecodeText(message.data),
|
|
53
|
+
BigInt(message.blockNumber).toString(),
|
|
54
|
+
message.blockHash,
|
|
55
|
+
]);
|
|
56
|
+
};
|
|
57
|
+
const prune = async () => {
|
|
58
|
+
await pool.query(`DELETE FROM message_archive WHERE first_seen_at < now() - INTERVAL '${retentionDays} days'`);
|
|
59
|
+
};
|
|
60
|
+
const query = async (filter) => {
|
|
61
|
+
const clauses = [];
|
|
62
|
+
const params = [];
|
|
63
|
+
const add = (clause, value) => {
|
|
64
|
+
params.push(value);
|
|
65
|
+
clauses.push(clause.replace(/\$\?/g, `$${params.length}`));
|
|
66
|
+
};
|
|
67
|
+
if (filter.chainId !== undefined)
|
|
68
|
+
add('chain_id = $?', filter.chainId);
|
|
69
|
+
if (filter.category !== undefined)
|
|
70
|
+
add('(category = $? OR category_text = $?)', filter.category);
|
|
71
|
+
if (filter.since)
|
|
72
|
+
add('first_seen_at >= $?', filter.since.toISOString());
|
|
73
|
+
if (filter.until)
|
|
74
|
+
add('first_seen_at <= $?', filter.until.toISOString());
|
|
75
|
+
if (filter.contains)
|
|
76
|
+
add('content ILIKE $?', `%${filter.contains}%`);
|
|
77
|
+
const where = clauses.length > 0 ? `WHERE ${clauses.join(' AND ')}` : '';
|
|
78
|
+
const limit = Math.min(Math.max(Number.parseInt(String(filter.limit ?? 100), 10) || 100, 1), 1000);
|
|
79
|
+
const offset = Math.max(Number.parseInt(String(filter.offset ?? 0), 10) || 0, 0);
|
|
80
|
+
const { rows } = await pool.query(`SELECT hash, chain_id, category, category_text, data, content, block_number, block_hash, first_seen_at FROM message_archive ${where} ORDER BY first_seen_at DESC LIMIT ${limit} OFFSET ${offset}`, params);
|
|
81
|
+
return rows;
|
|
82
|
+
};
|
|
83
|
+
return { record, prune, migrate, query };
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=postgres-archive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres-archive.js","sourceRoot":"","sources":["../../src/sinks/postgres-archive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,WAAW,EAAE,MAAM,MAAM,CAAA;AAyC5C,8FAA8F;AAC9F,MAAM,aAAa,GAAG,CAAC,GAAQ,EAAiB,EAAE;IAChD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;QACzD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAA;QAClC,qDAAqD;QACrD,IAAI,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAA;QAC9C,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,OAA+B,EAI/B,EAAE;IACF,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAA;IACxB,MAAM,aAAa,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,CAAA;IAE5C,MAAM,OAAO,GAAG,KAAK,IAAmB,EAAE;QACxC,MAAM,IAAI,CAAC,KAAK,CACd;;;;;;;;;;;QAWE,CACH,CAAA;QACD,MAAM,IAAI,CAAC,KAAK,CACd,wFAAwF,CACzF,CAAA;QACD,MAAM,IAAI,CAAC,KAAK,CACd,oGAAoG,CACrG,CAAA;QACD,MAAM,IAAI,CAAC,KAAK,CACd,uFAAuF,CACxF,CAAA;IACH,CAAC,CAAA;IAED,MAAM,MAAM,GAAG,KAAK,EAAE,OAAmB,EAAE,OAAuB,EAAiB,EAAE;QACnF,MAAM,IAAI,CAAC,KAAK,CACd;;;+CAGyC,EACzC;YACE,OAAO,CAAC,IAAI;YACZ,OAAO,CAAC,IAAI,CAAC,OAAO;YACpB,OAAO,CAAC,QAAQ;YAChB,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC;YAC/B,OAAO,CAAC,IAAI;YACZ,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC;YAC3B,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,QAAQ,EAAE;YACtC,OAAO,CAAC,SAAS;SAClB,CACF,CAAA;IACH,CAAC,CAAA;IAED,MAAM,KAAK,GAAG,KAAK,IAAmB,EAAE;QACtC,MAAM,IAAI,CAAC,KAAK,CACd,uEAAuE,aAAa,QAAQ,CAC7F,CAAA;IACH,CAAC,CAAA;IAED,MAAM,KAAK,GAAG,KAAK,EAAE,MAAoB,EAA8B,EAAE;QACvE,MAAM,OAAO,GAAa,EAAE,CAAA;QAC5B,MAAM,MAAM,GAAc,EAAE,CAAA;QAC5B,MAAM,GAAG,GAAG,CAAC,MAAc,EAAE,KAAc,EAAQ,EAAE;YACnD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YAClB,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;QAC5D,CAAC,CAAA;QACD,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS;YAAE,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,OAAO,CAAC,CAAA;QACtE,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS;YAAE,GAAG,CAAC,uCAAuC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAA;QAChG,IAAI,MAAM,CAAC,KAAK;YAAE,GAAG,CAAC,qBAAqB,EAAE,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAA;QACxE,IAAI,MAAM,CAAC,KAAK;YAAE,GAAG,CAAC,qBAAqB,EAAE,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAA;QACxE,IAAI,MAAM,CAAC,QAAQ;YAAE,GAAG,CAAC,kBAAkB,EAAE,IAAI,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAA;QACpE,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QACxE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;QAClG,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAA;QAChF,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAC/B,+HAA+H,KAAK,sCAAsC,KAAK,WAAW,MAAM,EAAE,EAClM,MAAM,CACP,CAAA;QACD,OAAO,IAAyB,CAAA;IAClC,CAAC,CAAA;IAED,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;AAC1C,CAAC,CAAA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RelayerContext, RelayerSink } from '../types.js';
|
|
2
|
+
import type { Queryable } from '../stores/postgres.js';
|
|
3
|
+
export type PostgresSinkOptions<T> = {
|
|
4
|
+
pool: Queryable;
|
|
5
|
+
table: string;
|
|
6
|
+
/** Maps an item to a durable row: a stable key and a JSON payload. */
|
|
7
|
+
toRow: (item: T, context: RelayerContext) => {
|
|
8
|
+
key: string;
|
|
9
|
+
payload: unknown;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
/** A generic durable record sink: one upserted row per item, keyed and JSON-bodied. */
|
|
13
|
+
export declare const postgresSink: <T>(options: PostgresSinkOptions<T>) => RelayerSink<T> & {
|
|
14
|
+
migrate(): Promise<void>;
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=postgres.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/sinks/postgres.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAEtD,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI;IACnC,IAAI,EAAE,SAAS,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,sEAAsE;IACtE,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,cAAc,KAAK;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAA;CAC/E,CAAA;AAED,uFAAuF;AACvF,eAAO,MAAM,YAAY,GAAI,CAAC,EAC5B,SAAS,mBAAmB,CAAC,CAAC,CAAC,KAC9B,WAAW,CAAC,CAAC,CAAC,GAAG;IAAE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAqB7C,CAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** A generic durable record sink: one upserted row per item, keyed and JSON-bodied. */
|
|
2
|
+
export const postgresSink = (options) => {
|
|
3
|
+
const { pool, table, toRow } = options;
|
|
4
|
+
return {
|
|
5
|
+
migrate: async () => {
|
|
6
|
+
await pool.query(`CREATE TABLE IF NOT EXISTS ${table} (
|
|
7
|
+
key TEXT PRIMARY KEY,
|
|
8
|
+
payload JSONB,
|
|
9
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
10
|
+
)`);
|
|
11
|
+
},
|
|
12
|
+
record: async (item, context) => {
|
|
13
|
+
const row = toRow(item, context);
|
|
14
|
+
await pool.query(`INSERT INTO ${table} (key, payload) VALUES ($1, $2)
|
|
15
|
+
ON CONFLICT (key) DO UPDATE SET payload = $2`, [row.key, JSON.stringify(row.payload)]);
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=postgres.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../src/sinks/postgres.ts"],"names":[],"mappings":"AAUA,uFAAuF;AACvF,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,OAA+B,EACgB,EAAE;IACjD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,OAAO,CAAA;IACtC,OAAO;QACL,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,MAAM,IAAI,CAAC,KAAK,CACd,8BAA8B,KAAK;;;;UAIjC,CACH,CAAA;QACH,CAAC;QACD,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;YAC9B,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;YAChC,MAAM,IAAI,CAAC,KAAK,CACd,eAAe,KAAK;sDAC0B,EAC9C,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CACvC,CAAA;QACH,CAAC;KACF,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Address } from 'viem';
|
|
2
|
+
import type { RelayerSource } from '../types.js';
|
|
3
|
+
export type BridgeAffirmationSourceOptions = {
|
|
4
|
+
/** The Arbitrary Message Bridge contract address on the watched chain. */
|
|
5
|
+
bridgeAddress: Address;
|
|
6
|
+
/** How many blocks back from finalized to scan. Defaults to 1000. */
|
|
7
|
+
lookback?: bigint;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Watches an Arbitrary Message Bridge for completed affirmations and yields the
|
|
11
|
+
* recipient of the most recent bridged transfer (zero or one address per poll).
|
|
12
|
+
* Reads at the finalized block, so results are reorg-safe.
|
|
13
|
+
*/
|
|
14
|
+
export declare const bridgeAffirmationSource: (options: BridgeAffirmationSourceOptions) => RelayerSource<Address>;
|
|
15
|
+
//# sourceMappingURL=bridge-affirmation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-affirmation.d.ts","sourceRoot":"","sources":["../../src/sources/bridge-affirmation.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAMb,MAAM,MAAM,CAAA;AACb,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD,MAAM,MAAM,8BAA8B,GAAG;IAC3C,0EAA0E;IAC1E,aAAa,EAAE,OAAO,CAAA;IACtB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAWD;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,GAClC,SAAS,8BAA8B,KACtC,aAAa,CAAC,OAAO,CAsBvB,CAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { getAbiItem, parseAbi, parseEventLogs, } from 'viem';
|
|
2
|
+
const bridgeAbi = parseAbi([
|
|
3
|
+
'event AffirmationCompleted(address sender, address executor, bytes32 messageId, bool status)',
|
|
4
|
+
]);
|
|
5
|
+
const transferAbi = parseAbi([
|
|
6
|
+
'event Transfer(address indexed from, address indexed to, uint256 amount)',
|
|
7
|
+
'event Mint(address indexed to, uint256 amount)',
|
|
8
|
+
]);
|
|
9
|
+
/**
|
|
10
|
+
* Watches an Arbitrary Message Bridge for completed affirmations and yields the
|
|
11
|
+
* recipient of the most recent bridged transfer (zero or one address per poll).
|
|
12
|
+
* Reads at the finalized block, so results are reorg-safe.
|
|
13
|
+
*/
|
|
14
|
+
export const bridgeAffirmationSource = (options) => {
|
|
15
|
+
const lookback = options.lookback ?? 1000n;
|
|
16
|
+
return {
|
|
17
|
+
poll: async (context) => {
|
|
18
|
+
const provider = context.publicClient;
|
|
19
|
+
const finalized = await provider.getBlock({ blockTag: 'finalized' });
|
|
20
|
+
const logs = await provider.getLogs({
|
|
21
|
+
address: options.bridgeAddress,
|
|
22
|
+
event: getAbiItem({ abi: bridgeAbi, name: 'AffirmationCompleted' }),
|
|
23
|
+
fromBlock: finalized.number - lookback,
|
|
24
|
+
toBlock: finalized.number,
|
|
25
|
+
});
|
|
26
|
+
if (logs.length === 0)
|
|
27
|
+
return [];
|
|
28
|
+
const latestTx = logs[logs.length - 1].transactionHash;
|
|
29
|
+
const receipt = await provider.getTransactionReceipt({ hash: latestTx });
|
|
30
|
+
const transfers = parseEventLogs({ abi: transferAbi, logs: receipt.logs });
|
|
31
|
+
const recipient = transfers
|
|
32
|
+
.map((event) => ('to' in event.args ? event.args.to : undefined))
|
|
33
|
+
.find((address) => Boolean(address));
|
|
34
|
+
return recipient ? [recipient] : [];
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=bridge-affirmation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-affirmation.js","sourceRoot":"","sources":["../../src/sources/bridge-affirmation.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,UAAU,EACV,QAAQ,EACR,cAAc,GACf,MAAM,MAAM,CAAA;AAUb,MAAM,SAAS,GAAG,QAAQ,CAAC;IACzB,8FAA8F;CAC/F,CAAC,CAAA;AAEF,MAAM,WAAW,GAAG,QAAQ,CAAC;IAC3B,0EAA0E;IAC1E,gDAAgD;CACjD,CAAC,CAAA;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CACrC,OAAuC,EACf,EAAE;IAC1B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,KAAM,CAAA;IAC3C,OAAO;QACL,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YACtB,MAAM,QAAQ,GAAG,OAAO,CAAC,YAA4B,CAAA;YACrD,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC,CAAA;YACpE,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC;gBAClC,OAAO,EAAE,OAAO,CAAC,aAAa;gBAC9B,KAAK,EAAE,UAAU,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,sBAAsB,EAAE,CAAC;gBACnE,SAAS,EAAE,SAAS,CAAC,MAAM,GAAG,QAAQ;gBACtC,OAAO,EAAE,SAAS,CAAC,MAAM;aAC1B,CAAC,CAAA;YACF,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,EAAE,CAAA;YAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,eAAsB,CAAA;YAC7D,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,qBAAqB,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;YACxE,MAAM,SAAS,GAAG,cAAc,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAA;YAC1E,MAAM,SAAS,GAAG,SAAS;iBACxB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAE,KAAK,CAAC,IAAI,CAAC,EAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;iBAC7E,IAAI,CAAC,CAAC,OAAO,EAAsB,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAA;YAC1D,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QACrC,CAAC;KACF,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RelayerContext, RelayerSource } from '../types.js';
|
|
2
|
+
/** A source that produces exactly one fresh item per poll. For producers like spam writers. */
|
|
3
|
+
export declare const generatedSource: <T>(produce: (context: RelayerContext) => T | Promise<T>) => RelayerSource<T>;
|
|
4
|
+
//# sourceMappingURL=generated.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generated.d.ts","sourceRoot":"","sources":["../../src/sources/generated.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhE,+FAA+F;AAC/F,eAAO,MAAM,eAAe,GAAI,CAAC,EAC/B,SAAS,CAAC,OAAO,EAAE,cAAc,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,KACnD,aAAa,CAAC,CAAC,CAEhB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generated.js","sourceRoot":"","sources":["../../src/sources/generated.ts"],"names":[],"mappings":"AAEA,+FAA+F;AAC/F,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,OAAoD,EAClC,EAAE,CAAC,CAAC;IACtB,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CAClD,CAAC,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RPCMessage } from '@msgboard/sdk';
|
|
2
|
+
import type { RelayerSource } from '../types.js';
|
|
3
|
+
export type MsgboardContentSourceOptions = {
|
|
4
|
+
/** A category name (zero-padded to bytes32) or bytes32 hex. Omit to watch all categories. */
|
|
5
|
+
category?: string;
|
|
6
|
+
};
|
|
7
|
+
/** Polls msgboard content. With no category, flattens messages across every category. */
|
|
8
|
+
export declare const msgboardContentSource: (options?: MsgboardContentSourceOptions) => RelayerSource<RPCMessage>;
|
|
9
|
+
//# sourceMappingURL=msgboard-content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"msgboard-content.d.ts","sourceRoot":"","sources":["../../src/sources/msgboard-content.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD,MAAM,MAAM,4BAA4B,GAAG;IACzC,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAQD,yFAAyF;AACzF,eAAO,MAAM,qBAAqB,GAChC,UAAS,4BAAiC,KACzC,aAAa,CAAC,UAAU,CAS1B,CAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { stringToHex } from 'viem';
|
|
2
|
+
/** Normalizes a category name or hex into a bytes32 hex category. */
|
|
3
|
+
const toCategoryHex = (category) => {
|
|
4
|
+
if (category.startsWith('0x') && category.length === 66)
|
|
5
|
+
return category;
|
|
6
|
+
return stringToHex(category, { size: 32 });
|
|
7
|
+
};
|
|
8
|
+
/** Polls msgboard content. With no category, flattens messages across every category. */
|
|
9
|
+
export const msgboardContentSource = (options = {}) => {
|
|
10
|
+
const category = options.category ? toCategoryHex(options.category) : undefined;
|
|
11
|
+
return {
|
|
12
|
+
poll: async (context) => {
|
|
13
|
+
const content = await context.client.content(category ? { category } : {});
|
|
14
|
+
const groups = Object.values(content);
|
|
15
|
+
return groups.flat();
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=msgboard-content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"msgboard-content.js","sourceRoot":"","sources":["../../src/sources/msgboard-content.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,WAAW,EAAE,MAAM,MAAM,CAAA;AAS5C,qEAAqE;AACrE,MAAM,aAAa,GAAG,CAAC,QAAgB,EAAO,EAAE;IAC9C,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,EAAE;QAAE,OAAO,QAAe,CAAA;IAC/E,OAAO,WAAW,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAA;AAC5C,CAAC,CAAA;AAED,yFAAyF;AACzF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,UAAwC,EAAE,EACf,EAAE;IAC7B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAC/E,OAAO;QACL,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YACtB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;YAC1E,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;YACrC,OAAO,MAAM,CAAC,IAAI,EAAE,CAAA;QACtB,CAAC;KACF,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RelayerStore } from '../types.js';
|
|
2
|
+
export type MemoryTtlStoreOptions = {
|
|
3
|
+
/** How long a remembered key stays known, in milliseconds. */
|
|
4
|
+
ttlMs: number;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* An in-memory dedup store that forgets a key after `ttlMs`. Doubles as a
|
|
8
|
+
* per-key rate limiter. State is process-local and lost on restart.
|
|
9
|
+
*/
|
|
10
|
+
export declare const memoryTtlStore: <T>(options: MemoryTtlStoreOptions) => RelayerStore<T>;
|
|
11
|
+
//# sourceMappingURL=memory-ttl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-ttl.d.ts","sourceRoot":"","sources":["../../src/stores/memory-ttl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C,MAAM,MAAM,qBAAqB,GAAG;IAClC,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,SAAS,qBAAqB,KAAG,YAAY,CAAC,CAAC,CAoBhF,CAAA"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An in-memory dedup store that forgets a key after `ttlMs`. Doubles as a
|
|
3
|
+
* per-key rate limiter. State is process-local and lost on restart.
|
|
4
|
+
*/
|
|
5
|
+
export const memoryTtlStore = (options) => {
|
|
6
|
+
const seenAt = new Map();
|
|
7
|
+
const isLive = (timestamp) => Date.now() - timestamp <= options.ttlMs;
|
|
8
|
+
return {
|
|
9
|
+
has: async (key) => {
|
|
10
|
+
const at = seenAt.get(key);
|
|
11
|
+
if (at === undefined)
|
|
12
|
+
return false;
|
|
13
|
+
if (isLive(at))
|
|
14
|
+
return true;
|
|
15
|
+
seenAt.delete(key);
|
|
16
|
+
return false;
|
|
17
|
+
},
|
|
18
|
+
remember: async (key) => {
|
|
19
|
+
seenAt.set(key, Date.now());
|
|
20
|
+
},
|
|
21
|
+
prune: async () => {
|
|
22
|
+
for (const [key, at] of seenAt) {
|
|
23
|
+
if (!isLive(at))
|
|
24
|
+
seenAt.delete(key);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=memory-ttl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-ttl.js","sourceRoot":"","sources":["../../src/stores/memory-ttl.ts"],"names":[],"mappings":"AAOA;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAI,OAA8B,EAAmB,EAAE;IACnF,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAA;IACxC,MAAM,MAAM,GAAG,CAAC,SAAiB,EAAW,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,IAAI,OAAO,CAAC,KAAK,CAAA;IACtF,OAAO;QACL,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACjB,MAAM,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YAC1B,IAAI,EAAE,KAAK,SAAS;gBAAE,OAAO,KAAK,CAAA;YAClC,IAAI,MAAM,CAAC,EAAE,CAAC;gBAAE,OAAO,IAAI,CAAA;YAC3B,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YAClB,OAAO,KAAK,CAAA;QACd,CAAC;QACD,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACtB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;QAC7B,CAAC;QACD,KAAK,EAAE,KAAK,IAAI,EAAE;YAChB,KAAK,MAAM,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,MAAM,EAAE,CAAC;gBAC/B,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;oBAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YACrC,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noop.d.ts","sourceRoot":"","sources":["../../src/stores/noop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C,mFAAmF;AACnF,eAAO,MAAM,SAAS,GAAI,CAAC,OAAK,YAAY,CAAC,CAAC,CAG5C,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noop.js","sourceRoot":"","sources":["../../src/stores/noop.ts"],"names":[],"mappings":"AAEA,mFAAmF;AACnF,MAAM,CAAC,MAAM,SAAS,GAAG,GAAuB,EAAE,CAAC,CAAC;IAClD,GAAG,EAAE,KAAK,IAAI,EAAE,CAAC,KAAK;IACtB,QAAQ,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;CACzB,CAAC,CAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { RelayerStore } from '../types.js';
|
|
2
|
+
/** The minimal database surface the Postgres store needs (a `pg.Pool` satisfies it). */
|
|
3
|
+
export type Queryable = {
|
|
4
|
+
query(text: string, params?: unknown[]): Promise<{
|
|
5
|
+
rows: unknown[];
|
|
6
|
+
}>;
|
|
7
|
+
};
|
|
8
|
+
export type PostgresStoreOptions = {
|
|
9
|
+
pool: Queryable;
|
|
10
|
+
/** Table name for the dedup rows. */
|
|
11
|
+
table: string;
|
|
12
|
+
/** Rows older than this are removed by `prune`, in milliseconds. */
|
|
13
|
+
maxAgeMs: number;
|
|
14
|
+
};
|
|
15
|
+
/** A durable dedup store backed by a Postgres table. Call `migrate()` once at startup. */
|
|
16
|
+
export declare const postgresStore: <T>(options: PostgresStoreOptions) => RelayerStore<T> & {
|
|
17
|
+
migrate(): Promise<void>;
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=postgres.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/stores/postgres.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,YAAY,EAAE,MAAM,aAAa,CAAA;AAE7D,wFAAwF;AACxF,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC,CAAA;CACtE,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,SAAS,CAAA;IACf,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAA;IACb,oEAAoE;IACpE,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,0FAA0F;AAC1F,eAAO,MAAM,aAAa,GAAI,CAAC,EAC7B,SAAS,oBAAoB,KAC5B,YAAY,CAAC,CAAC,CAAC,GAAG;IAAE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CA8B9C,CAAA"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** A durable dedup store backed by a Postgres table. Call `migrate()` once at startup. */
|
|
2
|
+
export const postgresStore = (options) => {
|
|
3
|
+
const { pool, table } = options;
|
|
4
|
+
const maxAgeSeconds = Math.floor(options.maxAgeMs / 1000);
|
|
5
|
+
return {
|
|
6
|
+
migrate: async () => {
|
|
7
|
+
await pool.query(`CREATE TABLE IF NOT EXISTS ${table} (
|
|
8
|
+
key TEXT PRIMARY KEY,
|
|
9
|
+
ref TEXT,
|
|
10
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
11
|
+
)`);
|
|
12
|
+
},
|
|
13
|
+
has: async (key) => {
|
|
14
|
+
const { rows } = await pool.query(`SELECT key FROM ${table} WHERE key = $1 LIMIT 1`, [key]);
|
|
15
|
+
return rows.length > 0;
|
|
16
|
+
},
|
|
17
|
+
remember: async (key, result) => {
|
|
18
|
+
await pool.query(`INSERT INTO ${table} (key, ref) VALUES ($1, $2)
|
|
19
|
+
ON CONFLICT (key) DO UPDATE SET ref = $2`, [key, result.ref ?? null]);
|
|
20
|
+
},
|
|
21
|
+
prune: async () => {
|
|
22
|
+
await pool.query(`DELETE FROM ${table} WHERE created_at < now() - INTERVAL '${maxAgeSeconds} seconds'`);
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=postgres.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../src/stores/postgres.ts"],"names":[],"mappings":"AAeA,0FAA0F;AAC1F,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,OAA6B,EACmB,EAAE;IAClD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,OAAO,CAAA;IAC/B,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;IACzD,OAAO;QACL,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,MAAM,IAAI,CAAC,KAAK,CACd,8BAA8B,KAAK;;;;UAIjC,CACH,CAAA;QACH,CAAC;QACD,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACjB,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,mBAAmB,KAAK,yBAAyB,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;YAC3F,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAA;QACxB,CAAC;QACD,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,MAAoB,EAAE,EAAE;YAC5C,MAAM,IAAI,CAAC,KAAK,CACd,eAAe,KAAK;kDACsB,EAC1C,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,CAC1B,CAAA;QACH,CAAC;QACD,KAAK,EAAE,KAAK,IAAI,EAAE;YAChB,MAAM,IAAI,CAAC,KAAK,CACd,eAAe,KAAK,yCAAyC,aAAa,WAAW,CACtF,CAAA;QACH,CAAC;KACF,CAAA;AACH,CAAC,CAAA"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { MsgBoardClient } from '@msgboard/sdk';
|
|
2
|
+
import type { Chain, PublicClient } from 'viem';
|
|
3
|
+
import type { Logger } from './logger.js';
|
|
4
|
+
/** The two operating modes. `observe` never produces an outbound side effect. */
|
|
5
|
+
export type RelayerMode = 'observe' | 'live';
|
|
6
|
+
/** Identifies the msgboard node a relayer watches. */
|
|
7
|
+
export type RelayerNode = {
|
|
8
|
+
/** The JSON-RPC URL of the msgboard node. */
|
|
9
|
+
rpcUrl: string;
|
|
10
|
+
/** The chain id of the node (1, 369, or 943). */
|
|
11
|
+
chainId: number;
|
|
12
|
+
};
|
|
13
|
+
/** Everything a source, action, or sink may need at runtime. */
|
|
14
|
+
export type RelayerContext = {
|
|
15
|
+
node: RelayerNode;
|
|
16
|
+
mode: RelayerMode;
|
|
17
|
+
chain: Chain;
|
|
18
|
+
/** A viem public client over `node.rpcUrl`. */
|
|
19
|
+
publicClient: PublicClient;
|
|
20
|
+
/** A msgboard SDK client wrapping `publicClient`. */
|
|
21
|
+
client: MsgBoardClient;
|
|
22
|
+
logger: Logger;
|
|
23
|
+
};
|
|
24
|
+
/** The outcome of a live action. */
|
|
25
|
+
export type ActionResult = {
|
|
26
|
+
/** True if the action's effect succeeded. */
|
|
27
|
+
ok: boolean;
|
|
28
|
+
/** An identifying reference, e.g. a transaction hash or message hash. */
|
|
29
|
+
ref?: string;
|
|
30
|
+
/** Optional structured detail for logging or storage. */
|
|
31
|
+
meta?: Record<string, unknown>;
|
|
32
|
+
};
|
|
33
|
+
/** Reads the current batch of candidate items from the watched pool. */
|
|
34
|
+
export type RelayerSource<T> = {
|
|
35
|
+
poll(context: RelayerContext): Promise<readonly T[]>;
|
|
36
|
+
};
|
|
37
|
+
/** A side-effecting operation, split so observe mode can describe without doing. */
|
|
38
|
+
export type RelayerAction<T> = {
|
|
39
|
+
/** Pure description of the intended effect; used for observe-mode logging. */
|
|
40
|
+
describe(item: T, context: RelayerContext): string;
|
|
41
|
+
/** The real outbound effect; only ever called in live mode. */
|
|
42
|
+
execute(item: T, context: RelayerContext): Promise<ActionResult>;
|
|
43
|
+
};
|
|
44
|
+
/** Action-level idempotency: "have I already acted on this?". Short retention. */
|
|
45
|
+
export type RelayerStore<_T> = {
|
|
46
|
+
has(key: string): Promise<boolean>;
|
|
47
|
+
remember(key: string, result: ActionResult): Promise<void>;
|
|
48
|
+
prune?(): Promise<void>;
|
|
49
|
+
};
|
|
50
|
+
/** Unconditional recording for history/observability. Long retention. Runs in BOTH modes. */
|
|
51
|
+
export type RelayerSink<T> = {
|
|
52
|
+
record(item: T, context: RelayerContext): Promise<void>;
|
|
53
|
+
prune?(): Promise<void>;
|
|
54
|
+
};
|
|
55
|
+
/** Derives a stable dedup key for an item. */
|
|
56
|
+
export type RelayerKey<T> = (item: T) => string;
|
|
57
|
+
/** Decides whether a candidate should be acted on, beyond dedup. */
|
|
58
|
+
export type RelayerCondition<T> = (item: T, context: RelayerContext) => boolean | Promise<boolean>;
|
|
59
|
+
/** Construction options for a Relayer. */
|
|
60
|
+
export type RelayerConfig<T> = {
|
|
61
|
+
node: RelayerNode;
|
|
62
|
+
/** Safety switch. Defaults to 'observe' — performs no outbound side effect. */
|
|
63
|
+
mode?: RelayerMode;
|
|
64
|
+
/** Poll cadence in milliseconds. Defaults to 30_000. */
|
|
65
|
+
intervalMs?: number;
|
|
66
|
+
source: RelayerSource<T>;
|
|
67
|
+
action: RelayerAction<T>;
|
|
68
|
+
key: RelayerKey<T>;
|
|
69
|
+
/** Action-level dedup. Defaults to an in-memory time-to-live store. */
|
|
70
|
+
store?: RelayerStore<T>;
|
|
71
|
+
/** Historical recording; runs in observe and live modes. */
|
|
72
|
+
sink?: RelayerSink<T>;
|
|
73
|
+
condition?: RelayerCondition<T>;
|
|
74
|
+
logger?: Logger;
|
|
75
|
+
/** Run `store.prune` / `sink.prune` every N ticks. Defaults to 30. */
|
|
76
|
+
pruneEveryTicks?: number;
|
|
77
|
+
};
|
|
78
|
+
/** What happened during one tick — returned by `runOnce()` for tests and one-shots. */
|
|
79
|
+
export type TickReport = {
|
|
80
|
+
/** Total items polled from the source. */
|
|
81
|
+
polled: number;
|
|
82
|
+
/** Items recorded to the sink. */
|
|
83
|
+
recorded: number;
|
|
84
|
+
/** Items that passed the condition and were not deduped. */
|
|
85
|
+
eligible: number;
|
|
86
|
+
/** Items whose action executed (live mode only). */
|
|
87
|
+
executed: number;
|
|
88
|
+
/** Items whose action was only described (observe mode). */
|
|
89
|
+
described: number;
|
|
90
|
+
/** Items skipped by the dedup store. */
|
|
91
|
+
deduped: number;
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@msgboard/relayer",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Controllable, safe-by-default pool-watcher relayer for the msgboard board",
|
|
5
|
+
"repository": "github:valve-tech/msgboard",
|
|
6
|
+
"author": "MsgBoard",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"module": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"import": "./dist/index.js",
|
|
18
|
+
"require": "./dist/index.js",
|
|
19
|
+
"default": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"keywords": ["msgboard", "relayer", "pool-watcher", "pulsechain"],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"prebuild": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
26
|
+
"build": "tsc",
|
|
27
|
+
"watch": "tsc -w",
|
|
28
|
+
"test": "vitest run",
|
|
29
|
+
"test:watch": "vitest",
|
|
30
|
+
"lint": "prettier --check ."
|
|
31
|
+
},
|
|
32
|
+
"files": ["dist/"],
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@msgboard/sdk": "^0.0.28",
|
|
35
|
+
"viem": "^2.25.0"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"pg": "^8.14.1"
|
|
39
|
+
},
|
|
40
|
+
"peerDependenciesMeta": {
|
|
41
|
+
"pg": { "optional": true }
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/pg": "^8.11.11",
|
|
45
|
+
"typescript": "^5.8.2",
|
|
46
|
+
"vitest": "^3.1.1"
|
|
47
|
+
}
|
|
48
|
+
}
|