@collectorcrypt/vrf-client 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/src/index.ts ADDED
@@ -0,0 +1,80 @@
1
+ /**
2
+ * @collectorcrypt/vrf-client
3
+ *
4
+ * TypeScript SDK for the cc-vrf on-chain VRF program. Wraps the Anchor IDL,
5
+ * Light Protocol compressed-PDA plumbing, and the RFC 9381 ECVRF library so
6
+ * consumers can publish, commit, and verify VRF proofs with one import.
7
+ */
8
+
9
+ // Re-export the ECVRF primitives so consumers don't need a second import.
10
+ export {
11
+ generateKeyPair,
12
+ publicKeyFromSeed,
13
+ proveVRF,
14
+ verifyVRF,
15
+ vrfProofToHash,
16
+ bytesToHex,
17
+ hexToBytes,
18
+ } from "@collectorcrypt/ecvrf";
19
+
20
+ export { CC_VRF_PROGRAM_ID, SUITE_EDWARDS25519_SHA512_TAI } from "./constants";
21
+
22
+ export {
23
+ deriveAuthorityAddress,
24
+ deriveProofCommitAddress,
25
+ deriveProofCommitWithBetaAddress,
26
+ memoHash,
27
+ alphaHash,
28
+ proofHash,
29
+ encodeLabel,
30
+ } from "./addresses";
31
+
32
+ export {
33
+ forceLightV2,
34
+ buildCreateContext,
35
+ buildCommitProofContext,
36
+ buildReadOnlyAuthorityContext,
37
+ buildMutateContext,
38
+ } from "./light";
39
+
40
+ export { getProgram } from "./program";
41
+
42
+ export {
43
+ buildInitAuthorityIx,
44
+ buildFreezeAuthorityIx,
45
+ buildRevokeAuthorityIx,
46
+ buildCommitProofIx,
47
+ buildCommitProofEventIx,
48
+ buildCommitProofWithBetaIx,
49
+ fetchAuthority,
50
+ fetchProofCommit,
51
+ fetchProofCommitWithBeta,
52
+ fetchProofCommitEvents,
53
+ decodeAuthority,
54
+ decodeProofCommit,
55
+ decodeProofCommitWithBeta,
56
+ asTx,
57
+ } from "./operations";
58
+
59
+ export type {
60
+ InitAuthorityInput,
61
+ FreezeAuthorityInput,
62
+ CommitProofInput,
63
+ ProofCommitEvent,
64
+ } from "./operations";
65
+
66
+ export {
67
+ verifyEndToEnd,
68
+ verifyAuthorityCommitEndToEnd,
69
+ pickCanonicalCommit,
70
+ } from "./verifyEndToEnd";
71
+
72
+ export type {
73
+ OnChainAuthority,
74
+ OnChainCommit,
75
+ VerifyAuthorityCommitEndToEndInput,
76
+ VerifyAuthorityCommitEndToEndResult,
77
+ VerifyEndToEndInput,
78
+ VerifyEndToEndResult,
79
+ PickCanonicalResult,
80
+ } from "./verifyEndToEnd";
package/src/light.ts ADDED
@@ -0,0 +1,259 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ import {
3
+ batchAddressTree,
4
+ bn,
5
+ CompressedAccountWithMerkleContext,
6
+ featureFlags,
7
+ PackedAccounts,
8
+ Rpc,
9
+ selectStateTreeInfo,
10
+ SystemAccountMetaConfig,
11
+ VERSION,
12
+ } from "@lightprotocol/stateless.js";
13
+
14
+ /**
15
+ * Force the SDK into V2 mode so our address derivations + state-tree usage
16
+ * match the on-chain program's ADDRESS_TREE_V2 expectation.
17
+ */
18
+ export function forceLightV2() {
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ (featureFlags as any).version = VERSION.V2;
21
+ }
22
+
23
+ /**
24
+ * For CREATE flows (init_authority, commit_proof): builds the validity-proof
25
+ * bundle + packed remaining-accounts list needed to mint a new compressed
26
+ * PDA at the given address.
27
+ */
28
+ export async function buildCreateContext(
29
+ rpc: Rpc,
30
+ programId: PublicKey,
31
+ newCompressedAddress: PublicKey,
32
+ existingInputs: {
33
+ hash: Uint8Array;
34
+ tree: PublicKey;
35
+ queue: PublicKey;
36
+ }[] = [],
37
+ ) {
38
+ forceLightV2();
39
+ const addressTree = new PublicKey(batchAddressTree);
40
+ const proofRes = await rpc.getValidityProofV0(
41
+ existingInputs.map((i) => ({
42
+ hash: bn(i.hash),
43
+ tree: i.tree,
44
+ queue: i.queue,
45
+ })),
46
+ [
47
+ {
48
+ tree: addressTree,
49
+ queue: addressTree,
50
+ address: bn(newCompressedAddress.toBytes()),
51
+ },
52
+ ],
53
+ );
54
+
55
+ const stateTreeInfos = await rpc.getStateTreeInfos();
56
+ const stateTreeInfo = selectStateTreeInfo(stateTreeInfos);
57
+
58
+ const systemAccountConfig = SystemAccountMetaConfig.new(programId);
59
+ const remainingAccounts =
60
+ PackedAccounts.newWithSystemAccountsV2(systemAccountConfig);
61
+ const addressMtIdx = remainingAccounts.insertOrGet(addressTree);
62
+ const outputStIdx = remainingAccounts.insertOrGet(stateTreeInfo.queue);
63
+
64
+ const packedAddressTreeInfo = {
65
+ rootIndex: proofRes.rootIndices[proofRes.rootIndices.length - 1],
66
+ addressMerkleTreePubkeyIndex: addressMtIdx,
67
+ addressQueuePubkeyIndex: addressMtIdx,
68
+ };
69
+
70
+ return {
71
+ proof: { 0: proofRes.compressedProof },
72
+ packedAddressTreeInfo,
73
+ outputStateTreeIndex: outputStIdx,
74
+ remainingAccountMetas: remainingAccounts.toAccountMetas().remainingAccounts,
75
+ };
76
+ }
77
+
78
+ /**
79
+ * For commit_proof specifically: one input (the existing authority, treated
80
+ * as read-only on chain) + one new address (the commit). Both indices come
81
+ * from the same getValidityProofV0 call so they reference the same proof
82
+ * and the same packed-accounts list.
83
+ */
84
+ export async function buildCommitProofContext(
85
+ rpc: Rpc,
86
+ programId: PublicKey,
87
+ authorityExisting: CompressedAccountWithMerkleContext,
88
+ newCommitAddress: PublicKey,
89
+ ) {
90
+ forceLightV2();
91
+ const addressTree = new PublicKey(batchAddressTree);
92
+
93
+ const proofRes = await rpc.getValidityProofV0(
94
+ [
95
+ {
96
+ hash: authorityExisting.hash,
97
+ tree: authorityExisting.treeInfo.tree,
98
+ queue: authorityExisting.treeInfo.queue,
99
+ },
100
+ ],
101
+ [
102
+ {
103
+ tree: addressTree,
104
+ queue: addressTree,
105
+ address: bn(newCommitAddress.toBytes()),
106
+ },
107
+ ],
108
+ );
109
+
110
+ const stateTreeInfos = await rpc.getStateTreeInfos();
111
+ const stateTreeInfo = selectStateTreeInfo(stateTreeInfos);
112
+
113
+ const systemAccountConfig = SystemAccountMetaConfig.new(programId);
114
+ const remainingAccounts =
115
+ PackedAccounts.newWithSystemAccountsV2(systemAccountConfig);
116
+
117
+ // Order matters: insert in the order the program will reference them via
118
+ // packed indices. Both the authority's tree and the address tree are
119
+ // shared in V2 (batched merkle tree), but we still pack the auth's tree
120
+ // first since the authority meta is constructed first below.
121
+ const authMerkleTreePubkeyIndex = remainingAccounts.insertOrGet(
122
+ authorityExisting.treeInfo.tree,
123
+ );
124
+ const authQueuePubkeyIndex = remainingAccounts.insertOrGet(
125
+ authorityExisting.treeInfo.queue,
126
+ );
127
+ const addressMtIdx = remainingAccounts.insertOrGet(addressTree);
128
+ const outputStIdx = remainingAccounts.insertOrGet(stateTreeInfo.queue);
129
+
130
+ const authorityReadOnlyMeta = {
131
+ treeInfo: {
132
+ rootIndex: proofRes.rootIndices[0],
133
+ proveByIndex: true,
134
+ merkleTreePubkeyIndex: authMerkleTreePubkeyIndex,
135
+ queuePubkeyIndex: authQueuePubkeyIndex,
136
+ leafIndex: authorityExisting.leafIndex,
137
+ },
138
+ address: authorityExisting.address,
139
+ };
140
+
141
+ const packedAddressTreeInfo = {
142
+ rootIndex: proofRes.rootIndices[proofRes.rootIndices.length - 1],
143
+ addressMerkleTreePubkeyIndex: addressMtIdx,
144
+ addressQueuePubkeyIndex: addressMtIdx,
145
+ };
146
+
147
+ return {
148
+ proof: { 0: proofRes.compressedProof },
149
+ authorityReadOnlyMeta,
150
+ packedAddressTreeInfo,
151
+ outputStateTreeIndex: outputStIdx,
152
+ remainingAccountMetas: remainingAccounts.toAccountMetas().remainingAccounts,
153
+ };
154
+ }
155
+
156
+ /**
157
+ * For event-mode commits: builds a validity-proof bundle for an existing
158
+ * authority treated as read-only. No new compressed address is created.
159
+ */
160
+ export async function buildReadOnlyAuthorityContext(
161
+ rpc: Rpc,
162
+ programId: PublicKey,
163
+ authorityExisting: CompressedAccountWithMerkleContext,
164
+ ) {
165
+ forceLightV2();
166
+ const proofRes = await rpc.getValidityProofV0(
167
+ [
168
+ {
169
+ hash: authorityExisting.hash,
170
+ tree: authorityExisting.treeInfo.tree,
171
+ queue: authorityExisting.treeInfo.queue,
172
+ },
173
+ ],
174
+ [],
175
+ );
176
+
177
+ const systemAccountConfig = SystemAccountMetaConfig.new(programId);
178
+ const remainingAccounts =
179
+ PackedAccounts.newWithSystemAccountsV2(systemAccountConfig);
180
+ const authMerkleTreePubkeyIndex = remainingAccounts.insertOrGet(
181
+ authorityExisting.treeInfo.tree,
182
+ );
183
+ const authQueuePubkeyIndex = remainingAccounts.insertOrGet(
184
+ authorityExisting.treeInfo.queue,
185
+ );
186
+
187
+ const authorityReadOnlyMeta = {
188
+ treeInfo: {
189
+ rootIndex: proofRes.rootIndices[0],
190
+ proveByIndex: true,
191
+ merkleTreePubkeyIndex: authMerkleTreePubkeyIndex,
192
+ queuePubkeyIndex: authQueuePubkeyIndex,
193
+ leafIndex: authorityExisting.leafIndex,
194
+ },
195
+ address: authorityExisting.address,
196
+ };
197
+
198
+ return {
199
+ proof: { 0: proofRes.compressedProof },
200
+ authorityReadOnlyMeta,
201
+ remainingAccountMetas: remainingAccounts.toAccountMetas().remainingAccounts,
202
+ };
203
+ }
204
+
205
+ /**
206
+ * For MUTATE flows (freeze_authority, revoke_authority): builds the
207
+ * validity-proof bundle + account meta for an existing compressed PDA.
208
+ */
209
+ export async function buildMutateContext(
210
+ rpc: Rpc,
211
+ programId: PublicKey,
212
+ existing: CompressedAccountWithMerkleContext,
213
+ ) {
214
+ forceLightV2();
215
+ const proofRes = await rpc.getValidityProofV0(
216
+ [
217
+ {
218
+ hash: existing.hash,
219
+ tree: existing.treeInfo.tree,
220
+ queue: existing.treeInfo.queue,
221
+ },
222
+ ],
223
+ [],
224
+ );
225
+
226
+ const stateTreeInfos = await rpc.getStateTreeInfos();
227
+ const stateTreeInfo = selectStateTreeInfo(stateTreeInfos);
228
+
229
+ const systemAccountConfig = SystemAccountMetaConfig.new(programId);
230
+ const remainingAccounts =
231
+ PackedAccounts.newWithSystemAccountsV2(systemAccountConfig);
232
+ const merkleTreePubkeyIndex = remainingAccounts.insertOrGet(
233
+ existing.treeInfo.tree,
234
+ );
235
+ const queuePubkeyIndex = remainingAccounts.insertOrGet(
236
+ existing.treeInfo.queue,
237
+ );
238
+ const outputStateTreeIndex = remainingAccounts.insertOrGet(
239
+ stateTreeInfo.queue,
240
+ );
241
+
242
+ const accountMeta = {
243
+ treeInfo: {
244
+ rootIndex: proofRes.rootIndices[0],
245
+ proveByIndex: true,
246
+ merkleTreePubkeyIndex,
247
+ queuePubkeyIndex,
248
+ leafIndex: existing.leafIndex,
249
+ },
250
+ address: existing.address,
251
+ outputStateTreeIndex,
252
+ };
253
+
254
+ return {
255
+ proof: { 0: proofRes.compressedProof },
256
+ accountMeta,
257
+ remainingAccountMetas: remainingAccounts.toAccountMetas().remainingAccounts,
258
+ };
259
+ }