Skip to content

Commit

Permalink
Merge pull request #7 from base-fundy/foundry-merge
Browse files Browse the repository at this point in the history
Foundry merge
  • Loading branch information
cserb authored Mar 29, 2024
2 parents 04f21c1 + 233e835 commit 8d294f7
Show file tree
Hide file tree
Showing 24 changed files with 1,378 additions and 512 deletions.
48 changes: 19 additions & 29 deletions packages/foundry/contracts/FundingRound.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ contract FundingRound {
}

// State variables
Project[] private _projects;
Project[] public projects;
IERC20 private immutable _mUSDC;
uint256 private _nextProjectId = 0;

Expand Down Expand Up @@ -50,13 +50,17 @@ contract FundingRound {
_mUSDC = IERC20(mUSDCAddress);
}

function getProjects() public view returns (Project[] memory) {
return projects;
}

/**
* @dev Allows the owner to create a new project.
* @param name Name of the project
* @param recipient Recipient address for the project funds
*/
function createProject(string calldata name, address recipient) external {
_projects.push(Project(_nextProjectId++, 0, name, recipient));
projects.push(Project(_nextProjectId++, 0, name, recipient));
emit ProjectCreated(_nextProjectId - 1, name, recipient);
}

Expand Down Expand Up @@ -87,16 +91,20 @@ contract FundingRound {
uint256 totalBalance = _mUSDC.balanceOf(address(this));
require(totalPoints > 0, "No projects to distribute to");

for (uint256 i = 0; i < _projects.length; i++) {
if (_projects[i].votingPoints > 0) {
for (uint256 i = 0; i < projects.length; i++) {
if (projects[i].votingPoints > 0) {
uint256 projectShare = (totalBalance *
_projects[i].votingPoints) / totalPoints;
//_mUSDC.transfer(_projects[i].recipient, projectShare);
// --- no sablier
projects[i].votingPoints) / totalPoints;
_mUSDC.transfer(projects[i].recipient, projectShare);

// --- with sablier
/*_projects[i].votingPoints) / totalPoints;
createStream(
uint128(projectShare * 1) / 4,
uint128(projectShare * 3) / 4,
_projects[i].recipient
);
);*/
}
}

Expand Down Expand Up @@ -158,8 +166,8 @@ contract FundingRound {
* @param points Number of voting points to add
*/
function _addPoints(uint256 projectId, uint256 points) private {
require(projectId < _projects.length, "Project does not exist");
_projects[projectId].votingPoints += points;
require(projectId < projects.length, "Project does not exist");
projects[projectId].votingPoints += points;
}

/**
Expand All @@ -171,29 +179,11 @@ contract FundingRound {
view
returns (uint256 totalPoints)
{
for (uint256 i = 0; i < _projects.length; i++) {
totalPoints += _projects[i].votingPoints;
for (uint256 i = 0; i < projects.length; i++) {
totalPoints += projects[i].votingPoints;
}
}

/**
* @dev Gets the details of a project by its ID.
* @param projectId The ID of the project to retrieve.
* @return The project details including id, votingPoints, name, and recipient address.
*/
function getProjectDetails(
uint256 projectId
) external view returns (uint256, uint256, string memory, address) {
require(projectId < _projects.length, "Project does not exist");

Project storage project = _projects[projectId];
return (
project.id,
project.votingPoints,
project.name,
project.recipient
);
}

/**
* @dev Returns the total amount of USDC stored in the contract.
Expand Down
14 changes: 10 additions & 4 deletions packages/foundry/script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,16 @@ contract DeployScript is ScaffoldETHDeploy {
}
vm.startBroadcast(deployerPrivateKey);

FundingRound fundingRound = new FundingRound(
address(0x8AD9aD7A3c31fB0c38EC884F520eC8155fD33246)
);
console.log("FundFactory deployed at:", address(fundingRound));
//FundFactory fundFactory = new FundFactory(address(mockUSDC));
//console.log("FundFactory deployed at:", address(fundFactory));
MockUSDC mockUSDC = new MockUSDC();
console.log("MockUSDC deployed at:", address(mockUSDC));

FundingRound fundingRound = new FundingRound(address(mockUSDC));
console.log("FundingRound deployed at:", address(fundingRound));

//fundFactory.createFundingRound();
//console.log("Funding round created through FundFactory");

vm.stopBroadcast();
/**
Expand Down
4 changes: 2 additions & 2 deletions packages/foundry/test/fundRoundTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ contract FundingRoundTest is Test {
fundingRound.contributeAndVote(projectIds, totalAmount);

// Verify project voting points are correctly updated
(, uint256 votingPoints1, , ) = fundingRound.getProjectDetails(0);
(, uint256 votingPoints2, , ) = fundingRound.getProjectDetails(1);
(, uint256 votingPoints1, , ) = fundingRound.projects(0);
(, uint256 votingPoints2, , ) = fundingRound.projects(1);
uint256 totalPointsExpected = _sqrt(100 ether);
uint256 individualPointExpected = totalPointsExpected / 2; // Since funds are equally divided

Expand Down
139 changes: 89 additions & 50 deletions packages/nextjs/app/fundy/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,82 +4,121 @@ import { useState } from "react";
import type { NextPage } from "next";
import { parseEther } from "viem";
// import { useAccount } from "wagmi";
import { useScaffoldContractRead } from "~~/hooks/scaffold-eth";
import { useDeployedContractInfo, useScaffoldContractRead } from "~~/hooks/scaffold-eth";
import { useScaffoldContractWrite } from "~~/hooks/scaffold-eth";

const FundyRound: NextPage = () => {
// const { address: connectedAddress } = useAccount();
const [votedProjects, setVotedProjects] = useState<bigint[]>([]);
const [fundingAmount, setFundingAmount] = useState<number>(0);
const [fundingAmount, setFundingAmount] = useState<bigint>(BigInt(0));
const [isApproved, setIsApproved] = useState<boolean>(false);

const { data: projects, isLoading: isProjectsLoading } = useScaffoldContractRead({
contractName: "YourContract",
contractName: "FundingRound",
functionName: "getProjects",
watch: true,
});

const { data: deployedFundingRoundContract } = useDeployedContractInfo("FundingRound");

const { writeAsync, isLoading } = useScaffoldContractWrite({
contractName: "YourContract",
functionName: "fundProjectsByIds",
args: [votedProjects.toString()],
contractName: "FundingRound",
functionName: "contributeAndVote",
args: [votedProjects, fundingAmount],
onBlockConfirmation: txnReceipt => {
console.log("📦 Transaction blockHash", txnReceipt.blockHash);
},
});

const { writeAsync: writeAsyncAllow, isLoading: isLoadingAllow } = useScaffoldContractWrite({
contractName: "MockUSDC",
functionName: "approve",
args: [deployedFundingRoundContract?.address, BigInt(100000)],
value: parseEther(fundingAmount.toString()),
onBlockConfirmation: txnReceipt => {
console.log("📦 Transaction blockHash", txnReceipt.blockHash);
setIsApproved(true);
},
});

return (
<>
<div className="hero min-h-screen" style={{ backgroundImage: "url(/hero.svg)" }}>
<div className="hero-content text-center text-neutral-content">
<div className="max-w-md">
<button className="btn btn-primary">Get Started</button>
</div>
<div className="max-w-md"></div>
</div>
</div>
<div className="p-6">
<h1 className="text-2xl">Funding round 1</h1>
<progress className="progress w-56" value="70" max="100"></progress>
{isProjectsLoading ? (
<span className="loading loading-spinner"></span>
) : (
projects?.map(project => (
<div key={project.id} className="card lg:card-side bg-base-100 shadow-xl mb-6">
<figure>
<img src="https://daisyui.com/images/stock/photo-1494232410401-ad00d5433cfa.jpg" alt="Album" />
</figure>
<div className="card-body">
<h3 className="card-title">{project.name}</h3>
<span>{project.balance.toString()}</span>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui
officia deserunt mollit anim id est laborum.
</p>
<div className="card-actions justify-end">
<label>Vote</label>
<input
type="checkbox"
onClick={() => {
setVotedProjects([...votedProjects, project.id]);
}}
/>
<div className="mt-28">
<div className="w-3/4 m-auto">
<h1 className="text-9xl text-white">Round 1 – Catalyst</h1>
<h2 className="text-3xl text-white mb-20">
Offer a helping hand fort the innovative gnome, that likes to tinker.
</h2>
{isProjectsLoading ? (
<span className="loading loading-spinner"></span>
) : (
projects?.map(project => (
<div key={project.id} className="card lg:card-side bg-base-100 shadow-xl mb-6">
<figure className="w-1/2">
<img src={`https://picsum.photos/id/${Number(project.id) + 100}/400`} alt="Album" />
</figure>
<div className="card-body w-1/2">
<h3 className="card-title text-4xl">{project.name}</h3>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore
et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.
</p>
<div className="card-actions justify-between">
<div className="badge badge-lg bg-orange-300">
<div className="p-5">Votes: {project.votingPoints.toString()}</div>
</div>
<div className="flex">
<label className="mr-2">select</label>
<input
type="checkbox"
className="toggle toddle-success"
checked={votedProjects.includes(project.id)}
onChange={() => {
setVotedProjects(
votedProjects.includes(project.id)
? votedProjects.filter(id => id !== project.id)
: [...votedProjects, project.id],
);
}}
/>
</div>
</div>
</div>
</div>
</div>
))
)}
<input
type="number"
onChange={event => {
setFundingAmount(Number(event.target.value));
}}
/>
<button className="btn btn-primary" onClick={() => writeAsync()} disabled={isLoading}>
{isLoading ? <span className="loading loading-spinner loading-sm"></span> : <>Fund</>}
</button>
))
)}
<div className="my-20 text-right">
<input
type="number"
className="input input-bordered w-full max-w-xs mr-10"
placeholder="Amount in USDC"
onChange={event => {
setFundingAmount(BigInt(event.target.value));
}}
/>
{isApproved ? (
<button
className="btn btn-ghost bg-purple-500"
onClick={() => writeAsyncAllow()}
disabled={isLoadingAllow}
>
{isLoadingAllow ? <span className="loading loading-spinner loading-sm"></span> : <>Approve</>}
</button>
) : (
<button className="btn btn-ghost bg-purple-500" onClick={() => writeAsync()} disabled={isLoading}>
{isLoading ? <span className="loading loading-spinner loading-sm"></span> : <>Fund</>}
</button>
)}
</div>
</div>
</div>
</>
);
Expand Down
Loading

0 comments on commit 8d294f7

Please sign in to comment.