Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: get bitcoin history from vm module #7

Merged
merged 6 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
41 changes: 18 additions & 23 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/glacier-sdk": "2.8.0-alpha.188",
"@avalabs/hw-app-avalanche": "0.14.1",
"@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-k2-components": "4.18.0-alpha.44",
"@avalabs/types": "2.8.0-alpha.188",
"@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": "3.0.1-alpha.1",
"@avalabs/hw-app-avalanche": "0.14.1",
"@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 @@ -213,15 +214,7 @@
},
"lavamoat": {
"allowScripts": {
"@avalabs/bridge-sdk>@avalabs/wallets-sdk>@ledgerhq/hw-app-btc>bitcoinjs-lib>bip32>tiny-secp256k1": false,
"@ethereumjs/common>ethereumjs-util>ethereum-cryptography>keccak": false,
"@avalabs/bridge-sdk>@avalabs/wallets-sdk>web3": false,
"@avalabs/bridge-sdk>@avalabs/wallets-sdk>web3>web3-bzz": false,
"@avalabs/bridge-sdk>@avalabs/wallets-sdk>web3>web3-core>web3-core-requestmanager>web3-providers-ws>websocket>bufferutil": false,
"@avalabs/bridge-sdk>@avalabs/wallets-sdk>web3>web3-core>web3-core-requestmanager>web3-providers-ws>websocket>es5-ext": false,
"@avalabs/bridge-sdk>@avalabs/wallets-sdk>web3>web3-core>web3-core-requestmanager>web3-providers-ws>websocket>utf-8-validate": false,
"@avalabs/bridge-sdk>@avalabs/wallets-sdk>web3>web3-shh": false,
"@avalabs/bridge-sdk>@avalabs/wallets-sdk>hdkey>secp256k1": false,
"@lavamoat/preinstall-always-fail": false,
"@types/web3>web3": false,
"@types/web3>web3>web3-bzz": false,
Expand All @@ -240,14 +233,16 @@
"web3>web3-bzz": false,
"web3>web3-shh": false,
"yarn": false,
"@avalabs/bridge-sdk>@avalabs/wallets-sdk>@avalabs/hw-app-avalanche>@ledgerhq/hw-app-eth>@ledgerhq/domain-service>eip55>keccak": false,
"@avalabs/vm-module-types": false,
"@avalabs/vm-module-types>@avalabs/wallets-sdk>@avalabs/hw-app-avalanche>@ledgerhq/hw-app-eth>@ledgerhq/domain-service>eip55>keccak": false,
"@avalabs/vm-module-types>@avalabs/wallets-sdk>@ledgerhq/hw-app-btc>bitcoinjs-lib>bip32>tiny-secp256k1": false,
"@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
Loading
Loading