Skip to content

Commit

Permalink
deployed on optimism and added claimInvoiceAmount func for seller
Browse files Browse the repository at this point in the history
  • Loading branch information
sharayu committed Sep 25, 2024
1 parent 133ef10 commit 83e1303
Show file tree
Hide file tree
Showing 11 changed files with 172 additions and 33 deletions.
20 changes: 19 additions & 1 deletion next-app/blockend/build/invoice.json
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@
},
{
"inputs": [],
"name": "claimInvoiceAMount",
"name": "claimInvoiceAmount",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
Expand Down Expand Up @@ -662,6 +662,11 @@
"internalType": "uint256",
"name": "tenure",
"type": "uint256"
},
{
"internalType": "bool",
"name": "isCompleted",
"type": "bool"
}
],
"internalType": "struct Invoice.InvoiceInfo",
Expand Down Expand Up @@ -786,6 +791,19 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "invoiceClaimedBySeller",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "invoiceHash",
Expand Down
88 changes: 79 additions & 9 deletions next-app/blockend/contracts/Invoice.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,23 @@ import "C:/Next/Alt-Investment/Alt-Investment/next-app/node_modules/@openzeppeli
import "C:/Next/Alt-Investment/Alt-Investment/next-app/node_modules/@openzeppelin/contracts/utils/Pausable.sol";
import "C:/Next/Alt-Investment/Alt-Investment/next-app/node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";


