REF: more stuff converted to typescript

This commit is contained in:
overtorment 2025-04-07 17:34:23 +01:00
parent ef934965b6
commit a72f874ffc
10 changed files with 163 additions and 84 deletions

View File

@ -1,6 +1,6 @@
import * as bip39 from 'bip39';
const WORDLISTS = [
const WORDLISTS: string[][] = [
bip39.wordlists.english,
bip39.wordlists.french,
bip39.wordlists.spanish,
@ -13,7 +13,7 @@ const WORDLISTS = [
bip39.wordlists.portuguese,
];
export function validateMnemonic(mnemonic) {
export function validateMnemonic(mnemonic: string) {
for (const wordlist of WORDLISTS) {
const valid = bip39.validateMnemonic(mnemonic, wordlist);
if (valid) return true;

View File

@ -2,7 +2,7 @@ import * as bip39 from 'bip39';
import createHash from 'create-hash';
// partial (11 or 23 word) seed phrase
export function generateChecksumWords(stringSeedPhrase) {
export function generateChecksumWords(stringSeedPhrase: string) {
const seedPhrase = stringSeedPhrase.toLowerCase().trim().split(' ');
if ((seedPhrase.length + 1) % 3 > 0) {

View File

@ -1,9 +1,9 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import PushNotificationIOS from '@react-native-community/push-notification-ios';
import { AppState, Platform } from 'react-native';
import { AppState, AppStateStatus, Platform } from 'react-native';
import { getApplicationName, getSystemName, getSystemVersion, getVersion, hasGmsSync, hasHmsSync } from 'react-native-device-info';
import { checkNotifications, requestNotifications, RESULTS } from 'react-native-permissions';
import PushNotification from 'react-native-push-notification';
import PushNotification, { ReceivedNotification } from 'react-native-push-notification';
import loc from '../loc';
import { groundControlUri } from './constants';
import { fetch } from '../util/fetch';
@ -15,7 +15,28 @@ export const NOTIFICATIONS_NO_AND_DONT_ASK_FLAG = 'NOTIFICATIONS_NO_AND_DONT_ASK
let alreadyConfigured = false;
let baseURI = groundControlUri;
const deepClone = obj => JSON.parse(JSON.stringify(obj));
type TPushToken = {
token: string;
os: string; // its actually ('ios' | 'android'), but types for the lib are a bit more generic...
};
// thats unwrapped `ReceivedNotification`, withall `data` fields inline
type TPayload = {
// inherited from `ReceivedNotification`:
subText?: string;
message?: string | object;
foreground: boolean;
userInteraction: boolean;
// hopefully stuffed in `data` and uwrapped when received:
address: string;
txid: string;
type: number;
hash: string;
};
function deepClone<T>(obj: T): T {
return JSON.parse(JSON.stringify(obj));
}
const checkAndroidNotificationPermission = async () => {
try {
@ -40,7 +61,7 @@ export const checkNotificationPermissionStatus = async () => {
// Listener to monitor notification permission status changes while app is running
let currentPermissionStatus = 'unavailable';
const handleAppStateChange = async nextAppState => {
const handleAppStateChange = async (nextAppState: AppStateStatus) => {
if (nextAppState === 'active') {
const isDisabledByUser = (await AsyncStorage.getItem(NOTIFICATIONS_NO_AND_DONT_ASK_FLAG)) === 'true';
if (!isDisabledByUser) {
@ -115,7 +136,7 @@ export const tryToObtainPermissions = async () => {
* @param txids {string[]}
* @returns {Promise<object>} Response object from API rest call
*/
export const majorTomToGroundControl = async (addresses, hashes, txids) => {
export const majorTomToGroundControl = async (addresses: string[], hashes: string[], txids: string[]) => {
console.debug('majorTomToGroundControl: Starting notification registration', {
addressCount: addresses?.length,
hashCount: hashes?.length,
@ -192,7 +213,7 @@ export const majorTomToGroundControl = async (addresses, hashes, txids) => {
export const checkPermissions = async () => {
try {
return new Promise(function (resolve) {
PushNotification.checkPermissions(result => {
PushNotification.checkPermissions((result: any) => {
resolve(result);
});
});
@ -208,7 +229,7 @@ export const checkPermissions = async () => {
* @param levelAll {Boolean}
* @returns {Promise<*>}
*/
export const setLevels = async levelAll => {
export const setLevels = async (levelAll: boolean) => {
const pushToken = await getPushToken();
if (!pushToken || !pushToken.token || !pushToken.os) return;
@ -231,12 +252,10 @@ export const setLevels = async levelAll => {
if (!levelAll) {
console.debug('Disabling notifications as user opted out...');
await Promise.all([
new Promise(resolve => PushNotification.removeAllDeliveredNotifications(resolve)),
new Promise(resolve => PushNotification.setApplicationIconBadgeNumber(0, resolve)),
new Promise(resolve => PushNotification.cancelAllLocalNotifications(resolve)),
AsyncStorage.setItem(NOTIFICATIONS_NO_AND_DONT_ASK_FLAG, 'true'),
]);
PushNotification.removeAllDeliveredNotifications();
PushNotification.setApplicationIconBadgeNumber(0);
PushNotification.cancelAllLocalNotifications();
await AsyncStorage.setItem(NOTIFICATIONS_NO_AND_DONT_ASK_FLAG, 'true');
console.debug('Notifications disabled successfully');
} else {
await AsyncStorage.removeItem(NOTIFICATIONS_NO_AND_DONT_ASK_FLAG); // Clear flag when enabling
@ -246,11 +265,11 @@ export const setLevels = async levelAll => {
}
};
export const addNotification = async notification => {
export const addNotification = async (notification: TPayload) => {
let notifications = [];
try {
const stringified = await AsyncStorage.getItem(NOTIFICATIONS_STORAGE);
notifications = JSON.parse(stringified);
notifications = JSON.parse(String(stringified));
if (!Array.isArray(notifications)) notifications = [];
} catch (e) {
console.error(e);
@ -294,10 +313,9 @@ const postTokenConfig = async () => {
}
};
const _setPushToken = async token => {
const _setPushToken = async (token: TPushToken) => {
try {
token = JSON.stringify(token);
return await AsyncStorage.setItem(PUSH_TOKEN, token);
return await AsyncStorage.setItem(PUSH_TOKEN, JSON.stringify(token));
} catch (error) {
console.error('Error setting push token:', error);
throw error;
@ -309,14 +327,14 @@ const _setPushToken = async token => {
*
* @returns {Promise<boolean>}
*/
export const configureNotifications = async onProcessNotifications => {
export const configureNotifications = async (onProcessNotifications?: () => void) => {
if (alreadyConfigured) {
console.debug('configureNotifications: Already configured, skipping');
return true;
}
return new Promise(resolve => {
const handleRegistration = async token => {
const handleRegistration = async (token: TPushToken) => {
if (__DEV__) {
console.debug('configureNotifications: Token received:', token);
}
@ -325,9 +343,11 @@ export const configureNotifications = async onProcessNotifications => {
resolve(true);
};
const handleNotification = async notification => {
// const handleNotification = async (notification: TPushNotification & { data: any }) => {
const handleNotification = async (notification: Omit<ReceivedNotification, 'userInfo'>) => {
// Deep clone to avoid modifying the original object
const payload = deepClone({
// @ts-ignore some missing properties hopefully will be unwrapped from `.data`
const payload: TPayload = deepClone({
...notification,
...notification.data,
});
@ -336,9 +356,11 @@ export const configureNotifications = async onProcessNotifications => {
const validData = Object.fromEntries(Object.entries(notification.data.data).filter(([_, value]) => value != null));
Object.assign(payload, validData);
}
// @ts-ignore stfu ts, its cleanup
payload.data = undefined;
if (!payload.title && !payload.message) {
if (!payload.subText && !payload.message) {
console.warn('Notification missing required fields:', payload);
return;
}
@ -369,7 +391,7 @@ export const configureNotifications = async onProcessNotifications => {
PushNotification.configure({
onRegister: handleRegistration,
onNotification: handleNotification,
onRegistrationError: error => {
onRegistrationError: (error: any) => {
console.error('Registration error:', error);
resolve(false);
},
@ -392,7 +414,7 @@ export const configureNotifications = async onProcessNotifications => {
* @param uri {string}
* @returns {Promise<boolean>} TRUE if valid, FALSE otherwise
*/
export const isGroundControlUriValid = async uri => {
export const isGroundControlUriValid = async (uri: string) => {
try {
const response = await fetch(`${uri}/ping`, { headers: _getHeaders() });
const json = await response.json();
@ -404,11 +426,10 @@ export const isGroundControlUriValid = async uri => {
export const isNotificationsCapable = hasGmsSync() || hasHmsSync() || Platform.OS !== 'android';
export const getPushToken = async () => {
export const getPushToken = async (): Promise<TPushToken> => {
try {
let token = await AsyncStorage.getItem(PUSH_TOKEN);
token = JSON.parse(token);
return token;
const token = await AsyncStorage.getItem(PUSH_TOKEN);
return JSON.parse(String(token)) as TPushToken;
} catch (e) {
console.error(e);
AsyncStorage.removeItem(PUSH_TOKEN);
@ -450,7 +471,7 @@ const getLevels = async () => {
* @param txids {string[]}
* @returns {Promise<object>} Response object from API rest call
*/
export const unsubscribe = async (addresses, hashes, txids) => {
export const unsubscribe = async (addresses: string[], hashes: string[], txids: string[]) => {
if (!Array.isArray(addresses) || !Array.isArray(hashes) || !Array.isArray(txids)) {
throw new Error('No addresses, hashes, or txids provided');
}
@ -501,10 +522,10 @@ export const clearStoredNotifications = async () => {
} catch (_) {}
};
export const getDeliveredNotifications = () => {
export const getDeliveredNotifications: () => Promise<Record<string, any>[]> = () => {
try {
return new Promise(resolve => {
PushNotification.getDeliveredNotifications(notifications => resolve(notifications));
PushNotification.getDeliveredNotifications((notifications: Record<string, any>[]) => resolve(notifications));
});
} catch (error) {
console.error('Error getting delivered notifications:', error);
@ -516,7 +537,7 @@ export const removeDeliveredNotifications = (identifiers = []) => {
PushNotification.removeDeliveredNotifications(identifiers);
};
export const setApplicationIconBadgeNumber = badges => {
export const setApplicationIconBadgeNumber = (badges: number) => {
PushNotification.setApplicationIconBadgeNumber(badges);
};
@ -528,7 +549,7 @@ export const getDefaultUri = () => {
return groundControlUri;
};
export const saveUri = async uri => {
export const saveUri = async (uri: string) => {
try {
baseURI = uri || groundControlUri;
await AsyncStorage.setItem(GROUNDCONTROL_BASE_URI, baseURI);
@ -573,11 +594,10 @@ export const isNotificationsEnabled = async () => {
}
};
export const getStoredNotifications = async () => {
export const getStoredNotifications = async (): Promise<TPayload[]> => {
let notifications = [];
try {
const stringified = await AsyncStorage.getItem(NOTIFICATIONS_STORAGE);
notifications = JSON.parse(stringified);
notifications = JSON.parse(String(await AsyncStorage.getItem(NOTIFICATIONS_STORAGE)));
if (!Array.isArray(notifications)) notifications = [];
} catch (e) {
if (e instanceof SyntaxError) {
@ -594,7 +614,7 @@ export const getStoredNotifications = async () => {
};
// on app launch (load module):
export const initializeNotifications = async onProcessNotifications => {
export const initializeNotifications = async (onProcessNotifications?: () => void) => {
console.debug('initializeNotifications: Starting initialization');
try {
const noAndDontAskFlag = await AsyncStorage.getItem(NOTIFICATIONS_NO_AND_DONT_ASK_FLAG);

View File

@ -411,7 +411,7 @@ class DeeplinkSchemaMatch {
return bip21.decode(replacedUri);
}
static bip21encode(address: string, options: TOptions): string {
static bip21encode(address: string, options?: TOptions): string {
// uppercase address if bech32 to satisfy BIP_0173
const isBech32 = address.startsWith('bc1');
if (isBech32) {

View File

@ -1,21 +1,30 @@
import BigNumber from 'bignumber.js';
import * as bitcoin from 'bitcoinjs-lib';
import assert from 'assert';
import * as BlueElectrum from '../blue_modules/BlueElectrum';
import { HDSegwitBech32Wallet } from './wallets/hd-segwit-bech32-wallet';
import { SegwitBech32Wallet } from './wallets/segwit-bech32-wallet';
import { CreateTransactionUtxo } from './wallets/types.ts';
import { CoinSelectOutput, CoinSelectReturnInput } from 'coinselect';
/**
* Represents transaction of a BIP84 wallet.
* Helpers for RBF, CPFP etc.
*/
export class HDSegwitBech32Transaction {
private _txhex: string | null;
private _txid: string | null;
private _wallet: HDSegwitBech32Wallet | undefined;
private _txDecoded: bitcoin.Transaction | undefined;
private _remoteTx: any;
/**
* @param txhex {string|null} Object is initialized with txhex
* @param txid {string|null} If txhex not present - txid whould be present
* @param wallet {HDSegwitBech32Wallet|null} If set - a wallet object to which transacton belongs
*/
constructor(txhex, txid, wallet) {
constructor(txhex: string | null, txid: string | null, wallet: HDSegwitBech32Wallet | null) {
if (!txhex && !txid) throw new Error('Bad arguments');
this._txhex = txhex;
this._txid = txid;
@ -40,6 +49,7 @@ export class HDSegwitBech32Transaction {
* @private
*/
async _fetchTxhexAndDecode() {
assert(this._txid, 'this._txid must be a string');
const hexes = await BlueElectrum.multiGetTransactionByTxid([this._txid], false, 10);
this._txhex = hexes[this._txid];
if (!this._txhex) throw new Error("Transaction can't be found in mempool");
@ -54,6 +64,7 @@ export class HDSegwitBech32Transaction {
*/
async getMaxUsedSequence() {
if (!this._txDecoded) await this._fetchTxhexAndDecode();
assert(this._txDecoded, 'Could not fetch tx and decode');
let max = 0;
for (const inp of this._txDecoded.ins) {
@ -81,7 +92,7 @@ export class HDSegwitBech32Transaction {
* @private
*/
async _fetchRemoteTx() {
const result = await BlueElectrum.multiGetTransactionByTxid([this._txid || this._txDecoded.getId()], true);
const result = await BlueElectrum.multiGetTransactionByTxid([this._txid || this._txDecoded!.getId()], true);
this._remoteTx = Object.values(result)[0];
}
@ -106,9 +117,9 @@ export class HDSegwitBech32Transaction {
if (!this._wallet) throw new Error('Wallet required for this method');
let found = false;
for (const tx of this._wallet.getTransactions()) {
if (tx.txid === (this._txid || this._txDecoded.getId())) {
if (tx.txid === (this._txid || this._txDecoded!.getId())) {
// its our transaction, and its spending transaction, which means we initiated it
if (tx.value < 0) found = true;
if (tx.value && tx.value < 0) found = true;
}
}
return found;
@ -125,8 +136,8 @@ export class HDSegwitBech32Transaction {
if (!this._wallet) throw new Error('Wallet required for this method');
let found = false;
for (const tx of this._wallet.getTransactions()) {
if (tx.txid === (this._txid || this._txDecoded.getId())) {
if (tx.value > 0) found = true;
if (tx.txid === (this._txid || this._txDecoded!.getId())) {
if (tx.value && tx.value > 0) found = true;
}
}
return found;
@ -147,27 +158,26 @@ export class HDSegwitBech32Transaction {
if (!this._wallet) throw new Error('Wallet required for this method');
if (!this._remoteTx) await this._fetchRemoteTx();
if (!this._txDecoded) await this._fetchTxhexAndDecode();
assert(this._txDecoded, 'could not fetch tx and decode');
const prevInputs = [];
for (const inp of this._txDecoded.ins) {
let reversedHash = Buffer.from(inp.hash).reverse();
reversedHash = reversedHash.toString('hex');
prevInputs.push(reversedHash);
prevInputs.push(Buffer.from(inp.hash).reverse().toString('hex'));
}
const prevTransactions = await BlueElectrum.multiGetTransactionByTxid(prevInputs, true);
// fetched, now lets count how much satoshis went in
let wentIn = 0;
const utxos = [];
const utxos: CreateTransactionUtxo[] = [];
for (const inp of this._txDecoded.ins) {
let reversedHash = Buffer.from(inp.hash).reverse();
reversedHash = reversedHash.toString('hex');
const reversedHash = Buffer.from(inp.hash).reverse().toString('hex');
if (prevTransactions[reversedHash] && prevTransactions[reversedHash].vout && prevTransactions[reversedHash].vout[inp.index]) {
let value = prevTransactions[reversedHash].vout[inp.index].value;
value = new BigNumber(value).multipliedBy(100000000).toNumber();
wentIn += value;
const address = SegwitBech32Wallet.witnessToAddress(inp.witness[inp.witness.length - 1]);
const witness = inp.witness[inp.witness.length - 1];
const address = String(SegwitBech32Wallet.witnessToAddress(Buffer.isBuffer(witness) ? witness.toString('hex') : witness));
utxos.push({ vout: inp.index, value, txid: reversedHash, address });
}
}
@ -185,7 +195,7 @@ export class HDSegwitBech32Transaction {
// lets take a look at change
let changeAmount = 0;
const targets = [];
const targets: { value?: number; address: string }[] = [];
for (const outp of this._remoteTx.vout) {
const address = outp.scriptPubKey.addresses[0];
const value = new BigNumber(outp.value).multipliedBy(100000000).toNumber();
@ -225,6 +235,7 @@ export class HDSegwitBech32Transaction {
async thereAreUnknownInputsInTx() {
if (!this._wallet) throw new Error('Wallet required for this method');
if (!this._txDecoded) await this._fetchTxhexAndDecode();
assert(this._txDecoded, 'could not fetch tx and decode');
const spentUtxos = this._wallet.getDerivedUtxoFromOurTransaction(true);
for (const inp of this._txDecoded.ins) {
@ -250,12 +261,19 @@ export class HDSegwitBech32Transaction {
async canCancelTx() {
if (!this._wallet) throw new Error('Wallet required for this method');
if (!this._txDecoded) await this._fetchTxhexAndDecode();
assert(this._txDecoded, 'could not fetch tx and decode');
if (await this.thereAreUnknownInputsInTx()) return false;
// if theres at least one output we dont own - we can cancel this transaction!
for (const outp of this._txDecoded.outs) {
if (!this._wallet.weOwnAddress(SegwitBech32Wallet.scriptPubKeyToAddress(outp.script))) return true;
const outpScript = outp.script;
if (
!this._wallet.weOwnAddress(
String(SegwitBech32Wallet.scriptPubKeyToAddress(Buffer.isBuffer(outpScript) ? outpScript.toString('hex') : outpScript)),
)
)
return true;
}
return false;
@ -278,7 +296,7 @@ export class HDSegwitBech32Transaction {
* @param newFeerate {number} Sat/byte. Should be greater than previous tx feerate
* @returns {Promise<{outputs: Array, tx: Transaction, inputs: Array, fee: Number}>}
*/
async createRBFcancelTx(newFeerate) {
async createRBFcancelTx(newFeerate: any) {
if (!this._wallet) throw new Error('Wallet required for this method');
if (!this._remoteTx) await this._fetchRemoteTx();
@ -303,7 +321,7 @@ export class HDSegwitBech32Transaction {
* @param newFeerate {number} Sat/byte
* @returns {Promise<{outputs: Array, tx: Transaction, inputs: Array, fee: Number}>}
*/
async createRBFbumpFee(newFeerate) {
async createRBFbumpFee(newFeerate: number) {
if (!this._wallet) throw new Error('Wallet required for this method');
if (!this._remoteTx) await this._fetchRemoteTx();
@ -333,7 +351,7 @@ export class HDSegwitBech32Transaction {
* @param newFeerate {number} sat/byte
* @returns {Promise<{outputs: Array, tx: Transaction, inputs: Array, fee: Number}>}
*/
async createCPFPbumpFee(newFeerate) {
async createCPFPbumpFee(newFeerate: number) {
if (!this._wallet) throw new Error('Wallet required for this method');
if (!this._remoteTx) await this._fetchRemoteTx();
@ -347,16 +365,21 @@ export class HDSegwitBech32Transaction {
const targetFeeRate = 2 * newFeerate - feeRate;
let add = 0;
let tx: bitcoin.Transaction | undefined, inputs: CoinSelectReturnInput[], outputs: CoinSelectOutput[], fee: number;
while (add <= 128) {
// eslint-disable-next-line no-var
var { tx, inputs, outputs, fee } = this._wallet.createTransaction(
const createdTx = this._wallet.createTransaction(
unconfirmedUtxos,
[{ address: myAddress }],
targetFeeRate + add,
myAddress,
HDSegwitBech32Wallet.defaultRBFSequence,
);
const combinedFeeRate = (oldFee + fee) / (this._txDecoded.virtualSize() + tx.virtualSize()); // avg
tx = createdTx.tx;
inputs = createdTx.inputs;
outputs = createdTx.outputs;
fee = createdTx.fee;
assert(tx, 'tx is createCPFPbumpFee() is undefined');
const combinedFeeRate = (oldFee + fee) / (this._txDecoded!.virtualSize() + tx.virtualSize()); // avg
if (Math.round(combinedFeeRate) < newFeerate) {
add *= 2;
if (!add) add = 2;
@ -366,6 +389,7 @@ export class HDSegwitBech32Transaction {
}
}
// @ts-ignore stfu
return { tx, inputs, outputs, fee };
}
}

View File

@ -5,14 +5,14 @@
import crypto from 'crypto';
// uses `crypto` module under nodejs/cli and shim under RN
// @see blue_modules/crypto.js
// check out 'react-native-crypto' in package.json
/**
* Generate cryptographically secure random bytes using native api.
* @param {number} size The number of bytes of randomness
* @return {Promise.<Buffer>} The random bytes
*/
export async function randomBytes(size) {
export async function randomBytes(size: number): Promise<Buffer> {
return new Promise((resolve, reject) => {
crypto.randomBytes(size, (err, data) => {
if (err) reject(err);

8
package-lock.json generated
View File

@ -128,6 +128,7 @@
"@types/crypto-js": "^4.2.2",
"@types/jest": "^29.5.2",
"@types/react": "^18.2.16",
"@types/react-native-push-notification": "^8.1.4",
"@types/react-test-renderer": "^19.0.0",
"@types/wif": "^2.0.5",
"@typescript-eslint/eslint-plugin": "^7.15.0",
@ -5696,6 +5697,13 @@
"@types/react": "*"
}
},
"node_modules/@types/react-native-push-notification": {
"version": "8.1.4",
"resolved": "https://registry.npmjs.org/@types/react-native-push-notification/-/react-native-push-notification-8.1.4.tgz",
"integrity": "sha512-qXu/NcQ7YSk5ZveDMNKFBQkLt9W5FCde3be+h8fYbEnmvd5O+v5m318XGhh8AMPXURAV9pSB5Ads08Wc0KTS7A==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/react-native-vector-icons": {
"version": "6.4.18",
"resolved": "https://registry.npmjs.org/@types/react-native-vector-icons/-/react-native-vector-icons-6.4.18.tgz",

View File

@ -23,6 +23,7 @@
"@types/crypto-js": "^4.2.2",
"@types/jest": "^29.5.2",
"@types/react": "^18.2.16",
"@types/react-native-push-notification": "^8.1.4",
"@types/react-test-renderer": "^19.0.0",
"@types/wif": "^2.0.5",
"@typescript-eslint/eslint-plugin": "^7.15.0",

View File

@ -4,6 +4,7 @@ import * as bip39 from 'bip39';
import * as bitcoin from 'bitcoinjs-lib';
import React, { Component } from 'react';
import { Linking, ScrollView, StyleSheet, View } from 'react-native';
// @ts-ignore theres no type declaration for this
import BlueCrypto from 'react-native-blue-crypto';
import wif from 'wif';
@ -24,9 +25,22 @@ import presentAlert from '../../components/Alert';
import Button from '../../components/Button';
import SaveFileButton from '../../components/SaveFileButton';
import loc from '../../loc';
import { CreateTransactionUtxo } from '../../class/wallets/types.ts';
const bip32 = BIP32Factory(ecc);
type TState = {
isLoading?: boolean;
isOk?: boolean;
errorMessage?: string;
};
function assertStrictEqual<T>(actual: T, expected: T, message?: string) {
if (expected !== actual) {
throw new Error(message || 'Assertion failed that ' + JSON.stringify(expected) + ' equals ' + JSON.stringify(actual));
}
}
const styles = StyleSheet.create({
center: {
alignItems: 'center',
@ -34,7 +48,9 @@ const styles = StyleSheet.create({
});
export default class SelfTest extends Component {
constructor(props) {
state: TState;
constructor(props: any) {
super(props);
this.state = {
isLoading: true,
@ -62,7 +78,7 @@ export default class SelfTest extends Component {
try {
if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {
const uniqs = {};
const uniqs: Record<string, 1> = {};
const w = new SegwitP2SHWallet();
for (let c = 0; c < 1000; c++) {
await w.generate();
@ -102,10 +118,10 @@ export default class SelfTest extends Component {
// skipping RN-specific test
}
let l = new LegacyWallet();
let l: LegacyWallet | SegwitP2SHWallet = new LegacyWallet();
l.setSecret('L4ccWrPMmFDZw4kzAKFqJNxgHANjdy6b7YKNXMwB4xac4FLF3Tov');
assertStrictEqual(l.getAddress(), '14YZ6iymQtBVQJk6gKnLCk49UScJK7SH4M');
let utxos = [
let utxos: CreateTransactionUtxo[] = [
{
txid: 'cc44e933a094296d9fe424ad7306f16916253a3d154d52e4f1a757c18242cec4',
vout: 0,
@ -115,10 +131,18 @@ export default class SelfTest extends Component {
},
];
let txNew = l.createTransaction(utxos, [{ value: 90000, address: '1GX36PGBUrF8XahZEGQqHqnJGW2vCZteoB' }], 1, l.getAddress());
const txBitcoin = bitcoin.Transaction.fromHex(txNew.tx.toHex());
let txNew = l.createTransaction(
utxos,
[{ value: 90000, address: '1GX36PGBUrF8XahZEGQqHqnJGW2vCZteoB' }],
1,
String(l.getAddress()),
0xffffffff,
false,
0,
);
const txBitcoin = bitcoin.Transaction.fromHex(txNew.tx!.toHex());
assertStrictEqual(
txNew.tx.toHex(),
txNew.tx!.toHex(),
'0200000001c4ce4282c157a7f1e4524d153d3a251669f10673ad24e49f6d2994a033e944cc000000006b48304502210091e58bd2021f2eeea8d39d7f7b053c9ccc52a747b60f1c3584ba33285e2d150602205b2d35a2536cbe157015e8c54a26f5fc350cc7c72b5ca80b9e548917993f652201210337c09b3cb889801638078fd4e6998218b28c92d338ea2602720a88847aedceb3ffffffff02905f0100000000001976a914aa381cd428a4e91327fd4434aa0a08ff131f1a5a88ac2e260000000000001976a91426e01119d265aa980390c49eece923976c218f1588ac00000000',
);
assertStrictEqual(txBitcoin.ins.length, 1);
@ -148,10 +172,18 @@ export default class SelfTest extends Component {
},
];
txNew = wallet.createTransaction(utxos, [{ value: 90000, address: '1GX36PGBUrF8XahZEGQqHqnJGW2vCZteoB' }], 1, wallet.getAddress());
const tx = bitcoin.Transaction.fromHex(txNew.tx.toHex());
txNew = wallet.createTransaction(
utxos,
[{ value: 90000, address: '1GX36PGBUrF8XahZEGQqHqnJGW2vCZteoB' }],
1,
String(wallet.getAddress()),
0xffffffff,
false,
0,
);
const tx = bitcoin.Transaction.fromHex(txNew.tx!.toHex());
assertStrictEqual(
txNew.tx.toHex(),
txNew.tx!.toHex(),
'020000000001010c86eb9013616e38b4752e56e5683e864cb34fcd7fe790bdc006b60c08446ba50000000017160014139dc70d73097f9d775f8a3280ba3e3435515641ffffffff02905f0100000000001976a914aa381cd428a4e91327fd4434aa0a08ff131f1a5a88aca73303000000000017a914749118baa93fb4b88c28909c8bf0a8202a0484f4870248304502210080545d30e3d30dff272ab11c91fd6150170b603239b48c3d56a3fa66bf240085022003762404e1b45975adc89f61ec1569fa19d6d4a8d405e060897754c489ebeade012103a5de146762f84055db3202c1316cd9008f16047f4f408c1482fdb108217eda0800000000',
);
assertStrictEqual(tx.ins.length, 1);
@ -192,7 +224,7 @@ export default class SelfTest extends Component {
//
if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {
const hd = new HDSegwitP2SHWallet();
const hashmap = {};
const hashmap: Record<string, 1> = {};
for (let c = 0; c < 1000; c++) {
await hd.generate();
const secret = hd.getSecret();
@ -323,10 +355,3 @@ export default class SelfTest extends Component {
);
}
}
function assertStrictEqual(actual, expected, message) {
if (expected !== actual) {
if (message) throw new Error(message);
throw new Error('Assertion failed that ' + JSON.stringify(expected) + ' equals ' + JSON.stringify(actual));
}
}

View File

@ -15,10 +15,11 @@ console.warn = (...args) => {
};
const consoleLogOrig = console.log;
console.log = (...args) => {
console.debug = console.log = (...args) => {
if (
typeof args[0] === 'string' &&
(args[0].startsWith('updating exchange rate') ||
args[0].startsWith('Created new currency formatter for') ||
args[0].startsWith('begin connection') ||
args[0].startsWith('TLS Connected to') ||
args[0].startsWith('connected to'))