Skip to content

Instantly share code, notes, and snippets.

@BlockmanCodes
Created May 15, 2022 13:50
Show Gist options
  • Save BlockmanCodes/8af5b1ca5eb653edc03ad124e0e688b7 to your computer and use it in GitHub Desktop.
Save BlockmanCodes/8af5b1ca5eb653edc03ad124e0e688b7 to your computer and use it in GitHub Desktop.
Ether Donation Dapp
{
"name": "client",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.2.0",
"@testing-library/user-event": "^13.5.0",
"bootstrap": "^5.1.3",
"ethers": "^5.6.6",
"react": "^18.1.0",
"react-bootstrap-icons": "^1.8.2",
"react-dom": "^18.1.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #1799F7;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.amountButton {
cursor: pointer;
color: #000;
background-color: #fff;
font-weight: 600;
display: inline-block;
text-align: center;
padding: .375rem .75rem;
font-family: -apple-system;
text-decoration: none;
width: 100%;
margin-left: 10px !important;
margin-top: 5px !important;
margin-bottom: 5px !important;
}
.amountButtonLeft {
padding-right: 5px !important;
}
.amountButtonRight {
padding-left: 5px !important;
}
.amountButton:hover {
color: #000 !important;
background-color: #ffe484;
border-color: #ffe484;
}
.amountButton:active {
box-shadow: 0 2px #666;
transform: translateY(3px);
}
.amountClicked {
color: #000 !important;
background-color: #ffe484;
border-color: #ffe484;
}
.donateHeader {
color: #ffe484;
font-size: 42px;
}
.dot {
float: left;
margin-top: 12px !important;
margin-left: 15px !important;
height: 15px !important;
width: 15px !important;
background-color: #bbb;
border-radius: 50%;
display: inline-block;
}
.redDot {
background-color: #f85520;
}
.greenDot {
background-color: #00FF00;
}
.donationBubbleLeft {
border-radius: 4em 4em / 4em 4em;
background-color: #fff;
width: 100%;
color: #000;
margin-top: 5px;
text-align: left;
padding-left: 10px;
}
.paddingLeft {
padding-left: 15px;
}
.byAddress {
font-size: 18px;
}
import './App.css';
import { ethers } from 'ethers';
import { useEffect, useState } from 'react';
import { SuitHeartFill } from 'react-bootstrap-icons';
import artifact from './artifacts/contracts/Donation.sol/Donation.json'
function App() {
const [provider, setProvider] = useState(undefined);
const [signer, setSigner] = useState(undefined);
const [contract, setContract] = useState(undefined);
const [amount, setAmount] = useState(0);
const [donations, setDonations] = useState([]);
const toString = bytes32 => ethers.utils.parseBytes32String(bytes32);
const toWei = ether => ethers.utils.parseEther(ether);
const toEther = wei => ethers.utils.formatEther(wei).toString();
useEffect(() => {
const init = async () => {
const provider = await new ethers.providers.Web3Provider(window.ethereum)
setProvider(provider)
const contract = await new ethers.Contract(
'0x5FbDB2315678afecb367f032d93F642f64180aa3',
artifact.abi
)
setContract(contract)
contract.connect(provider).getDonations()
.then((result) => {
const donations = result.map(el => [el[0], toEther(el[1])])
setDonations(donations)
})
}
init();
}, [])
const isConnected = () => (signer !== undefined)
const getSigner = async provider => {
provider.send("eth_requestAccounts", []);
const signer = provider.getSigner();
setSigner(signer)
}
const connect = () => {
getSigner(provider)
}
const sendDonation = async () => {
const wei = toWei(amount)
await signer.sendTransaction({
to: contract.address,
value: wei
})
setAmount('0')
}
return (
<div className="App">
<header className="App-header">
<div className="row" style={{width: '800px'}}>
<div className="col-md-4">
<div className="row">
<div className="col-md-12">
<h1 className="donateHeader">Donate ETH</h1>
</div>
</div>
<div className="row">
<div className="col-md-6 amountButtonLeft">
<a
onClick={ () => setAmount('0.1') }
className={"amountButton " + (amount === '0.1' ? 'amountClicked' : '')}>
0.1
</a>
</div>
<div className="col-md-6 amountButtonRight">
<a
onClick={ () => setAmount('0.5') }
className={"amountButton " + (amount === '0.5' ? 'amountClicked' : '')}>
0.5
</a>
</div>
</div>
<div className="row">
<div className="col-md-6 amountButtonLeft">
<a
onClick={ () => setAmount('1') }
className={"amountButton " + (amount === '1' ? 'amountClicked' : '')}>
1
</a>
</div>
<div className="col-md-6 amountButtonRight">
<a
onClick={ () => setAmount('2') }
className={"amountButton " + (amount === '2' ? 'amountClicked' : '')}>
2
</a>
</div>
</div>
<div className="row">
<div className="col-md-12">
<a
onClick={ () => sendDonation() }
className="amountButton">Donate</a>
</div>
</div>
<div className="row">
<div className="col-md-12">
{isConnected() ? (
<>
<span className="dot greenDot"></span>
<p style={{fontSize: '25px'}}>Connected</p>
</>
) : (
<>
<span className="dot redDot"></span>
<p style={{fontSize: '25px'}}>Not connected</p>
<button onClick={connect} className="btn btn-primary">Connect Wallet</button>
</>
)}
</div>
</div>
</div>
<div className="col-md-2">
</div>
<div className="col-md-6">
<div className="row">
<div className="col-md-12">
<h1 className="donateHeader">Recent Donations</h1>
</div>
</div>
{donations.map((ds,idx) => (
<>
<div className="donationBubbleLeft">
<SuitHeartFill fill="#FF7F97" />
<span className="paddingLeft">
{ds[1]} ETH
&nbsp;
<span className="byAddress">by {ds[0]?.substring(0,14)}...</span>
</span>
</div>
</>
))}
</div>
</div>
</header>
</div>
);
}
export default App;
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import 'bootstrap/dist/css/bootstrap.css';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
pragma solidity ^0.8.0;
contract Donation {
address owner;
uint256 totalDonations;
struct Donation {
address donor;
uint256 amount;
}
Donation donation;
Donation[] donations;
constructor() {
owner = msg.sender;
}
receive() external payable {
donation = Donation(
msg.sender,
msg.value
);
donations.push(donation);
totalDonations += msg.value;
}
function getDonations() external view returns (Donation[] memory) {
return donations;
}
function getTotalDonations() external view returns (uint256) {
return totalDonations;
}
}
{
"name": "ether-donation-dapp",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@nomiclabs/hardhat-ethers": "^2.0.6",
"@nomiclabs/hardhat-waffle": "^2.0.3",
"chai": "^4.3.6",
"ethereum-waffle": "^3.4.4",
"ethers": "^5.6.6",
"hardhat": "^2.9.5"
}
}
async function main() {
[signer1, signer2, signer3] = await ethers.getSigners();
const Donation = await ethers.getContractFactory('Donation', signer1);
const donation = await Donation.deploy();
console.log("Donation contract deployed to:", donation.address, "by", signer1.address);
await signer1.sendTransaction({
to: donation.address,
value: ethers.utils.parseUnits('0.1', 18)
});
await signer2.sendTransaction({
to: donation.address,
value: ethers.utils.parseUnits('0.1', 18)
});
await signer3.sendTransaction({
to: donation.address,
value: ethers.utils.parseUnits('2', 18)
});
}
// npx hardhat run --network localhost scripts/01_deploy.js
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
const { expect } = require("chai");
describe('Donation', function () {
beforeEach(async function() {
[signer1, signer2, signer3] = await ethers.getSigners();
Donation = await ethers.getContractFactory('Donation', signer1);
donation = await Donation.deploy()
});
describe('donateEther', function () {
it('transfers ether to the contract', async function() {
const provider = waffle.provider;
await signer2.sendTransaction({
to: donation.address,
value: '100'
});
expect(
await provider.getBalance(donation.address)
).to.equal('100');
});
})
describe('getTotalDonations', function () {
it('returns the sum of donations transferred to the contract', async function() {
const provider = waffle.provider;
await signer1.sendTransaction({
to: donation.address,
value: '1'
});
await signer2.sendTransaction({
to: donation.address,
value: '5'
});
await signer3.sendTransaction({
to: donation.address,
value: '50'
});
expect(
await donation.connect(provider).getTotalDonations()
).to.equal('56')
})
})
describe('getDonations', function () {
it('returns an array of donations transferred to the contract', async function() {
const provider = waffle.provider;
await signer1.sendTransaction({
to: donation.address,
value: '10'
});
await signer2.sendTransaction({
to: donation.address,
value: '20'
});
const donations = await donation.connect(provider).getDonations()
expect(donations[0].donor).to.equal(signer1.address);
expect(donations[0].amount).to.equal('10');
expect(donations[1].donor).to.equal(signer2.address);
expect(donations[1].amount).to.equal('20');
})
})
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment