diff --git a/.gitignore b/.gitignore index 89f70fad..758474b2 100644 --- a/.gitignore +++ b/.gitignore @@ -112,3 +112,21 @@ android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png +package.json +yarn.lock +android/app/src/main/assets/index.android.bundle +android/app/src/main/res/drawable-hdpi/node_modules_reactnavigation_drawer_src_views_assets_toggledrawericon.png +android/app/src/main/res/drawable-hdpi/node_modules_reactnavigation_elements_src_assets_backicon.png +android/app/src/main/res/drawable-mdpi/node_modules_reactnative_libraries_newappscreen_components_logo.png +android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_drawer_src_views_assets_toggledrawericon.png +android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_src_assets_backicon.png +android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_src_assets_backiconmask.png +android/app/src/main/res/drawable-mdpi/src_images_customicons_coinsicon.png +android/app/src/main/res/drawable-mdpi/src_images_customicons_recovericon.png +android/app/src/main/res/drawable-mdpi/src_images_customicons_revokeicon.png +android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_drawer_src_views_assets_toggledrawericon.png +android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_src_assets_backicon.png +android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_drawer_src_views_assets_toggledrawericon.png +android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_src_assets_backicon.png +android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_drawer_src_views_assets_toggledrawericon.png +android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_src_assets_backicon.png diff --git a/env/main.android.json b/env/main.android.json index 26469f8f..5b0581af 100644 --- a/env/main.android.json +++ b/env/main.android.json @@ -43,6 +43,7 @@ "SETTINGS_STORAGE_INTERNAL_KEY": "settings", "BLOCK_HEIGHT_STORAGE_INTERNAL_KEY": "blockHeight", "PERSONAL_DATA_STORAGE_INTERNAL_KEY": "personal", + "ATTESTATION_DATA_STORAGE_INTERNAL_KEY": "attestation", "SERVICE_STORAGE_INTERNAL_KEY": "serviceStoredData", "NOTIFICATIONS_STORAGE_INTERNAL_KEY": "notifications", "WIDGET_STORAGE_INTERNAL_KEY": "widgets", diff --git a/env/main.ios.json b/env/main.ios.json index 899b32af..030b2c83 100644 --- a/env/main.ios.json +++ b/env/main.ios.json @@ -43,6 +43,7 @@ "SETTINGS_STORAGE_INTERNAL_KEY": "settings", "BLOCK_HEIGHT_STORAGE_INTERNAL_KEY": "blockHeight", "PERSONAL_DATA_STORAGE_INTERNAL_KEY": "personal", + "ATTESTATION_DATA_STORAGE_INTERNAL_KEY": "attestation", "SERVICE_STORAGE_INTERNAL_KEY": "serviceStoredData", "NOTIFICATIONS_STORAGE_INTERNAL_KEY": "notifications", "WIDGET_STORAGE_INTERNAL_KEY": "widgets", diff --git a/index.js b/index.js index 5a4eb85b..41d1a4ca 100644 --- a/index.js +++ b/index.js @@ -16,9 +16,9 @@ const IGNORED_LOGS = [ LogBox.ignoreLogs(IGNORED_LOGS); +import './shims/crypto.js'; import App from './App'; import {name as appName} from './app.json'; -import './shims/crypto.js'; import './shims/stringify.js'; AppRegistry.registerComponent(appName, () => App); diff --git a/ios/assets/env/main.json b/ios/assets/env/main.json index 899b32af..030b2c83 100644 --- a/ios/assets/env/main.json +++ b/ios/assets/env/main.json @@ -43,6 +43,7 @@ "SETTINGS_STORAGE_INTERNAL_KEY": "settings", "BLOCK_HEIGHT_STORAGE_INTERNAL_KEY": "blockHeight", "PERSONAL_DATA_STORAGE_INTERNAL_KEY": "personal", + "ATTESTATION_DATA_STORAGE_INTERNAL_KEY": "attestation", "SERVICE_STORAGE_INTERNAL_KEY": "serviceStoredData", "NOTIFICATIONS_STORAGE_INTERNAL_KEY": "notifications", "WIDGET_STORAGE_INTERNAL_KEY": "widgets", diff --git a/metro.config.js b/metro.config.js index ba3e67d0..d654bbc4 100644 --- a/metro.config.js +++ b/metro.config.js @@ -19,6 +19,10 @@ module.exports = (async () => { resolver: { assetExts: assetExts.filter(ext => ext !== 'svg'), sourceExts: [...sourceExts, 'svg'], + extraNodeModules: { + stream: require.resolve('readable-stream'), + crypto: require.resolve('react-native-crypto'), + } }, }; })(); diff --git a/package.json b/package.json old mode 100644 new mode 100755 index 3a263e1d..73a74504 --- a/package.json +++ b/package.json @@ -117,7 +117,7 @@ "uuid": "8.0.0", "version_compare": "0.0.3", "verus-pay-utils": "git+https://github.com/michaeltout/verus-pay-utils.git", - "verus-typescript-primitives": "git+https://github.com/VerusCoin/verus-typescript-primitives.git", + "verus-typescript-primitives": "file:d:/dev/verus-typescript-primitives", "verus-zkedid-utils": "git+https://github.com/michaeltout/verus-zkedid-utils.git", "verusd-rpc-ts-client": "git+https://github.com/VerusCoin/verusd-rpc-ts-client.git", "verusid-ts-client": "git+https://github.com/VerusCoin/verusid-ts-client.git", @@ -220,8 +220,10 @@ "@sideway/formula": "3.0.1", "@babel/traverse": "7.23.2", "@babel/core": "7.21.3", + "@bitgo/utxo-lib": "git+https://github.com/VerusCoin/BitGoJS.git#7c754d4a5920198d9fe6827d3e23bd5cf431f264", "blake2b-wasm": "https://github.com/michaeltout/blake2b-wasm/", "@bitgo/blake2b-wasm": "https://github.com/michaeltout/blake2b-wasm/", + "verus-typescript-primitives": "file:d:/dev/verus-typescript-primitives", "browserify-sign": "4.2.2" }, "jest": { diff --git a/shim.js b/shim.js new file mode 100644 index 00000000..812d6b45 --- /dev/null +++ b/shim.js @@ -0,0 +1,26 @@ +if (typeof __dirname === 'undefined') global.__dirname = '/' +if (typeof __filename === 'undefined') global.__filename = '' +if (typeof process === 'undefined') { + global.process = require('process') +} else { + const bProcess = require('process') + for (var p in bProcess) { + if (!(p in process)) { + process[p] = bProcess[p] + } + } +} + +process.browser = false +if (typeof Buffer === 'undefined') global.Buffer = require('buffer').Buffer + +// global.location = global.location || { port: 80 } +const isDev = typeof __DEV__ === 'boolean' && __DEV__ +process.env['NODE_ENV'] = isDev ? 'development' : 'production' +if (typeof localStorage !== 'undefined') { + localStorage.debug = isDev ? '*' : '' +} + +// If using the crypto shim, uncomment the following line to ensure +// crypto is loaded first, so it can populate global.crypto +// require('crypto') diff --git a/src/actions/actions/account/dispatchers/account.js b/src/actions/actions/account/dispatchers/account.js index 22e2b6a1..16aac85e 100644 --- a/src/actions/actions/account/dispatchers/account.js +++ b/src/actions/actions/account/dispatchers/account.js @@ -8,6 +8,7 @@ import { clearChainLifecycle, } from "../../intervals/dispatchers/lifecycleManager"; import { initPersonalDataForUser } from "../../personal/dispatchers/personal"; +import { initAttestationDataForUser } from "../../attestations/dispatchers/attestations"; import { initServiceStoredDataForUser } from "../../services/dispatchers/services"; import { fetchUsers, validateLogin } from "../../UserData"; import { initSettings, saveGeneralSettings } from "../../WalletSettings"; @@ -100,6 +101,7 @@ export const initializeAccountData = async ( activateServiceLifecycle(); await initPersonalDataForUser(account.accountHash); + await initAttestationDataForUser(account.accountHash); store.dispatch(signIntoAuthenticatedAccount()); } else { throw new Error( diff --git a/src/actions/actions/attestations/creators/attestations.js b/src/actions/actions/attestations/creators/attestations.js new file mode 100644 index 00000000..bef5efda --- /dev/null +++ b/src/actions/actions/attestations/creators/attestations.js @@ -0,0 +1,10 @@ +import { SET_ATTESTATION_DATA } from "../../../../utils/constants/storeType" + +export const setAttestationData = ( + data = { + attestations_provisioned: null + } +) => ({ + type: SET_ATTESTATION_DATA, + data, +}); \ No newline at end of file diff --git a/src/actions/actions/attestations/dispatchers/attestations.js b/src/actions/actions/attestations/dispatchers/attestations.js new file mode 100644 index 00000000..23167a19 --- /dev/null +++ b/src/actions/actions/attestations/dispatchers/attestations.js @@ -0,0 +1,31 @@ +import store from "../../../../store" +import { deleteAttestationDataForUser, loadAttestationDataForUser, storeAttestationDataForUser } from "../../../../utils/nativeStore/attestationDataStorage" +import { requestPassword } from "../../../../utils/auth/authBox" +import { encryptkey } from "../../../../utils/seedCrypt" +import { setAttestationData } from "../creators/attestations" + +export const saveEncryptedAttestationDataForUser = async (encryptedData = {}, accountHash) => { + const attestationData = await storeAttestationDataForUser(encryptedData, accountHash) + store.dispatch(setAttestationData(encryptedData)) + return attestationData +} + +export const clearEncryptedAttestationDataForUser = async (accountHash) => { + const attestationData = await deleteAttestationDataForUser(accountHash) + store.dispatch(setAttestationData({})) + return attestationData +} + +export const modifyAttestationDataForUser = async (data = {}, dataType, accountHash) => { + let attestationData = {...(await loadAttestationDataForUser(accountHash))} + attestationData[dataType] = await encryptkey(await requestPassword(), JSON.stringify(data)) + await saveEncryptedAttestationDataForUser(attestationData, accountHash) + + return data +} + +export const initAttestationDataForUser = async (accountHash) => { + const attestationData = await loadAttestationDataForUser(accountHash) + store.dispatch(setAttestationData(attestationData)) + return attestationData +} \ No newline at end of file diff --git a/src/containers/DeepLink/DeepLink.js b/src/containers/DeepLink/DeepLink.js index d6f310c1..ea8f50bb 100644 --- a/src/containers/DeepLink/DeepLink.js +++ b/src/containers/DeepLink/DeepLink.js @@ -21,7 +21,12 @@ import { getCurrency, getIdentity } from '../../utils/api/channels/verusid/callC import { convertFqnToDisplayFormat } from '../../utils/fullyqualifiedname'; import { resetDeeplinkData } from '../../actions/actionCreators'; -const authorizedPermissions = [primitives.IDENTITY_VIEW.vdxfid,/*TODO: primitives.IDENTITY_AGREEMENT.vdxfid, primitives.ATTESTATION_READ_REQUEST.vdxfid */] +const authorizedPermissions = [primitives.IDENTITY_VIEW.vdxfid, + primitives.IDENTITY_AGREEMENT.vdxfid, + primitives.ATTESTATION_READ_REQUEST.vdxfid, + primitives.PROFILE_DATA_VIEW_REQUEST.vdxfid, + primitives.LOGIN_CONSENT_PERSONALINFO_WEBHOOK_VDXF_KEY.vdxfid, +] import { CoinDirectory } from '../../utils/CoinData/CoinDirectory'; import BigNumber from 'bignumber.js'; import { blocksToTime, satsToCoins } from '../../utils/math'; diff --git a/src/containers/DeepLink/LoginReceiveAttestation/LoginReceiveAttestation.js b/src/containers/DeepLink/LoginReceiveAttestation/LoginReceiveAttestation.js new file mode 100644 index 00000000..35fd6b91 --- /dev/null +++ b/src/containers/DeepLink/LoginReceiveAttestation/LoginReceiveAttestation.js @@ -0,0 +1,180 @@ +import { Component } from "react" +import { connect } from 'react-redux' +import { createAlert, resolveAlert } from "../../../actions/actions/alert/dispatchers/alert" +import { LoginReceiveAttestationRender } from "./LoginReceiveAttestation.render" +import { primitives } from "verusid-ts-client" +import { VDXFDataToUniValueArray } from "verus-typescript-primitives/dist/vdxf/classes/DataDescriptor.js"; +import * as VDXF_Data from "verus-typescript-primitives/dist/vdxf/vdxfDataKeys"; +import { SignatureData } from "verus-typescript-primitives/dist/vdxf/classes/SignatureData"; +import { verifyHash } from "../../../utils/api/channels/vrpc/requests/verifyHash"; +import { getSignatureInfo } from "../../../utils/api/channels/vrpc/requests/getSignatureInfo"; +const { ATTESTATION_NAME } = primitives; +import { IdentityVdxfidMap } from "verus-typescript-primitives/dist/vdxf/classes/IdentityData"; +import { ATTESTATIONS_PROVISIONED } from "../../../utils/constants/attestations"; +import { modifyAttestationDataForUser } from "../../../actions/actions/attestations/dispatchers/attestations"; + +class LoginReceiveAttestation extends Component { + constructor(props) { + super(props); + this.state = { + loginConsent: null, + loading: false, + ready: false, + personalDataURL: "", + signerFqn: this.props.route.params.signerFqn, + attestationName: "", + attestationData: {}, + completeAttestaton: {}, + }; + const dfg = 2111111 + } + + componentDidMount() { + this.updateDisplay(); + } + + cancel = () => { + if (this.props.route.params.cancel) { + this.props.route.params.cancel.cancel() + } + } + + validateAttestation = async (signatureData) => { + + const sigObject = SignatureData.fromJson(signatureData); + + const sigInfo = await getSignatureInfo( + sigObject.systemID, + sigObject.identityID, + sigObject.signatureAsVch.toString('base64'), + ); + + return await verifyHash(sigObject.systemID, sigObject.identityID, sigObject.signatureAsVch.toString('base64'), sigObject.getIdentityHash(sigInfo)); + + } + + getAttestationData = (dataDescriptors) => { + + const data = {}; + dataDescriptors.forEach((dataDescriptor) => { + const label = dataDescriptor.objectdata[Object.keys(dataDescriptor.objectdata)[0]].label; + let key = ""; + + if (label === ATTESTATION_NAME.vdxfid) { + key = `Attestation name` + } else { + key = IdentityVdxfidMap[label]?.name || label; + } + + const mime = dataDescriptor.objectdata[Object.keys(dataDescriptor.objectdata)[0]].mimetype || ""; + if (mime.startsWith("text/")) { + data[key] = { "message": dataDescriptor.objectdata[Object.keys(dataDescriptor.objectdata)[0]].objectdata.message }; + } else if (mime.startsWith("image/")) { + if (mime === "image/jpeg" || mime === "image/png") { + data[key] = { "image": `data:${mime};base64,${Buffer.from(dataDescriptor.objectdata[Object.keys(dataDescriptor.objectdata)[0]].objectdata, "hex").toString("base64")}` }; + } + } + }); + + return data; + + } + + updateDisplay() { + const { deeplinkData } = this.props.route.params + const loginConsent = new primitives.LoginConsentRequest(deeplinkData); + + if (loginConsent.challenge.attestations && loginConsent.challenge.attestations.length > 1) { + createAlert("Error", "Only one attestation is allowed to be received at a time."); + this.cancel(); + return; + } + + const checkAttestation = loginConsent.challenge.attestations[0]; + + if (checkAttestation.vdxfkey === primitives.ATTESTATION_PROVISION_OBJECT.vdxfid) { + + const dataDescriptorObject = VDXFDataToUniValueArray(Buffer.from(checkAttestation.data, "hex")); + + if (!Array.isArray(dataDescriptorObject)) { + createAlert("Error", "Invalid data descriptor object in Attestation."); + this.cancel(); + return; + } + + if (dataDescriptorObject.some((dataDescriptor) => Object.keys(dataDescriptor)[0] === VDXF_Data.DataURLKey().vdxfid)) { + + // TODO: Handle fetch data from URL + } + else if (dataDescriptorObject.some((dataDescriptor) => Object.keys(dataDescriptor)[0] === VDXF_Data.MMRDescriptorKey().vdxfid) && + dataDescriptorObject.some((dataDescriptor) => Object.keys(dataDescriptor)[0] === VDXF_Data.SignatureDataKey().vdxfid)) { + + const signatureData = dataDescriptorObject.find((dataDescriptor) => Object.keys(dataDescriptor)[0] === VDXF_Data.SignatureDataKey().vdxfid)[VDXF_Data.SignatureDataKey().vdxfid]; + const mmrData = dataDescriptorObject.find((dataDescriptor) => Object.keys(dataDescriptor)[0] === VDXF_Data.MMRDescriptorKey().vdxfid)[VDXF_Data.MMRDescriptorKey().vdxfid]; + + if (!this.validateAttestation(signatureData) || mmrData.mmrroot.objectdata != signatureData.signaturehash) { + createAlert("Error", "Invalid attestation signature."); + this.cancel(); + return; + } + + const attestationName = mmrData.datadescriptors.find((dataDescriptor) => dataDescriptor.objectdata[Object.keys(dataDescriptor.objectdata)[0]].label === ATTESTATION_NAME.vdxfid)?.objectdata; + + if (!attestationName || Object.values(attestationName)[0].label !== ATTESTATION_NAME.vdxfid) { + createAlert("Error", "Attestation has no name."); + this.cancel(); + return; + } else { + + this.setState({ attestationName: Object.values(attestationName)[0].objectdata.message }); + + } + + const containingData = this.getAttestationData(mmrData.datadescriptors); + this.setState({ attestationData: containingData, + completeAttestaton: {[loginConsent.getChallengeHash(1).toString('base64')]:{ name: Object.values(attestationName)[0].objectdata.message, + signer: this.state.signerFqn, + data: dataDescriptorObject}} + }); + + } else { + createAlert("Error", "Invalid attestation type."); + this.cancel(); + return; + } + + } + } + + handleContinue() { + this.setState( + { loading: true }, + async () => { + await modifyAttestationDataForUser( + this.state.completeAttestaton, + ATTESTATIONS_PROVISIONED, + this.props.activeAccount.accountHash + ); + + this.setState({ loading: false }); + this.props.route.params.onGoBack(true); + this.props.navigation.goBack(); + this.cancel(); + } + ); + }; + + + render() { + return LoginReceiveAttestationRender.call(this); + } +} + +const mapStateToProps = (state) => { + return { + activeAccount: state.authentication.activeAccount, + encryptedPersonalData: state.personal + } +}; + +export default connect(mapStateToProps)(LoginReceiveAttestation); \ No newline at end of file diff --git a/src/containers/DeepLink/LoginReceiveAttestation/LoginReceiveAttestation.render.js b/src/containers/DeepLink/LoginReceiveAttestation/LoginReceiveAttestation.render.js new file mode 100644 index 00000000..7c08dd2b --- /dev/null +++ b/src/containers/DeepLink/LoginReceiveAttestation/LoginReceiveAttestation.render.js @@ -0,0 +1,67 @@ +import React from "react"; +import { SafeAreaView, ScrollView, View, Image } from "react-native"; +import { Divider, List, Button, Text } from "react-native-paper"; +import Styles from "../../../styles"; +import Colors from '../../../globals/colors'; +import {convertFqnToDisplayFormat} from '../../../utils/fullyqualifiedname'; + +export const LoginReceiveAttestationRender = function (props) { + + return ( + + + + + + Agree to Receive the following attestation. + + + {`${convertFqnToDisplayFormat(this.state.attestationName)}`} + + + {`From: `}{`${this.state.signerFqn}`} + + {this.state.attestationData && Object.keys(this.state.attestationData).map(request => { + return ( + + this.state.attestationData[request]?.image ? : null} + /> + + + + ); + })} + + + + + + + + + + ); +}; diff --git a/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js b/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js index 1c808c4e..27671d7a 100644 --- a/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js +++ b/src/containers/DeepLink/LoginRequestInfo/LoginRequestInfo.js @@ -1,5 +1,5 @@ -import React, {useState, useEffect} from 'react'; -import {SafeAreaView, ScrollView, TouchableOpacity, View} from 'react-native'; +import React, { useState, useEffect } from 'react'; +import { SafeAreaView, ScrollView, TouchableOpacity, View } from 'react-native'; import Styles from '../../../styles/index'; import { primitives } from "verusid-ts-client" import { Button, Divider, List, Portal, Text } from 'react-native-paper'; @@ -29,6 +29,8 @@ const LoginRequestInfo = props => { const signedIn = useSelector(state => state.authentication.signedIn) const passthrough = useSelector((state) => state.deeplink.passthrough); const sendModalType = useSelector(state => state.sendModal.type) + const [permissions, setExtraPermissions] = useState(null); + const [ready, setReady] = useState(false); const dispatch = useDispatch() @@ -56,6 +58,8 @@ const LoginRequestInfo = props => { } else { if (passthrough?.fqnToAutoLink) { mainLoginMessage = `VerusID from ${signerFqn} now ready to link` + } else if (challenge.attestations && challenge.attestations.length > 0) { + mainLoginMessage = `Would you like to accept an attestation from ${signerFqn}?` } else { mainLoginMessage = `Would you like to request a VerusID from ${signerFqn}?` } @@ -77,7 +81,7 @@ const LoginRequestInfo = props => { loadFriendlyNames: async () => { try { const identityObj = await getVerusId(chain, iAddress); - + return getFriendlyNameMap(CoinDirectory.getBasicCoinObj(chain), identityObj); } catch (e) { return { @@ -122,6 +126,121 @@ const LoginRequestInfo = props => { } else setLoading(true) }, [sendModalType]); + const buildAlert = (request) => { + + const setPermission = () => { + const _permissions = permissions.map(permission => { + if (permission.vdxfkey === request.vdxfkey) { + return { ...permission, agreed: true }; + } + return permission; + }); + setExtraPermissions(_permissions); + } + + if (request.agreed) return; + + if (request.viewAttestation) { + if (!signedIn) return; + props.navigation.navigate("LoginShareAttestation", + { + deeplinkData, + fromService: false, + cancel: { cancel }, + onGoBack: (data) => data ? setPermission(data) : () => { }, + signerFqn + }); + return; + } + else if (request.attestationToAccept) { + if (!signedIn) return; + props.navigation.navigate("LoginReceiveAttestation", + { + deeplinkData, + fromService: false, + cancel: { cancel }, + onGoBack: (data) => data ? setPermission(data) : () => { }, + signerFqn + }); + return; + } + else if (request.openProfile) { + if (!signedIn) return; + props.navigation.navigate("PersonalSelectData", + { + deeplinkData, + fromService: false, + cancel: { cancel }, + onGoBack: (data) => data ? setPermission(data) : () => { }, + signerFqn + }); + return; + } + + return createAlert( + request.title, + request.data, + [ + { + text: 'DECLINE', + onPress: () => resolveAlert(false), + style: 'cancel', + }, + { + text: 'ACCEPT', onPress: () => { + setPermission(); + resolveAlert(true) + } + }, + ], + { cancelable: true }); + } + + useEffect(() => { + + if (req && req.challenge && req.challenge.requested_access) { + var loginTemp = []; + if (req.challenge.requested_access.length === 1 && req.challenge.requested_access.some(value => value.vdxfkey === primitives.IDENTITY_VIEW.vdxfid)) { + if (req.challenge.attestations && req.challenge.attestations.length > 0) { + loginTemp.push({ data: "Accept attestation", title: "Attestation Provisioning Request", attestationToAccept: true, agreed: false }); + } else { + setReady(true); + } + } else { + for (let i = 0; i < req.challenge.requested_access.length; i++) { + var tempdata = {}; + + if (req.challenge.requested_access[i].vdxfkey === primitives.IDENTITY_VIEW.vdxfid) { + continue; + } else if (req.challenge.requested_access[i].vdxfkey === primitives.IDENTITY_AGREEMENT.vdxfid) { + tempdata = { data: req.challenge.requested_access[i].toJson().data, title: "Agreement to accept" } + } else if (req.challenge.requested_access[i].vdxfkey === primitives.ATTESTATION_READ_REQUEST.vdxfid) { + tempdata = { data: "Agree to share attestation data", title: "Attestation View Request", viewAttestation: true } + } else if (req.challenge.requested_access[i].vdxfkey === primitives.PROFILE_DATA_VIEW_REQUEST.vdxfid) { + tempdata = { data: "Agree to share profile data", title: "Personal Data Input Request", openProfile: true } + } else if (req.challenge.requested_access[i].vdxfkey === primitives.LOGIN_CONSENT_PERSONALINFO_WEBHOOK_VDXF_KEY.vdxfid) { + continue; + } + loginTemp.push({ vdxfkey: req.challenge.requested_access[i].vdxfkey, ...tempdata, agreed: false }) + } + } + + if (loginTemp.length > 0) setExtraPermissions(loginTemp); + } + }, [req]); + + useEffect(() => { + if (permissions) { + for (let i = 0; i < permissions.length; i++) { + if (!permissions[i].agreed) + return; + } + setReady(true); + } + }, [permissions]); + + + const addRootSystem = async () => { setLoading(true) @@ -137,28 +256,28 @@ const LoginRequestInfo = props => { : activeAccount.keyDerivationVersion, ), ); - + const addCoinAction = await addCoin( fullCoinData, activeCoinList, activeAccount.id, fullCoinData.compatible_channels, ); - + if (addCoinAction) { dispatch(addCoinAction); - + const setUserCoinsAction = setUserCoins( activeCoinList, activeAccount.id, ); dispatch(setUserCoinsAction); - + refreshActiveChainLifecycles(setUserCoinsAction.payload.activeCoinsForUser); } else { createAlert("Error", "Error adding coin") } - } catch(e) { + } catch (e) { createAlert("Error", e.message) } @@ -175,7 +294,7 @@ const LoginRequestInfo = props => { onPress: () => resolveAlert(false), style: 'cancel', }, - {text: 'Yes', onPress: () => resolveAlert(true)}, + { text: 'Yes', onPress: () => resolveAlert(true) }, ], { cancelable: true, @@ -189,15 +308,20 @@ const LoginRequestInfo = props => { } } - const handleContinue = () => { + const handleContinue = async () => { if (signedIn) { + if (!ready) { + for (let i = 0; i < permissions.length; i++) { + const result = await buildAlert(permissions[i], i); + if (!result) return; + } + } const coinObj = CoinDirectory.findCoinObj(chain_id); if (!!coinObj.testnet != isTestnet) { createAlert( "Incorrect profile type", - `Please login to a ${ - coinObj.testnet ? 'testnet' : 'mainnet' - } profile to use this login request.`, ); + `Please login to a ${coinObj.testnet ? 'testnet' : 'mainnet' + } profile to use this login request.`,); return; } else if (!rootSystemAdded) { @@ -234,15 +358,13 @@ const LoginRequestInfo = props => { const data = { [SEND_MODAL_USER_ALLOWLIST]: allowList } - + openAuthenticateUserModal(data); } else { createAlert( "Cannot continue", - `No ${ - coinObj.testnet ? 'testnet' : 'mainnet' - } profiles found, cannot respond to ${ - coinObj.testnet ? 'testnet' : 'mainnet' + `No ${coinObj.testnet ? 'testnet' : 'mainnet' + } profiles found, cannot respond to ${coinObj.testnet ? 'testnet' : 'mainnet' } login request.`, ); } @@ -263,7 +385,7 @@ const LoginRequestInfo = props => { contentContainerStyle={Styles.focalCenter}> - + {mainLoginMessage} @@ -294,6 +416,23 @@ const LoginRequestInfo = props => { + {permissions && permissions.map((request, index) => { + return ( + buildAlert(request)}> + ( + + )} /> + + + ); + })} { }}> diff --git a/src/containers/DeepLink/LoginShareAttestation/LoginShareAttestation.js b/src/containers/DeepLink/LoginShareAttestation/LoginShareAttestation.js new file mode 100644 index 00000000..6511545a --- /dev/null +++ b/src/containers/DeepLink/LoginShareAttestation/LoginShareAttestation.js @@ -0,0 +1,207 @@ +import moment from "moment"; +import { Component } from "react" +import { connect } from 'react-redux' +import { createAlert, resolveAlert } from "../../../actions/actions/alert/dispatchers/alert" +import { modifyPersonalDataForUser } from "../../../actions/actionDispatchers"; +import { requestPersonalData } from "../../../utils/auth/authBox"; +import { PERSONAL_ATTRIBUTES, PERSONAL_CONTACT, PERSONAL_LOCATIONS, PERSONAL_PAYMENT_METHODS, PERSONAL_IMAGES } from "../../../utils/constants/personal"; +import { provideCustomBackButton } from "../../../utils/navigation/customBack"; +import { LoginShareAttestationRender } from "./LoginShareAttestation.render" +import { checkPersonalDataCatagories } from "../../../utils/personal/displayUtils"; +import { handleAttestationDataSend } from "../../../utils/deeplink/handlePersonalDataSend"; +import { primitives } from "verusid-ts-client" +import { ATTESTATIONS_PROVISIONED } from "../../../utils/constants/attestations"; +import { requestAttestationData } from "../../../utils/auth/authBox"; +import { IdentityVdxfidMap } from 'verus-typescript-primitives/dist/vdxf/classes/IdentityData'; +import { getIdentity } from '../../../utils/api/channels/verusid/callCreators'; +import { createAttestationResponse } from "../../../utils/attestations/createAttestationResponse"; + +const { IDENTITYDATA_CONTACT, IDENTITYDATA_PERSONAL_DETAILS, IDENTITYDATA_LOCATIONS, IDENTITYDATA_DOCUMENTS_AND_IMAGES, IDENTITYDATA_BANKING_INFORMATION } = primitives; +import * as VDXF_Data from "verus-typescript-primitives/dist/vdxf/vdxfDataKeys"; +import { DrawerContentScrollView } from "@react-navigation/drawer"; + +class LoginShareAttestation extends Component { + constructor(props) { + super(props); + this.state = { + attestationName: "", + attestationAcceptedAttestors: [], + attestationRequestedFields: [], + attestationRequestedVdxfKeys:[], + attestationID: "", + loading: false, + ready: false, + attestationDataURL: "", + signerFqn: this.props.route.params.signerFqn + }; + const sdf = 234111 + } + + componentDidMount() { + this.updateDisplay(); + } + + cancel = () => { + if (this.props.route.params.cancel) { + this.props.route.params.cancel.cancel() + } + } + + createAttestationReply = async () => { + + const reply = await createAttestationResponse(this.state.attestationID, this.state.attestationRequestedVdxfKeys) + return reply; + } + + + updateDisplay = async () => { + const { deeplinkData } = this.props.route.params + const loginConsent = new primitives.LoginConsentRequest(deeplinkData); + + const subjectKeys = {}; + let attestationName = ""; + let attestationAcceptedAttestors = []; + let attestationID = ""; + + const attestationDataURL = loginConsent.challenge.subject + .filter((permission) => permission.vdxfkey === primitives.LOGIN_CONSENT_PERSONALINFO_WEBHOOK_VDXF_KEY.vdxfid); + + for (let i = 0; i < loginConsent.challenge.subject.length; i++) { + + let permission = loginConsent.challenge.subject[i]; + + if (permission.vdxfkey == primitives.ATTESTATION_VIEW_REQUEST_NAME.vdxfid) { + attestationName = permission.data; + } else if (permission.vdxfkey == primitives.ATTESTATION_VIEW_REQUEST_ATTESTOR.vdxfid) { + let attestorFqn; + + try { + const reply = await getIdentity(loginConsent.system_id, permission.data); + attestorFqn = reply.result.friendlyname; + } catch (e) { + console.log("Error getting attestation signer ", e); + return false; + } + attestationAcceptedAttestors.push(attestorFqn); + + } + else { + subjectKeys[permission.data] = true; + } + }; + + let attestationData; + try { + attestationData = await requestAttestationData(ATTESTATIONS_PROVISIONED); + } catch (e) { + createAlert('Error Loading Attestations', e.message); + } + + // check that the requested attestestation name is in the attestationData + + let attestationRequestedFields = []; + const attestationDataKeys = Object.keys(attestationData); + const attestationDataValues = Object.values(attestationData); + let attestationRequestedVdxfKeys = loginConsent.challenge.subject + .map((permission) => permission.data); + + for (let i = 0; i < attestationDataKeys.length; i++) { + + for (let j = 0; j < attestationDataValues[i].data.length; j++) { + + const vdxfobjKey = Object.keys(attestationDataValues[i].data[j])[0]; + const vdxfobjValue = attestationDataValues[i].data[j][vdxfobjKey]; + + if (vdxfobjKey === VDXF_Data.MMRDescriptorKey().vdxfid) { + + for (let k = 0; k < vdxfobjValue.datadescriptors.length; k++) { + + const item = vdxfobjValue.datadescriptors[k].objectdata[VDXF_Data.DataDescriptorKey().vdxfid]; + + if (item.label === primitives.ATTESTATION_NAME.vdxfid) { + if (item.objectdata.message !== attestationName) { + continue; + } + } else if (item.label === primitives.ATTESTATION_VIEW_REQUEST_ATTESTOR.vdxfid) { + if (attestationAcceptedAttestors.indexOf(item.objectdata.message) === -1) { + continue; + } + } else if (subjectKeys[item.label]) { + let name; + + if (name = IdentityVdxfidMap[item.label]?.name) { + attestationRequestedFields.push(name); + } else { + attestationRequestedFields.push(item.label); + } + } + } + if (attestationRequestedFields && attestationAcceptedAttestors) { + attestationID = attestationDataKeys[0]; + } + } + } + } + this.setState({ attestationRequestedFields, + attestationAcceptedAttestors, + attestationName, + attestationID, + attestationDataURL: { vdxfkey: attestationDataURL[0].vdxfkey, uri: attestationDataURL[0].data }, + attestationRequestedVdxfKeys }); + } + + async handleContinue() { + this.setState({ loading: true }); + + const createdAttestation = await this.createAttestationReply() + + createAlert( + "Send Selected Attestation info", + "Are you sure you want to send your attestation data to: \n" + `${this.state.signerFqn}`, + [ + { + text: "No", + onPress: () => { + resolveAlert(); + }, + }, + { + text: "Yes", + onPress: () => { + resolveAlert(); + handleAttestationDataSend(createdAttestation, this.state.attestationDataURL) + .then(() => { + this.setState({ loading: false }); + this.props.route.params.onGoBack(true); + this.props.navigation.goBack(); + this.cancel(); + }).catch((e) => { + + this.setState({ loading: false }); + createAlert("Error, Failed to Send Attestation, server may be unavailable.", e.message); + + }) + }, + }, + ], + { + cancelable: false, + } + ); + + + }; + + render() { + return LoginShareAttestationRender.call(this); + } +} + +const mapStateToProps = (state) => { + return { + activeAccount: state.authentication.activeAccount, + encryptedPersonalData: state.personal + } +}; + +export default connect(mapStateToProps)(LoginShareAttestation); \ No newline at end of file diff --git a/src/containers/DeepLink/LoginShareAttestation/LoginShareAttestation.render.js b/src/containers/DeepLink/LoginShareAttestation/LoginShareAttestation.render.js new file mode 100644 index 00000000..0253aa2d --- /dev/null +++ b/src/containers/DeepLink/LoginShareAttestation/LoginShareAttestation.render.js @@ -0,0 +1,68 @@ +import React from "react"; +import { SafeAreaView, ScrollView, View } from "react-native"; +import { Divider, List, Button, Text } from "react-native-paper"; + +import Styles from "../../../styles"; + +import Colors from '../../../globals/colors'; + +export const LoginShareAttestationRender = function (props) { + + return ( + + + + + + Agree to share the following attestation data. + + + {`Attestation Name: `}{`${this.state.attestationName}`} + + {this.state.attestationAcceptedAttestors && + {`From: `}{`${this.state.attestationAcceptedAttestors[0]}`} + } + + + {this.state.attestationRequestedFields.map(request => { + return ( + + + + + + ); + })} + + + + + + + + + + ); +}; diff --git a/src/containers/DeepLink/PersonalSelectData/PersonalSelectData.js b/src/containers/DeepLink/PersonalSelectData/PersonalSelectData.js new file mode 100644 index 00000000..b05274d4 --- /dev/null +++ b/src/containers/DeepLink/PersonalSelectData/PersonalSelectData.js @@ -0,0 +1,187 @@ +import moment from "moment"; +import { Component } from "react" +import { connect } from 'react-redux' +import { createAlert, resolveAlert } from "../../../actions/actions/alert/dispatchers/alert" +import { modifyPersonalDataForUser } from "../../../actions/actionDispatchers"; +import { requestPersonalData } from "../../../utils/auth/authBox"; +import { PERSONAL_ATTRIBUTES, PERSONAL_CONTACT, PERSONAL_LOCATIONS, PERSONAL_PAYMENT_METHODS, PERSONAL_IMAGES } from "../../../utils/constants/personal"; +import { provideCustomBackButton } from "../../../utils/navigation/customBack"; +import { PersonalSelectDataRender } from "./PersonalSelectData.render" +import { checkPersonalDataCatagories } from "../../../utils/personal/displayUtils"; +import { handlePersonalDataSend } from "../../../utils/deeplink/handlePersonalDataSend"; +import { primitives } from "verusid-ts-client" + +const { IDENTITYDATA_CONTACT, IDENTITYDATA_PERSONAL_DETAILS, IDENTITYDATA_LOCATIONS, IDENTITYDATA_DOCUMENTS_AND_IMAGES, IDENTITYDATA_BANKING_INFORMATION} = primitives; + + +//const { defaultPersonalProfileDataTemplate } = primitives; +const EDIT = 'edit' +const REMOVE = 'remove' +const PERSONALDATACATAGORIES = [ + IDENTITYDATA_CONTACT.vdxfid, + IDENTITYDATA_LOCATIONS.vdxfid, + IDENTITYDATA_DOCUMENTS_AND_IMAGES.vdxfid, + IDENTITYDATA_PERSONAL_DETAILS.vdxfid, + IDENTITYDATA_BANKING_INFORMATION.vdxfid +]; + +const PERSONALDATALINKS = [ + "PersonalContact", + "PersonalLocations", + "PersonalImages", + "PersonalAttributes", + "PersonalPaymentMethods" +] +class PersonalSelectData extends Component { + constructor(props) { + super(props); + this.state = { + loginConsent: null, + nationalityModalOpen: false, + birthdaySelectorModalOpen: false, + editPropertyModal: { + open: false, + property: null, + label: "", + index: null + }, + loading: false, + ready: false, + catagoriesRequested: null, + personalDataURL: "", + signerFqn: this.props.route.params.signerFqn + }; + } + + componentDidMount() { + this.updateDisplay(); + } + + componentDidUpdate(lastProps) { + if (lastProps.encryptedPersonalData !== this.props.encryptedPersonalData) { + this.updateDisplay(); + } + } + + cancel = () => { + if (this.props.route.params.cancel) { + this.props.route.params.cancel.cancel() + } + } + + updateDisplay() { + const { deeplinkData } = this.props.route.params + const loginConsent = new primitives.LoginConsentRequest(deeplinkData); + + const personalDataURL = loginConsent.challenge.subject + .filter((permission) => permission.vdxfkey === primitives.LOGIN_CONSENT_PERSONALINFO_WEBHOOK_VDXF_KEY.vdxfid); + + const requestedPersonalData = loginConsent.challenge.subject + .filter((permission) => permission.vdxfkey === primitives.PROFILE_DATA_VIEW_REQUEST.vdxfid); + + const catagoriesRequested = {}; + requestedPersonalData.forEach((permission) => { + PERSONALDATACATAGORIES.forEach((category, idx) => { + if (category === permission.data) { + primitives.defaultPersonalProfileDataTemplate.forEach((templateCategory) => { + if (templateCategory.vdxfid === permission.data) { + catagoriesRequested[permission.data] = { title: templateCategory.category, + details: templateCategory.details, navigateTo: PERSONALDATALINKS[idx], color: "black" }; + } + }) + } + }) + }); + + checkPersonalDataCatagories(catagoriesRequested).then((success) => { + this.setState({ catagoriesRequested: catagoriesRequested, personalDataURL: {vdxfkey: personalDataURL[0].vdxfkey ,uri: personalDataURL[0].data}, ready: success}); + }); + } + + handleContinue() { + this.setState({loading: true}); + + this.getPersonalDataFromCategories().then((personalData) => { + + createAlert( + "Send Personal info", + "Are you sure you want to send your data to: \n" + `${this.state.signerFqn}`, + [ + { + text: "No", + onPress: () => { + resolveAlert(); + }, + }, + { + text: "Yes", + onPress: () => { + resolveAlert(); + handlePersonalDataSend(personalData, this.state.personalDataURL) + .then(() => { + this.setState({loading: false}); + this.props.route.params.onGoBack(true); + this.props.navigation.goBack(); + }) + }, + }, + ], + { + cancelable: false, + } + ); + } + ).catch((e) => { + this.setState({loading: false}); + createAlert("Error", e.message); + }); + + }; + + async getPersonalDataFromCategories() { + let personalData = {}; + + await Promise.all(Object.keys(this.state.catagoriesRequested).map(async (categoryVdxfkey) => { + switch (categoryVdxfkey) { + case IDENTITYDATA_PERSONAL_DETAILS.vdxfid: + personalData[IDENTITYDATA_PERSONAL_DETAILS.vdxfid] = await requestPersonalData(PERSONAL_ATTRIBUTES); + break; + case IDENTITYDATA_CONTACT.vdxfid: + personalData[IDENTITYDATA_CONTACT.vdxfid] = await requestPersonalData(PERSONAL_CONTACT); + break; + case IDENTITYDATA_LOCATIONS.vdxfid: + personalData[IDENTITYDATA_LOCATIONS.vdxfid] = await requestPersonalData(PERSONAL_LOCATIONS); + break; + case IDENTITYDATA_BANKING_INFORMATION.vdxfid: + personalData[IDENTITYDATA_BANKING_INFORMATION.vdxfid] = await requestPersonalData(PERSONAL_PAYMENT_METHODS); + break; + case IDENTITYDATA_DOCUMENTS_AND_IMAGES.vdxfid: + personalData[IDENTITYDATA_DOCUMENTS_AND_IMAGES.vdxfid] = await requestPersonalData(PERSONAL_IMAGES); + break; + default: + break; + } + })); + + return personalData; + } + + openAttributes(navigateTo) { + this.props.navigation.navigate("ProfileStackScreens", { + screen: navigateTo + }); + } + + render() { + return PersonalSelectDataRender.call(this); + } +} + +const mapStateToProps = (state) => { + return { + activeAccount: state.authentication.activeAccount, + encryptedPersonalData: state.personal + } +}; + +export default connect(mapStateToProps)(PersonalSelectData); \ No newline at end of file diff --git a/src/containers/DeepLink/PersonalSelectData/PersonalSelectData.render.js b/src/containers/DeepLink/PersonalSelectData/PersonalSelectData.render.js new file mode 100644 index 00000000..8e55b469 --- /dev/null +++ b/src/containers/DeepLink/PersonalSelectData/PersonalSelectData.render.js @@ -0,0 +1,63 @@ +import React from "react"; +import { SafeAreaView, ScrollView, View } from "react-native"; +import { Divider, List, Button, Text } from "react-native-paper"; + +import Styles from "../../../styles"; + +import Colors from '../../../globals/colors'; + +export const PersonalSelectDataRender = function (props) { + + return ( + + + + + + Agree to share the following personal data. + + {this.state.catagoriesRequested && Object.values(this.state.catagoriesRequested).map(request => { + return ( + + {request.details}} + onPress={this.state.loading ? () => { } : () => this.openAttributes(request.navigateTo)} + right={(props) => } + key={request.details} + /> + + + + ); + })} + + + + + + + + + + ); +}; diff --git a/src/containers/Personal/PersonalAttributes/PersonalAttributes.js b/src/containers/Personal/PersonalAttributes/PersonalAttributes.js index e4a872a8..18b16c09 100644 --- a/src/containers/Personal/PersonalAttributes/PersonalAttributes.js +++ b/src/containers/Personal/PersonalAttributes/PersonalAttributes.js @@ -1,11 +1,14 @@ import moment from "moment"; -import { Component } from "react" +import { Component } from "react" +import { Platform, NativeModules } from 'react-native' import { connect } from 'react-redux' import { modifyPersonalDataForUser } from "../../../actions/actionDispatchers"; import { requestPersonalData } from "../../../utils/auth/authBox"; import { PERSONAL_ATTRIBUTES, PERSONAL_BIRTHDAY, PERSONAL_NATIONALITIES } from "../../../utils/constants/personal"; import { provideCustomBackButton } from "../../../utils/navigation/customBack"; import { PersonalAttributesRender } from "./PersonalAttributes.render" +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_FIRSTNAME, IDENTITYDATA_MIDDLENAME, IDENTITYDATA_LASTNAME, IDENTITYDATA_DATEOFBIRTH, IDENTITYDATA_NATIONALITY } = primitives; const EDIT = 'edit' const REMOVE = 'remove' @@ -15,17 +18,11 @@ class PersonalAttributes extends Component { super(); this.state = { attributes: { - name: { - first: "John", - middle: "", - last: "Doe" - }, - birthday: { - day: null, - month: null, - year: null - }, - nationalities: [] + [IDENTITYDATA_FIRSTNAME.vdxfid]: "John", + [IDENTITYDATA_MIDDLENAME.vdxfid]: "", + [IDENTITYDATA_LASTNAME.vdxfid]: "Doe", + [IDENTITYDATA_DATEOFBIRTH.vdxfid]: {}, + [IDENTITYDATA_NATIONALITY.vdxfid]: [], }, nationalityModalOpen: false, birthdaySelectorModalOpen: false, @@ -126,21 +123,23 @@ class PersonalAttributes extends Component { } addNationality(nationalityCode) { - const nationalities = this.state.attributes.nationalities ? this.state.attributes.nationalities : [] - this.updateAttribute(PERSONAL_NATIONALITIES, [...nationalities, nationalityCode]) + const nationalities = this.state.attributes[IDENTITYDATA_NATIONALITY.vdxfid] + ? this.state.attributes[IDENTITYDATA_NATIONALITY.vdxfid] : [] + this.updateAttribute(IDENTITYDATA_NATIONALITY.vdxfid, [...nationalities, nationalityCode]) } removeNationality(index) { - let nationalities = this.state.attributes.nationalities ? this.state.attributes.nationalities : [] + let nationalities = this.state.attributes[IDENTITYDATA_NATIONALITY.vdxfid] + ? this.state.attributes[IDENTITYDATA_NATIONALITY.vdxfid] : [] nationalities.splice(index, 1); - this.updateAttribute(PERSONAL_NATIONALITIES, nationalities) + this.updateAttribute(IDENTITYDATA_NATIONALITY.vdxfid, nationalities) } setBirthday(date) { this.setState({ birthdaySelectorModalOpen: false }, () => { - this.updateAttribute(PERSONAL_BIRTHDAY, date) + this.updateAttribute(IDENTITYDATA_DATEOFBIRTH.vdxfid, date) }) } @@ -173,10 +172,16 @@ class PersonalAttributes extends Component { } renderDate() { - const { day, month, year } = this.state.attributes.birthday; + const { day, month, year } = this.state.attributes[IDENTITYDATA_DATEOFBIRTH.vdxfid]; const date = this.getDateClassInstance(day, month, year) - return date.toLocaleDateString("en-US", { + const deviceLanguage = + Platform.OS === 'ios' + ? NativeModules.SettingsManager.settings.AppleLocale || + NativeModules.SettingsManager.settings.AppleLanguages[0] //iOS 13 + : NativeModules.I18nManager.localeIdentifier; + + return date.toLocaleDateString(deviceLanguage, { year: "numeric", month: "long", day: "numeric", diff --git a/src/containers/Personal/PersonalAttributes/PersonalAttributes.render.js b/src/containers/Personal/PersonalAttributes/PersonalAttributes.render.js index 562e3646..c043c91d 100644 --- a/src/containers/Personal/PersonalAttributes/PersonalAttributes.render.js +++ b/src/containers/Personal/PersonalAttributes/PersonalAttributes.render.js @@ -7,6 +7,8 @@ import Styles from "../../../styles"; import { ISO_3166_COUNTRIES, ISO_3166_ALPHA_2_CODES } from "../../../utils/constants/iso3166"; import { PERSONAL_NATIONALITIES } from "../../../utils/constants/personal"; import { renderPersonalBirthday, renderPersonalFullName } from "../../../utils/personal/displayUtils"; +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_PERSONAL_DETAILS, IDENTITYDATA_FIRSTNAME, IDENTITYDATA_MIDDLENAME, IDENTITYDATA_LASTNAME, IDENTITYDATA_DATEOFBIRTH, IDENTITYDATA_NATIONALITY } = primitives; export const PersonalAttributesRender = function () { return ( @@ -46,12 +48,12 @@ export const PersonalAttributesRender = function () { flexHeight={0.5} visible={this.state.birthdaySelectorModalOpen} initialDate={ - this.state.attributes.birthday == null + this.state.attributes[IDENTITYDATA_DATEOFBIRTH.vdxfid] == null ? new Date() : this.getDateClassInstance( - this.state.attributes.birthday.day, - this.state.attributes.birthday.month, - this.state.attributes.birthday.year + this.state.attributes[IDENTITYDATA_DATEOFBIRTH.vdxfid].day, + this.state.attributes[IDENTITYDATA_DATEOFBIRTH.vdxfid].month, + this.state.attributes[IDENTITYDATA_DATEOFBIRTH.vdxfid].year ) } onSelect={(date) => this.setBirthday(date)} @@ -64,7 +66,7 @@ export const PersonalAttributesRender = function () { {"Name"} ( )} @@ -73,9 +75,9 @@ export const PersonalAttributesRender = function () { {"Date of birth"} - {this.state.attributes.birthday != null ? ( + {this.state.attributes[IDENTITYDATA_DATEOFBIRTH.vdxfid] != null ? ( ( )} @@ -91,9 +93,9 @@ export const PersonalAttributesRender = function () { {"Nationalities"} - {this.state.attributes.nationalities == null + {this.state.attributes[IDENTITYDATA_NATIONALITY.vdxfid] == null ? null - : this.state.attributes.nationalities.map((code, index) => { + : this.state.attributes[IDENTITYDATA_NATIONALITY.vdxfid].map((code, index) => { const nationality = ISO_3166_COUNTRIES[code]; return ( diff --git a/src/containers/Personal/PersonalAttributes/PersonalAttributesEditName/PersonalAttributesEditName.js b/src/containers/Personal/PersonalAttributes/PersonalAttributesEditName/PersonalAttributesEditName.js index 3dc753b5..a636d203 100644 --- a/src/containers/Personal/PersonalAttributes/PersonalAttributesEditName/PersonalAttributesEditName.js +++ b/src/containers/Personal/PersonalAttributes/PersonalAttributesEditName/PersonalAttributesEditName.js @@ -4,17 +4,14 @@ import { modifyPersonalDataForUser } from "../../../../actions/actionDispatchers import { PERSONAL_ATTRIBUTES } from "../../../../utils/constants/personal"; import { provideCustomBackButton } from "../../../../utils/navigation/customBack"; import { PersonalAttributesEditNameRender } from "./PersonalAttributesEditName.render" +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_PERSONAL_DETAILS, IDENTITYDATA_DATEOFBIRTH} = primitives; class PersonalAttributesEditName extends Component { constructor(props) { super(props); this.state = { attributes: props.route.params.attributes, - name: { - first: props.route.params.attributes.name ? props.route.params.attributes.name.first : "", - middle: props.route.params.attributes.name ? props.route.params.attributes.name.middle : "", - last: props.route.params.attributes.name ? props.route.params.attributes.name.last : "", - }, currentTextInputModal: null, loading: false, }; @@ -33,7 +30,7 @@ class PersonalAttributesEditName extends Component { updateName() { this.setState({ loading: true }, async () => { await modifyPersonalDataForUser( - {...this.state.attributes, name: this.state.name}, + this.state.attributes, PERSONAL_ATTRIBUTES, this.props.activeAccount.accountHash ); diff --git a/src/containers/Personal/PersonalAttributes/PersonalAttributesEditName/PersonalAttributesEditName.render.js b/src/containers/Personal/PersonalAttributes/PersonalAttributesEditName/PersonalAttributesEditName.render.js index 3f3f3b16..f0d93110 100644 --- a/src/containers/Personal/PersonalAttributes/PersonalAttributesEditName/PersonalAttributesEditName.render.js +++ b/src/containers/Personal/PersonalAttributes/PersonalAttributesEditName/PersonalAttributesEditName.render.js @@ -4,6 +4,8 @@ import { Divider, List, Portal } from "react-native-paper"; import Styles from "../../../../styles"; import TextInputModal from "../../../../components/TextInputModal/TextInputModal" import Colors from "../../../../globals/colors"; +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_PERSONAL_DETAILS, IDENTITYDATA_FIRSTNAME, IDENTITYDATA_MIDDLENAME, IDENTITYDATA_LASTNAME, IDENTITYDATA_DATEOFBIRTH, IDENTITYDATA_NATIONALITY } = primitives; export const PersonalAttributesEditNameRender = function () { return ( @@ -12,13 +14,13 @@ export const PersonalAttributesEditNameRender = function () { {this.state.currentTextInputModal != null && ( { if (text != null) this.setState({ - name: { - ...this.state.name, + attributes: { + ...this.state.attributes, [this.state.currentTextInputModal]: text, }, }); @@ -31,19 +33,19 @@ export const PersonalAttributesEditNameRender = function () { } onPress={ - this.state.loading ? () => {} : () => this.setState({ currentTextInputModal: "first" }) + this.state.loading ? () => {} : () => this.setState({ currentTextInputModal: IDENTITYDATA_FIRSTNAME.vdxfid }) } /> @@ -51,19 +53,19 @@ export const PersonalAttributesEditNameRender = function () { } onPress={ - this.state.loading ? () => {} : () => this.setState({ currentTextInputModal: "middle" }) + this.state.loading ? () => {} : () => this.setState({ currentTextInputModal: IDENTITYDATA_MIDDLENAME.vdxfid }) } /> @@ -71,19 +73,19 @@ export const PersonalAttributesEditNameRender = function () { } onPress={ - this.state.loading ? () => {} : () => this.setState({ currentTextInputModal: "last" }) + this.state.loading ? () => {} : () => this.setState({ currentTextInputModal: IDENTITYDATA_LASTNAME.vdxfid }) } /> diff --git a/src/containers/Personal/PersonalContact/PersonalContact.js b/src/containers/Personal/PersonalContact/PersonalContact.js index 6f046996..50e76313 100644 --- a/src/containers/Personal/PersonalContact/PersonalContact.js +++ b/src/containers/Personal/PersonalContact/PersonalContact.js @@ -9,6 +9,8 @@ import { } from "../../../utils/constants/personal"; import { provideCustomBackButton } from "../../../utils/navigation/customBack"; import { PersonalContactRender } from "./PersonalContact.render" +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_EMAIL, IDENTITYDATA_PHONENUMBER } = primitives; const EDIT = 'edit' const REMOVE = 'remove' @@ -18,8 +20,8 @@ class PersonalContact extends Component { super(); this.state = { contact: { - phone_numbers: [], - emails: [] + [IDENTITYDATA_PHONENUMBER.vdxfid]: [], + [IDENTITYDATA_EMAIL.vdxfid]: [] }, addPropertyModal: { open: false, @@ -157,13 +159,13 @@ class PersonalContact extends Component { } editPhone(phone, index) { - let phones = this.state.contact.phone_numbers ? this.state.contact.phone_numbers : [] + let phones = this.state.contact[IDENTITYDATA_PHONENUMBER.vdxfid] ? this.state.contact[IDENTITYDATA_PHONENUMBER.vdxfid] : [] if (index == null) { - this.updateContactPoint(PERSONAL_PHONE_NUMBERS, [...phones, phone]) + this.updateContactPoint(IDENTITYDATA_PHONENUMBER.vdxfid, [...phones, phone]) } else { phones[index] = phone - this.updateContactPoint(PERSONAL_PHONE_NUMBERS, phones) + this.updateContactPoint(IDENTITYDATA_PHONENUMBER.vdxfid, phones) } } @@ -179,26 +181,26 @@ class PersonalContact extends Component { } editEmail(email, index) { - let emails = this.state.contact.emails ? this.state.contact.emails : [] + let emails = this.state.contact[IDENTITYDATA_EMAIL.vdxfid] ? this.state.contact[IDENTITYDATA_EMAIL.vdxfid] : [] if (index == null) { - this.updateContactPoint(PERSONAL_EMAILS, [...emails, email]) + this.updateContactPoint(IDENTITYDATA_EMAIL.vdxfid, [...emails, email]) } else { emails[index] = email - this.updateContactPoint(PERSONAL_EMAILS, emails) + this.updateContactPoint(IDENTITYDATA_EMAIL.vdxfid, emails) } } removePhone(index) { - let phone_numbers = this.state.contact.phone_numbers ? this.state.contact.phone_numbers : [] + let phone_numbers = this.state.contact[IDENTITYDATA_PHONENUMBER.vdxfid] ? this.state.contact[IDENTITYDATA_PHONENUMBER.vdxfid] : [] phone_numbers.splice(index, 1); - this.updateContactPoint(PERSONAL_PHONE_NUMBERS, phone_numbers) + this.updateContactPoint(IDENTITYDATA_PHONENUMBER.vdxfid, phone_numbers) } removeEmail(index) { - let emails = this.state.contact.emails ? this.state.contact.emails : [] + let emails = this.state.contact[IDENTITYDATA_EMAIL.vdxfid] ? this.state.contact[IDENTITYDATA_EMAIL.vdxfid] : [] emails.splice(index, 1); - this.updateContactPoint(PERSONAL_EMAILS, emails) + this.updateContactPoint(IDENTITYDATA_EMAIL.vdxfid, emails) } finishPhoneEdit(phone, index) { diff --git a/src/containers/Personal/PersonalContact/PersonalContact.render.js b/src/containers/Personal/PersonalContact/PersonalContact.render.js index 9304024c..35e33d20 100644 --- a/src/containers/Personal/PersonalContact/PersonalContact.render.js +++ b/src/containers/Personal/PersonalContact/PersonalContact.render.js @@ -10,6 +10,8 @@ import { ISO_3166_COUNTRIES, ISO_3166_ALPHA_2_CODES } from "../../../utils/const import { CALLING_CODES_TO_ISO_3166 } from "../../../utils/constants/callingCodes"; import { PERSONAL_EMAILS, PERSONAL_NATIONALITIES, PERSONAL_PHONE_NUMBERS } from "../../../utils/constants/personal"; import { renderPersonalPhoneNumber } from "../../../utils/personal/displayUtils"; +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_EMAIL, IDENTITYDATA_PHONENUMBER } = primitives; export const PersonalContactRender = function () { return ( @@ -70,9 +72,9 @@ export const PersonalContactRender = function () { {"Email addresses"} - {this.state.contact.emails == null + {this.state.contact[IDENTITYDATA_EMAIL.vdxfid] == null ? null - : this.state.contact.emails.map((email, index) => { + : this.state.contact[IDENTITYDATA_EMAIL.vdxfid].map((email, index) => { return ( {"Phone numbers"} - {this.state.contact.phone_numbers == null + {this.state.contact[IDENTITYDATA_PHONENUMBER.vdxfid] == null ? null - : this.state.contact.phone_numbers.map((phone, index) => { + : this.state.contact[IDENTITYDATA_PHONENUMBER.vdxfid].map((phone, index) => { const type = phone.type; const typeFr = type.charAt(0).toUpperCase() + type.slice(1); diff --git a/src/containers/Personal/PersonalInfo/PersonalInfo.js b/src/containers/Personal/PersonalInfo/PersonalInfo.js index 5592ff26..dcea3f81 100644 --- a/src/containers/Personal/PersonalInfo/PersonalInfo.js +++ b/src/containers/Personal/PersonalInfo/PersonalInfo.js @@ -3,24 +3,25 @@ use to configure their personal wallet profile. They can then take this profile data and submit it to applications that require KYC if they want. -*/ +*/ import { Component } from "react" import { connect } from 'react-redux' import { requestPersonalData } from "../../../utils/auth/authBox"; import { PERSONAL_ATTRIBUTES } from "../../../utils/constants/personal"; import { PersonalInfoRender } from "./PersonalInfo.render" +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_MIDDLENAME, IDENTITYDATA_FIRSTNAME, IDENTITYDATA_LASTNAME, IDENTITYDATA_DATEOFBIRTH } = primitives; class PersonalInfo extends Component { constructor(props) { super(props); this.state = { attributes: { - name: { - first: "John", - middle: "", - last: "Doe" - } + [IDENTITYDATA_FIRSTNAME.vdxfid]: "John", + [IDENTITYDATA_MIDDLENAME.vdxfid]: "", + [IDENTITYDATA_LASTNAME.vdxfid]: "Doe", + [IDENTITYDATA_DATEOFBIRTH.vdxfid]: {}, }, loading: false }; diff --git a/src/containers/Personal/PersonalInfo/PersonalInfo.render.js b/src/containers/Personal/PersonalInfo/PersonalInfo.render.js index a4bd5e87..88262fa0 100644 --- a/src/containers/Personal/PersonalInfo/PersonalInfo.render.js +++ b/src/containers/Personal/PersonalInfo/PersonalInfo.render.js @@ -3,7 +3,8 @@ import { Text, SafeAreaView, ScrollView, View } from "react-native"; import { Divider, List, Avatar, Title } from "react-native-paper"; import Styles from "../../../styles"; import { renderPersonalFullName } from "../../../utils/personal/displayUtils"; - +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_FIRSTNAME, IDENTITYDATA_LASTNAME, IDENTITYDATA_PERSONAL_DETAILS} = primitives; export const PersonalInfoRender = function () { return ( @@ -11,9 +12,9 @@ export const PersonalInfoRender = function () { - {renderPersonalFullName(this.state.attributes.name).title} + {renderPersonalFullName(this.state.attributes).title} diff --git a/src/containers/Personal/PersonalIntroSlider/PersonalIntroSlider.js b/src/containers/Personal/PersonalIntroSlider/PersonalIntroSlider.js index 9fd3f046..ad247a84 100644 --- a/src/containers/Personal/PersonalIntroSlider/PersonalIntroSlider.js +++ b/src/containers/Personal/PersonalIntroSlider/PersonalIntroSlider.js @@ -16,6 +16,8 @@ import { useEffect } from "react"; import { modifyPersonalDataForUser } from "../../../actions/actionDispatchers"; import { PERSONAL_ATTRIBUTES } from "../../../utils/constants/personal"; import { createAlert } from "../../../actions/actions/alert/dispatchers/alert"; +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_PERSONAL_DETAILS, IDENTITYDATA_FIRSTNAME, IDENTITYDATA_MIDDLENAME, IDENTITYDATA_LASTNAME, IDENTITYDATA_DATEOFBIRTH, IDENTITYDATA_NATIONALITY } = primitives; const slides = [ { @@ -48,7 +50,7 @@ const NameForm = (props) => { const [last, setLast] = React.useState(''); useEffect(() => { - onChange({ first: first.trim(), middle: middle.trim(), last: last.trim() }); + onChange({ [IDENTITYDATA_FIRSTNAME.vdxfid]: first.trim(), [IDENTITYDATA_MIDDLENAME.vdxfid]: middle.trim(), [IDENTITYDATA_LASTNAME.vdxfid]: last.trim() }); }, [first, middle, last]); return ( @@ -104,9 +106,9 @@ class PersonalIntroSlider extends Component { super(); this.state = { name: { - first: '', - middle: '', - last: '' + [IDENTITYDATA_FIRSTNAME.vdxfid]: '', + [IDENTITYDATA_MIDDLENAME.vdxfid]: '', + [IDENTITYDATA_LASTNAME.vdxfid]: '' }, loading: false, currentSlide: 0 @@ -179,7 +181,7 @@ class PersonalIntroSlider extends Component { _onDone = () => { this.setState({loading: true}, async () => { await modifyPersonalDataForUser( - { name: this.state.name }, + this.state.name, PERSONAL_ATTRIBUTES, this.props.activeAccount.accountHash ) diff --git a/src/containers/Personal/PersonalLocations/PersonalLocations.render.js b/src/containers/Personal/PersonalLocations/PersonalLocations.render.js index 60b8fccd..d8467791 100644 --- a/src/containers/Personal/PersonalLocations/PersonalLocations.render.js +++ b/src/containers/Personal/PersonalLocations/PersonalLocations.render.js @@ -5,6 +5,8 @@ import ListSelectionModal from "../../../components/ListSelectionModal/ListSelec import Styles from "../../../styles"; import { ISO_3166_COUNTRIES } from "../../../utils/constants/iso3166"; import { renderPersonalTaxId } from "../../../utils/personal/displayUtils"; +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_HOMEADDRESS_STREET1, IDENTITYDATA_HOMEADDRESS_STREET2, IDENTITYDATA_HOMEADDRESS_CITY, IDENTITYDATA_HOMEADDRESS_REGION, IDENTITYDATA_HOMEADDRESS_POSTCODE, IDENTITYDATA_HOMEADDRESS_COUNTRY } = primitives; export const PersonalLocationsRender = function () { return ( @@ -64,24 +66,24 @@ export const PersonalLocationsRender = function () { 0 - ? `${address.street1}${ - address.street2 != null && address.street2.length > 0 - ? `, ${address.street2}` + address[IDENTITYDATA_HOMEADDRESS_STREET1.vdxfid]?.length > 0 + ? `${address[IDENTITYDATA_HOMEADDRESS_STREET1.vdxfid]}${ + address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid] != null && address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid].length > 0 + ? `, ${address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid]}` : "" }` : "Empty address" } description={`${ - address.postal_code.length > 0 ? `${address.postal_code} ` : "" + address[IDENTITYDATA_HOMEADDRESS_POSTCODE.vdxfid]?.length > 0 ? `${address[IDENTITYDATA_HOMEADDRESS_POSTCODE.vdxfid]} ` : "" }${ - address.state_province_region.length > 0 - ? `${address.state_province_region}, ` + address[IDENTITYDATA_HOMEADDRESS_REGION.vdxfid]?.length > 0 + ? `${address[IDENTITYDATA_HOMEADDRESS_REGION.vdxfid]}, ` : "" - }${address.city.length > 0 ? `${address.city}, ` : "Unknown City, "}${ - ISO_3166_COUNTRIES[address.country] != null - ? `${ISO_3166_COUNTRIES[address.country].emoji} ${ - ISO_3166_COUNTRIES[address.country].name + }${address[IDENTITYDATA_HOMEADDRESS_CITY.vdxfid]?.length > 0 ? `${address[IDENTITYDATA_HOMEADDRESS_CITY.vdxfid]}, ` : "Unknown City, "}${ + ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]] != null + ? `${ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]].emoji} ${ + ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]].name }` : "Unknown Country" }`} diff --git a/src/containers/Personal/PersonalLocations/PersonalLocationsEditAddress/PersonalLocationsEditAddress.js b/src/containers/Personal/PersonalLocations/PersonalLocationsEditAddress/PersonalLocationsEditAddress.js index 53b0ec32..a0627ec5 100644 --- a/src/containers/Personal/PersonalLocations/PersonalLocationsEditAddress/PersonalLocationsEditAddress.js +++ b/src/containers/Personal/PersonalLocations/PersonalLocationsEditAddress/PersonalLocationsEditAddress.js @@ -6,6 +6,8 @@ import { createAlert, resolveAlert } from "../../../../actions/actions/alert/dis import { PERSONAL_LOCATIONS } from "../../../../utils/constants/personal"; import { provideCustomBackButton } from "../../../../utils/navigation/customBack"; import { PersonalLocationsEditAddressRender } from "./PersonalLocationsEditAddress.render" +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_HOMEADDRESS_STREET1, IDENTITYDATA_HOMEADDRESS_STREET2, IDENTITYDATA_HOMEADDRESS_CITY, IDENTITYDATA_HOMEADDRESS_REGION, IDENTITYDATA_HOMEADDRESS_POSTCODE, IDENTITYDATA_HOMEADDRESS_COUNTRY } = primitives; class PersonalLocationsEditAddress extends Component { constructor(props) { @@ -19,12 +21,12 @@ class PersonalLocationsEditAddress extends Component { props.route.params.index ] : { - street1: "", - street2: "", - city: "", - state_province_region: "", - postal_code: "", - country: "", + [IDENTITYDATA_HOMEADDRESS_STREET1.vdxfid]: "", + [IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid]: "", + [IDENTITYDATA_HOMEADDRESS_CITY.vdxfid]: "", + [IDENTITYDATA_HOMEADDRESS_REGION.vdxfid]: "", + [IDENTITYDATA_HOMEADDRESS_POSTCODE.vdxfid]: "", + [IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]: "", }; this.state = { @@ -37,29 +39,30 @@ class PersonalLocationsEditAddress extends Component { }; this.addressTextInputLabels = { - street1: { + [IDENTITYDATA_HOMEADDRESS_STREET1.vdxfid]: { title: "Address line 1", description: "Street address, company name, P.O. box", placeholder: "required" }, - street2: { + [IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid]: { title: "Address line 2", description: "Apartment number, unit, floor, etc.", placeholder: "optional" }, - city: { + [IDENTITYDATA_HOMEADDRESS_CITY.vdxfid]: { title: "City", placeholder: "required" }, - state_province_region: { + [IDENTITYDATA_HOMEADDRESS_REGION.vdxfid]: { title: "State/Province/Region", placeholder: "optional" }, - postal_code: { + [IDENTITYDATA_HOMEADDRESS_POSTCODE.vdxfid]: { title: "ZIP/Postal Code", placeholder: "required" } } + const sdf =324 } componentDidMount() { @@ -142,7 +145,7 @@ class PersonalLocationsEditAddress extends Component { this.setState({ address: { ...this.state.address, - country: countryCode + [IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]: countryCode } }, () => this.updateAddress()) } diff --git a/src/containers/Personal/PersonalLocations/PersonalLocationsEditAddress/PersonalLocationsEditAddress.render.js b/src/containers/Personal/PersonalLocations/PersonalLocationsEditAddress/PersonalLocationsEditAddress.render.js index 7fe2db15..15c1f9a8 100644 --- a/src/containers/Personal/PersonalLocations/PersonalLocationsEditAddress/PersonalLocationsEditAddress.render.js +++ b/src/containers/Personal/PersonalLocations/PersonalLocationsEditAddress/PersonalLocationsEditAddress.render.js @@ -6,7 +6,8 @@ import TextInputModal from "../../../../components/TextInputModal/TextInputModal import ListSelectionModal from "../../../../components/ListSelectionModal/ListSelectionModal"; import { ISO_3166_COUNTRIES, ISO_3166_ALPHA_2_CODES } from "../../../../utils/constants/iso3166"; import Colors from "../../../../globals/colors"; - +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_HOMEADDRESS_COUNTRY } = primitives; export const PersonalLocationsEditAddressRender = function () { return ( @@ -83,15 +84,15 @@ export const PersonalLocationsEditAddressRender = function () { 0 - ? `${address.street1}${ - address.street2 != null && address.street2.length > 0 ? `, ${address.street2}` : "" + address[IDENTITYDATA_HOMEADDRESS_STREET1.vdxfid]?.length > 0 + ? `${address[IDENTITYDATA_HOMEADDRESS_STREET1.vdxfid]}${ + address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid] != null && address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid]?.length > 0 ? `, ${address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid]}` : "" }` : "Configure address", - `${address.postal_code.length > 0 ? `${address.postal_code} ` : ""}${ - address.state_province_region.length > 0 ? `${address.state_province_region}, ` : "" - }${address.city.length > 0 ? `${address.city}, ` : "Unknown City, "}${ - ISO_3166_COUNTRIES[address.country] != null - ? `${ISO_3166_COUNTRIES[address.country].emoji} ${ - ISO_3166_COUNTRIES[address.country].name + `${address[IDENTITYDATA_HOMEADDRESS_POSTCODE.vdxfid]?.length > 0 ? `${address[IDENTITYDATA_HOMEADDRESS_POSTCODE.vdxfid]} ` : ""}${ + address[IDENTITYDATA_HOMEADDRESS_REGION.vdxfid].length > 0 ? `${address[IDENTITYDATA_HOMEADDRESS_REGION.vdxfid]}, ` : "" + }${address[IDENTITYDATA_HOMEADDRESS_CITY.vdxfid].length > 0 ? `${address[IDENTITYDATA_HOMEADDRESS_CITY.vdxfid]}, ` : "Unknown City, "}${ + ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]] != null + ? `${ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]].emoji} ${ + ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]].name }` : "Unknown Country" }`, diff --git a/src/containers/Personal/PersonalPaymentMethods/PersonalPaymentMethodsEditBankAccountAddress/PersonalPaymentMethodsEditBankAccountAddress.render.js b/src/containers/Personal/PersonalPaymentMethods/PersonalPaymentMethodsEditBankAccountAddress/PersonalPaymentMethodsEditBankAccountAddress.render.js index 9dad0575..9e56d5fe 100644 --- a/src/containers/Personal/PersonalPaymentMethods/PersonalPaymentMethodsEditBankAccountAddress/PersonalPaymentMethodsEditBankAccountAddress.render.js +++ b/src/containers/Personal/PersonalPaymentMethods/PersonalPaymentMethodsEditBankAccountAddress/PersonalPaymentMethodsEditBankAccountAddress.render.js @@ -4,6 +4,8 @@ import { Divider, List } from "react-native-paper"; import Styles from "../../../../styles"; import Colors from "../../../../globals/colors"; import { ISO_3166_COUNTRIES } from "../../../../utils/constants/iso3166"; +import { primitives } from "verusid-ts-client" +const { IDENTITYDATA_HOMEADDRESS_STREET1, IDENTITYDATA_HOMEADDRESS_STREET2, IDENTITYDATA_HOMEADDRESS_CITY, IDENTITYDATA_HOMEADDRESS_REGION, IDENTITYDATA_HOMEADDRESS_POSTCODE, IDENTITYDATA_HOMEADDRESS_COUNTRY } = primitives; export const PersonalPaymentMethodsEditBankAccountAddressRender = function () { return ( @@ -20,24 +22,24 @@ export const PersonalPaymentMethodsEditBankAccountAddressRender = function () { 0 - ? `${address.street1}${ - address.street2 != null && address.street2.length > 0 - ? `, ${address.street2}` + address[IDENTITYDATA_HOMEADDRESS_STREET1.vdxfid]?.length > 0 + ? `${address[IDENTITYDATA_HOMEADDRESS_STREET1.vdxfid]}${ + address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid] != null && address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid].length > 0 + ? `, ${address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid]}` : "" }` : "Empty address" } description={`${ - address.postal_code.length > 0 ? `${address.postal_code} ` : "" + address[IDENTITYDATA_HOMEADDRESS_POSTCODE.vdxfid]?.length > 0 ? `${address[IDENTITYDATA_HOMEADDRESS_POSTCODE.vdxfid]} ` : "" }${ - address.state_province_region.length > 0 - ? `${address.state_province_region}, ` + address[IDENTITYDATA_HOMEADDRESS_REGION.vdxfid]?.length > 0 + ? `${address[IDENTITYDATA_HOMEADDRESS_REGION.vdxfid]}, ` : "" - }${address.city.length > 0 ? `${address.city}, ` : "Unknown City, "}${ - ISO_3166_COUNTRIES[address.country] != null - ? `${ISO_3166_COUNTRIES[address.country].emoji} ${ - ISO_3166_COUNTRIES[address.country].name + }${address[IDENTITYDATA_HOMEADDRESS_CITY.vdxfid]?.length > 0 ? `${address[IDENTITYDATA_HOMEADDRESS_CITY.vdxfid]}, ` : "Unknown City, "}${ + ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]] != null + ? `${ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]].emoji} ${ + ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]].name }` : "Unknown Country" }`} diff --git a/src/containers/RootStack/DeepLinkStackScreens/DeepLinkStackScreens.js b/src/containers/RootStack/DeepLinkStackScreens/DeepLinkStackScreens.js index 5f37647a..f87ab756 100644 --- a/src/containers/RootStack/DeepLinkStackScreens/DeepLinkStackScreens.js +++ b/src/containers/RootStack/DeepLinkStackScreens/DeepLinkStackScreens.js @@ -5,6 +5,10 @@ import DeepLink from '../../DeepLink/DeepLink'; import LoginRequestIdentity from '../../DeepLink/LoginRequestIdentity/LoginRequestIdentity'; import LoginRequestComplete from '../../DeepLink/LoginRequestComplete/LoginRequestComplete'; import InvoicePaymentConfiguration from '../../DeepLink/InvoicePaymentConfiguration/InvoicePaymentConfiguration'; +import PersonalSelectData from '../../DeepLink/PersonalSelectData/PersonalSelectData'; +import ProfileStackScreens from '../ProfileStackScreens/ProfileStackScreens'; +import LoginReceiveAttestation from '../../DeepLink/LoginReceiveAttestation/LoginReceiveAttestation'; +import LoginShareAttestation from '../../DeepLink/LoginShareAttestation/LoginShareAttestation'; const DeepLinkStack = createStackNavigator(); @@ -43,6 +47,35 @@ const DeepLinkStackScreens = props => { title: "Configure Payment" }} /> + null, + title: "Personal Data" + }} + /> + + null, + title: "Receive Attestation" + }} + /> + null, + title: "Share Attestation data" + }} + /> ); }; diff --git a/src/containers/RootStack/ServicesStackScreens/ServicesStackScreens.js b/src/containers/RootStack/ServicesStackScreens/ServicesStackScreens.js index c5005baa..b6c4056c 100644 --- a/src/containers/RootStack/ServicesStackScreens/ServicesStackScreens.js +++ b/src/containers/RootStack/ServicesStackScreens/ServicesStackScreens.js @@ -6,7 +6,7 @@ import Service from '../../Services/Service/Service' import WyreServiceAccountData from '../../Services/ServiceComponents/WyreService/WyreServiceAccount/WyreServiceAccountData/WyreServiceAccountData'; import WyreServiceAddPaymentMethod from '../../Services/ServiceComponents/WyreService/WyreServiceAccount/WyreServiceAddPaymentMethod/WyreServiceAddPaymentMethod'; import WyreServiceEditPaymentMethod from '../../Services/ServiceComponents/WyreService/WyreServiceAccount/WyreServiceEditPaymentMethod/WyreServiceEditPaymentMethod'; - +import ViewAttestation from '../../Services/ServiceComponents/AttestationService/ViewAttestation/ViewAttestation'; const ServicesStack = createStackNavigator(); const ServicesStackScreens = props => { @@ -43,6 +43,13 @@ const ServicesStackScreens = props => { title: "Edit Account", }} /> + ); }; diff --git a/src/containers/Services/Service/Service.js b/src/containers/Services/Service/Service.js index 83593652..6b942ded 100644 --- a/src/containers/Services/Service/Service.js +++ b/src/containers/Services/Service/Service.js @@ -3,11 +3,11 @@ */ import React, { Component } from "react" -import { PBAAS_PRECONVERT_SERVICE_ID, VERUSID_SERVICE_ID, WYRE_SERVICE_ID } from "../../../utils/constants/services"; +import { PBAAS_PRECONVERT_SERVICE_ID, VERUSID_SERVICE_ID, WYRE_SERVICE_ID, ATTESTATION_SERVICE_ID } from "../../../utils/constants/services"; import VerusIdService from "../ServiceComponents/VerusIdService/VerusIdService"; import WyreService from "../ServiceComponents/WyreService/WyreService"; import PbaasPreconvertService from "../ServiceComponents/PbaasPreconvertService/PbaasPreconvertService"; - +import AttestationService from "../ServiceComponents/AttestationService/AttestationService"; class Service extends Component { constructor(props) { super(props); @@ -18,7 +18,8 @@ class Service extends Component { this.SERVICE_COMPONENTS = { [WYRE_SERVICE_ID]: , [VERUSID_SERVICE_ID]: , - [PBAAS_PRECONVERT_SERVICE_ID]: + [PBAAS_PRECONVERT_SERVICE_ID]: , + [ATTESTATION_SERVICE_ID]: , } } diff --git a/src/containers/Services/ServiceComponents/AttestationService/AttestationService.js b/src/containers/Services/ServiceComponents/AttestationService/AttestationService.js new file mode 100644 index 00000000..d147de65 --- /dev/null +++ b/src/containers/Services/ServiceComponents/AttestationService/AttestationService.js @@ -0,0 +1,66 @@ +import React, { Component } from "react" +import { connect } from 'react-redux' +import { setServiceLoading } from "../../../../actions/actionCreators"; +import { createAlert } from "../../../../actions/actions/alert/dispatchers/alert"; +import { requestServiceStoredData } from "../../../../utils/auth/authBox"; +import { ATTESTATION_SERVICE_ID } from "../../../../utils/constants/services"; +import { VerusAttestationRender } from "./AttestationService.render"; +import { ATTESTATIONS_PROVISIONED } from "../../../../utils/constants/attestations"; +import { requestAttestationData } from "../../../../utils/auth/authBox"; + +class AttestationService extends Component { + constructor(props) { + super(props); + this.state = { + attestations: {}, + }; + this.props.navigation.setOptions({title: 'Attestations'}); + const sdf=234 + } + + async getAttestations() { + this.props.dispatch(setServiceLoading(true, ATTESTATION_SERVICE_ID)); + + try { + const attestationData = await requestAttestationData(ATTESTATIONS_PROVISIONED); + if (attestationData) { + this.setState({ + attestations: attestationData + }); + } + } catch (e) { + createAlert('Error Loading Attestations', e.message); + } + + this.props.dispatch(setServiceLoading(false, ATTESTATION_SERVICE_ID)); + } + + componentDidMount() { + + this.getAttestations(); + } + + viewDetails = (attestation) => { + this.props.navigation.navigate("ViewAttestation", {attestation}); + } + + componentDidUpdate(lastProps) { + // if (lastProps.encryptedIds !== this.props.encryptedIds) { + // this.getLinkedIds() + // } + } + + render() { + return VerusAttestationRender.call(this); + } +} + +const mapStateToProps = state => { + + return { + loading: state.services.loading[ATTESTATION_SERVICE_ID], + attestestationdata: state.attestation + }; +}; + +export default connect(mapStateToProps)(AttestationService); \ No newline at end of file diff --git a/src/containers/Services/ServiceComponents/AttestationService/AttestationService.render.js b/src/containers/Services/ServiceComponents/AttestationService/AttestationService.render.js new file mode 100644 index 00000000..7934faf1 --- /dev/null +++ b/src/containers/Services/ServiceComponents/AttestationService/AttestationService.render.js @@ -0,0 +1,54 @@ +import React from "react"; +import { View, Text } from 'react-native' +import styles from "../../../../styles"; +import AnimatedActivityIndicator from "../../../../components/AnimatedActivityIndicator"; +import {Divider, List} from 'react-native-paper'; + +export const VerusAttestationRender = function () { + return ( + + {(this.props.loading) && ( + + + + )} + {!this.props.loading && ( + + {Object.values(this.state.attestations || {}).length === 0 && ( + No attestations present + )} + {Object.values(this.state.attestations || {}).map((attestation, index) => { + + return ( + + this.viewDetails(attestation)} + right={props => ( + + )} + /> + + + ); + + + })} + + )} + + ); +}; diff --git a/src/containers/Services/ServiceComponents/AttestationService/ViewAttestation/ViewAttestation.js b/src/containers/Services/ServiceComponents/AttestationService/ViewAttestation/ViewAttestation.js new file mode 100644 index 00000000..257fec15 --- /dev/null +++ b/src/containers/Services/ServiceComponents/AttestationService/ViewAttestation/ViewAttestation.js @@ -0,0 +1,104 @@ +import React, { Component } from "react" +import { connect } from 'react-redux' + +import { primitives } from "verusid-ts-client" + +import * as VDXF_Data from "verus-typescript-primitives/dist/vdxf/vdxfDataKeys"; + +const { ATTESTATION_NAME } = primitives; +import { IdentityVdxfidMap } from "verus-typescript-primitives/dist/vdxf/classes/IdentityData"; +import { SafeAreaView, ScrollView, View, Image } from 'react-native' + +import { Divider, List, Button, Text } from 'react-native-paper'; +import Styles from "../../../../../styles"; +import Colors from '../../../../../globals/colors'; + +class ViewAttestation extends Component { + constructor(props) { + super(props); + this.state = { + attestationData: {}, + signer: "" + }; + } + + componentDidMount() { + this.updateDisplay(); + } + + getAttestationData = (dataDescriptors) => { + + const data = {}; + dataDescriptors.forEach((dataDescriptor) => { + const label = dataDescriptor.objectdata[Object.keys(dataDescriptor.objectdata)[0]].label; + let key = ""; + + if (label === ATTESTATION_NAME.vdxfid) { + key = `Attestation name` + } else { + key = IdentityVdxfidMap[label]?.name || label; + } + + const mime = dataDescriptor.objectdata[Object.keys(dataDescriptor.objectdata)[0]].mimetype || ""; + if (mime.startsWith("text/")) { + data[key] = { "message": dataDescriptor.objectdata[Object.keys(dataDescriptor.objectdata)[0]].objectdata.message }; + } else if (mime.startsWith("image/")) { + if (mime === "image/jpeg" || mime === "image/png") { + data[key] = { "image": `data:${mime};base64,${Buffer.from(dataDescriptor.objectdata[Object.keys(dataDescriptor.objectdata)[0]].objectdata, "hex").toString("base64")}` }; + } + } + }); + + return data; + + } + + updateDisplay() { + const { attestation } = this.props.route.params + + const signatureData = attestation.data.find((dataDescriptor) => Object.keys(dataDescriptor)[0] === VDXF_Data.SignatureDataKey().vdxfid)[VDXF_Data.SignatureDataKey().vdxfid]; + const mmrData = attestation.data.find((dataDescriptor) => Object.keys(dataDescriptor)[0] === VDXF_Data.MMRDescriptorKey().vdxfid)[VDXF_Data.MMRDescriptorKey().vdxfid]; + + const containingData = this.getAttestationData(mmrData.datadescriptors); + this.setState({ attestationData: containingData, signer:attestation.signer }); + + } + + render() { + return ( + + + + + + {`From: `}{`${this.state.signer}`} + + {this.state.attestationData && Object.keys(this.state.attestationData).map(request => { + return ( + + this.state.attestationData[request]?.image ? : null} + /> + + + ); + })} + + + ) + } +} + +const mapStateToProps = (state) => { + return { + activeAccount: state.authentication.activeAccount, + encryptedPersonalData: state.personal + } +}; + +export default connect(mapStateToProps)(ViewAttestation); \ No newline at end of file diff --git a/src/images/servicelogo/index.js b/src/images/servicelogo/index.js index 0becf58f..adba8100 100644 --- a/src/images/servicelogo/index.js +++ b/src/images/servicelogo/index.js @@ -1,8 +1,9 @@ -import { VERUSID_SERVICE_ID, WYRE_SERVICE_ID } from '../../utils/constants/services' +import { VERUSID_SERVICE_ID, WYRE_SERVICE_ID, ATTESTATION_SERVICE_ID } from '../../utils/constants/services' import WYRE_LIGHT from './wyre/wyre_light.svg' import VERUSID_LIGHT from './verusid/verusid_light.svg' export default { [WYRE_SERVICE_ID]: { light: WYRE_LIGHT }, - [VERUSID_SERVICE_ID]: { light: VERUSID_LIGHT } + [VERUSID_SERVICE_ID]: { light: VERUSID_LIGHT }, + [ATTESTATION_SERVICE_ID]: { light: VERUSID_LIGHT }, } \ No newline at end of file diff --git a/src/reducers/attestations.js b/src/reducers/attestations.js new file mode 100644 index 00000000..b505d6fc --- /dev/null +++ b/src/reducers/attestations.js @@ -0,0 +1,19 @@ +import {SIGN_OUT, SET_ATTESTATION_DATA} from '../utils/constants/storeType'; + +export const attestation = ( + state = { + attestations: null + }, + action, +) => { + switch (action.type) { + case SET_ATTESTATION_DATA: + return action.data; + case SIGN_OUT: + return { + attestations: null + }; + default: + return state; + } +}; diff --git a/src/reducers/index.js b/src/reducers/index.js index 250fa43d..bfd9704f 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -4,6 +4,7 @@ import { coins } from './coins'; import { ledger } from './ledger'; import { settings } from './settings'; import { personal } from './personal'; +import { attestation } from './attestations'; import { services } from './services'; import { electrum } from './cache/electrum'; import { headers } from './cache/headers'; @@ -71,5 +72,6 @@ export default combineReducers({ sendModal, loadingModal, secureLoading, - deeplink + deeplink, + attestation }); diff --git a/src/reducers/services.js b/src/reducers/services.js index d637c2e0..ded58121 100644 --- a/src/reducers/services.js +++ b/src/reducers/services.js @@ -2,7 +2,7 @@ The personal reducer stores data used by services */ -import { VERUSID_SERVICE_ID, WYRE_SERVICE_ID, PBAAS_PRECONVERT_SERVICE_ID } from "../utils/constants/services"; +import { VERUSID_SERVICE_ID, WYRE_SERVICE_ID, PBAAS_PRECONVERT_SERVICE_ID, ATTESTATION_SERVICE_ID } from "../utils/constants/services"; import { SET_SERVICE_ACCOUNT, SIGN_OUT, @@ -24,7 +24,8 @@ export const services = ( loading: { [WYRE_SERVICE_ID]: false, [VERUSID_SERVICE_ID]: false, - [PBAAS_PRECONVERT_SERVICE_ID]: false + [PBAAS_PRECONVERT_SERVICE_ID]: false, + [ATTESTATION_SERVICE_ID]: false }, }, action, @@ -94,7 +95,8 @@ export const services = ( loading: { [WYRE_SERVICE_ID]: false, [VERUSID_SERVICE_ID]: false, - [PBAAS_PRECONVERT_SERVICE_ID]: false + [PBAAS_PRECONVERT_SERVICE_ID]: false, + [ATTESTATION_SERVICE_ID]: false }, }; default: diff --git a/src/utils/api/channels/vrpc/requests/verifyHash.js b/src/utils/api/channels/vrpc/requests/verifyHash.js new file mode 100644 index 00000000..376cdddf --- /dev/null +++ b/src/utils/api/channels/vrpc/requests/verifyHash.js @@ -0,0 +1,15 @@ +import VrpcProvider from "../../../../vrpc/vrpcInterface" + +export const verifyHash = async (systemId, iAddrOrIdentity, base64Sig, hash) => { + const provider = VrpcProvider.getVerusIdInterface(systemId); + + const identity = await provider.interface.getIdentity(iAddrOrIdentity); + + return provider.verifyHash( + iAddrOrIdentity, + base64Sig, + hash, + identity.result, + systemId + ); +} \ No newline at end of file diff --git a/src/utils/asyncStore/serviceStoredDataStorage.js b/src/utils/asyncStore/serviceStoredDataStorage.js index 09c51e13..2850d246 100644 --- a/src/utils/asyncStore/serviceStoredDataStorage.js +++ b/src/utils/asyncStore/serviceStoredDataStorage.js @@ -1,6 +1,6 @@ import AsyncStorage from "@react-native-async-storage/async-storage"; import { SERVICE_STORAGE_INTERNAL_KEY } from "../../../env/index"; -import { VERUSID_SERVICE_ID, WYRE_SERVICE_ID } from "../constants/services"; +import { VERUSID_SERVICE_ID, WYRE_SERVICE_ID, ATTESTATION_SERVICE_ID } from "../constants/services"; export const storeServiceStoredData = (data) => { if (typeof data !== "object") diff --git a/src/utils/attestations/createAttestationResponse.js b/src/utils/attestations/createAttestationResponse.js new file mode 100644 index 00000000..55382eb0 --- /dev/null +++ b/src/utils/attestations/createAttestationResponse.js @@ -0,0 +1,37 @@ +import { requestAttestationData } from "../../utils/auth/authBox"; +import { ATTESTATIONS_PROVISIONED } from "../../utils/constants/attestations"; +import * as VDXF_Data from "verus-typescript-primitives/dist/vdxf/vdxfDataKeys"; + +export const createAttestationResponse = async (attestationID, requiredKeys) => { + + const attestationData = await requestAttestationData(ATTESTATIONS_PROVISIONED); + + if (!attestationData[attestationID]) { + throw new Error(`Attestation not found in attestation data`); + } + + const attestation = attestationData[attestationID]; + const attestataionKeysToRemove = []; + const indexedDatadescriptor = {} + + for (let i = 0; i < attestation.data.length; i++) { + if (Object.keys(attestation.data[i])[0] === VDXF_Data.MMRDescriptorKey().vdxfid) { + + for (let j = 0; j < attestation.data[i][VDXF_Data.MMRDescriptorKey().vdxfid].datadescriptors.length; j++) { + + const item = attestation.data[i][VDXF_Data.MMRDescriptorKey().vdxfid].datadescriptors[j].objectdata[VDXF_Data.DataDescriptorKey().vdxfid]; + + if (requiredKeys.indexOf(item.label) === -1) { + attestataionKeysToRemove.push(j) + } else { + indexedDatadescriptor[j] = attestation.data[i][VDXF_Data.MMRDescriptorKey().vdxfid].datadescriptors[j]; + } + + } + delete attestation.data[i][VDXF_Data.MMRDescriptorKey().vdxfid].datadescriptors + attestation.data[i][VDXF_Data.MMRDescriptorKey().vdxfid].datadescriptors = indexedDatadescriptor + } + }; + + return attestation +} \ No newline at end of file diff --git a/src/utils/auth/authBox.js b/src/utils/auth/authBox.js index 037c8698..3586e03e 100644 --- a/src/utils/auth/authBox.js +++ b/src/utils/auth/authBox.js @@ -176,4 +176,29 @@ export const requestServiceStoredData = async (service) => { throw new Error("Unable to decrypt service stored data for " + service); } } -}; \ No newline at end of file +}; + +export const requestAttestationData = async (dataType) => { + const state = store.getState() + + if ( + state.authentication.activeAccount == null + ) { + throw new Error("You must be signed in to retrieve attestation data"); + } else if (state.attestation[dataType] == null) { + return {} + } else { + const password = await requestPassword() + const data = decryptkey(password, state.attestation[dataType]) + + if (data !== false) { + try { + return JSON.parse(data) + } catch(e) { + throw new Error("Unable to parse attestation data") + } + } else { + throw new Error("Unable to decrypt attestation data"); + } + } +} \ No newline at end of file diff --git a/src/utils/constants/attestations.js b/src/utils/constants/attestations.js new file mode 100644 index 00000000..337c15e5 --- /dev/null +++ b/src/utils/constants/attestations.js @@ -0,0 +1 @@ +export const ATTESTATIONS_PROVISIONED = "attestations_provisioned" \ No newline at end of file diff --git a/src/utils/constants/services.js b/src/utils/constants/services.js index ba1900b1..40177952 100644 --- a/src/utils/constants/services.js +++ b/src/utils/constants/services.js @@ -3,6 +3,7 @@ import { WYRE_SERVICE } from "./intervalConstants" export const WYRE_SERVICE_ID = 'wyre_service' export const VERUSID_SERVICE_ID = 'verusid_service' export const PBAAS_PRECONVERT_SERVICE_ID = 'pbaas_preconvert' +export const ATTESTATION_SERVICE_ID = 'attestation_service' export const CONNECTED_SERVICE_DISPLAY_INFO = { [WYRE_SERVICE_ID]: { @@ -18,6 +19,11 @@ export const CONNECTED_SERVICE_DISPLAY_INFO = { title: "Preconvert Currency", description: "Participate in a PBaaS currency launch by sending your funds to the currency before it starts", decentralized: true + }, + [ATTESTATION_SERVICE_ID]: { + title: "Attestations", + description: "Stored attestations that can be used to prove information about you to services you use", + decentralized: true } } @@ -25,7 +31,7 @@ export const CONNECTED_SERVICE_CHANNELS = { [WYRE_SERVICE_ID]: WYRE_SERVICE } -export const CONNECTED_SERVICES = [VERUSID_SERVICE_ID, /*PBAAS_PRECONVERT_SERVICE_ID ,*/ WYRE_SERVICE_ID] +export const CONNECTED_SERVICES = [VERUSID_SERVICE_ID, /*PBAAS_PRECONVERT_SERVICE_ID ,*/ WYRE_SERVICE_ID, ATTESTATION_SERVICE_ID] // Wyre specific constants export const WYRE_INDIVIDUAL_NAME = 'individualLegalName' diff --git a/src/utils/constants/storeType.js b/src/utils/constants/storeType.js index 1717a2db..445b53ff 100644 --- a/src/utils/constants/storeType.js +++ b/src/utils/constants/storeType.js @@ -309,4 +309,7 @@ export const SET_PERSONAL_DATA = "SET_PERSONAL_DATA" // Services export const SET_SERVICE_STORED_DATA = "SET_SERVICE_STORED_DATA" -export const SET_SERVICE_DATA = "SET_SERVICE_DATA" \ No newline at end of file +export const SET_SERVICE_DATA = "SET_SERVICE_DATA" + +// Attestations +export const SET_ATTESTATION_DATA = "SET_ATTESTATION_DATA" \ No newline at end of file diff --git a/src/utils/deeplink/handlePersonalDataSend.js b/src/utils/deeplink/handlePersonalDataSend.js new file mode 100644 index 00000000..404a7cd0 --- /dev/null +++ b/src/utils/deeplink/handlePersonalDataSend.js @@ -0,0 +1,30 @@ +import axios from "axios"; +import * as primitives from "verus-typescript-primitives" + + +const handlers = { + [primitives.LOGIN_CONSENT_PERSONALINFO_WEBHOOK_VDXF_KEY.vdxfid]: async (uri, response) => { + return await axios.post( + uri, + response + ); + }, + [primitives.LOGIN_CONSENT_PERSONALINFO_WEBHOOK_VDXF_KEY.vdxfid]: async (uri, response) => { + return await axios.post( + uri, + response + ); + } +} + +export const handlePersonalDataSend = (response, redirectinfo) => { + const { vdxfkey, uri } = redirectinfo + + return handlers[vdxfkey] == null ? null : handlers[vdxfkey](uri, response); +} + +export const handleAttestationDataSend = (response, redirectinfo) => { + const { vdxfkey, uri } = redirectinfo + + return handlers[vdxfkey] == null ? null : handlers[vdxfkey](uri, response); +} \ No newline at end of file diff --git a/src/utils/nativeStore/attestationDataStorage.js b/src/utils/nativeStore/attestationDataStorage.js new file mode 100644 index 00000000..9847bb10 --- /dev/null +++ b/src/utils/nativeStore/attestationDataStorage.js @@ -0,0 +1,69 @@ +var RNFS = require('react-native-fs'); +import { ATTESTATION_DATA_STORAGE_INTERNAL_KEY } from '../../../env/index' + +export const storeAttestationData = (data) => { + if (typeof data !== 'object') throw new Error(`Attestation data store function expected object, received ${typeof data}`) + + return new Promise((resolve, reject) => { + RNFS.writeFile(RNFS.DocumentDirectoryPath + `/${ATTESTATION_DATA_STORAGE_INTERNAL_KEY}.txt`, JSON.stringify(data), 'utf8') + .then((success) => { + resolve(data); + }) + .catch(err => { + reject(err) + }) + }) +}; + +export const loadAttestationData = () => { + return new Promise((resolve, reject) => { + RNFS.readFile(RNFS.DocumentDirectoryPath + `/${ATTESTATION_DATA_STORAGE_INTERNAL_KEY}.txt`, "utf8") + .then(res => { + if (!res) { + resolve({}); + } else { + _res = JSON.parse(res); + resolve(_res); + } + }) + .catch(err => { + if (err.code === 'ENOENT') { + resolve({}); + } else { + reject(err); + } + }); + }) +}; + +export const clearAllAttestationData = () => { + return new Promise((resolve, reject) => { + RNFS.unlink(RNFS.DocumentDirectoryPath + `/${ATTESTATION_DATA_STORAGE_INTERNAL_KEY}.txt`) + .then(() => { + resolve(); + }) + .catch(err => reject(err)); + }) +}; + +export const storeAttestationDataForUser = async (data, accountHash) => { + let allAttestationData = { ...(await loadAttestationData()) } + allAttestationData[accountHash] = data + return (await storeAttestationData(allAttestationData))[accountHash] +} + +export const deleteAttestationDataForUser = async (accountHash) => { + let allAttestationData = { ...(await loadAttestationData()) } + delete allAttestationData[accountHash] + return (await storeAttestationData(allAttestationData))[accountHash] +} + +export const loadAttestationDataForUser = async (accountHash) => { + const allAttestationData = await loadAttestationData() + + if (allAttestationData[accountHash] == null) + return { + attestations_provisioned: null + }; + else return allAttestationData[accountHash]; +}; diff --git a/src/utils/nativeStore/personalDataStorage.js b/src/utils/nativeStore/personalDataStorage.js new file mode 100644 index 00000000..1f220b9e --- /dev/null +++ b/src/utils/nativeStore/personalDataStorage.js @@ -0,0 +1,67 @@ +var RNFS = require('react-native-fs'); +import { PERSONAL_DATA_STORAGE_INTERNAL_KEY } from '../../../env/index' + +export const storePersonalData = (data) => { + if (typeof data !== 'object') throw new Error(`Personal data store function expected object, received ${typeof data}`) + + return new Promise((resolve, reject) => { + RNFS.writeFile(RNFS.DocumentDirectoryPath + `/${PERSONAL_DATA_STORAGE_INTERNAL_KEY}.txt`, JSON.stringify(data), 'utf8') + .then((success) => { + resolve(data); + }) + .catch(err => { + reject(err) + }) + }) +}; + +export const loadPersonalData = () => { + return new Promise((resolve, reject) => { + RNFS.readFile(RNFS.DocumentDirectoryPath + `/${PERSONAL_DATA_STORAGE_INTERNAL_KEY}.txt`, "utf8") + .then(res => { + if (!res) { + resolve({}); + } else { + _res = JSON.parse(res); + resolve(_res); + } + }) + .catch(err => reject(err)); + }) +}; + +export const clearAllPersonalData = () => { + return new Promise((resolve, reject) => { + RNFS.unlink(RNFS.DocumentDirectoryPath + `/${PERSONAL_DATA_STORAGE_INTERNAL_KEY}.txt`) + .then(() => { + resolve(); + }) + .catch(err => reject(err)); + }) +}; + +export const storePersonalDataForUser = async (data, accountHash) => { + let allPersonalData = { ...(await loadPersonalData()) } + allPersonalData[accountHash] = data + return (await storePersonalData(allPersonalData))[accountHash] +} + +export const deletePersonalDataForUser = async (accountHash) => { + let allPersonalData = { ...(await loadPersonalData()) } + delete allPersonalData[accountHash] + return (await storePersonalData(allPersonalData))[accountHash] +} + +export const loadPersonalDataForUser = async (accountHash) => { + const allPersonalData = await loadPersonalData() + + if (allPersonalData[accountHash] == null) + return { + attributes: null, + contact: null, + locations: null, + payment_methods: null, + images: null, + }; + else return allPersonalData[accountHash]; +}; diff --git a/src/utils/personal/displayUtils.js b/src/utils/personal/displayUtils.js index 610d406f..d0606680 100644 --- a/src/utils/personal/displayUtils.js +++ b/src/utils/personal/displayUtils.js @@ -8,27 +8,47 @@ import { } from "../constants/personal"; import { Platform } from 'react-native'; var RNFS = require('react-native-fs'); +import { primitives } from "verusid-ts-client" +import { requestPersonalData } from "../auth/authBox"; +import { + PERSONAL_ATTRIBUTES, + PERSONAL_CONTACT, + PERSONAL_LOCATIONS, + PERSONAL_PAYMENT_METHODS, + PERSONAL_IMAGES, +} from "../constants/personal"; + +import { IdentityVdxfidMap } from 'verus-typescript-primitives/dist/vdxf/classes/IdentityData'; +import Colors from '../../globals/colors'; + +const { IDENTITYDATA_FIRSTNAME, IDENTITYDATA_LASTNAME, IDENTITYDATA_MIDDLENAME, + BANK_ACCOUNT_COUNTRY, BANK_ACCOUNT_CURRENCY, BANK_ACCOUNT_NUMBER, BANK_ACCOUNT_TYPE } = primitives; +const { IDENTITYDATA_HOMEADDRESS_STREET1, IDENTITYDATA_HOMEADDRESS_STREET2, IDENTITYDATA_HOMEADDRESS_CITY, IDENTITYDATA_HOMEADDRESS_REGION, IDENTITYDATA_HOMEADDRESS_POSTCODE, IDENTITYDATA_HOMEADDRESS_COUNTRY } = primitives; +const { IDENTITYDATA_CONTACT, IDENTITYDATA_PERSONAL_DETAILS, IDENTITYDATA_LOCATIONS, IDENTITYDATA_DOCUMENTS_AND_IMAGES, IDENTITYDATA_BANKING_INFORMATION } = primitives; + + +export const renderPersonalFullName = (state) => { + + if (!state[IDENTITYDATA_FIRSTNAME.vdxfid] && !state[IDENTITYDATA_LASTNAME.vdxfid]) { + return {title: "John Doe"} + } -export const renderPersonalFullName = (name) => { return { - title: `${name.first} ${ - name.middle != null && name.middle.length > 0 ? name.middle + " " : "" - }${name.last}` + title: `${state[IDENTITYDATA_FIRSTNAME.vdxfid] || ""} ${state[IDENTITYDATA_MIDDLENAME.vdxfid] != null && state[IDENTITYDATA_MIDDLENAME.vdxfid] > 0 ? state[IDENTITYDATA_MIDDLENAME.vdxfid] + " " : "" + }${state[IDENTITYDATA_LASTNAME.vdxfid] || ""}` }; }; export const renderPersonalPhoneNumber = (phone, includeEmoji = true) => { return { - title: `${ - includeEmoji && + title: `${includeEmoji && CALLING_CODES_TO_ISO_3166[phone.calling_code] != null && ISO_3166_COUNTRIES[CALLING_CODES_TO_ISO_3166[phone.calling_code]] != null - ? ISO_3166_COUNTRIES[CALLING_CODES_TO_ISO_3166[phone.calling_code]] - .emoji + " " - : "" - }${phone.calling_code.length > 0 ? phone.calling_code : "+0"} ${ - phone.number.length > 0 ? phone.number : "000000000" - }`, + ? ISO_3166_COUNTRIES[CALLING_CODES_TO_ISO_3166[phone.calling_code]] + .emoji + " " + : "" + }${phone.calling_code.length > 0 ? phone.calling_code : "+0"} ${phone.number.length > 0 ? phone.number : "000000000" + }`, }; }; @@ -74,14 +94,13 @@ export const renderPersonalDocument = (document) => { document.image_type == null ? 'Document' : PERSONAL_IMAGE_TYPE_SCHEMA[document.image_type] == null - ? "??" - : `${PERSONAL_IMAGE_TYPE_SCHEMA[document.image_type].title}${ - document.image_subtype == null || + ? "??" + : `${PERSONAL_IMAGE_TYPE_SCHEMA[document.image_type].title}${document.image_subtype == null || PERSONAL_IMAGE_SUBTYPE_SCHEMA[document.image_subtype] == null - ? "" - : ` (${PERSONAL_IMAGE_SUBTYPE_SCHEMA[ - document.image_subtype - ].title.toLowerCase()})` + ? "" + : ` (${PERSONAL_IMAGE_SUBTYPE_SCHEMA[ + document.image_subtype + ].title.toLowerCase()})` }`, }; }; @@ -102,37 +121,121 @@ export const renderPersonalTaxId = (taxCountry) => { export const renderPersonalAddress = (address) => { return { title: - address.street1.length > 0 - ? `${address.street1}${ - address.street2 != null && address.street2.length > 0 ? `, ${address.street2}` : "" - }` + address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid].length > 0 + ? `${address[IDENTITYDATA_HOMEADDRESS_STREET1.vdxfid]}${address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid] != null && address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid].length > 0 ? `, ${address[IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid]}` : "" + }` : "Empty address", - description: `${address.postal_code.length > 0 ? `${address.postal_code} ` : ""}${ - address.state_province_region.length > 0 ? `${address.state_province_region}, ` : "" - }${address.city.length > 0 ? `${address.city}, ` : "Unknown City, "}${ - ISO_3166_COUNTRIES[address.country] != null - ? `${ISO_3166_COUNTRIES[address.country].emoji} ${ISO_3166_COUNTRIES[address.country].name}` + description: `${address[IDENTITYDATA_HOMEADDRESS_POSTCODE.vdxfid].length > 0 ? `${address[IDENTITYDATA_HOMEADDRESS_POSTCODE.vdxfid]} ` : ""}${address[IDENTITYDATA_HOMEADDRESS_REGION.vdxfid]?.length > 0 ? `${address[IDENTITYDATA_HOMEADDRESS_REGION.vdxfid]}, ` : "" + }${address[IDENTITYDATA_HOMEADDRESS_CITY.vdxfid]?.length > 0 ? `${address[IDENTITYDATA_HOMEADDRESS_CITY.vdxfid]}, ` : "Unknown City, "}${ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]] != null + ? `${ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]].emoji} ${ISO_3166_COUNTRIES[address[IDENTITYDATA_HOMEADDRESS_COUNTRY.vdxfid]].name}` : "Unknown Country" - }`, + }`, }; } export const renderPersonalBankAccount = (account) => { - const accountLocaleString = ISO_3166_COUNTRIES[account.country] - ? `${ISO_3166_COUNTRIES[account.country].emoji} Account` + const accountLocaleString = ISO_3166_COUNTRIES[account[BANK_ACCOUNT_COUNTRY.vdxfid]] + ? `${ISO_3166_COUNTRIES[account[BANK_ACCOUNT_COUNTRY.vdxfid]].emoji} Account` : "Bank Account"; const accountNumberString = - account.account_number != null && account.account_number.length > 4 - ? ` ending in ${account.account_number.slice(-4)}` + account[BANK_ACCOUNT_NUMBER.vdxfid] != null && account[BANK_ACCOUNT_NUMBER.vdxfid].length > 4 + ? ` ending in ${account[BANK_ACCOUNT_NUMBER.vdxfid].slice(-4)}` : ""; - const accountDescription = `${ - account.primary_currency != null && account.primary_currency.length > 0 - ? account.primary_currency + " " - : "" - }${account.account_type}`; + const accountDescription = `${account[BANK_ACCOUNT_CURRENCY.vdxfid] != null && account[BANK_ACCOUNT_CURRENCY.vdxfid].length > 0 + ? account[BANK_ACCOUNT_CURRENCY.vdxfid] + " " + : "" + }${account[BANK_ACCOUNT_TYPE.vdxfid]}`; return { title: `${accountLocaleString}${accountNumberString}`, description: accountDescription, }; -}; \ No newline at end of file +}; + +/********************************/ +// template defaultPersonalProfileDataTemplate in the order: +// new PersonalDataCategory(), +// new ContactDataCategory(), +// new LocationDataCategory(), +// new BankingDataCategory(), +// new DocumentsCategory() +/********************************/ + +export const checkPersonalDataCatagories = async (profileDataRequested = []) => { + let success = true; + await Promise.all(Object.keys(profileDataRequested).map(async (permission) => { + let errorDetails = ""; + let profiletype; + let optionalKeys = {} + let attributes = {}; + switch (permission) { + + case IDENTITYDATA_PERSONAL_DETAILS.vdxfid: + attributes = await requestPersonalData(PERSONAL_ATTRIBUTES); + optionalKeys = { [primitives.IDENTITYDATA_MIDDLENAME.vdxfid]: true }; + profiletype = primitives.defaultPersonalProfileDataTemplate[0].data; + break; + case IDENTITYDATA_CONTACT.vdxfid: + attributes = await requestPersonalData(PERSONAL_CONTACT); + profiletype = primitives.defaultPersonalProfileDataTemplate[1].data; + break; + case IDENTITYDATA_LOCATIONS.vdxfid: + const locationReply = await requestPersonalData(PERSONAL_LOCATIONS); + attributes = locationReply.physical_addresses && locationReply.physical_addresses.length > 0 ? locationReply.physical_addresses[0] : {}; + optionalKeys = { [primitives.IDENTITYDATA_HOMEADDRESS_STREET2.vdxfid]: true }; + profiletype = primitives.defaultPersonalProfileDataTemplate[2].data; + break; + case IDENTITYDATA_BANKING_INFORMATION.vdxfid: + const bankRetval = await checkBankAccountPresent(); + attributes = bankRetval.attributes; + profiletype = bankRetval.profiletype; + break; + case IDENTITYDATA_DOCUMENTS_AND_IMAGES.vdxfid: + const retval = await checkDocumentsPresent(); + attributes = retval.attributes; + profiletype = retval.profiletype; + break; + } + + profiletype.forEach((templateCategory) => { + const one = attributes[templateCategory.vdxfkey]; + if (!optionalKeys[templateCategory.vdxfkey] && ((typeof one === 'object' && Array.isArray(one) && one.length === 0) || + (typeof one === 'object' && Object.keys(one).length === 0) || + (typeof one === 'string' && one.length === 0) + || one == undefined)) { + errorDetails += (errorDetails ? ", " : "") +`${IdentityVdxfidMap[templateCategory.vdxfkey]?.name || templateCategory.vdxfkey}`; + } + }) + + if (errorDetails.length > 0) { + profileDataRequested[permission].details = "Missing Information: " + errorDetails; + profileDataRequested[permission].color = Colors.warningButtonColor; + success = false; + } + + })); + return success; +} + +export const checkBankAccountPresent = async () => { + + const paymentMethods = await requestPersonalData(PERSONAL_PAYMENT_METHODS); + + if (!paymentMethods.bank_accounts || paymentMethods.bank_accounts.length === 0) { + return { profiletype: [{ vdxfkey: primitives.BANK_ACCOUNT.vdxfid }], attributes: { [primitives.BANK_ACCOUNT.vdxfid]: "" } }; + } + return { profiletype: [{ vdxfkey: primitives.BANK_ACCOUNT.vdxfid }], attributes: { [primitives.BANK_ACCOUNT.vdxfid]: "OK" } }; +} + +export const checkDocumentsPresent = async () => { + + const images = await requestPersonalData(PERSONAL_IMAGES); + + if (!images.documents || images.documents.length === 0) { + return { profiletype: [{ vdxfkey: primitives.IDENTITYDATA_DOCUMENTS_AND_IMAGES.vdxfid }], attributes: { [primitives.IDENTITYDATA_DOCUMENTS_AND_IMAGES.vdxfid]: "" }}; + } + return { profiletype: [{ vdxfkey: primitives.IDENTITYDATA_DOCUMENTS_AND_IMAGES.vdxfid }], attributes: { [primitives.IDENTITYDATA_DOCUMENTS_AND_IMAGES.vdxfid]: "OK" }}; + +} + +export const checkPersonalDataKeys = () => { } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 16b1187b..4bd255bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -946,7 +946,7 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@bitgo/blake2b-wasm@^3.0.1", "@bitgo/blake2b-wasm@https://github.com/michaeltout/blake2b-wasm/", "blake2b-wasm@https://github.com/BitGo/blake2b-wasm#193cdb71656c1a6c7f89b05d0327bb9b758d071b", "blake2b-wasm@https://github.com/michaeltout/blake2b-wasm/": +"@bitgo/blake2b-wasm@^3.0.1", "@bitgo/blake2b-wasm@https://github.com/michaeltout/blake2b-wasm/": version "2.0.0" resolved "https://github.com/michaeltout/blake2b-wasm/#a16070b30bb8d8aee670f0c69b642913eca79998" dependencies: @@ -960,7 +960,7 @@ "@bitgo/blake2b-wasm" "^3.0.1" nanoassert "^2.0.0" -"@bitgo/utxo-lib@git+https://github.com/VerusCoin/BitGoJS.git#7c754d4a5920198d9fe6827d3e23bd5cf431f264": +"@bitgo/utxo-lib@git+https://github.com/VerusCoin/BitGoJS.git#7c754d4a5920198d9fe6827d3e23bd5cf431f264", "@bitgo/utxo-lib@https://github.com/VerusCoin/BitGoJS.git#f8723bcc2bf4052de171ca8654de23fc06a03337": version "1.9.6" resolved "git+https://github.com/VerusCoin/BitGoJS.git#7c754d4a5920198d9fe6827d3e23bd5cf431f264" dependencies: @@ -986,31 +986,6 @@ optionalDependencies: secp256k1 "3.5.2" -"@bitgo/utxo-lib@https://github.com/VerusCoin/BitGoJS.git#f8723bcc2bf4052de171ca8654de23fc06a03337": - version "1.9.6" - resolved "https://github.com/VerusCoin/BitGoJS.git#f8723bcc2bf4052de171ca8654de23fc06a03337" - dependencies: - "@bitgo/blake2b" "3.0.1" - bech32 "0.0.3" - bigi "1.4.0" - bip32 "2.0.6" - bip66 "1.1.0" - bitcoin-ops "git+https://github.com/VerusCoin/bitcoin-ops" - bs58check "2.0.0" - create-hash "1.1.0" - create-hmac "1.1.3" - debug "~3.1.0" - ecurve "1.0.0" - merkle-lib "2.0.10" - pushdata-bitcoin "1.0.1" - randombytes "2.0.1" - safe-buffer "5.0.1" - typeforce "1.11.3" - varuint-bitcoin "1.0.4" - wif "2.0.1" - optionalDependencies: - secp256k1 "3.5.2" - "@callstack/react-theme-provider@^3.0.5": version "3.0.8" resolved "https://registry.yarnpkg.com/@callstack/react-theme-provider/-/react-theme-provider-3.0.8.tgz#d0d7ac71e422133c5f7b78f4c4aa1bc57f156f50" @@ -3367,6 +3342,12 @@ bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" +blake2b-wasm@^2.4.0, "blake2b-wasm@https://github.com/BitGo/blake2b-wasm#193cdb71656c1a6c7f89b05d0327bb9b758d071b", "blake2b-wasm@https://github.com/michaeltout/blake2b-wasm/": + version "2.0.0" + resolved "https://github.com/michaeltout/blake2b-wasm/#a16070b30bb8d8aee670f0c69b642913eca79998" + dependencies: + nanoassert "1.0.0" + "blake2b@git+https://github.com/michaeltout/blake2b": version "2.1.3" resolved "git+https://github.com/michaeltout/blake2b#777f844b986173d08fc27fe9547c6f6320a82d62" @@ -3374,6 +3355,13 @@ bl@^4.1.0: blake2b-wasm "https://github.com/BitGo/blake2b-wasm#193cdb71656c1a6c7f89b05d0327bb9b758d071b" nanoassert "^1.0.0" +"blake2b@https://github.com/VerusCoin/blake2b": + version "2.1.4" + resolved "https://github.com/VerusCoin/blake2b#578dc12b7b68a3ce76d1e1add1402b28a5890a6b" + dependencies: + blake2b-wasm "^2.4.0" + nanoassert "^2.0.0" + bn.js@5.2.1, bn.js@^5.0.0, bn.js@^5.2.0, bn.js@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" @@ -11557,21 +11545,22 @@ version_compare@0.0.3: bignumber.js "^9.0.0" verus-zkedid-utils "https://github.com/michaeltout/verus-zkedid-utils#dev" -"verus-typescript-primitives@git+https://github.com/VerusCoin/verus-typescript-primitives.git": - version "1.0.0" - resolved "git+https://github.com/VerusCoin/verus-typescript-primitives.git#b130230b0ba8f330972ea9466a7b152c1690dd75" +"verus-typescript-primitives@file:../../verus-typescript-primitives": + version "1.0.1" dependencies: base64url "3.0.1" bech32 "2.0.0" + blake2b "https://github.com/VerusCoin/blake2b" bn.js "5.2.1" bs58check "https://github.com/bitcoinjs/bs58check" create-hash "1.2.0" -"verus-typescript-primitives@https://github.com/VerusCoin/verus-typescript-primitives.git": - version "1.0.0" - resolved "https://github.com/VerusCoin/verus-typescript-primitives.git#ca735cf46b0dd4ad4a6d4b37ab451bd2f5af4c3e" +"verus-typescript-primitives@file:d:/dev/verus-typescript-primitives", "verus-typescript-primitives@https://github.com/VerusCoin/verus-typescript-primitives.git": + version "1.0.1" dependencies: base64url "3.0.1" + bech32 "2.0.0" + blake2b "https://github.com/VerusCoin/blake2b" bn.js "5.2.1" bs58check "https://github.com/bitcoinjs/bs58check" create-hash "1.2.0" @@ -11593,7 +11582,7 @@ version_compare@0.0.3: "verusd-rpc-ts-client@https://github.com/VerusCoin/verusd-rpc-ts-client.git": version "0.1.0" - resolved "https://github.com/VerusCoin/verusd-rpc-ts-client.git#c12448144559b8ffecf58630346a97cdb5356dde" + resolved "https://github.com/VerusCoin/verusd-rpc-ts-client.git#62d260563c23ecf9b3333355e8596191c764e415" dependencies: axios "1.6.5" verus-typescript-primitives "https://github.com/VerusCoin/verus-typescript-primitives.git"