Skip to content

Commit

Permalink
refactor: get bitcoin history from vm module (#7)
Browse files Browse the repository at this point in the history
Co-authored-by: Gergely Lovas <[email protected]>
  • Loading branch information
meeh0w and gergelylovas committed Aug 13, 2024
1 parent 09373dc commit 243e747
Show file tree
Hide file tree
Showing 16 changed files with 160 additions and 264 deletions.
28 changes: 16 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,23 @@
"sentry": "node sentryscript.js"
},
"dependencies": {
"@avalabs/vm-module-types": "0.1.0",
"@avalabs/avalanchejs": "4.0.5",
"@avalabs/bitcoin-module": "0.1.4",
"@avalabs/bridge-unified": "2.1.0",
"@avalabs/core-bridge-sdk": "3.0.0",
"@avalabs/core-chains-sdk": "3.0.0",
"@avalabs/core-coingecko-sdk": "3.0.0",
"@avalabs/core-covalent-sdk": "3.0.0",
"@avalabs/core-etherscan-sdk": "3.0.0",
"@avalabs/core-snowtrace-sdk": "3.0.0",
"@avalabs/core-token-prices-sdk": "3.0.0",
"@avalabs/core-utils-sdk": "3.0.0",
"@avalabs/core-wallets-sdk": "3.0.0",
"@avalabs/core-bridge-sdk": "3.0.1-alpha.1",
"@avalabs/core-chains-sdk": "3.0.1-alpha.1",
"@avalabs/core-coingecko-sdk": "3.0.1-alpha.1",
"@avalabs/core-covalent-sdk": "3.0.1-alpha.1",
"@avalabs/core-etherscan-sdk": "3.0.1-alpha.1",
"@avalabs/core-snowtrace-sdk": "3.0.1-alpha.1",
"@avalabs/core-token-prices-sdk": "3.0.1-alpha.1",
"@avalabs/core-utils-sdk": "3.0.1-alpha.1",
"@avalabs/core-wallets-sdk": "3.0.1-alpha.1",
"@avalabs/glacier-sdk": "2.8.0-alpha.188",
"@avalabs/hw-app-avalanche": "0.14.1",
"@avalabs/core-k2-components": "4.18.0-alpha.47",
"@avalabs/types": "2.8.0-alpha.188",
"@avalabs/types": "3.0.1-alpha.1",
"@avalabs/vm-module-types": "0.1.4",
"@blockaid/client": "0.10.0",
"@coinbase/cbpay-js": "1.6.0",
"@cubist-labs/cubesigner-sdk": "0.3.28",
Expand Down Expand Up @@ -238,7 +239,10 @@
"@avalabs/vm-module-types>@avalabs/wallets-sdk>hdkey>secp256k1": false,
"@avalabs/core-bridge-sdk>@avalabs/core-wallets-sdk>@avalabs/hw-app-avalanche>@ledgerhq/hw-app-eth>@ledgerhq/domain-service>eip55>keccak": false,
"@avalabs/core-bridge-sdk>@avalabs/core-wallets-sdk>@ledgerhq/hw-app-btc>bitcoinjs-lib>bip32>tiny-secp256k1": false,
"@avalabs/core-bridge-sdk>@avalabs/core-wallets-sdk>hdkey>secp256k1": false
"@avalabs/core-bridge-sdk>@avalabs/core-wallets-sdk>hdkey>secp256k1": false,
"@avalabs/bitcoin-module>@avalabs/core-wallets-sdk>@avalabs/hw-app-avalanche>@ledgerhq/hw-app-eth>@ledgerhq/domain-service>eip55>keccak": false,
"@avalabs/bitcoin-module>@avalabs/core-wallets-sdk>@ledgerhq/hw-app-btc>bitcoinjs-lib>bip32>tiny-secp256k1": false,
"@avalabs/bitcoin-module>@avalabs/core-wallets-sdk>hdkey>secp256k1": false
}
}
}
3 changes: 3 additions & 0 deletions src/background/runtime/BackgroundRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { singleton } from 'tsyringe';
import { LockService } from '@src/background/services/lock/LockService';
import { OnboardingService } from '@src/background/services/onboarding/OnboardingService';
import { BridgeService } from '@src/background/services/bridge/BridgeService';
import ModuleManager from '../vmModules/ModuleManager';

