Skip to content

Commit

Permalink
Prepare for treasury FE
Browse files Browse the repository at this point in the history
  • Loading branch information
tensojka committed Aug 13, 2024
1 parent 673454a commit 78c3f52
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 106 deletions.
35 changes: 2 additions & 33 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ import { useContractRead } from "@starknet-react/core";
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";
import SubmitProposalModal from "./components/SubmitProposalModal";

function App() {
const [isModalOpen, setIsModalOpen] = React.useState(false);
Expand All @@ -32,37 +31,7 @@ function App() {
return (
<main className="flex flex-col items-center min-h-screen gap-12 mt-16">
<Header />
{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-max">
{/* Close modal button */}
<button
className="absolute right-3 top-3 text-slate-400"
onClick={() => setIsModalOpen(false)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="w-6 h-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
<p className="text-xl font-bold">New proposal</p>
{/* New proposal form */}
<NewProposalForm setIsModalOpen={setIsModalOpen}/>
<CustomProposalForm setIsModalOpen={setIsModalOpen}/>
</div>
</dialog>
)}
<SubmitProposalModal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)} />

{/* List of proposals */}
<div className="flex max-w-[50rem] flex-col items-center w-full gap-2 p-6">
Expand Down
106 changes: 56 additions & 50 deletions frontend/src/components/NewProposalForm.tsx
Original file line number Diff line number Diff line change
@@ -1,86 +1,92 @@
import React, { useMemo } from "react";
import React, { useState } from "react";
import toast from "react-hot-toast";
import { CONTRACT_ADDR } from "../lib/config";
import { useAccount, useContractWrite } from "@starknet-react/core";
import CustomProposal from "./CustomProposal";

const proposalTypes = ["airdrop", "signal vote", "AMM", "governance", "treasury"];

export default function NewProposalForm({
setIsModalOpen,
}: {
setIsModalOpen: React.Dispatch<React.SetStateAction<boolean>>;
}) {
const { isConnected } = useAccount();
const [selectedType, setSelectedType] = useState<string | null>(null);
const [payload, setPayload] = useState<string>("");

// State variables for the payload and to_upgrade
const [payload, setPayload] = React.useState<string>("");
const [to_upgrade, setToUpgrade] = React.useState<string>("0");

// Create a call to submit a proposal
const calls = useMemo(() => {
const tx = {
const calls = React.useMemo(() => {
if (!selectedType) return [];
const typeIndex = proposalTypes.indexOf(selectedType);
return [{
contractAddress: CONTRACT_ADDR,
entrypoint: "submit_proposal",
calldata: [payload.toString(), to_upgrade.toString()],
};
return [tx];
}, [payload, to_upgrade, submitProposal]);
calldata: [payload, typeIndex.toString()],
}];
}, [selectedType, payload]);

// Use the useContractWrite hook to write the proposal
const { writeAsync } = useContractWrite({ calls });

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

// Check if the user is connected
if (!isConnected) {
toast.error("Please connect your wallet");
return;
}

// Check if the payload and to_upgrade fields are filled out
if (!payload || !to_upgrade) {
if (!selectedType || (selectedType !== "treasury" && !payload)) {
toast.error("Please fill out all fields");
return;
}

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

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 onSubmit={submitProposal} className="space-y-4">
<div className="flex justify-between space-x-2">
{proposalTypes.map((type) => (
<button
key={type}
type="button"
onClick={() => setSelectedType(type)}
className={`px-4 py-2 rounded flex-1 ${
selectedType === type ? "bg-blue-500 text-white" : "bg-gray-200"
}`}
>
{type}
</button>
))}
</div>

{selectedType && selectedType !== "treasury" && (
<div>
<label htmlFor="payload" className="block mb-2">Payload</label>
<input
id="payload"
type="text"
placeholder="(integer or hex, e.g.: 1 or 0x1)"
className="w-full p-2 border rounded-lg border-slate-300"
value={payload}
onChange={(e) => setPayload(e.target.value)}
/>
</div>
)}

{selectedType === "treasury" && <CustomProposal setIsModalOpen={setIsModalOpen}/>}

{selectedType !== "treasury" && (
<button
type="submit"
className="w-full p-2 bg-blue-500 text-white rounded-lg"
>
Submit
</button>
)}
</form>
);
}


41 changes: 41 additions & 0 deletions frontend/src/components/SubmitProposalModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from "react";
import NewProposalForm from "./NewProposalForm";

interface SubmitProposalModalProps {
isOpen: boolean;
onClose: () => void;
}

const SubmitProposalModal: React.FC<SubmitProposalModalProps> = ({ isOpen, onClose }) => {
if (!isOpen) return null;

return (
<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-4 p-8 bg-white rounded-lg">
<button
className="absolute right-3 top-3 text-slate-400"
onClick={onClose}
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="w-6 h-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
<p className="text-xl font-bold">New proposal</p>
<NewProposalForm setIsModalOpen={onClose} />
</div>
</dialog>
);
};

export default SubmitProposalModal;
6 changes: 3 additions & 3 deletions frontend/src/constants/config.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"NETWORK": "mainnet",
"NETWORK": "sepolia",
"API_URL": "https://api.carmine.finance",
"AMM_ADDRESS": "0x047472e6755afc57ada9550b6a3ac93129cc4b5f98f51c73e0644d129fd208d9",
"GOVERNANCE_ADDRESS": "0x001405ab78ab6ec90fba09e6116f373cda53b0ba557789a4578d8c1ec374ba0f",
"GOVERNANCE_ADDRESS": "0x057dfabb5a506bfd1937062562a1adf45c7c4c62d0377ccfc59a0b42d7ab3212",
"ETH_ADDRESS": "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
"USDC_ADDRESS": "0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8",
"BTC_ADDRESS": "0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac",
Expand All @@ -15,4 +15,4 @@
"ETH_STRK_PUT_ADDRESS": "0x04dcd9632353ed56e47be78f66a55a04e2c1303ebcb8ec7ea4c53f4fdf3834ec",
"STRK_USDC_CALL_ADDRESS": "0x2b629088a1d30019ef18b893cebab236f84a365402fa0df2f51ec6a01506b1d",
"STRK_USDC_PUT_ADDRESS": "0x6ebf1d8bd43b9b4c5d90fb337c5c0647b406c6c0045da02e6675c43710a326f"
}
}
7 changes: 1 addition & 6 deletions frontend/src/lib/config.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
export const CONTRACT_ADDR =
"0x03a71a6e71f77fce28191f7ec4ea5f134a73ea0130033af3d6989816b230023e";
"0x057dfabb5a506bfd1937062562a1adf45c7c4c62d0377ccfc59a0b42d7ab3212";
export const TOKEN_CONTRACT =
"0x2b91dd683bc4bcae7a9c5d0cbeba0e8d62fa657742e4640f1e8163dc10e9bd7";

