update sdk and app

This commit is contained in:
turnoffthiscomputer
2024-10-09 20:50:52 -07:00
parent 0c77f5a4d9
commit cbfe45cb1a
12 changed files with 191 additions and 224 deletions

View File

@@ -5,7 +5,7 @@ import { countryCodes, max_cert_bytes, } from '../../../common/src/constants/con
import { bgGreen, bgGreen2, greenColorLight, separatorColor, textBlack } from '../utils/colors';
import useUserStore from '../stores/userStore';
import useNavigationStore from '../stores/navigationStore';
import { AppType } from '../../../common/src/utils/appType';
import { DisclosureOptions, OpenPassportApp } from '../../../common/src/utils/appType';
import CustomButton from '../components/CustomButton';
import { formatProof, generateProof } from '../utils/prover';
import io, { Socket } from 'socket.io-client';
@@ -22,8 +22,8 @@ interface ProveScreenProps {
const ProveScreen: React.FC<ProveScreenProps> = ({ setSheetRegisterIsOpen }) => {
const [generatingProof, setGeneratingProof] = useState(false);
const selectedApp = useNavigationStore(state => state.selectedApp) as AppType;
const disclosureOptions = selectedApp.circuitMode === 'register' ? {} : (selectedApp as any).getDisclosureOptions();
const selectedApp = useNavigationStore(state => state.selectedApp) as OpenPassportApp;
const disclosureOptions = selectedApp.mode === 'register' ? {} : (selectedApp.args as any).disclosureOptions || {};
const {
toast,
setSelectedTab,
@@ -145,7 +145,7 @@ const ProveScreen: React.FC<ProveScreenProps> = ({ setSheetRegisterIsOpen }) =>
const inputs = generateCircuitInputsInApp(passportData, selectedApp);
if (selectedApp.circuitMode === 'register') {
if (selectedApp.mode === 'register') {
const { secret, dscSecret } = useUserStore.getState();
const cscaInputs = generateCircuitInputsDSC(dscSecret as string, passportData.dsc, max_cert_bytes);
@@ -206,50 +206,72 @@ const ProveScreen: React.FC<ProveScreenProps> = ({ setSheetRegisterIsOpen }) =>
}
};
// Refactored disclosureFieldsToText function
const disclosureFieldsToText = (key: keyof DisclosureOptions, option: any) => {
if (option.enabled) {
switch (key) {
case 'minimumAge':
return `I am older than ${option.value} years old.`;
case 'nationality':
return `I have a valid passport from ${option.value}.`;
case 'ofac':
return `My name is not present in the OFAC list.`;
case 'excludedCountries':
return option.value.length > 0
? `I am not part of the following countries: ${option.value
const disclosureFieldsToText = (key: string, value: string = "") => {
if (key === 'older_than') {
return `I am older than ${value} years old.`;
}
if (key === 'nationality') {
return `I have a valid passport from ${value}.`;
}
if (key === 'ofac') {
return `My name is not present in the ofac list.`;
}
if (key === 'forbidden_countries_list' && value && value.length > 0) {
return `I am not part of the following countries list: ${(value as any).map((country: string) => countryCodes[country as keyof typeof countryCodes]).join(', ')}.`;
.join(', ')}.`
: '';
default:
return '';
}
}
return '';
}
};
// Function to check if any disclosure option is enabled
const hasEnabledDisclosureOptions = Object.values(disclosureOptions).some(
(option: any) => option.enabled
);
return (
<YStack f={1} p="$3" pt="$8">
{Object.keys(disclosureOptions).length > 0 ? <YStack mt="$4">
{hasEnabledDisclosureOptions ? (
<YStack mt="$4">
<Text fontSize="$9">
<Text fow="bold" style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}>
{selectedApp.appName}
</Text>{' '}
is requesting you to prove the following information.
</Text>
<Text mt="$3" fontSize="$8" color={textBlack} style={{ opacity: 0.9 }}>
No{' '}
<Text style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}>
other
</Text>{' '}
information than the one selected below will be shared with {selectedApp.appName}.
</Text>
</YStack>
) : (
<Text fontSize="$9">
<Text fow="bold" style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}>{selectedApp.name}</Text> is requesting you to prove the following information.
<Text fow="bold" style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}>
{selectedApp.appName}
</Text>{' '}
is requesting you to prove you own a valid passport.
</Text>
<Text mt="$3" fontSize="$8" color={textBlack} style={{ opacity: 0.9 }}>
No <Text style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}>other</Text> information than the one selected below will be shared with {selectedApp.name}.
</Text>
</YStack> :
<Text fontSize="$9">
<Text fow="bold" style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}>{selectedApp.name}</Text> is requesting you to prove you own a valid passport.
</Text>
}
)}
<YStack mt="$6">
{Object.keys(disclosureOptions).map((key) => {
return (
<XStack key={key} gap="$3" mb="$3" ml="$3" >
{Object.entries(disclosureOptions).map(([key, option]: [string, any]) => {
const text = disclosureFieldsToText(key as keyof DisclosureOptions, option);
return text ? (
<XStack key={key} gap="$3" mb="$3" ml="$3">
<CheckCircle size={16} mt="$1.5" />
<Text fontSize="$7" color={textBlack} w="85%">
{disclosureFieldsToText(key, (disclosureOptions as any)[key])}
{text}
</Text>
</XStack>
);
) : null;
})}
</YStack>
@@ -288,5 +310,4 @@ const ProveScreen: React.FC<ProveScreenProps> = ({ setSheetRegisterIsOpen }) =>
);
};
export default ProveScreen;
export default ProveScreen;

View File

@@ -1,7 +1,7 @@
import { create } from 'zustand'
import { IsZkeyDownloading, ShowWarningModalProps } from '../utils/zkeyDownload';
import { useToastController } from '@tamagui/toast';
import { AppType } from '../../../common/src/utils/appType';
import { OpenPassportApp } from '../../../common/src/utils/appType';
interface NavigationState {
isZkeyDownloading: IsZkeyDownloading
@@ -10,8 +10,8 @@ interface NavigationState {
toast: ReturnType<typeof useToastController>
selectedTab: string
setSelectedTab: (tab: string) => void
selectedApp: AppType | null
setSelectedApp: (app: AppType | null) => void
selectedApp: OpenPassportApp | null
setSelectedApp: (app: OpenPassportApp | null) => void
showRegistrationErrorSheet: boolean
registrationErrorMessage: string
setToast: (toast: ReturnType<typeof useToastController>) => void;

View File

@@ -1,46 +1,44 @@
import { AppType } from '../../../common/src/utils/appType';
import { ArgumentsProveOffChain, DisclosureOptions, OpenPassportApp } from '../../../common/src/utils/appType';
import { PassportData } from '../../../common/src/utils/types';
import { generateCircuitInputsProve } from '../../../common/src/utils/generateInputs';
import { circuitToSelectorMode, DEFAULT_MAJORITY, k_dsc, k_dsc_ecdsa, n_dsc, n_dsc_ecdsa, PASSPORT_ATTESTATION_ID } from '../../../common/src/constants/constants';
import { circuitToSelectorMode, DEFAULT_MAJORITY } from '../../../common/src/constants/constants';
import { revealBitmapFromAttributes } from '../../../common/src/utils/revealBitmap';
import useUserStore from '../stores/userStore';
import { ArgumentsProve } from '../../../common/src/utils/appType'
import { parseDSC } from '../../../common/src/utils/certificates/handleCertificate';
import { generateCircuitInputsDSC } from '../../../common/src/utils/csca';
import namejson from '../../../common/ofacdata/outputs/nameSMT.json';
import { SMT } from '@ashpect/smt';
import { poseidon2 } from 'poseidon-lite';
export const generateCircuitInputsInApp = (
passportData: PassportData,
app: AppType
app: OpenPassportApp
): any => {
const disclosureOptions = (app.arguments as ArgumentsProve).disclosureOptions || {};
console.log('disclosureOptions', disclosureOptions);
switch (app.mode) {
case "prove_offchain":
const disclosureOptions: DisclosureOptions = (app.args as ArgumentsProveOffChain).disclosureOptions;
const { secret, dscSecret } = useUserStore.getState();
const selector_mode = circuitToSelectorMode[app.mode as keyof typeof circuitToSelectorMode];
const selector_dg1 = revealBitmapFromAttributes(disclosureOptions);
const selector_older_than = disclosureOptions.minimumAge.enabled ? 1 : 0;
const selector_ofac = disclosureOptions.ofac ? 1 : 0;
let smt = new SMT(poseidon2, true);
smt.import(namejson);
return generateCircuitInputsProve(
selector_mode,
secret,
dscSecret as string,
passportData,
app.scope,
selector_dg1,
selector_older_than,
disclosureOptions.minimumAge.value ?? DEFAULT_MAJORITY,
smt,
selector_ofac,
disclosureOptions.excludedCountries.value,
app.userId,
app.userIdType
);
}
const { secret, dscSecret } = useUserStore.getState();
const selector_mode = circuitToSelectorMode[app.circuitMode as keyof typeof circuitToSelectorMode];
console.log('selector_mode', selector_mode);
const selector_dg1 = revealBitmapFromAttributes(disclosureOptions as any).slice(0, -2) // have been moved to selector older_than
console.log('selector_dg1', selector_dg1);
let smt = new SMT(poseidon2, true);
smt.import(namejson);
console.log('(disclosureOptions as any).forbidden_countries', (disclosureOptions as any).forbidden_countries);
return generateCircuitInputsProve(
selector_mode,
secret,
dscSecret as string,
passportData,
app.scope,
selector_dg1,
1,
// (disclosureOptions as any).older_than ? 1 : 0,
disclosureOptions.older_than && disclosureOptions.older_than.length > 2 ? disclosureOptions.older_than : DEFAULT_MAJORITY,
smt,
// (disclosureOptions as any).ofac ? 1 : 0,
1,
(disclosureOptions as any).forbidden_countries_list ? (disclosureOptions as any).forbidden_countries_list : [],
app.userId,
app.userIdType
);
}

View File

@@ -1,9 +1,12 @@
import { NativeModules, Platform } from "react-native";
import { AppType, reconstructAppType } from "../../../common/src/utils/appType";
// import { AppType, reconstructAppType } from "../../../common/src/utils/appType";
import useNavigationStore from '../stores/navigationStore';
import { getCircuitName, parseDSC } from "../../../common/src/utils/certificates/handleCertificate";
import useUserStore from "../stores/userStore";
import { downloadZkey } from "./zkeyDownload";
import msgpack from "msgpack-lite";
import pako from "pako";
import { OpenPassportApp } from "../../../common/src/utils/appType";
export const scanQRCode = () => {
const { toast, setSelectedApp, setSelectedTab } = useNavigationStore.getState();
@@ -54,14 +57,17 @@ export const scanQRCode = () => {
const handleQRCodeScan = (result: string, toast: any, setSelectedApp: any, setSelectedTab: any) => {
try {
console.log(result);
const parsedJson = JSON.parse(result);
const app: AppType = reconstructAppType(parsedJson);
console.log(app);
const decodedResult = atob(result);
const uint8Array = new Uint8Array(decodedResult.split('').map(char => char.charCodeAt(0)));
const decompressedData = pako.inflate(uint8Array);
const unpackedData = msgpack.decode(decompressedData);
console.log(unpackedData);
const openPassportApp: OpenPassportApp = unpackedData;
setSelectedApp(openPassportApp);
const dsc = useUserStore.getState().passportData.dsc;
const sigAlgName = parseDSC(dsc!);
const circuitName = getCircuitName("prove", sigAlgName.signatureAlgorithm, sigAlgName.hashFunction);
downloadZkey(circuitName as any);
setSelectedApp(app);
// const sigAlgName = parseDSC(dsc!);
// const circuitName = getCircuitName("prove", sigAlgName.signatureAlgorithm, sigAlgName.hashFunction);
// downloadZkey(circuitName as any);
setSelectedTab("prove");
toast.show('✅', {
message: "QR code scanned",
@@ -69,6 +75,8 @@ const handleQRCodeScan = (result: string, toast: any, setSelectedApp: any, setSe
type: "success",
},
})
} catch (error) {
console.error('Error parsing QR code result:', error);
toast.show('Try again', {

View File

@@ -4,43 +4,6 @@ import { UserIdType } from "./utils";
export type CircuitName = "prove" | "disclose";
export type CircuitMode = "prove_onchain" | "register" | 'prove_offchain';
export type Mode = "prove_offchain" | "prove_onchain" | "register" | "disclose";
// export interface AppType {
// name: string,
// scope: string,
// userId: string,
// userIdType: UserIdType,
// websocketUrl: string,
// sessionId: string,
// mode: Mode,
// arguments: ArgumentsProve | ArgumentsRegister | ArgumentsDisclose
// }
// export interface ArgumentsProve {
// disclosureOptions: {
// older_than?: string,
// nationality?: string,
// ofac?: string,
// forbidden_countries_list?: string[]
// },
// }
// export interface ArgumentsRegister {
// merkleTreeUrl: string,
// modalServerUrl: string,
// }
// export interface ArgumentsDisclose {
// disclosureOptions: {
// older_than?: string,
// nationality?: string,
// ofac?: string,
// forbidden_countries_list?: string[]
// },
// merkle_root: string,
// merkletree_size: string,
// }
// OpenPassportAppType
export interface OpenPassportAppPartial {
@@ -48,6 +11,9 @@ export interface OpenPassportAppPartial {
appName: string;
scope: string;
websocketUrl: string;
sessionId: string;
userId: string;
userIdType: UserIdType;
}
export interface OpenPassportApp extends OpenPassportAppPartial {
@@ -81,7 +47,7 @@ export interface ArgumentsDisclose {
}
export interface DisclosureOptions {
minimumAge: { enabled: boolean; value: number }
minimumAge: { enabled: boolean; value: string }
nationality: { enabled: boolean; value: string }
excludedCountries: { enabled: boolean; value: string[] }
ofac: boolean

View File

@@ -150,7 +150,7 @@ export function generateCircuitInputsProve(
selector_older_than: string | number,
majority: string,
name_smt: SMT,
selector_ofac,
selector_ofac: string | number,
forbidden_countries_list: string[],
user_identifier: string,
user_identifier_type: 'uuid' | 'hex' | 'ascii' = DEFAULT_USER_ID_TYPE

View File

@@ -1,4 +1,5 @@
import { attributeToPosition } from "../constants/constants";
import { DisclosureOptions } from "./appType";
export function revealBitmapFromMapping(attributeToReveal: { [key: string]: string }): string[] {
const reveal_bitmap = Array(90).fill('0');
@@ -12,11 +13,11 @@ export function revealBitmapFromMapping(attributeToReveal: { [key: string]: stri
return reveal_bitmap;
}
export function revealBitmapFromAttributes(attributeToReveal: { [key: string]: boolean }): string[] {
const reveal_bitmap = Array(90).fill('0');
export function revealBitmapFromAttributes(disclosureOptions: DisclosureOptions): string[] {
const reveal_bitmap = Array(88).fill('0');
Object.entries(attributeToReveal).forEach(([attribute, reveal]) => {
if (attribute in attributeToPosition) {
Object.entries(disclosureOptions).forEach(([attribute, { enabled }]) => {
if (enabled && attribute in attributeToPosition) {
const [start, end] = attributeToPosition[attribute as keyof typeof attributeToPosition];
reveal_bitmap.fill('1', start, end + 1);
}

View File

@@ -5,10 +5,11 @@ import { UserIdType } from "../../common/src/utils/utils";
import * as pako from 'pako';
import msgpack from 'msgpack-lite';
import { OpenPassportAttestation } from "./index.web";
import { StringifyOptions } from "querystring";
export class OpenPassportVerifier {
private mode: Mode;
private scope: string;
private minimumAge: { enabled: boolean; value: number } = { enabled: false, value: 0 };
private minimumAge: { enabled: boolean; value: string } = { enabled: false, value: '18' };
private nationality: { enabled: boolean; value: typeof countryNames[number] } = { enabled: false, value: '' as typeof countryNames[number] };
private excludedCountries: { enabled: boolean; value: typeof countryNames[number][] } = { enabled: false, value: [] };
private ofac: boolean = false;
@@ -24,7 +25,13 @@ export class OpenPassportVerifier {
// Disclose
setMinimumAge(age: number): this {
this.minimumAge = { enabled: true, value: age };
if (age < 10) {
throw new Error("Minimum age must be at least 10 years old");
}
if (age > 100) {
throw new Error("Minimum age must be at most 100 years old");
}
this.minimumAge = { enabled: true, value: age.toString() };
return this;
}
@@ -66,6 +73,9 @@ export class OpenPassportVerifier {
mode: this.mode,
scope: this.scope,
websocketUrl: websocketUrl,
sessionId: sessionId,
userId: userId,
userIdType: userIdType,
};
let openPassportArguments: ArgumentsProveOffChain | ArgumentsRegisterOnChain;

View File

@@ -2,7 +2,6 @@ import React, { useEffect, useState } from 'react';
import {
OpenPassportAttestation,
OpenPassportVerifier,
OpenPassportVerifierReport,
} from '../index.web';
import { BounceLoader } from 'react-spinners';
import Lottie from 'lottie-react';
@@ -10,12 +9,9 @@ import CHECK_ANIMATION from './animations/check_animation.json';
import X_ANIMATION from './animations/x_animation.json';
import LED from './components/LED';
import {
DEFAULT_USER_ID_TYPE,
MODAL_SERVER_ADDRESS,
WEBSOCKET_URL,
} from '../../../common/src/constants/constants';
import { UserIdType } from '../../../common/src/utils/utils';
import { CircuitName, Mode } from '../../../common/src/utils/appType';
import { v4 as uuidv4 } from 'uuid';
import { QRcodeSteps } from './utils/utils';
import { containerStyle, ledContainerStyle, qrContainerStyle } from './utils/styles';
@@ -26,13 +22,16 @@ const QRCodeSVG = dynamic(() => import('qrcode.react').then((mod) => mod.QRCodeS
});
interface OpenPassportQRcodeProps {
appName: string;
userId: string;
userIdType: UserIdType;
openPassportVerifier: OpenPassportVerifier;
onSuccess: (proof: OpenPassportAttestation, report: OpenPassportVerifierReport) => void;
onSuccess: (attestation: OpenPassportAttestation) => void;
websocketUrl?: string;
size?: number;
}
const OpenPassportQRcode: React.FC<OpenPassportQRcodeProps> = ({ openPassportVerifier, onSuccess, websocketUrl = WEBSOCKET_URL, size = 300 }) => {
const OpenPassportQRcode: React.FC<OpenPassportQRcodeProps> = ({ appName, userId, userIdType, openPassportVerifier, onSuccess, websocketUrl = WEBSOCKET_URL, size = 300 }) => {
const [proofStep, setProofStep] = useState(QRcodeSteps.WAITING_FOR_MOBILE);
const [proofVerified, setProofVerified] = useState(null);
const [sessionId, setSessionId] = useState(uuidv4());
@@ -84,7 +83,7 @@ const OpenPassportQRcode: React.FC<OpenPassportQRcodeProps> = ({ openPassportVer
);
}
default:
return <QRCodeSVG value={openPassportVerifier.getIntent("Mock App", "mockUid", "uuid", sessionId)} size={size} />;
return <QRCodeSVG value={openPassportVerifier.getIntent(appName, userId, userIdType, sessionId)} size={size} />;
}
})()}
</div>

View File

@@ -19,55 +19,55 @@ const handleWebSocketMessage =
openPassportVerifier: OpenPassportVerifier,
onSuccess: (proof: OpenPassportAttestation, report: OpenPassportVerifierReport) => void
) =>
async (data) => {
console.log('received mobile status:', data.status);
switch (data.status) {
case 'mobile_connected':
setProofStep(QRcodeSteps.MOBILE_CONNECTED);
break;
case 'mobile_disconnected':
setProofStep(QRcodeSteps.WAITING_FOR_MOBILE);
break;
case 'proof_generation_started':
setProofStep(QRcodeSteps.PROOF_GENERATION_STARTED);
break;
case 'proof_generated':
setProofStep(QRcodeSteps.PROOF_GENERATED);
break;
case 'proof_generation_failed':
setProofVerified(false);
setProofStep(QRcodeSteps.PROOF_VERIFIED);
console.log('Proof generation failed');
break;
}
async (data) => {
console.log('received mobile status:', data.status);
switch (data.status) {
case 'mobile_connected':
setProofStep(QRcodeSteps.MOBILE_CONNECTED);
break;
case 'mobile_disconnected':
setProofStep(QRcodeSteps.WAITING_FOR_MOBILE);
break;
case 'proof_generation_started':
setProofStep(QRcodeSteps.PROOF_GENERATION_STARTED);
break;
case 'proof_generated':
setProofStep(QRcodeSteps.PROOF_GENERATED);
break;
case 'proof_generation_failed':
setProofVerified(false);
setProofStep(QRcodeSteps.PROOF_VERIFIED);
console.log('Proof generation failed');
break;
}
if (data.proof) {
console.log(data.proof);
try {
const local_proofVerified: OpenPassportVerifierReport = await openPassportVerifier.verify(
data.proof
);
setProofVerified(local_proofVerified.valid);
setProofStep(QRcodeSteps.PROOF_VERIFIED);
setTimeout(() => {
if (data.proof) {
console.log(data.proof);
try {
const local_proofVerified: OpenPassportVerifierReport = await openPassportVerifier.verify(
data.proof
);
setProofVerified(local_proofVerified.valid);
setProofStep(QRcodeSteps.PROOF_VERIFIED);
setTimeout(() => {
newSocket.emit('proof_verified', {
sessionId,
proofVerified: local_proofVerified.toString(),
});
if (local_proofVerified.valid) {
onSuccess(data.proof, local_proofVerified);
}
}, 1500); // wait for animation to finish before sending the proof to mobile
} catch (error) {
console.error('Error verifying proof:', error);
setProofVerified(false);
newSocket.emit('proof_verified', {
sessionId,
proofVerified: local_proofVerified.toString(),
proofVerified: { valid: false, error: error.message },
});
if (local_proofVerified.valid) {
onSuccess(data.proof, local_proofVerified);
}
}, 1500); // wait for animation to finish before sending the proof to mobile
} catch (error) {
console.error('Error verifying proof:', error);
setProofVerified(false);
newSocket.emit('proof_verified', {
sessionId,
proofVerified: { valid: false, error: error.message },
});
}
}
}
};
};
export function initWebSocket(
websocketUrl: string,
@@ -75,7 +75,7 @@ export function initWebSocket(
setProofStep: (step: number) => void,
setProofVerified: (proofVerified: boolean) => void,
openPassportVerifier: OpenPassportVerifier,
onSuccess: (proof: OpenPassportAttestation, report: OpenPassportVerifierReport) => void
onSuccess: (proof: OpenPassportAttestation) => void
) {
const socket = newSocket(websocketUrl, sessionId);
socket.on(

View File

@@ -26,4 +26,4 @@
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
}
}

View File

@@ -1,60 +1,24 @@
'use client';
import { OpenPassportQRcode } from '../../../../../src/QRcode/OpenPassportQRcode';
// import { OpenPassportQRcode } from '../../../../dist/bundle.web.js'
import { TextField } from '@mui/material';
import { useState } from 'react';
import { v4 as uuidv4 } from 'uuid';
import { COMMITMENT_TREE_TRACKER_URL } from '../../../../../../common/src/constants/constants';
import { OpenPassportVerifier } from '../../../../../src/OpenPassportVerifier';
export default function Prove() {
const [appName, setAppName] = useState('🌐 OpenPassport');
const [forbiddenCountriesList, setForbiddenCountriesList] = useState(["BMU", "BLZ", "PAN"]);
const [olderThan, setOlderThan] = useState("18");
const userId = uuidv4();
const scope = "scope"
const openPassportVerifier = new OpenPassportVerifier("prove_offchain", scope).setNationality("France").excludeCountries("Finland", "Norway").setMinimumAge(12);
return (
<div className="h-screen w-full bg-white flex flex-col items-center justify-center gap-4">
<div className="text-4xl text-black ">Prove circuit</div>
<OpenPassportQRcode
appName={appName}
scope="test"
appName="Mock App"
userId={userId}
olderThan={olderThan}
circuit="prove"
circuitMode="prove_offchain"
ofac="true"
forbidden_countries_list={forbiddenCountriesList}
devMode={true}
userIdType={"uuid"}
openPassportVerifier={openPassportVerifier}
onSuccess={(attestation) => {
// send the code to the backend server
}}
/>
<div className='h-12' />
<TextField
id="outlined-basic"
label="App Name"
variant="outlined"
value={appName}
onChange={(e) => { setAppName(e.target.value) }}
/>
<TextField
id="outlined-basic"
label="Older Than"
variant="outlined"
value={olderThan}
onChange={(e) => setOlderThan(e.target.value)}
/>
<TextField
id="outlined-basic"
label="Forbidden Countries List"
variant="outlined"
value={forbiddenCountriesList.join(',')}
onChange={(e) => setForbiddenCountriesList(e.target.value.split(','))}
/>
<TextField
id="outlined-basic"
label="Ofac"
variant="outlined"
value={"true"}
/>
</div>
);
}