@singleton()
export class BackgroundRuntime {
Expand All @@ -20,6 +21,8 @@ export class BackgroundRuntime {
this.registerInpageScript();
this.addContextMenus();

ModuleManager.init();

// Activate services which need to run all the or are required for bootstraping the wallet state
this.connectionService.activate();
this.lockService.activate();
Expand Down
1 change: 1 addition & 0 deletions src/background/services/balances/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ interface TokenBalanceDataWithDecimals extends TokenBalanceData {
decimals: number;
}

// TODO: remove TokenType once all VM modules are integrated (it should no longer be needed at this point)
export enum TokenType {
NATIVE = 'NATIVE',
ERC20 = 'ERC20',
Expand Down
4 changes: 3 additions & 1 deletion src/background/services/balances/nft/utils/isNFT.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { TokenType as VMModulesTokenType } from '@avalabs/vm-module-types';

import { TokenType } from '../../models';

export function isNFT(tokenType: TokenType) {
export function isNFT(tokenType: TokenType | VMModulesTokenType) {
return tokenType === TokenType.ERC721 || tokenType === TokenType.ERC1155;
}
2 changes: 2 additions & 0 deletions src/background/services/history/HistoryService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ describe('src/background/services/history/HistoryService.ts', () => {
const result = await service.getTxHistory({
...network1,
vmName: NetworkVMType.BITCOIN,
caipId: 'bip122:000000000019d6689c085ae165831e93',
});
expect(btcHistoryServiceMock.getHistory).toHaveBeenCalledTimes(1);
expect(result).toEqual([btcTxHistoryItem]);
Expand All @@ -188,6 +189,7 @@ describe('src/background/services/history/HistoryService.ts', () => {
const result = await service.getTxHistory({
...network1,
vmName: NetworkVMType.BITCOIN,
caipId: 'bip122:000000000019d6689c085ae165831e93',
});
expect(btcHistoryServiceMock.getHistory).toHaveBeenCalledTimes(1);
expect(result).toEqual([btcTxHistoryItem]);
Expand Down
4 changes: 2 additions & 2 deletions src/background/services/history/HistoryService.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { singleton } from 'tsyringe';
import { NetworkVMType } from '@avalabs/core-chains-sdk';

import { Network } from '../network/models';
import { NetworkWithCaipId } from '../network/models';
import { GlacierService } from '../glacier/GlacierService';
import { isPchainNetwork } from '../network/utils/isAvalanchePchainNetwork';
import { isXchainNetwork } from '../network/utils/isAvalancheXchainNetwork';
Expand All @@ -24,7 +24,7 @@ export class HistoryService {
private historyServicePVM: HistoryServicePVM
) {}

async getTxHistory(network: Network) {
async getTxHistory(network: NetworkWithCaipId) {
const isSupportedNetwork = await this.glacierService.isNetworkSupported(
network.chainId
);
Expand Down
111 changes: 32 additions & 79 deletions src/background/services/history/HistoryServiceBTC.ts
Original file line number Diff line number Diff line change
@@ -1,103 +1,56 @@
import { Blockchain } from '@avalabs/core-bridge-sdk';
import {
BITCOIN_NETWORK,
Network,
NetworkVMType,
} from '@avalabs/core-chains-sdk';
import { BitcoinHistoryTx, BitcoinProvider } from '@avalabs/core-wallets-sdk';
import { getExplorerAddress } from '@src/utils/getExplorerAddress';
import { NetworkVMType } from '@avalabs/core-chains-sdk';
import { singleton } from 'tsyringe';
import { AccountsService } from '../accounts/AccountsService';
import { TokenType } from '../balances/models';
import { NetworkService } from '../network/NetworkService';
import { HistoryServiceBridgeHelper } from './HistoryServiceBridgeHelper';
import { TransactionType, TxHistoryItem } from './models';
import { getProviderForNetwork } from '@src/utils/network/getProviderForNetwork';
import ModuleManager from '@src/background/vmModules/ModuleManager';
import { NetworkWithCaipId } from '../network/models';
import sentryCaptureException, {
SentryExceptionTypes,
} from '@src/monitoring/sentryCaptureException';

@singleton()
export class HistoryServiceBTC {
constructor(
private networkService: NetworkService,
private accountsService: AccountsService,
private bridgeHistoryHelperService: HistoryServiceBridgeHelper
) {}

private bitcoinAmount(amount: number, demonimation: number) {
if (amount < 0) {
amount = amount * -1;
}
return (amount / Math.pow(10, demonimation)).toString();
}

private txHistoryItemConverter(
tx: BitcoinHistoryTx,
network: Network
): TxHistoryItem {
const userAddress = this.accountsService.activeAccount?.addressBTC
? this.accountsService.activeAccount?.addressBTC
: '';
const txAddress = tx.addresses[0] ? tx.addresses[0] : '';
const denomination = BITCOIN_NETWORK.networkToken.decimals;
const isBridge = this.bridgeHistoryHelperService.isBridgeTransactionBTC(tx);
const type = isBridge
? TransactionType.BRIDGE
: tx.isSender
? TransactionType.SEND
: TransactionType.RECEIVE;
return {
isBridge,
isIncoming: !tx.isSender,
isOutgoing: tx.isSender,
isContractCall: false,
timestamp: new Date(tx.receivedTime * 1000).toISOString(),
hash: tx.hash,
isSender: tx.isSender,
from: tx.isSender ? userAddress : txAddress,
to: tx.isSender ? txAddress : userAddress,
tokens: [
{
decimal: denomination.toString(),
name: BITCOIN_NETWORK.networkToken.name,
symbol: BITCOIN_NETWORK.networkToken.symbol,
amount: this.bitcoinAmount(tx.amount, denomination),
type: TokenType.NATIVE,
},
],
gasUsed: tx.fee.toString(),
explorerLink: getExplorerAddress(
Blockchain.BITCOIN,
tx.hash,
network.chainId === BITCOIN_NETWORK.chainId
),
chainId: network.chainId.toString(),
type,
};
}

async getHistory(network: Network): Promise<TxHistoryItem[]> {
async getHistory(network: NetworkWithCaipId): Promise<TxHistoryItem[]> {
if (network?.vmName !== NetworkVMType.BITCOIN) {
return [];
}
const account = this.accountsService.activeAccount?.addressBTC;
const address = this.accountsService.activeAccount?.addressBTC;

if (!account) {
if (!address) {
return [];
}
const provider = getProviderForNetwork(network) as BitcoinProvider;

try {
const txHistory: BitcoinHistoryTx[] = await provider.getTxHistory(
account
);
const results: TxHistoryItem[] = [];
txHistory.forEach((tx) => {
const converted = this.txHistoryItemConverter(tx, network);
if (converted) {
results.push(converted);
}
const module = await ModuleManager.loadModuleByNetwork(network);
const { transactions } = await module.getTransactionHistory({
address,
network,
});

return transactions.map((tx) => {
const isBridge = this.bridgeHistoryHelperService.isBridgeTransactionBTC(
[tx.from, tx.to]
);

return {
...tx,
// BitcoinModule is not able to recognize bridge txs at the moment, so we need to do it here.
isBridge,
type: isBridge
? TransactionType.BRIDGE
: tx.isSender
? TransactionType.SEND
: TransactionType.RECEIVE,
};
});
return results;
} catch (error) {
} catch (error: any) {
sentryCaptureException(error, SentryExceptionTypes.INTERNAL_ERROR);
return [];
}
}
Expand Down
5 changes: 2 additions & 3 deletions src/background/services/history/HistoryServiceBridgeHelper.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ETHEREUM_ADDRESS } from '@src/utils/bridgeTransactionUtils';
import { BridgeService } from '../bridge/BridgeService';
import { BitcoinHistoryTx } from '@avalabs/core-wallets-sdk';
import { singleton } from 'tsyringe';
import { Erc20Tx } from '@avalabs/core-snowtrace-sdk';
import { Network } from '@avalabs/core-chains-sdk';
Expand Down Expand Up @@ -77,7 +76,7 @@ export class HistoryServiceBridgeHelper {
* config.criticalBitcoin?.walletAddresses.btc or
* config.criticalBitcoin?.walletAddresses.avalanche
*/
isBridgeTransactionBTC(tx: BitcoinHistoryTx): boolean {
isBridgeTransactionBTC(addresses: string[]): boolean {
const config = this.bridgeService.bridgeConfig;
const bitcoinWalletAddresses =
config?.config?.criticalBitcoin?.walletAddresses;
Expand All @@ -86,7 +85,7 @@ export class HistoryServiceBridgeHelper {
return false;
}

return tx.addresses.some((address) => {
return addresses.some((address) => {
return [
bitcoinWalletAddresses.btc,
bitcoinWalletAddresses.avalanche,
Expand Down
4 changes: 3 additions & 1 deletion src/background/services/history/HistoryServiceETH.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ export class HistoryServiceETH {

// Sort by timestamp
const joined = [...filteredNormalTxs, ...erc20Hist];
return joined.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
return joined.sort((a, b) =>
(b.timestamp as string).localeCompare(a.timestamp as string)
);
}
}
4 changes: 2 additions & 2 deletions src/background/services/history/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
RichAddress,
XChainTransactionType,
} from '@avalabs/glacier-sdk';
import { TokenType } from '../balances/models';
import { TokenType } from '@avalabs/vm-module-types';

export interface TxHistoryItemToken {
decimal?: string;
Expand All @@ -22,7 +22,7 @@ export interface TxHistoryItem {
isIncoming: boolean;
isOutgoing: boolean;
isSender: boolean;
timestamp: string;
timestamp: string | number;
hash: string;
from: string;
to: string;
Expand Down
2 changes: 1 addition & 1 deletion src/background/vmModules/ModuleManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe('ModuleManager', () => {
},
{
chainId: 'bip122:000000000019d6689c085ae165831e93',
method: 'bitcoin_randomMethod',
method: 'bitcoin_sendTransaction',
name: NetworkVMType.BITCOIN,
},
{
Expand Down
13 changes: 10 additions & 3 deletions src/background/vmModules/ModuleManager.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { Module } from '@avalabs/vm-module-types';
import { Environment, Module } from '@avalabs/vm-module-types';
import { BitcoinModule } from '@avalabs/bitcoin-module';
import { ethErrors } from 'eth-rpc-errors';

import { assertPresent } from '@src/utils/assertions';
import { isDevelopment } from '@src/utils/environment';

import { NetworkWithCaipId } from '../services/network/models';

import { AVMModule } from './mocks/avm';
import { EVMModule } from './mocks/evm';
import { PVMModule } from './mocks/pvm';
import { BitcoinModule } from './mocks/bitcoin';
import { CoreEthModule } from './mocks/coreEth';
import { VMModuleError } from './models';

Expand All @@ -32,9 +33,15 @@ class ModuleManager {
async init(): Promise<void> {
if (this.#_modules !== undefined) return;

const environment = isDevelopment()
? Environment.DEV
: Environment.PRODUCTION;

this.#modules = [
new EVMModule(),
new BitcoinModule(),
new BitcoinModule({
environment,
}),
new AVMModule(),
new CoreEthModule(),
new PVMModule(),
Expand Down
39 changes: 0 additions & 39 deletions src/background/vmModules/mocks/bitcoin.manifest.json

This file was deleted.

Loading

0 comments on commit 243e747

Please sign in to comment.