Skip to content

Commit

Permalink
add test simulation script
Browse files Browse the repository at this point in the history
  • Loading branch information
yuetloo committed Dec 13, 2023
1 parent 92f757b commit b80aec1
Show file tree
Hide file tree
Showing 2 changed files with 223 additions and 0 deletions.
1 change: 1 addition & 0 deletions contracts/tasks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ import './tally'
import './findStorageSlot'
import './setStorageRoot'
import './loadMerkleUsers'
import './simulateVoting'
222 changes: 222 additions & 0 deletions contracts/tasks/simulateVoting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
/**
* This script is used to simulate mass voting for testing purposes.
*
* sameple usage:
* yarn hardhat simulate-voting --fee 0.003 \
* --coordinator-pub-key <Coordinator's MACI serialized public key> \
* --total 20 \
* --factory <funding round factory contract address> \
* --contributors 30 \
* --recipients 2 \
* --network arbitrum-goerli
*/
import { task, types } from 'hardhat/config'
import { createMessage, PubKey, Keypair } from '@clrfund/maci-utils'
import { HDNode } from 'ethers/lib/utils'
import { getEventArg } from '../utils/contracts'

function getRandomRecipient(min: number, max: number) {
return Math.floor(Math.random() * (max - min + 1) + min)
}

task('simulate-voting', 'Simulate voting for the current round')
.addParam(
'factory',
'The funding round factory contract address',
undefined,
types.string
)
.addParam(
'contributors',
'The number of contributors to simulate',
5,
types.int
)
.addParam('recipients', 'The number of recipients to simulate', 2, types.int)
.addParam(
'total',
'The total amount of tokens to run this test with',
undefined,
types.string
)
.addParam(
'fee',
'The amount to fund the contributor wallets to pay for transaction fee'
)
.addParam('coordinatorPubKey', 'The MACI public key of the coordinator')
.setAction(
async (
{ factory, recipients, contributors, total, fee, coordinatorPubKey },
{ ethers }
) => {
const mnemonic = process.env.WALLET_MNEMONIC || ''
const [coordinator] = await ethers.getSigners()
console.log('coordinator', coordinator.address)

const pubKey = PubKey.unserialize(coordinatorPubKey)

const factoryContract = await ethers.getContractAt(
'FundingRoundFactory',
factory
)
const roundAddress = await factoryContract.getCurrentRound()
console.log('Current funding round', roundAddress)

const fundingRound = await ethers.getContractAt(
'FundingRound',
roundAddress,
coordinator
)
const maciAddress = await fundingRound.maci()
const maciContract = await ethers.getContractAt('MACI', maciAddress)
const tokenAddress = await fundingRound.nativeToken()
const token = await ethers.getContractAt(
'ERC20',
tokenAddress,
coordinator
)
const userRegistryAddress = await fundingRound.userRegistry()
const userRegistry = await ethers.getContractAt(
'SimpleUserRegistry',
userRegistryAddress
)

const decimals = await token.decimals()
const totalAmountInUnits = ethers.utils.parseUnits(total, decimals)
const contributionAmount = totalAmountInUnits.div(contributors)
const feeInUnits = ethers.utils.parseUnits(fee)
const hdnode = HDNode.fromMnemonic(mnemonic)
console.log('contribution amount', contributionAmount.toString())

for (let i = 1; i <= contributors; i += 1) {
const contributorHdNode = hdnode.derivePath(
ethers.utils.getAccountPath(i)
)
const contributor = new ethers.Wallet(
contributorHdNode.privateKey,
ethers.provider
)

// register user if not registered
const isVerifiedUser = await userRegistry.isVerifiedUser(
contributor.address
)
if (!isVerifiedUser) {
const addUserTx = await userRegistry.addUser(contributor.address)
await addUserTx.wait()
}
console.log('Contributor', i, contributor.address)

// fund wallet
const contributorBalance = await ethers.provider.getBalance(
contributor.address
)
console.log('Account balance', contributorBalance.toString())
if (contributorBalance.lt(feeInUnits)) {
console.log('funding wallet', feeInUnits.toString())
const fundTx = await coordinator.sendTransaction({
to: contributor.address,
value: feeInUnits,
})
await fundTx.wait()
}

// transfer usdc
const tokenBalance = await token.balanceOf(contributor.address)
console.log('Token balance', tokenBalance.toString())
if (tokenBalance.lt(contributionAmount)) {
const transferTx = await token.transfer(
contributor.address,
contributionAmount
)
await transferTx.wait()
console.log('transfered token')
}

// contribute
const allowance = await token.allowance(
contributor.address,
fundingRound.address
)
console.log('Token allowance', allowance.toString())

if (allowance.lt(contributionAmount)) {
const tokenAsContributor = token.connect(contributor)
console.log('Approving token..')
const approveTx = await tokenAsContributor.approve(
fundingRound.address,
contributionAmount
)
await approveTx.wait()
console.log('Approved token to funding rond')
}

const contributorStatus = await fundingRound.contributors(
contributor.address
)

console.log(
'Previously contributed voice credits',
contributorStatus.voiceCredits.toString()
)

if (contributorStatus.voiceCredits.gt(0)) {
console.log(`${contributor.address} already contributed`)
continue
}
const contributorKeypair = new Keypair()
console.log(
'Contributor privake key',
contributorKeypair.privKey.serialize()
)
const fundingRoundAsContributor = fundingRound.connect(contributor)
const contributionTx = await fundingRoundAsContributor.contribute(
contributorKeypair.pubKey.asContractParam(),
contributionAmount
)
const stateIndex = await getEventArg(
contributionTx,
maciContract,
'SignUp',
'_stateIndex'
)
const voiceCredits = await getEventArg(
contributionTx,
maciContract,
'SignUp',
'_voiceCreditBalance'
)
console.log('stateIndex', stateIndex.toString())
console.log('voiceCredits', voiceCredits.toString())

// get random project between 1 and recipient count
const recipientIndex = getRandomRecipient(1, recipients)
console.log('Recipient index', recipientIndex)

// create message
const nonce = 1
const [message, encPubKey] = createMessage(
stateIndex,
contributorKeypair,
null,
pubKey,
recipientIndex,
voiceCredits,
nonce
)

// vote
console.log('sending votes')
const voteTx = await fundingRoundAsContributor.submitMessageBatch(
[message.asContractParam()],
[encPubKey.asContractParam()]
)
await voteTx.wait()
console.log(
i,
`${contributor.address} voted with ${contributionAmount} credits`
)
console.log('')
}
}
)

0 comments on commit b80aec1

Please sign in to comment.