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

feat: cp-8368 historyservice #42

Merged
merged 18 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from 11 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
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
},
"dependencies": {
"@avalabs/avalanchejs": "4.0.5",
"@avalabs/bitcoin-module": "0.5.0",
"@avalabs/bridge-unified": "2.1.0",
"@avalabs/core-bridge-sdk": "3.1.0-alpha.4",
"@avalabs/core-chains-sdk": "3.1.0-alpha.4",
Expand All @@ -36,10 +35,12 @@
"@avalabs/core-token-prices-sdk": "3.1.0-alpha.4",
"@avalabs/core-utils-sdk": "3.1.0-alpha.4",
"@avalabs/core-wallets-sdk": "3.1.0-alpha.4",
"@avalabs/evm-module": "0.5.0",
"@avalabs/glacier-sdk": "3.1.0-alpha.4",
"@avalabs/hw-app-avalanche": "0.14.1",
"@avalabs/types": "3.1.0-alpha.3",
"@avalabs/avalanche-module": "0.5.0",
"@avalabs/bitcoin-module": "0.5.0",
"@avalabs/evm-module": "0.5.0",
vvava marked this conversation as resolved.
Show resolved Hide resolved
"@avalabs/vm-module-types": "0.5.0",
"@blockaid/client": "0.10.0",
"@coinbase/cbpay-js": "1.6.0",
Expand Down
44 changes: 23 additions & 21 deletions src/background/services/history/HistoryService.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {

Check failure on line 1 in src/background/services/history/HistoryService.test.ts

View workflow job for this annotation

GitHub Actions / Lint and build

Type '{ isContractCall: true; isIncoming: false; isOutgoing: true; isSender: true; timestamp: number; from: string; to: string; tokens: { name: string; symbol: string; amount: string; type: TokenType.NATIVE; }[]; ... 5 more ...; hash: string; }' is not assignable to type 'Transaction'.

Check failure on line 1 in src/background/services/history/HistoryService.test.ts

View workflow job for this annotation

GitHub Actions / Lint and build

Type '{ txType: XChainTransactionType.BASE_TX; vmType: string; isContractCall: boolean; isIncoming: boolean; isOutgoing: boolean; isSender: boolean; timestamp: number; hash: string; ... 7 more ...; explorerLink: string; }' is not assignable to type 'Transaction'.
PChainTransactionType,
XChainTransactionType,
} from '@avalabs/glacier-sdk';
Expand All @@ -9,13 +9,9 @@
NetworkVMType,
} from '@avalabs/core-chains-sdk';
import { HistoryService } from './HistoryService';
import {
PchainTxHistoryItem,
TransactionType,
TxHistoryItem,
XchainTxHistoryItem,
} from './models';
import { TxHistoryItem } from './models';
import { TokenType } from '../balances/models';
import { Transaction, TransactionType } from '@avalabs/vm-module-types';

