diff --git a/src/components/PageContainer/PageContainer.tsx b/src/components/PageContainer/PageContainer.tsx index 7d8f2675..7b9cd52d 100644 --- a/src/components/PageContainer/PageContainer.tsx +++ b/src/components/PageContainer/PageContainer.tsx @@ -40,11 +40,13 @@ export const PageContainer = ({ keyboardShouldPersistTaps="handled" noVerticalPadding={noVerticalPadding} > - {!header || headerHeight ? children : null} - {footerButton && } - {bottom > 0 && !noBottomMargin && !noVerticalPadding && ( - - )} + + {!header || headerHeight ? children : null} + {footerButton && } + {bottom > 0 && !noBottomMargin && !noVerticalPadding && ( + + )} + {footerButton && ( { onPress: handleSubmit(onSubmit) }} > + { getFieldState={getFieldState} currency={currency} /> + ); }; diff --git a/src/screens/Pos/Pos.tsx b/src/screens/Pos/Pos.tsx index 5021b122..e22bfef0 100644 --- a/src/screens/Pos/Pos.tsx +++ b/src/screens/Pos/Pos.tsx @@ -9,6 +9,7 @@ import { useRef, useState } from "react"; +import { View } from "react-native"; import { getFormattedUnit, AsyncStorage, @@ -134,7 +135,7 @@ export const Pos = () => { const { unitDecimals, unitDecimalPower } = useMemo(() => { const _unitDecimals = currencies.find((c) => c.value === unit)?.decimals ?? DEFAULT_DECIMALS; - const _unitDecimalPower = getUnitDecimalPower(unit); + const _unitDecimalPower = getUnitDecimalPower(unit || "USD"); return { unitDecimals: _unitDecimals, unitDecimalPower: _unitDecimalPower }; }, [unit]); @@ -159,7 +160,7 @@ export const Pos = () => { const requestInvoice = useCallback(async () => { await postInvoice({ amount: decimalFiat, - unit, + unit: unit || "USD", description, deviceName, deviceType @@ -212,20 +213,20 @@ export const Pos = () => { const [decimalCount, setDecimalCount] = useState(0); const [parts, springs, animateAmount] = useAnimateAmount({ - unit, + unit: unit || "USD", initialParts: initialValue, decimalCount }); const [plusParts, plusSprings, animatePlusAmount, setPlusParts] = useAnimateAmount({ - unit, + unit: unit || "USD", mode: AnimationMode.Plus, animationDelay: PLUS_ANIMATION_DELAY }); const [totalParts, totalSprings, animateTotalAmount] = useAnimateAmount({ - unit, + unit: unit || "USD", mode: AnimationMode.Plus }); @@ -368,12 +369,12 @@ export const Pos = () => { unitDecimals ]); + const [symbolsProps, symbolsApi] = useSymbolApi(); + const [movingPlusProps, movingPlusApi] = useSpring(() => ({ from: { top: 0, scale: 1, color: colors.white, opacity: 1 } })); - const [symbolsProps, symbolsApi] = useSymbolApi(); - const onPlus = useCallback(async () => { const newPlusParts = parts.filter((p) => !p.remove); const currentParts = newPlusParts.map((e) => e.text).join(""); @@ -388,7 +389,7 @@ export const Pos = () => { }, delay: springAnimationDelay, config: PLUS_ANIMATION_CONFIG - })); + }) as any); if (plusFiatAmount > 0) { await movingPlusApi.start(() => ({ @@ -464,9 +465,9 @@ export const Pos = () => { }, [handleKeyPress]); const registerRef = useCallback( - (index: number) => (ref: TouchableOpacity) => - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - (inputRef.current[index] = ref), + (index: number) => (ref: any) => { + inputRef.current[index] = ref; + }, [] ); @@ -567,6 +568,7 @@ export const Pos = () => { noPadding noBottomMargin > + {deviceNameModal} {isAtm && ( @@ -620,7 +622,7 @@ export const Pos = () => { {totalPartsComponents} - + { {rowValue.map((columnValue, columnIndex) => ( { /> + ) : ( diff --git a/src/screens/Pos/components/NumberInput/NumberInput.tsx b/src/screens/Pos/components/NumberInput/NumberInput.tsx index 3ceedd0d..44bbd89f 100644 --- a/src/screens/Pos/components/NumberInput/NumberInput.tsx +++ b/src/screens/Pos/components/NumberInput/NumberInput.tsx @@ -21,7 +21,7 @@ type NumberInputProps = { paddingBottom?: number; }; -export const NumberInput = forwardRef( +export const NumberInput = forwardRef( ( { value, diff --git a/src/screens/QRScanner/QRScanner.tsx b/src/screens/QRScanner/QRScanner.tsx index eed34542..a76d83aa 100644 --- a/src/screens/QRScanner/QRScanner.tsx +++ b/src/screens/QRScanner/QRScanner.tsx @@ -1,4 +1,5 @@ import { useCallback, useMemo, useState } from "react"; +import { View } from "react-native"; import { useWindowDimensions } from "react-native"; import { useLocation, useNavigate } from "@components/Router"; import { Image, Loader, PageContainer } from "@components"; @@ -99,6 +100,7 @@ export const QRScanner = () => { /> )} + {!isCameraLoading && !isLoading ? ( <> @@ -132,6 +134,7 @@ export const QRScanner = () => { ) : ( )} + ); diff --git a/src/screens/Settings/Settings.tsx b/src/screens/Settings/Settings.tsx index 7af8c9dd..73450845 100644 --- a/src/screens/Settings/Settings.tsx +++ b/src/screens/Settings/Settings.tsx @@ -1,4 +1,5 @@ import { useCallback, useContext, useEffect, useMemo, useState } from "react"; +import { View } from "react-native"; import { SBPContext, apiRootUrl, @@ -272,6 +273,7 @@ export const Settings = () => { left: { icon: faArrowLeft, onPress: -1 } }} > + {accountConfig ? ( !accountConfig.isAtm && @@ -459,6 +461,7 @@ export const Settings = () => { {versionTag} + ); }; diff --git a/src/screens/SignatureLogin/SignatureLogin.tsx b/src/screens/SignatureLogin/SignatureLogin.tsx index 31f3799e..7cff840b 100644 --- a/src/screens/SignatureLogin/SignatureLogin.tsx +++ b/src/screens/SignatureLogin/SignatureLogin.tsx @@ -1,4 +1,5 @@ import { useCallback, useContext, useRef, useState } from "react"; +import { View } from "react-native"; import { useTranslation } from "react-i18next"; import { SBPContext, apiRootUrl, platform } from "@config"; import { Controller, SubmitHandler, useForm } from "react-hook-form"; @@ -216,7 +217,13 @@ export const SignatureLogin = () => { signature, zPub, words: words.join(" "), - walletType: "local" + walletType: "local", + walletConfig: { + account: "local", + label: "Local Wallet", + type: "local", + zpub: zPub + } }); } catch (e) { if (isApiError(e)) { @@ -296,7 +303,8 @@ export const SignatureLogin = () => { signature: data.signature, zPub: data.zPub, words: data.words, - walletType: data.walletType + walletType: data.walletType, + walletConfig: data.walletConfig }); } }, @@ -347,6 +355,7 @@ export const SignatureLogin = () => { title: tRoot("common.login") }} > + { + ); }; diff --git a/src/screens/Signup/Signup.tsx b/src/screens/Signup/Signup.tsx index 6332cc92..a95f761d 100644 --- a/src/screens/Signup/Signup.tsx +++ b/src/screens/Signup/Signup.tsx @@ -1,4 +1,5 @@ import { useCallback, useContext, useEffect, useMemo, useState } from "react"; +import { View } from "react-native"; import { faArrowLeft, faAt, @@ -490,6 +491,7 @@ export const Signup = () => { onPress: handleSubmit(onSubmit) }} > + { /> + ); }; diff --git a/src/screens/Wallet/Wallet.tsx b/src/screens/Wallet/Wallet.tsx index 5dd450f1..ceaca653 100644 --- a/src/screens/Wallet/Wallet.tsx +++ b/src/screens/Wallet/Wallet.tsx @@ -1,4 +1,5 @@ import { useCallback, useEffect, useMemo, useState } from "react"; +import { View } from "react-native"; import { useTranslation } from "react-i18next"; import { Button, @@ -214,7 +215,9 @@ export const Wallet = () => { onClose={onSendModalClose} zPub={zPub} currentBalance={balance / 100000000} - /> + > + <> + )} { } : {})} > + @@ -311,6 +315,7 @@ export const Wallet = () => { })} /> + ); diff --git a/src/screens/Wallet/components/SendModal/SendModal.tsx b/src/screens/Wallet/components/SendModal/SendModal.tsx index e1399ad3..0857416a 100644 --- a/src/screens/Wallet/components/SendModal/SendModal.tsx +++ b/src/screens/Wallet/components/SendModal/SendModal.tsx @@ -42,7 +42,7 @@ import { import { Controller, SubmitHandler, useForm } from "react-hook-form"; import { useToast } from "react-native-toast-notifications"; import { HardwareReadyFunctionParams } from "@components/ConnectWalletModal/ConnectWalletModal"; -import { PrepareTransactionParams } from "@utils/wallet/prepare-transaction"; +import { LocalPrepareTransactionParams } from "@utils/wallet/prepare-transaction"; import { Platform } from "react-native"; import { faBluetooth, faUsb } from "@fortawesome/free-brands-svg-icons"; import { WalletType } from "@components/PayoutConfig/PayoutConfig"; @@ -121,7 +121,8 @@ export const SendModal = ({ useForm({ mode: "onTouched", defaultValues: { - isMax: false + address: "", + feeRate: "1" } }); @@ -142,10 +143,9 @@ export const SendModal = ({ useEffect(() => { (async () => { setWallet({ - type: - walletType || + type: (walletType || (await AsyncStorage.getItem(keyStoreWalletType)) || - "local", + "local") as WalletType, transport: (await AsyncStorage.getItem(keyStoreLedgerBluetoothId)) ? "bluetooth" : undefined @@ -156,13 +156,13 @@ export const SendModal = ({ const askPassword = useAskPassword(); const awaitWalletTransaction = useCallback( - async (params: Omit) => { + async (params: Omit) => { return new Promise((resolver, reject) => { try { const walletType = wallet?.type; if (walletType === "local") { prepareTransaction({ - walletType, + walletType: walletType as string, askWordsPassword: askPassword, ...params }) @@ -173,14 +173,19 @@ export const SendModal = ({ () => async (walletReadyProps: HardwareReadyFunctionParams) => { try { const result = await prepareTransaction({ - walletType, + walletType: walletType as string, ...walletReadyProps, ...params }); - resolver(result); + if (result) { + resolver(result); + } else { + reject(new Error("Transaction preparation failed")); + } } catch (e) { reject(e); } + return { messageToSign: "Transaction prepared" }; } ); } @@ -316,7 +321,7 @@ export const SendModal = ({ isOpen={!!customWalletFunction} customFunction={customWalletFunction} onClose={onCloseBitboxModal} - walletType={walletType} + walletType={walletType as WalletType} /> isMax || - (isValidBitcoinAmount(v) && parseFloat(v) <= currentBalance) + (v && isValidBitcoinAmount(v) && parseFloat(v) <= currentBalance) }} render={({ field: { onChange, onBlur, value = "" }, @@ -446,7 +451,7 @@ export const SendModal = ({ control={control} rules={{ required: !isMax, - validate: (v) => isMax || isValidFiatAmount(v) + validate: (v) => isMax || (v && isValidFiatAmount(v)) }} render={({ field: { onChange, onBlur, value = "" }, @@ -490,7 +495,7 @@ export const SendModal = ({ {t("balance")} : {currentBalance} BTC (~{" "} {accountCurrency - ? getFormattedUnit(btcToFiat(currentBalance), accountCurrency) + ? getFormattedUnit(btcToFiat(currentBalance), accountCurrency, 2) : ""} ) @@ -539,16 +544,20 @@ export const SendModal = ({ required: true }} render={({ field: { onChange, value } }) => { - return feesOptions.map((option, index) => ( - - )); + return ( + <> + {feesOptions.map((option, index) => ( + + ))} + + ); }} /> @@ -579,8 +588,8 @@ const FeeOption = ({ const isMedium = useIsScreenSizeMin("medium"); const onPress = useCallback(() => { - onSelect(option.label); - }, [onSelect, option.label]); + onSelect(option.value); + }, [onSelect, option.value]); return ( { )} */} + { {versionTag} + ); diff --git a/src/types/FieldsType.tsx b/src/types/FieldsType.tsx index 8ebe8462..b3a3804d 100644 --- a/src/types/FieldsType.tsx +++ b/src/types/FieldsType.tsx @@ -10,12 +10,12 @@ export enum FieldsType { } export const FieldByType = { - [FieldsType.Text]: (props) => , - [FieldsType.Multiline]: (props) => ( + [FieldsType.Text]: (props: any) => , + [FieldsType.Multiline]: (props: any) => ( ), - [FieldsType.Number]: (props) => , - [FieldsType.Email]: (props) => , - [FieldsType.Date]: (props) => , - [FieldsType.File]: (props) => + [FieldsType.Number]: (props: any) => , + [FieldsType.Email]: (props: any) => , + [FieldsType.Date]: (props: any) => , + [FieldsType.File]: (props: any) => }; diff --git a/src/types/styled-component-component-props.ts b/src/types/styled-component-component-props.ts index 7edb274d..a65df58a 100644 --- a/src/types/styled-component-component-props.ts +++ b/src/types/styled-component-component-props.ts @@ -1,7 +1,7 @@ import { ComponentPropsWithRef } from "react"; -import { AnyStyledComponent } from "styled-components"; +import { IStyledComponent } from "styled-components"; -export type StyledComponentComponentProps = - ComponentPropsWithRef & T extends AnyStyledComponent +export type StyledComponentComponentProps> = + ComponentPropsWithRef & T extends IStyledComponent ? React.ComponentProps : never; \ No newline at end of file diff --git a/src/utils/AsyncStorage/AsyncStorage.native.ts b/src/utils/AsyncStorage/AsyncStorage.native.ts index a76d4fb8..067733af 100644 --- a/src/utils/AsyncStorage/AsyncStorage.native.ts +++ b/src/utils/AsyncStorage/AsyncStorage.native.ts @@ -9,7 +9,7 @@ const getItem = async (key: string, prompt?: Keychain.AuthenticationPrompt) => { try { const encryptedValue = await Keychain.getGenericPassword({ service: key, - rules: SECURE_RULES, + // rules: SECURE_RULES // Commented out as rules is not a valid property, authenticationPrompt: prompt }); if (encryptedValue) return encryptedValue.password; @@ -36,7 +36,7 @@ const setItem = async ( return await Keychain.setGenericPassword(key, value, { service: key, accessControl, - rules: SECURE_RULES + // rules: SECURE_RULES // Commented out as rules is not a valid property }); }; diff --git a/src/utils/AsyncStorage/AsyncStorage.ts b/src/utils/AsyncStorage/AsyncStorage.ts index f76ed8cf..c19e009b 100644 --- a/src/utils/AsyncStorage/AsyncStorage.ts +++ b/src/utils/AsyncStorage/AsyncStorage.ts @@ -6,10 +6,10 @@ const textEncoder = new TextEncoder(); const textDecoder = new TextDecoder(); // Convert between binary and Base64 -const arrayBufferToBase64 = (buffer) => +const arrayBufferToBase64 = (buffer: ArrayBuffer) => btoa(String.fromCharCode(...new Uint8Array(buffer))); -const base64ToArrayBuffer = (base64) => +const base64ToArrayBuffer = (base64: string) => Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)).buffer; // eslint-disable-next-line @typescript-eslint/require-await diff --git a/src/utils/Bitbox/api/account.ts b/src/utils/Bitbox/api/account.ts index 43cb1563..3915a519 100644 --- a/src/utils/Bitbox/api/account.ts +++ b/src/utils/Bitbox/api/account.ts @@ -16,6 +16,11 @@ import { apiGet, apiPost } from "./request"; +// Mock types for missing imports +type CoinCode = string; +type Conversions = any; +type CoinUnit = string; + export type AccountCode = string; export type TKeystore = { @@ -40,7 +45,7 @@ export interface IAccount { } export const getAccounts = (): Promise => { - return apiGet("accounts"); + return apiGet("accounts") as Promise; }; export interface IReceiveAddress { @@ -56,7 +61,7 @@ export interface ReceiveAddressList { export const getReceiveAddressList = ( code: AccountCode ): Promise => { - return apiGet(`account/${code}/receive-addresses`); + return apiGet(`account/${code}/receive-addresses`) as Promise; }; export type ScriptType = "p2pkh" | "p2wpkh-p2sh" | "p2wpkh" | "p2tr"; @@ -90,7 +95,7 @@ export type TSigningConfigurationList = null | { export const getInfo = ( code: AccountCode ): Promise => { - return apiGet(`account/${code}/info`); + return apiGet(`account/${code}/info`) as Promise; }; export type AddressSignResponse = @@ -110,7 +115,7 @@ export const signAddress = ( msg: string, code: AccountCode ): Promise => { - return apiPost(`account/${code}/sign-address`, { format, msg, code }); + return apiPost(`account/${code}/sign-address`, { format, msg, code }) as Promise; }; export type FeeTargetCode = "custom" | "low" | "economy" | "normal" | "high"; @@ -147,7 +152,7 @@ export const proposeTx = ( accountCode: AccountCode, txInput: TTxInput ): Promise => { - return apiPost(`account/${accountCode}/tx-proposal`, txInput); + return apiPost(`account/${accountCode}/tx-proposal`, txInput) as Promise; }; export type ISendTx = @@ -163,5 +168,5 @@ export type ISendTx = }; export const sendTx = (code: AccountCode): Promise => { - return apiPost(`account/${code}/sendtx`); + return apiPost(`account/${code}/sendtx`) as Promise; }; \ No newline at end of file diff --git a/src/utils/Bitbox/api/bitbox02.ts b/src/utils/Bitbox/api/bitbox02.ts index dc5279b3..4019c792 100644 --- a/src/utils/Bitbox/api/bitbox02.ts +++ b/src/utils/Bitbox/api/bitbox02.ts @@ -35,17 +35,17 @@ type DeviceInfoResponse = SuccessResponse & { export const getDeviceInfo = ( deviceID: string ): Promise => { - return apiGet(`devices/bitbox02/${deviceID}/info`); + return apiGet(`devices/bitbox02/${deviceID}/info`) as Promise; }; export const checkSDCard = (deviceID: string): Promise => { - return apiGet(`devices/bitbox02/${deviceID}/check-sdcard`); + return apiGet(`devices/bitbox02/${deviceID}/check-sdcard`) as Promise; }; export const insertSDCard = ( deviceID: string ): Promise => { - return apiPost(`devices/bitbox02/${deviceID}/insert-sdcard`); + return apiPost(`devices/bitbox02/${deviceID}/insert-sdcard`) as Promise; }; export const setDeviceName = ( @@ -54,18 +54,18 @@ export const setDeviceName = ( ): Promise => { return apiPost(`devices/bitbox02/${deviceID}/set-device-name`, { name: newDeviceName - }); + }) as Promise; }; export const createBackup = ( deviceID: string, method: "sdcard" | "recovery-words" ): Promise => { - return apiPost(`devices/bitbox02/${deviceID}/backups/create`, method); + return apiPost(`devices/bitbox02/${deviceID}/backups/create`, method) as Promise; }; export const upgradeDeviceFirmware = (deviceID: string): Promise => { - return apiPost(`devices/bitbox02/${deviceID}/upgrade-firmware`); + return apiPost(`devices/bitbox02/${deviceID}/upgrade-firmware`) as Promise; }; export type TStatus = @@ -79,7 +79,7 @@ export type TStatus = | "uninitialized"; export const getStatus = (deviceID: string): Promise => { - return apiGet(`devices/bitbox02/${deviceID}/status`); + return apiGet(`devices/bitbox02/${deviceID}/status`) as Promise; }; type TChannelHash = { @@ -88,19 +88,19 @@ type TChannelHash = { }; export const getChannelHash = (deviceID: string): Promise => { - return apiGet(`devices/bitbox02/${deviceID}/channel-hash`); + return apiGet(`devices/bitbox02/${deviceID}/channel-hash`) as Promise; }; export const verifyChannelHash = ( deviceID: string, ok: boolean ): Promise => { - return apiPost(`devices/bitbox02/${deviceID}/channel-hash-verify`, ok); + return apiPost(`devices/bitbox02/${deviceID}/channel-hash-verify`, ok) as Promise; }; export const setPassword = ( deviceID: string, seedLen: 16 | 32 ): Promise => { - return apiPost(`devices/bitbox02/${deviceID}/set-password`, seedLen); + return apiPost(`devices/bitbox02/${deviceID}/set-password`, seedLen) as Promise; }; diff --git a/src/utils/Bitbox/api/bitbox02bootloader.ts b/src/utils/Bitbox/api/bitbox02bootloader.ts index 203c1b62..12d868fe 100644 --- a/src/utils/Bitbox/api/bitbox02bootloader.ts +++ b/src/utils/Bitbox/api/bitbox02bootloader.ts @@ -27,11 +27,12 @@ export type TStatus = { }; export const getStatus = (deviceID: string): Promise => { - return apiGet(`devices/bitbox02-bootloader/${deviceID}/status`); + return apiGet(`devices/bitbox02-bootloader/${deviceID}/status`) as Promise; }; export const useSyncStatus = () => { - const { subscribeEndpoint } = useContext(SBPBitboxContext); + const context = useContext(SBPBitboxContext); + const subscribeEndpoint = (context as any).subscribeEndpoint; return (deviceID: string) => (cb: TSubscriptionCallback) => { return subscribeEndpoint( @@ -53,13 +54,13 @@ export type TVersionInfo = { }; export const getVersionInfo = (deviceID: string): Promise => { - return apiGet(`devices/bitbox02-bootloader/${deviceID}/version-info`); + return apiGet(`devices/bitbox02-bootloader/${deviceID}/version-info`) as Promise; }; export const upgradeFirmware = (deviceID: string): Promise => { - return apiPost(`devices/bitbox02-bootloader/${deviceID}/upgrade-firmware`); + return apiPost(`devices/bitbox02-bootloader/${deviceID}/upgrade-firmware`) as Promise; }; export const screenRotate = (deviceID: string): Promise => { - return apiPost(`devices/bitbox02-bootloader/${deviceID}/screen-rotate`); + return apiPost(`devices/bitbox02-bootloader/${deviceID}/screen-rotate`) as Promise; }; diff --git a/src/utils/Bitbox/api/devices.ts b/src/utils/Bitbox/api/devices.ts index 5e2af74b..1bc64ce5 100644 --- a/src/utils/Bitbox/api/devices.ts +++ b/src/utils/Bitbox/api/devices.ts @@ -23,5 +23,5 @@ export type TDevices = { }; export const getDeviceList = (): Promise => { - return apiGet("devices/registered"); + return apiGet("devices/registered") as Promise; }; diff --git a/src/utils/Bitbox/api/event.ts b/src/utils/Bitbox/api/event.ts index 991c65f0..0ba4eba4 100644 --- a/src/utils/Bitbox/api/event.ts +++ b/src/utils/Bitbox/api/event.ts @@ -15,7 +15,8 @@ * limitations under the License. */ -import { TUnsubscribe } from "./websocket"; +// Mock type for missing websocket import +type TUnsubscribe = () => void; import { TEvent } from "./transport-common"; export type { TEvent, TUnsubscribe }; diff --git a/src/utils/Bitbox/api/request.ts b/src/utils/Bitbox/api/request.ts index 62f24d1e..0c360f87 100644 --- a/src/utils/Bitbox/api/request.ts +++ b/src/utils/Bitbox/api/request.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -export const call: (query: string) => Promise = window?.bitboxAndroidSend; +export const call: (query: string) => Promise = (window as any)?.bitboxAndroidSend; export const apiGet = (endpoint: string) => { return call( diff --git a/src/utils/FS/FS.ts b/src/utils/FS/FS.ts index 26dd3003..47f5e57f 100644 --- a/src/utils/FS/FS.ts +++ b/src/utils/FS/FS.ts @@ -2,7 +2,9 @@ const readFile = async ( _filepath: string, // eslint-disable-next-line @typescript-eslint/no-explicit-any _encodingOrOptions?: any -): Promise => {}; +): Promise => { + return ""; +}; const FS = { readFile }; diff --git a/src/utils/Linking/Linking.ts b/src/utils/Linking/Linking.ts index d3070422..f049f05e 100644 --- a/src/utils/Linking/Linking.ts +++ b/src/utils/Linking/Linking.ts @@ -5,5 +5,5 @@ export const Linking: Pick = { window.location.href = url; return new Promise(() => null); }, - canOpenURL: (url: string) => true + canOpenURL: (url: string) => Promise.resolve(true) }; diff --git a/src/utils/Printer/Printer.native.ts b/src/utils/Printer/Printer.native.ts index c9833cad..9e731d6f 100644 --- a/src/utils/Printer/Printer.native.ts +++ b/src/utils/Printer/Printer.native.ts @@ -19,7 +19,13 @@ export const printLabelValue = async (label: string, value: string) => { TOTAL_WIDTH - label.length - value.length )}${THOUSAND_SPACE.repeat(thousandsSpaceNb)}${value}`; await printText(text, { - font: NyxFont.monospace + font: NyxFont.monospace, + textSize: 12, + underline: false, + textScaleX: 1.0, + textScaleY: 1.0, + align: 0, + color: 0x000000 }); } catch (e) { console.error("error", e); diff --git a/src/utils/Printer/Printer.ts b/src/utils/Printer/Printer.ts index 9bb5a5d7..a6419b07 100644 --- a/src/utils/Printer/Printer.ts +++ b/src/utils/Printer/Printer.ts @@ -8,11 +8,11 @@ export enum PrinterAlignValue { type PrinterType = (typeof PrinterNative)["Printer"]; -const printLabelValue: PrinterType["printLabelValue"] = () => {}; -const printText: PrinterType["printText"] = () => {}; -const printQrCode: PrinterType["printQrCode"] = () => {}; -const paperOut: PrinterType["paperOut"] = () => {}; -const printBitmap: PrinterType["printBitmap"] = () => {}; +const printLabelValue: PrinterType["printLabelValue"] = async () => {}; +const printText: PrinterType["printText"] = async () => 0; +const printQrCode: PrinterType["printQrCode"] = async () => 0; +const paperOut: PrinterType["paperOut"] = async () => 0; +const printBitmap: PrinterType["printBitmap"] = async () => 0; const Printer = { printLabelValue, diff --git a/src/utils/diffStrings.ts b/src/utils/diffStrings.ts index 6eab7f86..df5af4eb 100644 --- a/src/utils/diffStrings.ts +++ b/src/utils/diffStrings.ts @@ -20,7 +20,7 @@ export const diffStrings = ( newIndex < newAmount.length && oldAmount[oldIndex] === newAmount[newIndex] ) { - array.push({ text: oldAmount[oldIndex] }); + array.push({ id: uuidv4(), text: oldAmount[oldIndex] }); oldIndex++; newIndex++; } else if ( @@ -28,26 +28,26 @@ export const diffStrings = ( newAmount[newIndex] === decimalSeparator ) { if (oldIndex < oldAmount.length) { - array.push({ text: oldAmount[oldIndex], remove: true }); + array.push({ id: uuidv4(), text: oldAmount[oldIndex], remove: true }); oldIndex++; } if (newIndex < newAmount.length) { - array.push({ text: newAmount[newIndex], add: true }); + array.push({ id: uuidv4(), text: newAmount[newIndex], add: true }); newIndex++; } } else { if (oldIndex < oldAmount.length) { - array.push({ text: oldAmount[oldIndex], remove: true }); + array.push({ id: uuidv4(), text: oldAmount[oldIndex], remove: true }); oldIndex++; } if (newIndex < newAmount.length) { - array.push({ text: newAmount[newIndex], add: true }); + array.push({ id: uuidv4(), text: newAmount[newIndex], add: true }); newIndex++; } } } } else { - array.push({ text: oldStr, remove: true }); + array.push({ id: uuidv4(), text: oldStr, remove: true }); } return degroup(array); diff --git a/src/utils/formattedUnitChanges.ts b/src/utils/formattedUnitChanges.ts index 9d61ddc9..0ffb1d63 100644 --- a/src/utils/formattedUnitChanges.ts +++ b/src/utils/formattedUnitChanges.ts @@ -19,22 +19,23 @@ export const formattedUnitChanges = ( if (typeof add === "number") { if (!rightPart || (leftPart === "0" && rightPart[0] === "0")) { - array.push({ text: leftPart }); + array.push({ id: uuidv4(), text: leftPart }); decimalSwitch = !rightPart; } else { if (leftPart === "0" && decimalCount === 0) { - array.push({ text: "0", remove: true }); + array.push({ id: uuidv4(), text: "0", remove: true }); } else { - array.push({ text: leftPart }); + array.push({ id: uuidv4(), text: leftPart }); } if (decimalCount === 0) { - array.push({ text: decimalSeparator, remove: true }); - array.push({ text: rightPart[0] }); + array.push({ id: uuidv4(), text: decimalSeparator, remove: true }); + array.push({ id: uuidv4(), text: rightPart[0] }); decimalSwitch = true; } } array.push({ + id: uuidv4(), text: decimalSeparator, add: decimalSwitch && decimalCount === 0 }); @@ -43,64 +44,64 @@ export const formattedUnitChanges = ( if (!rightPart) { if (decimalCount === 2) { - array.push({ text: firstChar, add: true }); + array.push({ id: uuidv4(), text: firstChar, add: true }); } else { - array.push({ text: "0", add: true }); + array.push({ id: uuidv4(), text: "0", add: true }); } } else { if (rightPart[0] === "0" && leftPart === "0" && decimalCount !== 1) { - array.push({ text: "0", remove: true }); + array.push({ id: uuidv4(), text: "0", remove: true }); if (decimalCount === 2) { - array.push({ text: firstChar, add: true }); - array.push({ text: "0" }); + array.push({ id: uuidv4(), text: firstChar, add: true }); + array.push({ id: uuidv4(), text: "0" }); } } if (decimalCount === 0) { - array.push({ text: rightPart.slice(-1) }); + array.push({ id: uuidv4(), text: rightPart.slice(-1) }); } else if (decimalCount === 1) { - array.push({ text: rightPart[0] }); + array.push({ id: uuidv4(), text: rightPart[0] }); if (firstChar === "0") { - array.push({ text: "0", add: true }); + array.push({ id: uuidv4(), text: "0", add: true }); } else { if (decimalCount !== 1) { - array.push({ text: "0", remove: true }); + array.push({ id: uuidv4(), text: "0", remove: true }); } - array.push({ text: firstChar, add: true }); + array.push({ id: uuidv4(), text: firstChar, add: true }); } } } if (decimalCount === 0) { - array.push({ text: firstChar, add: true }); + array.push({ id: uuidv4(), text: firstChar, add: true }); } } else if (add === "delete") { if (leftPart === "0") { - array.push({ text: leftPart }); + array.push({ id: uuidv4(), text: leftPart }); } else { if (leftPart.length === 1) { - array.push({ text: "0", add: true }); + array.push({ id: uuidv4(), text: "0", add: true }); } if (leftPart.length >= 1) { - array.push({ text: leftPart.slice(0, -1) }); - array.push({ text: decimalSeparator, add: true }); + array.push({ id: uuidv4(), text: leftPart.slice(0, -1) }); + array.push({ id: uuidv4(), text: decimalSeparator, add: true }); decimalSwitch = true; } } if (decimalCount === 2 && !rightPart) { - array.push({ text: decimalSeparator, remove: true }); + array.push({ id: uuidv4(), text: decimalSeparator, remove: true }); } if (leftPart !== "0") { - array.push({ text: leftPart[leftPart.length - 1] }); - array.push({ text: decimalSeparator, remove: decimalSwitch }); + array.push({ id: uuidv4(), text: leftPart[leftPart.length - 1] }); + array.push({ id: uuidv4(), text: decimalSeparator, remove: decimalSwitch }); } else { - array.push({ text: decimalSeparator, add: decimalSwitch }); - array.push({ text: "0", add: true }); + array.push({ id: uuidv4(), text: decimalSeparator, add: decimalSwitch }); + array.push({ id: uuidv4(), text: "0", add: true }); } - array.push({ text: rightPart[0] }); + array.push({ id: uuidv4(), text: rightPart[0] }); if (decimalCount !== 1) { - array.push({ text: rightPart[1], remove: true }); + array.push({ id: uuidv4(), text: rightPart[1], remove: true }); } } else if (add === "clear") { const isRightPartDefined = rightPart !== undefined; @@ -108,9 +109,10 @@ export const formattedUnitChanges = ( if (leftPart !== "0") { if (!isRightPartDefined) { - array.push({ text: "0" }); + array.push({ id: uuidv4(), text: "0" }); } array.push({ + id: uuidv4(), text: leftPart.slice( isRightPartDefined ? 0 : 1, isLastLeftPartCharZero ? -1 : undefined @@ -119,7 +121,7 @@ export const formattedUnitChanges = ( }); } if (isLastLeftPartCharZero) { - array.push({ text: "0" }); + array.push({ id: uuidv4(), text: "0" }); } const isLastRightPartCharZero = @@ -132,48 +134,49 @@ export const formattedUnitChanges = ( !isLastLeftPartCharZero && isRightPartDefined ) { - array.push({ text: "0", add: leftPart !== "0" }); + array.push({ id: uuidv4(), text: "0", add: leftPart !== "0" }); } if (decimalCount === 2 && !rightPart) { - array.push({ text: decimalSeparator, remove: true }); + array.push({ id: uuidv4(), text: decimalSeparator, remove: true }); } if (rightPart) { array.push({ + id: uuidv4(), text: `${decimalSeparator}${rightPart.slice(0, isLastRightPartCharZero ? -1 : undefined)}`, remove: true }); } if (isLastRightPartCharZero) { - array.push({ text: "0" }); + array.push({ id: uuidv4(), text: "0" }); } } else if (add === "decimal") { if (!rightPart) { - array.push({ text: leftPart }); - array.push({ text: decimalSeparator, add: true }); + array.push({ id: uuidv4(), text: leftPart }); + array.push({ id: uuidv4(), text: decimalSeparator, add: true }); } else { if (decimalCount === 0) { - array.push({ text: leftPart, remove: leftPart === "0" }); - array.push({ text: decimalSeparator, remove: true }); + array.push({ id: uuidv4(), text: leftPart, remove: leftPart === "0" }); + array.push({ id: uuidv4(), text: decimalSeparator, remove: true }); if (rightPart === "00") { - array.push({ text: "00" }); + array.push({ id: uuidv4(), text: "00" }); } const fullRight = rightPart.replace(/^0+/, ""); if (fullRight.length === 1) { - array.push({ text: "0", remove: leftPart === "0" }); + array.push({ id: uuidv4(), text: "0", remove: leftPart === "0" }); } - array.push({ text: fullRight }); + array.push({ id: uuidv4(), text: fullRight }); } - array.push({ text: decimalSeparator, add: true }); + array.push({ id: uuidv4(), text: decimalSeparator, add: true }); } } } else { - array.push({ text: amount }); + array.push({ id: uuidv4(), text: amount }); } - return degroup(array).map((r) => ({ ...r, id: uuidv4() })); + return degroup(array); }; const degroup = (array: StringPart[]) => { @@ -184,6 +187,7 @@ const degroup = (array: StringPart[]) => { if (elem.add || elem.remove) { degroupArr.push( ...elem.text.split("").map((c) => ({ + id: uuidv4(), text: c, add: elem.add, remove: elem.remove diff --git a/src/utils/getFormattedUnit.ts b/src/utils/getFormattedUnit.ts index 1284b8b9..7569ccc1 100644 --- a/src/utils/getFormattedUnit.ts +++ b/src/utils/getFormattedUnit.ts @@ -23,7 +23,7 @@ export const getFormattedUnit = ( amount: number, unit: string, floating?: number, - trailingDecimal?: boolean = false + trailingDecimal: boolean = false ) => { let prefix = ""; if (amount > 0 && amount < 0.01 && unit !== "BTC") { diff --git a/src/utils/measureText/measureText.native.ts b/src/utils/measureText/measureText.native.ts index f839aefb..dcc4afe8 100644 --- a/src/utils/measureText/measureText.native.ts +++ b/src/utils/measureText/measureText.native.ts @@ -8,6 +8,6 @@ export const measureText = ( return RNTextSize.measure({ text, fontSize, - fontFamily + fontFamily: fontFamily as string }); }; diff --git a/src/utils/measureText/measureText.tsx b/src/utils/measureText/measureText.tsx index 51baeef5..d8654e01 100644 --- a/src/utils/measureText/measureText.tsx +++ b/src/utils/measureText/measureText.tsx @@ -5,7 +5,7 @@ const ctx = document.createElement("canvas").getContext("2d"); export type MeasureTextFont = { fontSize: number; - fontFamily: number; + fontFamily: string; }; export const measureText = ( @@ -14,7 +14,11 @@ export const measureText = ( ) => { return new Promise<{ width: number }>((resolve) => { const elem = document.createElement("div"); - ctx.font = `${fontSize}px ${fontFamily}`; - resolve({ width: ctx?.measureText(text).width || 0 }); + if (ctx) { + ctx.font = `${fontSize}px ${fontFamily}`; + resolve({ width: ctx.measureText(text).width || 0 }); + } else { + resolve({ width: 0 }); + } }); }; diff --git a/src/utils/scaleDimensions.ts b/src/utils/scaleDimensions.ts index d1bcfca1..af0a1bab 100644 --- a/src/utils/scaleDimensions.ts +++ b/src/utils/scaleDimensions.ts @@ -1,4 +1,4 @@ -export const scaleDimensions = (width, height, maxWidth, maxHeight) => { +export const scaleDimensions = (width: number, height: number, maxWidth: number, maxHeight: number) => { const widthRatio = maxWidth / width; const heightRatio = maxHeight / height; diff --git a/src/utils/wallet/bitbox02/prepare/prepare.android.ts b/src/utils/wallet/bitbox02/prepare/prepare.android.ts index d4630915..491c39fb 100644 --- a/src/utils/wallet/bitbox02/prepare/prepare.android.ts +++ b/src/utils/wallet/bitbox02/prepare/prepare.android.ts @@ -1,13 +1,21 @@ import { getInfo } from "@utils/Bitbox/api/account"; -import { PrepareFunction } from "@utils/wallet/types"; +import { PrepareFunction, PrepareTransactionParams, PrepareTransactionReturn } from "@utils/wallet/types"; import { DEFAULT_SCRIPT_TYPE } from "@config"; -export const prepareTransaction: PrepareFunction = async ({ account }) => { +export const prepareTransaction: PrepareFunction = async ({ account }: PrepareTransactionParams): Promise => { + if (!account) { + throw new Error("Account is required for Android BitBox02"); + } + const accountInfo = await getInfo(account); const masterFingerprint = accountInfo?.signingConfigurations.find( (v) => v.bitcoinSimple.scriptType === DEFAULT_SCRIPT_TYPE )?.bitcoinSimple.keyInfo.rootFingerprint; + if (!masterFingerprint) { + throw new Error("Master fingerprint not found"); + } + return { masterFingerprint }; }; diff --git a/src/utils/wallet/bitbox02/prepare/prepare.ts b/src/utils/wallet/bitbox02/prepare/prepare.ts index 4cfe1d39..85929d54 100644 --- a/src/utils/wallet/bitbox02/prepare/prepare.ts +++ b/src/utils/wallet/bitbox02/prepare/prepare.ts @@ -1,5 +1,7 @@ -import { PrepareFunction } from "@utils/wallet/types"; +import { PrepareFunction, PrepareTransactionParams, PrepareTransactionReturn } from "@utils/wallet/types"; -export const prepareTransaction: PrepareFunction = ( +export const prepareTransaction: PrepareFunction = async ( _params: PrepareTransactionParams -): Promise => ({}); +): Promise => { + return { masterFingerprint: "" }; +}; diff --git a/src/utils/wallet/bitbox02/transaction/transaction.android.ts b/src/utils/wallet/bitbox02/transaction/transaction.android.ts index bd66de6c..d1f5d022 100644 --- a/src/utils/wallet/bitbox02/transaction/transaction.android.ts +++ b/src/utils/wallet/bitbox02/transaction/transaction.android.ts @@ -1,5 +1,5 @@ import { TTxInput, proposeTx, sendTx } from "@utils/Bitbox/api/account"; -import { CreateFunction } from "@utils/wallet/types"; +import { CreateFunction, CreateTransactionParams, CreateTransactionReturn } from "@utils/wallet/types"; const txInputHashToString = (txInputHash: Uint8Array) => { return Array.from(txInputHash) @@ -12,11 +12,19 @@ export const createTransaction: CreateFunction = async ({ account, psbt, feeRate -}) => { +}: CreateTransactionParams): Promise => { + if (!account) { + throw new Error("Account is required for Android BitBox02"); + } + const psbtOutput = psbt.txOutputs[psbt.data.outputs.findIndex((v) => !v.bip32Derivation)]; const outputAddress = psbtOutput.address; + if (!outputAddress) { + throw new Error("Output address not found"); + } + const outputValue = (Number(psbtOutput.value) / 100000000).toString(); const selectedUTXOs = psbt.txInputs.map( @@ -41,5 +49,5 @@ export const createTransaction: CreateFunction = async ({ } } - return {}; + return undefined; }; diff --git a/src/utils/wallet/bitbox02/transaction/transaction.ts b/src/utils/wallet/bitbox02/transaction/transaction.ts index e670f598..6540dbc8 100644 --- a/src/utils/wallet/bitbox02/transaction/transaction.ts +++ b/src/utils/wallet/bitbox02/transaction/transaction.ts @@ -1,7 +1,7 @@ -import { CreateFunction } from "@utils/wallet/types"; +import { CreateFunction, CreateTransactionParams, CreateTransactionReturn } from "@utils/wallet/types"; -export const createTransaction: CreateFunction = ( +export const createTransaction: CreateFunction = async ( _props: CreateTransactionParams -) => { - return { txHex: "" }; +): Promise => { + return { txHex: "", psbt: _props.psbt }; }; diff --git a/src/utils/wallet/ledger/prepare.ts b/src/utils/wallet/ledger/prepare.ts index 07a890ee..1239c8bc 100644 --- a/src/utils/wallet/ledger/prepare.ts +++ b/src/utils/wallet/ledger/prepare.ts @@ -1,11 +1,15 @@ -import { PrepareFunction } from "@utils/wallet/types"; +import { PrepareFunction, PrepareTransactionParams, PrepareTransactionReturn } from "@utils/wallet/types"; import { AppClient } from "ledger-bitcoin"; export const prepareTransaction: PrepareFunction = async ({ hardwareWallet -}) => { +}: PrepareTransactionParams): Promise => { + if (!hardwareWallet) { + throw new Error("Hardware wallet is required for Ledger"); + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - const app = new AppClient(hardwareWallet._transport); + const app = new AppClient((hardwareWallet as any)._transport); const masterFingerprint = await app.getMasterFingerprint(); diff --git a/src/utils/wallet/ledger/transaction.ts b/src/utils/wallet/ledger/transaction.ts index 7861f7a5..c2c80168 100644 --- a/src/utils/wallet/ledger/transaction.ts +++ b/src/utils/wallet/ledger/transaction.ts @@ -1,4 +1,4 @@ -import { CreateFunction } from "@utils/wallet/types"; +import { CreateFunction, CreateTransactionParams, CreateTransactionReturn } from "@utils/wallet/types"; import AppClient, { DefaultWalletPolicy } from "ledger-bitcoin"; import * as bitcoin from "bitcoinjs-lib"; @@ -9,9 +9,13 @@ export const createTransaction: CreateFunction = async ({ psbt, rootPath, masterFingerprint -}) => { +}: CreateTransactionParams): Promise => { + if (!hardwareWallet) { + throw new Error("Hardware wallet is required for Ledger"); + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - const app = new AppClient(hardwareWallet._transport); + const app = new AppClient((hardwareWallet as any)._transport); const xpub = await app.getExtendedPubkey(`${rootPath}`, false); @@ -20,12 +24,12 @@ export const createTransaction: CreateFunction = async ({ `[${masterFingerprint}/${rootPath.replace("m/", "")}]${xpub}` ); - const ledgerSignature = await app.signPsbt(psbt.toBase64(), signingPolicy); + const ledgerSignature = await app.signPsbt(psbt.toBase64(), signingPolicy, null); const newTxIsSegwit = true; for (let index = 0; index < ledgerSignature.length; index++) { - const { pubkey, signature } = ledgerSignature[index][1]; + const [inputIndex, { pubkey, signature }] = ledgerSignature[index]; const signer = { network: NETWORK, @@ -33,10 +37,10 @@ export const createTransaction: CreateFunction = async ({ sign: () => { const encodedSignature = (() => { if (newTxIsSegwit) { - return Buffer.from(signature, "hex"); + return Buffer.from(signature as unknown as string, "hex"); } return Buffer.concat([ - Buffer.from(signature, "hex"), + Buffer.from(signature as unknown as string, "hex"), Buffer.from("01", "hex") // SIGHASH_ALL ]); })(); @@ -45,7 +49,7 @@ export const createTransaction: CreateFunction = async ({ } }; - psbt.signInput(index, signer); + psbt.signInput(inputIndex, signer); } psbt.finalizeAllInputs(); diff --git a/src/utils/wallet/prepare-transaction.ts b/src/utils/wallet/prepare-transaction.ts index ed949acf..536a5586 100644 --- a/src/utils/wallet/prepare-transaction.ts +++ b/src/utils/wallet/prepare-transaction.ts @@ -13,7 +13,7 @@ import { } from "bitcoin-address-validation"; import axios from "axios"; import { HardwareReadyFunctionParams } from "@components/ConnectWalletModal/ConnectWalletModal"; -import { Wallet } from "./types"; +import { Wallet, PrepareTransactionParams as WalletPrepareTransactionParams, CreateTransactionParams as WalletCreateTransactionParams } from "./types"; import { Bip84Account } from "@types"; import { DEFAULT_NETWORK } from "@config"; import { AsyncStorage } from "@utils/AsyncStorage"; @@ -71,7 +71,7 @@ type OutputsTypes = { const getAddressType: ( address: string -) => InputAddressTypes | OutputAddressTypes = (address: string) => { +) => InputAddressTypes | OutputAddressTypes | undefined = (address: string) => { if (validate(address)) { const addressInfo = getAddressInfo(address); @@ -87,11 +87,12 @@ const getAddressType: ( case AddressType.p2tr: return "P2TR"; } - return addressInfo.type; + return addressInfo.type as any; } + return undefined; }; -export type PrepareTransactionParams = { +export type LocalPrepareTransactionParams = { zPub: string; utxos: FormattedUtxo[]; receiveAddress: string; @@ -113,26 +114,30 @@ export const prepareTransaction = async ({ account, walletType, askWordsPassword -}: PrepareTransactionParams) => { - let wallet: Wallet; +}: LocalPrepareTransactionParams) => { + let wallet: Wallet | undefined; let pathPrefix = ""; switch (walletType) { case "local": - wallet = local; + wallet = local as any; break; case "bitbox02": - wallet = bitbox02; + wallet = bitbox02 as any; break; case "ledger": - wallet = ledger; + wallet = ledger as any; // pathPrefix = "m/"; break; default: - break; + throw new Error(`Unsupported wallet type: ${walletType}`); + } + + if (!wallet) { + throw new Error("Wallet not initialized"); } - let inputs: InputsTypes = {}; - let outputs: OutputsTypes = {}; + let inputs: Partial = {}; + let outputs: Partial = {}; const psbt = new Psbt({ network: DEFAULT_NETWORK }); @@ -150,32 +155,38 @@ export const prepareTransaction = async ({ account, rootPath, askWordsPassword - }); + } as any); const receiveAddressType = getAddressType(receiveAddress); - outputs = { - ...outputs, - [receiveAddressType]: (outputs[receiveAddressType] || 0) + 1 - }; - - if (changeAddress) { - const changeAddressType = getAddressType(changeAddress.address); + if (receiveAddressType) { outputs = { ...outputs, - [changeAddressType]: (outputs[changeAddressType] || 0) + 1 + [receiveAddressType]: ((outputs as any)[receiveAddressType] || 0) + 1 }; } + if (changeAddress) { + const changeAddressType = getAddressType(changeAddress.address); + if (changeAddressType) { + outputs = { + ...outputs, + [changeAddressType]: ((outputs as any)[changeAddressType] || 0) + 1 + }; + } + } + const usedUtxos: FormattedUtxo[] = []; let inputAmount = 0; let finalFee = 0; for (const utxo of utxos) { const addressType = getAddressType(utxo.address); - inputs = { - ...inputs, - [addressType]: (inputs[addressType] || 0) + 1 - }; + if (addressType) { + inputs = { + ...inputs, + [addressType]: ((inputs as any)[addressType] || 0) + 1 + }; + } const { data: rawTx } = await axios.get( `https://mempool.space/api/tx/${utxo.txid}/hex` @@ -261,19 +272,19 @@ export const prepareTransaction = async ({ }; // Inspired by https://gist.github.com/junderw/b43af3253ea5865ed52cb51c200ac19c -const getByteCount = (inputs: InputsTypes, outputs: OutputsTypes) => { +const getByteCount = (inputs: Partial, outputs: Partial) => { let totalWeight = 0; let hasWitness = false; let inputCount = 0; let outputCount = 0; // assumes compressed pubkeys in all cases. - function checkUInt53(n) { + function checkUInt53(n: number) { if (n < 0 || n > Number.MAX_SAFE_INTEGER || n % 1 !== 0) throw new RangeError("value out of range"); } - function varIntLength(number) { + function varIntLength(number: number) { checkUInt53(number); return number < 0xfd @@ -286,30 +297,36 @@ const getByteCount = (inputs: InputsTypes, outputs: OutputsTypes) => { } Object.keys(inputs).forEach(function (key) { - checkUInt53(inputs[key]); - if (key.slice(0, 8) === "MULTISIG") { - // ex. "MULTISIG-P2SH:2-3" would mean 2 of 3 P2SH MULTISIG - const keyParts = key.split(":"); - if (keyParts.length !== 2) throw new Error("invalid input: " + key); - const newKey = keyParts[0]; - const mAndN = keyParts[1].split("-").map(function (item) { - return parseInt(item); - }); - - totalWeight += types.inputs[newKey] * inputs[key]; - const multiplyer = newKey === "MULTISIG-P2SH" ? 4 : 1; - totalWeight += (73 * mAndN[0] + 34 * mAndN[1]) * multiplyer * inputs[key]; - } else { - totalWeight += types.inputs[key] * inputs[key]; + const inputCountValue = inputs[key as keyof InputsTypes]; + if (inputCountValue) { + checkUInt53(inputCountValue); + if (key.slice(0, 8) === "MULTISIG") { + // ex. "MULTISIG-P2SH:2-3" would mean 2 of 3 P2SH MULTISIG + const keyParts = key.split(":"); + if (keyParts.length !== 2) throw new Error("invalid input: " + key); + const newKey = keyParts[0] as keyof typeof types.inputs; + const mAndN = keyParts[1].split("-").map(function (item) { + return parseInt(item); + }); + + totalWeight += types.inputs[newKey] * inputCountValue; + const multiplyer = newKey === "MULTISIG-P2SH" ? 4 : 1; + totalWeight += (73 * mAndN[0] + 34 * mAndN[1]) * multiplyer * inputCountValue; + } else { + totalWeight += types.inputs[key as keyof typeof types.inputs] * inputCountValue; + } + inputCount += inputCountValue; + if (key.indexOf("W") >= 0) hasWitness = true; } - inputCount += inputs[key]; - if (key.indexOf("W") >= 0) hasWitness = true; }); Object.keys(outputs).forEach(function (key) { - checkUInt53(outputs[key]); - totalWeight += types.outputs[key] * outputs[key]; - outputCount += outputs[key]; + const outputCountValue = outputs[key as keyof OutputsTypes]; + if (outputCountValue) { + checkUInt53(outputCountValue); + totalWeight += types.outputs[key as keyof typeof types.outputs] * outputCountValue; + outputCount += outputCountValue; + } }); if (hasWitness) totalWeight += 2; diff --git a/src/utils/wallet/trezor/prepare.ts b/src/utils/wallet/trezor/prepare.ts index 3d7b4f62..85929d54 100644 --- a/src/utils/wallet/trezor/prepare.ts +++ b/src/utils/wallet/trezor/prepare.ts @@ -1,3 +1,7 @@ -import { PrepareFunction } from "@utils/wallet/types"; +import { PrepareFunction, PrepareTransactionParams, PrepareTransactionReturn } from "@utils/wallet/types"; -export const prepareTransaction: PrepareFunction = async () => {}; +export const prepareTransaction: PrepareFunction = async ( + _params: PrepareTransactionParams +): Promise => { + return { masterFingerprint: "" }; +}; diff --git a/src/utils/wallet/trezor/transaction.ts b/src/utils/wallet/trezor/transaction.ts index cb91e029..6958cc15 100644 --- a/src/utils/wallet/trezor/transaction.ts +++ b/src/utils/wallet/trezor/transaction.ts @@ -1,4 +1,4 @@ -import { CreateFunction } from "@utils/wallet/types"; +import { CreateFunction, CreateTransactionParams, CreateTransactionReturn } from "@utils/wallet/types"; import AppClient, { DefaultWalletPolicy } from "ledger-bitcoin"; import * as bitcoin from "bitcoinjs-lib"; @@ -9,9 +9,13 @@ export const createTransaction: CreateFunction = async ({ psbt, rootPath, masterFingerprint -}) => { +}: CreateTransactionParams): Promise => { + if (!hardwareWallet) { + throw new Error("Hardware wallet is required for Trezor"); + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - const app = new AppClient(hardwareWallet._transport); + const app = new AppClient((hardwareWallet as any)._transport); const xpub = await app.getExtendedPubkey(`m/${rootPath}`, false); @@ -20,12 +24,12 @@ export const createTransaction: CreateFunction = async ({ `[${masterFingerprint}/${rootPath}]${xpub}` ); - const ledgerSignature = await app.signPsbt(psbt.toBase64(), signingPolicy); + const ledgerSignature = await app.signPsbt(psbt.toBase64(), signingPolicy, null); const newTxIsSegwit = true; for (let index = 0; index < ledgerSignature.length; index++) { - const { pubkey, signature } = ledgerSignature[index][1]; + const [inputIndex, { pubkey, signature }] = ledgerSignature[index]; const signer = { network: NETWORK, @@ -33,10 +37,10 @@ export const createTransaction: CreateFunction = async ({ sign: () => { const encodedSignature = (() => { if (newTxIsSegwit) { - return Buffer.from(signature, "hex"); + return Buffer.from(signature as unknown as string, "hex"); } return Buffer.concat([ - Buffer.from(signature, "hex"), + Buffer.from(signature as unknown as string, "hex"), Buffer.from("01", "hex") // SIGHASH_ALL ]); })(); @@ -45,7 +49,7 @@ export const createTransaction: CreateFunction = async ({ } }; - psbt.signInput(index, signer); + psbt.signInput(inputIndex, signer); } psbt.finalizeAllInputs(); diff --git a/src/utils/wallet/types.ts b/src/utils/wallet/types.ts index d4c83e14..2603a9cd 100644 --- a/src/utils/wallet/types.ts +++ b/src/utils/wallet/types.ts @@ -6,7 +6,7 @@ import { Psbt } from "bitcoinjs-lib"; export type HardwareWallet = PairedBitBox | Btc; -type PrepareTransactionParams = { +export type PrepareTransactionParams = { // ledger, bitbox-web hardwareWallet?: HardwareWallet; @@ -16,7 +16,7 @@ type PrepareTransactionParams = { rootPath: string; }; -type PrepareTransactionReturn = { +export type PrepareTransactionReturn = { masterFingerprint: string; bip84Account?: Bip84PrivateAccount; }; @@ -25,7 +25,7 @@ export type PrepareFunction = ( _params: PrepareTransactionParams ) => Promise; -type CreateTransactionParams = { +export type CreateTransactionParams = { psbt: Psbt; feeRate: number; usedUtxos: FormattedUtxo[];