@avalabs/evm-module 0.0.1 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/.turbo/turbo-lint.log +1 -1
  3. package/.turbo/turbo-test.log +12 -3
  4. package/CHANGELOG.md +29 -0
  5. package/dist/index.cjs +13 -3
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.cts +13 -3
  8. package/dist/index.d.ts +13 -3
  9. package/dist/index.js +9 -3
  10. package/dist/index.js.map +1 -1
  11. package/package.json +15 -4
  12. package/src/handlers/get-network-fee.test.ts +38 -0
  13. package/src/handlers/get-network-fee.ts +41 -0
  14. package/src/handlers/get-transaction-history/converters/etherscan-transaction-converter/convert-transaction-erc20.ts +51 -0
  15. package/src/handlers/get-transaction-history/converters/etherscan-transaction-converter/convert-transaction-normal.ts +58 -0
  16. package/src/handlers/get-transaction-history/converters/etherscan-transaction-converter/get-transaction-from-etherscan.test.ts +116 -0
  17. package/src/handlers/get-transaction-history/converters/etherscan-transaction-converter/get-transaction-from-etherscan.ts +65 -0
  18. package/src/handlers/get-transaction-history/converters/evm-transaction-converter/convert-transaction.ts +47 -0
  19. package/src/handlers/get-transaction-history/converters/evm-transaction-converter/get-nft-metadata.ts +35 -0
  20. package/src/handlers/get-transaction-history/converters/evm-transaction-converter/get-sender-info.ts +38 -0
  21. package/src/handlers/get-transaction-history/converters/evm-transaction-converter/get-tokens.ts +107 -0
  22. package/src/handlers/get-transaction-history/converters/evm-transaction-converter/get-transaction-from-glacier.test.ts +213 -0
  23. package/src/handlers/get-transaction-history/converters/evm-transaction-converter/get-transactions-from-glacier.ts +60 -0
  24. package/src/handlers/get-transaction-history/converters/evm-transaction-converter/get-tx-type.ts +48 -0
  25. package/src/handlers/get-transaction-history/index.test.ts +50 -0
  26. package/src/handlers/get-transaction-history/index.ts +37 -0
  27. package/src/handlers/get-transaction-history/utils/get-explorer-address-by-network.ts +7 -0
  28. package/src/handlers/get-transaction-history/utils/get-small-image-for-nft.ts +16 -0
  29. package/src/handlers/get-transaction-history/utils/ipfs-resolver-with-fallback.ts +18 -0
  30. package/src/handlers/get-transaction-history/utils/is-ethereum-chain-id.ts +15 -0
  31. package/src/handlers/get-transaction-history/utils/resolve.ts +7 -0
  32. package/src/index.ts +32 -20
  33. package/src/manifest.json +1 -1
  34. package/src/types.ts +3 -0
  35. package/tsconfig.json +5 -1
