Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions chrome-extension/public/injected.js

Large diffs are not rendered by default.

45 changes: 24 additions & 21 deletions chrome-extension/src/background/chains/bitcoinCashHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { v4 as uuidv4 } from 'uuid';
import { Chain, ChainToNetworkId, shortListSymbolToCaip, caipToNetworkId } from '../chainConfig';
import * as wallet from '../wallet';
import { createProviderRpcError } from '../utils';
import { fetchJsonWithTimeout } from '../fetchUtils';

const TAG = ' | bitcoinCashHandler | ';

Expand Down Expand Up @@ -41,24 +42,22 @@ export const handleBitcoinCashRequest = async (
isMax: params[0].isMax,
};

const buildTx = async function () {
try {
const buildResponse = await fetch('https://api.keepkey.info/api/v1/buildTx', {
// Build before approval — see bitcoinHandler for the race rationale.
let unsignedTx: any;
try {
unsignedTx = await fetchJsonWithTimeout<any>(
'https://api.keepkey.info/api/v1/buildTx',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...sendPayload, pubkeys }),
});
const unsignedTx = await buildResponse.json();
const storedEvent = await requestStorage.getEventById(requestInfo.id);
storedEvent.unsignedTx = unsignedTx;
await requestStorage.updateEventById(requestInfo.id, storedEvent);
chrome.runtime.sendMessage({ action: 'utxo_build_tx', unsignedTx: requestInfo });
} catch (e) {
console.error(e);
chrome.runtime.sendMessage({ action: 'transaction_error', eventId: requestInfo.id, error: JSON.stringify(e) });
}
};
buildTx();
},
{ timeoutMs: 15000, retries: 1 },
);
} catch (e) {
console.error(tag, 'buildTx failed:', e);
throw createProviderRpcError(4000, `Failed to build transaction: ${(e as Error)?.message || e}`);
}

const event = {
id: requestInfo.id,
Expand All @@ -74,6 +73,7 @@ export const handleBitcoinCashRequest = async (
injectScriptVersion: requestInfo.version,
chain: 'bitcoincash',
requestInfo,
unsignedTx,
type: 'transfer',
request: params,
status: 'request',
Expand All @@ -92,12 +92,15 @@ export const handleBitcoinCashRequest = async (
response.signedTx = signedTx;
await requestStorage.updateEventById(requestInfo.id, response);

const broadcastResponse = await fetch('https://api.keepkey.info/api/v1/broadcastTx', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ caip, signedTx: signedTx.serializedTx || signedTx }),
});
let txHash = await broadcastResponse.json();
let txHash: any = await fetchJsonWithTimeout<any>(
'https://api.keepkey.info/api/v1/broadcastTx',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ caip, signedTx: signedTx.serializedTx || signedTx }),
},
{ timeoutMs: 15000, retries: 1 },
);
if (txHash.txHash) txHash = txHash.txHash;
if (txHash.txid) txHash = txHash.txid;
response.txid = txHash;
Expand Down
72 changes: 39 additions & 33 deletions chrome-extension/src/background/chains/bitcoinHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { v4 as uuidv4 } from 'uuid';
import { Chain, ChainToNetworkId, shortListSymbolToCaip, caipToNetworkId } from '../chainConfig';
import * as wallet from '../wallet';
import { createProviderRpcError } from '../utils';
import { fetchJsonWithTimeout } from '../fetchUtils';

const TAG = ' | bitcoinHandler | ';

Expand Down Expand Up @@ -62,35 +63,27 @@ export const handleBitcoinRequest = async (
};
console.log(tag, 'Send Payload: ', sendPayload);

// Build UTXO transaction via Pioneer API HTTP call
const buildTx = async function () {
try {
const buildResponse = await fetch('https://api.keepkey.info/api/v1/buildTx', {
// Build the unsigned tx BEFORE creating the approval event so the
// event always carries an unsignedTx the moment the user sees it.
// The previous fire-and-forget pattern raced: if the user approved
// before buildTx resolved, response.unsignedTx was undefined; if
// buildTx finished before addEvent, getEventById returned null.
let unsignedTx: any;
try {
unsignedTx = await fetchJsonWithTimeout<any>(
'https://api.keepkey.info/api/v1/buildTx',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...sendPayload, pubkeys }),
});
const unsignedTx = await buildResponse.json();
console.log(tag, 'unsignedTx: ', unsignedTx);

const storedEvent = await requestStorage.getEventById(requestInfo.id);
storedEvent.unsignedTx = unsignedTx;
await requestStorage.updateEventById(requestInfo.id, storedEvent);

chrome.runtime.sendMessage({
action: 'utxo_build_tx',
unsignedTx: requestInfo,
});
} catch (e) {
console.error(e);
chrome.runtime.sendMessage({
action: 'transaction_error',
eventId: requestInfo.id,
error: JSON.stringify(e),
});
}
};
buildTx();
},
{ timeoutMs: 15000, retries: 1 },
);
console.log(tag, 'unsignedTx: ', unsignedTx);
} catch (e) {
console.error(tag, 'buildTx failed:', e);
throw createProviderRpcError(4000, `Failed to build transaction: ${(e as Error)?.message || e}`);
}

