Skip to content

Commit 6e603b4

Browse files
committed
create scripts for interacting with Tokenized Ballots
1 parent 0b8eb8c commit 6e603b4

10 files changed

+775
-0
lines changed

scripts/CastVote.ts

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// npx ts-node --files ./scripts/CastVote.ts CONTRACT_ADDRESS PROPOSAL_INDEX AMOUNT
2+
3+
import { createPublicClient, http, createWalletClient, hexToString } from "viem";
4+
5+
import { privateKeyToAccount } from "viem/accounts";
6+
import { sepolia } from "viem/chains";
7+
import { abi } from "../artifacts/contracts/TokenizedBallot.sol/TokenizedBallot.json";
8+
import * as dotenv from "dotenv";
9+
dotenv.config();
10+
11+
const providerApiKey = process.env.ALCHEMY_API_KEY || "";
12+
const voterPrivateKey = process.env.PRIVATE_KEY || "";
13+
14+
function validateParameters(parameters: string[]) {
15+
if (!parameters || parameters.length < 3)
16+
throw new Error("Parameters not provided");
17+
18+
const contractAddress = parameters[0] as `0x${string}`;
19+
if (!contractAddress) throw new Error("Contract address not provided");
20+
if (!/^0x[a-fA-F0-9]{40}$/.test(contractAddress))
21+
throw new Error("Invalid contract address");
22+
23+
const proposalIndex = parameters[1];
24+
if (isNaN(Number(proposalIndex))) throw new Error("Invalid proposal index");
25+
26+
const amount = parameters[2];
27+
if (isNaN(Number(amount))) throw new Error("Invalid amount");
28+
29+
return { contractAddress, proposalIndex, amount };
30+
}
31+
32+
async function main() {
33+
console.log("\n");
34+
const { contractAddress, proposalIndex, amount } = validateParameters(process.argv.slice(2));
35+
36+
const publicClient = createPublicClient({
37+
chain: sepolia,
38+
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
39+
});
40+
41+
const proposal = (await publicClient.readContract({
42+
address: contractAddress,
43+
abi,
44+
functionName: "proposals",
45+
args: [BigInt(proposalIndex)],
46+
})) as any[];
47+
const proposalName = hexToString(proposal[0], { size: 32 });
48+
49+
const account = privateKeyToAccount(`0x${voterPrivateKey}`);
50+
const sender = createWalletClient({
51+
account,
52+
chain: sepolia,
53+
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
54+
});
55+
56+
console.log(`Voting for proposal '${proposalName}' with ${amount} votes`);
57+
console.log("Confirm? (Y/n)");
58+
59+
const stdin = process.stdin;
60+
stdin.on("data", async function (d) {
61+
if (d.toString().trim() == "Y") {
62+
const hash = await sender.writeContract({
63+
address: contractAddress,
64+
abi,
65+
functionName: "vote",
66+
args: [proposalIndex, amount],
67+
});
68+
console.log("Transaction hash:", hash);
69+
console.log("Waiting for confirmations...");
70+
const publicClient = createPublicClient({
71+
chain: sepolia,
72+
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
73+
});
74+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
75+
console.log(`Transaction confirmed: ${receipt.status}`);
76+
} else {
77+
console.log("Operation cancelled");
78+
}
79+
process.exit();
80+
});
81+
}
82+
83+
main().catch((error) => {
84+
console.error(error);
85+
process.exitCode = 1;
86+
});

scripts/DelegateVotes.ts

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// npx ts-node --files ./scripts/DelegateVotes.ts CONTRACT_ADDRESS DELEGATE_ADDRESS
2+
3+
import { createPublicClient, http, createWalletClient } from "viem";
4+
5+
import { privateKeyToAccount } from "viem/accounts";
6+
import { sepolia } from "viem/chains";
7+
import { abi } from "../artifacts/contracts/MyToken.sol/MyToken.json";
8+
import * as dotenv from "dotenv";
9+
dotenv.config();
10+
11+
const providerApiKey = process.env.ALCHEMY_API_KEY || "";
12+
const delegatorPrivateKey = process.env.PRIVATE_KEY || "";
13+
14+
function validateParameters(parameters: string[]) {
15+
if (!parameters || parameters.length < 2)
16+
throw new Error("Parameters not provided");
17+
18+
const contractAddress = parameters[0] as `0x${string}`;
19+
if (!contractAddress) throw new Error("Contract address not provided");
20+
if (!/^0x[a-fA-F0-9]{40}$/.test(contractAddress))
21+
throw new Error("Invalid contract address");
22+
23+
const delegateAddress = parameters[1] as `0x${string}`;
24+
if (!delegateAddress) throw new Error("Delegate address not provided");
25+
if (!/^0x[a-fA-F0-9]{40}$/.test(delegateAddress))
26+
throw new Error("Invalid delegate address");
27+
28+
return { contractAddress, delegateAddress }
29+
}
30+
31+
async function main() {
32+
console.log("\n");
33+
const { contractAddress, delegateAddress } = validateParameters(process.argv.slice(2));
34+
35+
const account = privateKeyToAccount(`0x${delegatorPrivateKey}`);
36+
const sender = createWalletClient({
37+
account,
38+
chain: sepolia,
39+
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
40+
});
41+
42+
console.log(`Delegating from ${account.address} tokens to account: ${delegateAddress}`);
43+
console.log("Confirm? (Y/n)");
44+
45+
const stdin = process.stdin;
46+
stdin.on("data", async function (d) {
47+
if (d.toString().trim() == "Y") {
48+
const hash = await sender.writeContract({
49+
address: contractAddress,
50+
abi,
51+
functionName: "delegate",
52+
args: [delegateAddress],
53+
});
54+
console.log("Transaction hash:", hash);
55+
console.log("Waiting for confirmations...");
56+
const publicClient = createPublicClient({
57+
chain: sepolia,
58+
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
59+
});
60+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
61+
console.log(`Transaction confirmed: ${receipt.status}`);
62+
console.log(`Block: ${receipt.blockNumber}`)
63+
} else {
64+
console.log("Operation cancelled");
65+
}
66+
process.exit();
67+
});
68+
}
69+
70+
main().catch((error) => {
71+
console.error(error);
72+
process.exitCode = 1;
73+
});

scripts/DeployMyToken.ts

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// npx ts-node --files ./scripts/DeployMyToken.ts
2+
3+
import { createPublicClient, http, createWalletClient, formatEther } from "viem";
4+
5+
import { privateKeyToAccount } from "viem/accounts";
6+
import { sepolia } from "viem/chains";
7+
import { abi, bytecode } from "../artifacts/contracts/MyToken.sol/MyToken.json";
8+
import * as dotenv from "dotenv";
9+
dotenv.config();
10+
11+
const providerApiKey = process.env.ALCHEMY_API_KEY || "";
12+
const deployerPrivateKey = process.env.PRIVATE_KEY || "";
13+
14+
async function main() {
15+
const publicClient = createPublicClient({
16+
chain: sepolia,
17+
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
18+
});
19+
const blockNumber = await publicClient.getBlockNumber();
20+
console.log("Last block number:", blockNumber);
21+
22+
const account = privateKeyToAccount(`0x${deployerPrivateKey}`);
23+
const deployer = createWalletClient({
24+
account,
25+
chain: sepolia,
26+
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
27+
});
28+
console.log("Deployer address:", deployer.account.address);
29+
const balance = await publicClient.getBalance({
30+
address: deployer.account.address,
31+
});
32+
console.log(
33+
"Deployer balance:",
34+
formatEther(balance),
35+
deployer.chain.nativeCurrency.symbol
36+
);
37+
38+
console.log("\nDeploying Token contract");
39+
const hash = await deployer.deployContract({
40+
abi,
41+
bytecode: bytecode as `0x${string}`,
42+
});
43+
console.log("Transaction hash:", hash);
44+
console.log("Waiting for confirmations...");
45+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
46+
const contractAddress = receipt.contractAddress;
47+
console.log("Token contract deployed to:", contractAddress);
48+
}
49+
50+
main().catch((error) => {
51+
console.error(error);
52+
process.exitCode = 1;
53+
});

scripts/DeployTokenizedBallot.ts

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// npx ts-node --files ./scripts/DeployTokenizedBallot.ts TOKEN_CONTRACT TARGET_BLOCK_NUMBER PROPOSAL_NAMES
2+
3+
import { createPublicClient, http, createWalletClient, formatEther, toHex } from "viem";
4+
5+
import { privateKeyToAccount } from "viem/accounts";
6+
import { sepolia } from "viem/chains";
7+
import { abi, bytecode } from "../artifacts/contracts/TokenizedBallot.sol/TokenizedBallot.json";
8+
import * as dotenv from "dotenv";
9+
dotenv.config();
10+
11+
const providerApiKey = process.env.ALCHEMY_API_KEY || "";
12+
const deployerPrivateKey = process.env.PRIVATE_KEY || "";
13+
14+
function validateParameters(parameters: string[]) {
15+
if (!parameters || parameters.length < 3)
16+
throw new Error("Parameters not provided");
17+
18+
const tokenAddress = parameters[0] as `0x${string}`;
19+
if (!tokenAddress) throw new Error("Token address not provided");
20+
if (!/^0x[a-fA-F0-9]{40}$/.test(tokenAddress))
21+
throw new Error("Invalid token address");
22+
23+
const targetBlockNumber = parameters[1];
24+
if (isNaN(Number(targetBlockNumber))) throw new Error("Invalid target block number");
25+
26+
const proposals = parameters.slice(2);
27+
if (!proposals || proposals.length < 1)
28+
throw new Error("Proposals not provided");
29+
30+
return { tokenAddress, targetBlockNumber, proposals }
31+
}
32+
33+
async function main() {
34+
console.log("\n");
35+
const { tokenAddress, targetBlockNumber, proposals } = validateParameters(process.argv.slice(2));
36+
37+
console.log(`Deploying ballot with proposals: ${proposals}`);
38+
console.log("Confirm? (Y/n)");
39+
40+
const stdin = process.stdin;
41+
stdin.on("data", async function (d) {
42+
if (d.toString().trim() == "Y") {
43+
const publicClient = createPublicClient({
44+
chain: sepolia,
45+
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
46+
});
47+
const blockNumber = await publicClient.getBlockNumber();
48+
console.log("Last block number:", blockNumber);
49+
50+
const account = privateKeyToAccount(`0x${deployerPrivateKey}`);
51+
const deployer = createWalletClient({
52+
account,
53+
chain: sepolia,
54+
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
55+
});
56+
console.log("Deployer address:", deployer.account.address);
57+
const balance = await publicClient.getBalance({
58+
address: deployer.account.address,
59+
});
60+
console.log(
61+
"Deployer balance:",
62+
formatEther(balance),
63+
deployer.chain.nativeCurrency.symbol
64+
);
65+
66+
console.log("\nDeploying Ballot contract");
67+
const hash = await deployer.deployContract({
68+
abi,
69+
bytecode: bytecode as `0x${string}`,
70+
args: [
71+
proposals.map((prop) => toHex(prop, { size: 32 })),
72+
tokenAddress,
73+
targetBlockNumber
74+
]
75+
});
76+
console.log("Transaction hash:", hash);
77+
console.log("Waiting for confirmations...");
78+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
79+
const contractAddress = receipt.contractAddress;
80+
console.log("Ballot contract deployed to:", contractAddress);
81+
} else {
82+
console.log("Operation cancelled");
83+
}
84+
process.exit();
85+
});
86+
}
87+
88+
main().catch((error) => {
89+
console.error(error);
90+
process.exitCode = 1;
91+
});

scripts/GrantMinterRole.ts

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// npx ts-node --files ./scripts/GrantMinterRole.ts TOKEN_ADDRESS MINTER_ADDRESS
2+
3+
import { createPublicClient, http, createWalletClient, keccak256, toHex } from "viem";
4+
5+
import { privateKeyToAccount } from "viem/accounts";
6+
import { sepolia } from "viem/chains";
7+
import { abi } from "../artifacts/contracts/MyToken.sol/MyToken.json";
8+
import * as dotenv from "dotenv";
9+
dotenv.config();
10+
11+
const providerApiKey = process.env.ALCHEMY_API_KEY || "";
12+
const delegatorPrivateKey = process.env.PRIVATE_KEY || "";
13+
14+
const MINTER_ROLE = keccak256(toHex("MINTER_ROLE"));;
15+
16+
function validateParameters(parameters: string[]) {
17+
if (!parameters || parameters.length < 2)
18+
throw new Error("Parameters not provided");
19+
20+
const tokenAddress = parameters[0] as `0x${string}`;
21+
if (!tokenAddress) throw new Error("Token address not provided");
22+
if (!/^0x[a-fA-F0-9]{40}$/.test(tokenAddress))
23+
throw new Error("Invalid token address");
24+
25+
const minterAddress = parameters[1] as `0x${string}`;
26+
if (!minterAddress) throw new Error("Minter address not provided");
27+
if (!/^0x[a-fA-F0-9]{40}$/.test(minterAddress))
28+
throw new Error("Invalid minter address");
29+
30+
return { tokenAddress, minterAddress }
31+
}
32+
33+
async function main() {
34+
console.log("\n");
35+
const { tokenAddress, minterAddress } = validateParameters(process.argv.slice(2));
36+
37+
const account = privateKeyToAccount(`0x${delegatorPrivateKey}`);
38+
const sender = createWalletClient({
39+
account,
40+
chain: sepolia,
41+
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
42+
});
43+
44+
console.log(`Granting minter role to ${minterAddress}`);
45+
console.log("Confirm? (Y/n)");
46+
47+
const stdin = process.stdin;
48+
stdin.on("data", async function (d) {
49+
if (d.toString().trim() == "Y") {
50+
const hash = await sender.writeContract({
51+
address: tokenAddress,
52+
abi,
53+
functionName: "grantRole",
54+
args: [MINTER_ROLE, minterAddress],
55+
});
56+
console.log("Transaction hash:", hash);
57+
console.log("Waiting for confirmations...");
58+
const publicClient = createPublicClient({
59+
chain: sepolia,
60+
transport: http(`https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`),
61+
});
62+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
63+
console.log(`Transaction confirmed: ${receipt.status}`);
64+
console.log(`Block: ${receipt.blockNumber}`)
65+
} else {
66+
console.log("Operation cancelled");
67+
}
68+
process.exit();
69+
});
70+
}
71+
72+
main().catch((error) => {
73+
console.error(error);
74+
process.exitCode = 1;
75+
});

0 commit comments

Comments
 (0)