Skip to content

Commit

Permalink
refactor: user functions
Browse files Browse the repository at this point in the history
  • Loading branch information
ctrlc03 committed Jan 24, 2025
1 parent 8841796 commit 0d72188
Show file tree
Hide file tree
Showing 11 changed files with 468 additions and 70 deletions.
51 changes: 41 additions & 10 deletions packages/cli/ts/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env node

import { Command } from "@commander-js/extra-typings";
import { generateTallyCommitments, getPollParams, verify, getPoll } from "maci-sdk";
import { generateTallyCommitments, getPollParams, verify, getPoll, isUserRegistered, signup } from "maci-sdk";

import fs from "fs";
import path from "path";
Expand All @@ -20,8 +20,6 @@ import {
setVerifyingKeys,
mergeSignups,
timeTravel,
signup,
isRegisteredUser,
genProofs,
fundWallet,
proveOnChain,
Expand All @@ -31,7 +29,17 @@ import {
joinPoll,
isJoinedUser,
} from "./commands";
import { TallyData, banner, logError, logGreen, promptSensitiveValue, readContractAddress, success } from "./utils";
import {
DEFAULT_SG_DATA,
TallyData,
banner,
logError,
logGreen,
logRed,
promptSensitiveValue,
readContractAddress,
success,
} from "./utils";

// set the description version and name of the cli tool
const { description, version, name } = JSON.parse(
Expand Down Expand Up @@ -465,13 +473,17 @@ program

const maciAddress = cmdObj.maciAddress || (await readContractAddress("MACI", network?.name));

await signup({
const data = await signup({
maciPubKey: cmdObj.pubkey,
maciAddress,
sgDataArg: cmdObj.sgData,
quiet: cmdObj.quiet,
sgData: cmdObj.sgData ?? DEFAULT_SG_DATA,
signer,
});

logGreen(
cmdObj.quiet,
success(`State index: ${data.stateIndex.toString()}\n Transaction hash: ${data.transactionHash}`),
);
} catch (error) {
program.error((error as Error).message, { exitCode: 1 });
}
Expand All @@ -489,12 +501,17 @@ program

const maciAddress = cmdObj.maciAddress || (await readContractAddress("MACI", network?.name));

await isRegisteredUser({
const data = await isUserRegistered({
maciPubKey: cmdObj.pubkey,
maciAddress,
signer,
quiet: cmdObj.quiet,
});

if (data.isRegistered) {
logGreen(cmdObj.quiet, success(`State index: ${data.stateIndex?.toString()}`));
} else {
logRed(cmdObj.quiet, "User is not registered");
}
} catch (error) {
program.error((error as Error).message, { exitCode: 1 });
}
Expand All @@ -516,14 +533,28 @@ program

const maciAddress = cmdObj.maciAddress || (await readContractAddress("MACI", network?.name));

await isJoinedUser({
const data = await isJoinedUser({
pollPubKey: cmdObj.pubkey,
startBlock: cmdObj.startBlock!,
maciAddress,
pollId: cmdObj.pollId,
signer,
quiet: cmdObj.quiet,
});

if (data.isJoined) {
logGreen(
cmdObj.quiet,
success(
[
`Poll state index: ${data.pollStateIndex?.toString()}, registered: ${data.isJoined}`,
`Voice credits: ${data.voiceCredits?.toString()}`,
].join("\n"),
),
);
} else {
logRed(cmdObj.quiet, "User has not joined the poll");
}
} catch (error) {
program.error((error as Error).message, { exitCode: 1 });
}
Expand Down
8 changes: 2 additions & 6 deletions packages/sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,22 @@
"docs": "typedoc --plugin typedoc-plugin-markdown --options ./typedoc.json"
},
"dependencies": {
"@commander-js/extra-typings": "^12.1.0",
"@nomicfoundation/hardhat-toolbox": "^5.0.0",
"commander": "^12.1.0",
"dotenv": "^16.4.5",
"ethers": "^6.13.4",
"hardhat": "^2.22.8",
"maci-contracts": "^2.5.0",
"maci-crypto": "^2.5.0"
"maci-crypto": "^2.5.0",
"maci-domainobjs": "^2.5.0"
},
"devDependencies": {
"@types/chai": "^4.3.9",
"@types/chai-as-promised": "^7.1.8",
"@types/mocha": "^10.0.8",
"@types/node": "^22.9.0",
"@types/snarkjs": "^0.7.8",
"chai": "^4.3.10",
"chai-as-promised": "^7.1.2",
"mocha": "^10.7.3",
"nyc": "^17.1.0",
"snarkjs": "^0.7.5",
"ts-mocha": "^10.0.0",
"typescript": "^5.6.3"
},
Expand Down
3 changes: 2 additions & 1 deletion packages/sdk/ts/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export { getPoll, getPollParams } from "./poll";
export { verify } from "./verify";
export { generateTallyCommitments } from "./tallyCommitments";
export { isUserRegistered, isJoinedUser, signup } from "./user";

export {
linkPoseidonLibraries,
Expand All @@ -13,4 +14,4 @@ export {

export * from "maci-contracts/typechain-types";

export type { TallyData, VerifyArgs, IGetPollArgs, IGetPollData } from "./utils";
export type { TallyData, VerifyArgs, IGetPollArgs, IGetPollData, IIsRegisteredUser, IIsJoinedUser } from "./utils";
122 changes: 122 additions & 0 deletions packages/sdk/ts/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { ContractTransactionReceipt, isBytesLike } from "ethers";
import { MACI__factory as MACIFactory, Poll__factory as PollFactory } from "maci-contracts/typechain-types";
import { PubKey } from "maci-domainobjs";

import { contractExists, IRegisteredUserArgs, ISignupArgs, parsePollJoinEvents, parseSignupEvents } from "./utils";
import { IIsRegisteredUser, IJoinedUserArgs, ISignupData } from "./utils/interfaces";

/**
* Checks if user is registered with a given public key
* @param IRegisteredArgs - The arguments for the check register command
* @returns whether the user is registered or not and their state index
*/
export const isUserRegistered = async ({
maciAddress,
maciPubKey,
signer,
startBlock,
}: IRegisteredUserArgs): Promise<IIsRegisteredUser> => {
const maciContract = MACIFactory.connect(maciAddress, signer);
const publicKey = PubKey.deserialize(maciPubKey);
const startBlockNumber = startBlock || 0;
const currentBlock = await signer.provider!.getBlockNumber();

const { stateIndex } = await parseSignupEvents({
maciContract,
startBlock: startBlockNumber,
currentBlock,
publicKey,
});

return {
isRegistered: stateIndex !== undefined,
stateIndex,
};
};

/**
* Signup a user to the MACI contract
* @param {SignupArgs} args - The arguments for the signup command
* @returns {ISignupData} The state index of the user and transaction hash
*/
export const signup = async ({ maciPubKey, maciAddress, sgData, signer }: ISignupArgs): Promise<ISignupData> => {
// validate user key
if (!PubKey.isValidSerializedPubKey(maciPubKey)) {
throw new Error("Invalid MACI public key");
}

const userMaciPubKey = PubKey.deserialize(maciPubKey);

const validContract = await contractExists(signer.provider!, maciAddress);
if (!validContract) {
throw new Error("There is no contract deployed at the specified address");
}

// we validate that the signup data and voice credit data is valid
if (!isBytesLike(sgData)) {
throw new Error("invalid signup gateway data");
}

const maciContract = MACIFactory.connect(maciAddress, signer);

let stateIndex = "";
let receipt: ContractTransactionReceipt | null = null;

try {
// sign up to the MACI contract
const tx = await maciContract.signUp(userMaciPubKey.asContractParam(), sgData);
receipt = await tx.wait();

if (receipt?.status !== 1) {
throw new Error("The transaction failed");
}

const iface = maciContract.interface;

// get state index from the event
const [log] = receipt.logs;
const { args } = iface.parseLog(log as unknown as { topics: string[]; data: string }) || { args: [] };
[stateIndex, ,] = args;
} catch (error) {
throw new Error((error as Error).message);
}

return {
stateIndex: stateIndex ? stateIndex.toString() : "",
transactionHash: receipt.hash,
};
};

/**
* Checks if user is joined to a poll with the public key
* @param {IJoinedUserArgs} - The arguments for the join check command
* @returns user joined or not and poll state index, voice credit balance
*/
export const isJoinedUser = async ({
maciAddress,
pollId,
pollPubKey,
signer,
startBlock,
}: IJoinedUserArgs): Promise<{ isJoined: boolean; pollStateIndex?: string; voiceCredits?: string }> => {
const maciContract = MACIFactory.connect(maciAddress, signer);
const pollContracts = await maciContract.getPoll(pollId);
const pollContract = PollFactory.connect(pollContracts.poll, signer);

const pollPublicKey = PubKey.deserialize(pollPubKey);
const startBlockNumber = startBlock || 0;
const currentBlock = await signer.provider!.getBlockNumber();

const { pollStateIndex, voiceCredits } = await parsePollJoinEvents({
pollContract,
startBlock: startBlockNumber,
currentBlock,
pollPublicKey,
});

return {
isJoined: pollStateIndex !== undefined,
pollStateIndex,
voiceCredits,
};
};
1 change: 1 addition & 0 deletions packages/sdk/ts/utils/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const BLOCKS_STEP = 1000;
10 changes: 10 additions & 0 deletions packages/sdk/ts/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,15 @@ export type {
IGetPollParamsArgs,
ITallyCommitments,
IPollParams,
IRegisteredUserArgs,
IParseSignupEventsArgs,
ISignupData,
ISignupArgs,
IJoinedUserArgs,
IParsePollJoinEventsArgs,
IIsRegisteredUser,
IIsJoinedUser,
} from "./interfaces";
export { verifyPerVOSpentVoiceCredits, verifyTallyResults } from "./verifiers";
export { BLOCKS_STEP } from "./constants";
export { parsePollJoinEvents, parseSignupEvents } from "./user";
Loading

0 comments on commit 0d72188

Please sign in to comment.