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

Feat/list requests #4

Merged
merged 5 commits into from
Dec 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 💸 EasyPay 💸
# 💸 Easy2Pay 💸

💸 dApp for requesting payments in a currency (like USD, ETH and BTC), and accept different type of valid tokens that share a common value using [Chainlink Price Feeds](https://docs.chain.link/data-feeds/price-feeds).

Expand All @@ -9,15 +9,15 @@

## Quickstart

To get started with EasyPay, follow the steps below:
To get started with Easy2Pay, follow the steps below:

1. Make sure you have the [foundry toolkit installed](https://book.getfoundry.sh/getting-started/installation).

2. Clone this repo & install dependencies

```
git clone https://github.com/luloxi/EasyPay.git
cd EasyPay
git clone https://github.com/luloxi/Easy2Pay.git
cd Easy2Pay
yarn install
```

Expand Down Expand Up @@ -47,7 +47,7 @@ Visit your app on: `http://localhost:3000`. You can interact with your smart con

Run smart contract test with `yarn hardhat:test`

- Edit your smart contract `EasyPay.sol` in `packages/hardhat/contracts`
- Edit your smart contract `Easy2Pay.sol` in `packages/hardhat/contracts`
- Edit your frontend in `packages/nextjs/pages`
- Edit your deployment scripts in `packages/hardhat/deploy`

Expand All @@ -65,7 +65,7 @@ Run smart contract test with `yarn hardhat:test`

## Contents

- [💸 EasyPay 💸](#-easypay-)
- [💸 Easy2Pay 💸](#-easypay-)
- [Quickstart](#quickstart)
- [🏗 About Scaffold-ETH 2](#-about-scaffold-eth-2)
- [Contents](#contents)
Expand Down
84 changes: 84 additions & 0 deletions packages/foundry/contracts/Easy2Pay.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/**
@dev Struct to store payment requests
@variables amount: Amount of USD requested
@variables receiver: Address of payment receiver
@variables completed: Boolean to determine if payment was succesfully made
*/
struct PayRequest {
uint256 amount;
bool completed;
}

contract Easy2Pay {
address public owner;

/**
@dev Mapping to store PayRequest structs mapped to a unique requestId
*/
mapping(address receiver => PayRequest[]) public payRequests;

// Custom errors
error Easy2Pay__RequestDoesNotExist();
error Easy2Pay__InsufficientEther(
uint256 requestedAmount,
uint256 actualAmount
);
error Easy2Pay__PaymentAlreadyCompleted();
error Easy2Pay__FailedToSendEther();
error Easy2Pay__UnauthorizedAccess();

modifier onlyOwner() {
if (msg.sender != owner) revert Easy2Pay__UnauthorizedAccess();
_;
}

constructor() {
owner = msg.sender;
}

function requestPayment(uint256 _amount) public {
uint256 id = payRequests[msg.sender].length;
payRequests[msg.sender].push(PayRequest(_amount, false));
}

function getRequests(address receiver) public view returns(PayRequest[] memory) {
return payRequests[receiver];
}

function pay(address receiver, uint256 _requestId) public payable {
PayRequest storage request = payRequests[receiver][_requestId];

if (receiver == address(0))
revert Easy2Pay__RequestDoesNotExist();

if (request.amount > msg.value)
revert Easy2Pay__InsufficientEther(request.amount, msg.value);

if (request.completed) revert Easy2Pay__PaymentAlreadyCompleted();

request.completed = true;

// Call returns a boolean value indicating success or failure.
// This is the current recommended method to use to transfer ETH.
(bool sent, ) = receiver.call{value: msg.value}("");

if (!sent) revert Easy2Pay__FailedToSendEther();
}

// Function in case a payment is received where msg.data must be empty
receive() external payable {
if (msg.sender != address(0)) revert Easy2Pay__FailedToSendEther();
}

// Fallback function is called when msg.data is not empty
fallback() external payable {
if (msg.sender != address(0)) revert Easy2Pay__FailedToSendEther();
}

function setOwner(address _newOwner) public onlyOwner {
owner = _newOwner;
}
}
82 changes: 0 additions & 82 deletions packages/foundry/contracts/EasyPay.sol

This file was deleted.

6 changes: 3 additions & 3 deletions packages/foundry/script/Deploy.s.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "../contracts/EasyPay.sol";
import "../contracts/Easy2Pay.sol";
import "./DeployHelpers.s.sol";

contract DeployScript is ScaffoldETHDeploy {
Expand All @@ -15,10 +15,10 @@ contract DeployScript is ScaffoldETHDeploy {
);
}
vm.startBroadcast(deployerPrivateKey);
EasyPay easyPay = new EasyPay();
Easy2Pay easyPay = new Easy2Pay();
console.logString(
string.concat(
"EasyPay deployed at: ",
"Easy2Pay deployed at: ",
vm.toString(address(easyPay))
)
);
Expand Down
7 changes: 6 additions & 1 deletion packages/nextjs/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useCallback, useRef, useState } from "react";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/router";
import { Bars3Icon, BugAntIcon } from "@heroicons/react/24/outline";
import { BanknotesIcon, Bars3Icon, BugAntIcon } from "@heroicons/react/24/outline";
import { FaucetButton, RainbowKitCustomConnectButton } from "~~/components/scaffold-eth";
import { useOutsideClick } from "~~/hooks/scaffold-eth";

Expand All @@ -17,6 +17,11 @@ export const menuLinks: HeaderMenuLink[] = [
label: "Home",
href: "/",
},
{
label: "Payment Requests",
href: "/requests",
icon: <BanknotesIcon className="h-4 w-4" />,
},
{
label: "Debug Contracts",
href: "/debug",
Expand Down
1 change: 1 addition & 0 deletions packages/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"react-copy-to-clipboard": "~5.1.0",
"react-dom": "~18.2.0",
"react-hot-toast": "~2.4.0",
"react-qr-code": "^2.0.12",
"use-debounce": "~8.0.4",
"usehooks-ts": "~2.9.1",
"viem": "1.19.9",
Expand Down
49 changes: 49 additions & 0 deletions packages/nextjs/pages/makePayment.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useEffect, useState } from "react";
import { useSearchParams } from "next/navigation";
import { useConnectModal } from "@rainbow-me/rainbowkit";
import type { NextPage } from "next";
import { useAccount } from "wagmi";
import { useScaffoldContractWrite } from "~~/hooks/scaffold-eth";

const Requests: NextPage = () => {
const { isConnected } = useAccount();
const [hasBeenCalled, setHasBeenCalled] = useState(false);
const { openConnectModal } = useConnectModal();
const searchParams = useSearchParams();
const recipient = searchParams.get("recipient") || undefined;
const amount = searchParams.get("amount") || undefined;
const requestId = searchParams.get("requestId") || undefined;
const { writeAsync: sendPaymentTx, isLoading } = useScaffoldContractWrite({
contractName: "Easy2Pay",
functionName: "pay",
args: [recipient, BigInt(requestId || "0")],
// for payable functions, expressed in eth
value: BigInt(amount || "0"),
// the number of block confirmations to wait for before considering transaction to be confirmed (default : 1).
blockConfirmations: 1,
// the callback function to execute when the transaction is confirmed.
onBlockConfirmation: txnreceipt => {
console.log("transaction blockhash", txnreceipt.blockHash);
},
});

useEffect(() => {
if (!isConnected) openConnectModal?.();
}, [isConnected, openConnectModal]);

useEffect(() => {
const sendPaymentTxAndHandleErr = async () => {
const response = await sendPaymentTx();
console.log({ response });
};

if (recipient && amount && requestId && !hasBeenCalled && !isLoading && isConnected) {
sendPaymentTxAndHandleErr();
setHasBeenCalled(true);
}
}, [recipient, amount, requestId, hasBeenCalled, isConnected]);

return <></>;
};

export default Requests;
Loading
Loading