Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions containers/vote.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,11 @@ function useVote() {
support,
})
// If successful
.then(() => {
.then((res) => {
// Alert successful
alert("Success!");
alert(
`Success! View your transaction here https://etherscan.io/tx/${res.data.txHash}`
);
})
// Else,
.catch((error) => {
Expand Down
26 changes: 24 additions & 2 deletions helpers/database/awaitingTxs.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,9 @@ const insertVoteTx = async (tx) => {
* has already submitted a sig for this proposal.
* @param {String} address
* @param {Number} proposalId
* @param {Number} nonce Nonce to be used
*/
const voteAllowed = async (address, proposalId) => {
const voteAllowed = async (address, proposalId, nonce) => {
// Collect database connection
const { db } = await connectToDatabase();

Expand All @@ -91,7 +92,28 @@ const voteAllowed = async (address, proposalId) => {
.find({ from: address, executed: false, type: "vote" })
.toArray();

// If existing transactions, throw error
const existingVoteForProposal = await db
.find({ from: address, proposalId, type: "vote" })
.toArray();

const existingVoteWithNonce = await db
.find({ from: address, nonce, type: "vote" })
.toArray();

if (existingVoteForProposal.length > 0) {
const error = new Error("user has already voted for this proposal");
error.code = 409;
throw error;
}

if (existingVoteWithNonce.length > 0) {
const error = new Error(
"user has already voted with this nonce. Please try again later."
);
error.code = 409;
throw error;
}

if (existingUserTxs.length > 0) {
const error = new Error("only one pending vote at a time");
error.code = 409;
Expand Down
48 changes: 42 additions & 6 deletions helpers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
import Web3 from "web3"; // Web3
import axios from "axios"; // Axios requests
import { recoverTypedSignature } from "@metamask/eth-sig-util"; // EIP-712 sig verification
import { Relayer } from "@openzeppelin/defender-relay-client";
import { Defender } from "@openzeppelin/defender-sdk";

/**
* Instantiates server-side web3 connection
Expand Down Expand Up @@ -220,8 +220,9 @@ const canDelegate = async (address, delegatee = "0x") => {
* Checks if an address can vote by sig for the given proposal
* @param {String} address
* @param {Number} proposalId
* @param {Number} nonce Nonce to be used for the vote
*/
const canVote = async (address, proposalId) => {
const canVote = async (address, proposalId, nonce) => {
// Collect Web3 + contracts
const { web3, compToken, governorCharlie } = Web3Handler();

Expand Down Expand Up @@ -268,7 +269,7 @@ const canVote = async (address, proposalId) => {
// Collect current block number
web3.eth.getBlockNumber(),
// Check if vote is allowed from db
voteAllowed(address, proposalId),
voteAllowed(address, proposalId, nonce),
]);

if (currentBlock >= snapshotBlock) {
Expand All @@ -291,7 +292,7 @@ const canVote = async (address, proposalId) => {
}

// Not ongoing proposal. Leaves a block buffer for last relay
if (!(currentBlock < endBlock - 2025) || proposalState == 2 /* canceled */) {
if (!(currentBlock < endBlock - 100) || proposalState == 2 /* canceled */) {
const error = new Error("proposal voting period is not active");
error.code = 400;
throw error;
Expand Down Expand Up @@ -400,7 +401,7 @@ const vote = async (address, proposalId, support, v, r, s) => {
}

try {
await canVote(address, proposalId);
await canVote(address, proposalId, data.message.nonce);
} catch (error) {
// Pass error from db
if (typeof error.code == "number") {
Expand All @@ -422,16 +423,51 @@ const vote = async (address, proposalId, support, v, r, s) => {
proposalId,
type: "vote",
createdAt: new Date(),
executed: false,
executed: true, // will relay immediately,
nonce: data.message.nonce,
};

// Relay TX
const txHash = await relayVote(newTx);

// Insert vote transaction to db
await insertVoteTx(newTx);

// Send notification to admin using telegram
if (typeof process.env.NOTIFICATION_HOOK != "undefined") {
await axios.get(process.env.NOTIFICATION_HOOK + "New comp.vote voting sig");
}

return txHash;
};

const relayVote = async (voteSignature) => {
const { governorCharlie } = Web3Handler();

const client = new Defender({
relayerApiKey: process.env.DEFENDER_API_KEY,
relayerApiSecret: process.env.DEFENDER_API_SECRET,
});

const web3jsTx = governorCharlie.methods.castVoteBySig(
voteSignature.proposalId,
voteSignature.support,
voteSignature.from,
`${voteSignature.r}${voteSignature.s.substring(
2
)}${voteSignature.v.substring(2)}`
);

const tx = {
to: GOVERNOR_CHARLIE_ADDRESS,
data: web3jsTx.encodeABI(),
gasLimit: Math.round((await web3jsTx.estimateGas()) * 1.2),
value: 0,
maxPriorityFeePerGas: "500000000",
maxFeePerGas: "50000000000"
};

return (await client.relaySigner.sendTransaction(tx)).hash;
};

/**
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"@davatar/react": "^1.8.1",
"@ethersproject/providers": "^5.7.2",
"@metamask/eth-sig-util": "^4.0.0",
"@openzeppelin/defender-relay-client": "^1.48.0",
"@openzeppelin/defender-sdk": "^2.5.0",
"@vercel/analytics": "^1.2.2",
"@walletconnect/web3-provider": "^1.6.6",
"axios": "^0.21.0",
Expand Down
1 change: 1 addition & 0 deletions pages/api/governance/proposals.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const statesKey = [
/// Global defining titles for misformatted proposals
const MISFORMATTED_PROPOSAL_TITLES = {
380: "[Gauntlet] Supply Cap Recommendations (12/09/24)",
450: "OpenZeppelin Security Partnership - Annual Renewal 2025",
};

const initialProposalBravo = 42;
Expand Down
7 changes: 5 additions & 2 deletions pages/api/vote.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ export default async (req, res) => {
vInput = transaction.v;
}

let txHash;
try {
// Send vote
await vote(
txHash = await vote(
transaction.address,
transaction.proposalId,
transaction.support,
Expand All @@ -35,5 +36,7 @@ export default async (req, res) => {
}

// Else, return success
res.status(200).end();
res.status(200).send({
txHash,
});
};
2 changes: 1 addition & 1 deletion pages/delegate.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export default function Delegate({
<div className={styles.card}>
{/* Card header */}
<div>
<h4>Addresses by Voting Weight</h4>
<h4>Addresses by Proposals Voted</h4>
</div>

{/* Card legend */}
Expand Down
3 changes: 1 addition & 2 deletions pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,7 @@ function ProposalsContent({ defaultProposals, pages, setPages }) {
>
Info
</button>
{proposal.state.value === "Active" ||
proposal.state.value === "Pending" ? (
{proposal.state.value === "Active" ? (
// Check if proposal is active
web3 ? (
// If authenticated and proposal active, return voting + info buttons
Expand Down
7 changes: 4 additions & 3 deletions styles/page.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,11 @@
}

> div:nth-child(2) {
float: right;

display: flex;
justify-content: flex-end;
align-items: center;
button {
transform: translateY(6px);
transform: translateY(0);
display: inline-block;
padding: 0.6rem 1.2rem;
margin: 0px 8px;
Expand Down
Loading