Skip to content

Commit

Permalink
feat: Custom proposal ability added
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreiZg committed Aug 10, 2024
1 parent 0290d25 commit 673454a
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 104 deletions.
24 changes: 7 additions & 17 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ import React from "react";
// import { useBlock } from "@starknet-react/core";
import Header from "./components/Header";
import { useContractRead } from "@starknet-react/core";
import Tokens from "./helpers/tokens";
//import Tokens from "./helpers/tokens";
import { abi } from "./lib/abi";
import Proposal from "./components/Proposal";
import { CONTRACT_ADDR } from "./lib/config";
import NewProposalForm from "./components/NewProposalForm";
import CustomProposalForm from "./components/CustomProposal";
// import { useAccount } from "@starknet-react/core";

function App() {
const [isModalOpen, setIsModalOpen] = React.useState(false);
const [isModalOpen, setIsModalOpen] = React.useState(false);

// Call the contract function get_live_proposals to get the live proposals
const { data, isError, isLoading, error } = useContractRead({
const { data, isError, isLoading,error } = useContractRead({
functionName: "get_live_proposals",
args: [],
abi,
Expand All @@ -34,7 +35,7 @@ function App() {
{isModalOpen && (
<dialog
className="fixed inset-0 z-50 flex items-center justify-center w-full h-full p-6 bg-black bg-opacity-50">
<div className="relative flex flex-col items-center gap-5 p-8 bg-white rounded-lg w-[50%] h-[55%]">
<div className="relative flex flex-col items-center gap-5 p-8 bg-white rounded-lg w-[50%] h-max">
{/* Close modal button */}
<button
className="absolute right-3 top-3 text-slate-400"
Expand All @@ -55,9 +56,10 @@ function App() {
/>
</svg>
</button>
<p className="text-xl font-bold">New treasury proposal</p>
<p className="text-xl font-bold">New proposal</p>
{/* New proposal form */}
<NewProposalForm setIsModalOpen={setIsModalOpen}/>
<CustomProposalForm setIsModalOpen={setIsModalOpen}/>
</div>
</dialog>
)}
Expand All @@ -81,18 +83,6 @@ function App() {
It may take a few seconds for new proposals to appear here
after they are submitted.
</div>
<h1 className='w-full text-3xl font-bold my-5'>Treasury status</h1>
<div className='w-full'>
{/* Map over the Tokens array to create a list item for each token */}
{Tokens.map((token, index) => {
return(
<ul key={index} className='flex justify-between border rounded-xl border-black p-3 mb-5 bg-amber-50'>
<li>{token}</li>
<li className='font-bold'>0</li>
</ul>
)
})}
</div>
{isLoading ? (
<div className="text-center">loading...</div>
) : (
Expand Down
113 changes: 113 additions & 0 deletions frontend/src/components/CustomProposal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import React, { useState, useMemo } from 'react';
import { useAccount, useContractWrite } from '@starknet-react/core';
import { toast } from 'react-hot-toast';
import { CONTRACT_ADDR } from '../lib/config';

export default function CustomProposalForm({
setIsModalOpen,
}: {
setIsModalOpen: React.Dispatch<React.SetStateAction<boolean>>;
}) {
const { isConnected } = useAccount();

const MAX_CALLEDATA_FIELDS = 3;

// State variables for custom proposal type and calldata
const [customProposalType, setCustomProposalType] = useState<string>('0');
const [calldata, setCalldata] = useState<string[]>([]);

// Create a call to submit custom proposal
const calls = useMemo(() => {
const tx = {
contractAddress: CONTRACT_ADDR,
entrypoint: 'submit_custom_proposal',
calldata: [
customProposalType.toString(),
...calldata.map((data) => data.toString()),
],
};
return [tx];
}, [customProposalType, calldata]);

const { writeAsync } = useContractWrite({ calls });

function submitCustomProposal(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();

if (!isConnected) {
toast.error('Please connect your wallet');
return;
}

if (!customProposalType || calldata.length === 0) {
toast.error('Please fill out all fields');
return;
}

writeAsync()
.then(() => {
toast.success('Custom proposal submitted');
})
.catch((e) => {
toast.error('Something went wrong');
console.error(e);
})
.finally(() => {
setIsModalOpen(false);
});
}

const handleCalldataChange = (index: number, value: string) => {
const updatedCalldata = [...calldata];
updatedCalldata[index] = value;
setCalldata(updatedCalldata);
};

const addCalldataField = () => {
if (calldata.length < MAX_CALLEDATA_FIELDS) {
setCalldata([...calldata, '']);
} else {
toast.error('Maximum number of calldata fields reached');
}
};

return (
<form onSubmit={submitCustomProposal} className='w-[75%]'>
<label htmlFor="#customProposalType">Custom Proposal Type</label>
<input
id="#customProposalType"
type="text"
placeholder="Enter proposal type (e.g., 0, 1, 2)"
className="w-full p-2 mb-2 border rounded-lg border-slate-300"
onChange={(e) => setCustomProposalType(e.target.value)}
/>

<label>Calldata</label>
{calldata.map((data, index) => (
<input
key={index}
type="text"
placeholder={`Calldata ${index + 1}`}
className="w-full p-2 mb-2 border rounded-lg border-slate-300"
value={data}
onChange={(e) => handleCalldataChange(index, e.target.value)}
/>
))}

<button
type="button"
onClick={addCalldataField}
className="w-full p-2 mt-2 text-blue-500 border border-blue-500 rounded-lg"
>
Add Calldata Field
</button>

<button
type="submit"
className="w-full p-2 mt-4 text-white bg-blue-500 rounded-lg"
>
Submit
</button>
</form>
);
}
154 changes: 70 additions & 84 deletions frontend/src/components/NewProposalForm.tsx
Original file line number Diff line number Diff line change
@@ -1,100 +1,86 @@
import React, { useState } from "react";
import React, { useMemo } from "react";
import toast from "react-hot-toast";
import Tokens from "../helpers/tokens";
import { CONTRACT_ADDR } from "../lib/config";
import { useAccount, useContractWrite } from "@starknet-react/core";

export default function NewProposalForm({
setIsModalOpen,
}: {
setIsModalOpen: React.Dispatch<React.SetStateAction<boolean>>;
setIsModalOpen,
}: {
setIsModalOpen: React.Dispatch<React.SetStateAction<boolean>>;
}) {
const { isConnected } = useAccount();
const { isConnected } = useAccount();

// Use the useContractWrite hook to write the proposal
const { writeAsync } = useContractWrite({ /*calls*/ });
// State variables for the payload and to_upgrade
const [payload, setPayload] = React.useState<string>("");
const [to_upgrade, setToUpgrade] = React.useState<string>("0");

function submitProposal(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
// Create a call to submit a proposal
const calls = useMemo(() => {
const tx = {
contractAddress: CONTRACT_ADDR,
entrypoint: "submit_proposal",
calldata: [payload.toString(), to_upgrade.toString()],
};
return [tx];
}, [payload, to_upgrade, submitProposal]);

// Check if the user is connected
if (!isConnected) {
toast.error("Please connect your wallet");
return;
}
// Use the useContractWrite hook to write the proposal
const { writeAsync } = useContractWrite({ calls });

// Call the write function to submit the proposal
writeAsync()
.then(() => {
toast.success("Proposal submitted");
})
.catch((e) => {
toast.error("Something went wrong");
console.error(e);
})
.finally(() => {
setIsModalOpen(false);
});
}

// State to track the selected option in the dropdown
const [selectedOption, setSelectedOption] = useState<string>('');
function submitProposal(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();

// Handler for changes in the dropdown selection
const handleSelectChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
setSelectedOption(e.target.value);
// Check if the user is connected
if (!isConnected) {
toast.error("Please connect your wallet");
return;
}

// Component to render when 'distribution' is selected
const Distribution: React.FC = () => (
<div>
<h1>Recipient</h1>
<input className='border border-gray-300 rounded-lg p-0.5 w-full' type="text"/>
<h1>Token to distribute</h1>
<select className='border border-gray-300 rounded-lg p-0.5 w-full'>
{/* Map over the Tokens array to create an option for each token */}
{Tokens.map((token,index) => {
return(
<option className='text-black' key={index}>{token}</option>
)
})}
</select>
<h1>Amount</h1>
<input
type="number"
className='border border-gray-300 rounded-lg p-0.5 w-full'
placeholder='0'
/>
<button
type="submit"
className="w-full p-2 mt-4 text-white bg-blue-500 rounded-lg"
>
Submit
</button>
</div>
);

const renderComponent = (option: string) => {
switch (option) {
case 'distribution':
return <Distribution/>;
default:
return null;
}
// Check if the payload and to_upgrade fields are filled out
if (!payload || !to_upgrade) {
toast.error("Please fill out all fields");
return;
}

// Call the write function to submit the proposal
writeAsync()
.then(() => {
toast.success("Proposal submitted");
})
.catch((e) => {
toast.error("Something went wrong");
console.error(e);
})
.finally(() => {
setIsModalOpen(false);
});
}

return (
<form onSubmit={submitProposal}>
<select className='border border-gray-300 rounded-lg p-1 w-full' onChange={handleSelectChange}>
<option>Select an option</option>
<option value='zklend'>ZkLend</option>
<option value='nostra'>Nostra</option>
<option value='carmine'>Carmine</option>
<option value='distribution'>Distribution</option>
</select>

{/* Render the component based on the selected option */}
{selectedOption && renderComponent(selectedOption)}
</form>
);
return (
<form onSubmit={submitProposal}>
<label htmlFor="#payload">Payload</label>
<input
id="#payload"
type="text"
placeholder="(integer or hex, e.g.: 1 or 0x1)"
className="w-full p-2 mb-2 border rounded-lg border-slate-300"
onChange={(e) => setPayload(e.target.value)}
/>
<label htmlFor="#to_upgrade">To Upgrade</label>
<select
id="#to_upgrade"
className="w-full p-2 border rounded-lg border-slate-300"
onChange={(e) => setToUpgrade(e.target.value)}
defaultValue={to_upgrade}
>
<option value="0">amm</option>
<option value="1">governance</option>
<option value="2">CARM token</option>
<option value="3">merkle tree root</option>
<option value="4">no-op/signal vote</option>
</select>
</form>
);
}


5 changes: 2 additions & 3 deletions frontend/src/components/Proposal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,16 @@ export default function Proposal({
React.useState<boolean>(false);
const [isCommentModalOpen, setIsCommentModalOpen] =
React.useState<boolean>(false);

// Call the contract function get_proposal_details with the proposalId to get the proposal details


const { data, isLoading } = useContractRead({
functionName: "get_proposal_details",
args: [proposalId.toString()],
abi,
address: CONTRACT_ADDR,
watch: false,
retry: false
});
});

// Convert the proposal type from number to string
const proposal_type = {
Expand Down

0 comments on commit 673454a

Please sign in to comment.