-
-
Notifications
You must be signed in to change notification settings - Fork 200
/
ERC20Standard.ts
140 lines (130 loc) · 4.12 KB
/
ERC20Standard.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import { toUtf8 } from '@ethereumjs/util';
import { Contract } from '@ethersproject/contracts';
import type { Web3Provider } from '@ethersproject/providers';
import { decodeSingle } from '@metamask/abi-utils';
import { ERC20 } from '@metamask/controller-utils';
import { abiERC20 } from '@metamask/metamask-eth-abis';
import { assertIsStrictHexString } from '@metamask/utils';
import type BN from 'bn.js';
import { ethersBigNumberToBN } from '../assetsUtil';
export class ERC20Standard {
private readonly provider: Web3Provider;
constructor(provider: Web3Provider) {
this.provider = provider;
}
/**
* Get balance or count for current account on specific asset contract.
*
* @param address - Asset ERC20 contract address.
* @param selectedAddress - Current account public address.
* @returns Promise resolving to BN object containing balance for current account on specific asset contract.
*/
async getBalanceOf(address: string, selectedAddress: string): Promise<BN> {
const contract = new Contract(address, abiERC20, this.provider);
const balance = await contract.balanceOf(selectedAddress);
return ethersBigNumberToBN(balance);
}
/**
* Query for the decimals for a given ERC20 asset.
*
* @param address - ERC20 asset contract string.
* @returns Promise resolving to the 'decimals'.
*/
async getTokenDecimals(address: string): Promise<string> {
const contract = new Contract(address, abiERC20, this.provider);
try {
const decimals = await contract.decimals();
return decimals.toString();
} catch (err) {
// Mirror previous implementation
if (
err instanceof Error &&
err.message.includes('call revert exception')
) {
throw new Error('Failed to parse token decimals');
}
throw err;
}
}
/**
* Query for the name for a given ERC20 asset.
*
* @param address - ERC20 asset contract string.
* @returns Promise resolving to the 'name'.
*/
async getTokenName(address: string): Promise<string> {
const contract = new Contract(address, abiERC20, this.provider);
try {
const name = await contract.name();
return name.toString();
} catch (err) {
// Mirror previous implementation
if (
err instanceof Error &&
err.message.includes('call revert exception')
) {
throw new Error('Failed to parse token name');
}
throw err;
}
}
/**
* Query for symbol for a given ERC20 asset.
*
* @param address - ERC20 asset contract address.
* @returns Promise resolving to the 'symbol'.
*/
async getTokenSymbol(address: string): Promise<string> {
// Signature for calling `symbol()`
const payload = { to: address, data: '0x95d89b41' };
const result = await this.provider.call(payload);
assertIsStrictHexString(result);
// Parse as string - treat empty string as failure
try {
const decoded = decodeSingle('string', result);
if (decoded?.length > 0) {
return decoded;
}
} catch {
// Ignore error
}
// Parse as bytes - treat empty string as failure
try {
const utf8 = toUtf8(result);
if (utf8.length > 0) {
return utf8;
}
} catch {
// Ignore error
}
throw new Error('Failed to parse token symbol');
}
/**
* Query if a contract implements an interface.
*
* @param address - Asset contract address.
* @param userAddress - The public address for the currently active user's account.
* @returns Promise resolving an object containing the standard, decimals, symbol and balance of the given contract/userAddress pair.
*/
async getDetails(
address: string,
userAddress?: string,
): Promise<{
standard: string;
symbol: string | undefined;
decimals: string | undefined;
balance: BN | undefined;
}> {
const [decimals, symbol, balance] = await Promise.all([
this.getTokenDecimals(address),
this.getTokenSymbol(address),
userAddress ? this.getBalanceOf(address, userAddress) : undefined,
]);
return {
decimals,
symbol,
balance,
standard: ERC20,
};
}
}