Skip to content

Commit

Permalink
create scripts for interacting with Tokenized Ballots
Browse files Browse the repository at this point in the history
  • Loading branch information
trliner committed Aug 23, 2024
1 parent 0b8eb8c commit 6e603b4
Show file tree
Hide file tree
Showing 10 changed files with 775 additions and 0 deletions.
86 changes: 86 additions & 0 deletions scripts/CastVote.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// npx ts-node --files ./scripts/CastVote.ts CONTRACT_ADDRESS PROPOSAL_INDEX AMOUNT

import { createPublicClient, http, createWalletClient, hexToString } from "viem";

import { privateKeyToAccount } from "viem/accounts";
import { sepolia } from "viem/chains";
import { abi } from "../artifacts/contracts/TokenizedBallot.sol/TokenizedBallot.json";
import * as dotenv from "dotenv";
dotenv.config();

const providerApiKey = process.env.ALCHEMY_API_KEY || "";
const voterPrivateKey = process.env.PRIVATE_KEY || "";

function validateParameters(parameters: string[]) {
if (!parameters || parameters.length < 3)
throw new Error("Parameters not provided");

const contractAddress = parameters[0] as `0x${string}`;
if (!contractAddress) throw new Error("Contract address not provided");
if (!/^0x[a-fA-F0-9]{40}$/.test(contractAddress))
throw new Error("Invalid contract address");

const proposalIndex = parameters[1];
if (isNaN(Number(proposalIndex))) throw new Error("Invalid proposal index");

const amount = parameters[2];
if (isNaN(Number(amount))) throw new Error("Invalid amount");

return { contractAddress, proposalIndex, amount };
}

