chore(suite): autofix newlines

This commit is contained in:
Peter Sanderson
2024-02-20 14:27:29 +01:00
committed by Peter Sanderson
parent 6d23f67570
commit c82455e746
887 changed files with 2419 additions and 4 deletions

View File

@@ -273,10 +273,10 @@ module.exports = {
},
],
'prefer-numeric-literals': 'error',
"padding-line-between-statements": [
"error",
{ blankLine: "always", prev: "*", next: "return" },
]
'padding-line-between-statements': [
'error',
{ blankLine: 'always', prev: '*', next: 'return' },
],
},
overrides: [
{

View File

@@ -95,6 +95,7 @@ export class Analytics<T extends AnalyticsEvent> {
console.error(
`Unable to report ${data.type}. Analytics is not initialized! Missing: ${listOfMissingFields}`,
);
return;
}

View File

@@ -47,6 +47,7 @@ export const filterTokenTransfers = (
const all: (string | null)[] = addresses.map(a => {
if (typeof a === 'string') return a;
if (typeof a === 'object' && typeof a.address === 'string') return a.address;
return null;
});
@@ -58,6 +59,7 @@ export const filterTokenTransfers = (
(transfer.to && all.indexOf(transfer.to) >= 0)
);
}
return false;
})
.map(transfer => {
@@ -288,6 +290,7 @@ export const transformTokenInfo = (
if (!tokens || !Array.isArray(tokens)) return undefined;
const info = tokens.reduce((arr, token) => {
if (token.type === 'XPUBAddress') return arr;
return arr.concat([
{
...token,
@@ -295,6 +298,7 @@ export const transformTokenInfo = (
},
]);
}, [] as TokenInfo[]);
return info.length > 0 ? info : undefined;
};
@@ -304,6 +308,7 @@ export const transformAddresses = (
if (!tokens || !Array.isArray(tokens)) return undefined;
const addresses = tokens.reduce((arr, t) => {
if (t.type !== 'XPUBAddress') return arr;
return arr.concat([
{
address: t.name,

View File

@@ -37,6 +37,7 @@ export const transformUtxos = (utxos: BlockfrostUtxos[]): Utxo[] => {
});
}),
);
return result;
};
@@ -64,6 +65,7 @@ const getSubtype = (tx: Pick<BlockfrostTransaction, 'txData'>) => {
// transaction could both register staking address and delegate stake at the same time. In that case we treat it as "stake registration"
return 'stake_registration';
}
return 'stake_deregistration';
}
@@ -90,6 +92,7 @@ export const transformTokenInfo = (
if (!tokens || !Array.isArray(tokens)) return undefined;
const info = tokens.map(token => {
const { assetName } = parseAsset(token.unit);
return {
type: 'BLOCKFROST',
name: token.fingerprint!, // this is safe as fingerprint is defined for all tokens except lovelace and lovelace is never included in account.tokens

View File

@@ -95,6 +95,7 @@ type SplTokenAccount = { account: AccountInfo<SplTokenAccountData>; pubkey: Publ
const isSplTokenAccount = (tokenAccount: ApiTokenAccount): tokenAccount is SplTokenAccount => {
const { parsed } = tokenAccount.account.data;
return (
tokenAccount.account.data.program === 'spl-token' &&
'info' in parsed &&
@@ -117,6 +118,7 @@ export const transformTokenInfo = (
A.filter(isSplTokenAccount),
A.map(tokenAccount => {
const { info } = tokenAccount.account.data.parsed;
return {
type: 'SPL', // Designation for Solana tokens
contract: info.mint,
@@ -260,6 +262,7 @@ export const getTargets = (
if (txType === 'unknown') {
return false;
}
// count in only positive effects, for `sent` tx they gonna be represented as negative, for `recv` as positive
return effect.amount.isGreaterThan(0);
})
@@ -271,6 +274,7 @@ export const getTargets = (
amount: effect.amount.abs().toString(),
isAccountTarget: effect.address === accountAddress && txType !== 'sent',
};
return target;
});
@@ -383,6 +387,7 @@ export const getDetails = (
if (txType === 'self') {
vout.push(getVin({ address: accountAddress }, vout.length));
}
return {
size: transaction.meta?.computeUnitsConsumed || 0,
totalInput: senders
@@ -406,6 +411,7 @@ export const getAmount = (
if (txType === 'self') {
return accountEffect.amount?.abs().toString();
}
return accountEffect.amount.toString();
};
@@ -485,6 +491,7 @@ export const getTokens = (
if (isAccountDestination) {
return 'recv';
}
return 'sent';
};

View File

@@ -20,6 +20,7 @@ export const filterTargets = (addresses: Addresses, targets: VinVout[]): VinVout
.map(a => {
if (typeof a === 'string') return a;
if (typeof a === 'object' && typeof a.address === 'string') return a.address;
return undefined;
})
.filter(isNotUndefined);
@@ -65,5 +66,6 @@ export const sortTxsFromLatest = (transactions: Transaction[]) => {
}
from = to;
}
return txs;
};

View File

@@ -91,6 +91,7 @@ class BlockchainLink extends TypedEmitter<Events> {
this.worker.onmessage = this.onMessage.bind(this);
this.worker.onerror = this.onError.bind(this);
}
return this.worker;
}
@@ -99,6 +100,7 @@ class BlockchainLink extends TypedEmitter<Events> {
const worker = await this.getWorker();
const { promiseId, promise } = this.deferred.create();
worker.postMessage({ id: promiseId, ...message });
return promise;
}
@@ -271,6 +273,7 @@ class BlockchainLink extends TypedEmitter<Events> {
// eslint-disable-next-line require-await
async disconnect(): Promise<boolean> {
if (!this.worker) return true;
return this.sendMessage({
type: MESSAGES.DISCONNECT,
});
@@ -283,6 +286,7 @@ class BlockchainLink extends TypedEmitter<Events> {
if (data.id === -1) {
this.onEvent(data);
return;
}

View File

@@ -349,8 +349,10 @@ const init = (instances: any[]) => {
const b = i.blockchain;
if (i.selected) {
fillValues(i.data);
return `<option value="${b.name}" selected>${b.name}</option>`;
}
return `<option value="${b.name}">${b.name}</option>`;
})
.join('');

View File

@@ -8,6 +8,7 @@ export const onClear = () => {
export const getInputValue = (id: string): string => {
const input = document.getElementById(id) as HTMLInputElement;
return input.value;
};

View File

@@ -103,6 +103,7 @@ export abstract class BaseWebsocket<T extends EventMap> extends TypedEmitter<T &
this.options.onSending?.(message);
ws.send(JSON.stringify(req));
return promise;
}
@@ -142,6 +143,7 @@ export abstract class BaseWebsocket<T extends EventMap> extends TypedEmitter<T &
// remove previous subscriptions
this.subscriptions.splice(index, 1);
}
return index;
}

View File

@@ -98,6 +98,7 @@ export abstract class BaseWorker<API> {
this.debug('Connected');
this.api = api;
this.connectPromise = undefined;
return api;
});
}
@@ -120,6 +121,7 @@ export abstract class BaseWorker<API> {
return this.tryConnect(url).catch(error => {
this.debug('Connection failed', error);
return this.connectRecursive(rest);
});
}
@@ -142,21 +144,25 @@ export abstract class BaseWorker<API> {
this.proxyAgent = data.settings.proxy
? SocksProxyAgent(data.settings.proxy)
: undefined;
return true;
}
if (data.type === MESSAGES.CONNECT) {
await this.connect();
this.post({ id, type: RESPONSES.CONNECT, payload: true });
return true;
}
if (data.type === MESSAGES.DISCONNECT) {
this.disconnect();
this.post({ id, type: RESPONSES.DISCONNECTED, payload: true });
return true;
}
if (data.type === MESSAGES.TERMINATE) {
this.disconnect();
this.cleanup();
return true;
}
}

View File

@@ -18,6 +18,7 @@ type Request<T> = T & Context;
const getInfo = async (request: Request<MessageTypes.GetInfo>) => {
const api = await request.connect();
const info = await api.getServerInfo();
return {
type: RESPONSES.GET_INFO,
payload: {
@@ -30,6 +31,7 @@ const getInfo = async (request: Request<MessageTypes.GetInfo>) => {
const getBlockHash = async (request: Request<MessageTypes.GetBlockHash>) => {
const api = await request.connect();
const info = await api.getBlockHash(request.payload);
return {
type: RESPONSES.GET_BLOCK_HASH,
payload: info.hash,
@@ -39,6 +41,7 @@ const getBlockHash = async (request: Request<MessageTypes.GetBlockHash>) => {
const getBlock = async (request: Request<MessageTypes.GetBlock>) => {
const api = await request.connect();
const info = await api.getBlock(request.payload);
return {
type: RESPONSES.GET_BLOCK,
payload: info,
@@ -49,6 +52,7 @@ const getAccountInfo = async (request: Request<MessageTypes.GetAccountInfo>) =>
const { payload } = request;
const api = await request.connect();
const info = await api.getAccountInfo(payload);
return {
type: RESPONSES.GET_ACCOUNT_INFO,
payload: utils.transformAccountInfo(info),
@@ -59,6 +63,7 @@ const getAccountUtxo = async (request: Request<MessageTypes.GetAccountUtxo>) =>
const { payload } = request;
const api = await request.connect();
const utxos = await api.getAccountUtxo(payload);
return {
type: RESPONSES.GET_ACCOUNT_UTXO,
payload: utils.transformAccountUtxo(utxos),
@@ -71,6 +76,7 @@ const getAccountBalanceHistory = async (
const { payload } = request;
const api = await request.connect();
const history = await api.getAccountBalanceHistory(payload);
return {
type: RESPONSES.GET_ACCOUNT_BALANCE_HISTORY,
payload: history,
@@ -81,6 +87,7 @@ const getCurrentFiatRates = async (request: Request<MessageTypes.GetCurrentFiatR
const { payload } = request;
const api = await request.connect();
const fiatRates = await api.getCurrentFiatRates(payload);
return {
type: RESPONSES.GET_CURRENT_FIAT_RATES,
payload: fiatRates,
@@ -93,6 +100,7 @@ const getFiatRatesForTimestamps = async (
const { payload } = request;
const api = await request.connect();
const { tickers } = await api.getFiatRatesForTimestamps(payload);
return {
type: RESPONSES.GET_FIAT_RATES_FOR_TIMESTAMPS,
payload: { tickers },
@@ -103,6 +111,7 @@ const getFiatRatesTickersList = async (request: Request<MessageTypes.GetFiatRate
const { payload } = request;
const api = await request.connect();
const tickers = await api.getFiatRatesTickersList(payload);
return {
type: RESPONSES.GET_FIAT_RATES_TICKERS_LIST,
payload: {
@@ -116,6 +125,7 @@ const getTransaction = async (request: Request<MessageTypes.GetTransaction>) =>
const api = await request.connect();
const rawtx = await api.getTransaction(request.payload);
const tx = utils.transformTransaction(rawtx);
return {
type: RESPONSES.GET_TRANSACTION,
payload: tx,
@@ -126,6 +136,7 @@ const getTransactionHex = async (request: Request<MessageTypes.GetTransactionHex
const api = await request.connect();
const { hex } = await api.getTransaction(request.payload);
if (!hex) throw new CustomError(`Missing hex of ${request.payload}`);
return {
type: RESPONSES.GET_TRANSACTION_HEX,
payload: hex,
@@ -135,6 +146,7 @@ const getTransactionHex = async (request: Request<MessageTypes.GetTransactionHex
const pushTransaction = async (request: Request<MessageTypes.PushTransaction>) => {
const api = await request.connect();
const resp = await api.pushTransaction(request.payload);
return {
type: RESPONSES.PUSH_TRANSACTION,
payload: resp.result,
@@ -144,6 +156,7 @@ const pushTransaction = async (request: Request<MessageTypes.PushTransaction>) =
const estimateFee = async (request: Request<MessageTypes.EstimateFee>) => {
const api = await request.connect();
const resp = await api.estimateFee(request.payload);
return {
type: RESPONSES.ESTIMATE_FEE,
payload: resp,
@@ -217,6 +230,7 @@ const subscribeAccounts = async (ctx: Context, accounts: SubscriptionAccountInfo
api.on('notification', ev => onTransaction(ctx, ev));
state.addSubscription('notification');
}
return api.subscribeAddresses(state.getAddresses());
};
@@ -229,6 +243,7 @@ const subscribeAddresses = async (ctx: Context, addresses: string[]) => {
api.on('notification', ev => onTransaction(ctx, ev));
state.addSubscription('notification');
}
return api.subscribeAddresses(state.getAddresses());
};
@@ -237,6 +252,7 @@ const subscribeBlock = async (ctx: Context) => {
const api = await ctx.connect();
ctx.state.addSubscription('block');
api.on('block', ev => onNewBlock(ctx, ev));
return api.subscribeBlock();
};
@@ -246,6 +262,7 @@ const subscribeFiatRates = async (ctx: Context, currency?: string) => {
ctx.state.addSubscription('fiatRates');
api.on('fiatRates', ev => onNewFiatRates(ctx, ev));
}
return api.subscribeFiatRates(currency);
};
@@ -255,6 +272,7 @@ const subscribeMempool = async (ctx: Context) => {
ctx.state.addSubscription('mempool');
api.on('mempool', ev => onMempoolTx(ctx, ev));
}
return api.subscribeMempool();
};
@@ -295,8 +313,10 @@ const unsubscribeAccounts = async (
// remove listeners
api.removeAllListeners('notification');
state.removeSubscription('notification');
return api.unsubscribeAddresses();
}
// subscribe remained addresses
return api.subscribeAddresses(subscribed);
};
@@ -313,8 +333,10 @@ const unsubscribeAddresses = async ({ state, connect }: Context, addresses?: str
// remove listeners
api.removeAllListeners('notification');
state.removeSubscription('notification');
return api.unsubscribeAddresses();
}
// subscribe remained addresses
return api.subscribeAddresses(subscribed);
};
@@ -324,6 +346,7 @@ const unsubscribeBlock = async ({ state, connect }: Context) => {
const api = await connect();
api.removeAllListeners('block');
state.removeSubscription('block');
return api.unsubscribeBlock();
};
@@ -332,6 +355,7 @@ const unsubscribeFiatRates = async ({ state, connect }: Context) => {
const api = await connect();
api.removeAllListeners('fiatRates');
state.removeSubscription('fiatRates');
return api.unsubscribeFiatRates();
};
@@ -340,6 +364,7 @@ const unsubscribeMempool = async ({ state, connect }: Context) => {
const api = await connect();
api.removeAllListeners('mempool');
state.removeSubscription('mempool');
return api.unsubscribeMempool();
};

View File

@@ -144,44 +144,52 @@ export class BlockbookAPI extends BaseWebsocket<BlockbookEvents> {
subscribeAddresses(addresses: string[]) {
this.removeSubscription('notification');
this.addSubscription('notification', result => this.emit('notification', result));
return this.send('subscribeAddresses', { addresses });
}
unsubscribeAddresses() {
const index = this.removeSubscription('notification');
return index >= 0 ? this.send('unsubscribeAddresses') : { subscribed: false };
}
subscribeBlock() {
this.removeSubscription('block');
this.addSubscription('block', result => this.emit('block', result));
return this.send('subscribeNewBlock');
}
unsubscribeBlock() {
const index = this.removeSubscription('block');
return index >= 0 ? this.send('unsubscribeNewBlock') : { subscribed: false };
}
subscribeFiatRates(currency?: string) {
this.removeSubscription('fiatRates');
this.addSubscription('fiatRates', result => this.emit('fiatRates', result));
return this.send('subscribeFiatRates', { currency });
}
unsubscribeFiatRates() {
const index = this.removeSubscription('fiatRates');
return index >= 0 ? this.send('unsubscribeFiatRates') : { subscribed: false };
}
subscribeMempool() {
this.removeSubscription('mempool');
this.addSubscription('mempool', result => this.emit('mempool', result));
return this.send('subscribeNewTransaction');
}
unsubscribeMempool() {
const index = this.removeSubscription('mempool');
return index >= 0 ? this.send('unsubscribeNewTransaction') : { subscribed: false };
}
}

View File

@@ -21,6 +21,7 @@ type Request<T> = T & Context;
const getInfo = async (request: Request<MessageTypes.GetInfo>) => {
const api = await request.connect();
const info = await api.getServerInfo();
return {
type: RESPONSES.GET_INFO,
payload: {
@@ -33,6 +34,7 @@ const getInfo = async (request: Request<MessageTypes.GetInfo>) => {
const getBlockHash = async (request: Request<MessageTypes.GetBlockHash>) => {
const api = await request.connect();
const blockMessage = await api.getBlockHash(request.payload);
return {
type: RESPONSES.GET_BLOCK_HASH,
payload: blockMessage.hash,
@@ -44,6 +46,7 @@ const getAccountBalanceHistory = async (
) => {
const socket = await request.connect();
const history = await socket.getAccountBalanceHistory(request.payload);
return {
type: RESPONSES.GET_ACCOUNT_BALANCE_HISTORY,
payload: history,
@@ -54,6 +57,7 @@ const getTransaction = async (request: Request<MessageTypes.GetTransaction>) =>
const api = await request.connect();
const txData = await api.getTransaction(request.payload);
const tx = transformTransaction({ txData });
return {
type: RESPONSES.GET_TRANSACTION,
payload: tx,
@@ -76,6 +80,7 @@ const estimateFee = async (request: Request<MessageTypes.EstimateFee>) => {
const pushTransaction = async (request: Request<MessageTypes.PushTransaction>) => {
const api = await request.connect();
const payload = await api.pushTransaction(request.payload);
return {
type: RESPONSES.PUSH_TRANSACTION,
payload,
@@ -85,6 +90,7 @@ const pushTransaction = async (request: Request<MessageTypes.PushTransaction>) =
const getAccountInfo = async (request: Request<MessageTypes.GetAccountInfo>) => {
const api = await request.connect();
const info = await api.getAccountInfo(request.payload);
return {
type: RESPONSES.GET_ACCOUNT_INFO,
payload: transformAccountInfo(info),
@@ -94,6 +100,7 @@ const getAccountInfo = async (request: Request<MessageTypes.GetAccountInfo>) =>
const getAccountUtxo = async (request: Request<MessageTypes.GetAccountUtxo>) => {
const api = await request.connect();
const utxos = await api.getAccountUtxo(request.payload);
return {
type: RESPONSES.GET_ACCOUNT_UTXO,
payload: transformUtxos(utxos),
@@ -138,6 +145,7 @@ const subscribeBlock = async (ctx: Context) => {
const api = await ctx.connect();
ctx.state.addSubscription('block');
api.on('block', ev => onNewBlock(ctx, ev));
return api.subscribeBlock();
};
@@ -149,6 +157,7 @@ const subscribeAccounts = async (ctx: Context, accounts: SubscriptionAccountInfo
api.on('notification', ev => onTransaction(ctx, ev));
state.addSubscription('notification');
}
return api.subscribeAddresses(state.getAddresses());
};
@@ -160,6 +169,7 @@ const subscribeAddresses = async (ctx: Context, addresses: string[]) => {
api.on('notification', ev => onTransaction(ctx, ev));
state.addSubscription('notification');
}
return api.subscribeAddresses(state.getAddresses());
};
@@ -188,6 +198,7 @@ const unsubscribeBlock = async ({ state, connect }: Context) => {
const api = await connect();
api.removeAllListeners('block');
state.removeSubscription('block');
return api.unsubscribeBlock();
};
@@ -204,8 +215,10 @@ const unsubscribeAccounts = async (
// remove listeners
api.removeAllListeners('notification');
state.removeSubscription('notification');
return api.unsubscribeAddresses();
}
// subscribe remained addresses
return api.subscribeAddresses(subscribed);
};
@@ -222,8 +235,10 @@ const unsubscribeAddresses = async ({ state, connect }: Context, addresses?: str
// remove listeners
socket.removeAllListeners('notification');
state.removeSubscription('notification');
return socket.unsubscribeAddresses();
}
// subscribe remained addresses
return socket.subscribeAddresses(subscribed);
};

View File

@@ -21,6 +21,7 @@ interface BlockfrostEvents {
export class BlockfrostAPI extends BaseWebsocket<BlockfrostEvents> {
protected createWebsocket() {
const { url } = this.options;
// options are not used in web builds (see ./src/utils/ws)
return new WebSocket(url, {
agent: this.options.agent,
@@ -68,22 +69,26 @@ export class BlockfrostAPI extends BaseWebsocket<BlockfrostEvents> {
subscribeBlock() {
this.removeSubscription('block');
this.addSubscription('block', result => this.emit('block', result));
return this.send('SUBSCRIBE_BLOCK');
}
subscribeAddresses(addresses: string[]) {
this.removeSubscription('notification');
this.addSubscription('notification', result => this.emit('notification', result));
return this.send('SUBSCRIBE_ADDRESS', { addresses });
}
unsubscribeBlock() {
const index = this.removeSubscription('block');
return index >= 0 ? this.send('UNSUBSCRIBE_BLOCK') : { subscribed: false };
}
unsubscribeAddresses() {
const index = this.removeSubscription('notification');
return index >= 0 ? this.send('UNSUBSCRIBE_ADDRESS') : { subscribed: false };
}
}

View File

@@ -27,6 +27,7 @@ export class BatchingJsonRpcClient extends JsonRpcClient {
protected send(message: string) {
if (this.batchingDisabled) {
super.send(message);
return;
}
const { queue } = this;

View File

@@ -31,11 +31,13 @@ export class CachingElectrumClient extends ElectrumClient {
const [cachedStatus, cachedResponse] = cached;
if (cachedStatus === status) {
this.cached++;
return cachedResponse;
}
}
const response = await super.request(method, ...params);
this.cache[descriptor] = [status, response];
return response;
}
@@ -48,6 +50,7 @@ export class CachingElectrumClient extends ElectrumClient {
// Subscribe to the new scripthash and store the status
const newStatus = await super.request('blockchain.scripthash.subscribe', scripthash);
this.statuses[scripthash] = newStatus;
return newStatus;
}
@@ -59,25 +62,30 @@ export class CachingElectrumClient extends ElectrumClient {
case 'blockchain.scripthash.listunspent': {
const [scripthash] = params;
const status = await this.trySubscribe(scripthash);
return this.cacheRequest(status, method, params);
}
case 'blockchain.transaction.get': {
const curBlock = this.lastBlock?.hex;
if (curBlock === undefined) break;
return this.cacheRequest(curBlock, method, params);
}
case 'blockchain.scripthash.subscribe': {
const [scripthash] = params;
return this.trySubscribe(scripthash);
}
case 'blockchain.scripthash.unsubscribe': {
const [scripthash] = params;
delete this.statuses[scripthash];
return super.request(method, ...params);
}
default:
break;
}
return super.request(method, ...params);
}

View File

@@ -86,6 +86,7 @@ export class ElectrumClient extends BatchingJsonRpcClient implements ElectrumAPI
request(method: string, ...params: any[]) {
this.timeLastCall = new Date().getTime();
return super.request(method, ...params);
}

View File

@@ -131,6 +131,7 @@ const onRequest = async <T extends Message>(
// @ts-expect-error
const { method, params } = request.payload;
return client
.request(method, ...params)
.then((res: any) => ({ type: method, payload: res }));

View File

@@ -29,6 +29,7 @@ export const blockListener = (worker: BaseWorker<ElectrumAPI>) => {
state.addSubscription('block');
api().on('blockchain.headers.subscribe', onBlock);
}
return { subscribed: true };
};
@@ -37,6 +38,7 @@ export const blockListener = (worker: BaseWorker<ElectrumAPI>) => {
api().off('blockchain.headers.subscribe', onBlock);
state.removeSubscription('block');
}
return { subscribed: false };
};

View File

@@ -22,6 +22,7 @@ const mostRecent = (previous: HistoryTx | undefined, current: HistoryTx) => {
if (current.height === -1) return current;
if (previous.height === 0) return previous;
if (current.height === 0) return current;
return previous.height >= current.height ? previous : current;
};
@@ -72,6 +73,7 @@ export const txListener = (worker: BaseWorker<ElectrumAPI>) => {
api().request('blockchain.scripthash.subscribe', scripthash),
),
);
return { subscribed: true };
};
@@ -93,6 +95,7 @@ export const txListener = (worker: BaseWorker<ElectrumAPI>) => {
api().request('blockchain.scripthash.unsubscribe', scripthash),
),
);
return { subscribed: false };
};

View File

@@ -60,6 +60,7 @@ const aggregateTransactions = (txs: (Transaction & { blockTime: number })[], gro
});
i = j;
}
return result;
};

View File

@@ -27,6 +27,7 @@ const getBalances =
confirmed: 0,
unconfirmed: 0,
};
return {
address,
path,

View File

@@ -35,6 +35,7 @@ const getAccountUtxo: Api<Req, Res> = async (client, descriptor) => {
if (parsed.valid) {
const utxos = await client.request('blockchain.scripthash.listunspent', parsed.scripthash);
return utxos.map(transformUtxo(height));
}
@@ -51,6 +52,7 @@ const getAccountUtxo: Api<Req, Res> = async (client, descriptor) => {
.then(utxos => utxos.map(transformUtxo(height, { address, path }))),
),
).then(res => res.flat());
return result;
};

View File

@@ -4,6 +4,7 @@ import type { GetBlockHash as Res } from '@trezor/blockchain-link-types/lib/resp
const getBlockHash: Api<Req, Res> = async (client, payload) => {
const blockheader = await client.request('blockchain.block.header', payload);
return blockheaderToBlockhash(blockheader);
};

View File

@@ -10,6 +10,7 @@ const getInfo: Api<Req, Res> = client => {
block: { hex, height },
version: [_name, version],
} = client.getInfo() || throwError('Client not initialized');
return Promise.resolve({
url,
version,

View File

@@ -5,6 +5,7 @@ import type { GetTransaction as Res } from '@trezor/blockchain-link-types/lib/re
const getTransaction: Api<Req, Res> = async (client, payload) => {
const [tx] = await getTransactions(client, [{ tx_hash: payload, height: -1 }]);
return transformTransaction(tx);
};

View File

@@ -4,6 +4,7 @@ import type { PushTransaction as Res } from '@trezor/blockchain-link-types/lib/r
const pushTransaction: Api<Req, Res> = async (client, payload) => {
const res = await client.request('blockchain.transaction.broadcast', payload);
return res;
};

View File

@@ -7,6 +7,7 @@ export class TcpSocket extends SocketBase {
const socket = new TCPSocket();
this.configureSocket(socket);
this.bindSocket(socket, listener);
return new Promise<TCPSocket>((resolve, reject) => {
const errorHandler = (err: Error) => reject(err);
socket.on('error', errorHandler);

View File

@@ -7,6 +7,7 @@ export class TlsSocket extends SocketBase {
const socket = new TLSSocket(null as any /* TODO omg why? */);
this.configureSocket(socket);
this.bindSocket(socket, listener);
return new Promise<TLSSocket>((resolve, reject) => {
const errorHandler = (err: Error) => reject(err);
socket.on('error', errorHandler);

View File

@@ -25,6 +25,7 @@ export class TorSocket extends SocketBase {
listener.onConnect();
this.configureSocket(socket);
this.bindSocket(socket, listener);
return socket;
}
}

View File

@@ -84,6 +84,7 @@ export const createAddressManager = (getNetwork: () => Network | undefined) => {
([_acc, { change, unused, used }]) =>
!!change.concat(used, unused).find(ad => ad.address === address),
) || [];
return {
descriptor: account || address,
addresses,

View File

@@ -14,6 +14,7 @@ export const discoverAddress =
async ({ address, path }: { address: string; path: string }): Promise<AddressHistory> => {
const scripthash = addressToScripthash(address, client.getInfo()?.network);
const history = await client.request('blockchain.scripthash.get_history', scripthash);
return {
address,
scripthash,

View File

@@ -12,18 +12,21 @@ import type {
const transformOpReturn = (hex: string) => {
const [, _len, data] = hex.match(/^6a(?:4c)?([0-9a-f]{2})([0-9a-f]*)$/i) ?? [];
return data ? `OP_RETURN (${Buffer.from(data, 'hex').toString('ascii')})` : undefined;
};
const parseAddresses = ({ address, addresses, type, hex }: TxOut['scriptPubKey']) => {
if (type === 'nulldata') {
const opReturn = transformOpReturn(hex);
return {
addresses: opReturn ? [opReturn] : [],
isAddress: false,
};
}
const addrs = !address ? addresses || [] : [address];
return {
addresses: addrs,
isAddress: addrs.length === 1,
@@ -56,6 +59,7 @@ const formatTransaction =
const valueIn = vinRegular
.map(({ txid, vout }) => getVout(txid, vout).value)
.reduce((a, b) => a + b, 0);
return {
txid,
hex,

View File

@@ -5,17 +5,20 @@ export const btcToSat = (btc: number) => Math.round(100000000 * btc).toString();
export const addressToScripthash = (address: string, network?: Network) => {
const script = a.toOutputScript(address, network);
const scripthash = c.sha256(script).reverse().toString('hex');
return scripthash;
};
export const scriptToScripthash = (hex: string) => {
const buffer = Buffer.from(hex, 'hex');
return c.sha256(buffer).reverse().toString('hex');
};
export const blockheaderToBlockhash = (header: string) => {
const buffer = Buffer.from(header, 'hex');
const hash = c.hash256(buffer).reverse().toString('hex');
return hash;
};

View File

@@ -25,8 +25,10 @@ const transformError = (error: any) => {
if (error.data) {
return new CustomError(code, `${error.name} ${error.data.error_message}`);
}
return new CustomError(code, error.toString());
}
return error;
};
@@ -55,6 +57,7 @@ const getMempoolAccountInfo = async (api: RippleAPI, account: string) => {
ledger_index: 'current',
queue: true,
});
return {
xrpBalance: info.account_data.Balance,
sequence: info.account_data.Sequence,
@@ -176,6 +179,7 @@ const getTransaction = async ({ connect, payload }: Request<MessageTypes.GetTran
const api = await connect();
const rawtx = await api.request('tx', { transaction: payload, binary: false });
const tx = utils.transformTransaction(rawtx);
return {
type: RESPONSES.GET_TRANSACTION,
payload: tx,
@@ -211,6 +215,7 @@ const estimateFee = async (request: Request<MessageTypes.EstimateFee>) => {
request.payload && Array.isArray(request.payload.blocks)
? request.payload.blocks.map(() => ({ feePerUnit: drops }))
: [{ feePerUnit: drops }];
return {
type: RESPONSES.ESTIMATE_FEE,
payload,
@@ -274,6 +279,7 @@ const subscribeAccounts = async (ctx: Context, accounts: SubscriptionAccountInfo
accounts_proposed: uniqueAddresses,
});
}
return { subscribed: state.getAddresses().length > 0 };
};
@@ -298,6 +304,7 @@ const subscribeAddresses = async (ctx: Context, addresses: string[]) => {
await api.request('subscribe', request);
}
return { subscribed: state.getAddresses().length > 0 };
};
@@ -307,6 +314,7 @@ const subscribeBlock = async (ctx: Context) => {
api.on('ledger', ev => onNewBlock(ctx, ev));
ctx.state.addSubscription('ledger');
}
return { subscribed: true };
};
@@ -323,6 +331,7 @@ const subscribe = async (request: Request<MessageTypes.Subscribe>) => {
} else {
throw new CustomError('invalid_param', '+type');
}
return {
type: RESPONSES.SUBSCRIBE,
payload: response,
@@ -458,6 +467,7 @@ class RippleWorker extends BaseWorker<RippleAPI> {
});
this.post({ id: -1, type: RESPONSES.CONNECTED });
return api;
}

View File

@@ -56,6 +56,7 @@ const getAllSignatures = async (
keepFetching = signatures.length === limit;
allSignatures = [...allSignatures, ...signatures];
}
return allSignatures;
};
@@ -71,6 +72,7 @@ const fetchTransactionPage = async (
resultArray[chunkIndex] = []; // start a new chunk
}
resultArray[chunkIndex].push(item);
return resultArray;
}, [] as string[][]);
@@ -93,6 +95,7 @@ const pushTransaction = async (request: Request<MessageTypes.PushTransaction>) =
const rawTx = request.payload.startsWith('0x') ? request.payload.slice(2) : request.payload;
const api = await request.connect();
const payload = await api.sendRawTransaction(Buffer.from(rawTx, 'hex'));
return {
type: RESPONSES.PUSH_TRANSACTION,
payload,
@@ -231,6 +234,7 @@ const getInfo = async (request: Request<MessageTypes.GetInfo>) => {
version: (await api.getVersion())['solana-core'],
decimals: 9,
};
return {
type: RESPONSES.GET_INFO,
payload: { ...serverInfo },
@@ -394,6 +398,7 @@ const subscribeAccounts = async (
});
state.addAccounts([{ ...a, subscriptionId }]);
});
return { subscribed: newAccounts.length > 0 };
};
@@ -438,6 +443,7 @@ const subscribe = async (request: Request<MessageTypes.Subscribe>) => {
default:
throw new CustomError('worker_unknown_request', `+${request.type}`);
}
return {
type: RESPONSES.SUBSCRIBE,
payload: response,
@@ -456,6 +462,7 @@ const unsubscribe = (request: Request<MessageTypes.Unsubscribe>) => {
default:
throw new CustomError('worker_unknown_request', `+${request.type}`);
}
return {
type: RESPONSES.UNSUBSCRIBE,
payload: { subscribed: request.state.getAccounts().length > 0 },
@@ -489,6 +496,7 @@ class SolanaWorker extends BaseWorker<SolanaAPI> {
tryConnect(url: string): Promise<SolanaAPI> {
const api = new Connection(url, { wsEndpoint: url.replace('https', 'wss') });
this.post({ id: -1, type: RESPONSES.CONNECTED });
return Promise.resolve(api);
}

View File

@@ -14,10 +14,12 @@ export class WorkerState {
private validateAddresses(addr: string[]) {
if (!Array.isArray(addr)) throw new CustomError('invalid_param', '+addresses');
const seen: string[] = [];
return addr.filter(a => {
if (typeof a !== 'string') return false;
if (seen.indexOf(a) >= 0) return false;
seen.push(a);
return true;
});
}
@@ -25,6 +27,7 @@ export class WorkerState {
addAddresses(addr: string[]) {
const unique = this.validateAddresses(addr).filter(a => this.addresses.indexOf(a) < 0);
this.addresses = this.addresses.concat(unique);
return unique;
}
@@ -35,18 +38,22 @@ export class WorkerState {
removeAddresses(addr: string[]) {
const unique = this.validateAddresses(addr);
this.addresses = this.addresses.filter(a => unique.indexOf(a) < 0);
return this.addresses;
}
private validateAccounts(acc: SubscriptionAccountInfo[]): SubscriptionAccountInfo[] {
if (!Array.isArray(acc)) throw new CustomError('invalid_param', '+accounts');
const seen: string[] = [];
return acc.filter(a => {
if (a && typeof a === 'object' && typeof a.descriptor === 'string') {
if (seen.indexOf(a.descriptor) >= 0) return false;
seen.push(a.descriptor);
return true;
}
return false;
});
}
@@ -54,8 +61,10 @@ export class WorkerState {
private getAccountAddresses(acc: SubscriptionAccountInfo) {
if (acc.addresses) {
const { change, used, unused } = acc.addresses;
return change.concat(used, unused).map(a => a.address);
}
return [acc.descriptor];
}
@@ -68,6 +77,7 @@ export class WorkerState {
[] as string[],
);
this.addAddresses(addresses);
return valid;
}
@@ -80,6 +90,7 @@ export class WorkerState {
if (used.find(ad => ad.address === address)) return true;
if (unused.find(ad => ad.address === address)) return true;
}
return false;
});
}
@@ -99,6 +110,7 @@ export class WorkerState {
);
this.accounts = this.accounts.filter(a => accountsToRemove.indexOf(a) < 0);
this.removeAddresses(addressesToRemove);
return this.accounts;
}
@@ -130,6 +142,7 @@ export class WorkerState {
if (obj[key] && typeof obj[key] === 'object') this.removeEmpty(obj[key]);
else if (obj[key] === undefined) delete obj[key];
});
return obj;
}

View File

@@ -15,6 +15,7 @@ export const prioritizeEndpoints = (urls: string[]) =>
} else if (hostname?.endsWith('.onion')) {
priority += 1;
}
return [url, priority];
})
.sort(([, a], [, b]) => b - a)

View File

@@ -17,6 +17,7 @@ export const rippleWorkerFactory = () => {
// require('../../../lib/workers/ripple/index.js');
// });
}
return new Worker('./build/web/ripple-worker.js');
};
@@ -34,6 +35,7 @@ export const blockbookWorkerFactory = () => {
// require('../../../lib/workers/blockbook/index.js');
// });
}
return new Worker('./build/web/blockbook-worker.js');
};
@@ -47,6 +49,7 @@ export const blockfrostWorkerFactory = () => {
});
// return new TinyWorker('./build/module/blockfrost-worker.js');
}
return new Worker('./build/web/blockfrost-worker.js');
};

View File

@@ -112,8 +112,10 @@ describe('Worker', () => {
const blob = new Blob([js], {
type: 'application/javascript',
});
return new Worker(URL.createObjectURL(blob));
}
return new TinyWorker(() => {
setTimeout(() => {
// @ts-expect-error self is not typed
@@ -156,8 +158,10 @@ describe('Worker', () => {
const blob = new Blob([js], {
type: 'application/javascript',
});
return new Worker(URL.createObjectURL(blob));
}
return new TinyWorker(() => {
self.onmessage = () => {
// @ts-expect-error undefined "x"

View File

@@ -46,6 +46,7 @@ export class CoinjoinAddressController {
private deriveMore(type: 'receive' | 'change', from: number, count: number) {
const prederived = this.cache?.[`${type}Prederived`];
return deriveAddresses(prederived, this.xpub, type, from, count, this.network).map(
({ address, path }) => ({
address,

View File

@@ -84,6 +84,7 @@ export class CoinjoinBackend extends TypedEmitter<Events> {
cache,
network: this.network,
});
return Promise.resolve(accountInfo);
}
@@ -122,6 +123,7 @@ export class CoinjoinBackend extends TypedEmitter<Events> {
if (addressFirstPage.txs === 0) {
const networkInfo = await this.client.fetchNetworkInfo();
return {
blockHash: networkInfo.bestHash,
blockHeight: networkInfo.bestHeight,
@@ -150,6 +152,7 @@ export class CoinjoinBackend extends TypedEmitter<Events> {
private getLogger(): Logger {
const emit = (level: LogLevel) => (payload: string) => this.emit('log', { level, payload });
return {
debug: emit('debug'),
info: emit('info'),

View File

@@ -50,6 +50,7 @@ export class CoinjoinBackendClient {
fetchBlock(height: number, options?: RequestOptions): Promise<BlockbookBlock> {
const identity = this.identitiesBlockbook[height & 0x3]; // Works only when identities.length === 4
return this.getBlockbookApi(api => api.getBlock(height), { identity, ...options });
}
@@ -62,6 +63,7 @@ export class CoinjoinBackendClient {
fetchTransaction(txid: string, options?: RequestOptions): Promise<BlockbookTransaction> {
const lastCharCode = txid.charCodeAt(txid.length - 1);
const identity = this.identitiesBlockbook[lastCharCode & 0x3]; // Works only when identities.length === 4
return this.getBlockbookApi(api => api.getTransaction(txid), { identity, ...options });
}
@@ -85,8 +87,10 @@ export class CoinjoinBackendClient {
if (!blockFiltersBatch.length) return { status: 'up-to-date' };
const filters = blockFiltersBatch.map(item => {
const [blockHeight, blockHash, filter] = item.split(':');
return { blockHeight: Number(blockHeight), blockHash, filter };
});
return { status: 'ok', filters, ...rest };
})
.catch<BlockFilterResponse>(error => {
@@ -134,6 +138,7 @@ export class CoinjoinBackendClient {
newApi = await this.getBlockbookApi(api => api);
} catch {
this.emitter.emit('mempoolDisconnected');
return;
}
@@ -185,6 +190,7 @@ export class CoinjoinBackendClient {
{ identity, ...options }: RequestOptions = {},
): Promise<T> {
let preferOnion = true;
return scheduleAction(
async () => {
const urlIndex = this.blockbookRequestId++ % this.blockbookUrls.length;
@@ -203,6 +209,7 @@ export class CoinjoinBackendClient {
}
throw error;
});
return callbackFn(api);
},
{ attempts: 3, timeout: HTTP_REQUEST_TIMEOUT, gap: HTTP_REQUEST_GAP, ...options },

View File

@@ -128,6 +128,7 @@ export class CoinjoinMempoolController {
),
);
this.lastPurge = new Date().getTime();
return [...this.mempool.values()];
}
@@ -160,6 +161,7 @@ export class CoinjoinMempoolController {
}
this.lastPurge = new Date().getTime();
return Array.from(set, txid => this.mempool.get(txid)!);
}

View File

@@ -73,6 +73,7 @@ export class CoinjoinWebsocketController {
this.logger?.debug(`WS CLOSED ${socketId}`);
});
}
return socket;
}

View File

@@ -48,6 +48,7 @@ export const deriveAddresses = (
const derived = countNew
? deriveNewAddresses(descriptor, type, fromNew, countNew, network)
: [];
return prederived.slice(fromPrederived, fromPrederived + countPrederived).concat(derived);
};

View File

@@ -24,6 +24,7 @@ const createFilter = (data: Buffer, { P = P_DEFAULT, M = M_DEFAULT }) => {
const filter = Golomb.fromNBytes(P, data);
// In golomb package, M is hardcoded to 784931. With custom value, m must be calculated separately (as M * n).
filter.m = new U64(M).mul(new U64(filter.n));
return filter;
};
@@ -34,6 +35,7 @@ export const getFilter = (filterHex: string, { P, M, key }: FilterParams = {}) =
if (!filterHex) return () => false;
const filter = createFilter(Buffer.from(filterHex, 'hex'), { P, M });
const keyBuffer = key ? Buffer.from(key, 'hex').subarray(0, KEY_SIZE) : ZERO_KEY;
return (script: Buffer) => filter.match(keyBuffer, script);
};
@@ -41,5 +43,6 @@ export const getMultiFilter = (filterHex: string, { P, M, key }: FilterParams =
if (!filterHex) return () => false;
const filter = createFilter(Buffer.from(filterHex, 'hex'), { P, M });
const keyBuffer = key ? Buffer.from(key, 'hex').subarray(0, KEY_SIZE) : ZERO_KEY;
return (scripts: Buffer[]) => !!scripts.length && filter.matchAny(keyBuffer, scripts);
};

View File

@@ -43,6 +43,7 @@ const enhanceAddress =
const txs = transactions.filter(tx => doesTxContainAddress(address)(tx.details));
const sent = sumAddressValues(txs, address, tx => tx.details.vin);
const received = sumAddressValues(txs, address, tx => tx.details.vout);
return {
address,
path,

View File

@@ -22,6 +22,7 @@ const getHeightData = (tx: Transaction) =>
const getAddressData = (vout: VinVout, paths: AddressPaths) => {
const address = vout.addresses?.[0] ?? throwError('Address is missing from tx output');
return {
address,
path: paths[address],

View File

@@ -71,6 +71,7 @@ export class Account {
// find inputs/outputs already registered in Round(s)
findDetainedElements(rounds: Round[]) {
const { accountKey } = this;
return rounds.flatMap(round => {
if (round.Phase > 0) {
const registeredInputs = getRoundEvents('InputAdded', round.CoinjoinState.Events);
@@ -93,6 +94,7 @@ export class Account {
return [...inputs, ...outputs];
}
return [];
});
}

View File

@@ -61,6 +61,7 @@ export class Alice {
type,
timestamp: Date.now(),
};
return {
accountKey: this.accountKey,
path: this.path,

View File

@@ -52,6 +52,7 @@ export class CoinjoinClient extends TypedEmitter<CoinjoinClientEvents> {
if (this.abortController.signal.aborted) {
this.abortController = new AbortController();
}
return this.status.start();
}
@@ -188,6 +189,7 @@ export class CoinjoinClient extends TypedEmitter<CoinjoinClientEvents> {
// and update fresh data from Status
return currentRound.onPhaseChange(round);
}
return [];
}),
);
@@ -268,6 +270,7 @@ export class CoinjoinClient extends TypedEmitter<CoinjoinClientEvents> {
private getLogger(): Logger {
const emit = (level: LogLevel) => (payload: string) =>
this.emit('log', { level, payload: redacted(payload) });
return {
debug: emit('debug'),
info: emit('info'),

View File

@@ -98,6 +98,7 @@ export class CoinjoinPrison extends TypedEmitter<CoinjoinPrisonEvents> {
} else {
id = inmate.accountKey;
}
return this.inmates.find(i => i.id === id);
}
@@ -144,6 +145,7 @@ export class CoinjoinPrison extends TypedEmitter<CoinjoinPrisonEvents> {
// regardless of their sentenceEnd
if (!rounds.includes(inmate.roundId)) return false;
}
return inmate.sentenceEnd > now;
});

View File

@@ -186,6 +186,7 @@ export class CoinjoinRound extends TypedEmitter<Events> {
const unlock = () => {
this.logger.warn(`Aborting round ${this.id}`);
abort();
return promise;
};
@@ -236,6 +237,7 @@ export class CoinjoinRound extends TypedEmitter<Events> {
const { info: log } = this.logger;
if (this.inputs.length === 0) {
log('Trying to process round without inputs');
return this;
}
await this.processPhase(accounts);
@@ -252,6 +254,7 @@ export class CoinjoinRound extends TypedEmitter<Events> {
if (shouldBeExcluded) {
input.clearConfirmationInterval();
}
return !shouldBeExcluded;
});
} else if (this.phase > RoundPhase.InputRegistration) {
@@ -322,6 +325,7 @@ export class CoinjoinRound extends TypedEmitter<Events> {
this.logger.info(`Requesting ownership for ~~${input.outpoint}~~`);
input.setRequest('ownership');
});
return {
type: 'ownership',
roundId: this.id,
@@ -338,6 +342,7 @@ export class CoinjoinRound extends TypedEmitter<Events> {
this.logger.info(`Requesting witness for ~~${input.outpoint}~~`);
input.setRequest('signature');
});
return {
type: 'signature',
roundId: this.id,

View File

@@ -75,6 +75,7 @@ export class Status extends TypedEmitter<StatusEvents> {
'warn',
`Unexpected phase change: ${nextRound.Id} ${known.Phase} => ${nextRound.Phase}`,
);
// possible corner-case:
// - suite fetch the /status, next fetch will be in ~20 sec. + potential network delay
// - round is currently in phase "0" but will be changed to "1" in few seconds,
@@ -82,6 +83,7 @@ export class Status extends TypedEmitter<StatusEvents> {
// - suite fetch the /status, round phase is changed from 0 to 2
return true;
}
return false;
})
.concat(
@@ -192,6 +194,7 @@ export class Status extends TypedEmitter<StatusEvents> {
this.emit('update', statusEvent);
this.rounds = status.RoundStates;
return statusEvent;
}
}
@@ -254,6 +257,7 @@ export class Status extends TypedEmitter<StatusEvents> {
// start lifecycle only if status is present
this.setStatusTimeout();
return { success: true as const, ...status, version };
} catch (error) {
return { success: false as const, error: error.message };

View File

@@ -27,6 +27,7 @@ const transformVinVout = (vinvout: EnhancedVinVout, network: Network) => {
if (vinvout.isAccountOwned) return { Address, Value } as AnalyzeInternalVinVout;
const ScriptPubKey = addressBjs.toOutputScript(Address, network).toString('hex');
return {
ScriptPubKey,
Value,
@@ -48,6 +49,7 @@ export const getRawLiquidityClue = (
.flatMap(vout => transformVinVout(vout, options.network))
.filter(vout => !('address' in vout))
.map(o => Number(o.Value));
return middleware.initLiquidityClue(externalAmounts, {
baseUrl: options.middlewareUrl,
signal: options.signal,
@@ -90,6 +92,7 @@ export const getAnonymityScores = async (
return scores.reduce(
(dict, { Address, AnonymitySet }) => {
dict[Address] = AnonymitySet;
return dict;
},
{} as Record<string, number>,

View File

@@ -21,6 +21,7 @@ export const getStatus = async (options: RequestOptions) => {
},
options,
);
return data;
};

View File

@@ -44,6 +44,7 @@ const parseResult = (headers: Headers, text: string) => {
// fall down and return text
}
}
return text;
};
@@ -101,6 +102,7 @@ export const coordinatorRequest = async <R = void>(
}
const text = await response.text();
return { response, text };
};

View File

@@ -33,6 +33,7 @@ export const getRealCredentials = async (
},
options,
);
return data.RealCredentialRequests;
};
@@ -44,6 +45,7 @@ export const getZeroCredentials = async (issuer: IssuerParameter, options: Reque
},
options,
);
return data.ZeroCredentialRequests;
};
@@ -62,6 +64,7 @@ export const getCredentials = async (
},
options,
);
return data.Credentials;
};
@@ -78,6 +81,7 @@ export const getOutputsAmounts = async (
options: RequestOptions,
) => {
const data = await request<{ OutputAmounts: number[] }>('get-outputs-amounts', body, options);
return data.OutputAmounts;
};
@@ -97,6 +101,7 @@ export const selectInputsForRound = async (
options: RequestOptions,
) => {
const data = await request<{ Indices: number[] }>('select-inputs-for-round', body, options);
return data.Indices;
};
@@ -115,6 +120,7 @@ export const initLiquidityClue = async (ExternalAmounts: number[], options: Requ
{ ExternalAmounts },
options,
);
return data.RawLiquidityClue;
};
@@ -129,6 +135,7 @@ export const updateLiquidityClue = async (
{ RawLiquidityClue: rawLiquidityClue, MaxSuggestedAmount, ExternalAmounts },
options,
);
return data.RawLiquidityClue;
};
@@ -142,6 +149,7 @@ export const getLiquidityClue = async (
{ rawLiquidityClue, maxSuggestedAmount },
options,
);
return data.LiquidityClue;
};

View File

@@ -31,6 +31,7 @@ const confirmInput = async (
}
if (input.confirmedAmountCredentials && input.confirmedVsizeCredentials) {
options.logger.info(`Input ~~${input.outpoint}~~ already confirmed. Skipping.`);
return input;
}
@@ -75,6 +76,7 @@ const confirmInput = async (
!confirmationData.RealVsizeCredentials
) {
logger.info(`Confirmed in phase ${round.phase} ~~${input.outpoint}~~ in ~~${round.id}~~`);
return input;
}
@@ -198,6 +200,7 @@ export const connectionConfirmation = async (
if (!input.getConfirmationInterval()) {
input.setConfirmationInterval(interval);
}
return interval.promise;
}),
).then(result =>

View File

@@ -31,11 +31,13 @@ const registerInput = async (
// stop here and request for ownership proof from the wallet
if (!input.ownershipProof) {
logger.info(`Waiting for ~~${input.outpoint}~~ ownership proof`);
return input;
}
if (input.registrationData) {
logger.info(`Input ~~${input.outpoint}~~ already registered. Skipping.`);
return input;
}
@@ -182,6 +184,7 @@ const registerInput = async (
return input;
} catch (error) {
input.setError(error);
return input;
}
};

View File

@@ -64,9 +64,11 @@ const getOutputAmounts = async (params: GetOutputAmountsParams) => {
{ signal, baseUrl: middlewareUrl },
);
logger.info(`Decompose amounts: ${outputAmounts.join(',')}`);
return outputAmounts.map(amount => {
const miningFee = Math.floor((outputSize * roundParameters.MiningFeeRate) / 1000);
const coordinatorFee = 0; // NOTE: middleware issue https://github.com/zkSNACKs/WalletWasabi/issues/8814 should be `amount > plebsDontPayThreshold ? Math.floor(roundParameters.coordinationFeeRate.rate * amount) : 0` but middleware does not considerate coordinationFeeRate and plebs for external amounts
return amount + coordinatorFee + miningFee;
});
};
@@ -238,6 +240,7 @@ const findCredentialsForTarget = (
if (pair) {
return [cre, pair];
}
return [];
})
.find(pair => pair.length === 2);
@@ -405,6 +408,7 @@ export const outputDecomposition = async (
if (!input.confirmedAmountCredentials || !input.confirmedVsizeCredentials) {
throw new Error(`Missing confirmed credentials for ~~${input.outpoint}~~`);
}
return input.accountKey;
},
true,
@@ -460,6 +464,7 @@ export const outputDecomposition = async (
options,
result: [],
});
return result;
}),
);
@@ -468,6 +473,7 @@ export const outputDecomposition = async (
return Object.keys(groupInputsByAccount).map((accountKey, index) => {
if (!joinedCredentials[index])
throw new Error(`Missing joined credentials at index ${index}`);
return {
accountKey,
outputs: joinedCredentials[index],

View File

@@ -91,6 +91,7 @@ const registerOutput = async (
sentenceEnd: Infinity, // this address should never be recycled
},
);
return tryToRegisterOutput(false);
}
if (error.errorCode === WabiSabiProtocolErrorCode.NotEnoughFunds) {
@@ -157,6 +158,7 @@ export const outputRegistration = async (
if (!account) throw new Error(`Unknown account ~~${accountKey}~~`);
const assignedAddresses: AccountAddress[] = [];
return Promise.all(
arrayShuffle(outputs).map(output =>
registerOutput(round, account, output, assignedAddresses, options),
@@ -180,5 +182,6 @@ export const outputRegistration = async (
round.inputs.forEach(input => input.setError(new Error(message)));
}
return round;
};

View File

@@ -37,6 +37,7 @@ export const getRoundCandidates = ({
prison,
}: Omit<SelectRoundProps, 'aliceGenerator' | 'accounts' | 'runningAffiliateServer'>) => {
const now = Date.now();
return statusRounds
.filter(
round =>
@@ -100,6 +101,7 @@ export const getAccountCandidates = ({
// account was detained
if (prison.isDetained(accountKey)) {
logger.info(`Account ~~${accountKey}~~ detained`);
return [];
}
@@ -107,6 +109,7 @@ export const getAccountCandidates = ({
account.utxos,
utxo => {
const blamedUtxo = blameOfInputs.find(i => i.id === utxo.outpoint);
return blamedUtxo?.roundId;
},
true,
@@ -114,6 +117,7 @@ export const getAccountCandidates = ({
if (Object.keys(blameOfUtxos).length > 0) {
logger.info(`Found account candidate for blame round ~~${accountKey}~~`);
return {
...account,
blameOf: blameOfUtxos,
@@ -130,6 +134,7 @@ export const getAccountCandidates = ({
key: account.accountKey,
reason: SessionPhase.SkippingRound,
});
return [];
}
@@ -167,6 +172,7 @@ export const getAccountCandidates = ({
key: account.accountKey,
reason: SessionPhase.BlockedUtxos,
});
return [];
}
}
@@ -205,6 +211,7 @@ export const getAccountCandidates = ({
}
logger.info(`Found account candidate ~~${accountKey}~~ with ${utxos.length} inputs`);
return {
...account,
blameOf: null,
@@ -273,8 +280,10 @@ const selectInputsForBlameRound = ({
if (inputs.length > 0) {
round.inputs.push(...inputs);
logger.info(`Created blame round ~~${round.id}~~ with ${round.inputs.length} inputs`);
return true;
}
return false;
});
@@ -303,6 +312,7 @@ export const selectInputsForRound = async ({
accountCandidates: blameOfAccounts,
options,
});
return blameRound;
}
@@ -323,6 +333,7 @@ export const selectInputsForRound = async ({
AllowedOutputAmounts: roundParameters.AllowedOutputAmounts,
AllowedInputTypes: roundParameters.AllowedInputTypes,
};
return Promise.all(
// ...and for each Account
normalAccounts.map(account => {
@@ -342,6 +353,7 @@ export const selectInputsForRound = async ({
logger.info(
`Skipping round ~~${round.id}~~ for ~~${account.accountKey}~~. Fees to high ${roundParameters.MiningFeeRate} ${roundParameters.CoordinationFeeRate.Rate}`,
);
return [];
}
@@ -372,6 +384,7 @@ export const selectInputsForRound = async ({
.then(indices => indices.filter(i => Utxos[i])) // filter valid existing indices
.catch(error => {
logger.error(`selectInputsForRound failed ${error.message}`);
return [] as number[];
});
}),
@@ -384,6 +397,7 @@ export const selectInputsForRound = async ({
const maxUtxosInRound = Math.max(...sumUtxosInRounds);
if (maxUtxosInRound < 1) {
logger.info('No results from selectInputsForRound');
return;
}
@@ -450,6 +464,7 @@ export const selectRound = async ({
phase: SessionPhase.AffiliateServerOffline,
accountKeys: unregisteredAccountKeys,
});
return;
}
@@ -463,6 +478,7 @@ export const selectRound = async ({
});
if (roundCandidates.length < 1) {
logger.info('No suitable rounds');
return;
}
@@ -477,6 +493,7 @@ export const selectRound = async ({
if (accountCandidates.length < 1) {
logger.info('No suitable accounts');
return;
}
@@ -494,9 +511,11 @@ export const selectRound = async ({
phase: SessionPhase.RetryingRoundPairing,
accountKeys: unregisteredAccountKeys,
});
return;
}
logger.info(`Created new round ~~${newRound.id}~~ with ${newRound.inputs.length} inputs`);
return newRound;
};

View File

@@ -52,6 +52,7 @@ const getTransactionData = (
const { index, hash } = readOutpoint(Coin.Outpoint);
const internal = myInputsInRound.find(a => compareOutpoint(a.outpoint, Coin.Outpoint));
const address = getAddressFromScriptPubKey(Coin.TxOut.ScriptPubKey, options.network);
return {
path: internal?.path,
outpoint: internal?.outpoint || Coin.Outpoint, // NOTE: internal outpoints are in lowercase, coordinators in uppercase
@@ -72,6 +73,7 @@ const getTransactionData = (
o => Output.ScriptPubKey === o.scriptPubKey,
);
const address = getAddressFromScriptPubKey(Output.ScriptPubKey, options.network);
return {
path: internalOutput?.path,
address,
@@ -98,6 +100,7 @@ const updateRawLiquidityClue = async (
const externalAmounts = tx.outputs
.filter(o => !account.changeAddresses.find(addr => addr.address === o.address))
.map(o => o.amount);
return middleware.updateLiquidityClue(
account.rawLiquidityClue,
round.roundParameters.MaxSuggestedAmount,
@@ -174,12 +177,14 @@ export const transactionSigning = async (
inputsWithError.forEach(input => {
logger.error(`Trying to sign input with assigned error ${input.error?.message}`);
});
return round;
}
const alreadyRequested = round.inputs.some(input => input.requested?.type === 'signature');
if (alreadyRequested) {
logger.error(`Signature request was not fulfilled`);
return round;
}
@@ -187,6 +192,7 @@ export const transactionSigning = async (
logger.warn(`Missing affiliate request. Waiting for status`);
round.setSessionPhase(SessionPhase.AwaitingCoinjoinTransaction);
round.transactionSignTries.push(Date.now());
return round;
}
@@ -194,6 +200,7 @@ export const transactionSigning = async (
const resolvedTime = Math.max(
...round.inputs.map(i => {
const res = i.getResolvedRequest('signature');
return res?.timestamp || 0;
}),
);
@@ -211,6 +218,7 @@ export const transactionSigning = async (
);
round.transactionData = transactionData;
round.liquidityClues = liquidityClues;
return round;
}

View File

@@ -63,12 +63,14 @@ export function prefixScriptPubKey(scriptPubKey: string, useHex: false): Buffer;
export function prefixScriptPubKey(scriptPubKey: string, useHex = true) {
const [OP, hash] = scriptPubKey.split(' ');
const script = bscript.fromASM(`OP_${OP} ${hash}`);
return useHex ? script.toString('hex') : script;
}
// return address from WabiSabi.scriptPubKey
export const getAddressFromScriptPubKey = (scriptPubKey: string, network: Network) => {
const script = prefixScriptPubKey(scriptPubKey, false);
return baddress.fromOutputScript(script, network);
};
@@ -88,6 +90,7 @@ export const getOutputSize = (type: AllowedScriptTypes) => {
export const getExternalOutputSize = (scriptPubKey: string) => {
const type = getScriptTypeFromScriptPubKey(scriptPubKey);
return getOutputSize(type);
};
@@ -97,6 +100,7 @@ export const readOutpoint = (outpoint: string) => {
const txid = reader.readSlice(32);
const index = reader.readUInt32();
const hash = bUtils.reverseBuffer(txid).toString('hex');
return { index, hash, txid: txid.toString('hex') };
};
@@ -111,6 +115,7 @@ const compareByteArray = (left: Buffer, right: Buffer) => {
if (left[i] < right[i]) return -1;
if (left[i] > right[i]) return 1;
}
return left.length - right.length;
};
@@ -122,8 +127,10 @@ export const mergePubkeys = (outputs: CoinjoinOutputAddedEvent[]) =>
if (duplicates.length > 1) {
if (a.find(o => o.Output.ScriptPubKey === item.Output.ScriptPubKey)) return a;
const Value = duplicates.reduce((v, b) => v + b.Output.Value, 0);
return a.concat({ ...item, Output: { ...item.Output, Value } });
}
return a.concat(item);
}, [] as CoinjoinOutputAddedEvent[]);
@@ -140,6 +147,7 @@ export const sortInputs = (a: CoinjoinInput, b: CoinjoinInput) => {
export const sortOutputs = (a: CoinjoinOutput, b: CoinjoinOutput) => {
if (a.Value === b.Value)
return compareByteArray(Buffer.from(a.ScriptPubKey), Buffer.from(b.ScriptPubKey));
return b.Value - a.Value;
};

View File

@@ -33,6 +33,7 @@ export const patchResponse = (obj: any) => {
}
});
}
return obj;
};
@@ -54,11 +55,13 @@ const createHeaders = (options: RequestOptions) => {
if (typeof options.userAgent === 'string') {
headers['User-Agent'] = options.userAgent || 'Trezor Suite';
}
return headers;
};
export const httpGet = (url: string, query?: Record<string, any>, options: RequestOptions = {}) => {
const queryString = query ? `?${new URLSearchParams(query)}` : '';
return fetch(`${url}${queryString}`, {
method: 'GET',
signal: options.signal,
@@ -77,5 +80,6 @@ export const httpPost = (url: string, body?: Record<string, any>, options: Reque
// Randomize identity password to reset TOR circuit for this identity
export const resetIdentityCircuit = (identity: string) => {
const [user] = identity.split(':');
return `${user}:${getWeakRandomId(16)}`;
};

View File

@@ -8,6 +8,7 @@ export const redacted = (message: string) =>
match.length - 2,
)}`;
}
return `[redacted]`;
})
: `${message}`;

View File

@@ -31,6 +31,7 @@ export const getRoundParameters = (round: Round) => {
if (events.length < 1) return;
const [{ RoundParameters }] = events;
return RoundParameters;
};
@@ -39,6 +40,7 @@ export const getCommitmentData = (identifier: string, roundId: string) => {
const name = Buffer.from(identifier);
const len = Buffer.allocUnsafe(1);
len.writeUInt8(name.length, 0);
return Buffer.concat([len, name, Buffer.from(roundId, 'hex')]).toString('hex');
};
@@ -104,6 +106,7 @@ export const getCoinjoinRoundDeadlines = (round: PartialCoinjoinRound) => {
case RoundPhase.InputRegistration: {
const deadline =
new Date(round.InputRegistrationEnd).getTime() + ROUND_REGISTRATION_END_OFFSET;
return {
phaseDeadline: deadline,
roundDeadline:
@@ -116,6 +119,7 @@ export const getCoinjoinRoundDeadlines = (round: PartialCoinjoinRound) => {
case RoundPhase.ConnectionConfirmation: {
const deadline =
now + readTimeSpan(round.RoundParameters.ConnectionConfirmationTimeout);
return {
phaseDeadline: deadline,
roundDeadline:
@@ -126,6 +130,7 @@ export const getCoinjoinRoundDeadlines = (round: PartialCoinjoinRound) => {
}
case RoundPhase.OutputRegistration: {
const deadline = now + readTimeSpan(round.RoundParameters.OutputRegistrationTimeout);
return {
phaseDeadline: deadline,
roundDeadline:
@@ -135,6 +140,7 @@ export const getCoinjoinRoundDeadlines = (round: PartialCoinjoinRound) => {
case RoundPhase.TransactionSigning:
case RoundPhase.Ended: {
const deadline = now + readTimeSpan(round.RoundParameters.TransactionSigningTimeout);
return {
phaseDeadline: deadline,
roundDeadline: deadline,
@@ -165,6 +171,7 @@ export const findNearestDeadline = (rounds: Round[]) => {
const deadlines = rounds.map(r => {
const phaseDeadline = estimatePhaseDeadline(r);
const timeLeft = phaseDeadline ? new Date(phaseDeadline).getTime() - now : 0;
return timeLeft > 0 ? timeLeft : now;
});

View File

@@ -65,6 +65,7 @@ describe('CoinjoinAddressController', () => {
controller.analyze(
({ address }) => {
const index = SEGWIT_RECEIVE_ADDRESSES.indexOf(address);
return index % 2 === 0 ? [index] : [];
},
(txs: number[]) => result.push(...txs),

View File

@@ -19,6 +19,7 @@ describe('CoinjoinBackendClient', () => {
let lastBackend = '';
jest.spyOn((client as any).websockets, 'getOrCreate').mockImplementation(args => {
[lastBackend] = (args as any).url.split('/');
return new Proxy({}, { get: (_, b, c) => b !== 'then' && (() => c) });
});
@@ -42,6 +43,7 @@ describe('CoinjoinBackendClient', () => {
jest.spyOn((client as any).websockets, 'getOrCreate').mockImplementation(args => {
const { identity } = args as any;
identities.push(identity);
return Promise.reject(new Error(identity === 'is' ? 'unknown' : WS_ERROR_403));
});
@@ -85,6 +87,7 @@ describe('CoinjoinBackendClient', () => {
const { url, identity } = args as any;
urls.push(url);
identities.push(identity);
return Promise.reject(
new Error(url.includes('.onion') ? WS_ERROR_TIMEOUT : WS_ERROR_403),
);

View File

@@ -33,6 +33,7 @@ describe('CoinjoinMempoolController', () => {
analyze: (getTxs, onTxs) => {
const txs = getTxs({ address: ADDRESS } as AccountAddress);
onTxs?.(txs);
return { receive: [], change: [] };
},
}),

View File

@@ -8,6 +8,7 @@ import * as CONSTANTS from '../../src/constants';
// mock random delay function
jest.mock('@trezor/utils', () => {
const originalModule = jest.requireActual('@trezor/utils');
return {
__esModule: true,
...originalModule,
@@ -18,6 +19,7 @@ jest.mock('@trezor/utils', () => {
// mock ROUND_PHASE_PROCESS_TIMEOUT, use getter to mock individually for each test
jest.mock('../../src/constants', () => {
const originalModule = jest.requireActual('../../src/constants');
return {
__esModule: true,
...originalModule,

View File

@@ -15,6 +15,7 @@ const fastForward = (time: number) => jest.advanceTimersByTimeAsync(time);
// use getters to allow mocking different values in each test case
jest.mock('../../src/constants', () => {
const originalModule = jest.requireActual('../../src/constants');
return {
__esModule: true,
...originalModule,
@@ -64,6 +65,7 @@ describe('Status', () => {
if (url === 'status') {
coordinatorRequestSpy();
}
return Promise.resolve({
...STATUS_EVENT,
RoundStates: [{ ...DEFAULT_ROUND }],
@@ -121,6 +123,7 @@ describe('Status', () => {
if (url === 'status') {
coordinatorRequestSpy();
}
return Promise.resolve({
...STATUS_EVENT,
RoundStates: [{ ...DEFAULT_ROUND, Phase: coordinatorRequestSpy.mock.calls.length }], // increment phase on each request to trigger update event
@@ -164,6 +167,7 @@ describe('Status', () => {
identities.push(id);
}
}
return Promise.resolve({
...STATUS_EVENT,
RoundStates: [{ ...DEFAULT_ROUND }],
@@ -282,6 +286,7 @@ describe('Status', () => {
if (url === 'status') {
coordinatorRequestSpy();
}
return Promise.resolve({
...STATUS_EVENT,
RoundStates: [{ ...round }], // NOTE: always return new reference for the Round from mock

View File

@@ -10,6 +10,7 @@ let server: Awaited<ReturnType<typeof createServer>>;
jest.mock('@trezor/utils', () => {
const originalModule = jest.requireActual('@trezor/utils');
return {
__esModule: true,
...originalModule,
@@ -187,6 +188,7 @@ describe('connectionConfirmation', () => {
if (i > 0) {
return a - timestamps[i - 1];
}
return 0;
})
.forEach(ts => {

View File

@@ -6,6 +6,7 @@ import { createCoinjoinRound } from '../fixtures/round.fixture';
// mock random delay function
jest.mock('@trezor/utils', () => {
const originalModule = jest.requireActual('@trezor/utils');
return {
__esModule: true,
...originalModule,
@@ -211,6 +212,7 @@ describe('inputRegistration', () => {
if (url.endsWith('/input-registration')) {
// respond after phaseDeadline
setTimeout(resolve, 4000);
return;
}
resolve();

View File

@@ -7,6 +7,7 @@ import { createCoinjoinRound } from '../fixtures/round.fixture';
// mock random delay function
jest.mock('@trezor/utils', () => {
const originalModule = jest.requireActual('@trezor/utils');
return {
__esModule: true,
...originalModule,

View File

@@ -357,6 +357,7 @@ describe('selectRound', () => {
if (url.endsWith('/select-inputs-for-round')) {
const Indices = data.Utxos.flatMap((utxo: any, i: number) => {
if (utxo.Amount < data.MiningFeeRate) return [];
return i;
});
resolve({ Indices });
@@ -421,6 +422,7 @@ describe('selectRound', () => {
spy();
const Indices = data.Utxos.flatMap((utxo: any, i: number) => {
if (utxo.Amount < 1000 + data.MiningFeeRate) return [];
return i;
});

View File

@@ -9,6 +9,7 @@ import { createCoinjoinRound } from '../fixtures/round.fixture';
// mock random delay function
jest.mock('@trezor/utils', () => {
const originalModule = jest.requireActual('@trezor/utils');
return {
__esModule: true,
...originalModule,

View File

@@ -34,5 +34,6 @@ export const mockFilterSequence = (
prevHash: filters[i - 1]?.blockHash ?? baseHash,
});
}
return filters;
};

View File

@@ -19,5 +19,6 @@ export const createInput = (
input[key] = options[key];
});
}
return input;
};

View File

@@ -92,6 +92,7 @@ export class MockBackendClient extends CoinjoinBackendClient {
.slice(from, from + count)
.map(({ height, hash, filter }) => `${height}:${hash}:${filter}`),
});
return Promise.reject(new Error('Block not found'));
}
// no default

View File

@@ -10,6 +10,7 @@ export class MockFilterClient implements FilterClient {
fetchNetworkInfo(): ReturnType<FilterClient['fetchNetworkInfo']> {
const tip = this.filters[this.filters.length - 1];
return Promise.resolve({ bestHeight: tip.blockHeight } as any);
}
@@ -22,6 +23,7 @@ export class MockFilterClient implements FilterClient {
return Promise.resolve({ status: 'up-to-date' });
}
const from = this.filters.findIndex(f => f.prevHash === knownHash);
return from < 0
? Promise.resolve({ status: 'not-found' })
: Promise.resolve({

View File

@@ -26,6 +26,7 @@ export class MockMempoolClient implements MempoolClient {
fetchTransaction(txid: string) {
const tx = this.mempoolTxs.find(t => t.txid === txid);
return tx
? Promise.resolve(tx as unknown as BlockbookTransaction)
: Promise.reject(new Error('not found'));
@@ -35,11 +36,13 @@ export class MockMempoolClient implements MempoolClient {
subscribeMempoolTxs(listener: (tx: BlockbookTransaction) => void) {
this.listener = listener;
return Promise.resolve();
}
unsubscribeMempoolTxs(_listener: (tx: BlockbookTransaction) => void) {
this.listener = undefined;
return Promise.resolve();
}
}

View File

@@ -21,6 +21,7 @@ const DEFAULT = {
if (!data || !data.AmountsToRequest) {
return { RealCredentialRequests: {} };
}
return {
RealCredentialRequests: {
CredentialsRequest: {
@@ -52,6 +53,7 @@ const DEFAULT = {
if (!data || !data.CredentialsResponse) {
return { Credentials: [{}, {}] };
}
return {
Credentials: data.CredentialsResponse,
};
@@ -93,6 +95,7 @@ const DEFAULT = {
if (!data || !data.RealAmountCredentialRequests) {
return {};
}
return {
RealAmountCredentials: data.RealAmountCredentialRequests.Requested.map((a: number) => ({
Value: a,
@@ -150,6 +153,7 @@ const handleRequest = (
response.setHeader('Content-Type', 'application/json');
response.write(JSON.stringify(testResponse));
response.end();
return;
}

View File

@@ -50,6 +50,7 @@ const CACHE_PARAMS = `${CACHE_DIR}/anonymityScoreParams.json`;
const originalWrite = req.write.bind(req);
req.write = (chunk: Buffer) => {
chunks.push(chunk);
return originalWrite(chunk);
};

View File

@@ -59,6 +59,7 @@ https.request = (...args) => {
response.on('response', res => {
res.on('data', (data: Buffer) => (bytes += data.byteLength));
});
return response;
};
@@ -68,6 +69,7 @@ http.request = (...args) => {
response.on('response', res => {
res.on('data', (data: Buffer) => (bytes += data.byteLength));
});
return response;
};
@@ -95,8 +97,10 @@ const filtersFromWasabi = async (hash: string) => {
const buffer = await response.buffer();
const { filters }: { filters: string[] } = JSON.parse(buffer.toString());
log(hash, bytes, buffer.byteLength);
return filters.map(data => {
const [_blockHeight, blockHash, _filter, _prevHash, _blockTime] = data.split(':');
return blockHash;
});
}
@@ -132,6 +136,7 @@ const getWebsocket = async () => {
return (bestKnownBlockHash: string) => {
compressed = 0;
const reqID = (messageID++).toString();
return new Promise<string[]>((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error('timeout')), TIMEOUT);
ws.once('message', (message: string) => {

View File

@@ -14,6 +14,7 @@ import { DEFAULT_ROUND, STATUS_EVENT, STATUS_TRANSFORMED } from '../fixtures/rou
// mock random delay function
jest.mock('@trezor/utils', () => {
const originalModule = jest.requireActual('@trezor/utils');
return {
__esModule: true,
...originalModule,

View File

@@ -166,6 +166,7 @@ export const ConfirmOnDevice = ({
}: ConfirmOnDeviceProps) => {
const hasSteps = steps && activeStep !== undefined;
const theme = useTheme();
return (
<Wrapper
animation={isConfirmed ? AnimationDirection.Down : AnimationDirection.Up}

View File

@@ -177,6 +177,7 @@ export const Dropdown = forwardRef(
// do not loose focus when clicking within the menu
if (!content && document.activeElement === menuRef.current) {
toggleRef.current?.focus();
return;
}

View File

@@ -51,6 +51,7 @@ const getColor = (score: OptionalZXCVBNScore, password: string) => {
const getPasswordScore = async (password: string) => {
const zxcvbn = await import(/* webpackChunkName: "zxcvbn" */ 'zxcvbn');
return zxcvbn.default(password).score;
};

View File

@@ -38,6 +38,7 @@ export default {
export const All: StoryFn = () => {
const flags = Object.keys(FLAGS) as FlagType[];
return (
<Wrapper>
{flags.map(country => (

View File

@@ -281,6 +281,7 @@ export const Select = ({
selectRef.current?.blur();
}
}
return null;
},
[onChange, menuIsOpen],

View File

@@ -28,5 +28,6 @@ export const shimmerEffect = css<{ elevation: Elevation }>`
export const getValue = (value: string | number | undefined) => {
if (!value) return null;
if (typeof value === 'number') return `${value}px`;
return value;
};

View File

@@ -29,6 +29,7 @@ type EaringType = keyof typeof motionEasing;
export const motionEasingStrings = Object.entries(motionEasing).reduce(
(acc: Record<EaringType, string>, [key, value]) => {
acc[key as EaringType] = `cubic-bezier(${value.join(',')})`;
return acc;
},
{} as Record<EaringType, string>,

View File

@@ -12,6 +12,7 @@ const ThemeProvider = ({ children, theme }: ThemeProviderProps) => {
if (!children) {
return null;
}
return <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>;
};

View File

@@ -14,6 +14,7 @@ export const getInputColor = (theme: DefaultTheme, { checked, disabled }: InputC
if (!checked) {
return theme.backgroundNeutralDisabled;
}
return disabled ? theme.backgroundPrimarySubtleOnElevation0 : theme.backgroundPrimaryDefault;
};
@@ -21,6 +22,7 @@ export const getLabelColor = (theme: DefaultTheme, { alert, disabled }: LabelCol
if (alert) {
return theme.borderAlertRed;
}
return disabled ? theme.textDisabled : theme.textDefault;
};

View File

@@ -103,6 +103,7 @@ export abstract class AbstractMessageChannel<
this.handshakeWithPeer();
}
}
return this.handshakeFinished.promise;
}
@@ -112,6 +113,7 @@ export abstract class AbstractMessageChannel<
*/
protected handshakeWithPeer(): Promise<void> {
this.logger?.log(this.channel.here, 'handshake');
return scheduleAction(
async () => {
this.postMessage(
@@ -169,10 +171,12 @@ export abstract class AbstractMessageChannel<
if (!this.legacyMode) {
if (!channel?.peer || channel.peer !== this.channel.here) {
this.logger?.warn('to wrong peer', channel?.peer, 'should be', this.channel.here);
return;
}
if (!channel?.here || this.channel.peer !== channel.here) {
this.logger?.warn('from wrong peer', channel?.here, 'should be', this.channel.peer);
return;
}
}
@@ -189,10 +193,12 @@ export abstract class AbstractMessageChannel<
// When received channel-handshake-request in lazyHandshake mode we start from this side.
this.handshakeWithPeer();
}
return;
}
if (type === 'channel-handshake-confirm') {
this.handshakeFinished?.resolve(undefined);
return;
}
@@ -222,6 +228,7 @@ export abstract class AbstractMessageChannel<
this.messagesQueue.push(message);
}
}
return;
}

View File

@@ -104,6 +104,7 @@ class Storage extends TypedEmitter<Events> {
} catch (err) {
// memory storage is fallback of the last resort
console.warn('long term storage not available');
return memoryStorage;
}
}

Some files were not shown because too many files have changed in this diff Show More