Skip to content

Commit

Permalink
feat: add support for dfns wallet api
Browse files Browse the repository at this point in the history
  • Loading branch information
Polybius93 committed Dec 4, 2024
1 parent 2a2c239 commit b31fca7
Show file tree
Hide file tree
Showing 30 changed files with 1,036 additions and 16 deletions.
10 changes: 10 additions & 0 deletions config.devnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@
"rippleIssuerAddress": "rLTBw1MEy45uE5qmkWseinbj7h4zmdQuR8",
"xrplWebsocket": "wss://s.altnet.rippletest.net:51233",
"ledgerApp": "Bitcoin Test",
"dfnsConfiguration": {
"dfnsBaseURL": "https://api.dfns.ninja",
"dfnsCustomerConfigurations": [
{
"name": "Tungsten",
"organizationID": "or-3pqgf-ugmhq-8969lp77c7f8uf81",
"applicationID": "ap-513sv-f5knb-811qggt4oh6hd5s9"
}
]
},
"merchants": [
{
"name": "Amber",
Expand Down
10 changes: 10 additions & 0 deletions config.mainnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@
"rippleIssuerAddress": "rGcyRGrZPaJAZbZDi4NqRFLA5GQH63iFpD",
"xrplWebsocket": "wss://xrpl.ws/",
"ledgerApp": "Bitcoin",
"dfnsConfiguration": {
"dfnsBaseURL": "https://api.dfns.ninja",
"dfnsCustomerConfigurations": [
{
"name": "Tungsten",
"organizationID": "or-3pqgf-ugmhq-8969lp77c7f8uf81",
"applicationID": "ap-513sv-f5knb-811qggt4oh6hd5s9"
}
]
},
"merchants": [
{
"name": "Amber",
Expand Down
10 changes: 10 additions & 0 deletions config.testnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@
"rippleIssuerAddress": "ra3oyRVfy4yD4NJPrVcewvDtisZ3FhkcYL",
"xrplWebsocket": "wss://testnet.xrpl-labs.com/",
"ledgerApp": "Bitcoin Test",
"dfnsConfiguration": {
"dfnsBaseURL": "https://api.dfns.ninja",
"dfnsCustomerConfigurations": [
{
"name": "Tungsten",
"organizationID": "or-3pqgf-ugmhq-8969lp77c7f8uf81",
"applicationID": "ap-513sv-f5knb-811qggt4oh6hd5s9"
}
]
},
"merchants": [
{
"name": "Amber",
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
"dependencies": {
"@chakra-ui/icons": "^2.1.1",
"@chakra-ui/react": "^2.8.2",
"@dfns/sdk": "^0.5.9",
"@dfns/sdk-browser": "^0.5.9",
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@fontsource-variable/onest": "^5.0.3",
Expand All @@ -46,7 +48,7 @@
"concurrently": "^8.2.2",
"d3": "^7.9.0",
"decimal.js": "^10.4.3",
"dlc-btc-lib": "2.4.18",
"dlc-btc-lib": "2.4.19-dfns-beta",
"dotenv": "^16.3.1",
"ethers": "5.7.2",
"formik": "^2.4.5",
Expand Down
1 change: 1 addition & 0 deletions public/images/logos/dfns-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions src/app/components/modals/components/modal-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { AnyAction } from '@reduxjs/toolkit';
import { RootState } from '@store/index';
import { modalActions } from '@store/slices/modal/modal.actions';

import { DFNSModal } from '../dfns-modal/dfns-modal';
import { LedgerModal } from '../ledger-modal/ledger-modal';
import { SelectBitcoinWalletModal } from '../select-bitcoin-wallet-modal/select-bitcoin-wallet-modal';
import { SuccessfulFlowModal } from '../successful-flow-modal/successful-flow-modal';
Expand All @@ -21,6 +22,7 @@ export function ModalContainer(): React.JSX.Element {
isSuccesfulFlowModalOpen,
isSelectBitcoinWalletModalOpen,
isLedgerModalOpen,
isDFNSModalOpen,
} = useSelector((state: RootState) => state.modal);

const handleClosingModal = (actionCreator: () => AnyAction) => {
Expand Down Expand Up @@ -60,6 +62,10 @@ export function ModalContainer(): React.JSX.Element {
isOpen={isLedgerModalOpen}
handleClose={() => handleClosingModal(modalActions.toggleLedgerModalVisibility)}
/>
<DFNSModal
isOpen={isDFNSModalOpen}
handleClose={() => handleClosingModal(modalActions.toggleDFNSModalVisibility)}
/>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Button, FormControl, FormErrorMessage, FormLabel, Input, VStack } from '@chakra-ui/react';
import { DFNSCustomerConfiguration } from '@models/configuration';
import { useForm } from '@tanstack/react-form';

interface DFNSModalRegisterFormProps {
onSubmit: (
credentialCode: string,
selectedDFNSOrganization: DFNSCustomerConfiguration
) => Promise<void>;
selectedDFNSOrganization: DFNSCustomerConfiguration;
}

export function DFNSModalRegisterForm({
onSubmit,
selectedDFNSOrganization,
}: DFNSModalRegisterFormProps): React.JSX.Element {
const formAPI = useForm({
defaultValues: {
credentialCode: '',
},
onSubmit: async ({ value }) => {
await onSubmit(value.credentialCode, selectedDFNSOrganization);
},
});

return (
<VStack w={'100%'}>
<form
onSubmit={e => {
e.preventDefault();
e.stopPropagation();
void formAPI.handleSubmit();
}}
>
<VStack spacing={4} w={'100%'}>
<formAPI.Field
name="credentialCode"
validators={{
onChange: ({ value }) => {
if (!value) return 'Credential Code is required';
if (value.length < 9) return 'Credential Code must be at least 9 characters';
return undefined;
},
}}
children={field => (
<FormControl isInvalid={!!field.state.meta.errorMap.onChange}>
<FormLabel color={'white.01'} fontSize={'md'}>
Credential Code
</FormLabel>
<Input
type="string"
value={field.state.value}
onChange={e => field.handleChange(e.target.value)}
placeholder="xxxxxxxxx"
/>
<FormErrorMessage>{field.state.meta.errorMap.onChange}</FormErrorMessage>
</FormControl>
)}
/>
<formAPI.Subscribe
selector={state => [state.canSubmit]}
children={([canSubmit]) => (
<Button variant={'dfns'} type="submit" isDisabled={!canSubmit}>
Submit
</Button>
)}
/>
</VStack>
</form>
</VStack>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { HStack, Text } from '@chakra-ui/react';

interface DFNSModalErrorBoxProps {
error: string | undefined;
}

export function DFNSModalErrorBox({ error }: DFNSModalErrorBoxProps): React.JSX.Element | false {
return (
!!error && (
<HStack
p={'5%'}
w={'375px'}
spacing={4}
border={'1px solid'}
borderRadius={'md'}
borderColor={'red'}
justifyContent={'center'}
>
<Text fontFamily={'Inter'} fontSize={'xs'} fontWeight={'600'}>
{error}
</Text>
</HStack>
)
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Button, FormControl, FormErrorMessage, FormLabel, Input, VStack } from '@chakra-ui/react';
import { DFNSCustomerConfiguration } from '@models/configuration';
import { useForm } from '@tanstack/react-form';

interface DFNSModalLoginFormProps {
onSubmit: (email: string, selectedDFNSOrganization: DFNSCustomerConfiguration) => Promise<void>;
selectedDFNSOrganization: DFNSCustomerConfiguration;
}

const validateEmail = (email: string) => {
if (!email) return 'Email address is required';

const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) return 'Please enter a valid email address';

const [localPart] = email.split('@');
if (localPart.length > 64) return 'Local part of email cannot exceed 64 characters';
if (email.length > 254) return 'Email address cannot exceed 254 characters';

return undefined;
};

export function DFNSModalLoginForm({
onSubmit,
selectedDFNSOrganization,
}: DFNSModalLoginFormProps): React.JSX.Element {
const formAPI = useForm({
defaultValues: {
email: '',
},
onSubmit: async ({ value }) => {
await onSubmit(value.email, selectedDFNSOrganization);
},
});

return (
<VStack w={'100%'}>
<form
onSubmit={e => {
e.preventDefault();
e.stopPropagation();
void formAPI.handleSubmit();
}}
>
<VStack spacing={4} w={'100%'}>
<formAPI.Field
name="email"
validators={{
onChange: ({ value }) => validateEmail(value),
}}
children={field => (
<FormControl isInvalid={!!field.state.meta.errorMap.onChange}>
<FormLabel color={'white.01'} fontSize={'md'}>
E-Mail Address
</FormLabel>
<Input
type="email"
value={field.state.value}
onChange={e => field.handleChange(e.target.value)}
placeholder="[email protected]"
/>
<FormErrorMessage>{field.state.meta.errorMap.onChange}</FormErrorMessage>
</FormControl>
)}
/>
<formAPI.Subscribe
selector={state => [state.canSubmit]}
children={([canSubmit]) => (
<Button variant={'dfns'} type="submit" isDisabled={!canSubmit}>
Submit
</Button>
)}
/>
</VStack>
</form>
</VStack>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { ReactNode } from 'react';

import {
Image,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalHeader,
ModalOverlay,
VStack,
} from '@chakra-ui/react';

interface DFNSModalLayoutProps {
logo: string;
isOpen: boolean;
onClose: () => void;
children: ReactNode;
}

export function DFNSModalLayout({
logo,
isOpen,
onClose,
children,
}: DFNSModalLayoutProps): React.JSX.Element {
return (
<Modal isOpen={isOpen} onClose={onClose} variant={'dfns'}>
<ModalOverlay />
<ModalContent>
<ModalHeader>
<Image src={logo} alt={'DFNS Logo'} boxSize={'100px'} objectFit={'contain'} />
</ModalHeader>
<ModalCloseButton />
<ModalBody>
<VStack minHeight={'425px'} spacing={'25px'}>
{children}
</VStack>
</ModalBody>
</ModalContent>
</Modal>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { HStack, Spinner, Text } from '@chakra-ui/react';

interface DFNSModalLoadingStackProps {
isLoading: [boolean, string];
}
export function DFNSModalLoadingStack({
isLoading,
}: DFNSModalLoadingStackProps): React.JSX.Element | false {
return (
isLoading[0] && (
<HStack py={'5%'} w={'375px'} spacing={4} bgColor={'#D6D7EB'} justifyContent={'center'}>
<Text fontFamily={'Inter'} fontSize={'sm'} fontWeight={'600'} color={'#170C33'}>
{isLoading[1]}
</Text>
<Spinner size={'sm'} color="#170C33" />
</HStack>
)
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Button, Text, VStack } from '@chakra-ui/react';

interface DFNSModalNavigatorButtonProps {
isRegister: boolean;
setIsRegister: (isRegister: boolean) => void;
isVisible: boolean;
}

export function DFNSModalNavigatorButton({
isRegister,
setIsRegister,
isVisible,
}: DFNSModalNavigatorButtonProps): React.JSX.Element | false {
if (!isVisible) return false;

return (
<VStack w={'100%'} spacing={'25px'}>
<Text fontSize="sm" color="gray.500">
{!isRegister
? 'New user? Click the button below to register with your DFNS credential code.'
: 'Existing user? Sign in with your e-mail address.'}
</Text>
<Button variant={'dfns'} onClick={() => setIsRegister(!isRegister)}>
{isRegister ? 'Login' : 'Register'}
</Button>
</VStack>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Button, HStack, Text } from '@chakra-ui/react';
import { truncateAddress } from 'dlc-btc-lib/utilities';

interface DFNSModalAddressButtonProps {
addressInformation: { address: string | undefined; walletID: string };
setWalletID: (walletID: string) => void;
}

export function DFNSModalAddressButton({
addressInformation,
setWalletID,
}: DFNSModalAddressButtonProps): React.JSX.Element {
const { address, walletID } = addressInformation;
return (
<Button variant={'bitcoinAddress'} w={'100%'} h={'15px'} onClick={() => setWalletID(walletID)}>
<HStack w={'375px'} justifyContent={'center'}>
<Text fontSize={'xs'} fontWeight={'bold'}>
{truncateAddress(address!)}
</Text>
</HStack>
</Button>
);
}
Loading

0 comments on commit b31fca7

Please sign in to comment.