Skip to content

Instantly share code, notes, and snippets.

@Ernesto-tha-great
Created June 17, 2022 15:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ernesto-tha-great/4f129e7b7c09fea285f22f90a9cd17b8 to your computer and use it in GitHub Desktop.
Save Ernesto-tha-great/4f129e7b7c09fea285f22f90a9cd17b8 to your computer and use it in GitHub Desktop.
@tailwind base;
@tailwind components;
@tailwind utilities;
@import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;500;600;700&display=swap");
* html {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
margin: 0;
font-family: "Open Sans", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.gradient-bg-welcome {
background-color:#0f0e13;
background-image:
radial-gradient(at 0% 0%, hsla(253,16%,7%,1) 0, transparent 50%),
radial-gradient(at 50% 0%, hsla(225,39%,30%,1) 0, transparent 50%),
radial-gradient(at 100% 0%, hsla(339,49%,30%,1) 0, transparent 50%);
}
.gradient-bg-services {
background-color:#0f0e13;
background-image:
radial-gradient(at 0% 0%, hsla(253,16%,7%,1) 0, transparent 50%),
radial-gradient(at 50% 100%, hsla(225,39%,25%,1) 0, transparent 50%);
}
.gradient-bg-transactions {
background-color: #0f0e13;
background-image:
radial-gradient(at 0% 100%, hsla(253,16%,7%,1) 0, transparent 50%),
radial-gradient(at 50% 0%, hsla(225,39%,25%,1) 0, transparent 50%);
}
.gradient-bg-footer {
background-color: #0f0e13;
background-image:
radial-gradient(at 0% 100%, hsla(253,16%,7%,1) 0, transparent 53%),
radial-gradient(at 50% 150%, hsla(339,49%,30%,1) 0, transparent 50%);
}
.blue-glassmorphism {
background: rgb(39, 51, 89, 0.4);
border-radius: 16px;
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.2);
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
border: 1px solid rgba(0, 0, 0, 0.3);
}
/* white glassmorphism */
.white-glassmorphism {
background: rgba(255, 255, 255, 0.05);
border-radius: 16px;
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
border: 1px solid rgba(255, 255, 255, 0.3);
}
.eth-card {
background-color:#a099ff;
background-image:
radial-gradient(at 83% 67%, rgb(152, 231, 156) 0, transparent 58%),
radial-gradient(at 67% 20%, hsla(357,94%,71%,1) 0, transparent 59%),
radial-gradient(at 88% 35%, hsla(222,81%,65%,1) 0, transparent 50%),
radial-gradient(at 31% 91%, hsla(9,61%,61%,1) 0, transparent 52%),
radial-gradient(at 27% 71%, hsla(336,91%,65%,1) 0, transparent 49%),
radial-gradient(at 74% 89%, hsla(30,98%,65%,1) 0, transparent 51%),
radial-gradient(at 53% 75%, hsla(174,94%,68%,1) 0, transparent 45%);
}
.text-gradient {
background-color: #fff;
background-image: radial-gradient(at 4% 36%, hsla(0,0%,100%,1) 0, transparent 53%), radial-gradient(at 100% 60%, rgb(0, 0, 0) 0, transparent 50%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
@tailwind base;
@tailwind components;
@tailwind utilities;
import * as React from "react";
import deployedContracts from "@celo-progressive-dapp-starter/hardhat/deployments/hardhat_contracts.json";
import { useCelo } from '@celo/react-celo';
import AppLayout from "@/components/layout/AppLayout";
import Welcome from "@/components/Welcome";
export default function App() {
const { network } = useCelo();
const contracts =
deployedContracts[network?.chainId?.toString()]?.[
network?.name?.toLocaleLowerCase()
]?.contracts;
return (
<AppLayout title="Celo Starter" description="Celo Starter">
<Welcome contractData={contracts?.CrowdFund} />
</AppLayout>
);
}
import React from 'react'
interface InputProps {
placeholder: string,
name: string,
type: string,
handleChange: (e: React.ChangeEvent<HTMLInputElement>, name: string) => void
}
export const Input = ({placeholder, name, type, handleChange}: InputProps) => (
<input
placeholder={placeholder}
type={type}
step="0.0001"
onChange={(e) => handleChange(e, name)}
className='my-2 w-full rounded-sm p-2 outline-none bg-transparent text-white border-none text-sm white-glassmorphism'
/>
)
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Importing OpenZeppelin's SafeMath Implementation
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract Project {
using SafeMath for uint256;
enum ProjectState {
Fundraising,
Expired,
Successful
}
IERC20 private cUSDToken;
// Initialize public variables
address payable public creator;
uint public goalAmount;
uint public completeAt;
uint256 public currentBalance;
uint public raisingDeadline;
string public title;
string public description;
string public imageLink;
// Initialize state at fundraising
ProjectState public state = ProjectState.Fundraising;
mapping (address => uint) public contributions;
// Event when funding is received
event ReceivedFunding(address contributor, uint amount, uint currentTotal);
// Event for when the project creator has received their funds
event CreatorPaid(address recipient);
modifier theState(ProjectState _state) {
require(state == _state);
_;
}
constructor
(
IERC20 token,
address payable projectCreator,
string memory projectTitle,
string memory projectDescription,
string memory projectImageLink,
uint fundRaisingDeadline,
uint projectGoalAmount
) {
cUSDToken = token;
creator = projectCreator;
title = projectTitle;
description = projectDescription;
imageLink = projectImageLink;
goalAmount = projectGoalAmount;
raisingDeadline = fundRaisingDeadline;
currentBalance = 0;
}
// Fund a project
function contribute(uint256 amount) external theState(ProjectState.Fundraising) payable {
cUSDToken.transferFrom(msg.sender, address(this), amount);
contributions[msg.sender] = contributions[msg.sender].add(amount);
currentBalance = currentBalance.add(amount);
emit ReceivedFunding(msg.sender, amount, currentBalance);
checkIfFundingExpired();
}
// check project state
function checkIfFundingExpired() public {
if (block.timestamp > raisingDeadline) {
state = ProjectState.Expired;
}
}
function payOut() external returns (bool result) {
require(msg.sender == creator);
uint256 totalRaised = currentBalance;
currentBalance = 0;
if (cUSDToken.transfer(msg.sender, totalRaised)) {
emit CreatorPaid(creator);
state = ProjectState.Successful;
return true;
}
else {
currentBalance = totalRaised;
state = ProjectState.Successful;
}
return false;
}
function getDetails() public view returns
(
address payable projectCreator,
string memory projectTitle,
string memory projectDescription,
string memory projectImageLink,
uint fundRaisingDeadline,
ProjectState currentState,
uint256 projectGoalAmount,
uint256 currentAmount
) {
projectCreator = creator;
projectTitle = title;
projectDescription = description;
projectImageLink = imageLink;
fundRaisingDeadline = raisingDeadline;
currentState = state;
projectGoalAmount = goalAmount;
currentAmount = currentBalance;
return (projectCreator, projectTitle, projectDescription, projectImageLink, fundRaisingDeadline, currentState, projectGoalAmount, currentAmount);
}
}
import React from 'react'
import Image from "next/image";
import { truncateAddress, formatTime } from '@/utils';
export const ProjectCard = ({item }) => (
<div className='white-glassmorphism m-4 flex flex-1
2xl:min-w-[450px]
2xl:max-w-[450px]
sm:min-w-[270px]
sm:max-w-[300px]
flex-col p-3 rounded-md hover:shadow-2xl
'>
<div className='flex flex-col items-center w-full mt-3'>
<div className='flex flex-col justify-start w-full p-2'>
<h3 className='text-white text-center mb-2 -mt-6 font-semibold'>{item.projectTitle}</h3>
<Image src={item.projectImageLink} alt='logo' className='rounded-md' width={80} height={180}/>
<h3 className='text-white mb-2 mt-2 font-small'>{item.projectDescription}</h3>
<h3 className='text-white mb-2 font-small'>Goal Amount: {item.projectGoalAmount}</h3>
<h3 className='text-white mb-2 font-small'>Creator: { truncateAddress(item.projectCreator)}</h3>
<h3 className='text-white mb-2 font-small'>Deadline: { formatTime(item.fundRaisingDeadline)}</h3>
</div>
</div>
</div>
);
import React, {useState, useEffect} from 'react'
import {SiEthereum} from 'react-icons/si';
import {BsInfoCircle} from 'react-icons/bs';
import { truncateAddress } from '@/utils';
import { useCelo } from '@celo/react-celo';
import { CrowdFund } from "@celo-progressive-dapp-starter/hardhat/types/Crowdfund";
import {Project} from "@celo-progressive-dapp-starter/hardhat/types/Project"
import { CeloContract } from '@celo/contractkit/lib/base';
import deployedContracts from "@celo-progressive-dapp-starter/hardhat/artifacts/contracts/Project.sol/Project.json";
import {ProjectCard} from './ProjectCard';
import { Input } from './Input';
const Welcome = ({contractData }) => {
const { address, connect, kit } = useCelo();
const [formData, setFormData] = useState({
title: '',
desc: '',
img: '',
duration: '',
goal: ''
});
const [results, setResults] = useState<any>([]);
useEffect(() => {
const fetchProjects = async () => {
const result = await contract.methods.returnProjects().call();
const data2 = []
for (let i = 0; i < result.length; i++) {
const projectContract = contractData
? (new kit.connection.web3.eth.Contract(
deployedContracts.abi,
result[i]
) as any as Project)
: null;
const data = await projectContract.methods.getDetails().call()
const structuredData = {
projectCreator: data.projectCreator,
projectTitle: data.projectTitle,
projectDescription: data.projectDescription,
projectImageLink: data.projectImageLink,
fundRaisingDeadline: data.fundRaisingDeadline,
projectGoalAmount: data.projectGoalAmount
}
data2.push(structuredData)
}
setResults(data2)
}
fetchProjects()
}, [])
const handleSubmit = (e) => {
const {title, desc, img, duration, goal } = formData;
e.preventDefault();
if (!title || !desc || !duration || !goal || !img ) {
return alert('Please fill all the fields');
}else {
createProject();
}
}
const contract = contractData
? (new kit.connection.web3.eth.Contract(
contractData.abi,
contractData.address
) as any as CrowdFund)
: null;
const handleChange = (e: { target: { value: any; }; }, name: any) => {
setFormData(( prevState ) => ({ ...prevState, [name]: e.target.value }));
}
const createProject = async () => {
const stableTokenAddress = await kit.registry.addressFor(CeloContract.StableToken)
const gasPriceMinimumContract = await kit.contracts.connection.gasPrice()
const {title, desc, img, duration, goal } = formData;
await contract.methods.startProject(stableTokenAddress, title, desc, img, duration, goal).send({from: address, gasPrice: gasPriceMinimumContract})
}
return (
<div className='flex flex-1 flex-col'>
<div className=' flex w-full justify-center items-center'>
<div className='flex mf:flex-row flex-col items-start justify-between md:p-20 py-12 px-4'>
<div className='flex flex-1 justify-start flex-col mf:mr-10'>
<h1 className='text-white text-3xl sm:text-5xl text-gradient py-1'>
pool Crypto <br /> solve real problems
</h1>
<p className='text-left mt-5 text-white font-light md:w-9/12 w-11/12 text-base'>
create and support campaigns to help towards a greener and sustainable enviroment for the future!
</p>
{!address && (
<button className='flex flex-row justify-center items-center my-5 bg-[#2952e3] p-3 rounded-full cursor-pointer hover:bg-[#2546bd]'
type='button'
onClick={() => connect().catch(e => console.log(e))}
>
<p className='text-white text-base font-semibold'>Connect Wallet</p>
</button>
)}
<div className='white-glassmorphism text-white p-5 mt-4 rounded-md'>
<div className='items-center'>
<p className='font-bold mb-5'>How it Works</p>
<p className='my-3'>1. Creator creates a new project </p>
<p className='my-3'>2. Contributors contribute until deadline</p>
<p className='my-3'>3. If total pledged doesn&apos;t get met on deadline date, contributors expire the project and refund donated funds back</p>
<p className='my-3'>4. If total amount pledged reaches the goal, creator declares the fundraise a success</p>
</div>
</div>
</div>
<div className='flex flex-col flex-1 items-center justify-start w-full mf:mt-0 mt-10'>
<div className='p-3 justify-end items-start flex-col rounded-xl h-40 sm:w-72 w-full my-5 eth-card white-glassmorphism'>
<div className='flex justify-between flex-col w-full h-full'>
<div className='flex justify-between items-start'>
<div className='w-10 h-10 rounded-full border-2 border-white flex justify-center items-center'>
<SiEthereum fontSize={21} className='text-white text-2xl' />
</div>
<BsInfoCircle fontSize={17} className='text-white text-2xl ml-2' />
</div>
<div>
<p className='text-white font-light text-sm'>{truncateAddress(address)}</p>
<p className='text-white font-semibold text-lg mt-1'>Address</p>
</div>
</div>
</div>
<div className='p-5 sm:w-96 w-full flex flex-col justify-start items-center blue-glassmorphism'>
<h2 className='text-white font-medium'>Start a Project</h2>
<Input placeholder='Project Title' name='title' type='text' handleChange={handleChange} />
<Input placeholder='Project Description' name='desc' type='text' handleChange={handleChange} />
<Input placeholder='Image Link' name='img' type='text' handleChange={handleChange} />
<Input placeholder='Duration in days.' name='duration' type='number' handleChange={handleChange} />
<Input placeholder='goal amount' name='goal' type='number' handleChange={handleChange} />
<div className='h-[1px] w-full bg-gray-400 my-2' />
<button className='text-white w-full mt-2 border-[1px] p-2 border-[#3d4f7c] rounded-full cursor-pointer' type='button' onClick={handleSubmit}>
Create
</button>
</div>
</div>
</div>
</div>
<div className='blue-glassmorphism grdient-bg-transactions'>
<div className='flex flex-col md:p-12 py-12 px-4'>
{address ? (
<>
<h3 className='text-white text-3xl text-center my-2'>Latest Campaigns</h3>
<div className='flex flex-wrap justify-center items-center mt-10'>
{ results?.map((item, index) => (
<ProjectCard item={item} key={index} />
)).reverse()}
</div>
</>
) : (
<h3 className='text-white text-3xl text-center my-2'>Connect your account</h3>
)}
</div>
</div>
</div>
)
}
export default Welcome
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment