@rateloop/contracts 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/README.md +27 -0
  2. package/dist/cjs/abis/AdvisoryVoteRecorderAbi.js +935 -0
  3. package/dist/cjs/abis/CategoryRegistryAbi.js +518 -0
  4. package/dist/cjs/abis/ClusterPayoutOracleAbi.js +2580 -0
  5. package/dist/cjs/abis/ConfidentialityEscrowAbi.js +1619 -0
  6. package/dist/cjs/abis/ContentRegistryAbi.js +2606 -0
  7. package/dist/cjs/abis/FeedbackBonusEscrowAbi.js +1496 -0
  8. package/dist/cjs/abis/FeedbackRegistryAbi.js +642 -0
  9. package/dist/cjs/abis/FrontendRegistryAbi.js +1406 -0
  10. package/dist/cjs/abis/LaunchDistributionPoolAbi.js +2936 -0
  11. package/dist/cjs/abis/LoopReputationAbi.js +1365 -0
  12. package/dist/cjs/abis/ProfileRegistryAbi.js +708 -0
  13. package/dist/cjs/abis/ProtocolConfigAbi.js +2075 -0
  14. package/dist/cjs/abis/QuestionRewardPoolEscrowAbi.js +2910 -0
  15. package/dist/cjs/abis/RateLoopGovernorAbi.js +2018 -0
  16. package/dist/cjs/abis/RaterRegistryAbi.js +3062 -0
  17. package/dist/cjs/abis/RaterRegistryConfidentialityAbi.js +117 -0
  18. package/dist/cjs/abis/RoundRewardDistributorAbi.js +1523 -0
  19. package/dist/cjs/abis/RoundVotingEngineAbi.js +2295 -0
  20. package/dist/cjs/abis/TimelockControllerAbi.js +1001 -0
  21. package/dist/cjs/abis/X402QuestionSubmitterAbi.js +2137 -0
  22. package/dist/cjs/abis/index.js +43 -0
  23. package/dist/cjs/deployedContracts.js +168595 -0
  24. package/dist/cjs/deployments.js +48 -0
  25. package/dist/cjs/index.js +27 -0
  26. package/dist/cjs/package.json +3 -0
  27. package/dist/cjs/protocol.js +99 -0
  28. package/dist/cjs/types.js +2 -0
  29. package/dist/cjs/voting.js +336 -0
  30. package/dist/cjs/votingCore.js +301 -0
  31. package/dist/esm/abis/AdvisoryVoteRecorderAbi.d.ts +727 -0
  32. package/dist/esm/abis/AdvisoryVoteRecorderAbi.d.ts.map +1 -0
  33. package/dist/esm/abis/AdvisoryVoteRecorderAbi.js +932 -0
  34. package/dist/esm/abis/CategoryRegistryAbi.d.ts +396 -0
  35. package/dist/esm/abis/CategoryRegistryAbi.d.ts.map +1 -0
  36. package/dist/esm/abis/CategoryRegistryAbi.js +515 -0
  37. package/dist/esm/abis/ClusterPayoutOracleAbi.d.ts +2005 -0
  38. package/dist/esm/abis/ClusterPayoutOracleAbi.d.ts.map +1 -0
  39. package/dist/esm/abis/ClusterPayoutOracleAbi.js +2577 -0
  40. package/dist/esm/abis/ConfidentialityEscrowAbi.d.ts +1255 -0
  41. package/dist/esm/abis/ConfidentialityEscrowAbi.d.ts.map +1 -0
  42. package/dist/esm/abis/ConfidentialityEscrowAbi.js +1616 -0
  43. package/dist/esm/abis/ContentRegistryAbi.d.ts +2036 -0
  44. package/dist/esm/abis/ContentRegistryAbi.d.ts.map +1 -0
  45. package/dist/esm/abis/ContentRegistryAbi.js +2603 -0
  46. package/dist/esm/abis/FeedbackBonusEscrowAbi.d.ts +1163 -0
  47. package/dist/esm/abis/FeedbackBonusEscrowAbi.d.ts.map +1 -0
  48. package/dist/esm/abis/FeedbackBonusEscrowAbi.js +1493 -0
  49. package/dist/esm/abis/FeedbackRegistryAbi.d.ts +498 -0
  50. package/dist/esm/abis/FeedbackRegistryAbi.d.ts.map +1 -0
  51. package/dist/esm/abis/FeedbackRegistryAbi.js +639 -0
  52. package/dist/esm/abis/FrontendRegistryAbi.d.ts +1084 -0
  53. package/dist/esm/abis/FrontendRegistryAbi.d.ts.map +1 -0
  54. package/dist/esm/abis/FrontendRegistryAbi.js +1403 -0
  55. package/dist/esm/abis/LaunchDistributionPoolAbi.d.ts +2273 -0
  56. package/dist/esm/abis/LaunchDistributionPoolAbi.d.ts.map +1 -0
  57. package/dist/esm/abis/LaunchDistributionPoolAbi.js +2933 -0
  58. package/dist/esm/abis/LoopReputationAbi.d.ts +1042 -0
  59. package/dist/esm/abis/LoopReputationAbi.d.ts.map +1 -0
  60. package/dist/esm/abis/LoopReputationAbi.js +1362 -0
  61. package/dist/esm/abis/ProfileRegistryAbi.d.ts +545 -0
  62. package/dist/esm/abis/ProfileRegistryAbi.d.ts.map +1 -0
  63. package/dist/esm/abis/ProfileRegistryAbi.js +705 -0
  64. package/dist/esm/abis/ProtocolConfigAbi.d.ts +1617 -0
  65. package/dist/esm/abis/ProtocolConfigAbi.d.ts.map +1 -0
  66. package/dist/esm/abis/ProtocolConfigAbi.js +2072 -0
  67. package/dist/esm/abis/QuestionRewardPoolEscrowAbi.d.ts +2287 -0
  68. package/dist/esm/abis/QuestionRewardPoolEscrowAbi.d.ts.map +1 -0
  69. package/dist/esm/abis/QuestionRewardPoolEscrowAbi.js +2907 -0
  70. package/dist/esm/abis/RateLoopGovernorAbi.d.ts +1544 -0
  71. package/dist/esm/abis/RateLoopGovernorAbi.d.ts.map +1 -0
  72. package/dist/esm/abis/RateLoopGovernorAbi.js +2015 -0
  73. package/dist/esm/abis/RaterRegistryAbi.d.ts +2390 -0
  74. package/dist/esm/abis/RaterRegistryAbi.d.ts.map +1 -0
  75. package/dist/esm/abis/RaterRegistryAbi.js +3059 -0
  76. package/dist/esm/abis/RaterRegistryConfidentialityAbi.d.ts +132 -0
  77. package/dist/esm/abis/RaterRegistryConfidentialityAbi.d.ts.map +1 -0
  78. package/dist/esm/abis/RaterRegistryConfidentialityAbi.js +114 -0
  79. package/dist/esm/abis/RoundRewardDistributorAbi.d.ts +1182 -0
  80. package/dist/esm/abis/RoundRewardDistributorAbi.d.ts.map +1 -0
  81. package/dist/esm/abis/RoundRewardDistributorAbi.js +1520 -0
  82. package/dist/esm/abis/RoundVotingEngineAbi.d.ts +1797 -0
  83. package/dist/esm/abis/RoundVotingEngineAbi.d.ts.map +1 -0
  84. package/dist/esm/abis/RoundVotingEngineAbi.js +2292 -0
  85. package/dist/esm/abis/TimelockControllerAbi.d.ts +771 -0
  86. package/dist/esm/abis/TimelockControllerAbi.d.ts.map +1 -0
  87. package/dist/esm/abis/TimelockControllerAbi.js +998 -0
  88. package/dist/esm/abis/X402QuestionSubmitterAbi.d.ts +1663 -0
  89. package/dist/esm/abis/X402QuestionSubmitterAbi.d.ts.map +1 -0
  90. package/dist/esm/abis/X402QuestionSubmitterAbi.js +2134 -0
  91. package/dist/esm/abis/index.d.ts +21 -0
  92. package/dist/esm/abis/index.d.ts.map +1 -0
  93. package/dist/esm/abis/index.js +20 -0
  94. package/dist/esm/deployedContracts.d.ts +8 -0
  95. package/dist/esm/deployedContracts.d.ts.map +1 -0
  96. package/dist/esm/deployedContracts.js +168593 -0
  97. package/dist/esm/deployments.d.ts +6 -0
  98. package/dist/esm/deployments.d.ts.map +1 -0
  99. package/dist/esm/deployments.js +39 -0
  100. package/dist/esm/index.d.ts +7 -0
  101. package/dist/esm/index.d.ts.map +1 -0
  102. package/dist/esm/index.js +6 -0
  103. package/dist/esm/protocol.d.ts +64 -0
  104. package/dist/esm/protocol.d.ts.map +1 -0
  105. package/dist/esm/protocol.js +94 -0
  106. package/dist/esm/types.d.ts +16 -0
  107. package/dist/esm/types.d.ts.map +1 -0
  108. package/dist/esm/types.js +1 -0
  109. package/dist/esm/voting.d.ts +75 -0
  110. package/dist/esm/voting.d.ts.map +1 -0
  111. package/dist/esm/voting.js +279 -0
  112. package/dist/esm/votingCore.d.ts +55 -0
  113. package/dist/esm/votingCore.d.ts.map +1 -0
  114. package/dist/esm/votingCore.js +286 -0
  115. package/package.json +122 -0
