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

DAC-289 Transaction payment by the Customer #46

Merged
merged 28 commits into from
Feb 9, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
b5c37b0
DAC-289 Trial to initiate transaction to a test wallet using Lucid
amnambiar Jan 12, 2023
d1fc917
Merge branch 'master' of https://github.com/input-output-hk/dapps-cer…
amnambiar Jan 17, 2023
4c8a641
Merge branch 'HOTFIX-DAC-409' of https://github.com/input-output-hk/d…
amnambiar Jan 19, 2023
b9c8900
DAC-289 Completed a transaction interaction with browser wallet
amnambiar Jan 19, 2023
628d999
DAC-298 Added api GET /wallet-address; cleanup and corrections
amnambiar Jan 19, 2023
e678f0b
[Fix] click on logo to come back to working area is blank - fixed
amnambiar Jan 19, 2023
0a659ed
DAC-289 Wallet connection error handling; Success modal
amnambiar Jan 19, 2023
9da9c21
[FIX] Once connected to a wallet and not clicked Logout, reloads shou…
amnambiar Jan 20, 2023
7548122
DAC-289 Hide Get Ceritificate if certification already issued
amnambiar Jan 20, 2023
4d5b41a
[FIX] Loader within Connect Wallet modal
amnambiar Jan 20, 2023
93e0bed
DAC-289 To show error in readable format
amnambiar Jan 20, 2023
ac150f0
DAC-289 Cleanup
amnambiar Jan 20, 2023
7b8c1a5
DAC-289 Review comments
amnambiar Jan 24, 2023
7291687
Merge branch 'master' of https://github.com/input-output-hk/dapps-cer…
amnambiar Jan 26, 2023
5a17477
DAC-289 Fixed code error
amnambiar Jan 26, 2023
d1b145d
Merge branch 'master' of https://github.com/input-output-hk/dapps-cer…
amnambiar Jan 26, 2023
07ed422
Synced package-lock
amnambiar Jan 26, 2023
046933b
Imp- Updated package.json to render app locally
amnambiar Jan 26, 2023
3533c21
DAC-289 Fix to internal navigation break
amnambiar Jan 26, 2023
a944e33
DAC-289 Commented WIP code
amnambiar Jan 26, 2023
62c0be8
Cleanup of timeouts
amnambiar Jan 26, 2023
1f0e483
DAC-289 Minor fix
amnambiar Jan 27, 2023
cd3458b
Fixes - Connect Wallet displayed after entering dapp profile details …
amnambiar Jan 27, 2023
71cae84
[FIX] Coming back to Certification form gives empty UI after being na…
amnambiar Jan 30, 2023
c5e6f1d
DAC-289 cleanup
amnambiar Jan 31, 2023
21dad99
DAC-466 DAC-289 Fix to JS heap allocation failure errors
amnambiar Feb 3, 2023
2b2efa6
FIX to send bech_32 receive-address in Authorization instead of hex c…
amnambiar Feb 7, 2023
6bcf960
Merge branch 'master' of https://github.com/input-output-hk/dapps-cer…
amnambiar Feb 9, 2023
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
8,704 changes: 4,348 additions & 4,356 deletions react-web/package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions react-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"dependencies": {
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@emurgo/cardano-serialization-lib-asmjs": "^11.2.1",
"@hookform/resolvers": "^2.9.7",
"@mui/icons-material": "5.10.14",
"@mui/material": "5.10.14",
Expand All @@ -20,6 +21,7 @@
"@types/react-dom": "^18.0.6",
"@types/react-table": "^7.7.12",
"axios": "^0.27.2",
"buffer": "^6.0.3",
"dayjs": "^1.11.7",
"html-react-parser": "^3.0.4",
"node-sass": "^7.0.1",
Expand Down
5 changes: 5 additions & 0 deletions react-web/src/app/App.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,9 @@
header button {
float: right;
margin: 10px;
}

section > svg.spinner {
top: 100px;
left: 50%;
}
4 changes: 3 additions & 1 deletion react-web/src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ const PageLayout = () => {
<Header />
{/* Load page content here */}
<section data-testid="contentWrapper" id="contentWrapper">
<Outlet />
<Suspense fallback={<Loader />}>
<Outlet />
</Suspense>
</section>
</>
);
Expand Down
26 changes: 21 additions & 5 deletions react-web/src/components/ConnectWallet/ConnectWallet.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React, { useEffect, useState, useCallback } from "react";
import './ConnectWallet.scss';
import { useAppDispatch } from "store/store";
import { getProfileDetails } from "store/slices/auth.slice";

import Modal from "components/Modal/Modal";
import Button from "components/Button/Button";
import Loader from "components/Loader/Loader";

import { useAppDispatch } from "store/store";
import { getProfileDetails } from "store/slices/auth.slice";
import './ConnectWallet.scss';

const wallets: Array<string> = ['lace', 'nami', 'yoroi']

Expand All @@ -18,17 +20,21 @@ let CardanoNS = window.cardano;
const ConnectWallet = () => {
const dispatch = useAppDispatch();
const [wallet, setWallet] = useState(null)
const [walletName, setWalletName] = useState("")
const [address, setAddress] = useState(null)
const [isOpen, setIsOpen] = useState(false)
const [walletLoading, setWalletLoading] = useState(false)

const openConnectWalletModal = useCallback(() => setIsOpen(true),[])

const onCloseModal = useCallback(() => setIsOpen(false),[])

const loadWallet = async (walletName: string) => {
try {
setWalletLoading(true)
const enabledWallet = await CardanoNS[walletName].enable();
setWallet(enabledWallet)
setWalletName(walletName)
if (enabledWallet) {
setAddress(await enabledWallet.getChangeAddress())
}
Expand All @@ -40,9 +46,18 @@ const ConnectWallet = () => {

useEffect(() => {
if (address) {
dispatch(getProfileDetails({"address": address, "wallet": wallet}))
(async () => {
try {
const response: any = await dispatch(getProfileDetails({"address": address, "wallet": wallet, "walletName": walletName})).catch(handleError)
setWalletLoading(false)
} catch(error) {
setWalletLoading(false)
handleError(error)
bogdan-manole marked this conversation as resolved.
Show resolved Hide resolved
// dispatch(clearCache())
}
})()
}
}, [dispatch, address, wallet])
}, [dispatch, address, wallet, walletName])

return (
<>
Expand All @@ -69,6 +84,7 @@ const ConnectWallet = () => {
}
})
}
{ walletLoading ? <Loader /> : null}
</div>
</Modal>
</>
Expand Down
157 changes: 157 additions & 0 deletions react-web/src/components/CreateCertificate/CreateCertificate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { useState } from "react";
import { useAppSelector } from "store/store";

import Button from "components/Button/Button";
import Modal from "components/Modal/Modal";


import { Address,
Value,
BigNum,
LinearFee,
TransactionBuilderConfigBuilder,
TransactionUnspentOutputs,
TransactionUnspentOutput,
TransactionBuilder,
TransactionWitnessSet,
Transaction,
TransactionOutput,
CoinSelectionStrategyCIP2
} from '@emurgo/cardano-serialization-lib-asmjs';
import Toast from "components/Toast/Toast";
import { fetchData } from "api/api";

let Buffer = require("buffer/").Buffer;

const CreateCertificate = () => {
const { uuid } = useAppSelector((state) => state.certification);
const { address, wallet } = useAppSelector((state) => state.auth);
const [ certifying, setCertifying ] = useState(false);
const [ certified, setCertified ] = useState(false);
const [ transactionId, setTransactionId ] = useState("")
const [ showError, setShowError ] = useState("");
const [ openModal, setOpenModal ] = useState(false);
const [ disableCertify, setDisableCertify ] = useState(false);

const onCloseModal = () => { setOpenModal(false) }

const handleError = (errorObj: any) => {
let errorMsg = ''
if (typeof errorObj === 'string') {
errorMsg = errorObj + ' Please try again.'
} else if (errorObj?.info) {
errorMsg = errorObj.info + ' Please try again.'
} else if (errorObj?.response?.message) {
errorMsg = errorObj?.response.message + ' Please try again.'
} else if (errorObj?.response?.data) {
errorMsg = errorObj.response.statusText + ' - ' + errorObj.response.data
}
setShowError(errorMsg.length > 50 ? 'Something wrong occurred. Please try again later.' : errorMsg);
setTimeout(() => { setShowError("") }, 5000)
setCertifying(false);
if (errorObj?.response?.status === 403) {
setDisableCertify(true)
}
}

const triggerSubmitCertificate = async (txnId: string) => {
const response: any = await fetchData.post('/run/' + uuid + '/certificate' + '?transactionid=' + txnId).catch(handleError)
try {
console.log('broadcasted tnx data ', response.data);
setTransactionId(response.data.transactionId)
setOpenModal(true)
setCertifying(false)
setCertified(true)
} catch(e) { }
}

const triggerGetCertificate = async () => {
setCertifying(true);
setShowError("")
try {
// const walletAddressRes: any = await fetchData.get('/wallet-address').catch(handleError)
// const applicationWallet_receiveAddr = walletAddressRes.data;
/** For mock */
const applicationWallet_receiveAddr = 'addr_test1qz2rzeqq8n82gajfp35enq3mxhaynx6zhuql2c7yaljr25mfaznfszxu8275k6v7n05w5azzmxahfzdq564xuuyg73pqnqtrrc'
bogdan-manole marked this conversation as resolved.
Show resolved Hide resolved
// To be replaced with API
const cert_fee_ada = 3
const cert_fee_lovelace = BigNum.from_str((cert_fee_ada * 1000000).toString())

const protocolParams: any = {
linearFee: {
minFeeA: "440",
minFeeB: "175381",
},
minUtxo: "34482",
poolDeposit: "500000000",
keyDeposit: "2000000",
maxValSize: 5000,
maxTxSize: 16384,
priceMem: 0.0577,
priceStep: 0.0000721,
// minFeeCoefficient: 44,
// minFeeConstant: 155_381,
coinsPerUtxoByte: "4310"
}

let linearFee = LinearFee.new(
BigNum.from_str(protocolParams.linearFee.minFeeA),
BigNum.from_str(protocolParams.linearFee.minFeeB)
);
let txnBuilderConfigBuilder = TransactionBuilderConfigBuilder.new()
.fee_algo(linearFee)
.coins_per_utxo_byte(BigNum.from_str(protocolParams.coinsPerUtxoByte))
.key_deposit(BigNum.from_str(protocolParams.keyDeposit))
.pool_deposit(BigNum.from_str(protocolParams.poolDeposit))
.max_value_size(protocolParams.maxValSize)
.max_tx_size(protocolParams.maxTxSize)

let txBuilder = TransactionBuilder.new(txnBuilderConfigBuilder.build())

wallet.getUtxos().then((utxos: any) =>{
let txnUnspentOutputs = TransactionUnspentOutputs.new()
utxos.forEach((utxo: any) => {
txnUnspentOutputs.add(TransactionUnspentOutput.from_hex(utxo))
})
txBuilder.add_output(TransactionOutput.new(Address.from_bech32(applicationWallet_receiveAddr), Value.new(cert_fee_lovelace) ))
txBuilder.add_inputs_from(txnUnspentOutputs, CoinSelectionStrategyCIP2.LargestFirst)
txBuilder.add_change_if_needed(Address.from_hex(address))

const encodedTx = Buffer.from(txBuilder.build_tx().to_bytes()).toString("hex");
wallet.signTx(encodedTx).then((signed: string) =>{
const txVkeyWitnesses = TransactionWitnessSet.from_bytes(
Buffer.from(signed, "hex")
);
const txSigned = Transaction.new(txBuilder.build(), txVkeyWitnesses );
const encodedSignedTx = Buffer.from(txSigned.to_bytes()).toString("hex");
wallet.submitTx(encodedSignedTx).then((txnId: string) => {
console.log(' transaction id - ', txnId)
triggerSubmitCertificate(txnId)
}).catch(handleError)
}).catch(handleError)
}).catch(handleError)
} catch (e) {
handleError(e)
}
}

return (<>
{certified || disableCertify ? null : (<Button
displayStyle="gradient"
onClick={() => triggerGetCertificate()}
buttonLabel="Get Certificate"
showLoader={certifying}
/>)}
{transactionId ? (
<Modal open={openModal} title="Certification Successful" onCloseModal={onCloseModal}>
<span>
View your certification broadcasted on-chain&nbsp;
<a target="_blank" rel="noreferrer" href={`https://preprod.cardanoscan.io/transaction/${transactionId}`}>here</a>!
</span>
</Modal>
): null}
{showError ? <Toast message={showError} /> : null}
</>);
}

export default CreateCertificate;
41 changes: 37 additions & 4 deletions react-web/src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect, useState, memo, useCallback } from "react";
import { useNavigate, Link } from "react-router-dom";
import { useAppDispatch, useAppSelector } from "store/store";
import { logout } from "store/slices/auth.slice";
import { logout, getProfileDetails } from "store/slices/auth.slice";
import "./Header.scss";

