-
-
Notifications
You must be signed in to change notification settings - Fork 248
/
Copy pathutil.ts
62 lines (49 loc) · 2.7 KB
/
util.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
import { BigNumber, Contract, providers } from 'ethers'
import { getAddress, hexDataSlice } from 'ethers/lib/utils'
import { TokenMapping } from '../common/interfaces'
import { Allowance } from './interfaces'
import { convertString, toFloat, unpackResult } from '../common/util'
import { DUMMY_ADDRESS, DUMMY_ADDRESS_2 } from '../common/constants';
export async function getAllowancesFromApprovals(contract: Contract, ownerAddress: string, approvals: providers.Log[]) {
const deduplicatedApprovals = approvals
.filter((approval, i) => i === approvals.findIndex(other => approval.topics[2] === other.topics[2]))
let allowances: Allowance[] = await Promise.all(
deduplicatedApprovals.map((approval) => getAllowanceFromApproval(contract, ownerAddress, approval))
)
return allowances
}
async function getAllowanceFromApproval(multicallContract: Contract, ownerAddress: string, approval: providers.Log) {
const spender = getAddress(hexDataSlice(approval.topics[2], 12))
const allowance = (await unpackResult(multicallContract.functions.allowance(ownerAddress, spender))).toString()
return { spender, allowance }
}
export async function getTokenData(contract: Contract, ownerAddress: string, tokenMapping: TokenMapping = {}) {
const tokenData = tokenMapping[getAddress(contract.address)]
const [totalSupplyBN, balance, symbol, decimals] = await Promise.all([
unpackResult(contract.functions.totalSupply()),
convertString(unpackResult(contract.functions.balanceOf(ownerAddress))),
// Use the tokenlist symbol + decimals if present (simplifies handing MKR et al)
tokenData?.symbol ?? unpackResult(contract.functions.symbol()),
tokenData?.decimals ?? unpackResult(contract.functions.decimals()),
throwIfNotErc20(contract),
])
const totalSupply = totalSupplyBN.toString()
return { symbol, decimals, totalSupply, balance }
}
export function formatAllowance(allowance: string, decimals: number, totalSupply: string): string {
const allowanceBN = BigNumber.from(allowance)
const totalSupplyBN = BigNumber.from(totalSupply)
if (allowanceBN.gt(totalSupplyBN)) {
return 'Unlimited'
}
return toFloat(Number(allowanceBN), decimals)
}
async function throwIfNotErc20(contract: Contract) {
// If the function isApprovedForAll does not exist it will throw (and is not ERC721)
const [allowance] = await contract.functions.allowance(DUMMY_ADDRESS, DUMMY_ADDRESS_2)
// The only acceptable value for checking the allowance from 0x00...01 to 0x00...02 is 0
// This could happen when the contract is not ERC20 but does have a fallback function
if (allowance.toString() !== '0') {
throw new Error('Response to allowance was not 0, indicating that this is not an ERC20 contract')
}
}