@@ -0,0 +1,301 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MAX_PREDICTED_UP_PERCENT = exports.MIN_PREDICTED_UP_PERCENT = exports.MAX_PREDICTED_UP_BPS = exports.MIN_PREDICTED_UP_BPS = void 0;
4
+ exports.normalizePredictedUpBps = normalizePredictedUpBps;
5
+ exports.predictionPercentToBps = predictionPercentToBps;
6
+ exports.bpsToPredictionPercent = bpsToPredictionPercent;
7
+ exports.packVoteRoundContext = packVoteRoundContext;
8
+ exports.unpackVoteRoundContext = unpackVoteRoundContext;
9
+ exports.deriveVoteTlockRevealAvailableAtSeconds = deriveVoteTlockRevealAvailableAtSeconds;
10
+ exports.encodeRbtsVotePlaintext = encodeRbtsVotePlaintext;
11
+ exports.decodeRbtsVotePlaintext = decodeRbtsVotePlaintext;
12
+ exports.buildCommitHash = buildCommitHash;
13
+ exports.buildRbtsCommitHash = buildRbtsCommitHash;
14
+ exports.buildCommitKey = buildCommitKey;
15
+ exports.parseTlockCiphertextMetadata = parseTlockCiphertextMetadata;
16
+ const viem_1 = require("viem");
17
+ const protocol_1 = require("./protocol");
18
+ const AGE_ARMOR_HEADER = "-----BEGIN AGE ENCRYPTED FILE-----";
19
+ const AGE_ARMOR_FOOTER = "-----END AGE ENCRYPTED FILE-----";
20
+ const AGE_ARMOR_LINE_CHUNK_SIZE = 64;
21
+ const AGE_VERSION_LINE = "age-encryption.org/v1";
22
+ const AGE_RECIPIENT_PREFIX = "-> ";
23
+ const AGE_MAC_PREFIX = "--- ";
24
+ const TLOCK_STANZA_PREFIX = "-> tlock ";
25
+ const UNPADDED_BASE64_LINE = /^[A-Za-z0-9+/]+$/;
26
+ const AGE_MAC_LENGTH = 32;
27
+ const MIN_TLOCK_STANZA_BODY_LENGTH = 80;
28
+ const MIN_ENCRYPTED_BODY_LENGTH = 65;
29
+ const ROUND_REFERENCE_RATING_MASK = 0xffffn;
30
+ const RBTS_PLAINTEXT_VERSION = 2;
31
+ exports.MIN_PREDICTED_UP_BPS = protocol_1.USER_PREDICTION_BPS.min;
32
+ exports.MAX_PREDICTED_UP_BPS = protocol_1.USER_PREDICTION_BPS.max;
33
+ exports.MIN_PREDICTED_UP_PERCENT = protocol_1.USER_PREDICTION_PERCENT.min;
34
+ exports.MAX_PREDICTED_UP_PERCENT = protocol_1.USER_PREDICTION_PERCENT.max;
35
+ function normalizePredictedUpBps(predictedUpBps) {
36
+ if (!Number.isInteger(predictedUpBps) ||
37
+ predictedUpBps < exports.MIN_PREDICTED_UP_BPS ||
38
+ predictedUpBps > exports.MAX_PREDICTED_UP_BPS) {
39
+ throw new Error("predictedUpBps must be an integer from 100 to 9900");
40
+ }
41
+ return predictedUpBps;
42
+ }
43
+ function predictionPercentToBps(predictedUpPercent) {
44
+ if (!Number.isFinite(predictedUpPercent) ||
45
+ predictedUpPercent < exports.MIN_PREDICTED_UP_PERCENT ||
46
+ predictedUpPercent > exports.MAX_PREDICTED_UP_PERCENT) {
47
+ throw new Error("predicted up percentage must be from 1 to 99");
48
+ }
49
+ return normalizePredictedUpBps(Math.round(predictedUpPercent * 100));
50
+ }
51
+ function bpsToPredictionPercent(predictedUpBps) {
52
+ return normalizePredictedUpBps(predictedUpBps) / 100;
53
+ }
54
+ function packVoteRoundContext(roundId, roundReferenceRatingBps) {
55
+ if (roundId <= 0n) {
56
+ throw new Error("roundId must be positive");
57
+ }
58
+ if (!Number.isInteger(roundReferenceRatingBps) ||
59
+ roundReferenceRatingBps < 0 ||
60
+ roundReferenceRatingBps > 65_535) {
61
+ throw new Error("roundReferenceRatingBps must fit uint16");
62
+ }
63
+ return (roundId << 16n) | BigInt(roundReferenceRatingBps);
64
+ }
65
+ function unpackVoteRoundContext(roundContext) {
66
+ return {
67
+ roundId: roundContext >> 16n,
68
+ roundReferenceRatingBps: Number(roundContext & ROUND_REFERENCE_RATING_MASK),
69
+ };
70
+ }
71
+ /**
72
+ * C-3 (2026-05-22 audit): the formula here uses (targetRound - 1) * period because the
73
+ * local convention (see voting.ts's computeTargetRoundForBeaconTime lineage) treats round
74
+ * 1 as occurring at the genesis time itself. The drand network's own signature-publishing
75
+ * schedule produces round R at `genesis + R * period`, so callers may see "reveal
76
+ * available" up to one period before drand has actually published the round's signature;
77
+ * this is currently absorbed as a brief retry at the call site.
78
+ *
79
+ * If a follow-up confirms drand's schedule should govern the displayed availability
80
+ * (rather than the local round numbering), change to `targetRound * periodSeconds` and
81
+ * update the boundary tests in voting.test.ts in lockstep. Do not change one without
82
+ * the other.
83
+ */
84
+ function deriveVoteTlockRevealAvailableAtSeconds(targetRound, chainInfo) {
85
+ if (targetRound <= 0n || chainInfo.periodSeconds <= 0n) {
86
+ return 0n;
87
+ }
88
+ return (chainInfo.genesisTimeSeconds + (targetRound - 1n) * chainInfo.periodSeconds);
89
+ }
90
+ function saltToBytes(salt) {
91
+ const hex = salt.startsWith("0x") ? salt.slice(2) : salt;
92
+ if (hex.length !== 64)
93
+ throw new Error("salt must be 32 bytes");
94
+ const bytes = new Uint8Array(32);
95
+ for (let i = 0; i < 32; i++) {
96
+ bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
97
+ }
98
+ return bytes;
99
+ }
100
+ function bytesToHex(bytes) {
101
+ return `0x${Array.from(bytes)
102
+ .map((byte) => byte.toString(16).padStart(2, "0"))
103
+ .join("")}`;
104
+ }
105
+ function encodeRbtsVotePlaintext(isUp, predictedUpBps, salt) {
106
+ const normalizedPrediction = normalizePredictedUpBps(predictedUpBps);
107
+ const plaintext = new Uint8Array(36);
108
+ plaintext[0] = RBTS_PLAINTEXT_VERSION;
109
+ plaintext[1] = isUp ? 1 : 0;
110
+ plaintext[2] = normalizedPrediction >> 8;
111
+ plaintext[3] = normalizedPrediction & 0xff;
112
+ plaintext.set(saltToBytes(salt), 4);
113
+ return plaintext;
114
+ }
115
+ function decodeRbtsVotePlaintext(plaintext) {
116
+ if (plaintext.length !== 36 || plaintext[0] !== RBTS_PLAINTEXT_VERSION)
117
+ return null;
118
+ if (plaintext[1] !== 0 && plaintext[1] !== 1)
119
+ return null;
120
+ const predictedUpBps = (plaintext[2] << 8) | plaintext[3];
121
+ if (predictedUpBps < exports.MIN_PREDICTED_UP_BPS ||
122
+ predictedUpBps > exports.MAX_PREDICTED_UP_BPS)
123
+ return null;
124
+ return {
125
+ isUp: plaintext[1] === 1,
126
+ predictedUpBps,
127
+ predictedUpPercent: bpsToPredictionPercent(predictedUpBps),
128
+ salt: bytesToHex(plaintext.slice(4, 36)),
129
+ };
130
+ }
131
+ function buildCommitHash(isUp, predictedUpBps, salt, voter, contentId, roundId, roundReferenceRatingBps, targetRound, drandChainHash, ciphertext) {
132
+ const ciphertextHash = (0, viem_1.keccak256)(ciphertext);
133
+ return (0, viem_1.keccak256)((0, viem_1.encodePacked)([
134
+ "bool",
135
+ "uint16",
136
+ "bytes32",
137
+ "address",
138
+ "uint256",
139
+ "uint256",
140
+ "uint16",
141
+ "uint64",
142
+ "bytes32",
143
+ "bytes32",
144
+ ], [
145
+ isUp,
146
+ normalizePredictedUpBps(predictedUpBps),
147
+ salt,
148
+ voter,
149
+ contentId,
150
+ roundId,
151
+ roundReferenceRatingBps,
152
+ targetRound,
153
+ drandChainHash,
154
+ ciphertextHash,
155
+ ]));
156
+ }
157
+ function buildRbtsCommitHash(isUp, predictedUpBps, salt, voter, contentId, roundId, roundReferenceRatingBps, targetRound, drandChainHash, ciphertext) {
158
+ return buildCommitHash(isUp, predictedUpBps, salt, voter, contentId, roundId, roundReferenceRatingBps, targetRound, drandChainHash, ciphertext);
159
+ }
160
+ function buildCommitKey(voter, commitHash) {
161
+ return (0, viem_1.keccak256)((0, viem_1.encodePacked)(["address", "bytes32"], [voter, commitHash]));
162
+ }
163
+ function decodeBase64Bytes(value) {
164
+ if (typeof globalThis.atob !== "function") {
165
+ return null;
166
+ }
167
+ try {
168
+ const binary = globalThis.atob(value);
169
+ const bytes = new Uint8Array(binary.length);
170
+ for (let index = 0; index < binary.length; index++) {
171
+ bytes[index] = binary.charCodeAt(index);
172
+ }
173
+ return bytes;
174
+ }
175
+ catch {
176
+ return null;
177
+ }
178
+ }
179
+ function asciiBytesToString(bytes, start, end) {
180
+ let value = "";
181
+ for (let index = start; index < end; index++) {
182
+ value += String.fromCharCode(bytes[index]);
183
+ }
184
+ return value;
185
+ }
186
+ function decodeAgeArmor(armored) {
187
+ const trimmed = armored.trim();
188
+ if (!trimmed.startsWith(AGE_ARMOR_HEADER) ||
189
+ !trimmed.endsWith(AGE_ARMOR_FOOTER)) {
190
+ return null;
191
+ }
192
+ const payload = trimmed.slice(AGE_ARMOR_HEADER.length, trimmed.length - AGE_ARMOR_FOOTER.length);
193
+ const lines = payload.split(/\r?\n/);
194
+ if (lines.some((line) => line.length > AGE_ARMOR_LINE_CHUNK_SIZE)) {
195
+ return null;
196
+ }
197
+ if (lines.some((line) => line.length > 0 && !/^[A-Za-z0-9+/=]+$/.test(line))) {
198
+ return null;
199
+ }
200
+ const lastLine = lines.at(-1) ?? "";
201
+ if (lastLine.length > AGE_ARMOR_LINE_CHUNK_SIZE) {
202
+ return null;
203
+ }
204
+ return decodeBase64Bytes(lines.join(""));
205
+ }
206
+ function readAsciiLine(payload, cursor) {
207
+ if (cursor >= payload.length)
208
+ return null;
209
+ let end = cursor;
210
+ while (end < payload.length &&
211
+ payload[end] !== 0x0a &&
212
+ payload[end] !== 0x0d) {
213
+ end++;
214
+ }
215
+ if (end >= payload.length) {
216
+ return null;
217
+ }
218
+ let nextCursor = end + 1;
219
+ if (payload[end] === 0x0d &&
220
+ nextCursor < payload.length &&
221
+ payload[nextCursor] === 0x0a) {
222
+ nextCursor++;
223
+ }
224
+ return {
225
+ line: asciiBytesToString(payload, cursor, end),
226
+ nextCursor,
227
+ };
228
+ }
229
+ function isValidUnpaddedBase64Line(line) {
230
+ return (line.length > 0 &&
231
+ line.length <= AGE_ARMOR_LINE_CHUNK_SIZE &&
232
+ UNPADDED_BASE64_LINE.test(line));
233
+ }
234
+ function unpaddedBase64DecodedLength(charLength) {
235
+ const remainder = charLength % 4;
236
+ if (remainder === 1)
237
+ return null;
238
+ return Math.floor(charLength / 4) * 3 + (remainder === 0 ? 0 : remainder - 1);
239
+ }
240
+ function parseTlockCiphertextMetadata(ciphertext) {
241
+ try {
242
+ const armored = (0, viem_1.hexToString)(ciphertext);
243
+ const agePayload = decodeAgeArmor(armored);
244
+ if (!agePayload)
245
+ return null;
246
+ const versionLine = readAsciiLine(agePayload, 0);
247
+ if (!versionLine || versionLine.line !== AGE_VERSION_LINE) {
248
+ return null;
249
+ }
250
+ const stanzaLine = readAsciiLine(agePayload, versionLine.nextCursor);
251
+ if (!stanzaLine || !stanzaLine.line.startsWith(TLOCK_STANZA_PREFIX)) {
252
+ return null;
253
+ }
254
+ const recipientMatch = /^-> tlock ([0-9]+) ([0-9a-fA-F]{64})$/.exec(stanzaLine.line);
255
+ if (!recipientMatch) {
256
+ return null;
257
+ }
258
+ let cursor = stanzaLine.nextCursor;
259
+ let stanzaBodyCharLength = 0;
260
+ while (cursor < agePayload.length) {
261
+ const bodyLine = readAsciiLine(agePayload, cursor);
262
+ if (!bodyLine)
263
+ return null;
264
+ if (bodyLine.line.startsWith(AGE_MAC_PREFIX)) {
265
+ break;
266
+ }
267
+ if (bodyLine.line.startsWith(AGE_RECIPIENT_PREFIX) ||
268
+ !isValidUnpaddedBase64Line(bodyLine.line)) {
269
+ return null;
270
+ }
271
+ stanzaBodyCharLength += bodyLine.line.length;
272
+ cursor = bodyLine.nextCursor;
273
+ }
274
+ const decodedStanzaBodyLength = unpaddedBase64DecodedLength(stanzaBodyCharLength);
275
+ if (decodedStanzaBodyLength == null ||
276
+ decodedStanzaBodyLength < MIN_TLOCK_STANZA_BODY_LENGTH) {
277
+ return null;
278
+ }
279
+ const macLine = readAsciiLine(agePayload, cursor);
280
+ if (!macLine || !macLine.line.startsWith(AGE_MAC_PREFIX)) {
281
+ return null;
282
+ }
283
+ const mac = macLine.line.slice(AGE_MAC_PREFIX.length);
284
+ const decodedMacLength = unpaddedBase64DecodedLength(mac.length);
285
+ if (!isValidUnpaddedBase64Line(mac) ||
286
+ decodedMacLength !== AGE_MAC_LENGTH) {
287
+ return null;
288
+ }
289
+ if (agePayload.length - macLine.nextCursor < MIN_ENCRYPTED_BODY_LENGTH) {
290
+ return null;
291
+ }
292
+ const [, roundStr, chainHash] = recipientMatch;
293
+ return {
294
+ targetRound: BigInt(roundStr),
295
+ drandChainHash: `0x${chainHash.toLowerCase()}`,
296
+ };
297
+ }
298
+ catch {
299
+ return null;
300
+ }
301
+ }