@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,6 @@
|
|
|
1
|
+
import type { GenericContract } from "./types.js";
|
|
2
|
+
export declare function getSharedDeployment(chainId: number, contractName: string): GenericContract | undefined;
|
|
3
|
+
export declare function getSharedDeploymentAddress(chainId: number, contractName: string): `0x${string}` | undefined;
|
|
4
|
+
export declare function getSharedChainStartBlock(chainId: number): number | undefined;
|
|
5
|
+
export declare function getSharedDeploymentStartBlock(chainId: number, contractName: string): number | undefined;
|
|
6
|
+
//# sourceMappingURL=deployments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deployments.d.ts","sourceRoot":"","sources":["../../src/deployments.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAA+B,MAAM,SAAS,CAAC;AAQ5E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAEtG;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,KAAK,MAAM,EAAE,GAAG,SAAS,CAO3G;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAe5E;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAWvG"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { isAddress } from "viem";
|
|
2
|
+
import deployedContracts from "./deployedContracts.js";
|
|
3
|
+
const sharedDeployments = deployedContracts;
|
|
4
|
+
function isValidStartBlock(value) {
|
|
5
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
6
|
+
}
|
|
7
|
+
export function getSharedDeployment(chainId, contractName) {
|
|
8
|
+
return sharedDeployments[chainId]?.[contractName];
|
|
9
|
+
}
|
|
10
|
+
export function getSharedDeploymentAddress(chainId, contractName) {
|
|
11
|
+
const address = getSharedDeployment(chainId, contractName)?.address;
|
|
12
|
+
if (!address || !isAddress(address)) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
return address;
|
|
16
|
+
}
|
|
17
|
+
export function getSharedChainStartBlock(chainId) {
|
|
18
|
+
const contracts = sharedDeployments[chainId];
|
|
19
|
+
if (!contracts) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
const deployedBlocks = Object.values(contracts)
|
|
23
|
+
.map(contract => contract?.deployedOnBlock)
|
|
24
|
+
.filter(isValidStartBlock);
|
|
25
|
+
if (deployedBlocks.length === 0) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
return Math.min(...deployedBlocks);
|
|
29
|
+
}
|
|
30
|
+
export function getSharedDeploymentStartBlock(chainId, contractName) {
|
|
31
|
+
const contractStartBlock = getSharedDeployment(chainId, contractName)?.deployedOnBlock;
|
|
32
|
+
if (isValidStartBlock(contractStartBlock)) {
|
|
33
|
+
return contractStartBlock;
|
|
34
|
+
}
|
|
35
|
+
if (!getSharedDeployment(chainId, contractName)) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
return getSharedChainStartBlock(chainId);
|
|
39
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from "./abis/index.js";
|
|
2
|
+
export * from "./deployments.js";
|
|
3
|
+
export { default as deployedContracts } from "./deployedContracts.js";
|
|
4
|
+
export * from "./protocol.js";
|
|
5
|
+
export * from "./types.js";
|
|
6
|
+
export * from "./voting.js";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACnE,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export declare const BPS_SCALE: 10000;
|
|
2
|
+
export declare const USER_PREDICTION_BPS: {
|
|
3
|
+
readonly min: 100;
|
|
4
|
+
readonly max: 9900;
|
|
5
|
+
};
|
|
6
|
+
export declare const USER_PREDICTION_PERCENT: {
|
|
7
|
+
readonly min: number;
|
|
8
|
+
readonly max: number;
|
|
9
|
+
};
|
|
10
|
+
export declare const ROUND_STATE: {
|
|
11
|
+
readonly Open: 0;
|
|
12
|
+
readonly Settled: 1;
|
|
13
|
+
readonly Cancelled: 2;
|
|
14
|
+
readonly Tied: 3;
|
|
15
|
+
readonly RevealFailed: 4;
|
|
16
|
+
};
|
|
17
|
+
export type RoundState = (typeof ROUND_STATE)[keyof typeof ROUND_STATE];
|
|
18
|
+
export declare const ROUND_STATE_LABEL: Record<RoundState, string>;
|
|
19
|
+
export declare const DEFAULT_ROUND_CONFIG: {
|
|
20
|
+
readonly epochDurationSeconds: number;
|
|
21
|
+
readonly maxDurationSeconds: number;
|
|
22
|
+
readonly minVoters: 3;
|
|
23
|
+
readonly maxVoters: 100;
|
|
24
|
+
};
|
|
25
|
+
export declare const DEFAULT_REVEAL_GRACE_PERIOD_SECONDS: number;
|
|
26
|
+
/** Matches `RoundCleanupLib.REVEAL_FAILED_GRACE_MULTIPLIER` in foundry. */
|
|
27
|
+
export declare const REVEAL_FAILED_GRACE_MULTIPLIER = 24;
|
|
28
|
+
export declare const EPOCH_WEIGHT_BPS: {
|
|
29
|
+
readonly blind: 10000;
|
|
30
|
+
readonly informed: 2500;
|
|
31
|
+
};
|
|
32
|
+
export declare const PLATFORM_REWARD_SPLIT_BPS: {
|
|
33
|
+
readonly frontend: 300;
|
|
34
|
+
};
|
|
35
|
+
export declare const REWARD_SPLIT_BPS: {
|
|
36
|
+
readonly voter: 9600;
|
|
37
|
+
readonly submitter: 0;
|
|
38
|
+
readonly platform: 300;
|
|
39
|
+
readonly treasury: 100;
|
|
40
|
+
};
|
|
41
|
+
export declare const SCORE_SPREAD_POLICY: {
|
|
42
|
+
readonly intensityBps: 15000;
|
|
43
|
+
readonly forfeitMinReveals: 8;
|
|
44
|
+
readonly maxForfeitBps: 5000;
|
|
45
|
+
};
|
|
46
|
+
export declare const QUESTION_REWARD_PARTICIPANT_FLOORS: {
|
|
47
|
+
readonly minParticipants: 3;
|
|
48
|
+
readonly highValueAmount: 1000000000;
|
|
49
|
+
readonly highValueMinParticipants: 5;
|
|
50
|
+
readonly veryHighValueAmount: 10000000000;
|
|
51
|
+
readonly veryHighValueMinParticipants: 8;
|
|
52
|
+
};
|
|
53
|
+
export declare function requiredQuestionRewardParticipants(amountAtomic: bigint | number): number;
|
|
54
|
+
export declare const CONFIDENTIALITY_FLAG_PRIVATE_FOREVER: 1;
|
|
55
|
+
export declare const MIN_NONZERO_CONFIDENTIALITY_BOND = 1000000n;
|
|
56
|
+
export declare const USDC_BY_CHAIN_ID: Record<number, `0x${string}`>;
|
|
57
|
+
export declare const USDC_EIP712_DOMAIN_NAME_BY_CHAIN_ID: Record<number, string>;
|
|
58
|
+
export declare function getUsdcEip712DomainName(chainId: number): string;
|
|
59
|
+
export declare const WORLD_ID_V3_ROUTER_BY_CHAIN_ID: Record<number, `0x${string}`>;
|
|
60
|
+
/** @deprecated Use `USDC_BY_CHAIN_ID`; retained while downstream packages migrate names. */
|
|
61
|
+
export declare const WORLD_CHAIN_USDC_BY_CHAIN_ID: Record<number, `0x${string}`>;
|
|
62
|
+
export declare const BOUNTY_ELIGIBILITY_CREDENTIAL_MASK: 14;
|
|
63
|
+
export declare const BOUNTY_ELIGIBILITY_RECENT_RECHECK_FLAG: 128;
|
|
64
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/protocol.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,EAAG,KAAe,CAAC;AAEzC,eAAO,MAAM,mBAAmB;;;CAGtB,CAAC;AAEX,eAAO,MAAM,uBAAuB;;;CAG1B,CAAC;AAEX,eAAO,MAAM,WAAW;;;;;;CAMd,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,OAAO,WAAW,CAAC,CAAC;AAExE,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAMxD,CAAC;AAEF,eAAO,MAAM,oBAAoB;;;;;CAKvB,CAAC;AAEX,eAAO,MAAM,mCAAmC,QAAU,CAAC;AAE3D,2EAA2E;AAC3E,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAEjD,eAAO,MAAM,gBAAgB;;;CAGnB,CAAC;AAEX,eAAO,MAAM,yBAAyB;;CAE5B,CAAC;AAEX,eAAO,MAAM,gBAAgB;;;;;CAKnB,CAAC;AAEX,eAAO,MAAM,mBAAmB;;;;CAItB,CAAC;AAEX,eAAO,MAAM,kCAAkC;;;;;;CAMrC,CAAC;AAEX,wBAAgB,kCAAkC,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CASxF;AAED,eAAO,MAAM,oCAAoC,EAAG,CAAU,CAAC;AAE/D,eAAO,MAAM,gCAAgC,WAAa,CAAC;AAE3D,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,MAAM,EAAE,CAK1D,CAAC;AAEF,eAAO,MAAM,mCAAmC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAKtE,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,eAAO,MAAM,8BAA8B,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,MAAM,EAAE,CAKxE,CAAC;AAEF,4FAA4F;AAC5F,eAAO,MAAM,4BAA4B,+BAAmB,CAAC;AAE7D,eAAO,MAAM,kCAAkC,EAAG,EAAa,CAAC;AAChE,eAAO,MAAM,sCAAsC,EAAG,GAAa,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
export const BPS_SCALE = 10_000;
|
|
2
|
+
export const USER_PREDICTION_BPS = {
|
|
3
|
+
min: 100,
|
|
4
|
+
max: 9_900,
|
|
5
|
+
};
|
|
6
|
+
export const USER_PREDICTION_PERCENT = {
|
|
7
|
+
min: USER_PREDICTION_BPS.min / 100,
|
|
8
|
+
max: USER_PREDICTION_BPS.max / 100,
|
|
9
|
+
};
|
|
10
|
+
export const ROUND_STATE = {
|
|
11
|
+
Open: 0,
|
|
12
|
+
Settled: 1,
|
|
13
|
+
Cancelled: 2,
|
|
14
|
+
Tied: 3,
|
|
15
|
+
RevealFailed: 4,
|
|
16
|
+
};
|
|
17
|
+
export const ROUND_STATE_LABEL = {
|
|
18
|
+
[ROUND_STATE.Open]: "Open",
|
|
19
|
+
[ROUND_STATE.Settled]: "Settled",
|
|
20
|
+
[ROUND_STATE.Cancelled]: "Cancelled",
|
|
21
|
+
[ROUND_STATE.Tied]: "Tied",
|
|
22
|
+
[ROUND_STATE.RevealFailed]: "RevealFailed",
|
|
23
|
+
};
|
|
24
|
+
export const DEFAULT_ROUND_CONFIG = {
|
|
25
|
+
epochDurationSeconds: 20 * 60,
|
|
26
|
+
maxDurationSeconds: 20 * 60,
|
|
27
|
+
minVoters: 3,
|
|
28
|
+
maxVoters: 100,
|
|
29
|
+
};
|
|
30
|
+
export const DEFAULT_REVEAL_GRACE_PERIOD_SECONDS = 60 * 60;
|
|
31
|
+
/** Matches `RoundCleanupLib.REVEAL_FAILED_GRACE_MULTIPLIER` in foundry. */
|
|
32
|
+
export const REVEAL_FAILED_GRACE_MULTIPLIER = 24;
|
|
33
|
+
export const EPOCH_WEIGHT_BPS = {
|
|
34
|
+
blind: BPS_SCALE,
|
|
35
|
+
informed: 2_500,
|
|
36
|
+
};
|
|
37
|
+
export const PLATFORM_REWARD_SPLIT_BPS = {
|
|
38
|
+
frontend: 300,
|
|
39
|
+
};
|
|
40
|
+
export const REWARD_SPLIT_BPS = {
|
|
41
|
+
voter: 9_600,
|
|
42
|
+
submitter: 0,
|
|
43
|
+
platform: PLATFORM_REWARD_SPLIT_BPS.frontend,
|
|
44
|
+
treasury: 100,
|
|
45
|
+
};
|
|
46
|
+
export const SCORE_SPREAD_POLICY = {
|
|
47
|
+
intensityBps: 15_000,
|
|
48
|
+
forfeitMinReveals: 8,
|
|
49
|
+
maxForfeitBps: 5_000,
|
|
50
|
+
};
|
|
51
|
+
export const QUESTION_REWARD_PARTICIPANT_FLOORS = {
|
|
52
|
+
minParticipants: 3,
|
|
53
|
+
highValueAmount: 1_000_000_000,
|
|
54
|
+
highValueMinParticipants: 5,
|
|
55
|
+
veryHighValueAmount: 10_000_000_000,
|
|
56
|
+
veryHighValueMinParticipants: SCORE_SPREAD_POLICY.forfeitMinReveals,
|
|
57
|
+
};
|
|
58
|
+
export function requiredQuestionRewardParticipants(amountAtomic) {
|
|
59
|
+
const amount = typeof amountAtomic === "bigint" ? amountAtomic : BigInt(amountAtomic);
|
|
60
|
+
if (amount >= BigInt(QUESTION_REWARD_PARTICIPANT_FLOORS.veryHighValueAmount)) {
|
|
61
|
+
return QUESTION_REWARD_PARTICIPANT_FLOORS.veryHighValueMinParticipants;
|
|
62
|
+
}
|
|
63
|
+
if (amount >= BigInt(QUESTION_REWARD_PARTICIPANT_FLOORS.highValueAmount)) {
|
|
64
|
+
return QUESTION_REWARD_PARTICIPANT_FLOORS.highValueMinParticipants;
|
|
65
|
+
}
|
|
66
|
+
return QUESTION_REWARD_PARTICIPANT_FLOORS.minParticipants;
|
|
67
|
+
}
|
|
68
|
+
export const CONFIDENTIALITY_FLAG_PRIVATE_FOREVER = 1;
|
|
69
|
+
export const MIN_NONZERO_CONFIDENTIALITY_BOND = 1000000n;
|
|
70
|
+
export const USDC_BY_CHAIN_ID = {
|
|
71
|
+
8453: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
72
|
+
84532: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
73
|
+
480: "0x79A02482A880bCE3F13e09Da970dC34db4CD24d1",
|
|
74
|
+
4801: "0x66145f38cBAC35Ca6F1Dfb4914dF98F1614aeA88",
|
|
75
|
+
};
|
|
76
|
+
export const USDC_EIP712_DOMAIN_NAME_BY_CHAIN_ID = {
|
|
77
|
+
8453: "USD Coin",
|
|
78
|
+
84532: "USDC",
|
|
79
|
+
480: "USDC",
|
|
80
|
+
4801: "USDC",
|
|
81
|
+
};
|
|
82
|
+
export function getUsdcEip712DomainName(chainId) {
|
|
83
|
+
return USDC_EIP712_DOMAIN_NAME_BY_CHAIN_ID[chainId] ?? "USDC";
|
|
84
|
+
}
|
|
85
|
+
export const WORLD_ID_V3_ROUTER_BY_CHAIN_ID = {
|
|
86
|
+
8453: "0xBCC7e5910178AFFEEeBA573ba6903E9869594163",
|
|
87
|
+
84532: "0x42FF98C4E85212a5D31358ACbFe76a621b50fC02",
|
|
88
|
+
480: "0x17B354dD2595411ff79041f930e491A4Df39A278",
|
|
89
|
+
4801: "0x57f928158C3EE7CDad1e4D8642503c4D0201f611",
|
|
90
|
+
};
|
|
91
|
+
/** @deprecated Use `USDC_BY_CHAIN_ID`; retained while downstream packages migrate names. */
|
|
92
|
+
export const WORLD_CHAIN_USDC_BY_CHAIN_ID = USDC_BY_CHAIN_ID;
|
|
93
|
+
export const BOUNTY_ELIGIBILITY_CREDENTIAL_MASK = 0x0e;
|
|
94
|
+
export const BOUNTY_ELIGIBILITY_RECENT_RECHECK_FLAG = 0x80;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type InheritedFunctions = {
|
|
2
|
+
readonly [key: string]: string;
|
|
3
|
+
};
|
|
4
|
+
export type GenericContract = {
|
|
5
|
+
address: `0x${string}`;
|
|
6
|
+
abi: readonly unknown[];
|
|
7
|
+
inheritedFunctions?: InheritedFunctions;
|
|
8
|
+
external?: true;
|
|
9
|
+
deployedOnBlock?: number;
|
|
10
|
+
};
|
|
11
|
+
export type GenericContractsDeclaration = {
|
|
12
|
+
[chainId: number]: {
|
|
13
|
+
[contractName: string]: GenericContract;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AAEpE,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,KAAK,MAAM,EAAE,CAAC;IACvB,GAAG,EAAE,SAAS,OAAO,EAAE,CAAC;IACxB,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,CAAC,OAAO,EAAE,MAAM,GAAG;QACjB,CAAC,YAAY,EAAE,MAAM,GAAG,eAAe,CAAC;KACzC,CAAC;CACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { type Address } from "viem";
|
|
2
|
+
import { type RbtsCommitHash, type VoteCiphertext, type VoteDrandChainHash, type VoteSalt, type VoteTlockChainInfo } from "./votingCore.js";
|
|
3
|
+
export { MAX_PREDICTED_UP_BPS, MAX_PREDICTED_UP_PERCENT, MIN_PREDICTED_UP_BPS, MIN_PREDICTED_UP_PERCENT, bpsToPredictionPercent, buildCommitHash, buildCommitKey, buildRbtsCommitHash, decodeRbtsVotePlaintext, deriveVoteTlockRevealAvailableAtSeconds, encodeRbtsVotePlaintext, normalizePredictedUpBps, packVoteRoundContext, parseTlockCiphertextMetadata, predictionPercentToBps, unpackVoteRoundContext, type RbtsCommitHash, type TlockCiphertextMetadata, type VoteCiphertext, type VoteCommitHash, type VoteCommitMetadata, type VoteDrandChainHash, type VoteSalt, type VoteTlockChainInfo, } from "./votingCore.js";
|
|
4
|
+
type TlockChainInfo = {
|
|
5
|
+
period: number;
|
|
6
|
+
genesis_time: number;
|
|
7
|
+
hash: string;
|
|
8
|
+
};
|
|
9
|
+
type TlockClient = {
|
|
10
|
+
chain: () => {
|
|
11
|
+
info: () => Promise<TlockChainInfo>;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
type TlockEncryptFn = (targetRound: number, payload: Uint8Array, client: unknown) => Promise<string>;
|
|
15
|
+
type TlockDecryptFn = (ciphertext: string, client: unknown) => Promise<Uint8Array>;
|
|
16
|
+
export type VoteTlockRuntime = {
|
|
17
|
+
client?: TlockClient;
|
|
18
|
+
now?: () => number;
|
|
19
|
+
roundStartTimeSeconds?: bigint | number | null;
|
|
20
|
+
candidateTimestampOffsetsSeconds?: readonly number[];
|
|
21
|
+
targetRound?: bigint | number;
|
|
22
|
+
drandChainHash?: VoteDrandChainHash | null;
|
|
23
|
+
drandGenesisTimeSeconds?: bigint | number | null;
|
|
24
|
+
drandPeriodSeconds?: bigint | number | null;
|
|
25
|
+
encryptFn?: TlockEncryptFn;
|
|
26
|
+
decryptFn?: TlockDecryptFn;
|
|
27
|
+
};
|
|
28
|
+
export declare function getVoteTlockChainInfo(runtime?: VoteTlockRuntime): Promise<VoteTlockChainInfo>;
|
|
29
|
+
export declare function tlockEncryptVote(isUp: boolean, predictedUpBps: number, salt: VoteSalt, epochDurationSeconds: number, runtime?: VoteTlockRuntime): Promise<VoteCiphertext>;
|
|
30
|
+
export declare function decryptTlockVoteCiphertext(ciphertext: VoteCiphertext, runtime?: VoteTlockRuntime): Promise<{
|
|
31
|
+
isUp: boolean;
|
|
32
|
+
predictedUpBps: number;
|
|
33
|
+
predictedUpPercent: number;
|
|
34
|
+
salt: VoteSalt;
|
|
35
|
+
} | null>;
|
|
36
|
+
export declare function createTlockVoteCommit(params: {
|
|
37
|
+
voter: Address;
|
|
38
|
+
isUp: boolean;
|
|
39
|
+
predictedUpBps: number;
|
|
40
|
+
salt: VoteSalt;
|
|
41
|
+
contentId: bigint;
|
|
42
|
+
roundId: bigint;
|
|
43
|
+
roundReferenceRatingBps: number;
|
|
44
|
+
epochDurationSeconds: number;
|
|
45
|
+
}, runtime?: VoteTlockRuntime): Promise<{
|
|
46
|
+
ciphertext: VoteCiphertext;
|
|
47
|
+
ciphertextHash: `0x${string}`;
|
|
48
|
+
commitHash: `0x${string}`;
|
|
49
|
+
targetRound: bigint;
|
|
50
|
+
drandChainHash: VoteDrandChainHash;
|
|
51
|
+
roundReferenceRatingBps: number;
|
|
52
|
+
commitKey: `0x${string}`;
|
|
53
|
+
}>;
|
|
54
|
+
export declare function createTlockRbtsVoteCommit(params: {
|
|
55
|
+
voter: Address;
|
|
56
|
+
isUp: boolean;
|
|
57
|
+
predictedUpBps: number;
|
|
58
|
+
salt: VoteSalt;
|
|
59
|
+
contentId: bigint;
|
|
60
|
+
roundId: bigint;
|
|
61
|
+
roundReferenceRatingBps: number;
|
|
62
|
+
epochDurationSeconds: number;
|
|
63
|
+
}, runtime?: VoteTlockRuntime): Promise<{
|
|
64
|
+
ciphertext: VoteCiphertext;
|
|
65
|
+
ciphertextHash: `0x${string}`;
|
|
66
|
+
commitHash: RbtsCommitHash;
|
|
67
|
+
targetRound: bigint;
|
|
68
|
+
drandChainHash: VoteDrandChainHash;
|
|
69
|
+
roundReferenceRatingBps: number;
|
|
70
|
+
isUp: boolean;
|
|
71
|
+
predictedUpBps: number;
|
|
72
|
+
predictedUpPercent: number;
|
|
73
|
+
commitKey: `0x${string}`;
|
|
74
|
+
}>;
|
|
75
|
+
//# sourceMappingURL=voting.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voting.d.ts","sourceRoot":"","sources":["../../src/voting.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { hexToString, keccak256, stringToHex } from "viem";
|
|
2
|
+
import { bpsToPredictionPercent, buildCommitHash, buildCommitKey, buildRbtsCommitHash, decodeRbtsVotePlaintext, encodeRbtsVotePlaintext, normalizePredictedUpBps, } from "./votingCore.js";
|
|
3
|
+
export { MAX_PREDICTED_UP_BPS, MAX_PREDICTED_UP_PERCENT, MIN_PREDICTED_UP_BPS, MIN_PREDICTED_UP_PERCENT, bpsToPredictionPercent, buildCommitHash, buildCommitKey, buildRbtsCommitHash, decodeRbtsVotePlaintext, deriveVoteTlockRevealAvailableAtSeconds, encodeRbtsVotePlaintext, normalizePredictedUpBps, packVoteRoundContext, parseTlockCiphertextMetadata, predictionPercentToBps, unpackVoteRoundContext, } from "./votingCore.js";
|
|
4
|
+
let tlockModulePromise;
|
|
5
|
+
const AGE_ARMOR_HEADER = "-----BEGIN AGE ENCRYPTED FILE-----";
|
|
6
|
+
const AGE_ARMOR_FOOTER = "-----END AGE ENCRYPTED FILE-----";
|
|
7
|
+
const MIN_ENCRYPTED_BODY_LENGTH = 65;
|
|
8
|
+
const MAINNET_QUICKNET_CHAIN_HASH = "52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971";
|
|
9
|
+
const TLOCK_JS_TESTNET_CHAIN_HASH = "7672797f548f3f4748ac4bf3352fc6c6b6468c9ad40ad456a397545c6e2df5bf";
|
|
10
|
+
const QUICKNET_T_CHAIN = {
|
|
11
|
+
url: "https://testnet-api.drand.cloudflare.com/cc9c398442737cbd141526600919edd69f1d6f9b4adb67e4d912fbc64341a9a5",
|
|
12
|
+
chainHash: "cc9c398442737cbd141526600919edd69f1d6f9b4adb67e4d912fbc64341a9a5",
|
|
13
|
+
publicKey: "b15b65b46fb29104f6a4b5d1e11a8da6344463973d423661bb0804846a0ecd1ef93c25057f1c0baab2ac53e56c662b66072f6d84ee791a3382bfb055afab1e6a375538d8ffc451104ac971d2dc9b168e2d3246b0be2015969cbaac298f6502da",
|
|
14
|
+
};
|
|
15
|
+
const RATELOOP_TLOCK_USER_AGENT = "rateloop-tlock";
|
|
16
|
+
async function loadTlockModule() {
|
|
17
|
+
tlockModulePromise ??= import("tlock-js").then((module) => ({
|
|
18
|
+
HttpCachingChain: module.HttpCachingChain,
|
|
19
|
+
HttpChainClient: module.HttpChainClient,
|
|
20
|
+
mainnetClient: module.mainnetClient,
|
|
21
|
+
testnetClient: module.testnetClient,
|
|
22
|
+
timelockEncrypt: module.timelockEncrypt,
|
|
23
|
+
timelockDecrypt: module.timelockDecrypt,
|
|
24
|
+
}));
|
|
25
|
+
return tlockModulePromise;
|
|
26
|
+
}
|
|
27
|
+
function normalizeDrandChainHash(hash) {
|
|
28
|
+
if (!hash)
|
|
29
|
+
return null;
|
|
30
|
+
const normalized = hash.toLowerCase();
|
|
31
|
+
if (!/^0x[0-9a-f]{64}$/u.test(normalized)) {
|
|
32
|
+
throw new Error("Invalid drand chain hash");
|
|
33
|
+
}
|
|
34
|
+
return normalized.slice(2);
|
|
35
|
+
}
|
|
36
|
+
function normalizeOptionalPositiveBigInt(value, label) {
|
|
37
|
+
if (value == null)
|
|
38
|
+
return null;
|
|
39
|
+
const normalized = typeof value === "bigint" ? value : BigInt(Math.trunc(value));
|
|
40
|
+
if (normalized <= 0n) {
|
|
41
|
+
throw new Error(`Invalid drand ${label}`);
|
|
42
|
+
}
|
|
43
|
+
return normalized;
|
|
44
|
+
}
|
|
45
|
+
function createHttpTlockClient(tlockModule, chain) {
|
|
46
|
+
const options = {
|
|
47
|
+
disableBeaconVerification: false,
|
|
48
|
+
noCache: false,
|
|
49
|
+
chainVerificationParams: {
|
|
50
|
+
chainHash: chain.chainHash,
|
|
51
|
+
publicKey: chain.publicKey,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
const httpChain = new tlockModule.HttpCachingChain(chain.url, options);
|
|
55
|
+
return new tlockModule.HttpChainClient(httpChain, options, {
|
|
56
|
+
userAgent: RATELOOP_TLOCK_USER_AGENT,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function resolveTlockClientForRuntime(tlockModule, runtime = {}) {
|
|
60
|
+
if (runtime.client) {
|
|
61
|
+
return runtime.client;
|
|
62
|
+
}
|
|
63
|
+
const expectedHash = normalizeDrandChainHash(runtime.drandChainHash);
|
|
64
|
+
if (!expectedHash || expectedHash === MAINNET_QUICKNET_CHAIN_HASH) {
|
|
65
|
+
return tlockModule.mainnetClient();
|
|
66
|
+
}
|
|
67
|
+
if (expectedHash === QUICKNET_T_CHAIN.chainHash) {
|
|
68
|
+
return createHttpTlockClient(tlockModule, QUICKNET_T_CHAIN);
|
|
69
|
+
}
|
|
70
|
+
if (expectedHash === TLOCK_JS_TESTNET_CHAIN_HASH) {
|
|
71
|
+
return tlockModule.testnetClient();
|
|
72
|
+
}
|
|
73
|
+
throw new Error(`Unsupported drand chain 0x${expectedHash}. Update ProtocolConfig to drand quicknet or quicknet-t before voting.`);
|
|
74
|
+
}
|
|
75
|
+
function assertTlockChainInfoMatchesRuntime(chainInfo, runtime = {}) {
|
|
76
|
+
const expectedHash = normalizeDrandChainHash(runtime.drandChainHash);
|
|
77
|
+
if (!expectedHash)
|
|
78
|
+
return;
|
|
79
|
+
const actualHash = chainInfo.hash.toLowerCase();
|
|
80
|
+
if (actualHash !== expectedHash) {
|
|
81
|
+
throw new Error(`Tlock client chain 0x${actualHash} does not match vote round drand chain 0x${expectedHash}.`);
|
|
82
|
+
}
|
|
83
|
+
const expectedGenesisTime = normalizeOptionalPositiveBigInt(runtime.drandGenesisTimeSeconds, "genesis time");
|
|
84
|
+
if (expectedGenesisTime != null &&
|
|
85
|
+
BigInt(chainInfo.genesis_time) !== expectedGenesisTime) {
|
|
86
|
+
throw new Error(`Tlock client genesis ${chainInfo.genesis_time} does not match vote round drand genesis ${expectedGenesisTime.toString()}.`);
|
|
87
|
+
}
|
|
88
|
+
const expectedPeriod = normalizeOptionalPositiveBigInt(runtime.drandPeriodSeconds, "period");
|
|
89
|
+
if (expectedPeriod != null && BigInt(chainInfo.period) !== expectedPeriod) {
|
|
90
|
+
throw new Error(`Tlock client period ${chainInfo.period} does not match vote round drand period ${expectedPeriod.toString()}.`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export async function getVoteTlockChainInfo(runtime = {}) {
|
|
94
|
+
const tlockModule = await loadTlockModule();
|
|
95
|
+
const client = resolveTlockClientForRuntime(tlockModule, runtime);
|
|
96
|
+
const chainInfo = await client.chain().info();
|
|
97
|
+
assertTlockChainInfoMatchesRuntime(chainInfo, runtime);
|
|
98
|
+
return {
|
|
99
|
+
periodSeconds: BigInt(chainInfo.period),
|
|
100
|
+
genesisTimeSeconds: BigInt(chainInfo.genesis_time),
|
|
101
|
+
drandChainHash: `0x${chainInfo.hash.toLowerCase()}`,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
async function createTlockVoteArtifacts(isUp, predictedUpBps, salt, epochDurationSeconds, runtime = {}) {
|
|
105
|
+
const tlockModule = await loadTlockModule();
|
|
106
|
+
const { timelockEncrypt } = tlockModule;
|
|
107
|
+
const client = resolveTlockClientForRuntime(tlockModule, runtime);
|
|
108
|
+
const now = runtime.now ?? Date.now;
|
|
109
|
+
const encryptFn = runtime.encryptFn ?? timelockEncrypt;
|
|
110
|
+
const chainInfo = await client.chain().info();
|
|
111
|
+
assertTlockChainInfoMatchesRuntime(chainInfo, runtime);
|
|
112
|
+
const targetRound = runtime.targetRound != null
|
|
113
|
+
? normalizeTlockTargetRound(runtime.targetRound)
|
|
114
|
+
: deriveAcceptedTlockTargetRound(now(), epochDurationSeconds, chainInfo, runtime.roundStartTimeSeconds, runtime.candidateTimestampOffsetsSeconds);
|
|
115
|
+
const armored = await encryptFn(targetRound, encodeRbtsVotePlaintext(isUp, predictedUpBps, salt), client);
|
|
116
|
+
const ciphertext = stringToHex(armored);
|
|
117
|
+
return {
|
|
118
|
+
ciphertext,
|
|
119
|
+
ciphertextHash: keccak256(ciphertext),
|
|
120
|
+
targetRound: BigInt(targetRound),
|
|
121
|
+
drandChainHash: `0x${chainInfo.hash.toLowerCase()}`,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function roundAtOrAfter(targetTimeMs, chainInfo) {
|
|
125
|
+
if (!Number.isFinite(targetTimeMs)) {
|
|
126
|
+
throw new Error("Cannot use Infinity or NaN as a beacon time");
|
|
127
|
+
}
|
|
128
|
+
const genesisTimeMs = chainInfo.genesis_time * 1000;
|
|
129
|
+
const periodMs = chainInfo.period * 1000;
|
|
130
|
+
if (!Number.isFinite(genesisTimeMs) ||
|
|
131
|
+
!Number.isFinite(periodMs) ||
|
|
132
|
+
periodMs <= 0) {
|
|
133
|
+
throw new Error("Invalid tlock chain timing");
|
|
134
|
+
}
|
|
135
|
+
if (targetTimeMs < genesisTimeMs) {
|
|
136
|
+
throw new Error("Cannot request a round before the genesis time");
|
|
137
|
+
}
|
|
138
|
+
return Math.ceil((targetTimeMs - genesisTimeMs) / periodMs) + 1;
|
|
139
|
+
}
|
|
140
|
+
function roundAt(targetTimeMs, chainInfo) {
|
|
141
|
+
const genesisTimeMs = chainInfo.genesis_time * 1000;
|
|
142
|
+
const periodMs = chainInfo.period * 1000;
|
|
143
|
+
if (!Number.isFinite(genesisTimeMs) ||
|
|
144
|
+
!Number.isFinite(periodMs) ||
|
|
145
|
+
periodMs <= 0) {
|
|
146
|
+
throw new Error("Invalid tlock chain timing");
|
|
147
|
+
}
|
|
148
|
+
if (targetTimeMs < genesisTimeMs) {
|
|
149
|
+
throw new Error("Cannot request a round before the genesis time");
|
|
150
|
+
}
|
|
151
|
+
return Math.floor((targetTimeMs - genesisTimeMs) / periodMs) + 1;
|
|
152
|
+
}
|
|
153
|
+
function normalizeRoundStartTimeMs(roundStartTimeSeconds) {
|
|
154
|
+
if (roundStartTimeSeconds == null)
|
|
155
|
+
return null;
|
|
156
|
+
const normalized = typeof roundStartTimeSeconds === "bigint"
|
|
157
|
+
? Number(roundStartTimeSeconds)
|
|
158
|
+
: Number(roundStartTimeSeconds);
|
|
159
|
+
if (!Number.isFinite(normalized) || normalized <= 0)
|
|
160
|
+
return null;
|
|
161
|
+
return Math.floor(normalized) * 1000;
|
|
162
|
+
}
|
|
163
|
+
function deriveRevealableAfterMs(commitTimeMs, epochDurationSeconds, roundStartTimeMs) {
|
|
164
|
+
const epochDurationMs = Math.max(1, Math.floor(epochDurationSeconds)) * 1000;
|
|
165
|
+
const anchorTimeMs = roundStartTimeMs ?? commitTimeMs;
|
|
166
|
+
const elapsedMs = Math.max(0, commitTimeMs - anchorTimeMs);
|
|
167
|
+
const epochIndex = Math.floor(elapsedMs / epochDurationMs);
|
|
168
|
+
return anchorTimeMs + (epochIndex + 1) * epochDurationMs;
|
|
169
|
+
}
|
|
170
|
+
function deriveAcceptedTlockTargetRound(nowMs, epochDurationSeconds, chainInfo, roundStartTimeSeconds, candidateTimestampOffsetsSeconds) {
|
|
171
|
+
if (!Number.isFinite(nowMs)) {
|
|
172
|
+
throw new Error("Cannot use Infinity or NaN as a beacon time");
|
|
173
|
+
}
|
|
174
|
+
const roundStartTimeMs = normalizeRoundStartTimeMs(roundStartTimeSeconds);
|
|
175
|
+
const drandPeriodMs = Math.max(1, Math.floor(chainInfo.period)) * 1000;
|
|
176
|
+
const candidateOffsets = candidateTimestampOffsetsSeconds &&
|
|
177
|
+
candidateTimestampOffsetsSeconds.length > 0
|
|
178
|
+
? candidateTimestampOffsetsSeconds
|
|
179
|
+
: buildDefaultCandidateTimestampOffsetsSeconds(chainInfo.period);
|
|
180
|
+
let minAcceptedTargetRound = 0;
|
|
181
|
+
let maxAcceptedTargetRound = 0;
|
|
182
|
+
for (const offsetSeconds of candidateOffsets) {
|
|
183
|
+
const commitTimeMs = nowMs + Math.floor(offsetSeconds) * 1000;
|
|
184
|
+
const revealableAfterMs = deriveRevealableAfterMs(commitTimeMs, epochDurationSeconds, roundStartTimeMs);
|
|
185
|
+
const minTargetRound = roundAtOrAfter(revealableAfterMs, chainInfo);
|
|
186
|
+
const maxTargetRound = roundAt(revealableAfterMs + 2 * drandPeriodMs, chainInfo);
|
|
187
|
+
if (minTargetRound <= 0 ||
|
|
188
|
+
maxTargetRound <= 0 ||
|
|
189
|
+
minTargetRound > maxTargetRound) {
|
|
190
|
+
throw new Error("No valid drand target round for the commit window");
|
|
191
|
+
}
|
|
192
|
+
minAcceptedTargetRound = Math.max(minAcceptedTargetRound, minTargetRound);
|
|
193
|
+
maxAcceptedTargetRound =
|
|
194
|
+
maxAcceptedTargetRound === 0
|
|
195
|
+
? maxTargetRound
|
|
196
|
+
: Math.min(maxAcceptedTargetRound, maxTargetRound);
|
|
197
|
+
}
|
|
198
|
+
if (minAcceptedTargetRound === 0 ||
|
|
199
|
+
minAcceptedTargetRound > maxAcceptedTargetRound) {
|
|
200
|
+
throw new Error("No shared drand target round for commit windows");
|
|
201
|
+
}
|
|
202
|
+
return maxAcceptedTargetRound;
|
|
203
|
+
}
|
|
204
|
+
function buildDefaultCandidateTimestampOffsetsSeconds(drandPeriodSeconds) {
|
|
205
|
+
const safePeriodSeconds = Math.max(1, Math.floor(drandPeriodSeconds));
|
|
206
|
+
return Array.from({ length: safePeriodSeconds }, (_, index) => index);
|
|
207
|
+
}
|
|
208
|
+
function normalizeTlockTargetRound(targetRound) {
|
|
209
|
+
const normalized = typeof targetRound === "bigint"
|
|
210
|
+
? Number(targetRound)
|
|
211
|
+
: Number.isInteger(targetRound)
|
|
212
|
+
? targetRound
|
|
213
|
+
: Number.NaN;
|
|
214
|
+
if (!Number.isSafeInteger(normalized) || normalized <= 0) {
|
|
215
|
+
throw new Error("targetRound must be a positive safe integer");
|
|
216
|
+
}
|
|
217
|
+
return normalized;
|
|
218
|
+
}
|
|
219
|
+
export async function tlockEncryptVote(isUp, predictedUpBps, salt, epochDurationSeconds, runtime = {}) {
|
|
220
|
+
const { ciphertext } = await createTlockVoteArtifacts(isUp, predictedUpBps, salt, epochDurationSeconds, runtime);
|
|
221
|
+
return ciphertext;
|
|
222
|
+
}
|
|
223
|
+
export async function decryptTlockVoteCiphertext(ciphertext, runtime = {}) {
|
|
224
|
+
const tlockModule = await loadTlockModule();
|
|
225
|
+
const { timelockDecrypt } = tlockModule;
|
|
226
|
+
const client = resolveTlockClientForRuntime(tlockModule, runtime);
|
|
227
|
+
const decryptFn = runtime.decryptFn ?? timelockDecrypt;
|
|
228
|
+
const armored = hexToString(ciphertext);
|
|
229
|
+
// Cheap structural sanity check before handing the payload to the tlock library.
|
|
230
|
+
// The age armor header alone is ~36 chars and the smallest valid body is bounded by
|
|
231
|
+
// MIN_ENCRYPTED_BODY_LENGTH, so anything shorter than the armor framing + body
|
|
232
|
+
// floor cannot plausibly decrypt to our 36-byte RBTS plaintext.
|
|
233
|
+
if (armored.length <
|
|
234
|
+
AGE_ARMOR_HEADER.length +
|
|
235
|
+
AGE_ARMOR_FOOTER.length +
|
|
236
|
+
MIN_ENCRYPTED_BODY_LENGTH) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
if (!armored.includes(AGE_ARMOR_HEADER) ||
|
|
240
|
+
!armored.includes(AGE_ARMOR_FOOTER)) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
if (runtime.drandChainHash) {
|
|
244
|
+
const chainInfo = await client.chain().info();
|
|
245
|
+
assertTlockChainInfoMatchesRuntime(chainInfo, runtime);
|
|
246
|
+
}
|
|
247
|
+
const plaintext = await decryptFn(armored, client);
|
|
248
|
+
return decodeRbtsVotePlaintext(plaintext);
|
|
249
|
+
}
|
|
250
|
+
export async function createTlockVoteCommit(params, runtime = {}) {
|
|
251
|
+
const { ciphertext, ciphertextHash, targetRound, drandChainHash } = await createTlockVoteArtifacts(params.isUp, params.predictedUpBps, params.salt, params.epochDurationSeconds, runtime);
|
|
252
|
+
const commitHash = buildCommitHash(params.isUp, params.predictedUpBps, params.salt, params.voter, params.contentId, params.roundId, params.roundReferenceRatingBps, targetRound, drandChainHash, ciphertext);
|
|
253
|
+
return {
|
|
254
|
+
ciphertext,
|
|
255
|
+
ciphertextHash,
|
|
256
|
+
commitHash,
|
|
257
|
+
targetRound,
|
|
258
|
+
drandChainHash,
|
|
259
|
+
roundReferenceRatingBps: params.roundReferenceRatingBps,
|
|
260
|
+
commitKey: buildCommitKey(params.voter, commitHash),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
export async function createTlockRbtsVoteCommit(params, runtime = {}) {
|
|
264
|
+
const predictedUpBps = normalizePredictedUpBps(params.predictedUpBps);
|
|
265
|
+
const { ciphertext, ciphertextHash, targetRound, drandChainHash } = await createTlockVoteArtifacts(params.isUp, predictedUpBps, params.salt, params.epochDurationSeconds, runtime);
|
|
266
|
+
const commitHash = buildRbtsCommitHash(params.isUp, predictedUpBps, params.salt, params.voter, params.contentId, params.roundId, params.roundReferenceRatingBps, targetRound, drandChainHash, ciphertext);
|
|
267
|
+
return {
|
|
268
|
+
ciphertext,
|
|
269
|
+
ciphertextHash,
|
|
270
|
+
commitHash,
|
|
271
|
+
targetRound,
|
|
272
|
+
drandChainHash,
|
|
273
|
+
roundReferenceRatingBps: params.roundReferenceRatingBps,
|
|
274
|
+
isUp: params.isUp,
|
|
275
|
+
predictedUpBps,
|
|
276
|
+
predictedUpPercent: bpsToPredictionPercent(predictedUpBps),
|
|
277
|
+
commitKey: buildCommitKey(params.voter, commitHash),
|
|
278
|
+
};
|
|
279
|
+
}
|