// export const formatAddress = (addr: string) => {
// if (addr.length === 66) return addr;
// return addr.padStart(66, "0");
// };

export const formatAddress = (addr: string) =>
addr.length === 66
? addr
Expand Down
31 changes: 27 additions & 4 deletions src/contract.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ mod Governance {
use konoha::airdrop::airdrop as airdrop_component;
use konoha::discussion::discussion as discussion_component;
use konoha::proposals::proposals as proposals_component;
use konoha::proposals::{IProposalsDispatcher, IProposalsDispatcherTrait};
use konoha::staking::staking as staking_component;
use konoha::staking::{IStakingDispatcher, IStakingDispatcherTrait};
use konoha::streaming::streaming as streaming_component;
use konoha::types::BlockNumber;
use konoha::types::ContractType;
use konoha::types::CustomProposalConfig;
use konoha::types::PropDetails;
use konoha::types::VoteStatus;
use konoha::upgrades::upgrades as upgrades_component;
Expand Down Expand Up @@ -137,13 +139,14 @@ mod Governance {
// This is not used in production on mainnet, because the governance token is already deployed (and distributed).

let governance_address = get_contract_address();
assert(governance_address.into() != 0, 'gov addr zero??');

let mut voting_token_calldata: Array<felt252> = ArrayTrait::new();
voting_token_calldata.append(governance_address.into());
let (voting_token_address, _) = deploy_syscall(
voting_token_class, 42, voting_token_calldata.span(), true
voting_token_class, 42, voting_token_calldata.span(), false
)
.unwrap();
.expect('unable to deploy votingtoken');
self.governance_token_address.write(voting_token_address);

let mut floating_token_calldata: Array<felt252> = ArrayTrait::new();
Expand All @@ -152,9 +155,9 @@ mod Governance {
floating_token_calldata.append(recipient.into());
floating_token_calldata.append(governance_address.into());
let (floating_token_address, _) = deploy_syscall(
floating_token_class, 42, floating_token_calldata.span(), true
floating_token_class, 1337, floating_token_calldata.span(), false
)
.unwrap();
.expect('unable to deploy floatingtoken');

let staking = IStakingDispatcher { contract_address: governance_address };
staking.set_floating_token_address(floating_token_address);
Expand All @@ -166,6 +169,26 @@ mod Governance {
staking.set_curve_point(THREE_MONTHS, 120);
staking.set_curve_point(SIX_MONTHS, 160);
staking.set_curve_point(ONE_YEAR, 250);

let treasury_classhash = 0x06a38a382eddf3d7ebf9673516ac7cf1ff185a1ecbf490cb07f1687e531eb9ec
.try_into()
.unwrap();
let mut treasury_calldata: Array<felt252> = ArrayTrait::new();
treasury_calldata.append(governance_address.into());
treasury_calldata.append(0x1); // carmine amm addr
treasury_calldata.append(0x1); // zklend addr
let (treasury_address, _) = deploy_syscall(
treasury_classhash, 42, treasury_calldata.span(), true
)
.unwrap();
let proposals = IProposalsDispatcher { contract_address: governance_address };
let send_tokens_custom_proposal_config: CustomProposalConfig = CustomProposalConfig {
target: treasury_address.into(),
selector: selector!("send_tokens_to_address"),
library_call: false
};

proposals.add_custom_proposal_config(send_tokens_custom_proposal_config);
}

#[abi(embed_v0)]
Expand Down
22 changes: 12 additions & 10 deletions src/proposals.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ trait IProposals<TContractState> {
prop_id: felt252,
);
fn get_total_delegated_to(self: @TContractState, to_addr: ContractAddress) -> u128;
fn add_custom_proposal_config(ref self: TContractState, config: CustomProposalConfig) -> u32;
}

#[starknet::component]
Expand Down Expand Up @@ -269,16 +270,6 @@ mod proposals {
};
i
}

fn add_custom_proposal_config(
ref self: ComponentState<TContractState>, config: CustomProposalConfig
) -> u32 {
let idx = self._find_free_custom_proposal_type();
assert(config.target.is_non_zero(), 'target must be nonzero');
assert(config.selector.is_non_zero(), 'selector must be nonzero');
self.custom_proposal_type.write(idx, config);
idx
}
}

#[embeddable_as(ProposalsImpl)]
Expand Down Expand Up @@ -552,5 +543,16 @@ mod proposals {
) -> CustomProposalConfig {
self.custom_proposal_type.read(i)
}

fn add_custom_proposal_config(
ref self: ComponentState<TContractState>, config: CustomProposalConfig
) -> u32 {
assert(get_caller_address() == get_contract_address(), 'can only be called by self');
let idx = self._find_free_custom_proposal_type();
assert(config.target.is_non_zero(), 'target must be nonzero');
assert(config.selector.is_non_zero(), 'selector must be nonzero');
self.custom_proposal_type.write(idx, config);
idx
}
}
}
Loading

0 comments on commit 78c3f52

Please sign in to comment.