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

Wallet Create account #129

Merged
merged 8 commits into from
Sep 21, 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
11 changes: 10 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
"source": "src/index.js",
"dependencies": {
"@concordium/react-components": "^0.3.0",
"@fingerprintjs/fingerprintjs": "^4.0.1",
"@fortawesome/free-solid-svg-icons": "^6.4.2",
"@fortawesome/react-fontawesome": "^0.2.0",
"@wagmi/core": "^1.3.8",
"@web3modal/ethereum": "^2.7.0",
"@web3modal/react": "^2.7.0",
Expand All @@ -17,13 +20,19 @@
"cross-env": "^7.0.3",
"dotenv": "^16.0.3",
"ethers": "^6.6.5",
"formik": "^2.4.4",
"friendly-challenge": "^0.9.13",
"lodash": "^4.17.21",
"query-string": "^8.1.0",
"react": "^18.2.0",
"react-bootstrap": "^2.8.0",
"react-device-detect": "^2.2.3",
"react-secure-storage": "^1.2.2",
"react-toastify": "^9.1.3",
"reactstrap": "^9.2.0",
"viem": "^1.0.0",
"wagmi": "^1.3.2"
"wagmi": "^1.3.2",
"yup": "^1.2.0"
},
"tsup": {
"entry": [
Expand Down
17 changes: 12 additions & 5 deletions src/SSOButton/Providers/Concordium/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,20 @@ import ConnectConcordium from './connect';
import SignMessageConcordium from './sign';
import secureLocalStorage from 'react-secure-storage';
import { getClientApp } from '../../../utils';

const SSOConcordiumProvider = () => {
interface WalletConnectionPropsExtends extends WalletConnectionProps {
setIsAccountExist: any;
}
const SSOConcordiumProvider = ({ setIsAccountExist }: any) => {
const { network } = getClientApp();

return (
<WithWalletConnector network={network === 'testnet' ? TESTNET : MAINNET}>
{(props) => <ConcordiumApp {...props} />}
{(props) => <ConcordiumApp {...props} setIsAccountExist={setIsAccountExist} />}
</WithWalletConnector>
);
};

const ConcordiumApp = (props: WalletConnectionProps) => {
const ConcordiumApp = (props: WalletConnectionPropsExtends) => {
const {
activeConnectorType,
activeConnector,
Expand All @@ -35,6 +37,7 @@ const ConcordiumApp = (props: WalletConnectionProps) => {
connectedAccounts,
genesisHashes,
setActiveConnectorType,
setIsAccountExist,
} = props;

const { connection, setConnection, account, genesisHash } = useConnection(
Expand Down Expand Up @@ -117,7 +120,11 @@ const ConcordiumApp = (props: WalletConnectionProps) => {
) : (
<>
{rpcGenesisHash ? (
<SignMessageConcordium account={account} connection={connection} />
<SignMessageConcordium
account={account}
connection={connection}
setIsAccountExist={setIsAccountExist}
/>
) : (
<button className="btn btn-dark">
<span
Expand Down
115 changes: 105 additions & 10 deletions src/SSOButton/Providers/Concordium/sign.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,29 @@ import React, { useContext, useState } from 'react';
import useWallet from '../../../Hooks/useWallet';

import { toast } from 'react-toastify';
import { shortenString } from '../../../utils';
import { stringMessage } from '@concordium/react-components';
import { getChallenge, getStatement, shortenString, verifyProof } from '../../../utils';
import { SSOModalContext } from '../../modal';
import concordium_logo from '../../images/concordium_logo.png';
import { detectConcordiumProvider } from '@concordium/browser-wallet-api-helpers';
import { CloseButton, Modal, ModalBody } from 'react-bootstrap';
import CreateAccount from '../CreateAccount';
import { stringMessage } from '@concordium/react-components';

const SignMessageConcordium = ({ account, connection }: any) => {
const SignMessageConcordium = ({ account, connection, setIsAccountExist }: any) => {
const [status, setStatus] = useState('');
const [isExist, setIsExist] = useState(true);
const [proof, setProof] = useState(false);
const [loading, setLoading] = useState(false);
const [show, setShow] = useState(false);

const wallet = 'concordium';
const { handleOnData } = useContext(SSOModalContext);
const { noCreateAccount, handleOnData } = useContext(SSOModalContext);

const { getWalletNonce, verifySignature } = useWallet(wallet, account);

console.log('noCreateAccountSignMessageConcordium', noCreateAccount);
const handleConnect = async () => {
setStatus('connect');
const nonce = await getWalletNonce();

if (nonce) {
try {
setStatus('sign');
Expand All @@ -27,20 +34,65 @@ const SignMessageConcordium = ({ account, connection }: any) => {

if (signature) {
const data = await verifySignature(wallet, account, convertedSignature);
handleOnData(data);
handleOnData({ ...data, loginType: 'concordium' });
}
} catch (error) {
toast(error.message);
}
} else {
if (!noCreateAccount) {
setIsExist(false);
setIsAccountExist({ status: false, type: 'concordium' });
}
}
setStatus('');
};

const handleCreate = async () => {
try {
if (!proof) {
const responseProof = await handleProof();
responseProof && setShow(true);
} else {
setShow(true);
}
} catch (error) {
console.log(error);
toast.error(error);
}
};

const handleProof = async () => {
setLoading(true);
try {
const challenge = await getChallenge(account ?? '');
const statement = await getStatement();
const provider = await detectConcordiumProvider();
const proof = await provider.requestIdProof(account ?? '', statement, challenge);
console.log('proof', proof);
const re = await verifyProof(challenge, proof);
console.log('verifyProof', re);

if (re) {
setProof(true);
setLoading(false);
}
return true;
} catch (error) {
setProof(false);
setLoading(false);
return false;
}
};

return (
<>
<button
disabled={status !== ''}
className="btn btn-dark btn-concordium w-100 fw-medium py-2 px-4 fs-18 lh-sm text-white d-flex align-items-center"
onClick={handleConnect}
disabled={status !== '' || loading}
className="btn btn-dark btn-concordium w-100 fw-medium py-2 px-4 fs-18 lh-sm text-white d-flex align-items-center text-start"
onClick={() => {
isExist ? handleConnect() : handleCreate();
}}
>
{status ? (
<div className="d-flex align-items-center">
Expand All @@ -55,13 +107,56 @@ const SignMessageConcordium = ({ account, connection }: any) => {
: `Please wait to connect...`}
</span>
</div>
) : !isExist ? (
<>
<img src={concordium_logo} className="me-3 align-text-bottom" alt="Concordium logo" />
{loading ? (
<>
<span
className="spinner-border spinner-border-sm me-1"
role="status"
aria-hidden="true"
></span>
<span className="ms-1">{!proof ? 'Authorizing' : 'Loading...'}</span>
</>
) : (
<>Create account via Concordium ({account && shortenString(account)})</>
)}
</>
) : (
<>
<img src={concordium_logo} className="me-3 align-text-bottom" alt="Concordium logo" />
Sign in via Concordium ({account && shortenString(account)})
</>
)}
</button>
{!noCreateAccount && (
<Modal
show={show}
centered
onHide={() => {
setShow(!show);
}}
size={'lg'}
className="aesirxsso aesirxsso-register"
>
<CloseButton
onClick={() => {
setShow(!show);
}}
/>
<ModalBody className="p-4 pt-5 bg-white rounded-3">
<CreateAccount
setShow={setShow}
setIsExist={setIsExist}
setIsAccountExist={setIsAccountExist}
accountAddress={account}
connection={connection}
wallet={'concordium'}
/>
</ModalBody>
</Modal>
)}
</>
);
};
Expand Down
94 changes: 94 additions & 0 deletions src/SSOButton/Providers/CreateAccount/CustomField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { faCircleExclamation } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import React from 'react';
import { Form } from 'react-bootstrap';
import { getClientApp } from '../../../utils';
const CustomField = ({ formik, field }: any) => {
const { registerForm } = getClientApp();
const fieldName =
field.fieldtype == 'email' ? `field${field.fieldId}_1_email` : `field${field.fieldId}_1`;
if (field.fieldtype === 'hidden') {
return null;
}
if (field.name == 'Order ID' || field.name === 'Message') {
return null;
}
if (field.fieldId?.toString() === registerForm?.product?.toString()) {
return null;
}
return (
<Form.Group className="mb-4 w-100">
<Form.Label className="text-primary mb-2">
{field.name}
{field.required == '1' && <span className="text-danger">*</span>}
</Form.Label>
<div className="position-relative">
{field.fieldId == registerForm.username && (
<div
className={`position-absolute w-40px h-40px top-50 ms-1 translate-middle-y bg-gray-stroke-2 rounded d-flex justify-content-center align-items-center`}
>
@
</div>
)}
{field.fieldtype === 'textarea' ? (
<Form.Control
as={field.fieldtype}
rows={3}
name={fieldName}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values[fieldName]}
className={`py-13px lh-sm ${
formik.touched[fieldName] && formik.errors[fieldName] ? 'border-danger' : ''
}`}
/>
) : field.fieldtype === 'select' ? (
<Form.Select
name={fieldName}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
className={`py-13px lh-sm ${
formik.touched[fieldName] && formik.errors[fieldName] ? 'border-danger' : ''
}`}
defaultValue={'default'}
>
<option disabled={true} value="default">
--Choose your product--
</option>
{field?.fieldOptions.map((item: any) => {
return (
<option id={item.id} key={item?.id} value={item?.value}>
{item?.label}{' '}
{item?.price
? !parseInt(item?.price)
? 'Free'
: `$${parseInt(item?.price)}`
: null}
</option>
);
})}
</Form.Select>
) : (
<Form.Control
type={field.fieldtype}
name={fieldName}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values[fieldName]}
className={`py-13px lh-sm ${
formik.touched[fieldName] && formik.errors[fieldName] ? 'border-danger' : ''
} ${field.fieldId == registerForm.username ? 'ps-5' : ''}`}
/>
)}
</div>
{formik.touched[fieldName] && formik.errors[fieldName] ? (
<p className="mt-2 mb-0 p-0 bg-white border-0 text-danger d-flex align-items-center">
<FontAwesomeIcon icon={faCircleExclamation} width={14} height={14} />
<span className="fs-7 fw-semibold ms-2 lh-1">{formik.errors[fieldName]}</span>
</p>
) : null}
</Form.Group>
);
};

export default CustomField;
36 changes: 36 additions & 0 deletions src/SSOButton/Providers/CreateAccount/FriendlyCaptcha.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React, { useEffect, useRef } from 'react';
import { WidgetInstance } from 'friendly-challenge';
import { toast } from 'react-toastify';

const FriendlyCaptcha = ({ setCaptcha }: any) => {
const captchaRef = useRef<any>(null);
const widget = useRef<any>();

useEffect(() => {
if (!widget.current && captchaRef.current) {
widget.current = new WidgetInstance(captchaRef.current, {
doneCallback: (solution) => {
setCaptcha(solution);
},
errorCallback: (err) => {
console.log(err);
toast.error('There was an error when trying to solve the Captcha.');
},
startMode: 'none',
});
}
return () => {
if (widget.current != undefined) widget.current.reset();
};
}, [captchaRef]);

return (
<div
ref={captchaRef}
className="frc-captcha"
data-sitekey={process.env.NEXT_PUBLIC_FRIENDLY_CAPTCHA}
/>
);
};

export default FriendlyCaptcha;
Loading