Skip to content

Commit

Permalink
Add wallet connect (#253)
Browse files Browse the repository at this point in the history
* init commit

* fix for freelancer

* hire when the escrow balance is equal of greater of the total cost

* hire when the escrow balance is equal of greater of the total cost

* click deposit button

* add deposit success screen

* make multichain service a singleton

* make multichain service a singleton

* yarn lint fixes

* use npm run build

* install git

* use yarn build

* remove unused function

* dont open wallet unless needed

* disable continue button until the deposit is complete

* disable continue button until transfer completes

* fix bug on deposit

* rename balance to escrow balance
  • Loading branch information
samelamin authored Nov 7, 2023
1 parent 22bbb0f commit 071ed24
Show file tree
Hide file tree
Showing 21 changed files with 2,412 additions and 563 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ ENV IMAGE_TAG=$COMMIT_SHA
WORKDIR /

RUN apt-get update
RUN apt-get install -y make
RUN apt-get install -y make git

COPY . .

Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"@types/react-slick": "^0.23.10",
"@typescript-eslint/eslint-plugin": "^5.59.11",
"@typescript-eslint/parser": "^5.59.11",
"@walletconnect/web3-provider": "^1.8.0",
"apexcharts": "^3.44.0",
"axios": "^1.5.1",
"bad-words": "^3.0.4",
Expand All @@ -53,7 +54,7 @@
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-unused-imports": "^2.0.0",
"ethers": "^6.8.0",
"ethers": "^6.8.1",
"express": "^4.18.2",
"formidable": "^3.5.1",
"formidable-serverless": "^1.1.1",
Expand Down Expand Up @@ -98,7 +99,8 @@
"swiper": "^11.0.3",
"test-utils": "^1.1.1",
"typescript": "5.0.2",
"uuidv4": "^6.2.12"
"uuidv4": "^6.2.12",
"web3modal": "^1.9.12"
},
"devDependencies": {
"@testing-library/react": "^14.0.0",
Expand Down
2 changes: 1 addition & 1 deletion src/components/Application/BriefOwnerHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ const BriefOwnerHeader = (props: BriefOwnerHeaderProps) => {
</div>

<div className='flex flex-col gap-1 mt-5 justify-center lg:items-end'>
<p className='text-content'>Balance</p>
<p className='text-content'>Escrow Balance</p>
{application?.currency_id !== Currency.IMBU ? (
<p className='text-sm text-imbue-purple'>
{loadingWallet === 'loading' && 'Loading Wallet...'}
Expand Down
125 changes: 101 additions & 24 deletions src/components/HirePopup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import Fade from '@mui/material/Fade';
import Modal from '@mui/material/Modal';
import { blake2AsHex } from '@polkadot/util-crypto';
import { WalletAccount } from '@talismn/connect-wallets';
import { ethers } from 'ethers'
import Image from 'next/image';
import { useRouter } from 'next/router';
import React, { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';

import { sendNotification } from '@/utils';
import { getBalance } from '@/utils/helper';
import { ERC_20_ABI,getBalance, getEVMContract } from '@/utils/helper';

import { Currency, OffchainProjectState } from '@/model';
import { changeBriefApplicationStatus } from '@/redux/services/briefService';
Expand Down Expand Up @@ -44,6 +45,8 @@ export const HirePopup = ({
const mobileView = useMediaQuery('(max-width:480px)');

const [success, setSuccess] = useState<boolean>(false);
const [depositSuccess, setDepositSuccess] = useState<boolean>(false);
const [depositCompleted, setDepositCompleted] = useState<boolean>(false);
const [error, setError] = useState<any>();
const [escrowAddress, setEscrowAddress] = useState<string>('');
const [escrowBalance, setEscrowBalance] = useState<number>(0);
Expand All @@ -69,6 +72,16 @@ export const HirePopup = ({
zIndex: 1,
};


const updateEscrowInfo = async () => {
const escrowAddress = await getOffchainEscrowAddress(application.id);
setEscrowAddress(escrowAddress);
const allBalances = await getOffchainEscrowBalance(application.id);
const currency = Currency[application.currency_id].toString().toLowerCase();
const escrowBalance = Number(allBalances[currency]) ?? 0;
setEscrowBalance(escrowBalance);
};

useEffect(() => {
const checkBalance = async () => {
setFreelancerImbueBalance('Checking Imbue Balance');
Expand All @@ -81,15 +94,6 @@ export const HirePopup = ({
setFreelancerImbueBalance(balance);
};


const updateEscrowInfo = async () => {
const escrowAddress = await getOffchainEscrowAddress(application.id);
setEscrowAddress(escrowAddress);
const allBalances = await getOffchainEscrowBalance(application.id);
const currency = Currency[application.currency_id].toString().toLowerCase();
const escrowBalance = Number(allBalances[currency]) ?? 0;
setEscrowBalance(escrowBalance)
};
updateEscrowInfo();
openHirePopup && checkBalance();
}, [freelancer.web3_address, application?.currency_id, user, openHirePopup]);
Expand Down Expand Up @@ -182,15 +186,15 @@ export const HirePopup = ({
<div className='lg:flex gap-1 lg:items-center rounded-2xl bg-imbue-coral px-3 py-1 text-sm text-white'>
<ErrorOutlineOutlinedIcon className='h-4 w-4 inline' />
<p className='inline'>
Freelance does not currently have the necessary deposit
Freelancer does not currently have the necessary deposit
balance (500 $IMBU) to start the work
</p>
</div>
) : (
<div className='lg:flex gap-1 lg:items-center rounded-2xl bg-primary px-3 py-1 text-sm text-black'>
<CheckCircleOutlineIcon className='h-4 w-4 inline' />
<p className='inline'>
Freelance currently has the necessary deposit balance (500
Freelancer currently has the necessary deposit balance (500
$IMBU) to start the work
</p>
</div>
Expand Down Expand Up @@ -272,7 +276,47 @@ export const HirePopup = ({
};

const SecondContent = () => {
if (application.currency < 100) {
const depositIntoEscrow = async () => {

switch (application.currency_id) {
case Currency.ETH: {
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const transferAmount = ethers.parseEther((totalCostWithoutFee - escrowBalance).toPrecision(5).toString());
const depositTx = await signer.sendTransaction({ to: escrowAddress, value: transferAmount });
setDepositSuccess(true);
await provider.waitForTransaction(depositTx.hash, 1, 150000);
setDepositCompleted(true);
await updateEscrowInfo();
break;
}
case Currency.USDT: {

console.log("***** escrow address is ");
console.log(escrowAddress);
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const contract = await getEVMContract(application.currency_id);
console.log("***** contract is ");
console.log(contract);
if (!contract) {
break;
}
const token = new ethers.Contract(contract.address, ERC_20_ABI, signer);
const transferAmount = ethers.parseUnits((totalCostWithoutFee - escrowBalance).toPrecision(5).toString(), contract.decimals);
const depositTx = await token
.transfer(escrowAddress, transferAmount);
setDepositSuccess(true);
await provider.waitForTransaction(depositTx.hash, 1, 150000);
setDepositCompleted(true);
await updateEscrowInfo();
break;
}
}

}

if (application.currency_id < 100) {
return (
<div className='flex flex-col justify-center items-center modal-container px-5 lg:px-0 lg:w-2/3 mx-auto my-auto text-content'>
<p className='text-center w-full text-lg lg:text-xl my-4 text-content-primary'>
Expand Down Expand Up @@ -336,17 +380,29 @@ export const HirePopup = ({
</span>
${Currency[application.currency_id]}
</p>
<button
onClick={() => {
setstage(2);
}}
disabled={escrowBalance == 0}
className='primary-btn in-dark w-button lg:w-1/3 lg:mx-16 disabled'
style={{ textAlign: 'center' }}
>
Hire
</button>
</div>
{escrowBalance < (totalCostWithoutFee) ? (
<button
onClick={() => depositIntoEscrow()}
className='primary-btn in-dark w-button lg:w-1/3 lg:mx-16 disabled'
style={{ textAlign: 'center' }}
>
Deposit Funds
</button>
) : (
<button
onClick={
() => {
setstage(2);
}
}
className='primary-btn in-dark w-button lg:w-1/3 lg:mx-16 disabled'
style={{ textAlign: 'center' }}
>
Hire
</button>
)
}
</div >
);
}

Expand Down Expand Up @@ -419,6 +475,27 @@ export const HirePopup = ({
</button>
</div>
</SuccessScreen>


<SuccessScreen
title={`Your transfer has been sent.
Please do not navigate away from this page until your transfer completes`}
open={depositSuccess}
setOpen={setDepositSuccess}
>
<div className='flex flex-col gap-4 w-1/2'>
<button
disabled={!depositCompleted}
onClick={() =>{setDepositSuccess(false); setstage(0)}}
className='primary-btn in-dark w-button w-full !m-0'
>


{depositCompleted ? "Continue" : "Waiting for deposit confirmation......." }
</button>
</div>
</SuccessScreen>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ export default function MilestoneApprovedNotifications(activity: any) {
{activity.data.title || 'Title'}
</p>
<p className='text-sm mt-3'>
Great news! The{' '}
Great news! a {' '}
<span className='text-lg text-imbue-purple underline'>milestone</span>{' '}
has been successfully completed for the{' '}
<span className='text-lg text-imbue-purple underline'>project</span> .
has been successfully approved
</p>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default function SubmitMilestoneNotification(activity: any) {
>
{activity.data.sender.display_name?.split(' ')[0] || 'someone'}
</span>{' '}
has submited the{' '}
has submitted a
<span className='text-lg text-imbue-purple'>milestone</span> for the{' '}
<span className='text-lg text-imbue-purple'>project</span>. Take a
moment to review and provide your feedback.
Expand Down
2 changes: 1 addition & 1 deletion src/components/PopupScreens/SwitchToFreelancer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const SwitchToFreelancer = () => {
setloading(true)
try {
const freelancer = await getFreelancerProfile(user?.username);
if (freelancer?.id) setIsFreelancer(true);
setIsFreelancer(freelancer !== undefined);
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
Expand Down
4 changes: 2 additions & 2 deletions src/components/Project/ExpandableMilestone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ const ExpandableDropDowns = (props: ExpandableDropDownsProps) => {
await sendNotification(
project.approvers,
'submit_Milestone.testing',
'A New Milestone has been made',
`A New Milestone has been submitted on project ${project.name}`,
`Milestone Submitted Successfully`,
Number(project.id),
milestone.milestone_index + 1
Expand Down Expand Up @@ -300,7 +300,7 @@ const ExpandableDropDowns = (props: ExpandableDropDownsProps) => {
}
setLoading(false);
setSuccess(true);
setSuccessTitle('Withdraw successfull');
setSuccessTitle('Withdraw successful');
} else if (result.txError) {
setLoading(false);
setError({ message: result.errorMessage });
Expand Down
4 changes: 2 additions & 2 deletions src/components/Project/V2/ExpandableMilestone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ const ExpandableMilestone = (props: ExpandableMilestonProps) => {

if (project.currency_id < 100) {
setSuccess(true);
setSuccessTitle('Withdraw successfull');
setSuccessTitle('Withdraw successful');
}


Expand All @@ -299,7 +299,7 @@ const ExpandableMilestone = (props: ExpandableMilestonProps) => {
setError({ message: withdrawResult.errorMessage });
} else {
setSuccess(true);
setSuccessTitle('Withdraw successfull');
setSuccessTitle('Withdraw successful');
}
setLoading(false);
}
Expand Down
6 changes: 4 additions & 2 deletions src/pages/api/grants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
User,
} from '@/lib/models';
import { updateProject } from '@/lib/models';
import { generateAddress } from '@/utils/multichain';
import { MultiChainService } from '@/utils/multichain';

import db from '@/db';

Expand Down Expand Up @@ -45,6 +45,8 @@ export default nextConnect()
const grant: Grant = req.body as Grant;
const filter = new Filter();
const userAuth: Partial<User> | any = await authenticate('jwt', req, res);
const multichain = await MultiChainService.build();

verifyUserIdFromJwt(req, res, [userAuth.id]);
await db.transaction(async (tx: any) => {
try {
Expand All @@ -62,7 +64,7 @@ export default nextConnect()

const grantId = await insertGrant(filterdGrants)(tx);
if (grant.currency_id >= 100) {
const offchainEscrowAddress = await generateAddress(Number(grantId), grant.currency_id);
const offchainEscrowAddress = await multichain.generateAddress(Number(grantId), grant.currency_id);
const grantAsProject = await fetchProjectById(Number(grantId))(tx);
if (grantAsProject) {
grantAsProject.escrow_address = offchainEscrowAddress;
Expand Down
6 changes: 3 additions & 3 deletions src/pages/api/payments/[id]/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import nextConnect from 'next-connect';
import passport from 'passport';

import { fetchProjectById } from '@/lib/models';
import { generateAddress } from '@/utils/multichain';
import { MultiChainService } from '@/utils/multichain';

import db from '@/db';

Expand All @@ -20,15 +20,15 @@ export default nextConnect()
await db.transaction(async (tx: any) => {
try {
const project = await fetchProjectById(Number(projectId))(tx);

const multichain = await MultiChainService.build();
if (!project) {
return res.status(404).end();
}

if (project.currency_id < 100) {
address = project.escrow_address;
} else {
address = await generateAddress(projectId, project.currency_id);
address = await multichain.generateAddress(projectId, project.currency_id);
}

} catch (e) {
Expand Down
5 changes: 3 additions & 2 deletions src/pages/api/payments/[id]/balance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import nextConnect from 'next-connect';
import passport from 'passport';

import { getBalance } from '@/utils/multichain';
import { MultiChainService } from '@/utils/multichain';


export default nextConnect()
.use(passport.initialize())
.get(async (req: NextApiRequest, res: NextApiResponse) => {
const { id } = req.query;
const projectId = Number(id);
const projectBalance = await getBalance(projectId);
const multichainService = await MultiChainService.build();
const projectBalance = await multichainService.getBalance(projectId);
if (projectBalance == undefined) {
return res.status(404).end();
}
Expand Down
5 changes: 3 additions & 2 deletions src/pages/api/payments/[id]/mint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import nextConnect from 'next-connect';
import passport from 'passport';

import { mintTokens } from '@/utils/multichain';
import { MultiChainService } from '@/utils/multichain';

import { BasicTxResponse } from '@/model';

Expand All @@ -17,8 +17,9 @@ export default nextConnect()
const { id } = req.query;
const projectId = Number(id);
const { beneficiary } = req.body;
const multichainService = await MultiChainService.build();
try {
const result: BasicTxResponse = await mintTokens(projectId, beneficiary);
const result: BasicTxResponse = await multichainService.mintTokens(projectId, beneficiary);
if (!result.txError) {
return res.status(200).json(result);
}
Expand Down
Loading

0 comments on commit 071ed24

Please sign in to comment.