async function main() {
console.log("\n");
const { contractAddress, proposalIndex, amount } = validateParameters(process.argv.slice(2));

const publicClient = createPublicClient({
chain: sepolia,
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
});

const proposal = (await publicClient.readContract({
address: contractAddress,
abi,
functionName: "proposals",
args: [BigInt(proposalIndex)],
})) as any[];
const proposalName = hexToString(proposal[0], { size: 32 });

const account = privateKeyToAccount(`0x${voterPrivateKey}`);
const sender = createWalletClient({
account,
chain: sepolia,
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
});

console.log(`Voting for proposal '${proposalName}' with ${amount} votes`);
console.log("Confirm? (Y/n)");

const stdin = process.stdin;
stdin.on("data", async function (d) {
if (d.toString().trim() == "Y") {
const hash = await sender.writeContract({
address: contractAddress,
abi,
functionName: "vote",
args: [proposalIndex, amount],
});
console.log("Transaction hash:", hash);
console.log("Waiting for confirmations...");
const publicClient = createPublicClient({
chain: sepolia,
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
});
const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log(`Transaction confirmed: ${receipt.status}`);
} else {
console.log("Operation cancelled");
}
process.exit();
});
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
73 changes: 73 additions & 0 deletions scripts/DelegateVotes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// npx ts-node --files ./scripts/DelegateVotes.ts CONTRACT_ADDRESS DELEGATE_ADDRESS

import { createPublicClient, http, createWalletClient } from "viem";

import { privateKeyToAccount } from "viem/accounts";
import { sepolia } from "viem/chains";
import { abi } from "../artifacts/contracts/MyToken.sol/MyToken.json";
import * as dotenv from "dotenv";
dotenv.config();

const providerApiKey = process.env.ALCHEMY_API_KEY || "";
const delegatorPrivateKey = process.env.PRIVATE_KEY || "";

function validateParameters(parameters: string[]) {
if (!parameters || parameters.length < 2)
throw new Error("Parameters not provided");

const contractAddress = parameters[0] as `0x${string}`;
if (!contractAddress) throw new Error("Contract address not provided");
if (!/^0x[a-fA-F0-9]{40}$/.test(contractAddress))
throw new Error("Invalid contract address");

const delegateAddress = parameters[1] as `0x${string}`;
if (!delegateAddress) throw new Error("Delegate address not provided");
if (!/^0x[a-fA-F0-9]{40}$/.test(delegateAddress))
throw new Error("Invalid delegate address");

return { contractAddress, delegateAddress }
}

async function main() {
console.log("\n");
const { contractAddress, delegateAddress } = validateParameters(process.argv.slice(2));

const account = privateKeyToAccount(`0x${delegatorPrivateKey}`);
const sender = createWalletClient({
account,
chain: sepolia,
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
});

console.log(`Delegating from ${account.address} tokens to account: ${delegateAddress}`);
console.log("Confirm? (Y/n)");

const stdin = process.stdin;
stdin.on("data", async function (d) {
if (d.toString().trim() == "Y") {
const hash = await sender.writeContract({
address: contractAddress,
abi,
functionName: "delegate",
args: [delegateAddress],
});
console.log("Transaction hash:", hash);
console.log("Waiting for confirmations...");
const publicClient = createPublicClient({
chain: sepolia,
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
});
const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log(`Transaction confirmed: ${receipt.status}`);
console.log(`Block: ${receipt.blockNumber}`)
} else {
console.log("Operation cancelled");
}
process.exit();
});
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
53 changes: 53 additions & 0 deletions scripts/DeployMyToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// npx ts-node --files ./scripts/DeployMyToken.ts

import { createPublicClient, http, createWalletClient, formatEther } from "viem";

import { privateKeyToAccount } from "viem/accounts";
import { sepolia } from "viem/chains";
import { abi, bytecode } from "../artifacts/contracts/MyToken.sol/MyToken.json";
import * as dotenv from "dotenv";
dotenv.config();

const providerApiKey = process.env.ALCHEMY_API_KEY || "";
const deployerPrivateKey = process.env.PRIVATE_KEY || "";

async function main() {
const publicClient = createPublicClient({
chain: sepolia,
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
});
const blockNumber = await publicClient.getBlockNumber();
console.log("Last block number:", blockNumber);

const account = privateKeyToAccount(`0x${deployerPrivateKey}`);
const deployer = createWalletClient({
account,
chain: sepolia,
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
});
console.log("Deployer address:", deployer.account.address);
const balance = await publicClient.getBalance({
address: deployer.account.address,
});
console.log(
"Deployer balance:",
formatEther(balance),
deployer.chain.nativeCurrency.symbol
);

console.log("\nDeploying Token contract");
const hash = await deployer.deployContract({
abi,
bytecode: bytecode as `0x${string}`,
});
console.log("Transaction hash:", hash);
console.log("Waiting for confirmations...");
const receipt = await publicClient.waitForTransactionReceipt({ hash });
const contractAddress = receipt.contractAddress;
console.log("Token contract deployed to:", contractAddress);
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
91 changes: 91 additions & 0 deletions scripts/DeployTokenizedBallot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// npx ts-node --files ./scripts/DeployTokenizedBallot.ts TOKEN_CONTRACT TARGET_BLOCK_NUMBER PROPOSAL_NAMES

import { createPublicClient, http, createWalletClient, formatEther, toHex } from "viem";

import { privateKeyToAccount } from "viem/accounts";
import { sepolia } from "viem/chains";
import { abi, bytecode } from "../artifacts/contracts/TokenizedBallot.sol/TokenizedBallot.json";
import * as dotenv from "dotenv";
dotenv.config();

const providerApiKey = process.env.ALCHEMY_API_KEY || "";
const deployerPrivateKey = process.env.PRIVATE_KEY || "";

function validateParameters(parameters: string[]) {
if (!parameters || parameters.length < 3)
throw new Error("Parameters not provided");

const tokenAddress = parameters[0] as `0x${string}`;
if (!tokenAddress) throw new Error("Token address not provided");
if (!/^0x[a-fA-F0-9]{40}$/.test(tokenAddress))
throw new Error("Invalid token address");

const targetBlockNumber = parameters[1];
if (isNaN(Number(targetBlockNumber))) throw new Error("Invalid target block number");

const proposals = parameters.slice(2);
if (!proposals || proposals.length < 1)
throw new Error("Proposals not provided");

return { tokenAddress, targetBlockNumber, proposals }
}

async function main() {
console.log("\n");
const { tokenAddress, targetBlockNumber, proposals } = validateParameters(process.argv.slice(2));

console.log(`Deploying ballot with proposals: ${proposals}`);
console.log("Confirm? (Y/n)");

const stdin = process.stdin;
stdin.on("data", async function (d) {
if (d.toString().trim() == "Y") {
const publicClient = createPublicClient({
chain: sepolia,
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
});
const blockNumber = await publicClient.getBlockNumber();
console.log("Last block number:", blockNumber);

const account = privateKeyToAccount(`0x${deployerPrivateKey}`);
const deployer = createWalletClient({
account,
chain: sepolia,
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
});
console.log("Deployer address:", deployer.account.address);
const balance = await publicClient.getBalance({
address: deployer.account.address,
});
console.log(
"Deployer balance:",
formatEther(balance),
deployer.chain.nativeCurrency.symbol
);

console.log("\nDeploying Ballot contract");
const hash = await deployer.deployContract({
abi,
bytecode: bytecode as `0x${string}`,
args: [
proposals.map((prop) => toHex(prop, { size: 32 })),
tokenAddress,
targetBlockNumber
]
});
console.log("Transaction hash:", hash);
console.log("Waiting for confirmations...");
const receipt = await publicClient.waitForTransactionReceipt({ hash });
const contractAddress = receipt.contractAddress;
console.log("Ballot contract deployed to:", contractAddress);
} else {
console.log("Operation cancelled");
}
process.exit();
});
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
75 changes: 75 additions & 0 deletions scripts/GrantMinterRole.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// npx ts-node --files ./scripts/GrantMinterRole.ts TOKEN_ADDRESS MINTER_ADDRESS

import { createPublicClient, http, createWalletClient, keccak256, toHex } from "viem";

import { privateKeyToAccount } from "viem/accounts";
import { sepolia } from "viem/chains";
import { abi } from "../artifacts/contracts/MyToken.sol/MyToken.json";
import * as dotenv from "dotenv";
dotenv.config();

const providerApiKey = process.env.ALCHEMY_API_KEY || "";
const delegatorPrivateKey = process.env.PRIVATE_KEY || "";

const MINTER_ROLE = keccak256(toHex("MINTER_ROLE"));;

function validateParameters(parameters: string[]) {
if (!parameters || parameters.length < 2)
throw new Error("Parameters not provided");

const tokenAddress = parameters[0] as `0x${string}`;
if (!tokenAddress) throw new Error("Token address not provided");
if (!/^0x[a-fA-F0-9]{40}$/.test(tokenAddress))
throw new Error("Invalid token address");

const minterAddress = parameters[1] as `0x${string}`;
if (!minterAddress) throw new Error("Minter address not provided");
if (!/^0x[a-fA-F0-9]{40}$/.test(minterAddress))
throw new Error("Invalid minter address");

return { tokenAddress, minterAddress }
}

async function main() {
console.log("\n");
const { tokenAddress, minterAddress } = validateParameters(process.argv.slice(2));

const account = privateKeyToAccount(`0x${delegatorPrivateKey}`);
const sender = createWalletClient({
account,
chain: sepolia,
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
});

console.log(`Granting minter role to ${minterAddress}`);
console.log("Confirm? (Y/n)");

const stdin = process.stdin;
stdin.on("data", async function (d) {
if (d.toString().trim() == "Y") {
const hash = await sender.writeContract({
address: tokenAddress,
abi,
functionName: "grantRole",
args: [MINTER_ROLE, minterAddress],
});
console.log("Transaction hash:", hash);
console.log("Waiting for confirmations...");
const publicClient = createPublicClient({
chain: sepolia,
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
});
const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log(`Transaction confirmed: ${receipt.status}`);
console.log(`Block: ${receipt.blockNumber}`)
} else {
console.log("Operation cancelled");
}
process.exit();
});
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Loading

0 comments on commit 6e603b4

Please sign in to comment.