11import { createPublicClient , createWalletClient , http , Hex , getContract } from "viem" ;
2- import { privateKeyToAccount } from "viem/accounts" ;
2+ import { mnemonicToAccount } from "viem/accounts" ;
33import { hardhat } from "viem/chains" ;
44import { encrypt , decrypt , DECRYPTION_DELAY } from "./shutter" ;
55import { abi as DisputeKitShutterPoCAbi } from "../deployments/localhost/DisputeKitShutterPoC.json" ;
@@ -9,35 +9,35 @@ import crypto from "crypto";
99const SEPARATOR = "␟" ; // U+241F
1010
1111// Store encrypted votes for later decryption
12- interface EncryptedVote {
12+ type EncryptedVote = {
1313 encryptedCommitment : string ;
1414 identity : Hex ;
1515 timestamp : number ;
16- voteId : bigint ;
16+ voteIDs : bigint [ ] ;
1717 salt : Hex ;
18- }
18+ } ;
1919
2020const encryptedVotes : EncryptedVote [ ] = [ ] ;
2121
22- const PRIVATE_KEY = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 " as const ;
22+ const disputeKitAddress = "0x5FbDB2315678afecb367f032d93F642f64180aa3 " as const ;
2323
24- const CONTRACT_ADDRESS = "0x5FbDB2315678afecb367f032d93F642f64180aa3" as const ;
24+ const MNEMONIC = "test test test test test test test test test test test junk" ;
25+ const account = mnemonicToAccount ( MNEMONIC ) ;
2526
2627const transport = http ( ) ;
2728const publicClient = createPublicClient ( {
2829 chain : hardhat ,
2930 transport,
3031} ) ;
3132
32- const account = privateKeyToAccount ( PRIVATE_KEY ) ;
3333const walletClient = createWalletClient ( {
3434 account,
3535 chain : hardhat ,
3636 transport,
3737} ) ;
3838
3939const disputeKit = getContract ( {
40- address : CONTRACT_ADDRESS ,
40+ address : disputeKitAddress ,
4141 abi : DisputeKitShutterPoCAbi ,
4242 client : { public : publicClient , wallet : walletClient } ,
4343} ) ;
@@ -49,6 +49,41 @@ function generateSalt(): Hex {
4949 return ( "0x" + crypto . randomBytes ( 32 ) . toString ( "hex" ) ) as Hex ;
5050}
5151
52+ /**
53+ * Encodes vote parameters into a message string with separators
54+ */
55+ function encode ( {
56+ coreDisputeID,
57+ voteIDs,
58+ choice,
59+ justification,
60+ salt,
61+ } : {
62+ coreDisputeID : bigint ;
63+ voteIDs : bigint [ ] ;
64+ choice : bigint ;
65+ justification : string ;
66+ salt : Hex ;
67+ } ) : string {
68+ return `${ coreDisputeID } ${ SEPARATOR } ${ voteIDs . join ( "," ) } ${ SEPARATOR } ${ choice } ${ SEPARATOR } ${ justification } ${ SEPARATOR } ${ salt } ` ;
69+ }
70+
71+ /**
72+ * Decodes a message string into its component parts
73+ * @param message The message to decode
74+ * @returns Object containing the decoded components
75+ */
76+ function decode ( message : string ) {
77+ const [ coreDisputeID , voteIDsStr , choice , justification , salt ] = message . split ( SEPARATOR ) ;
78+ return {
79+ coreDisputeID,
80+ voteIDs : voteIDsStr . split ( "," ) . map ( ( id ) => BigInt ( id ) ) ,
81+ choice,
82+ justification,
83+ salt,
84+ } ;
85+ }
86+
5287/**
5388 * Cast a commit on-chain
5489 */
@@ -64,15 +99,23 @@ async function castCommit({
6499 justification : string ;
65100} ) {
66101 try {
67- // Create message with U+241F separator
68- const message = `${ coreDisputeID } ${ SEPARATOR } ${ voteIDs [ 0 ] } ${ SEPARATOR } ${ choice } ${ SEPARATOR } ${ justification } ` ;
102+ // Generate salt first
103+ const salt = generateSalt ( ) ;
104+
105+ // Encode the vote parameters into a message
106+ const message = encode ( {
107+ coreDisputeID,
108+ voteIDs,
109+ choice,
110+ justification,
111+ salt,
112+ } ) ;
69113
70114 // Encrypt the message using shutter.ts
71115 const { encryptedCommitment, identity } = await encrypt ( message ) ;
72116
73- // Generate salt and compute hash
74- const salt = generateSalt ( ) ;
75- const commitHash = await disputeKit . read . hashVote ( [ coreDisputeID , voteIDs [ 0 ] , choice , justification , salt ] ) ;
117+ // Compute hash using all vote IDs
118+ const commitHash = await disputeKit . read . hashVote ( [ coreDisputeID , voteIDs , choice , justification , salt ] ) ;
76119
77120 // Cast the commit on-chain
78121 const txHash = await disputeKit . write . castCommit ( [ coreDisputeID , voteIDs , commitHash , identity as Hex ] ) ;
@@ -89,7 +132,7 @@ async function castCommit({
89132 encryptedCommitment,
90133 identity : identity as Hex ,
91134 timestamp : Math . floor ( Date . now ( ) / 1000 ) ,
92- voteId : voteIDs [ 0 ] ,
135+ voteIDs,
93136 salt,
94137 } ) ;
95138
@@ -116,16 +159,16 @@ export async function autoVote() {
116159 // Attempt to decrypt the vote
117160 const decryptedMessage = await decrypt ( vote . encryptedCommitment , vote . identity ) ;
118161
119- // Parse the decrypted message
120- const [ coreDisputeID , , choice , justification ] = decryptedMessage . split ( SEPARATOR ) ;
162+ // Decode the decrypted message
163+ const { coreDisputeID, voteIDs , choice, justification, salt } = decode ( decryptedMessage ) ;
121164
122165 // Cast the vote on-chain
123166 const txHash = await disputeKit . write . castVote ( [
124167 BigInt ( coreDisputeID ) ,
125- [ vote . voteId ] ,
168+ voteIDs ,
126169 BigInt ( choice ) ,
127170 justification ,
128- vote . salt ,
171+ salt ,
129172 ] ) ;
130173
131174 // Wait for transaction to be mined
@@ -139,7 +182,7 @@ export async function autoVote() {
139182 const index = encryptedVotes . indexOf ( vote ) ;
140183 if ( index > - 1 ) encryptedVotes . splice ( index , 1 ) ;
141184 } catch ( error ) {
142- console . error ( `Error processing vote ${ vote . voteId } :` , error ) ;
185+ console . error ( `Error processing vote ${ vote . voteIDs . join ( "," ) } :` , error ) ;
143186 }
144187 }
145188
@@ -158,9 +201,9 @@ async function main() {
158201 try {
159202 // Cast an encrypted commit
160203 await castCommit ( {
161- coreDisputeID : BigInt ( 0 ) ,
162- voteIDs : [ BigInt ( 0 ) ] ,
163- choice : BigInt ( 2 ) ,
204+ coreDisputeID : 0n ,
205+ voteIDs : [ 0n , 1n , 2n ] ,
206+ choice : 2n ,
164207 justification : "This is my vote justification" ,
165208 } ) ;
166209
0 commit comments