import AvatarDropDown from "components/AvatarDropdown/AvatarDropdown";
Expand All @@ -15,6 +15,23 @@ const Header = () => {
const [pollForAddress, setPollForAddress] = useState(false);
const navigate = useNavigate();

useEffect(() => {
// check if address, walletName is in localStorage - login user without having to connect to wallet again
const addressCache = localStorage.getItem('address')
const walletNameCache = localStorage.getItem('walletName')
if (addressCache?.length && walletNameCache?.length) {
(async () => {
try {
const enabledWallet = await window.cardano[walletNameCache].enable()
dispatch(getProfileDetails({"address": addressCache, "wallet": enabledWallet, "walletName": walletNameCache}))
} catch(e) {
console.log(e)
}
})()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
bogdan-manole marked this conversation as resolved.
Show resolved Hide resolved

useEffect(() => {
if (isLoggedIn) {
navigate("/");
Expand Down Expand Up @@ -42,6 +59,22 @@ const Header = () => {
pollForAddress
);

const hasCachedAddress = () => {
return (!localStorage.getItem('address')?.length || !localStorage.getItem('walletName')?.length)
}

const ShowConnectWallet = memo(() => {
return (<>
{hasCachedAddress() ? <ConnectWallet /> : null}
</>)
})

const ShowAvatarDropdown = memo(() => {
return (<>
{(address && wallet) ? <AvatarDropDown /> : null}
</>)
})

const NoAuthMenu = memo(() => {
return (
<>
Expand All @@ -55,7 +88,7 @@ const Header = () => {
<Link to="support">Support</Link>
</li>
<li className="button-wrap">
<ConnectWallet />
<ShowConnectWallet />
</li>
</>
);
Expand All @@ -73,7 +106,7 @@ const Header = () => {
<Link to="history">Test History</Link>
</li>
<li>
<AvatarDropDown />
<ShowAvatarDropdown />
</li>
</>
);
Expand All @@ -90,7 +123,7 @@ const Header = () => {

return (
<header className="header">
<Link to="/">
<Link to="/" state={{insideNavigation: true}}>
<img
src="images/logo.png"
alt="IOHK logo"
Expand Down
Loading