const event = {
id: requestInfo.id,
Expand All @@ -106,6 +99,7 @@ export const handleBitcoinRequest = async (
injectScriptVersion: requestInfo.version,
chain: 'bitcoin',
requestInfo,
unsignedTx,
type: 'transfer',
request: params,
status: 'request',
Expand All @@ -131,13 +125,20 @@ export const handleBitcoinRequest = async (
await requestStorage.updateEventById(requestInfo.id, response);

try {
// Broadcast via Pioneer API
const broadcastResponse = await fetch('https://api.keepkey.info/api/v1/broadcastTx', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ caip, signedTx: signedTx.serializedTx || signedTx }),
});
let txHash = await broadcastResponse.json();
// Broadcast via Pioneer API. fetchJsonWithTimeout enforces an
// explicit response.ok check + retry on 5xx — without that,
// a transient Pioneer hiccup (e.g. node failover) would either
// hang the dApp or surface as a malformed JSON error from the
// raw `await response.json()` below.
let txHash: any = await fetchJsonWithTimeout<any>(
'https://api.keepkey.info/api/v1/broadcastTx',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ caip, signedTx: signedTx.serializedTx || signedTx }),
},
{ timeoutMs: 15000, retries: 1 },
);
if (txHash.txHash) txHash = txHash.txHash;
if (txHash.txid) txHash = txHash.txid;

Expand All @@ -158,6 +159,11 @@ export const handleBitcoinRequest = async (
eventId: requestInfo.id,
error: JSON.stringify(e),
});
// Re-throw so the dApp sees the actual broadcast error. Without
// this the case falls through to `default:` below and the dApp
// gets "Method transfer not supported" instead of the real
// failure (timeout, HTTP 5xx, etc.).
throw e instanceof Error ? e : createProviderRpcError(4000, `Broadcast failed: ${String(e)}`);
}
} else {
throw createProviderRpcError(4200, 'User denied transaction');
Expand Down
33 changes: 20 additions & 13 deletions chrome-extension/src/background/chains/cosmosHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { v4 as uuidv4 } from 'uuid';
import { Chain, ChainToNetworkId, shortListSymbolToCaip, caipToNetworkId } from '../chainConfig';
import * as wallet from '../wallet';
import { createProviderRpcError } from '../utils';
import { fetchJsonWithTimeout } from '../fetchUtils';

const TAG = ' | cosmosHandler | ';

Expand Down Expand Up @@ -43,16 +44,19 @@ export const handleCosmosRequest = async (

let unsignedTx: any;
try {
const buildResponse = await fetch('https://api.keepkey.info/api/v1/buildTx', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...sendPayload, pubkeys }),
});
unsignedTx = await buildResponse.json();
unsignedTx = await fetchJsonWithTimeout<any>(
'https://api.keepkey.info/api/v1/buildTx',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...sendPayload, pubkeys }),
},
{ timeoutMs: 15000, retries: 1 },
);
console.log(tag, 'unsignedTx: ', unsignedTx);
} catch (e) {
console.error(tag, 'buildTx failed:', e);
throw createProviderRpcError(4000, 'Failed to build transaction');
throw createProviderRpcError(4000, `Failed to build transaction: ${(e as Error)?.message || e}`);
}

const event = {
Expand Down Expand Up @@ -90,12 +94,15 @@ export const handleCosmosRequest = async (
response.signedTx = signedTx;
await requestStorage.updateEventById(requestInfo.id, response);

const broadcastResponse = await fetch('https://api.keepkey.info/api/v1/broadcastTx', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ caip, signedTx: signedTx.serializedTx || signedTx }),
});
let txHash = await broadcastResponse.json();
let txHash: any = await fetchJsonWithTimeout<any>(
'https://api.keepkey.info/api/v1/broadcastTx',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ caip, signedTx: signedTx.serializedTx || signedTx }),
},
{ timeoutMs: 15000, retries: 1 },
);
if (txHash.txHash) txHash = txHash.txHash;
if (txHash.txid) txHash = txHash.txid;

Expand Down
45 changes: 24 additions & 21 deletions chrome-extension/src/background/chains/dashHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { v4 as uuidv4 } from 'uuid';
import { Chain, ChainToNetworkId, shortListSymbolToCaip, caipToNetworkId } from '../chainConfig';
import * as wallet from '../wallet';
import { createProviderRpcError } from '../utils';
import { fetchJsonWithTimeout } from '../fetchUtils';

const TAG = ' | dashHandler | ';

Expand Down Expand Up @@ -41,24 +42,22 @@ export const handleDashRequest = async (
isMax: params[0].isMax,
};