describe('src/background/services/history/HistoryService.ts', () => {
let service: HistoryService;
Expand Down Expand Up @@ -80,7 +76,7 @@
gasUsed: 'gasUsed',
explorerLink: 'explorerLink',
chainId: 'chainId',
type: TransactionType.SEND,
txType: TransactionType.SEND,
};

const btcTxHistoryItem: TxHistoryItem = {
Expand All @@ -104,29 +100,35 @@
gasUsed: 'gasUsed',
explorerLink: 'explorerLink',
chainId: 'chainId',
type: TransactionType.SEND,
txType: TransactionType.SEND,
};
const pchainTxHistoryItem: PchainTxHistoryItem = {
const pchainTxHistoryItem: Transaction = {
isContractCall: true,
isIncoming: false,
isOutgoing: true,
isSender: true,
timestamp: 'timestamp',
from: ['from'],
to: ['to'],
token: {
name: 'tokenName',
symbol: 'tokenSymbol',
amount: 'amount',
type: TokenType.NATIVE,
},
timestamp: 1111,
from: 'from',
to: 'to',
tokens: [
{
name: 'tokenName',
symbol: 'tokenSymbol',
amount: 'amount',
type: TokenType.NATIVE,
},
],
gasUsed: 'gasUsed',
explorerLink: 'explorerLink',
chainId: 'chainId',
type: PChainTransactionType.BASE_TX,
txType: PChainTransactionType.BASE_TX,
vmType: 'PVM',
hash: 'hash',
};

const xchainTxHistoryItem: XchainTxHistoryItem = {
const xchainTxHistoryItem: Transaction = {
...pchainTxHistoryItem,
type: XChainTransactionType.BASE_TX,
txType: XChainTransactionType.BASE_TX,
vmType: 'AVM',
};

Expand Down
164 changes: 14 additions & 150 deletions src/background/services/history/HistoryServiceAVM.ts
Original file line number Diff line number Diff line change
@@ -1,166 +1,30 @@
import { singleton } from 'tsyringe';
import { Network } from '@avalabs/core-chains-sdk';
import { Avalanche } from '@avalabs/core-wallets-sdk';
import { AccountsService } from '../accounts/AccountsService';
import {
BlockchainId,
ListXChainTransactionsResponse,
Network as NetworkType,
PChainTransaction,
PrimaryNetworkChainName,
SortOrder,
XChainLinearTransaction,
XChainNonLinearTransaction,
} from '@avalabs/glacier-sdk';
import { GlacierService } from '../glacier/GlacierService';
import { TxHistoryItemToken, XchainTxHistoryItem } from './models';
import Big from 'big.js';
import { getAvaxAssetId } from './utils/getAvaxAssetId';
import { TokenType } from '../balances/models';
import { stripAddressPrefix } from '@src/utils/stripAddressPrefix';
import { getExplorerAddressByNetwork } from '@src/utils/getExplorerAddress';
import { isXchainNetwork } from '../network/utils/isAvalancheXchainNetwork';
import { getTokenValue } from '../balances/utils/getTokenValue';
import { ModuleManager } from '@src/background/vmModules/ModuleManager';
import { NetworkWithCaipId } from '../network/models';
import { Transaction } from '@avalabs/vm-module-types';

@singleton()
export class HistoryServiceAVM {
constructor(
private accountsService: AccountsService,
private glacierService: GlacierService
private moduleManager: ModuleManager
) {}

isXchainTransactions(value): value is ListXChainTransactionsResponse {
return value.chainInfo.chainName === PrimaryNetworkChainName.X_CHAIN;
}

aggregateValue(
value: PChainTransaction['value'],
isTestnet: boolean
): Big | undefined {
return value
.filter((value_) => value_.assetId === getAvaxAssetId(isTestnet))
.reduce(
(accumulator, value_) => accumulator.add(value_.amount),
new Big(0)
);
}

convertToTxHistoryItem(
tx: XChainNonLinearTransaction | XChainLinearTransaction,
network: Network,
address: string
): XchainTxHistoryItem {
const avax = network.networkToken;

const isImportExport = ['ImportTx', 'ExportTx'].includes(tx.txType);
const xBlockchainId = network.isTestnet
? Avalanche.FujiContext.xBlockchainID
: Avalanche.MainnetContext.xBlockchainID;

const importExportAmount = tx.emittedUtxos
.filter(
(utxo) =>
utxo.asset.assetId === getAvaxAssetId(!!network.isTestnet) &&
((tx.txType === 'ImportTx' &&
utxo.consumedOnChainId === xBlockchainId) ||
(tx.txType === 'ExportTx' &&
utxo.consumedOnChainId !== xBlockchainId))
)
.reduce((agg, utxo) => agg.add(utxo.asset.amount), new Big(0));

const totalAmountCreated = tx.amountCreated
.filter((asset) => asset.assetId === getAvaxAssetId(!!network.isTestnet))
.reduce(
(accumulator, asset) => accumulator.add(asset.amount),
new Big(0)
);
const totalAmountUnlocked = tx.amountUnlocked
.filter((asset) => asset.assetId === getAvaxAssetId(!!network.isTestnet))
.reduce(
(accumulator, asset) => accumulator.add(asset.amount),
new Big(0)
);

const nAvaxAmt = isImportExport ? importExportAmount : totalAmountCreated;
const avaxAmt = nAvaxAmt
? new Big(getTokenValue(avax.decimals, nAvaxAmt.toNumber()))
: new Big(0);

const froms = new Set(
tx.consumedUtxos?.flatMap((utxo) => utxo.addresses) || []
);
const tos = new Set(
tx.emittedUtxos?.flatMap((utxo) => utxo.addresses) || []
);
const nAvaxFee = totalAmountUnlocked.minus(totalAmountCreated);
const avaxBurnedAmount = nAvaxFee
? new Big(getTokenValue(avax.decimals, nAvaxFee.toNumber()))
: new Big(0);

const isSender = froms.has(stripAddressPrefix(address));
const timestamp = new Date(tx.timestamp * 1000);

const token: TxHistoryItemToken = {
decimal: avax.decimals.toString(),
name: avax.name,
symbol: avax.symbol,
amount: avaxAmt?.toString() ?? '0',
type: TokenType.NATIVE,
};

const result: XchainTxHistoryItem = {
from: Array.from(froms),
to: Array.from(tos),
isSender,
timestamp: timestamp.toISOString(),
token,
gasUsed: avaxBurnedAmount.toString(),
explorerLink: getExplorerAddressByNetwork(network, tx.txHash, 'tx'),
chainId: network.chainId.toString(),
type: tx.txType,
vmType: 'AVM',
};

return result;
}

async getHistory(network: Network, otherAddress?: string) {
if (!network?.chainId || !isXchainNetwork(network)) {
return [];
}

async getHistory(network: NetworkWithCaipId, otherAddress?: string) {
const address =
otherAddress ?? this.accountsService.activeAccount?.addressAVM;

if (!address) {
return [];
}

const param = {
blockchainId: (network.isTestnet
? Avalanche.FujiContext.xBlockchainID
: Avalanche.MainnetContext.xBlockchainID) as BlockchainId,
network: network.isTestnet ? NetworkType.FUJI : NetworkType.MAINNET,
addresses: address,
pageSize: 25,
sortOrder: SortOrder.DESC,
};

try {
const rawResult =
await this.glacierService.listLatestPrimaryNetworkTransactions(param);

if (!this.isXchainTransactions(rawResult)) {
return [];
}

const formattedResult = rawResult.transactions.map((tx) =>
this.convertToTxHistoryItem(tx, network, address)
);

return formattedResult;
} catch {
if (!network?.chainId || !isXchainNetwork(network) || !address) {
return [];
}
const module = await this.moduleManager.loadModuleByNetwork(network);
const { transactions } = await module.getTransactionHistory({
address,
network,
});
return transactions.map((transaction) => {
return { ...transaction, vmType: 'AVM' } as Transaction;
});
}
}
5 changes: 3 additions & 2 deletions src/background/services/history/HistoryServiceBTC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { NetworkVMType } from '@avalabs/core-chains-sdk';
import { singleton } from 'tsyringe';
import { AccountsService } from '../accounts/AccountsService';
import { HistoryServiceBridgeHelper } from './HistoryServiceBridgeHelper';
import { TransactionType, TxHistoryItem } from './models';
import { TxHistoryItem } from './models';
import { ModuleManager } from '@src/background/vmModules/ModuleManager';
import { NetworkWithCaipId } from '../network/models';
import sentryCaptureException, {
SentryExceptionTypes,
} from '@src/monitoring/sentryCaptureException';
import { TransactionType } from '@avalabs/vm-module-types';

@singleton()
export class HistoryServiceBTC {
Expand Down Expand Up @@ -43,7 +44,7 @@ export class HistoryServiceBTC {
...tx,
// BitcoinModule is not able to recognize bridge txs at the moment, so we need to do it here.
isBridge,
type: isBridge
txType: isBridge
? TransactionType.BRIDGE
: tx.isSender
? TransactionType.SEND
Expand Down
Loading
Loading