-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* submit application & use useSmartAccount hook * Use Hex & Address types * Sumbit votes * Remove unnecessary todos
- Loading branch information
1 parent
a6787b5
commit 791d8a2
Showing
6 changed files
with
175 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
import { | ||
IPublishBatchArgs, | ||
IPublishBatchData, | ||
PubKey, | ||
MACI__factory as MACIFactory, | ||
Poll__factory as PollFactory, | ||
} from "maci-cli/sdk"; | ||
import { | ||
IG1ContractParams, | ||
IMessageContractParams, | ||
Keypair, | ||
PCommand, | ||
PrivKey, | ||
} from "maci-domainobjs"; | ||
import { genRandomSalt } from "maci-crypto"; | ||
import { publicClient } from "./permissionless"; | ||
import { SmartAccountClient } from "permissionless"; | ||
import { EntryPoint } from "permissionless/types"; | ||
import { KernelEcdsaSmartAccount } from "permissionless/accounts"; | ||
import { Address, HttpTransport, Chain } from "viem"; | ||
|
||
const MESSAGE_TREE_ARITY = 5; | ||
|
||
type ISmartAccountPublishBatchArgs = IPublishBatchArgs & { | ||
smartAccount: KernelEcdsaSmartAccount<EntryPoint, HttpTransport, Chain>; | ||
smartAccountClient: SmartAccountClient<EntryPoint, HttpTransport, Chain>; | ||
}; | ||
|
||
|
||
/** | ||
* @notice copied from maci-cli/sdk to add sponsorship | ||
* Batch publish new messages to a MACI Poll contract | ||
* @param {IPublishBatchArgs} args - The arguments for the publish command | ||
* @returns {IPublishBatchData} The ephemeral private key used to encrypt the message, transaction hash | ||
*/ | ||
export const publishBatch = async ({ | ||
messages, | ||
pollId, | ||
maciAddress, | ||
publicKey, | ||
privateKey, | ||
signer, | ||
quiet = true, | ||
smartAccount, | ||
smartAccountClient, | ||
}: ISmartAccountPublishBatchArgs): Promise<IPublishBatchData> => { | ||
if (!PubKey.isValidSerializedPubKey(publicKey)) { | ||
throw new Error("invalid MACI public key"); | ||
} | ||
|
||
if (!PrivKey.isValidSerializedPrivKey(privateKey)) { | ||
throw new Error("invalid MACI private key"); | ||
} | ||
|
||
if (pollId < 0n) { | ||
throw new Error(`invalid poll id ${pollId}`); | ||
} | ||
|
||
const userMaciPubKey = PubKey.deserialize(publicKey); | ||
const userMaciPrivKey = PrivKey.deserialize(privateKey); | ||
const maciContract = MACIFactory.connect(maciAddress, signer); | ||
const pollContracts = await maciContract.getPoll(pollId); | ||
|
||
const pollContract = PollFactory.connect(pollContracts.poll, signer); | ||
|
||
const [treeDepths, coordinatorPubKeyResult] = await Promise.all([ | ||
pollContract.treeDepths(), | ||
pollContract.coordinatorPubKey(), | ||
]); | ||
const maxVoteOptions = Number( | ||
BigInt(MESSAGE_TREE_ARITY) ** treeDepths.voteOptionTreeDepth | ||
); | ||
|
||
// validate the vote options index against the max leaf index on-chain | ||
messages.forEach(({ stateIndex, voteOptionIndex, salt, nonce }) => { | ||
if (voteOptionIndex < 0 || maxVoteOptions < voteOptionIndex) { | ||
throw new Error("invalid vote option index"); | ||
} | ||
|
||
// check < 1 cause index zero is a blank state leaf | ||
if (stateIndex < 1) { | ||
throw new Error("invalid state index"); | ||
} | ||
|
||
if (nonce < 0) { | ||
throw new Error("invalid nonce"); | ||
} | ||
}); | ||
|
||
const coordinatorPubKey = new PubKey([ | ||
BigInt(coordinatorPubKeyResult.x.toString()), | ||
BigInt(coordinatorPubKeyResult.y.toString()), | ||
]); | ||
|
||
const encryptionKeypair = new Keypair(); | ||
const sharedKey = Keypair.genEcdhSharedKey( | ||
encryptionKeypair.privKey, | ||
coordinatorPubKey | ||
); | ||
|
||
const payload: [IMessageContractParams, IG1ContractParams][] = messages.map( | ||
({ salt, stateIndex, voteOptionIndex, newVoteWeight, nonce }) => { | ||
const userSalt = salt ? BigInt(salt) : genRandomSalt(); | ||
|
||
// create the command object | ||
const command = new PCommand( | ||
stateIndex, | ||
userMaciPubKey, | ||
voteOptionIndex, | ||
newVoteWeight, | ||
nonce, | ||
BigInt(pollId), | ||
userSalt | ||
); | ||
|
||
// sign the command with the user private key | ||
const signature = command.sign(userMaciPrivKey); | ||
|
||
const message = command.encrypt(signature, sharedKey); | ||
|
||
return [ | ||
message.asContractParam(), | ||
encryptionKeypair.pubKey.asContractParam(), | ||
]; | ||
} | ||
); | ||
|
||
const preparedMessages = payload.map(([message]) => message); | ||
const preparedKeys = payload.map(([, key]) => key); | ||
|
||
// TODO: (merge-ok) make this type casting/handling nicer | ||
const reversedMessages = preparedMessages.reverse().map((item) => ({ | ||
data: item.data.map((val) => BigInt(val)) as [ | ||
bigint, | ||
bigint, | ||
bigint, | ||
bigint, | ||
bigint, | ||
bigint, | ||
bigint, | ||
bigint, | ||
bigint, | ||
bigint, | ||
], | ||
})); | ||
const reversedKeys = preparedKeys.reverse() as readonly { | ||
x: bigint; | ||
y: bigint; | ||
}[]; | ||
|
||
const { request } = await publicClient.simulateContract({ | ||
account: smartAccount, | ||
address: pollContracts.poll as Address, | ||
abi: PollFactory.abi, | ||
functionName: "publishMessageBatch", | ||
args: [reversedMessages, reversedKeys], | ||
}); | ||
const txHash = await smartAccountClient.writeContract(request); | ||
|
||
return { | ||
hash: txHash, | ||
encryptedMessages: preparedMessages, | ||
privateKey: encryptionKeypair.privKey.serialize(), | ||
}; | ||
}; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.