const buildTx = async function () {
try {
const buildResponse = await fetch('https://api.keepkey.info/api/v1/buildTx', {
// Build before approval — see bitcoinHandler for the race rationale.
let unsignedTx: any;
try {
unsignedTx = await fetchJsonWithTimeout<any>(
'https://api.keepkey.info/api/v1/buildTx',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...sendPayload, pubkeys }),
});
const unsignedTx = await buildResponse.json();
const storedEvent = await requestStorage.getEventById(requestInfo.id);
storedEvent.unsignedTx = unsignedTx;
await requestStorage.updateEventById(requestInfo.id, storedEvent);
chrome.runtime.sendMessage({ action: 'utxo_build_tx', unsignedTx: requestInfo });
} catch (e) {
console.error(e);
chrome.runtime.sendMessage({ action: 'transaction_error', eventId: requestInfo.id, error: JSON.stringify(e) });
}
};
buildTx();
},
{ timeoutMs: 15000, retries: 1 },
);
} catch (e) {
console.error(tag, 'buildTx failed:', e);
throw createProviderRpcError(4000, `Failed to build transaction: ${(e as Error)?.message || e}`);
}

const event = {
id: requestInfo.id,
Expand All @@ -74,6 +73,7 @@ export const handleDashRequest = async (
injectScriptVersion: requestInfo.version,
chain: 'dash',
requestInfo,
unsignedTx,
type: 'transfer',
request: params,
status: 'request',
Expand All @@ -92,12 +92,15 @@ export const handleDashRequest = async (
response.signedTx = signedTx;
await requestStorage.updateEventById(requestInfo.id, response);

const broadcastResponse = await fetch('https://api.keepkey.info/api/v1/broadcastTx', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ caip, signedTx: signedTx.serializedTx || signedTx }),
});
let txHash = await broadcastResponse.json();
let txHash: any = await fetchJsonWithTimeout<any>(
'https://api.keepkey.info/api/v1/broadcastTx',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ caip, signedTx: signedTx.serializedTx || signedTx }),
},
{ timeoutMs: 15000, retries: 1 },
);
if (txHash.txHash) txHash = txHash.txHash;
if (txHash.txid) txHash = txHash.txid;
response.txid = txHash;
Expand Down
45 changes: 24 additions & 21 deletions chrome-extension/src/background/chains/dogecoinHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { v4 as uuidv4 } from 'uuid';
import { Chain, ChainToNetworkId, shortListSymbolToCaip, caipToNetworkId } from '../chainConfig';
import * as wallet from '../wallet';
import { createProviderRpcError } from '../utils';
import { fetchJsonWithTimeout } from '../fetchUtils';

const TAG = ' | dogecoinHandler | ';

Expand Down Expand Up @@ -44,24 +45,22 @@ export const handleDogecoinRequest = async (
isMax: params[0].isMax,
};

const buildTx = async function () {
try {
const buildResponse = await fetch('https://api.keepkey.info/api/v1/buildTx', {
// Build before approval — see bitcoinHandler for the race rationale.
let unsignedTx: any;
try {
unsignedTx = await fetchJsonWithTimeout<any>(
'https://api.keepkey.info/api/v1/buildTx',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...sendPayload, pubkeys }),
});
const unsignedTx = await buildResponse.json();
const storedEvent = await requestStorage.getEventById(requestInfo.id);
storedEvent.unsignedTx = unsignedTx;
await requestStorage.updateEventById(requestInfo.id, storedEvent);
chrome.runtime.sendMessage({ action: 'utxo_build_tx', unsignedTx: requestInfo });
} catch (e) {
console.error(e);
chrome.runtime.sendMessage({ action: 'transaction_error', eventId: requestInfo.id, error: JSON.stringify(e) });
}
};
buildTx();
},
{ timeoutMs: 15000, retries: 1 },
);
} catch (e) {
console.error(tag, 'buildTx failed:', e);
throw createProviderRpcError(4000, `Failed to build transaction: ${(e as Error)?.message || e}`);
}

const event = {
id: requestInfo.id,
Expand All @@ -77,6 +76,7 @@ export const handleDogecoinRequest = async (
injectScriptVersion: requestInfo.version,
chain: 'dogecoin',
requestInfo,
unsignedTx,
type: 'transfer',
request: params,
status: 'request',
Expand All @@ -95,12 +95,15 @@ export const handleDogecoinRequest = async (
response.signedTx = signedTx;
await requestStorage.updateEventById(requestInfo.id, response);

const broadcastResponse = await fetch('https://api.keepkey.info/api/v1/broadcastTx', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ caip, signedTx: signedTx.serializedTx || signedTx }),
});
let txHash = await broadcastResponse.json();
let txHash: any = await fetchJsonWithTimeout<any>(
'https://api.keepkey.info/api/v1/broadcastTx',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ caip, signedTx: signedTx.serializedTx || signedTx }),
},
{ timeoutMs: 15000, retries: 1 },
);
if (txHash.txHash) txHash = txHash.txHash;
if (txHash.txid) txHash = txHash.txid;
response.txid = txHash;
Expand Down
Loading
Loading