Skip to content

Commit

Permalink
feat: show process status
Browse files Browse the repository at this point in the history
  • Loading branch information
eccentricexit committed May 8, 2021
1 parent 0e78c1d commit 17e8421
Show file tree
Hide file tree
Showing 9 changed files with 617 additions and 368 deletions.
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
REACT_APP_BACKEND_URL=http://localhost:3001/v1
REACT_APP_TARGET_WALLET=0xd84c2518d25c158777feef141354e8bbf3d73cdd
REACT_APP_ERC20_ADDRESS=0xe4140B67DD27834b8A685987CBD09a264d59A06F
REACT_APP_META_TX_PROXY_ADDRESS=0x1724C57Fcd8c583777e2E1ADb3902FFF1889e2eB
REACT_APP_ERC20_ADDRESS=0x4200000000000000000000000000000000000006
REACT_APP_META_TX_PROXY_ADDRESS=0xfB167C545F79Fb85ddcBae360BF93ebdE7C5CF89
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@
"@types/react-dom": "^16.9.8",
"@web3-react/core": "^6.1.9",
"@web3-react/injected-connector": "^6.0.7",
"bumbag": "^2.0.0",
"ethers": "5.0.24",
"ethers": "^5.1.4",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-scripts": "4.0.1",
"react-use-sync": "0.0.8",
"ui-neumorphism": "^1.1.3",
"web-vitals": "^0.2.4"
},
"eslintConfig": {
Expand Down
18 changes: 18 additions & 0 deletions src/abis/erc20.ovm.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
"internalType": "string",
"name": "_name",
"type": "string"
},
{
"internalType": "string",
"name": "_symbol",
"type": "string"
}
],
"stateMutability": "nonpayable",
Expand Down Expand Up @@ -188,6 +193,19 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "symbol",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalSupply",
Expand Down
218 changes: 162 additions & 56 deletions src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Input, Button, Stack, Card, Flex, Alert, Text } from 'bumbag';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { UnsupportedChainIdError } from '@web3-react/core';
import { Body1, Body2, Button, H1 } from 'ui-neumorphism';
import { ethers } from 'ethers';
import { useDebounce, useWallet } from './hooks';
import { useDebounce, useInterval, useWallet } from './hooks';
import erc20Abi from './abis/erc20.ovm.json';
import metaTxProxyAbi from './abis/metaTxProxy.ovm.json';
import { ERC20MetaTransaction } from './utils';
import { UnsupportedChainIdError } from '@web3-react/core';

