diff --git a/package-lock.json b/package-lock.json index b2fa08760..7a4f1f312 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@fortawesome/free-solid-svg-icons": "6.4.0", "@fortawesome/react-native-fontawesome": "0.2.7", "@hathor/unleash-client": "0.1.0", - "@hathor/wallet-lib": "1.9.0", + "@hathor/wallet-lib": "1.10.0", "@notifee/react-native": "5.7.0", "@react-native-async-storage/async-storage": "1.19.0", "@react-native-firebase/app": "16.7.0", @@ -2550,9 +2550,9 @@ } }, "node_modules/@hathor/wallet-lib": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@hathor/wallet-lib/-/wallet-lib-1.9.0.tgz", - "integrity": "sha512-8m6sr/PObnRoCsmalV2AqKO+tMPyzI/Lw/eJ0KgtaZSn7drBeFlDRhYyEilxpFqq9BCzvx18i8tXAudFoW2OUQ==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@hathor/wallet-lib/-/wallet-lib-1.10.0.tgz", + "integrity": "sha512-oxQoxnwZNDrgjnvblx0ipVOxMNeM8W2PJkQpyzZJyl1QpRyhqHqqyYD0iSn1bhQEq8C1IRoSrvjsd3Af5eHdWQ==", "license": "MIT", "dependencies": { "abstract-level": "1.0.4", diff --git a/package.json b/package.json index fc695b30a..ad37671ec 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "@fortawesome/free-solid-svg-icons": "6.4.0", "@fortawesome/react-native-fontawesome": "0.2.7", "@hathor/unleash-client": "0.1.0", - "@hathor/wallet-lib": "1.9.0", + "@hathor/wallet-lib": "1.10.0", "@notifee/react-native": "5.7.0", "@react-native-async-storage/async-storage": "1.19.0", "@react-native-firebase/app": "16.7.0", diff --git a/src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js b/src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js index 1c7b945d8..818fbf3ca 100644 --- a/src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js +++ b/src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js @@ -14,12 +14,14 @@ import { import { useDispatch, useSelector } from 'react-redux'; import { t } from 'ttag'; import { get } from 'lodash'; +import { Network } from '@hathor/wallet-lib'; import { COLORS } from '../../../styles/themes'; import { commonStyles } from '../theme'; import { onExceptionCaptured } from '../../../actions'; -import { NANOCONTRACT_BLUEPRINTINFO_STATUS as STATUS } from '../../../constants'; +import { DEFAULT_TOKEN, NANOCONTRACT_BLUEPRINTINFO_STATUS as STATUS } from '../../../constants'; import { FeedbackContent } from '../../FeedbackContent'; import Spinner from '../../Spinner'; +import { getTimestampFormat, parseScriptData, renderValue } from '../../../utils'; /** * Get method info from registered blueprint data. @@ -63,10 +65,16 @@ export const NanoContractMethodArgs = ({ blueprintId, method, ncArgs }) => { return null; } const dispatch = useDispatch(); + const network = useSelector((state) => new Network(state.networkSettings.network)); + const tokens = useSelector((state) => state.tokens); const blueprintInfo = useSelector((state) => state.nanoContract.blueprint[blueprintId]); // It results a in a list of entries like: - // >>> [['oracle_script', 'abc'], ['token_uid', '00'], ['date_last_bet', 123]] + // >>>[ + // >>> ['oracle_script', 'abc', 'TxOutputScript'], + // >>> ['token_uid', '00', 'TokenUid'], + // >>> ['date_last_bet', 123, 'Timestamp'] + // >>>] // or a fallback like: // >>> [['Position 0', 'abc'], ['Position 1', '00'], ['Position 2', 123]] const argEntries = useMemo(() => { @@ -76,7 +84,7 @@ export const NanoContractMethodArgs = ({ blueprintId, method, ncArgs }) => { const methodInfo = getMethodInfoFromBlueprint(blueprintInfo, method); if (methodInfo) { - return ncArgs.map((arg, idx) => [methodInfo.args[idx].name, arg]); + return ncArgs.map((arg, idx) => [methodInfo.args[idx].name, arg, methodInfo.args[idx].type]); } // Send this condition to sentry because it should never happen. @@ -109,7 +117,7 @@ export const NanoContractMethodArgs = ({ blueprintId, method, ncArgs }) => { && ( - {argEntries.map(([argName, argValue]) => ( + {argEntries.map(([argName, argValue, argType]) => ( { {argName} - {argValue} + + + ))} @@ -129,6 +144,60 @@ export const NanoContractMethodArgs = ({ blueprintId, method, ncArgs }) => { ) }; +/** + * Component responsible to render the appropriate format for the value + * taking in consideration the type. + * + * Remarks + * The values received here when derived from 'byte' type like + * 'TxOutputScript', 'TokenUid' and 'VertexId' are already in their + * hexadecimal format. + * + * Values of type 'Address', which also derives from 'byte' are + * in base58 format. + * + * Values of type 'SignedData[Result]' arrives here in presentation + * format. + * + * @param {Object} props + * @param {string} props.type An argument type + * @param {string} props.value An argument value + * @param {Object} props.network A network object + * @param {Object} props.tokens A map of registered tokens + */ +const ArgValue = ({ type, value, network, tokens }) => { + if (type === 'Amount') { + return renderValue(value); + } + + if (type === 'Timestamp') { + return getTimestampFormat(value); + } + + if (type === 'TxOutputScript') { + const parsedScript = parseScriptData(value, network); + if (parsedScript && parsedScript.getType() === 'data') { + return parsedScript.data; + } + + if (parsedScript) { + return parsedScript.address.base58; + } + } + + if (type === 'TokenUid') { + if (value === DEFAULT_TOKEN.uid) { + return DEFAULT_TOKEN.symbol; + } + + if (value in tokens) { + return tokens[value].symbol; + } + } + + return value; +}; + const styles = StyleSheet.create({ argPosition: { flexShrink: 10, diff --git a/src/utils.js b/src/utils.js index 52a9a4230..51c316f97 100644 --- a/src/utils.js +++ b/src/utils.js @@ -18,6 +18,9 @@ import { KEYCHAIN_USER, NETWORK_MAINNET, NANO_CONTRACT_FEATURE_TOGGLE } from './ import { STORE } from './store'; import { TxHistory } from './models'; import { COLORS, STYLE } from './styles/themes'; +import { logger } from './logger'; + +const log = logger('utils'); export const Strong = (props) => {props.children}; @@ -497,3 +500,30 @@ export function hasError(invalidModel) { .values({ ...invalidModel }) .reduce((_hasError, currValue) => _hasError || !isEmpty(currValue), false); } + +/** + * Parses a script data to return an instance of script type. + * + * @example + * parseScriptData('P2PKH or P2SH script', networkObj); + * >>> { address, timelock } + * + * @example + * parseScriptData('Data script', networkObj); + * >>> { data } + * + * @param {string} scriptData A script in its hexadecimal format + * @param {Object} network A network object + * + * @return {P2PKH | P2SH | ScriptData | null} Parsed script object + */ +export const parseScriptData = (scriptData, network) => { + try { + const script = hathorLib.bufferUtils.hexToBuffer(scriptData); + return hathorLib.scriptsUtils.parseScript(script, network); + } catch (error) { + log.error('Error parsing script data.', error); + // Avoid throwing exception when we can't parse the script no matter the reason + return null; + } +}