-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathtokens.ts
More file actions
163 lines (144 loc) · 5.02 KB
/
tokens.ts
File metadata and controls
163 lines (144 loc) · 5.02 KB
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import { formatEther, formatUnits } from 'viem'
import { Asset, AssetType, EventParams, Interaction } from '../types.js'
import { sameAddress } from '../helpers/address.js'
const toKeys = ['to', '_to', 'dst']
const fromKeys = ['from', '_from', 'src']
const valueKeys = ['value', 'amount', 'wad', '_amount']
export const ethAddress = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'
const transferEvents = ['Transfer', 'TransferBatch', 'TransferSingle']
function findValue(params: EventParams): EventParams[keyof EventParams] {
// Find first value that is not null
const match = valueKeys.find((key) => params[key] != null)
if (match != null) {
return params[match]
} else {
return ''
}
}
function getTokenType(interaction: Interaction): AssetType {
let tokenType = AssetType.DEFAULT
if (interaction.contractType === 'ERC1155') {
tokenType = AssetType.ERC1155
// ERC-721
} else if (interaction.contractType === 'ERC721') {
tokenType = AssetType.ERC721
// ERC-20
} else if (interaction.contractType === 'ERC20' || interaction.contractType === 'WETH') {
tokenType = AssetType.ERC20
}
return tokenType
}
function getTokens(interactions: Interaction[]): Asset[] {
const filteredInteractions = interactions.filter((d) => transferEvents.includes(d.event.eventName || ''))
return filteredInteractions
.map((interaction) => {
// NOTE: Already filtered by eventName, but we do not have yet configured
// a more robust typing for events for TS to auotmatically handle it
if ('nativeTransfer' in interaction.event) return []
const event = interaction.event
const tokenType = getTokenType(interaction)
const value = findValue(event.params)
const fromKey = fromKeys.find((key) => key in event.params && event.params[key] != null)
const toKey = toKeys.find((key) => key in event.params && event.params[key] != null)
const tokenId = (event.params.id ?? event.params.tokenId)?.toString()
if (!fromKey || !toKey) {
console.error('Invalid event:', event)
return []
}
const to = event.params[toKey] as string
const from = event.params[fromKey] as string
if (tokenType === AssetType.ERC20) {
const decimals = interaction.decimals ?? 18
const amountNumber = formatUnits(BigInt(value as string), decimals)
return [
{
type: tokenType,
name: interaction.contractName,
symbol: interaction.contractSymbol,
address: interaction.contractAddress,
amount: amountNumber,
to,
from,
},
]
} else if (tokenType === AssetType.ERC721) {
return [
{
type: tokenType,
name: interaction.contractName,
symbol: interaction.contractSymbol,
address: interaction.contractAddress,
amount: '1',
tokenId,
to,
from,
},
]
} else if (tokenType === AssetType.ERC1155) {
return [
{
type: tokenType,
name: interaction.contractName,
symbol: interaction.contractSymbol,
address: interaction.contractAddress,
tokenId,
amount: value as string,
to,
from,
},
]
}
console.warn('Unsupported type:', interaction)
// TODO: Batch transfers are not supported yet
return []
})
.flat()
}
function getNativeTokenValueEvents(interactions: Interaction[], from: string): Asset[] {
return interactions.reduce((acc, interaction) => {
if (
'nativeTransfer' in interaction.event &&
interaction.event.nativeTransfer &&
// NOTE: We already have native transfer from receipt, thus we ignore the one from trace
!sameAddress(interaction.event.params.from, from)
) {
const eventParams = interaction.event.params
return [
...acc,
{
from: eventParams.from,
to: eventParams.to,
type: AssetType.native,
amount: getNativeTokenValueSent(eventParams.value),
name: 'Ethereum', // TODO: Make chain agnostic
symbol: 'ETH',
address: ethAddress,
},
]
} else {
return acc
}
}, [] as Asset[])
}
function getNativeTokenValueSent(nativeValueSent: string | undefined): string {
return Number(formatEther(BigInt(nativeValueSent || 0)))
.toString()
.replace(/^(\d+\.\d*?[0-9])0+$/g, '$1')
}
export function getAssetsTransfers(interactions: Interaction[], value: string, from: string, to: string): Asset[] {
const assets = getTokens(interactions)
const ethValueSent = getNativeTokenValueSent(value)
const nativeTransfer = getNativeTokenValueEvents(interactions, from)
if (Number(ethValueSent)) {
assets.push({
from,
to,
type: AssetType.native,
amount: ethValueSent,
name: 'Ethereum', // TODO: Make chain agnostic
symbol: 'ETH',
address: ethAddress,
})
}
return [...assets, ...nativeTransfer]
}