interface SignRequest {
from: string;
Expand All @@ -15,12 +15,38 @@ interface SignRequest {
nonce: number;
expiry: number;
}
interface BrcodePreview {
id: string;
status: string;
name: string;
taxId: string;
bankCode: string;
branchCode: string;
accountNumber: string;
accountType: string;
allowChange: boolean;
amount: number;
reconciliationId: string;
description: string;
tokenAmountRequired: string;
tokenSymbol: string;
}

const paymentStatusToLabel: { [key: string]: string } = {
'0': 'Request received and awaits token tx submission.',
'1': 'Token transfer tx submitted, awaiting confirmation.',
'2': 'Request confirmed and is being processed.',
'3': 'These should be used for failed payments that do not warrant refund.',
'4': 'This is pending a refund.',
'5': 'This payment request failed and the client was refunded.',
'6': 'Waiting fiat payment.',
'7': 'Request processed.',
};

const App = (): JSX.Element => {
const {
account,
onConnectWallet,
active,
chainId,
library,
error: walletError,
Expand Down Expand Up @@ -54,47 +80,63 @@ const App = (): JSX.Element => {

const [brcode, setBrcode] = useState('');
const [brcodePreview, setBrcodePreview] = useState<
{ tokenAmountRequired: string } | undefined
BrcodePreview | undefined
>();
const onBrcodeReceived = useCallback((event) => {
setBrcode(event.target.value || '');
}, []);
const debouncedBrcode = useDebounce(brcode, 400);
useEffect(() => {
(async () => {
if (!debouncedBrcode) return;

setBrcodePreview(
await (
await fetch(
`${process.env.REACT_APP_BACKEND_URL}/amount-required?brcode=${debouncedBrcode}&tokenAddress=${process.env.REACT_APP_ERC20_ADDRESS}`
)
).json()
);
const response = await (
await fetch(
`${process.env.REACT_APP_BACKEND_URL}/amount-required?brcode=${debouncedBrcode}&tokenAddress=${process.env.REACT_APP_ERC20_ADDRESS}`
)
).json();

if (response.error) throw new Error(response.error);

setBrcodePreview(response);
})();
}, [debouncedBrcode]);

const [allowance, setAllowance] = useState<ethers.BigNumber | undefined>();
const [symbol, setSymbol] = useState<string | undefined>();
const [balance, setBalance] = useState<ethers.BigNumber | undefined>();
useEffect(() => {
(async () => {
if (!account || !erc20 || !metaTxProxy) return;

setAllowance(await erc20.allowances(account, metaTxProxy.address));
const [
allowanceReturned,
symbolReturned,
balanceReturned,
] = await Promise.all([
erc20.allowance(account, metaTxProxy.address),
erc20.symbol(),
erc20.balanceOf(account),
]);
setAllowance(allowanceReturned);
setSymbol(symbolReturned);
setBalance(balanceReturned);
})();
}, [account, erc20, metaTxProxy]);
const allowanceEnough = useMemo(() => {
if (!allowance || !brcodePreview) return false;
const { tokenAmountRequired } = brcodePreview;
if (!tokenAmountRequired) return;
return allowance.gte(
ethers.BigNumber.from(brcodePreview.tokenAmountRequired)
);
}, [allowance, brcodePreview]);
const increaseAllowance = useCallback(async () => {
if (!erc20 || !brcodePreview || !metaTxProxy) return;
erc20.approve(
const tx = await erc20.approve(
metaTxProxy.address,
'1000000000000000000000000000000000000000'
);
}, [brcodePreview, erc20, metaTxProxy]);
await tx.wait();
setAllowance(await erc20.allowance(account, metaTxProxy.address));
}, [account, brcodePreview, erc20, metaTxProxy]);

const generatePixInvoice = useCallback(async () => {
try {
Expand All @@ -112,6 +154,9 @@ const App = (): JSX.Element => {
}
}, []);

const [paymentRequestSent, setPaymentRequestSent] = useState<
boolean | undefined
>();
const onRequestPay = useCallback(async () => {
try {
if (
Expand Down Expand Up @@ -167,6 +212,7 @@ const App = (): JSX.Element => {
brcode: brcode,
}),
});
setPaymentRequestSent(true);
} catch (error) {
console.error(
`Failure!${error && error.message ? `\n\n${error.message}` : ''}`
Expand All @@ -183,45 +229,105 @@ const App = (): JSX.Element => {
signer,
]);

const [paymentState, setPaymentState] = useState();
const [polling, setPolling] = useState(true);
const { status: paymentStatusCode } = paymentState || { status: '' };
useInterval(
async () => {
if (paymentStatusCode === '7') {
setPolling(false);
return;
}
if (!brcodePreview || !paymentRequestSent) return;
setPaymentState(
await (
await fetch(
`${process.env.REACT_APP_BACKEND_URL}/payment-status?id=${brcodePreview?.id}`
)
).json()
);
},
polling ? 2000 : undefined
);

return (
<Flex alignX="center" flexDirection="column">
<Text>Cipay</Text>
{error && (
<Alert title="Error" type="danger">
{JSON.stringify(error)}
</Alert>
)}
<Card>
<Stack>
<Input
placeholder="Enter your the QR code here."
onChange={onBrcodeReceived}
value={brcode}
/>
<Flex alignX="right">
<Button onClick={generatePixInvoice}>Generate Pix Invoice</Button>
{allowance && brcodePreview && !allowanceEnough && (
<Button palette="primary" onClick={increaseAllowance}>
Unlock Token
</Button>
)}
{brcodePreview &&
(active ? (
library &&
account && (
<Button palette="primary" onClick={onRequestPay}>
Pay
</Button>
)
) : (
<Button palette="primary" onClick={onConnectWallet}>
Connect
</Button>
))}
</Flex>
</Stack>
</Card>
</Flex>
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: '32px 24px',
}}
>
<div
style={{
maxWidth: '500px',
minWidth: '400px',
}}
>
<H1 style={{ marginBottom: '24px' }}>Cipay</H1>
{!brcode && <Body1>Generate Invoice a test invoice</Body1>}
<Body2 style={{ wordBreak: 'break-all' }}>Invoice: {brcode}</Body2>
{brcodePreview && balance && (
<div style={{ margin: '24px 0' }}>
<Body2>Invoice Due: {brcodePreview?.amount} BRL</Body2>
<Body2>
You will be charged{' '}
{ethers.utils.formatUnits(brcodePreview?.tokenAmountRequired, 18)}{' '}
{symbol}
</Body2>
<Body2>
Current balance: {ethers.utils.formatUnits(balance, 18)}{' '}
</Body2>
</div>
)}
{paymentState && (
<div style={{ margin: '24px 0' }}>
<Body2>
Payment status: {paymentStatusToLabel[String(paymentStatusCode)]}
</Body2>
</div>
)}
<div style={{ marginTop: '24px' }}>
{!brcode && (
<Button
bgColor="var(--primary)"
color="#fff"
onClick={generatePixInvoice}
>
Generate Invoice
</Button>
)}
{brcode && !account && (
<Button
color="#fff"
bgColor="var(--primary)"
onClick={onConnectWallet}
>
Connect
</Button>
)}
{brcode && account && !allowanceEnough && brcodePreview && (
<Button
color="#fff"
bgColor="var(--primary)"
onClick={increaseAllowance}
>
Unlock tokens
</Button>
)}
{brcode && account && allowanceEnough && !paymentRequestSent && (
<Button
color="#fff"
bgColor="var(--primary)"
onClick={onRequestPay}
>
Pay
</Button>
)}
</div>
</div>
</div>
);
};

Expand Down
1 change: 1 addition & 0 deletions src/hooks/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { default as useEagerConnect } from './eager-connect';
export { default as useInactiveListener } from './inactive-listener';
export { default as useWallet } from './wallet';
export { default as useDebounce } from './debounce';
export { default as useInterval } from './interval';
22 changes: 22 additions & 0 deletions src/hooks/interval.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useRef, useEffect } from 'react';

function useInterval(callback: () => void, delay: number | undefined) {
const savedCallback = useRef(callback);

// Remember the latest callback if it changes.
useEffect(() => {
savedCallback.current = callback;
}, [callback]);

// Set up the interval.
useEffect(() => {
// Don't schedule if no delay is specified.
if (delay === undefined) return;

const id = setInterval(() => savedCallback.current(), delay);

return () => clearInterval(id);
}, [delay]);
}

export default useInterval;
Loading

0 comments on commit 17e8421

Please sign in to comment.