@@ -0,0 +1,60 @@
1
+ import type { GetTransactionHistory, TransactionHistoryResponse } from '@avalabs/vm-module-types';
2
+ import { Glacier } from '@avalabs/glacier-sdk';
3
+ import { convertTransaction } from './convert-transaction';
4
+
5
+ export const getTransactionsFromGlacier = async ({
6
+ chainId,
7
+ explorerUrl,
8
+ networkToken,
9
+ address,
10
+ nextPageToken,
11
+ offset,
12
+ glacierApiUrl,
13
+ }: GetTransactionHistory): Promise<TransactionHistoryResponse> => {
14
+ if (!glacierApiUrl) {
15
+ throw new Error('Glacier API URL is required');
16
+ }
17
+
18
+ const glacierSdk = new Glacier({ BASE: glacierApiUrl });
19
+
20
+ try {
21
+ const response = await glacierSdk.evmTransactions.listTransactions({
22
+ chainId: chainId.toString(),
23
+ address,
24
+ pageToken: nextPageToken,
25
+ pageSize: offset,
26
+ });
27
+
28
+ const convertedTxs = await Promise.all(
29
+ response.transactions
30
+ .filter(
31
+ // Currently not showing failed tx
32
+ (tranasaction) => tranasaction.nativeTransaction.txStatus === '1',
33
+ )
34
+ .map((transactions) =>
35
+ convertTransaction({
36
+ transactions,
37
+ explorerUrl,
38
+ networkToken,
39
+ chainId,
40
+ address,
41
+ }).then((tx) => tx),
42
+ ),
43
+ );
44
+
45
+ const transactions = convertedTxs.filter(
46
+ // Filtering txs with 0 value since there is no change in balance
47
+ (transaction) => transaction.tokens.find((token) => Number(token.amount) !== 0),
48
+ );
49
+
50
+ return {
51
+ transactions,
52
+ nextPageToken: response.nextPageToken,
53
+ };
54
+ } catch {
55
+ return {
56
+ transactions: [],
57
+ nextPageToken: '',
58
+ };
59
+ }
60
+ };
@@ -0,0 +1,48 @@
1
+ import type { TransactionDetails } from '@avalabs/glacier-sdk';
2
+ import { TokenType, TransactionType, type TxToken } from '@avalabs/vm-module-types';
3
+ import startCase from 'lodash.startcase';
4
+
5
+ export const getTxType = (
6
+ { nativeTransaction, erc20Transfers, erc721Transfers }: TransactionDetails,
7
+ address: string,
8
+ tokens: TxToken[],
9
+ ): TransactionType => {
10
+ const nativeOnly = !erc20Transfers && !erc721Transfers;
11
+ const method = parseRawMethod(nativeTransaction.method?.methodName);
12
+
13
+ const isSwap = method.toLowerCase().includes('swap');
14
+ const isNativeSend = nativeOnly && nativeTransaction.from.address === address;
15
+ const isNativeReceive = nativeOnly && nativeTransaction.to.address === address;
16
+ const isNFTPurchase = method === 'Market Buy Orders With Eth' || method === 'Buy NFT';
17
+ const isApprove = method === 'Approve';
18
+ const isTransfer = method.toLowerCase().includes('transfer');
19
+ const isAirdrop = method.toLowerCase().includes('airdrop');
20
+ const isUnwrap = method.toLowerCase().includes('unwrap');
21
+ const isFillOrder = method === 'Fill Order';
22
+ const isNFTSend = isTransfer && !!tokens[0] && isNFT(tokens[0].type) && tokens[0].from?.address === address;
23
+ const isNFTReceive = isTransfer && !!tokens[0] && isNFT(tokens[0].type) && tokens[0].to?.address === address;
24
+
25
+ if (isSwap) return TransactionType.SWAP;
26
+ if (isNativeSend) return TransactionType.SEND;
27
+ if (isNativeReceive) return TransactionType.RECEIVE;
28
+ if (isNFTPurchase) return TransactionType.NFT_BUY;
29
+ if (isApprove) return TransactionType.APPROVE;
30
+ if (isNFTSend) return TransactionType.NFT_SEND;
31
+ if (isNFTReceive) return TransactionType.NFT_RECEIVE;
32
+ if (isTransfer) return TransactionType.TRANSFER;
33
+ if (isAirdrop) return TransactionType.AIRDROP;
34
+ if (isUnwrap) return TransactionType.UNWRAP;
35
+ if (isFillOrder) return TransactionType.FILL_ORDER;
36
+ return TransactionType.UNKNOWN;
37
+ };
38
+
39
+ function isNFT(tokenType: TokenType) {
40
+ return tokenType === TokenType.ERC721 || tokenType === TokenType.ERC1155;
41
+ }
42
+
43
+ const parseRawMethod = (method = ''): string => {
44
+ if (method.includes('(')) {
45
+ return startCase(method.split('(', 1)[0]);
46
+ }
47
+ return method;
48
+ };
@@ -0,0 +1,50 @@
1
+ import { getTransactionHistory } from './index';
2
+ import { getTransactionFromEtherscan } from './converters/etherscan-transaction-converter/get-transaction-from-etherscan';
3
+ import { getTransactionsFromGlacier } from './converters/evm-transaction-converter/get-transactions-from-glacier';
4
+
5
+ jest.mock('./converters/evm-transaction-converter/get-transactions-from-glacier', () => ({
6
+ getTransactionsFromGlacier: jest.fn(),
7
+ }));
8
+
9
+ jest.mock('./converters/etherscan-transaction-converter/get-transaction-from-etherscan', () => ({
10
+ getTransactionFromEtherscan: jest.fn(),
11
+ }));
12
+
13
+ describe('get-transaction-history', () => {
14
+ it('should have called getTransactionFromEtherscan', async () => {
15
+ await getTransactionHistory({
16
+ chainId: 1,
17
+ isTestnet: false,
18
+ networkToken: {
19
+ name: 'networkToken',
20
+ symbol: 'networkToken',
21
+ decimals: 1,
22
+ description: 'description',
23
+ logoUri: 'logoUri',
24
+ },
25
+ explorerUrl: 'explorerUrl',
26
+ address: 'address',
27
+ nextPageToken: 'nextPageToken',
28
+ offset: 1,
29
+ });
30
+ expect(getTransactionFromEtherscan).toHaveBeenCalled();
31
+ });
32
+ it('should have called getTransactionsFromGlacier', async () => {
33
+ await getTransactionHistory({
34
+ chainId: 41334,
35
+ isTestnet: false,
36
+ networkToken: {
37
+ name: 'networkToken',
38
+ symbol: 'networkToken',
39
+ decimals: 1,
40
+ description: 'description',
41
+ logoUri: 'logoUri',
42
+ },
43
+ explorerUrl: 'explorerUrl',
44
+ address: 'address',
45
+ nextPageToken: 'nextPageToken',
46
+ offset: 1,
47
+ });
48
+ expect(getTransactionsFromGlacier).toHaveBeenCalled();
49
+ });
50
+ });
@@ -0,0 +1,37 @@
1
+ import { getTransactionFromEtherscan } from './converters/etherscan-transaction-converter/get-transaction-from-etherscan';
2
+ import { isEthereumChainId } from './utils/is-ethereum-chain-id';
3
+ import type { GetTransactionHistory, TransactionHistoryResponse } from '@avalabs/vm-module-types';
4
+ import { getTransactionsFromGlacier } from './converters/evm-transaction-converter/get-transactions-from-glacier';
5
+
6
+ export const getTransactionHistory = async ({
7
+ chainId,
8
+ isTestnet,
9
+ networkToken,
10
+ explorerUrl,
11
+ address,
12
+ nextPageToken,
13
+ offset,
14
+ glacierApiUrl,
15
+ }: GetTransactionHistory): Promise<TransactionHistoryResponse> => {
16
+ if (isEthereumChainId(chainId)) {
17
+ return getTransactionFromEtherscan({
18
+ isTestnet,
19
+ networkToken,
20
+ explorerUrl,
21
+ chainId,
22
+ address,
23
+ nextPageToken,
24
+ offset,
25
+ });
26
+ }
27
+ return getTransactionsFromGlacier({
28
+ isTestnet,
29
+ networkToken,
30
+ explorerUrl,
31
+ chainId,
32
+ address,
33
+ nextPageToken,
34
+ offset,
35
+ glacierApiUrl,
36
+ });
37
+ };
@@ -0,0 +1,7 @@
1
+ export function getExplorerAddressByNetwork(
2
+ explorerUrl: string,
3
+ hash: string,
4
+ hashType: 'address' | 'tx' = 'tx',
5
+ ): string {
6
+ return `${explorerUrl}/${hashType}/${hash}`;
7
+ }
@@ -0,0 +1,16 @@
1
+ import { ipfsResolverWithFallback } from './ipfs-resolver-with-fallback';
2
+
3
+ const COVALENT_IMG_SIZER = 'https://image-proxy.svc.prod.covalenthq.com/cdn-cgi/image';
4
+
5
+ /**
6
+ * Covalent has an on the fly image resizer, it resolves image urls then resizes the image.
7
+ *
8
+ * This allows us to request smaller images depending on the UI needs
9
+ *
10
+ * @param imgUrl the url of the image to convert to size
11
+ * @returns The url to the image which is sized at the time of request
12
+ */
13
+ export function getSmallImageForNFT(imgUrl: string, imageSize: '256' | '512' | '1024' = '256') {
14
+ const url = ipfsResolverWithFallback(imgUrl);
15
+ return `${COVALENT_IMG_SIZER}/width=${imageSize},fit/${url}`;
16
+ }
@@ -0,0 +1,18 @@
1
+ import { ipfsResolver } from '@avalabs/utils-sdk';
2
+
3
+ export const CLOUDFLARE_IPFS_URL = 'https://cloudflare-ipfs.com';
4
+
5
+ export function ipfsResolverWithFallback(
6
+ sourceUrl: string | undefined,
7
+ desiredGatewayPrefix: string = CLOUDFLARE_IPFS_URL,
8
+ ) {
9
+ if (!sourceUrl) {
10
+ return '';
11
+ }
12
+
13
+ try {
14
+ return ipfsResolver(sourceUrl, desiredGatewayPrefix);
15
+ } catch {
16
+ return sourceUrl;
17
+ }
18
+ }
@@ -0,0 +1,15 @@
1
+ enum ChainId {
2
+ ETHEREUM_HOMESTEAD = 1,
3
+ ETHEREUM_TEST_RINKEBY = 4,
4
+ ETHEREUM_TEST_GOERLY = 5,
5
+ ETHEREUM_TEST_SEPOLIA = 11155111,
6
+ }
7
+
8
+ export const isEthereumChainId = (chainId: number): boolean => {
9
+ return (
10
+ ChainId.ETHEREUM_HOMESTEAD === chainId ||
11
+ ChainId.ETHEREUM_TEST_GOERLY === chainId ||
12
+ ChainId.ETHEREUM_TEST_RINKEBY === chainId ||
13
+ ChainId.ETHEREUM_TEST_SEPOLIA === chainId
14
+ );
15
+ };
@@ -0,0 +1,7 @@
1
+ export async function resolve<T = unknown>(promise: Promise<T>) {
2
+ try {
3
+ return promise.then((res) => [res, null]).catch((err) => [null, err]);
4
+ } catch (err) {
5
+ return Promise.resolve([null, err]);
6
+ }
7
+ }
package/src/index.ts CHANGED
@@ -1,22 +1,34 @@
1
- import { parseManifest } from '@internal/types';
2
- import type { Module } from '@internal/types';
1
+ import type { Module, Manifest, NetworkFees, GetTransactionHistory } from '@avalabs/vm-module-types';
2
+ import { parseManifest } from '@avalabs/vm-module-types';
3
+ import type { JsonRpcProvider } from 'ethers';
4
+ import { getNetworkFee } from './handlers/get-network-fee';
5
+ import { getTransactionHistory } from './handlers/get-transaction-history';
6
+ import ManifestJson from './manifest.json';
3
7
 
