Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interact with dao contract functions #104

Merged
merged 33 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
dd2ed5b
chore: separate connected key and kit service
0xExp-po Dec 3, 2024
9946702
chore: check empty descriptions before submit
0xExp-po Dec 3, 2024
92c3af1
chore: add proposal name input
0xExp-po Dec 3, 2024
9ba0fca
chore: add create proposal function to new proposal page
0xExp-po Dec 3, 2024
8b7474c
fix: create proposal error
0xExp-po Dec 4, 2024
7886484
chore: add voting dates input
0xExp-po Dec 4, 2024
740f421
chore: update proposal data interface
0xExp-po Dec 4, 2024
2356f07
feat: fetch proposals from contract
0xExp-po Dec 4, 2024
677e55d
feat: fetch proposal data from contract
0xExp-po Dec 4, 2024
c37fc13
feat: implement vote function
0xExp-po Dec 4, 2024
e1cf39c
chore: define the response type, create contract error messages
0xExp-po Dec 4, 2024
c16daee
chore: add error handler to voting function
0xExp-po Dec 4, 2024
da95617
chore: change the response data type
0xExp-po Dec 4, 2024
e80c248
chore: add error handler to submit proposal function
0xExp-po Dec 4, 2024
820d203
chore: add error handler to commit hash function
0xExp-po Dec 4, 2024
bcac3df
chore: add error handler to register project function
0xExp-po Dec 4, 2024
ee71b0f
feat: add error handler to read contract functions
0xExp-po Dec 4, 2024
9713448
chore: remove demo proposal data
0xExp-po Dec 5, 2024
bd30995
fix: update check condition for proposal submit
0xExp-po Dec 5, 2024
773f2f5
chore: update contract response data type
0xExp-po Dec 6, 2024
d8a4955
chore: add process checker in submit proposal function
0xExp-po Dec 6, 2024
8a385b9
update stellar wallet kit version
0xExp-po Dec 9, 2024
473de08
chore: update submit proposal function
0xExp-po Dec 9, 2024
3b66ef1
fix: linting error
0xExp-po Dec 9, 2024
7258ee0
chore: add console log to check delegation
0xExp-po Dec 9, 2024
0bd0e37
Fix signing and sending
tupui Dec 9, 2024
08dd2f4
Merge branch 'main' of https://github.com/0xExp-po/soroban-versioning…
0xExp-po Dec 10, 2024
f13f3f3
Merge branch 'main' of https://github.com/0xExp-po/soroban-versioning…
0xExp-po Dec 11, 2024
970016c
feat: create challenge to submit proposal
0xExp-po Dec 11, 2024
3e8e2a6
fix: regular expression for markdown image
0xExp-po Dec 11, 2024
5e020db
chore: set delegation expire time as 1 min
0xExp-po Dec 11, 2024
3c1b4ab
chore: divide proof env data into two parts
0xExp-po Dec 12, 2024
0588b41
fix: linter error
0xExp-po Dec 12, 2024
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
3 changes: 2 additions & 1 deletion dapp/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ PUBLIC_DEFAULT_FEE="100"
PUBLIC_DEFAULT_TIMEOUT=30

STORACHA_SING_PRIVATE_KEY=""
STORACHA_PROOF=""
STORACHA_PROOF_1=""
STORACHA_PROOF_2=""
Binary file modified dapp/bun.lockb
Binary file not shown.
21 changes: 11 additions & 10 deletions dapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,28 @@
"@astrojs/react": "^4.0.0",
"@astrojs/tailwind": "5.1.3",
"@creit.tech/sorobandomains-sdk": "^0.1.4",
"@creit.tech/stellar-wallets-kit": "^1.2.5",
"@mdxeditor/editor": "^3.19.2",
"@nanostores/react": "^0.8.0",
"@creit.tech/stellar-wallets-kit": "^1.3.0",
"@mdxeditor/editor": "^3.20.0",
"@nanostores/react": "^0.8.2",
"@stellar/stellar-sdk": "^13.0.0",
"@web3-storage/w3up-client": "^16.5.1",
"@web3-storage/w3up-client": "^16.5.2",
"astro": "4.16.17",
"astro-seo": "^0.8.4",
"github-markdown-css": "^5.8.0",
"crypto-js": "^4.2.0",
"github-markdown-css": "^5.8.1",
"install": "^0.13.0",
"js-sha3": "^0.9.3",
"markdown-to-jsx": "^7.6.2",
"markdown-to-jsx": "^7.7.1",
"nanostores": "^0.11.3",
"react18-json-view": "^0.2.8",
"tailwindcss": "^3.4.15",
"typescript": "^5.6.3"
"tailwindcss": "^3.4.16",
"typescript": "^5.7.2"
},
"devDependencies": {
"@web3-storage/w3cli": "^7.9.1",
"dotenv": "^16.4.5",
"dotenv": "^16.4.7",
"glob": "^11.0.0",
"prettier": "^3.3.3",
"prettier": "^3.4.2",
"prettier-plugin-astro": "^0.14.1"
},
"name": "dapp",
Expand Down
4 changes: 2 additions & 2 deletions dapp/src/components/ConnectWallet.astro
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@

