forked from tahowallet/extension
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathassets.ts
406 lines (369 loc) · 12 KB
/
assets.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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
import { TokenList } from "@uniswap/token-lists"
import { UNIXTime, HexString } from "./types"
import {
NetworkSpecific,
SmartContract,
Network,
NetworkBaseAsset,
} from "./networks"
import { fromFixedPoint } from "./lib/fixed-point"
/**
* A reference to a token list, with the name, URL, and potentially logo of the
* list. Used to track the one or more token lists that include a given asset's
* metadata.
*/
export type TokenListCitation = {
name: string
url: string
logoURL?: string
}
/**
* A `@uniswap/token-lists` token list alongside the URL that provided that
* token list.
*/
export type TokenListAndReference = {
url: string
tokenList: TokenList
}
/**
* Metadata for a given asset, as well as the one or more token lists that
* provided that metadata.
*
* Note that the metadata is entirely optional.
*/
export type AssetMetadata = {
coinGeckoID?: string
logoURL?: string
websiteURL?: string
tokenLists?: TokenListCitation[]
/**
* Set by the user to indicate explicitly trust in a
* legitimate asset.
*/
verified?: boolean
removed?: boolean
}
/**
* Every asset has metadata. Some of them have network-specific metadata.
*/
export type NetworkSpecificAssetMetadata = AssetMetadata & {
/**
* Set on assets discovered through transaction annotations per address
*/
discoveryTxHash?: {
[address: HexString]: HexString
}
}
export type AnyAssetMetadata = AssetMetadata | NetworkSpecificAssetMetadata
/**
* The name and symbol of an arbitrary asset, fungible or non-fungible,
* alongside potential metadata about that asset.
*/
export type Asset = {
symbol: string
name: string
metadata?: AssetMetadata
}
/**
* An asset whose metadata comes from CoinGecko and includes an associated
* asset id in CoinGecko's records.
*/
export type CoinGeckoAsset = Asset & {
metadata?: Asset["metadata"] & {
coinGeckoID: string
}
}
/*
* Fungible assets include coins, currencies, and many tokens.
*/
export type FungibleAsset = Asset & {
decimals: number
}
/**
* A simple alias for FungibleAsset to denote types that are expected to be
* fiat currencies, typically used outside of the cryptocurrency world.
*
* Currently *does not offer type safety*, just documentation value; see
* https://github.com/microsoft/TypeScript/issues/202 and for a TS feature that
* would give this some more teeth. Right now, any `FiatCurrency` can be assigned
* to any `FungibleAsset` and vice versa.
*/
export type FiatCurrency = FungibleAsset
/**
* Any asset that exists on a particular network; see {@link NetworkSpecific)
* for information on network-specific objects.
*/
export type NetworkSpecificAsset = NetworkSpecific &
Asset & {
metadata?: NetworkSpecificAssetMetadata
}
/**
* Any asset that is managed by a smart contract; see {@link SmartContract) for
* information on smart contract objects.
*/
export type SmartContractAsset = SmartContract & Asset
/**
* Any fungible asset that is managed by a smart contract; see
* {@link SmartContract) for information on smart contract objects.
*/
export type SmartContractFungibleAsset = FungibleAsset &
SmartContract &
NetworkSpecificAsset
/*
* The primary type representing amounts in fungible or non-fungible asset
* transactions.
*/
export type AssetAmount = {
asset: Asset
amount: bigint
}
/*
* The primary type representing amounts in fungible asset transactions.
*/
export type FungibleAssetAmount = AnyAssetAmount<FungibleAsset>
/*
* A union of all assets we expect to price.
*/
export type AnyAsset =
| Asset
| NetworkSpecificAsset
| FiatCurrency
| FungibleAsset
| SmartContractFungibleAsset
| NetworkBaseAsset
/**
* An asset that can be swapped with our current providers
*/
export type SwappableAsset = SmartContractFungibleAsset | NetworkBaseAsset
/**
* An amount associated with a smart contract; used to carry information like
* per-account smart contract asset balances when full asset information isn't
* available.
*/
export type SmartContractAmount = {
smartContract: SmartContract
amount: bigint
}
/*
* The primary type representing amounts in fungible asset transactions.
*/
export type AnyAssetAmount<T extends AnyAsset = AnyAsset> = {
asset: T
amount: bigint
}
/*
* Represents a price relationship between two assets, fungible or non-fungible,
* at a given time.
*
* PricePoint is the preferred price type, as it includes both sides of a pair
* and doesn't give up any accuracy.
*/
export type PricePoint = {
pair: [AnyAsset, AnyAsset]
amounts: [bigint, bigint]
time: UNIXTime
}
/*
* Used to represent the price (per single unit) of an asset, fungible or
* non-fungible, against a fungible asset. Note the fungible asset can be
* something like an ERC-20 token or fiat currency.
*
* In almost all cases, PricePoint should be preferred. UnitPricePoint should
* only be used when the details of the other side of a price pair are unknown.
*/
export type UnitPricePoint<T extends AnyAsset> = {
unitPrice: AnyAssetAmount<T>
time: UNIXTime
}
/**
* An object representing a transfer of an asset from one address to another.
* Includes information on where the information on the transfer was found, as
* well as the transaction that executed the transfer.
*/
export type AssetTransfer = {
network: Network
assetAmount: AssetAmount
from: HexString
to: HexString
dataSource: "alchemy" | "local"
txHash: string
}
/**
* Type guard to check if an AnyAsset is actually a FungibleAsset.
*/
export function isFungibleAsset(asset: AnyAsset): asset is FungibleAsset {
return "decimals" in asset
}
/**
* Type guard to check if an AnyAsset is actually a SmartContractFungibleAsset.
*/
export function isSmartContractFungibleAsset<T extends AnyAsset>(
asset: T,
): asset is T & SmartContractFungibleAsset {
return "homeNetwork" in asset && isFungibleAsset(asset)
}
/**
* Type guard to check if an AnyAssetAmount is actually a FungibleAssetAmount.
*
* WARNING: Only use this if AnyAssetAmount<T> typing isn't enough to carry the
* FungibleAsset nature of the internal asset!
*/
export function isFungibleAssetAmount(
assetAmount: AnyAssetAmount,
): assetAmount is FungibleAssetAmount {
return isFungibleAsset(assetAmount.asset)
}
/**
* Flips `pair` and `amounts` values in the PricePoint object.
*
* @param pricePoint
* @returns pricePoint with flipped pair and amounts
*/
export function flipPricePoint(pricePoint: PricePoint): PricePoint {
const { pair, amounts, time } = pricePoint
return {
pair: [pair[1], pair[0]],
amounts: [amounts[1], amounts[0]],
time,
}
}
/**
* Converts the given source asset amount, fungible or non-fungible, to a target
* asset amount based on the conversion rate in the passed price point.
*
* In both cases, the price point is expected to have the source asset first
* and then the target asset. For non-fungible assets, the price point must
* have an `amount` of 1 for the source asset.
*
* The converted amount is returned as an AssetAmount of the target asset.
*
* If the source or target assets are not fungible, or if the source asset in
* the price point does not match the asset of the `sourceAssetAmount`, returns
* `undefined`.
*
* @param sourceAssetAmount The AssetAmount being converted. The asset of this
* amount should match the first asset in the price point.
* @param assetPricePoint A PricePoint with the first item being the source asset
* and the second being the target asset. If undefined, this function will
* return undefined.
*
* @return If the source and target assets are both fungible, the target asset
* amount corresponding to the passed source asset amount. If the source
* asset is non-fungible and the price point is the price of one unit of
* the source asset, the target asset amount in the price point. Otherwise,
* undefined.
*/
export function convertAssetAmountViaPricePoint<T extends AnyAssetAmount>(
sourceAssetAmount: T,
assetPricePoint: PricePoint | undefined,
): FungibleAssetAmount | undefined {
if (typeof assetPricePoint === "undefined") {
return undefined
}
const [sourceAsset, targetAsset] = assetPricePoint.pair
const [sourceConversionFactor, targetConversionFactor] =
assetPricePoint.amounts
if (
sourceAssetAmount.asset.symbol === sourceAsset.symbol &&
isFungibleAsset(sourceAsset) &&
isFungibleAsset(targetAsset)
) {
// A price point gives us X of the source asset = Y of the target asset, as
// a pair of fixed-point values. We have M of the source asset, and want to
// find out how much of the target asset that is.
//
// The simple version is that we want to do M * X / Y; however, we have the
// conversion _factor_ for X and Y, which are the mathematical inverse of
// what we refer to as X and Y above. As such, we can instead express this as
// M * T / S, where S is the source conversion factor and T is the target
// conversion factor.
//
// Below, M is the source asset amount, S is the sourceConversionFactor,
// and T is the targetConversionFactor. Extra parentheses are added around
// the multiplication to emphasize order matters! If we computed M / S
// first we would risk losing precision in the integer division.
const targetCurrencyAmount =
(sourceAssetAmount.amount * targetConversionFactor) /
sourceConversionFactor
// Reduce the fixed-point representation to the target asset's decimals.
return {
asset: targetAsset,
amount: targetCurrencyAmount,
}
}
// For non-fungible assets, require that the target asset be fungible and
// that the source conversion factor be 1, i.e. that the price point tells us
// what 1 of the source asset is in target asset terms. Generally in these
// cases we expect the source asset amount to be 1, but we multiply out just
// in case.
if (
sourceAssetAmount.asset.symbol === sourceAsset.symbol &&
isFungibleAsset(targetAsset) &&
sourceConversionFactor === 1n
) {
return {
asset: targetAsset,
amount: sourceAssetAmount.amount * targetConversionFactor,
}
}
return undefined
}
/**
* Looks at the provided price point and extracts a unit price for the first
* asset in the price point, i.e. returns the number of the second asset
* equivalent to one of the first asset. In addition to handling strange
* ratios, recognizes a unit in the appropriate fixed point decimal count of
* the target asset.
*
* Like convertAssetAmountViaPricePoint, returns undefined if either of the two
* assets in the price point are not fungible, or if the provided price point
* is undefined.
*/
export function unitPricePointForPricePoint(
assetPricePoint: PricePoint | undefined,
): UnitPricePoint<FungibleAsset> | undefined {
if (typeof assetPricePoint === "undefined") {
return undefined
}
const sourceAsset = assetPricePoint.pair[0]
const unitPrice = convertAssetAmountViaPricePoint(
{
amount:
"decimals" in sourceAsset
? 1n * 10n ** BigInt(sourceAsset.decimals)
: 1n,
asset: sourceAsset,
},
assetPricePoint,
)
if (typeof unitPrice !== "undefined") {
return {
unitPrice,
time: assetPricePoint.time,
}
}
return undefined
}
/**
* Given a FungibleAssetAmount and a desired number of decimals, convert the
* amount to a floating point JavaScript number with the specified number of
* decimal points (modulo floating point precision oddities).
*
* NOTE: The resulting number may not have perfect accuracy, and is truncated
* rather than rounded. It should not be used for further math that requires
* accuracy.
*
* @param assetAmount The asset and (fixed point bigint) amount to convert.
* @return The floating point JavaScript number representation of the given
* asset amount, truncated to the given number of decimal points.
*/
export function assetAmountToDesiredDecimals(
assetAmount: FungibleAssetAmount,
desiredDecimals: number,
): number {
const {
amount,
asset: { decimals },
} = assetAmount
return fromFixedPoint(amount, decimals, desiredDecimals)
}