contract InvoiceFactory is AccessControl {

bytes32 public constant FACTOR_ROLE = keccak256("FACTOR_ROLE");
address[] public deployedInvoices;
mapping(address => bool) public activeInvoices;
address public immutable admin;

// Fixed USDC token address (Ethereum mainnet)
address public constant USDC_ADDRESS = 0x2295B8D2b756163cFe8C6C9151be898E6109Eaa8;
// Fixed USDC token address (=
address public USDC_ADDRESS;

event InvoiceCreated(address invoiceAddress, string ipfsHash);
event FactorAdded(address factor);
event FactorRemoved(address factor);

constructor() {
constructor(address usdc_address) {
admin = msg.sender;
USDC_ADDRESS = usdc_address;
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(FACTOR_ROLE, msg.sender);
}
Expand Down Expand Up @@ -63,7 +63,7 @@ contract InvoiceFactory is AccessControl {
Invoice(invoiceAddress).setFactor(newFactor);
}

mapping(address => address[]) public investorInvoices; // Mapping of investor address to an array of invoice addresses.
mapping(address => address[]) private investorInvoices; // Mapping of investor address to an array of invoice addresses.

function addInvestorInvoice(address investor, address invoiceAddress) external {
require(activeInvoices[invoiceAddress], "Invoice is not active");
Expand Down Expand Up @@ -102,6 +102,10 @@ contract Invoice is ReentrancyGuard, AccessControl, Pausable {
uint256 public expectedRepaymentDate;
uint256 public expectedPayoutDate;
bool public isCompleted;
string public sellerName;
string public buyerName;
uint256 xirr;
bool public invoiceClaimedBySeller;

struct Investor {
uint256 investedAmount;
Expand All @@ -113,12 +117,32 @@ contract Invoice is ReentrancyGuard, AccessControl, Pausable {
mapping(address => Investor) public investors;
address[] public investorList;

struct InvoiceInfo{
string invoiceHash;
string agreementHash;

string sellerName;
string buyerName;
uint256 xirr;

uint256 totalInvoiceAmount;
uint256 fees;
uint256 amountPerUnit;
uint256 repaymentPerUnit;
uint256 totalUnits;
uint256 purchasedUnits;
uint256 tenure;
bool isCompleted;
}

event InvoiceApproved(address factor, uint256 totalAmount, uint256 totalUnits);
event InvoicePurchased(address investor, uint256 units, uint256 amount);
event InvoicePaid(address payer, uint256 amount);
event FundsTransferredToSeller(address seller, uint256 amount);
event InvestorRepaid(address investor, uint256 amount);
event FeesTransferredToFactor(address factor, uint256 amount);
event InvoiceSigned(address seller);


constructor(string memory _ipfsHash, address _seller, address _factoryAddress) {
require(_seller != address(0), "Seller address cannot be zero");
Expand Down Expand Up @@ -153,7 +177,10 @@ contract Invoice is ReentrancyGuard, AccessControl, Pausable {
uint256 _totalUnits,
uint256 _tenure,
string memory _agreementHash,
uint256 _fees
uint256 _fees,
string memory _sellerName,
string memory _buyerName,
uint256 _xirr
) external onlyRole(FACTOR_ROLE) whenNotPaused {
require(_fees < _totalInvoiceAmount, "Fees cannot be greater than total invoice amount");
require(_totalInvoiceAmount == _amountPerUnit*(_totalUnits), "Invalid total amount");
Expand All @@ -168,6 +195,9 @@ contract Invoice is ReentrancyGuard, AccessControl, Pausable {
tenure = _tenure;
expectedRepaymentDate = block.timestamp + (tenure * (86400));
// expectedPayoutDate = block.timestamp.add(1 days); // Assuming 1 day for payout after full investment
sellerName = _sellerName;
buyerName = _buyerName;
xirr = _xirr;

emit InvoiceApproved(msg.sender, totalInvoiceAmount, totalUnits);
}
Expand All @@ -178,7 +208,9 @@ contract Invoice is ReentrancyGuard, AccessControl, Pausable {
uint256 purchaseAmount = _units * amountPerUnit;

require(usdcToken.balanceOf(msg.sender) >= purchaseAmount, "Insufficient USDC balance");
require(usdcToken.allowance(msg.sender, address(this)) >= purchaseAmount, "Insufficient allowance");
// require(usdcToken.allowance(msg.sender, address(this)) >= purchaseAmount, "Insufficient allowance");
require(!invoiceClaimedBySeller, "Invoice is already claimed");


// Update state before external calls
purchasedUnits += _units;
Expand All @@ -202,12 +234,19 @@ contract Invoice is ReentrancyGuard, AccessControl, Pausable {
}
}

function signAgreement() external onlyRole(SELLER_ROLE) {
//Think on how to get it signed by Payer and Seller
//Request should go to seller's and payer's dashboard to sign - but apart from that
//It should be signed just by the seller - perhaps. Confirm on this with AB
emit InvoiceSigned(msg.sender);
}


function payInvoice() external nonReentrant whenNotPaused {
require(!isCompleted, "Invoice already completed");
require(purchasedUnits == totalUnits, "Invoice not fully funded");
// require(purchasedUnits == totalUnits, "Invoice not fully funded");

uint256 totalPayable = (totalUnits * repaymentPerUnit) + fees;
uint256 totalPayable = (purchasedUnits * repaymentPerUnit) + fees;

// Update state before external calls
isCompleted = true;
Expand Down Expand Up @@ -251,6 +290,16 @@ contract Invoice is ReentrancyGuard, AccessControl, Pausable {
emit FeesTransferredToFactor(msg.sender, feeAmount);
}

function claimInvoiceAmount() external onlyRole(SELLER_ROLE) nonReentrant whenNotPaused {
require(!isCompleted, "Invoice already completed");
require(purchasedUnits > 0, "No amount to claim");

invoiceClaimedBySeller = true;

usdcToken.safeTransfer(msg.sender, purchasedUnits*amountPerUnit);
emit FundsTransferredToSeller(seller, purchasedUnits*amountPerUnit);
}

function _transferToSeller() internal {
require(seller != address(0), "Seller address not set");
require(usdcToken.transfer(seller, totalInvoiceAmount), "Transfer to seller failed");
Expand All @@ -272,4 +321,25 @@ contract Invoice is ReentrancyGuard, AccessControl, Pausable {
function getAllInvestors() external view returns (address[] memory) {
return investorList;
}

function getInvoiceInfo() public view returns(InvoiceInfo memory){
InvoiceInfo memory invoiceInfo;
invoiceInfo.agreementHash = agreementHash;
invoiceInfo.amountPerUnit = amountPerUnit;
invoiceInfo.buyerName = buyerName;
invoiceInfo.fees = fees;
invoiceInfo.invoiceHash = invoiceHash;
invoiceInfo.sellerName = sellerName;
invoiceInfo.xirr = xirr;
invoiceInfo.purchasedUnits = purchasedUnits;
invoiceInfo.repaymentPerUnit = repaymentPerUnit;
invoiceInfo.tenure = tenure;
invoiceInfo.totalInvoiceAmount = totalInvoiceAmount;
invoiceInfo.totalUnits = totalUnits;
invoiceInfo.isCompleted = isCompleted;

return invoiceInfo;

}

}
4 changes: 2 additions & 2 deletions next-app/blockend/interact.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import web3 from "./web3";
// The MetaMask plugin also allows signing transactions to
// send ether and pay to change state within the blockchain.
// For this, you need the account signer...
const usdcAddress = '0xf9fA7f55a571B847e29Af95eE2FA4145ED1EF62B';
const usdcAddress = '0x0a4f24Bf4F510a15F3276B805CeeAdCD842D3A33';
export const usdcContract = new web3.eth.Contract(usdcAbi, usdcAddress);


Expand All @@ -49,7 +49,7 @@ export const usdcContract = new web3.eth.Contract(usdcAbi, usdcAddress);
// const signer = provider.getSigner();
// Create an instance of the factory contract

const factoryAddress = '0x357F8Ffc31631664641136B33DDe937cAc319faE';
const factoryAddress = '0x86F67fda98f438f6e50c5D4fecF4BA6b93000c20';
export const factoryContract = new web3.eth.Contract(factoryAbi, factoryAddress);


Expand Down
1 change: 1 addition & 0 deletions next-app/chakra/button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export const Button: ComponentStyleConfig = {
borderColor: "gray.300",
_hover: {
bg: "gray.50",
color: "black",
},
},

Expand Down
2 changes: 1 addition & 1 deletion next-app/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ const handleRoute = (path: string) => (event: React.MouseEvent<HTMLButtonElement
</Show>


<Flex width={"30%"} mr={5} align={"center"} justify={"flex-end"}>
<Flex width={"40%"} mr={5} align={"center"} justify={"flex-end"}>
{/* {isConnected && <Profile/>} */}
{/* SIWE */}
<MintUSDC address={address}/>
Expand Down
4 changes: 2 additions & 2 deletions next-app/components/Homepage/Homepage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,8 @@ function Homepage({}: Props) {

})

} catch{

} catch(error){
console.log(error);
}

}
Expand Down
35 changes: 26 additions & 9 deletions next-app/components/Invest/InvoiceDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
useNumberInput,
HStack,
Center,
useToast,
} from "@chakra-ui/react"
import Link from 'next/link'
import {useRouter} from 'next/router';
Expand All @@ -25,6 +26,9 @@ function InvoiceDetails(props:any) {

const {address} = useAccount();

const toast = useToast()


const router = useRouter();
const { invoiceAddress } = router.query;

Expand Down Expand Up @@ -52,11 +56,24 @@ function InvoiceDetails(props:any) {
let units = parseInt(input?.value);

try {
// const res = await usdcContract.methods.approve(invoiceAddress, units * amountPerUnit )).send({from: address});
usdcContract.methods.approve(invoiceAddress, units * amountPerUnit )
.send({from: address}).then((res)=>{
invoiceContract.methods.purchaseInvoice(parseInt(input.value))
.send({from: address}).then((res)=>{
if(res){
toast({
title: 'Invoice Purchase',
description: "Invoice units purchased successfully.",
status: 'success',
duration: 9000,
isClosable: true,
})
}
})
})


await invoiceContract.methods.purchaseInvoice(parseInt(input.value))
.send({from: address});


const investorRes = await fetch(`/api/investors?investorAddress=${address}`);

Expand All @@ -81,9 +98,9 @@ function InvoiceDetails(props:any) {
body: JSON.stringify(updatedInvestor),
});

if (!updateRes.ok) {
throw new Error(`Error updating investor: ${updateRes.status}`);
}
// if (!updateRes.ok) {
// throw new Error(`Error updating investor: ${updateRes.status}`);
// }
} else {

const newInvestor = {
Expand All @@ -99,9 +116,9 @@ function InvoiceDetails(props:any) {
body: JSON.stringify(newInvestor),
});

if (!createRes.ok) {
throw new Error(`Error creating investor: ${createRes.status}`);
}
// if (!createRes.ok) {
// throw new Error(`Error creating investor: ${createRes.status}`);
// }

const newInvestorData = await createRes.json();
setInvestor(newInvestorData); // Assuming setInvestor updates state or context
Expand Down
Loading

0 comments on commit 83e1303

Please sign in to comment.