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