-
Notifications
You must be signed in to change notification settings - Fork 18
/
Token.kt
299 lines (263 loc) · 9.62 KB
/
Token.kt
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
package org.p2p.core.token
import android.os.Parcelable
import java.math.BigDecimal
import java.math.BigInteger
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
import org.p2p.core.crypto.Base58String
import org.p2p.core.crypto.toBase58Instance
import org.p2p.core.utils.Constants
import org.p2p.core.utils.Constants.SOL_NAME
import org.p2p.core.utils.Constants.USDC_MINT
import org.p2p.core.utils.Constants.USDT_MINT
import org.p2p.core.utils.Constants.WRAPPED_ETH_MINT
import org.p2p.core.utils.Constants.WRAPPED_SOL_MINT
import org.p2p.core.utils.asCurrency
import org.p2p.core.utils.asUsd
import org.p2p.core.utils.formatFiat
import org.p2p.core.utils.formatToken
import org.p2p.core.utils.isZero
import org.p2p.core.utils.orZero
import org.p2p.core.utils.scaleNine
import org.p2p.core.utils.scaleTwo
import org.p2p.core.utils.toLamports
import org.p2p.core.utils.toPowerValue
import org.p2p.core.wrapper.eth.EthAddress
sealed class Token constructor(
open val publicKey: String?,
open val tokenSymbol: String,
open val decimals: Int,
open val mintAddress: String,
open val programId: String? = null,
open val tokenName: String,
open val iconUrl: String?,
open val isWrapped: Boolean,
open var rate: BigDecimal?,
open var currency: String = Constants.USD_READABLE_SYMBOL,
open val tokenExtensions: TokenExtensions,
) : Parcelable {
@Parcelize
data class Active constructor(
// put tokenSymbol first for toString()
override val tokenSymbol: String,
override val publicKey: String,
val totalInUsd: BigDecimal?,
val total: BigDecimal,
val visibility: TokenVisibility,
val tokenServiceAddress: String,
override val tokenExtensions: TokenExtensions,
override val decimals: Int,
override val mintAddress: String,
override val programId: String?,
override val tokenName: String,
override val iconUrl: String?,
override val isWrapped: Boolean,
override var rate: BigDecimal?,
override var currency: String = Constants.USD_READABLE_SYMBOL,
) : Token(
publicKey = publicKey,
tokenSymbol = tokenSymbol,
decimals = decimals,
mintAddress = mintAddress,
programId = programId,
tokenName = tokenName,
iconUrl = iconUrl,
isWrapped = isWrapped,
rate = rate,
currency = currency,
tokenExtensions = tokenExtensions,
) {
@IgnoredOnParcel
val totalInLamports: BigInteger
get() = total.toLamports(decimals)
@IgnoredOnParcel
val totalInUsdScaled: BigDecimal?
get() = totalInUsd?.scaleTwo()
@IgnoredOnParcel
val isZero: Boolean
get() = total.isZero()
@IgnoredOnParcel
val isHidden: Boolean
get() = visibility == TokenVisibility.HIDDEN
@IgnoredOnParcel
val canTokenBeHidden: Boolean
get() = tokenExtensions.canTokenBeHidden != false
@IgnoredOnParcel
val mintAddressB58: Base58String
get() = mintAddress.toBase58Instance()
fun isDefinitelyHidden(isZerosHidden: Boolean): Boolean {
val isHiddenByUser = visibility == TokenVisibility.HIDDEN
val isHiddenByDefault = isZerosHidden &&
isZero &&
visibility == TokenVisibility.DEFAULT
val isHidden = isHiddenByUser || isHiddenByDefault
return canTokenBeHidden && isHidden
}
fun getFormattedUsdTotal(includeSymbol: Boolean = true): String? {
return if (includeSymbol) totalInUsd?.asUsd() else totalInUsd?.formatFiat()
}
fun getFormattedTotal(includeSymbol: Boolean = false): String =
if (includeSymbol) {
"${total.formatToken(decimals)} $tokenSymbol"
} else {
total.formatToken(decimals)
}
}
@Parcelize
data class Eth constructor(
override val publicKey: String,
val totalInUsd: BigDecimal?,
val total: BigDecimal,
var isClaiming: Boolean = false,
var latestActiveBundleId: String? = null,
val tokenServiceAddress: String = publicKey,
override val tokenSymbol: String,
override val decimals: Int,
override val mintAddress: String,
override val tokenName: String,
override val iconUrl: String?,
override var rate: BigDecimal?,
override var currency: String = Constants.USD_READABLE_SYMBOL
) : Token(
publicKey = publicKey,
tokenSymbol = tokenSymbol,
decimals = decimals,
mintAddress = mintAddress,
tokenName = tokenName,
iconUrl = iconUrl,
isWrapped = false,
rate = rate,
currency = currency,
tokenExtensions = TokenExtensions.NONE
) {
@IgnoredOnParcel
val isEth: Boolean
get() = mintAddress == WRAPPED_ETH_MINT
@IgnoredOnParcel
val totalInLamports: BigInteger
get() = total.toLamports(decimals)
@IgnoredOnParcel
val isZero: Boolean
get() = total.isZero()
fun getFormattedUsdTotal(includeSymbol: Boolean = true): String? {
return if (includeSymbol) totalInUsd?.asUsd() else totalInUsd?.formatFiat()
}
fun getFormattedTotal(includeSymbol: Boolean = false): String {
val decimals = if (isEth && decimals > 8) 8 else decimals
val amount = total.formatToken(decimals)
return if (includeSymbol) {
"$amount $tokenSymbol"
} else {
amount
}
}
fun getEthAddress(): EthAddress {
return EthAddress(publicKey)
}
}
@Parcelize
data class Other constructor(
override val tokenSymbol: String,
override val decimals: Int,
override val mintAddress: String,
override val tokenName: String,
override val iconUrl: String?,
override val isWrapped: Boolean,
override var rate: BigDecimal?,
override var currency: String = Constants.USD_READABLE_SYMBOL,
override val tokenExtensions: TokenExtensions,
) : Token(
publicKey = null,
tokenSymbol = tokenSymbol,
decimals = decimals,
mintAddress = mintAddress,
tokenName = tokenName,
iconUrl = iconUrl,
isWrapped = isWrapped,
rate = rate,
currency = currency,
tokenExtensions = tokenExtensions
)
@IgnoredOnParcel
val isSOL: Boolean
get() = mintAddress.equals(WRAPPED_SOL_MINT, ignoreCase = true)
@IgnoredOnParcel
val isSpl: Boolean
get() = !isSOL
@IgnoredOnParcel
val isToken2022: Boolean
get() = programId == Constants.SOLANA_TOKEN_2022_PROGRAM_ID
@IgnoredOnParcel
val isUSDC: Boolean
get() = mintAddress.equals(USDC_MINT, ignoreCase = true)
@IgnoredOnParcel
val isUSDT: Boolean
get() = mintAddress.equals(USDT_MINT, ignoreCase = true)
@IgnoredOnParcel
val usdRateOrZero: BigDecimal
get() = rate.orZero()
@IgnoredOnParcel
val currencyFormattedRate: String
get() = usdRateOrZero.asCurrency(currencySymbol)
@IgnoredOnParcel
val currencySymbol: String
get() = when (currency) {
Constants.USD_READABLE_SYMBOL -> Constants.USD_SYMBOL
Constants.GBP_READABLE_SYMBOL -> Constants.GBP_SYMBOL
Constants.EUR_READABLE_SYMBOL -> Constants.EUR_SYMBOL
else -> currency
}
@IgnoredOnParcel
val isActive: Boolean
get() = this is Active
fun getFormattedName(): String = if (isSOL) SOL_NAME else tokenName
companion object {
fun createSOL(
publicKey: String,
tokenMetadata: TokenMetadata,
amount: Long,
solPrice: BigDecimal?
): Active {
val total: BigDecimal = BigDecimal(amount).divide(tokenMetadata.decimals.toPowerValue())
return Active(
publicKey = publicKey,
tokenSymbol = tokenMetadata.symbol,
decimals = tokenMetadata.decimals,
mintAddress = tokenMetadata.mintAddress,
programId = null,
tokenName = SOL_NAME,
iconUrl = tokenMetadata.iconUrl,
totalInUsd = if (amount == 0L) null else solPrice?.let { total.multiply(it) },
total = total.scaleNine(tokenMetadata.decimals),
rate = solPrice,
tokenServiceAddress = Constants.TOKEN_SERVICE_NATIVE_SOL_TOKEN,
visibility = TokenVisibility.DEFAULT,
isWrapped = tokenMetadata.isWrapped,
tokenExtensions = TokenExtensions()
)
}
}
}
fun List<Token.Active>.findSolOrThrow(): Token.Active = first { it.isSOL }
fun List<Token.Active>.findSolOrNull(): Token.Active? = firstOrNull { it.isSOL }
fun List<Token.Active>.findByMintAddress(mintAddress: String): Token.Active? =
firstOrNull { it.mintAddress == mintAddress }
@JvmName("findByNullableMintAddress")
fun List<Token.Active>.findByMintAddress(mintAddress: String?): Token.Active? =
mintAddress?.let(::findByMintAddress)
fun List<Token.Active>.sortedWithPreferredStableCoins(): List<Token.Active> {
return sortedWith(
compareBy<Token.Active> {
when (it.mintAddress) {
USDC_MINT -> 1
USDT_MINT -> 2
else -> 3
}
}
.thenComparing(
compareByDescending {
it.totalInUsd.orZero()
}
)
)
}