<script>
import { truncateMiddle } from "../utils/utils";
import { kit } from "./stellar-wallets-kit";
import {
initializeConnection,
kit,
loadedPublicKey,
setPublicKey,
} from "./stellar-wallets-kit";
} from "../service/walletService";

document.addEventListener("astro:page-load", () => {
initializeConnection();
Expand Down
23 changes: 17 additions & 6 deletions dapp/src/components/page/dashboard/ProjectCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ const ProjectCard = ({ config }) => {
refreshLocalStorage();
try {
setProjectId(config.projectName.toLowerCase());
const project = await getProject();
const res = await getProject();
const project = res.data;
if (project && project.name && project.config && project.maintainers) {
setProject(project);
const { username, repoName } = getAuthorRepo(project.config.url);
Expand All @@ -39,15 +40,25 @@ const ProjectCard = ({ config }) => {

const latestSha = await getProjectHash();
if (
typeof latestSha === "string" &&
latestSha.match(/^[a-f0-9]{40}$/)
latestSha.data &&
typeof latestSha.data === "string" &&
latestSha.data.match(/^[a-f0-9]{40}$/)
) {
setProjectLatestSha(latestSha);
} else setProjectLatestSha("");
setProjectLatestSha(latestSha.data);
} else {
setProjectLatestSha("");
if (latestSha.error) {
alert(latestSha.errorMessage);
}
}

projectCardModalOpen.set(true);
} else {
alert(`There is not such project: ${config.projectName}`);
if (res.error) {
alert(res.errorMessage);
} else {
alert(`There is not such project: ${config.projectName}`);
}
}
} catch (e) {
console.error(e);
Expand Down
4 changes: 3 additions & 1 deletion dapp/src/components/page/dashboard/ProjectList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ const ProjectList = () => {
const checkProjectOnChain = async (projectName) => {
setIsLoading(true);
try {
const project = await getProjectFromName(projectName);
const res = await getProjectFromName(projectName);
const project = res.data;
if (project && project.name && project.config && project.maintainers) {
const tomlData = await fetchTOMLFromConfigUrl(project.config.url);
if (tomlData) {
Expand All @@ -118,6 +119,7 @@ const ProjectList = () => {
setIsInOnChain(true);
} else {
setIsInOnChain(false);
alert(res.errorMessage);
}
} catch (error) {
console.error("Error checking project on-chain:", error);
Expand Down
7 changes: 5 additions & 2 deletions dapp/src/components/page/governance/GovernancePageTitle.astro
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Topic from "../../utils/Topic.astro";
<div
class="relative flex flex-col items-center md:flex-row justify-between mb-1.5 mt-4 sm:mt-8 md:mt-12 gap-y-4"
>
<Topic title="Project Governance" description="" id="proposal-page-topic" />
<Topic title="" description="" id="proposal-page-topic" />
<div id="create-proposal-button" class="">
<PrimaryButton
wrapId="wrap-create-proposal-button"
Expand Down Expand Up @@ -36,9 +36,12 @@ import Topic from "../../utils/Topic.astro";
titleElement.textContent = `${capitalizeFirstLetter(_projectName)} Governance`;
}
projectName = _projectName;
const projectInfo = await getProjectFromName(_projectName);
const res = await getProjectFromName(_projectName);
const projectInfo = res.data;
if (projectInfo && projectInfo.maintainers) {
maintainers = projectInfo?.maintainers;
} else {
alert(res.errorMessage);
}
}
});
Expand Down
146 changes: 130 additions & 16 deletions dapp/src/components/page/governance/ProposalForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,21 @@ const ProposalForm: React.FC = () => {
const connectedAddress = useStore(connectedPublicKey);
const projectName = useStore(projectNameForGovernance);
const [projectMaintainers, setProjectMaintainers] = useState<string[]>([]);
const [proposalName, setProposalName] = useState("");
const [votingDays, setVotingDays] = useState<number>(5);

useEffect(() => {
setIsClient(true);
}, []);

const getProjectMaintainers = async () => {
if (projectName) {
const projectInfo = await getProjectFromName(projectName);
const res = await getProjectFromName(projectName);
const projectInfo = res.data;
if (projectInfo && projectInfo.maintainers) {
setProjectMaintainers(projectInfo?.maintainers);
} else if (res.error) {
alert(res.errorMessage);
}
} else {
alert("Project name is not provided");
Expand Down Expand Up @@ -112,13 +117,56 @@ const ProposalForm: React.FC = () => {
},
};

const isDescriptionValid = (description: string, words: number = 3) => {
return description.trim().split(/\s+/).length >= words;
};

const submitProposal = async () => {
if (!projectName) {
alert("Project name is required");
return;
}

if (
!proposalName ||
proposalName.length < 10 ||
proposalName.length > 256
) {
alert("Proposal name is required");
return;
}

if (votingDays < 1 || votingDays > 30) {
alert("Voting days must be between 5 and 30");
return;
}

if (!isDescriptionValid(mdText, 10)) {
alert("Proposal description must contain at least 10 words.");
return;
}

if (!isDescriptionValid(approveDescription)) {
alert("Approved description must contain at least 3 words.");
return;
}

if (rejectXdr && !isDescriptionValid(rejectDescription)) {
alert("Rejected description must contain at least 3 words.");
return;
}

if (cancelledXdr && !isDescriptionValid(cancelledDescription)) {
alert("Cancelled description must contain at least 3 words.");
return;
}

if (!connectedAddress) {
alert("Please connect your wallet first");
return;
}

if (projectMaintainers.includes(connectedAddress)) {
if (!projectMaintainers.includes(connectedAddress)) {
alert("You are not a maintainer of this project");
return;
}
Expand All @@ -128,11 +176,25 @@ const ProposalForm: React.FC = () => {
const client = await Client.create();
const apiUrl = `/api/w3up-delegation`;

const { generateChallengeTransaction } = await import(
"@service/ChallengeService"
);

const did = client.agent.did();

const signedTxXdr = await generateChallengeTransaction(did);

const response = await fetch(apiUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ did: client.agent.did() }),
body: JSON.stringify({ signedTxXdr, projectName, did }),
});

if (!response.ok) {
const errorData = await response.json();
alert(`Error: ${errorData.error}`);
throw new Error(errorData.error);
}
const data = await response.arrayBuffer();

const delegation = await Delegation.extract(new Uint8Array(data));
Expand All @@ -141,9 +203,11 @@ const ProposalForm: React.FC = () => {
cause: delegation.error,
});
}
console.log("Delegation successfully extracted from server");

const space = await client.addSpace(delegation.ok);
client.setCurrentSpace(space.did());

await client.setCurrentSpace(space.did());

const proposalOutcome: ProposalOutcome = {
approved: {
Expand Down Expand Up @@ -179,25 +243,62 @@ const ProposalForm: React.FC = () => {

files.push(new File([description], "proposal.md"));

if (!files) {
alert("Failed to create proposal files");
return;
}

const directoryCid = await client.uploadDirectory(files);
console.log("Proposal successfully uploaded to IPFS");

setIsLoading(false);
alert(
`Proposal submitted successfully! CID: ${getIpfsBasicLink(directoryCid.toString())}`,
if (!directoryCid) {
alert("Failed to upload proposal");
return;
}

const { createProposal } = await import("@service/WriteContractService");

const res = await createProposal(
projectName,
proposalName,
directoryCid.toString(),
Math.floor(Date.now() / 1000) + 86400 * votingDays,
);

window.location.href = `/governance?name=${projectName}`;
} catch (error) {
throw new Error("Error submitting proposal", {
cause: error,
});
setIsLoading(false);
if (res.error) {
alert(res.errorMessage);
} else {
alert(
`Proposal submitted successfully! Proposal ID: ${res.data}, CID: ${getIpfsBasicLink(directoryCid.toString())}`,
);

window.location.href = `/governance?name=${projectName}`;
}
} catch (error: any) {
setIsLoading(false);
console.error(
"submit proposal error:",
error?.cause ? error.cause : error,
);
alert("Error submitting proposal.");
}
};

return (
<div>
<h3 className="text-base sm:text-lg md:text-2xl font-semibold py-2">
Proposal Description
<h3 className="text-base sm:text-lg md:text-[26px] font-semibold my-10 mb-3">
Name
</h3>
<input
type="text"
value={proposalName}
onChange={(e) => setProposalName(e.target.value)}
className="w-full p-2 border border-zinc-700 rounded-md focus:outline-none"
placeholder="Enter your proposal name here..."
/>
<h3 className="text-base sm:text-lg md:text-[26px] font-semibold my-10 mb-3">
Description
</h3>
<div className="rounded-md border border-zinc-700 overflow-hidden">
<MDXEditor
Expand Down Expand Up @@ -265,8 +366,8 @@ const ProposalForm: React.FC = () => {
placeholder="Input your proposal description here..."
/>
</div>
<h3 className="text-base sm:text-lg md:text-2xl font-semibold py-2">
Proposal Outcome
<h3 className="text-base sm:text-lg md:text-[26px] font-semibold my-10 mb-8">
Outcome
</h3>
<div className="w-full max-w-[840px] mx-auto flex flex-col gap-4 sm:gap-6 md:gap-10">
<OutcomeInput
Expand All @@ -290,6 +391,19 @@ const ProposalForm: React.FC = () => {
xdr={cancelledXdr}
setXdr={setCancelledXdr}
/>
<div className="w-full flex items-center">
<h5 className="w-[80px] sm:w-[90px] md:w-[105px] pr-1.5 text-right text-base text-nowrap">
Voting dates:
</h5>
<input
type="number"
value={votingDays}
onChange={(e) => setVotingDays(Number(e.target.value))}
className="w-14 pr-2 text-right text-base border border-zinc-700 rounded-md focus:outline-none"
placeholder=""
/>
<div className="pl-1.5 text-base">days</div>
</div>
<div className="ml-[80px] sm:ml-[90px] md:ml-[105px]">
<button
className="w-full py-5 bg-zinc-900 rounded-[14px] justify-center gap-2.5 inline-flex"
Expand Down
Loading
Loading