4
- export const evm: Module = {
5
- getManifest: () => {
6
- const manifest = require('./manifest.json');
7
- const result = parseManifest(manifest);
8
- return result.success ? result.data : undefined;
9
- },
10
- getBalances: () => {
11
- return Promise.resolve('EVM balances');
12
- },
13
- getTransactionHistory: () => {
14
- return Promise.resolve('EVM transaction history');
15
- },
16
- getNetworkFee: () => {
17
- return Promise.resolve('EVM network fee');
18
- },
19
- getAddress: () => {
8
+ export class EvmModule implements Module {
9
+ #provider: JsonRpcProvider;
10
+
11
+ constructor(provider: JsonRpcProvider) {
12
+ this.#provider = provider;
13
+ }
14
+ getAddress(): Promise<string> {
20
15
  return Promise.resolve('EVM address');
21
- },
22
- };
16
+ }
17
+
18
+ getBalances(): Promise<string> {
19
+ return Promise.resolve('EVM balances');
20
+ }
21
+
22
+ getManifest(): Manifest | undefined {
23
+ const result = parseManifest(ManifestJson);
24
+ return result.success ? result.data : undefined;
25
+ }
26
+
27
+ getNetworkFee(): Promise<NetworkFees> {
28
+ return getNetworkFee(this.#provider);
29
+ }
30
+
31
+ getTransactionHistory(params: GetTransactionHistory) {
32
+ return getTransactionHistory(params);
33
+ }
34
+ }
package/src/manifest.json CHANGED
@@ -32,7 +32,7 @@
32
32
  "permissions": {
33
33
  "rpc": {
34
34
  "dapps": true,
35
- "methods": ["eth_sendTransaction", "eth_*"]
35
+ "methods": ["eth_sendTransaction", "eth_*", "getTransactionHistory"]
36
36
  }
37
37
  },
38
38
  "manifestVersion": "0.0"
package/src/types.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { TransactionType } from '@avalabs/vm-module-types';
2
+
3
+ export const NonContractCallTypes = [TransactionType.SEND, TransactionType.RECEIVE, TransactionType.TRANSFER];
package/tsconfig.json CHANGED
@@ -4,5 +4,9 @@
4
4
  "outDir": "./dist",
5
5
  "incremental": false // Need to turn off because of tsup dts
6
6
  },
7
- "include": ["src", "src/types"]
7
+ "include": ["src"],
8
+ "references": [
9
+ { "path": "../../packages/vm-module-types/tsconfig.json" },
10
+ { "path": "../../packages-internal/types/tsconfig.json" }
11
+ ]
8
12
  }