@verify-network/protocol 0.0.0-stage → 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Verify Network
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,148 @@
1
- # Temporary Holding Version
1
+ # @verify-network/protocol
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The off-chain half of the Verify Network VRF protocol. Use it to verify a fulfillment the router delivered, to sign and send a direct request to a node, and to compute request ids, fees, and the typed data the router checks. Every function mirrors the router byte for byte.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ npm install @verify-network/protocol viem effect
9
+ ```
10
+
11
+ `viem` (`^2.56.3`) and `effect` (`^4.0.0`) are peer dependencies. Node 22 or later.
12
+
13
+ ## Entry points
14
+
15
+ | Import | Contents |
16
+ | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
17
+ | `@verify-network/protocol` | Everything below, plus the direct-request client (`DirectClient`), the router quote (`Quote`), the router watch, and the schemas |
18
+ | `@verify-network/protocol/ecvrf` | ECVRF-SECP256K1-SHA256-TAI (RFC 9381): `prove`, `verify`, `deriveRandomness`, `proofCommitment`, `isSentinel`, `isFulfillable` |
19
+ | `@verify-network/protocol/event` | The router's `RandomnessRequested` and `RandomnessFulfilled` events and their decoders |
20
+ | `@verify-network/protocol/fee` | The reservation, the charge, and the settlement, as the router computes them |
21
+ | `@verify-network/protocol/fulfillment` | The EIP-712 typed data an enclave signs, and its digest |
22
+ | `@verify-network/protocol/request` | `requestId`, the stale delay, and the callback data limit |
23
+
24
+ The subpath entries do not load Effect. Import from them in a browser bundle. The root entry loads Effect for its schemas, the quote, the router watch, and the direct-request client.
25
+
26
+ ## Verify a fulfillment
27
+
28
+ A fulfillment is valid when three things hold. The enclave signature recovers to the request's fulfiller. The proof verifies under that node's key for the request id. The randomness is the one the proof derives.
29
+
30
+ In a browser, import the same names from `/ecvrf`, `/event`, and `/fulfillment` instead of the root.
31
+
32
+ ```ts
33
+ import {
34
+ decodeRandomnessFulfilled,
35
+ decodeRandomnessRequested,
36
+ deriveRandomness,
37
+ fulfillmentDigest,
38
+ isFulfillable,
39
+ isSentinel,
40
+ proofCommitment,
41
+ randomnessFulfilledEvent,
42
+ randomnessRequestedEvent,
43
+ verify,
44
+ } from "@verify-network/protocol";
45
+ import {
46
+ isAddressEqual,
47
+ parseAbi,
48
+ recoverAddress,
49
+ type Address,
50
+ type Hex,
51
+ type PublicClient,
52
+ } from "viem";
53
+
54
+ const nodes = parseAbi(["function publicKeyOf(address node) view returns (uint256[2])"]);
55
+
56
+ export const checkFulfillment = async ({
57
+ publicClient,
58
+ router,
59
+ requestId,
60
+ fromBlock,
61
+ }: {
62
+ publicClient: PublicClient;
63
+ router: Address;
64
+ requestId: Hex;
65
+ fromBlock: bigint;
66
+ }) => {
67
+ const [requested] = await publicClient.getLogs({
68
+ address: router,
69
+ event: randomnessRequestedEvent,
70
+ args: { requestId },
71
+ fromBlock,
72
+ });
73
+ const [fulfilled] = await publicClient.getLogs({
74
+ address: router,
75
+ event: randomnessFulfilledEvent,
76
+ args: { requestId },
77
+ fromBlock,
78
+ });
79
+ if (requested === undefined || fulfilled === undefined) throw new Error("not fulfilled yet");
80
+
81
+ const { request } = decodeRandomnessRequested(requested);
82
+ const { proof, randomness, enclaveSignature } = decodeRandomnessFulfilled(fulfilled);
83
+ const chainId = await publicClient.getChainId();
84
+ const publicKey = await publicClient.readContract({
85
+ address: router,
86
+ abi: nodes,
87
+ functionName: "publicKeyOf",
88
+ args: [request.fulfiller],
89
+ });
90
+
91
+ const signer = await recoverAddress({
92
+ hash: fulfillmentDigest({
93
+ chainId,
94
+ router,
95
+ requestId,
96
+ proofCommitment: proofCommitment(proof),
97
+ }),
98
+ signature: enclaveSignature,
99
+ });
100
+ const proven = isSentinel(proof)
101
+ ? !isFulfillable(publicKey, requestId)
102
+ : verify({ publicKey, proof, alpha: requestId });
103
+ return (
104
+ isAddressEqual(signer, request.fulfiller) && proven && deriveRandomness(proof) === randomness
105
+ );
106
+ };
107
+ ```
108
+
109
+ A fulfillment that fails this check is evidence for `challengeFulfillment(request, proof, enclaveSignature)` on the router. Anyone may send it while the node still holds stake.
110
+
111
+ ## Send a direct request
112
+
113
+ A direct request skips the consumer's own transaction. A signer signs an authorization, posts it to one node, and the node lands the request and its fulfillment in one transaction. The signer's balance on the router pays.
114
+
115
+ `DirectClient` finds the fastest serving node on the router, signs, posts, and waits until the fulfillment lands. Its methods return Effects. `run` turns one into a promise, so a caller needs no Effect code.
116
+
117
+ ```ts
118
+ import { DirectClient, run } from "@verify-network/protocol";
119
+ import { createPublicClient, http, toHex, type Chain } from "viem";
120
+ import { privateKeyToAccount } from "viem/accounts";
121
+ import { base } from "viem/chains";
122
+
123
+ // Widened to `Chain`: `DirectClient` takes a `PublicClient` of any chain.
124
+ const publicClient = createPublicClient({ chain: base as Chain, transport: http() });
125
+ const client = await run(
126
+ DirectClient.connect({
127
+ publicClient,
128
+ chainId: base.id,
129
+ router: "0x…",
130
+ account: privateKeyToAccount("0x…"), // a session key or a backend key, funded on the router
131
+ }),
132
+ );
133
+
134
+ const result = await run(client.request({ consumer: "0x…", callbackData: toHex("round-1") }));
135
+ if (result._tag === "Landed") {
136
+ console.log(result.requestId, result.randomness, result.hash);
137
+ } else {
138
+ // NotLanded: the deadline passed on the chain's clock and nothing landed. Fall back to an on-chain request.
139
+ }
140
+ ```
141
+
142
+ The consumer contract implements `IDirectVrfReceiver` from `@verify-network/contracts`. The signer is passed to its callback, so the consumer decides which signers it accepts.
143
+
144
+ To sign an authorization yourself, use `signDirectRequest` and `directRequestId`. `validateEndpoint` checks an endpoint read from the chain before you connect to it.
145
+
146
+ ## License
147
+
148
+ MIT
@@ -0,0 +1,9 @@
1
+ import { Address } from "viem";
2
+ //#region src/domain.d.ts
3
+ /** One router deployment: what request ids and fulfillment digests are bound to. */
4
+ type RouterDomain = {
5
+ readonly chainId: bigint | number;
6
+ readonly router: Address;
7
+ };
8
+ //#endregion
9
+ export { RouterDomain as t };
@@ -0,0 +1,69 @@
1
+ import { Hex } from "viem";
2
+ //#region src/ecvrf.d.ts
3
+ /**
4
+ * ECVRF-SECP256K1-SHA256-TAI, the RFC 9381 cipher suite the router's `ECVRFVerifier` checks,
5
+ * on `@noble/curves` and `@noble/hashes`. Hash-to-curve, the challenge, the proof encoding,
6
+ * and the randomness derivation mirror `VRF.sol` so the enclave and the router agree byte for
7
+ * byte; the nonce is RFC 6979, as RFC 9381 prescribes for the suite. Every function is pure and
8
+ * synchronous, so it runs unchanged on a `node:worker_threads` worker, under Node and Bun.
9
+ */
10
+ /** A proof as the router takes it: `[gammaX, gammaY, c, s]`. */
11
+ export type Proof = readonly [gammaX: bigint, gammaY: bigint, c: bigint, s: bigint];
12
+ /** An enclave key as the router stores it: the uncompressed point's `[x, y]`. */
13
+ export type PublicKey = readonly [x: bigint, y: bigint];
14
+ /**
15
+ * `VRF.HASH_TO_CURVE_MAX_ITERATIONS`: the try-and-increment attempts the on-chain verifier makes
16
+ * before declaring an alpha unfulfillable (probability 2^-50 per request).
17
+ */
18
+ export declare const HASH_TO_CURVE_MAX_ITERATIONS = 50;
19
+ /** What a fulfiller submits for an alpha that fails the hash-to-curve check. */
20
+ export declare const SENTINEL_PROOF: Proof;
21
+ /** The router recognises the sentinel by its gamma alone. */
22
+ export declare const isSentinel: ([gammaX, gammaY]: Proof) => boolean;
23
+ /**
24
+ * The `[x, y]` point the router stores, from the key viem derives (`0x04 || x || y`). Throws
25
+ * when the key is not on the curve.
26
+ */
27
+ export declare const publicKeyPoint: (publicKey: Hex) => PublicKey;
28
+ /**
29
+ * Proves `alpha` (the request id) under the enclave key. Deterministic: RFC 6979 fixes the nonce.
30
+ * Throws when `alpha` has no curve point within the on-chain cap; the fulfiller submits the
31
+ * sentinel for such a request (see `isFulfillable`).
32
+ */
33
+ export declare const prove: ({ privateKey, alpha }: {
34
+ privateKey: Hex;
35
+ alpha: Hex;
36
+ }) => Proof;
37
+ /**
38
+ * Whether `proof` proves `alpha` under `publicKey`, as `ECVRFVerifier.verify` decides: gamma must
39
+ * be a curve point with the `y` as submitted (the router multiplies it as given), `c` must fit 16
40
+ * bytes, `s` must be below the group order, and `alpha` must hash to a point within the cap.
41
+ */
42
+ export declare const verify: ({ publicKey, proof, alpha }: {
43
+ publicKey: PublicKey;
44
+ proof: Proof;
45
+ alpha: Hex;
46
+ }) => boolean;
47
+ /**
48
+ * The 81-byte RFC 9381 proof string: compressed gamma, `c` in 16 bytes, `s` in 32. Throws when
49
+ * a scalar does not fit.
50
+ */
51
+ export declare const encodeProof: ([gammaX, gammaY, c, s]: Proof) => Hex;
52
+ /** The router's four limbs from an RFC 9381 proof string. Throws when the string is malformed. */
53
+ export declare const decodeProof: (encoded: Hex) => Proof;
54
+ /**
55
+ * The randomness a proof yields, as `VRF.gammaToHash` derives it: sha256 of the suite byte,
56
+ * `0x03`, the compressed gamma, and a zero byte. Zero for the sentinel, as the router emits it.
57
+ */
58
+ export declare const deriveRandomness: (proof: Proof) => Hex;
59
+ /** What the enclave signs over: `keccak256(abi.encode(proof))` in the router. */
60
+ export declare const proofCommitment: (proof: Proof) => Hex;
61
+ /**
62
+ * The try-and-increment attempts `VRF.tryHashToPoint` needs to hash `alpha` to a point for
63
+ * `publicKey`, or `null` when none is found within `maxIterations`: such a request cannot be
64
+ * proven and is closed with the sentinel.
65
+ */
66
+ export declare const hashToCurveIterations: (publicKey: PublicKey, alpha: Hex, maxIterations?: number) => number | null;
67
+ /** Whether the on-chain verifier can hash `alpha` to a point for `publicKey`. */
68
+ export declare const isFulfillable: (publicKey: PublicKey, alpha: Hex, maxIterations?: number) => boolean;
69
+ //#endregion
package/dist/ecvrf.js ADDED
@@ -0,0 +1,174 @@
1
+ import { bytesToHex, encodeAbiParameters, hexToBytes, keccak256, zeroHash } from "viem";
2
+ import { secp256k1 } from "@noble/curves/secp256k1.js";
3
+ import { bytesToNumberBE, concatBytes, createHmacDrbg, numberToBytesBE } from "@noble/curves/utils.js";
4
+ import { hmac } from "@noble/hashes/hmac.js";
5
+ import { sha256 } from "@noble/hashes/sha2.js";
6
+ //#region src/ecvrf.ts
7
+ /**
8
+ * `VRF.HASH_TO_CURVE_MAX_ITERATIONS`: the try-and-increment attempts the on-chain verifier makes
9
+ * before declaring an alpha unfulfillable (probability 2^-50 per request).
10
+ */
11
+ const HASH_TO_CURVE_MAX_ITERATIONS = 50;
12
+ /** What a fulfiller submits for an alpha that fails the hash-to-curve check. */
13
+ const SENTINEL_PROOF = [
14
+ 0n,
15
+ 0n,
16
+ 0n,
17
+ 0n
18
+ ];
19
+ /** The router recognises the sentinel by its gamma alone. */
20
+ const isSentinel = ([gammaX, gammaY]) => gammaX === 0n && gammaY === 0n;
21
+ const { Point } = secp256k1;
22
+ const G = Point.BASE;
23
+ /** The group order, RFC 9381's `q`. */
24
+ const Q = Point.Fn.ORDER;
25
+ /** The suite string; the byte after it separates the three hashes the suite takes. */
26
+ const SUITE = 254;
27
+ /** RFC 9381 sizes for the suite: the challenge `c` is 16 bytes, the proof string 33 + 16 + 32. */
28
+ const C_BYTES = 16;
29
+ const C_LIMIT = 1n << 128n;
30
+ const PROOF_BYTES = 81;
31
+ /** The curve point at `[x, y]`. Throws when there is none, including for the sentinel. */
32
+ const point = ([x, y]) => {
33
+ const p = Point.fromAffine({
34
+ x,
35
+ y
36
+ });
37
+ p.assertValidity();
38
+ return p;
39
+ };
40
+ const limbs = (p) => {
41
+ const { x, y } = p.toAffine();
42
+ return [x, y];
43
+ };
44
+ /** `VRF.encodePoint`: SEC 1 compressed, the parity prefix then `x`, over the limbs as given. */
45
+ const encodePoint = (x, y) => concatBytes(Uint8Array.of(y % 2n === 0n ? 2 : 3), numberToBytesBE(x, 32));
46
+ const compressed = (p) => encodePoint(...limbs(p));
47
+ /**
48
+ * The `[x, y]` point the router stores, from the key viem derives (`0x04 || x || y`). Throws
49
+ * when the key is not on the curve.
50
+ */
51
+ const publicKeyPoint = (publicKey) => limbs(Point.fromBytes(hexToBytes(publicKey)));
52
+ /**
53
+ * `VRF.tryHashToPoint`: try-and-increment over
54
+ * `sha256(0xfe || 0x01 || compressed key || alpha || counter || 0x00)`, reading each digest as
55
+ * the `x` of an even-`y` point; a digest of zero or at least the field prime yields none, as
56
+ * `EllipticCurve.isOnCurve` decides. The point and the attempts it took, or `null` past the cap.
57
+ */
58
+ const hashToCurve = (publicKey, alpha, maxIterations) => {
59
+ const prefix = concatBytes(Uint8Array.of(SUITE, 1), encodePoint(...publicKey), alpha);
60
+ for (let counter = 0; counter < maxIterations; counter++) {
61
+ const digest = sha256(concatBytes(prefix, Uint8Array.of(counter, 0)));
62
+ try {
63
+ return {
64
+ point: Point.fromBytes(concatBytes(Uint8Array.of(2), digest)),
65
+ iterations: counter + 1
66
+ };
67
+ } catch {}
68
+ }
69
+ return null;
70
+ };
71
+ /**
72
+ * `VRF.hashPoints`: the first 16 bytes of `sha256(0xfe || 0x02 || H || Gamma || U || V || 0x00)`,
73
+ * the points compressed.
74
+ */
75
+ const challenge = (points) => bytesToNumberBE(sha256(concatBytes(Uint8Array.of(SUITE, 2), ...points.map(compressed), Uint8Array.of(0))).subarray(0, C_BYTES));
76
+ const drbg = createHmacDrbg(32, 32, (key, message) => hmac(sha256, key, message));
77
+ /**
78
+ * RFC 9381 §5.4.2.1: the nonce RFC 6979 §3.2 derives from the secret scalar and `sha256` of the
79
+ * compressed `H`, so a proof is a function of the key and alpha alone.
80
+ */
81
+ const nonce = (x, h) => {
82
+ const h1 = bytesToNumberBE(sha256(compressed(h))) % Q;
83
+ return drbg(concatBytes(numberToBytesBE(x, 32), numberToBytesBE(h1, 32)), (candidate) => {
84
+ const k = bytesToNumberBE(candidate);
85
+ return k > 0n && k < Q ? k : void 0;
86
+ });
87
+ };
88
+ /**
89
+ * Proves `alpha` (the request id) under the enclave key. Deterministic: RFC 6979 fixes the nonce.
90
+ * Throws when `alpha` has no curve point within the on-chain cap; the fulfiller submits the
91
+ * sentinel for such a request (see `isFulfillable`).
92
+ */
93
+ const prove = ({ privateKey, alpha }) => {
94
+ const x = bytesToNumberBE(hexToBytes(privateKey));
95
+ const publicKey = G.multiply(x);
96
+ const hashed = hashToCurve(limbs(publicKey), hexToBytes(alpha), 50);
97
+ if (hashed === null) throw new Error("ECVRF alpha has no curve point within the on-chain cap");
98
+ const h = hashed.point;
99
+ const gamma = h.multiply(x);
100
+ const k = nonce(x, h);
101
+ const c = challenge([
102
+ h,
103
+ gamma,
104
+ G.multiply(k),
105
+ h.multiply(k)
106
+ ]);
107
+ return [
108
+ ...limbs(gamma),
109
+ c,
110
+ (k + c * x) % Q
111
+ ];
112
+ };
113
+ /**
114
+ * Whether `proof` proves `alpha` under `publicKey`, as `ECVRFVerifier.verify` decides: gamma must
115
+ * be a curve point with the `y` as submitted (the router multiplies it as given), `c` must fit 16
116
+ * bytes, `s` must be below the group order, and `alpha` must hash to a point within the cap.
117
+ */
118
+ const verify = ({ publicKey, proof, alpha }) => {
119
+ const [gammaX, gammaY, c, s] = proof;
120
+ if (c >= C_LIMIT || s >= Q) return false;
121
+ try {
122
+ const y = point(publicKey);
123
+ const gamma = point([gammaX, gammaY]);
124
+ const hashed = hashToCurve(publicKey, hexToBytes(alpha), 50);
125
+ if (hashed === null) return false;
126
+ const h = hashed.point;
127
+ const u = G.mulAddUnsafe(s, y.negate(), c);
128
+ const v = h.mulAddUnsafe(s, gamma.negate(), c);
129
+ return challenge([
130
+ h,
131
+ gamma,
132
+ u,
133
+ v
134
+ ]) === c;
135
+ } catch {
136
+ return false;
137
+ }
138
+ };
139
+ /**
140
+ * The 81-byte RFC 9381 proof string: compressed gamma, `c` in 16 bytes, `s` in 32. Throws when
141
+ * a scalar does not fit.
142
+ */
143
+ const encodeProof = ([gammaX, gammaY, c, s]) => bytesToHex(concatBytes(encodePoint(gammaX, gammaY), numberToBytesBE(c, C_BYTES), numberToBytesBE(s, 32)));
144
+ /** The router's four limbs from an RFC 9381 proof string. Throws when the string is malformed. */
145
+ const decodeProof = (encoded) => {
146
+ const proof = hexToBytes(encoded);
147
+ if (proof.length !== PROOF_BYTES) throw new Error("ECVRF proof must be 81 bytes");
148
+ const gamma = Point.fromBytes(proof.subarray(0, 33));
149
+ const c = bytesToNumberBE(proof.subarray(33, 49));
150
+ const s = bytesToNumberBE(proof.subarray(49));
151
+ if (s >= Q) throw new Error("ECVRF proof scalar s must be below the group order");
152
+ return [
153
+ ...limbs(gamma),
154
+ c,
155
+ s
156
+ ];
157
+ };
158
+ /**
159
+ * The randomness a proof yields, as `VRF.gammaToHash` derives it: sha256 of the suite byte,
160
+ * `0x03`, the compressed gamma, and a zero byte. Zero for the sentinel, as the router emits it.
161
+ */
162
+ const deriveRandomness = (proof) => isSentinel(proof) ? zeroHash : bytesToHex(sha256(concatBytes(Uint8Array.of(SUITE, 3), encodePoint(proof[0], proof[1]), Uint8Array.of(0))));
163
+ /** What the enclave signs over: `keccak256(abi.encode(proof))` in the router. */
164
+ const proofCommitment = (proof) => keccak256(encodeAbiParameters([{ type: "uint256[4]" }], [proof]));
165
+ /**
166
+ * The try-and-increment attempts `VRF.tryHashToPoint` needs to hash `alpha` to a point for
167
+ * `publicKey`, or `null` when none is found within `maxIterations`: such a request cannot be
168
+ * proven and is closed with the sentinel.
169
+ */
170
+ const hashToCurveIterations = (publicKey, alpha, maxIterations = 50) => hashToCurve(publicKey, hexToBytes(alpha), maxIterations)?.iterations ?? null;
171
+ /** Whether the on-chain verifier can hash `alpha` to a point for `publicKey`. */
172
+ const isFulfillable = (publicKey, alpha, maxIterations = 50) => hashToCurveIterations(publicKey, alpha, maxIterations) !== null;
173
+ //#endregion
174
+ export { HASH_TO_CURVE_MAX_ITERATIONS, SENTINEL_PROOF, decodeProof, deriveRandomness, encodeProof, hashToCurveIterations, isFulfillable, isSentinel, proofCommitment, prove, publicKeyPoint, verify };
@@ -0,0 +1,101 @@
1
+ import { Proof } from "./ecvrf.js";
2
+ import { Request } from "./request.js";
3
+ import { Hex, Log } from "viem";
4
+ //#region src/event.d.ts
5
+ /**
6
+ * `VrfFacet.RandomnessRequested`: the id, consumer, and fulfiller indexed for filtering, the
7
+ * whole request as data so anyone can recompute the id and the fulfiller can echo it. Spelled
8
+ * out here because the protocol package does not depend on the contracts package; the
9
+ * contracts package's `test/Vrf.test.ts` decodes a real log through it and catches drift.
10
+ */
11
+ export declare const randomnessRequestedEvent: {
12
+ readonly name: "RandomnessRequested";
13
+ readonly type: 'event';
14
+ readonly inputs: readonly [{
15
+ readonly type: "bytes32";
16
+ readonly name: "requestId";
17
+ readonly indexed: true;
18
+ }, {
19
+ readonly type: "address";
20
+ readonly name: "consumer";
21
+ readonly indexed: true;
22
+ }, {
23
+ readonly type: "address";
24
+ readonly name: "fulfiller";
25
+ readonly indexed: true;
26
+ }, {
27
+ readonly type: 'tuple';
28
+ readonly components: readonly [{
29
+ readonly type: "address";
30
+ readonly name: "consumer";
31
+ }, {
32
+ readonly type: "uint64";
33
+ readonly name: "sequence";
34
+ }, {
35
+ readonly type: "address";
36
+ readonly name: "fulfiller";
37
+ }, {
38
+ readonly type: "uint40";
39
+ readonly name: "assignedAt";
40
+ }, {
41
+ readonly type: "uint96";
42
+ readonly name: "fee";
43
+ }, {
44
+ readonly type: "uint96";
45
+ readonly name: "gasPrice";
46
+ }, {
47
+ readonly type: "uint32";
48
+ readonly name: "callbackGasLimit";
49
+ }, {
50
+ readonly type: "bytes";
51
+ readonly name: "callbackData";
52
+ }];
53
+ readonly name: "request";
54
+ }];
55
+ };
56
+ /**
57
+ * `VrfFacet.RandomnessFulfilled`: the id indexed, the randomness and the evidence (the proof and
58
+ * the enclave signature, which a challenge takes) as data. Spelled out here for the same reason
59
+ * as the request event, and held to the real log by the same test.
60
+ */
61
+ export declare const randomnessFulfilledEvent: {
62
+ readonly name: "RandomnessFulfilled";
63
+ readonly type: 'event';
64
+ readonly inputs: readonly [{
65
+ readonly type: "bytes32";
66
+ readonly name: "requestId";
67
+ readonly indexed: true;
68
+ }, {
69
+ readonly type: "bytes32";
70
+ readonly name: "randomness";
71
+ }, {
72
+ readonly type: "uint256[4]";
73
+ readonly name: "proof";
74
+ }, {
75
+ readonly type: "bytes";
76
+ readonly name: "enclaveSignature";
77
+ }];
78
+ };
79
+ /** The part of a log the decoder reads, as every viem client returns it. */
80
+ export type EventLog = Pick<Log, "data" | "topics">;
81
+ /**
82
+ * Decodes a `RandomnessRequested` log into its id and a `Request` in the schema's shape, so
83
+ * the decoded request passes to `requestId` or a typed router call unchanged. Throws on a log
84
+ * of another event.
85
+ */
86
+ export declare const decodeRandomnessRequested: (log: EventLog) => {
87
+ readonly requestId: Hex;
88
+ readonly request: Request;
89
+ };
90
+ /**
91
+ * Decodes a `RandomnessFulfilled` log into the id it closed, the randomness delivered, and the
92
+ * evidence (the proof and the enclave signature) a challenge or a verifier takes. Throws on a
93
+ * log of another event.
94
+ */
95
+ export declare const decodeRandomnessFulfilled: (log: EventLog) => {
96
+ readonly requestId: Hex;
97
+ readonly randomness: Hex;
98
+ readonly proof: Proof;
99
+ readonly enclaveSignature: Hex;
100
+ };
101
+ //#endregion
package/dist/event.js ADDED
@@ -0,0 +1,53 @@
1
+ import { decodeEventLog, parseAbiItem } from "viem";
2
+ //#region src/event.ts
3
+ /**
4
+ * `VrfFacet.RandomnessRequested`: the id, consumer, and fulfiller indexed for filtering, the
5
+ * whole request as data so anyone can recompute the id and the fulfiller can echo it. Spelled
6
+ * out here because the protocol package does not depend on the contracts package; the
7
+ * contracts package's `test/Vrf.test.ts` decodes a real log through it and catches drift.
8
+ */
9
+ const randomnessRequestedEvent = parseAbiItem("event RandomnessRequested(bytes32 indexed requestId, address indexed consumer, address indexed fulfiller, (address consumer, uint64 sequence, address fulfiller, uint40 assignedAt, uint96 fee, uint96 gasPrice, uint32 callbackGasLimit, bytes callbackData) request)");
10
+ /**
11
+ * `VrfFacet.RandomnessFulfilled`: the id indexed, the randomness and the evidence (the proof and
12
+ * the enclave signature, which a challenge takes) as data. Spelled out here for the same reason
13
+ * as the request event, and held to the real log by the same test.
14
+ */
15
+ const randomnessFulfilledEvent = parseAbiItem("event RandomnessFulfilled(bytes32 indexed requestId, bytes32 randomness, uint256[4] proof, bytes enclaveSignature)");
16
+ /**
17
+ * Decodes a `RandomnessRequested` log into its id and a `Request` in the schema's shape, so
18
+ * the decoded request passes to `requestId` or a typed router call unchanged. Throws on a log
19
+ * of another event.
20
+ */
21
+ const decodeRandomnessRequested = (log) => {
22
+ const { args } = decodeEventLog({
23
+ abi: [randomnessRequestedEvent],
24
+ eventName: "RandomnessRequested",
25
+ data: log.data,
26
+ topics: log.topics
27
+ });
28
+ return {
29
+ requestId: args.requestId,
30
+ request: args.request
31
+ };
32
+ };
33
+ /**
34
+ * Decodes a `RandomnessFulfilled` log into the id it closed, the randomness delivered, and the
35
+ * evidence (the proof and the enclave signature) a challenge or a verifier takes. Throws on a
36
+ * log of another event.
37
+ */
38
+ const decodeRandomnessFulfilled = (log) => {
39
+ const { args } = decodeEventLog({
40
+ abi: [randomnessFulfilledEvent],
41
+ eventName: "RandomnessFulfilled",
42
+ data: log.data,
43
+ topics: log.topics
44
+ });
45
+ return {
46
+ requestId: args.requestId,
47
+ randomness: args.randomness,
48
+ proof: args.proof,
49
+ enclaveSignature: args.enclaveSignature
50
+ };
51
+ };
52
+ //#endregion
53
+ export { decodeRandomnessFulfilled, decodeRandomnessRequested, randomnessFulfilledEvent, randomnessRequestedEvent };